diff --git a/VIRTUOS_ExpansionPluginTests/.gitattributes b/VIRTUOS_ExpansionPluginTests/.gitattributes
new file mode 100644
index 0000000..3373152
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/.gitattributes
@@ -0,0 +1,2 @@
+* text=auto
+*.bat eol=crlf
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/.gitignore b/VIRTUOS_ExpansionPluginTests/.gitignore
new file mode 100644
index 0000000..c66e5a0
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/.gitignore
@@ -0,0 +1,13 @@
+
+.hg/
+binaries/
+deriveddatacache/
+.vs/
+build/
+intermediate/
+PACKPLUGIN/
+COMPPLUGIN/
+saved/
+*.orig
+*.sln
+*.xlsx
diff --git a/VIRTUOS_ExpansionPluginTests/.vsconfig b/VIRTUOS_ExpansionPluginTests/.vsconfig
new file mode 100644
index 0000000..1a9d718
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/.vsconfig
@@ -0,0 +1,13 @@
+{
+ "version": "1.0",
+ "components": [
+ "Microsoft.Net.Component.4.6.2.TargetingPack",
+ "Microsoft.VisualStudio.Component.VC.14.36.17.6.x86.x64",
+ "Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
+ "Microsoft.VisualStudio.Component.Windows10SDK.22000",
+ "Microsoft.VisualStudio.Workload.CoreEditor",
+ "Microsoft.VisualStudio.Workload.ManagedDesktop",
+ "Microsoft.VisualStudio.Workload.NativeDesktop",
+ "Microsoft.VisualStudio.Workload.NativeGame"
+ ]
+}
diff --git a/VIRTUOS_ExpansionPluginTests/Config/DefaultEditor.ini b/VIRTUOS_ExpansionPluginTests/Config/DefaultEditor.ini
new file mode 100644
index 0000000..f0a38bf
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Config/DefaultEditor.ini
@@ -0,0 +1,8 @@
+[/Script/UnrealEd.AssetViewerSettings]
+-Profiles=(ProfileName="Default",DirectionalLightIntensity=2.620000,DirectionalLightColor=(R=0.990000,G=0.839850,B=0.732600,A=1.000000),SkyLightIntensity=0.880000,bRotateLightingRig=False,bShowEnvironment=True,bShowFloor=True,EnvironmentCubeMapPath="/Engine/EditorMaterials/AssetViewer/EpicQuadPanorama_CC+EV1.EpicQuadPanorama_CC+EV1",PostProcessingSettings=(bOverride_WhiteTemp=True,bOverride_WhiteTint=False,bOverride_ColorSaturation=True,bOverride_ColorContrast=True,bOverride_ColorGamma=True,bOverride_ColorGain=True,bOverride_ColorOffset=True,bOverride_ColorSaturationShadows=False,bOverride_ColorContrastShadows=False,bOverride_ColorGammaShadows=False,bOverride_ColorGainShadows=False,bOverride_ColorOffsetShadows=False,bOverride_ColorSaturationMidtones=False,bOverride_ColorContrastMidtones=False,bOverride_ColorGammaMidtones=False,bOverride_ColorGainMidtones=False,bOverride_ColorOffsetMidtones=False,bOverride_ColorSaturationHighlights=False,bOverride_ColorContrastHighlights=False,bOverride_ColorGammaHighlights=False,bOverride_ColorGainHighlights=False,bOverride_ColorOffsetHighlights=False,bOverride_ColorCorrectionShadowsMax=False,bOverride_ColorCorrectionHighlightsMin=False,bOverride_FilmWhitePoint=False,bOverride_FilmSaturation=False,bOverride_FilmChannelMixerRed=False,bOverride_FilmChannelMixerGreen=False,bOverride_FilmChannelMixerBlue=False,bOverride_FilmContrast=False,bOverride_FilmDynamicRange=False,bOverride_FilmHealAmount=False,bOverride_FilmToeAmount=False,bOverride_FilmShadowTint=False,bOverride_FilmShadowTintBlend=False,bOverride_FilmShadowTintAmount=False,bOverride_FilmSlope=True,bOverride_FilmToe=True,bOverride_FilmShoulder=True,bOverride_FilmBlackClip=True,bOverride_FilmWhiteClip=True,bOverride_SceneColorTint=False,bOverride_SceneFringeIntensity=False,bOverride_AmbientCubemapTint=False,bOverride_AmbientCubemapIntensity=False,bOverride_BloomIntensity=True,bOverride_BloomThreshold=False,bOverride_Bloom1Tint=False,bOverride_Bloom1Size=False,bOverride_Bloom2Size=False,bOverride_Bloom2Tint=False,bOverride_Bloom3Tint=False,bOverride_Bloom3Size=False,bOverride_Bloom4Tint=False,bOverride_Bloom4Size=False,bOverride_Bloom5Tint=False,bOverride_Bloom5Size=False,bOverride_Bloom6Tint=False,bOverride_Bloom6Size=False,bOverride_BloomSizeScale=False,bOverride_BloomDirtMaskIntensity=False,bOverride_BloomDirtMaskTint=False,bOverride_BloomDirtMask=False,bOverride_AutoExposureMethod=True,bOverride_AutoExposureLowPercent=False,bOverride_AutoExposureHighPercent=False,bOverride_AutoExposureMinBrightness=True,bOverride_AutoExposureMaxBrightness=True,bOverride_AutoExposureSpeedUp=False,bOverride_AutoExposureSpeedDown=False,bOverride_AutoExposureBias=True,bOverride_HistogramLogMin=True,bOverride_HistogramLogMax=True,bOverride_LensFlareIntensity=False,bOverride_LensFlareTint=False,bOverride_LensFlareTints=False,bOverride_LensFlareBokehSize=False,bOverride_LensFlareBokehShape=False,bOverride_LensFlareThreshold=False,bOverride_VignetteIntensity=True,bOverride_GrainIntensity=False,bOverride_GrainJitter=False,bOverride_AmbientOcclusionIntensity=True,bOverride_AmbientOcclusionStaticFraction=True,bOverride_AmbientOcclusionRadius=True,bOverride_AmbientOcclusionFadeDistance=False,bOverride_AmbientOcclusionFadeRadius=False,bOverride_AmbientOcclusionDistance=False,bOverride_AmbientOcclusionRadiusInWS=False,bOverride_AmbientOcclusionPower=True,bOverride_AmbientOcclusionBias=True,bOverride_AmbientOcclusionQuality=True,bOverride_AmbientOcclusionMipBlend=True,bOverride_AmbientOcclusionMipScale=True,bOverride_AmbientOcclusionMipThreshold=True,bOverride_LPVIntensity=False,bOverride_LPVDirectionalOcclusionIntensity=False,bOverride_LPVDirectionalOcclusionRadius=False,bOverride_LPVDiffuseOcclusionExponent=False,bOverride_LPVSpecularOcclusionExponent=False,bOverride_LPVDiffuseOcclusionIntensity=False,bOverride_LPVSpecularOcclusionIntensity=False,bOverride_LPVSize=False,bOverride_LPVSecondaryOcclusionIntensity=False,bOverride_LPVSecondaryBounceIntensity=False,bOverride_LPVGeometryVolumeBias=False,bOverride_LPVVplInjectionBias=False,bOverride_LPVEmissiveInjectionIntensity=False,bOverride_IndirectLightingColor=False,bOverride_IndirectLightingIntensity=False,bOverride_ColorGradingIntensity=True,bOverride_ColorGradingLUT=True,bOverride_DepthOfFieldFocalDistance=False,bOverride_DepthOfFieldFstop=False,bOverride_DepthOfFieldSensorWidth=False,bOverride_DepthOfFieldDepthBlurRadius=False,bOverride_DepthOfFieldDepthBlurAmount=False,bOverride_DepthOfFieldFocalRegion=False,bOverride_DepthOfFieldNearTransitionRegion=False,bOverride_DepthOfFieldFarTransitionRegion=False,bOverride_DepthOfFieldScale=True,bOverride_DepthOfFieldMaxBokehSize=False,bOverride_DepthOfFieldNearBlurSize=False,bOverride_DepthOfFieldFarBlurSize=False,bOverride_DepthOfFieldMethod=True,bOverride_MobileHQGaussian=False,bOverride_DepthOfFieldBokehShape=False,bOverride_DepthOfFieldOcclusion=False,bOverride_DepthOfFieldColorThreshold=False,bOverride_DepthOfFieldSizeThreshold=False,bOverride_DepthOfFieldSkyFocusDistance=False,bOverride_DepthOfFieldVignetteSize=False,bOverride_MotionBlurAmount=False,bOverride_MotionBlurMax=False,bOverride_MotionBlurPerObjectSize=False,bOverride_ScreenPercentage=False,bOverride_ScreenSpaceReflectionIntensity=True,bOverride_ScreenSpaceReflectionQuality=True,bOverride_ScreenSpaceReflectionMaxRoughness=True,bOverride_ScreenSpaceReflectionRoughnessScale=False,WhiteTemp=6700.000000,WhiteTint=0.000000,ColorSaturation=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrast=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGamma=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGain=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffset=(X=0.005000,Y=0.005000,Z=0.005000,W=0.000000),ColorSaturationShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrastShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGammaShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGainShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffsetShadows=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorCorrectionShadowsMax=0.090000,ColorSaturationMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrastMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGammaMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGainMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffsetMidtones=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorSaturationHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrastHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGammaHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGainHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffsetHighlights=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorCorrectionHighlightsMin=0.500000,FilmWhitePoint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),FilmShadowTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),FilmShadowTintBlend=0.500000,FilmShadowTintAmount=0.000000,FilmSaturation=1.000000,FilmChannelMixerRed=(R=1.000000,G=0.000000,B=0.000000,A=1.000000),FilmChannelMixerGreen=(R=0.000000,G=1.000000,B=0.000000,A=1.000000),FilmChannelMixerBlue=(R=0.000000,G=0.000000,B=1.000000,A=1.000000),FilmContrast=0.030000,FilmToeAmount=1.000000,FilmHealAmount=1.000000,FilmDynamicRange=4.000000,FilmSlope=0.880000,FilmToe=0.550000,FilmShoulder=0.260000,FilmBlackClip=0.000000,FilmWhiteClip=0.040000,SceneColorTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),SceneFringeIntensity=0.000000,BloomIntensity=0.675000,BloomThreshold=-1.000000,BloomSizeScale=4.000000,Bloom1Size=0.300000,Bloom2Size=1.000000,Bloom3Size=2.000000,Bloom4Size=10.000000,Bloom5Size=30.000000,Bloom6Size=64.000000,Bloom1Tint=(R=0.346500,G=0.346500,B=0.346500,A=1.000000),Bloom2Tint=(R=0.138000,G=0.138000,B=0.138000,A=1.000000),Bloom3Tint=(R=0.117600,G=0.117600,B=0.117600,A=1.000000),Bloom4Tint=(R=0.066000,G=0.066000,B=0.066000,A=1.000000),Bloom5Tint=(R=0.066000,G=0.066000,B=0.066000,A=1.000000),Bloom6Tint=(R=0.061000,G=0.061000,B=0.061000,A=1.000000),BloomDirtMaskIntensity=0.000000,BloomDirtMaskTint=(R=0.500000,G=0.500000,B=0.500000,A=1.000000),BloomDirtMask=None,AmbientCubemapTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),AmbientCubemapIntensity=1.000000,AmbientCubemap=None,AutoExposureMethod=AEM_Histogram,AutoExposureLowPercent=80.000000,AutoExposureHighPercent=98.300003,AutoExposureMinBrightness=1.000000,AutoExposureMaxBrightness=1.000000,AutoExposureSpeedUp=3.000000,AutoExposureSpeedDown=1.000000,AutoExposureBias=0.330000,HistogramLogMin=-8.000000,HistogramLogMax=4.000000,LensFlareIntensity=1.000000,LensFlareTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),LensFlareBokehSize=3.000000,LensFlareThreshold=8.000000,LensFlareBokehShape=None,LensFlareTints[0]=(R=1.000000,G=0.800000,B=0.400000,A=0.600000),LensFlareTints[1]=(R=1.000000,G=1.000000,B=0.600000,A=0.530000),LensFlareTints[2]=(R=0.800000,G=0.800000,B=1.000000,A=0.460000),LensFlareTints[3]=(R=0.500000,G=1.000000,B=0.400000,A=0.390000),LensFlareTints[4]=(R=0.500000,G=0.800000,B=1.000000,A=0.310000),LensFlareTints[5]=(R=0.900000,G=1.000000,B=0.800000,A=0.270000),LensFlareTints[6]=(R=1.000000,G=0.800000,B=0.400000,A=0.220000),LensFlareTints[7]=(R=0.900000,G=0.700000,B=0.700000,A=0.150000),VignetteIntensity=0.161468,GrainJitter=0.000000,GrainIntensity=0.000000,AmbientOcclusionIntensity=1.000000,AmbientOcclusionStaticFraction=1.000000,AmbientOcclusionRadius=73.477997,AmbientOcclusionRadiusInWS=False,AmbientOcclusionFadeDistance=8000.000000,AmbientOcclusionFadeRadius=5000.000000,AmbientOcclusionDistance=80.000000,AmbientOcclusionPower=1.200000,AmbientOcclusionBias=3.000000,AmbientOcclusionQuality=100.000000,AmbientOcclusionMipBlend=0.600000,AmbientOcclusionMipScale=1.700000,AmbientOcclusionMipThreshold=0.010000,IndirectLightingColor=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),IndirectLightingIntensity=1.000000,ColorGradingIntensity=0.000000,ColorGradingLUT=Texture2D'/Engine/EditorResources/RGBTable16x1_AssetViewer.RGBTable16x1_AssetViewer',DepthOfFieldMethod=DOFM_BokehDOF,bMobileHQGaussian=False,DepthOfFieldFstop=4.000000,DepthOfFieldSensorWidth=24.576000,DepthOfFieldFocalDistance=1000.000000,DepthOfFieldDepthBlurAmount=1.000000,DepthOfFieldDepthBlurRadius=0.000000,DepthOfFieldFocalRegion=0.000000,DepthOfFieldNearTransitionRegion=300.000000,DepthOfFieldFarTransitionRegion=500.000000,DepthOfFieldScale=0.000000,DepthOfFieldMaxBokehSize=15.000000,DepthOfFieldNearBlurSize=15.000000,DepthOfFieldFarBlurSize=15.000000,DepthOfFieldBokehShape=None,DepthOfFieldOcclusion=0.400000,DepthOfFieldColorThreshold=1.000000,DepthOfFieldSizeThreshold=0.080000,DepthOfFieldSkyFocusDistance=0.000000,DepthOfFieldVignetteSize=200.000000,MotionBlurAmount=0.500000,MotionBlurMax=5.000000,MotionBlurPerObjectSize=0.500000,LPVIntensity=1.000000,LPVVplInjectionBias=0.640000,LPVSize=5312.000000,LPVSecondaryOcclusionIntensity=0.000000,LPVSecondaryBounceIntensity=0.000000,LPVGeometryVolumeBias=0.384000,LPVEmissiveInjectionIntensity=1.000000,LPVDirectionalOcclusionIntensity=0.000000,LPVDirectionalOcclusionRadius=8.000000,LPVDiffuseOcclusionExponent=1.000000,LPVSpecularOcclusionExponent=7.000000,LPVDiffuseOcclusionIntensity=1.000000,LPVSpecularOcclusionIntensity=1.000000,ScreenSpaceReflectionIntensity=100.000000,ScreenSpaceReflectionQuality=100.000000,ScreenSpaceReflectionMaxRoughness=1.000000,ScreenPercentage=100.000000,WeightedBlendables=(Array=),Blendables=),bPostProcessingEnabled=True,LightingRigRotation=109.389069,RotationSpeed=2.000000,DirectionalLightRotation=(Pitch=-39.999985,Yaw=-67.500015,Roll=0.000000))
+-Profiles=(ProfileName="Default",DirectionalLightIntensity=2.620000,DirectionalLightColor=(R=0.990000,G=0.839850,B=0.732600,A=1.000000),SkyLightIntensity=0.880000,bRotateLightingRig=False,bShowEnvironment=True,bShowFloor=True,EnvironmentCubeMapPath="/Engine/EditorMaterials/AssetViewer/EpicQuadPanorama_CC+EV1.EpicQuadPanorama_CC+EV1",PostProcessingSettings=(bOverride_WhiteTemp=True,bOverride_WhiteTint=False,bOverride_ColorSaturation=True,bOverride_ColorContrast=True,bOverride_ColorGamma=True,bOverride_ColorGain=True,bOverride_ColorOffset=True,bOverride_ColorSaturationShadows=False,bOverride_ColorContrastShadows=False,bOverride_ColorGammaShadows=False,bOverride_ColorGainShadows=False,bOverride_ColorOffsetShadows=False,bOverride_ColorSaturationMidtones=False,bOverride_ColorContrastMidtones=False,bOverride_ColorGammaMidtones=False,bOverride_ColorGainMidtones=False,bOverride_ColorOffsetMidtones=False,bOverride_ColorSaturationHighlights=False,bOverride_ColorContrastHighlights=False,bOverride_ColorGammaHighlights=False,bOverride_ColorGainHighlights=False,bOverride_ColorOffsetHighlights=False,bOverride_ColorCorrectionShadowsMax=False,bOverride_ColorCorrectionHighlightsMin=False,bOverride_FilmWhitePoint=False,bOverride_FilmSaturation=False,bOverride_FilmChannelMixerRed=False,bOverride_FilmChannelMixerGreen=False,bOverride_FilmChannelMixerBlue=False,bOverride_FilmContrast=False,bOverride_FilmDynamicRange=False,bOverride_FilmHealAmount=False,bOverride_FilmToeAmount=False,bOverride_FilmShadowTint=False,bOverride_FilmShadowTintBlend=False,bOverride_FilmShadowTintAmount=False,bOverride_FilmSlope=True,bOverride_FilmToe=True,bOverride_FilmShoulder=True,bOverride_FilmBlackClip=True,bOverride_FilmWhiteClip=True,bOverride_SceneColorTint=False,bOverride_SceneFringeIntensity=False,bOverride_AmbientCubemapTint=False,bOverride_AmbientCubemapIntensity=False,bOverride_BloomIntensity=True,bOverride_BloomThreshold=False,bOverride_Bloom1Tint=False,bOverride_Bloom1Size=False,bOverride_Bloom2Size=False,bOverride_Bloom2Tint=False,bOverride_Bloom3Tint=False,bOverride_Bloom3Size=False,bOverride_Bloom4Tint=False,bOverride_Bloom4Size=False,bOverride_Bloom5Tint=False,bOverride_Bloom5Size=False,bOverride_Bloom6Tint=False,bOverride_Bloom6Size=False,bOverride_BloomSizeScale=False,bOverride_BloomDirtMaskIntensity=False,bOverride_BloomDirtMaskTint=False,bOverride_BloomDirtMask=False,bOverride_AutoExposureMethod=True,bOverride_AutoExposureLowPercent=False,bOverride_AutoExposureHighPercent=False,bOverride_AutoExposureMinBrightness=True,bOverride_AutoExposureMaxBrightness=True,bOverride_AutoExposureSpeedUp=False,bOverride_AutoExposureSpeedDown=False,bOverride_AutoExposureBias=True,bOverride_HistogramLogMin=True,bOverride_HistogramLogMax=True,bOverride_LensFlareIntensity=False,bOverride_LensFlareTint=False,bOverride_LensFlareTints=False,bOverride_LensFlareBokehSize=False,bOverride_LensFlareBokehShape=False,bOverride_LensFlareThreshold=False,bOverride_VignetteIntensity=True,bOverride_GrainIntensity=False,bOverride_GrainJitter=False,bOverride_AmbientOcclusionIntensity=True,bOverride_AmbientOcclusionStaticFraction=True,bOverride_AmbientOcclusionRadius=True,bOverride_AmbientOcclusionFadeDistance=False,bOverride_AmbientOcclusionFadeRadius=False,bOverride_AmbientOcclusionDistance=False,bOverride_AmbientOcclusionRadiusInWS=False,bOverride_AmbientOcclusionPower=True,bOverride_AmbientOcclusionBias=True,bOverride_AmbientOcclusionQuality=True,bOverride_AmbientOcclusionMipBlend=True,bOverride_AmbientOcclusionMipScale=True,bOverride_AmbientOcclusionMipThreshold=True,bOverride_LPVIntensity=False,bOverride_LPVDirectionalOcclusionIntensity=False,bOverride_LPVDirectionalOcclusionRadius=False,bOverride_LPVDiffuseOcclusionExponent=False,bOverride_LPVSpecularOcclusionExponent=False,bOverride_LPVDiffuseOcclusionIntensity=False,bOverride_LPVSpecularOcclusionIntensity=False,bOverride_LPVSize=False,bOverride_LPVSecondaryOcclusionIntensity=False,bOverride_LPVSecondaryBounceIntensity=False,bOverride_LPVGeometryVolumeBias=False,bOverride_LPVVplInjectionBias=False,bOverride_LPVEmissiveInjectionIntensity=False,bOverride_IndirectLightingColor=False,bOverride_IndirectLightingIntensity=False,bOverride_ColorGradingIntensity=True,bOverride_ColorGradingLUT=True,bOverride_DepthOfFieldFocalDistance=False,bOverride_DepthOfFieldFstop=False,bOverride_DepthOfFieldSensorWidth=False,bOverride_DepthOfFieldDepthBlurRadius=False,bOverride_DepthOfFieldDepthBlurAmount=False,bOverride_DepthOfFieldFocalRegion=False,bOverride_DepthOfFieldNearTransitionRegion=False,bOverride_DepthOfFieldFarTransitionRegion=False,bOverride_DepthOfFieldScale=True,bOverride_DepthOfFieldMaxBokehSize=False,bOverride_DepthOfFieldNearBlurSize=False,bOverride_DepthOfFieldFarBlurSize=False,bOverride_DepthOfFieldMethod=True,bOverride_MobileHQGaussian=False,bOverride_DepthOfFieldBokehShape=False,bOverride_DepthOfFieldOcclusion=False,bOverride_DepthOfFieldColorThreshold=False,bOverride_DepthOfFieldSizeThreshold=False,bOverride_DepthOfFieldSkyFocusDistance=False,bOverride_DepthOfFieldVignetteSize=False,bOverride_MotionBlurAmount=False,bOverride_MotionBlurMax=False,bOverride_MotionBlurPerObjectSize=False,bOverride_ScreenPercentage=False,bOverride_ScreenSpaceReflectionIntensity=True,bOverride_ScreenSpaceReflectionQuality=True,bOverride_ScreenSpaceReflectionMaxRoughness=True,bOverride_ScreenSpaceReflectionRoughnessScale=False,WhiteTemp=6700.000000,WhiteTint=0.000000,ColorSaturation=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrast=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGamma=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGain=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffset=(X=0.005000,Y=0.005000,Z=0.005000,W=0.000000),ColorSaturationShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrastShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGammaShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGainShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffsetShadows=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorCorrectionShadowsMax=0.090000,ColorSaturationMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrastMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGammaMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGainMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffsetMidtones=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorSaturationHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrastHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGammaHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGainHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffsetHighlights=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorCorrectionHighlightsMin=0.500000,FilmWhitePoint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),FilmShadowTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),FilmShadowTintBlend=0.500000,FilmShadowTintAmount=0.000000,FilmSaturation=1.000000,FilmChannelMixerRed=(R=1.000000,G=0.000000,B=0.000000,A=1.000000),FilmChannelMixerGreen=(R=0.000000,G=1.000000,B=0.000000,A=1.000000),FilmChannelMixerBlue=(R=0.000000,G=0.000000,B=1.000000,A=1.000000),FilmContrast=0.030000,FilmToeAmount=1.000000,FilmHealAmount=1.000000,FilmDynamicRange=4.000000,FilmSlope=0.880000,FilmToe=0.550000,FilmShoulder=0.260000,FilmBlackClip=0.000000,FilmWhiteClip=0.040000,SceneColorTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),SceneFringeIntensity=0.000000,BloomIntensity=0.675000,BloomThreshold=-1.000000,BloomSizeScale=4.000000,Bloom1Size=0.300000,Bloom2Size=1.000000,Bloom3Size=2.000000,Bloom4Size=10.000000,Bloom5Size=30.000000,Bloom6Size=64.000000,Bloom1Tint=(R=0.346500,G=0.346500,B=0.346500,A=1.000000),Bloom2Tint=(R=0.138000,G=0.138000,B=0.138000,A=1.000000),Bloom3Tint=(R=0.117600,G=0.117600,B=0.117600,A=1.000000),Bloom4Tint=(R=0.066000,G=0.066000,B=0.066000,A=1.000000),Bloom5Tint=(R=0.066000,G=0.066000,B=0.066000,A=1.000000),Bloom6Tint=(R=0.061000,G=0.061000,B=0.061000,A=1.000000),BloomDirtMaskIntensity=0.000000,BloomDirtMaskTint=(R=0.500000,G=0.500000,B=0.500000,A=1.000000),BloomDirtMask=None,AmbientCubemapTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),AmbientCubemapIntensity=1.000000,AmbientCubemap=None,AutoExposureMethod=AEM_Histogram,AutoExposureLowPercent=80.000000,AutoExposureHighPercent=98.300003,AutoExposureMinBrightness=1.000000,AutoExposureMaxBrightness=1.000000,AutoExposureSpeedUp=3.000000,AutoExposureSpeedDown=1.000000,AutoExposureBias=0.330000,HistogramLogMin=-8.000000,HistogramLogMax=4.000000,LensFlareIntensity=1.000000,LensFlareTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),LensFlareBokehSize=3.000000,LensFlareThreshold=8.000000,LensFlareBokehShape=None,LensFlareTints[0]=(R=1.000000,G=0.800000,B=0.400000,A=0.600000),LensFlareTints[1]=(R=1.000000,G=1.000000,B=0.600000,A=0.530000),LensFlareTints[2]=(R=0.800000,G=0.800000,B=1.000000,A=0.460000),LensFlareTints[3]=(R=0.500000,G=1.000000,B=0.400000,A=0.390000),LensFlareTints[4]=(R=0.500000,G=0.800000,B=1.000000,A=0.310000),LensFlareTints[5]=(R=0.900000,G=1.000000,B=0.800000,A=0.270000),LensFlareTints[6]=(R=1.000000,G=0.800000,B=0.400000,A=0.220000),LensFlareTints[7]=(R=0.900000,G=0.700000,B=0.700000,A=0.150000),VignetteIntensity=0.161468,GrainJitter=0.000000,GrainIntensity=0.000000,AmbientOcclusionIntensity=1.000000,AmbientOcclusionStaticFraction=1.000000,AmbientOcclusionRadius=73.477997,AmbientOcclusionRadiusInWS=False,AmbientOcclusionFadeDistance=8000.000000,AmbientOcclusionFadeRadius=5000.000000,AmbientOcclusionDistance=80.000000,AmbientOcclusionPower=1.200000,AmbientOcclusionBias=3.000000,AmbientOcclusionQuality=100.000000,AmbientOcclusionMipBlend=0.600000,AmbientOcclusionMipScale=1.700000,AmbientOcclusionMipThreshold=0.010000,IndirectLightingColor=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),IndirectLightingIntensity=1.000000,ColorGradingIntensity=0.000000,ColorGradingLUT=Texture2D'/Engine/EditorResources/RGBTable16x1_AssetViewer.RGBTable16x1_AssetViewer',DepthOfFieldMethod=DOFM_BokehDOF,bMobileHQGaussian=False,DepthOfFieldFstop=4.000000,DepthOfFieldSensorWidth=24.576000,DepthOfFieldFocalDistance=1000.000000,DepthOfFieldDepthBlurAmount=1.000000,DepthOfFieldDepthBlurRadius=0.000000,DepthOfFieldFocalRegion=0.000000,DepthOfFieldNearTransitionRegion=300.000000,DepthOfFieldFarTransitionRegion=500.000000,DepthOfFieldScale=0.000000,DepthOfFieldMaxBokehSize=15.000000,DepthOfFieldNearBlurSize=15.000000,DepthOfFieldFarBlurSize=15.000000,DepthOfFieldBokehShape=None,DepthOfFieldOcclusion=0.400000,DepthOfFieldColorThreshold=1.000000,DepthOfFieldSizeThreshold=0.080000,DepthOfFieldSkyFocusDistance=0.000000,DepthOfFieldVignetteSize=200.000000,MotionBlurAmount=0.500000,MotionBlurMax=5.000000,MotionBlurPerObjectSize=0.500000,LPVIntensity=1.000000,LPVVplInjectionBias=0.640000,LPVSize=5312.000000,LPVSecondaryOcclusionIntensity=0.000000,LPVSecondaryBounceIntensity=0.000000,LPVGeometryVolumeBias=0.384000,LPVEmissiveInjectionIntensity=1.000000,LPVDirectionalOcclusionIntensity=0.000000,LPVDirectionalOcclusionRadius=8.000000,LPVDiffuseOcclusionExponent=1.000000,LPVSpecularOcclusionExponent=7.000000,LPVDiffuseOcclusionIntensity=1.000000,LPVSpecularOcclusionIntensity=1.000000,ScreenSpaceReflectionIntensity=100.000000,ScreenSpaceReflectionQuality=100.000000,ScreenSpaceReflectionMaxRoughness=1.000000,ScreenPercentage=100.000000,WeightedBlendables=(Array=),Blendables=),bPostProcessingEnabled=True,LightingRigRotation=109.389069,RotationSpeed=2.000000,DirectionalLightRotation=(Pitch=-39.999985,Yaw=-67.500015,Roll=0.000000))
+-Profiles=(ProfileName="Default",DirectionalLightIntensity=2.620000,DirectionalLightColor=(R=0.990000,G=0.839850,B=0.732600,A=1.000000),SkyLightIntensity=0.880000,bRotateLightingRig=False,bShowEnvironment=True,bShowFloor=True,EnvironmentCubeMapPath="",PostProcessingSettings=(bOverride_WhiteTemp=True,bOverride_WhiteTint=False,bOverride_ColorSaturation=True,bOverride_ColorContrast=True,bOverride_ColorGamma=True,bOverride_ColorGain=True,bOverride_ColorOffset=True,bOverride_ColorSaturationShadows=False,bOverride_ColorContrastShadows=False,bOverride_ColorGammaShadows=False,bOverride_ColorGainShadows=False,bOverride_ColorOffsetShadows=False,bOverride_ColorSaturationMidtones=False,bOverride_ColorContrastMidtones=False,bOverride_ColorGammaMidtones=False,bOverride_ColorGainMidtones=False,bOverride_ColorOffsetMidtones=False,bOverride_ColorSaturationHighlights=False,bOverride_ColorContrastHighlights=False,bOverride_ColorGammaHighlights=False,bOverride_ColorGainHighlights=False,bOverride_ColorOffsetHighlights=False,bOverride_ColorCorrectionShadowsMax=False,bOverride_ColorCorrectionHighlightsMin=False,bOverride_FilmWhitePoint=False,bOverride_FilmSaturation=False,bOverride_FilmChannelMixerRed=False,bOverride_FilmChannelMixerGreen=False,bOverride_FilmChannelMixerBlue=False,bOverride_FilmContrast=False,bOverride_FilmDynamicRange=False,bOverride_FilmHealAmount=False,bOverride_FilmToeAmount=False,bOverride_FilmShadowTint=False,bOverride_FilmShadowTintBlend=False,bOverride_FilmShadowTintAmount=False,bOverride_FilmSlope=True,bOverride_FilmToe=True,bOverride_FilmShoulder=True,bOverride_FilmBlackClip=True,bOverride_FilmWhiteClip=True,bOverride_SceneColorTint=False,bOverride_SceneFringeIntensity=False,bOverride_AmbientCubemapTint=False,bOverride_AmbientCubemapIntensity=False,bOverride_BloomIntensity=True,bOverride_BloomThreshold=False,bOverride_Bloom1Tint=False,bOverride_Bloom1Size=False,bOverride_Bloom2Size=False,bOverride_Bloom2Tint=False,bOverride_Bloom3Tint=False,bOverride_Bloom3Size=False,bOverride_Bloom4Tint=False,bOverride_Bloom4Size=False,bOverride_Bloom5Tint=False,bOverride_Bloom5Size=False,bOverride_Bloom6Tint=False,bOverride_Bloom6Size=False,bOverride_BloomSizeScale=False,bOverride_BloomDirtMaskIntensity=False,bOverride_BloomDirtMaskTint=False,bOverride_BloomDirtMask=False,bOverride_AutoExposureMethod=True,bOverride_AutoExposureLowPercent=False,bOverride_AutoExposureHighPercent=False,bOverride_AutoExposureMinBrightness=True,bOverride_AutoExposureMaxBrightness=True,bOverride_AutoExposureSpeedUp=False,bOverride_AutoExposureSpeedDown=False,bOverride_AutoExposureBias=True,bOverride_HistogramLogMin=True,bOverride_HistogramLogMax=True,bOverride_LensFlareIntensity=False,bOverride_LensFlareTint=False,bOverride_LensFlareTints=False,bOverride_LensFlareBokehSize=False,bOverride_LensFlareBokehShape=False,bOverride_LensFlareThreshold=False,bOverride_VignetteIntensity=True,bOverride_GrainIntensity=False,bOverride_GrainJitter=False,bOverride_AmbientOcclusionIntensity=True,bOverride_AmbientOcclusionStaticFraction=True,bOverride_AmbientOcclusionRadius=True,bOverride_AmbientOcclusionFadeDistance=False,bOverride_AmbientOcclusionFadeRadius=False,bOverride_AmbientOcclusionDistance=False,bOverride_AmbientOcclusionRadiusInWS=False,bOverride_AmbientOcclusionPower=True,bOverride_AmbientOcclusionBias=True,bOverride_AmbientOcclusionQuality=True,bOverride_AmbientOcclusionMipBlend=True,bOverride_AmbientOcclusionMipScale=True,bOverride_AmbientOcclusionMipThreshold=True,bOverride_LPVIntensity=False,bOverride_LPVDirectionalOcclusionIntensity=False,bOverride_LPVDirectionalOcclusionRadius=False,bOverride_LPVDiffuseOcclusionExponent=False,bOverride_LPVSpecularOcclusionExponent=False,bOverride_LPVDiffuseOcclusionIntensity=False,bOverride_LPVSpecularOcclusionIntensity=False,bOverride_LPVSize=False,bOverride_LPVSecondaryOcclusionIntensity=False,bOverride_LPVSecondaryBounceIntensity=False,bOverride_LPVGeometryVolumeBias=False,bOverride_LPVVplInjectionBias=False,bOverride_LPVEmissiveInjectionIntensity=False,bOverride_IndirectLightingColor=False,bOverride_IndirectLightingIntensity=False,bOverride_ColorGradingIntensity=True,bOverride_ColorGradingLUT=True,bOverride_DepthOfFieldFocalDistance=False,bOverride_DepthOfFieldFstop=False,bOverride_DepthOfFieldSensorWidth=False,bOverride_DepthOfFieldDepthBlurRadius=False,bOverride_DepthOfFieldDepthBlurAmount=False,bOverride_DepthOfFieldFocalRegion=False,bOverride_DepthOfFieldNearTransitionRegion=False,bOverride_DepthOfFieldFarTransitionRegion=False,bOverride_DepthOfFieldScale=True,bOverride_DepthOfFieldMaxBokehSize=False,bOverride_DepthOfFieldNearBlurSize=False,bOverride_DepthOfFieldFarBlurSize=False,bOverride_DepthOfFieldMethod=True,bOverride_MobileHQGaussian=False,bOverride_DepthOfFieldBokehShape=False,bOverride_DepthOfFieldOcclusion=False,bOverride_DepthOfFieldColorThreshold=False,bOverride_DepthOfFieldSizeThreshold=False,bOverride_DepthOfFieldSkyFocusDistance=False,bOverride_DepthOfFieldVignetteSize=False,bOverride_MotionBlurAmount=False,bOverride_MotionBlurMax=False,bOverride_MotionBlurPerObjectSize=False,bOverride_ScreenPercentage=False,bOverride_ScreenSpaceReflectionIntensity=True,bOverride_ScreenSpaceReflectionQuality=True,bOverride_ScreenSpaceReflectionMaxRoughness=True,bOverride_ScreenSpaceReflectionRoughnessScale=False,WhiteTemp=6700.000000,WhiteTint=0.000000,ColorSaturation=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrast=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGamma=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGain=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffset=(X=0.005000,Y=0.005000,Z=0.005000,W=0.000000),ColorSaturationShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrastShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGammaShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGainShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffsetShadows=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorCorrectionShadowsMax=0.090000,ColorSaturationMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrastMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGammaMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGainMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffsetMidtones=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorSaturationHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrastHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGammaHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGainHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffsetHighlights=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorCorrectionHighlightsMin=0.500000,FilmWhitePoint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),FilmShadowTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),FilmShadowTintBlend=0.500000,FilmShadowTintAmount=0.000000,FilmSaturation=1.000000,FilmChannelMixerRed=(R=1.000000,G=0.000000,B=0.000000,A=1.000000),FilmChannelMixerGreen=(R=0.000000,G=1.000000,B=0.000000,A=1.000000),FilmChannelMixerBlue=(R=0.000000,G=0.000000,B=1.000000,A=1.000000),FilmContrast=0.030000,FilmToeAmount=1.000000,FilmHealAmount=0.180000,FilmDynamicRange=4.000000,FilmSlope=0.880000,FilmToe=0.550000,FilmShoulder=0.260000,FilmBlackClip=0.000000,FilmWhiteClip=0.040000,SceneColorTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),SceneFringeIntensity=0.000000,BloomIntensity=1.000000,BloomThreshold=1.000000,BloomSizeScale=4.000000,Bloom1Size=1.000000,Bloom2Size=4.000000,Bloom3Size=16.000000,Bloom4Size=32.000000,Bloom5Size=64.000000,Bloom6Size=64.000000,Bloom1Tint=(R=0.500000,G=0.500000,B=0.500000,A=1.000000),Bloom2Tint=(R=0.500000,G=0.500000,B=0.500000,A=1.000000),Bloom3Tint=(R=0.500000,G=0.500000,B=0.500000,A=1.000000),Bloom4Tint=(R=0.500000,G=0.500000,B=0.500000,A=1.000000),Bloom5Tint=(R=0.500000,G=0.500000,B=0.500000,A=1.000000),Bloom6Tint=(R=0.500000,G=0.500000,B=0.500000,A=1.000000),BloomDirtMaskIntensity=1.000000,BloomDirtMaskTint=(R=0.500000,G=0.500000,B=0.500000,A=1.000000),BloomDirtMask=None,AmbientCubemapTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),AmbientCubemapIntensity=1.000000,AmbientCubemap=None,AutoExposureMethod=AEM_Histogram,AutoExposureLowPercent=80.000000,AutoExposureHighPercent=98.300003,AutoExposureMinBrightness=1.000000,AutoExposureMaxBrightness=1.000000,AutoExposureSpeedUp=3.000000,AutoExposureSpeedDown=1.000000,AutoExposureBias=0.330000,HistogramLogMin=-8.000000,HistogramLogMax=4.000000,LensFlareIntensity=1.000000,LensFlareTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),LensFlareBokehSize=3.000000,LensFlareThreshold=8.000000,LensFlareBokehShape=None,LensFlareTints[0]=(R=1.000000,G=0.800000,B=0.400000,A=0.600000),LensFlareTints[1]=(R=1.000000,G=1.000000,B=0.600000,A=0.530000),LensFlareTints[2]=(R=0.800000,G=0.800000,B=1.000000,A=0.460000),LensFlareTints[3]=(R=0.500000,G=1.000000,B=0.400000,A=0.390000),LensFlareTints[4]=(R=0.500000,G=0.800000,B=1.000000,A=0.310000),LensFlareTints[5]=(R=0.900000,G=1.000000,B=0.800000,A=0.270000),LensFlareTints[6]=(R=1.000000,G=0.800000,B=0.400000,A=0.220000),LensFlareTints[7]=(R=0.900000,G=0.700000,B=0.700000,A=0.150000),VignetteIntensity=0.161468,GrainJitter=0.000000,GrainIntensity=0.000000,AmbientOcclusionIntensity=1.000000,AmbientOcclusionStaticFraction=1.000000,AmbientOcclusionRadius=73.477997,AmbientOcclusionRadiusInWS=False,AmbientOcclusionFadeDistance=8000.000000,AmbientOcclusionFadeRadius=5000.000000,AmbientOcclusionDistance=80.000000,AmbientOcclusionPower=1.200000,AmbientOcclusionBias=3.000000,AmbientOcclusionQuality=100.000000,AmbientOcclusionMipBlend=0.600000,AmbientOcclusionMipScale=1.700000,AmbientOcclusionMipThreshold=0.010000,IndirectLightingColor=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),IndirectLightingIntensity=1.000000,ColorGradingIntensity=0.000000,ColorGradingLUT=Texture2D'/Engine/EditorResources/RGBTable16x1_AssetViewer.RGBTable16x1_AssetViewer',DepthOfFieldMethod=DOFM_BokehDOF,bMobileHQGaussian=False,DepthOfFieldFstop=4.000000,DepthOfFieldSensorWidth=24.576000,DepthOfFieldFocalDistance=1000.000000,DepthOfFieldDepthBlurAmount=1.000000,DepthOfFieldDepthBlurRadius=0.000000,DepthOfFieldFocalRegion=0.000000,DepthOfFieldNearTransitionRegion=300.000000,DepthOfFieldFarTransitionRegion=500.000000,DepthOfFieldScale=0.000000,DepthOfFieldMaxBokehSize=15.000000,DepthOfFieldNearBlurSize=15.000000,DepthOfFieldFarBlurSize=15.000000,DepthOfFieldBokehShape=None,DepthOfFieldOcclusion=0.400000,DepthOfFieldColorThreshold=1.000000,DepthOfFieldSizeThreshold=0.080000,DepthOfFieldSkyFocusDistance=0.000000,DepthOfFieldVignetteSize=200.000000,MotionBlurAmount=0.500000,MotionBlurMax=5.000000,MotionBlurPerObjectSize=0.500000,LPVIntensity=1.000000,LPVVplInjectionBias=0.640000,LPVSize=5312.000000,LPVSecondaryOcclusionIntensity=0.000000,LPVSecondaryBounceIntensity=0.000000,LPVGeometryVolumeBias=0.384000,LPVEmissiveInjectionIntensity=1.000000,LPVDirectionalOcclusionIntensity=0.000000,LPVDirectionalOcclusionRadius=8.000000,LPVDiffuseOcclusionExponent=1.000000,LPVSpecularOcclusionExponent=7.000000,LPVDiffuseOcclusionIntensity=1.000000,LPVSpecularOcclusionIntensity=1.000000,ScreenSpaceReflectionIntensity=100.000000,ScreenSpaceReflectionQuality=100.000000,ScreenSpaceReflectionMaxRoughness=1.000000,ScreenPercentage=100.000000,WeightedBlendables=(Array=),Blendables=),bPostProcessingEnabled=True,LightingRigRotation=109.389069,RotationSpeed=2.000000,DirectionalLightRotation=(Pitch=-39.999985,Yaw=-67.500015,Roll=0.000000))
++Profiles=(ProfileName="Default",DirectionalLightIntensity=2.620000,DirectionalLightColor=(R=0.990000,G=0.839850,B=0.732600,A=1.000000),SkyLightIntensity=0.880000,bRotateLightingRig=False,bShowEnvironment=True,bShowFloor=True,EnvironmentCubeMapPath="/Engine/EditorMaterials/AssetViewer/EpicQuadPanorama_CC+EV1.EpicQuadPanorama_CC+EV1",PostProcessingSettings=(bOverride_WhiteTemp=True,bOverride_WhiteTint=False,bOverride_ColorSaturation=True,bOverride_ColorContrast=True,bOverride_ColorGamma=True,bOverride_ColorGain=True,bOverride_ColorOffset=True,bOverride_ColorSaturationShadows=False,bOverride_ColorContrastShadows=False,bOverride_ColorGammaShadows=False,bOverride_ColorGainShadows=False,bOverride_ColorOffsetShadows=False,bOverride_ColorSaturationMidtones=False,bOverride_ColorContrastMidtones=False,bOverride_ColorGammaMidtones=False,bOverride_ColorGainMidtones=False,bOverride_ColorOffsetMidtones=False,bOverride_ColorSaturationHighlights=False,bOverride_ColorContrastHighlights=False,bOverride_ColorGammaHighlights=False,bOverride_ColorGainHighlights=False,bOverride_ColorOffsetHighlights=False,bOverride_ColorCorrectionShadowsMax=False,bOverride_ColorCorrectionHighlightsMin=False,bOverride_FilmWhitePoint=False,bOverride_FilmSaturation=False,bOverride_FilmChannelMixerRed=False,bOverride_FilmChannelMixerGreen=False,bOverride_FilmChannelMixerBlue=False,bOverride_FilmContrast=False,bOverride_FilmDynamicRange=False,bOverride_FilmHealAmount=False,bOverride_FilmToeAmount=False,bOverride_FilmShadowTint=False,bOverride_FilmShadowTintBlend=False,bOverride_FilmShadowTintAmount=False,bOverride_FilmSlope=True,bOverride_FilmToe=True,bOverride_FilmShoulder=True,bOverride_FilmBlackClip=True,bOverride_FilmWhiteClip=True,bOverride_SceneColorTint=False,bOverride_SceneFringeIntensity=False,bOverride_AmbientCubemapTint=False,bOverride_AmbientCubemapIntensity=False,bOverride_BloomIntensity=True,bOverride_BloomThreshold=False,bOverride_Bloom1Tint=False,bOverride_Bloom1Size=False,bOverride_Bloom2Size=False,bOverride_Bloom2Tint=False,bOverride_Bloom3Tint=False,bOverride_Bloom3Size=False,bOverride_Bloom4Tint=False,bOverride_Bloom4Size=False,bOverride_Bloom5Tint=False,bOverride_Bloom5Size=False,bOverride_Bloom6Tint=False,bOverride_Bloom6Size=False,bOverride_BloomSizeScale=False,bOverride_BloomDirtMaskIntensity=False,bOverride_BloomDirtMaskTint=False,bOverride_BloomDirtMask=False,bOverride_AutoExposureMethod=True,bOverride_AutoExposureLowPercent=False,bOverride_AutoExposureHighPercent=False,bOverride_AutoExposureMinBrightness=True,bOverride_AutoExposureMaxBrightness=True,bOverride_AutoExposureSpeedUp=False,bOverride_AutoExposureSpeedDown=False,bOverride_AutoExposureBias=True,bOverride_HistogramLogMin=True,bOverride_HistogramLogMax=True,bOverride_LensFlareIntensity=False,bOverride_LensFlareTint=False,bOverride_LensFlareTints=False,bOverride_LensFlareBokehSize=False,bOverride_LensFlareBokehShape=False,bOverride_LensFlareThreshold=False,bOverride_VignetteIntensity=True,bOverride_GrainIntensity=False,bOverride_GrainJitter=False,bOverride_AmbientOcclusionIntensity=True,bOverride_AmbientOcclusionStaticFraction=True,bOverride_AmbientOcclusionRadius=True,bOverride_AmbientOcclusionFadeDistance=False,bOverride_AmbientOcclusionFadeRadius=False,bOverride_AmbientOcclusionDistance=False,bOverride_AmbientOcclusionRadiusInWS=False,bOverride_AmbientOcclusionPower=True,bOverride_AmbientOcclusionBias=True,bOverride_AmbientOcclusionQuality=True,bOverride_AmbientOcclusionMipBlend=True,bOverride_AmbientOcclusionMipScale=True,bOverride_AmbientOcclusionMipThreshold=True,bOverride_LPVIntensity=False,bOverride_LPVDirectionalOcclusionIntensity=False,bOverride_LPVDirectionalOcclusionRadius=False,bOverride_LPVDiffuseOcclusionExponent=False,bOverride_LPVSpecularOcclusionExponent=False,bOverride_LPVDiffuseOcclusionIntensity=False,bOverride_LPVSpecularOcclusionIntensity=False,bOverride_LPVSize=False,bOverride_LPVSecondaryOcclusionIntensity=False,bOverride_LPVSecondaryBounceIntensity=False,bOverride_LPVGeometryVolumeBias=False,bOverride_LPVVplInjectionBias=False,bOverride_LPVEmissiveInjectionIntensity=False,bOverride_IndirectLightingColor=False,bOverride_IndirectLightingIntensity=False,bOverride_ColorGradingIntensity=True,bOverride_ColorGradingLUT=True,bOverride_DepthOfFieldFocalDistance=False,bOverride_DepthOfFieldFstop=False,bOverride_DepthOfFieldSensorWidth=False,bOverride_DepthOfFieldDepthBlurRadius=False,bOverride_DepthOfFieldDepthBlurAmount=False,bOverride_DepthOfFieldFocalRegion=False,bOverride_DepthOfFieldNearTransitionRegion=False,bOverride_DepthOfFieldFarTransitionRegion=False,bOverride_DepthOfFieldScale=True,bOverride_DepthOfFieldMaxBokehSize=False,bOverride_DepthOfFieldNearBlurSize=False,bOverride_DepthOfFieldFarBlurSize=False,bOverride_DepthOfFieldMethod=True,bOverride_MobileHQGaussian=False,bOverride_DepthOfFieldBokehShape=False,bOverride_DepthOfFieldOcclusion=False,bOverride_DepthOfFieldColorThreshold=False,bOverride_DepthOfFieldSizeThreshold=False,bOverride_DepthOfFieldSkyFocusDistance=False,bOverride_DepthOfFieldVignetteSize=False,bOverride_MotionBlurAmount=False,bOverride_MotionBlurMax=False,bOverride_MotionBlurPerObjectSize=False,bOverride_ScreenPercentage=False,bOverride_ScreenSpaceReflectionIntensity=True,bOverride_ScreenSpaceReflectionQuality=True,bOverride_ScreenSpaceReflectionMaxRoughness=True,bOverride_ScreenSpaceReflectionRoughnessScale=False,WhiteTemp=6700.000000,WhiteTint=0.000000,ColorSaturation=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrast=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGamma=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGain=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffset=(X=0.005000,Y=0.005000,Z=0.005000,W=0.000000),ColorSaturationShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrastShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGammaShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGainShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffsetShadows=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorCorrectionShadowsMax=0.090000,ColorSaturationMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrastMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGammaMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGainMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffsetMidtones=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorSaturationHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrastHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGammaHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGainHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffsetHighlights=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorCorrectionHighlightsMin=0.500000,FilmWhitePoint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),FilmShadowTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),FilmShadowTintBlend=0.500000,FilmShadowTintAmount=0.000000,FilmSaturation=1.000000,FilmChannelMixerRed=(R=1.000000,G=0.000000,B=0.000000,A=1.000000),FilmChannelMixerGreen=(R=0.000000,G=1.000000,B=0.000000,A=1.000000),FilmChannelMixerBlue=(R=0.000000,G=0.000000,B=1.000000,A=1.000000),FilmContrast=0.030000,FilmToeAmount=1.000000,FilmHealAmount=1.000000,FilmDynamicRange=4.000000,FilmSlope=0.880000,FilmToe=0.550000,FilmShoulder=0.260000,FilmBlackClip=0.000000,FilmWhiteClip=0.040000,SceneColorTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),SceneFringeIntensity=0.000000,BloomIntensity=0.675000,BloomThreshold=-1.000000,BloomSizeScale=4.000000,Bloom1Size=0.300000,Bloom2Size=1.000000,Bloom3Size=2.000000,Bloom4Size=10.000000,Bloom5Size=30.000000,Bloom6Size=64.000000,Bloom1Tint=(R=0.346500,G=0.346500,B=0.346500,A=1.000000),Bloom2Tint=(R=0.138000,G=0.138000,B=0.138000,A=1.000000),Bloom3Tint=(R=0.117600,G=0.117600,B=0.117600,A=1.000000),Bloom4Tint=(R=0.066000,G=0.066000,B=0.066000,A=1.000000),Bloom5Tint=(R=0.066000,G=0.066000,B=0.066000,A=1.000000),Bloom6Tint=(R=0.061000,G=0.061000,B=0.061000,A=1.000000),BloomDirtMaskIntensity=0.000000,BloomDirtMaskTint=(R=0.500000,G=0.500000,B=0.500000,A=1.000000),BloomDirtMask=None,AmbientCubemapTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),AmbientCubemapIntensity=1.000000,AmbientCubemap=None,AutoExposureMethod=AEM_Histogram,AutoExposureLowPercent=80.000000,AutoExposureHighPercent=98.300003,AutoExposureMinBrightness=1.000000,AutoExposureMaxBrightness=1.000000,AutoExposureSpeedUp=3.000000,AutoExposureSpeedDown=1.000000,AutoExposureBias=0.330000,HistogramLogMin=-8.000000,HistogramLogMax=4.000000,LensFlareIntensity=1.000000,LensFlareTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),LensFlareBokehSize=3.000000,LensFlareThreshold=8.000000,LensFlareBokehShape=None,LensFlareTints[0]=(R=1.000000,G=0.800000,B=0.400000,A=0.600000),LensFlareTints[1]=(R=1.000000,G=1.000000,B=0.600000,A=0.530000),LensFlareTints[2]=(R=0.800000,G=0.800000,B=1.000000,A=0.460000),LensFlareTints[3]=(R=0.500000,G=1.000000,B=0.400000,A=0.390000),LensFlareTints[4]=(R=0.500000,G=0.800000,B=1.000000,A=0.310000),LensFlareTints[5]=(R=0.900000,G=1.000000,B=0.800000,A=0.270000),LensFlareTints[6]=(R=1.000000,G=0.800000,B=0.400000,A=0.220000),LensFlareTints[7]=(R=0.900000,G=0.700000,B=0.700000,A=0.150000),VignetteIntensity=0.161468,GrainJitter=0.000000,GrainIntensity=0.000000,AmbientOcclusionIntensity=1.000000,AmbientOcclusionStaticFraction=1.000000,AmbientOcclusionRadius=73.477997,AmbientOcclusionRadiusInWS=False,AmbientOcclusionFadeDistance=8000.000000,AmbientOcclusionFadeRadius=5000.000000,AmbientOcclusionDistance=80.000000,AmbientOcclusionPower=1.200000,AmbientOcclusionBias=3.000000,AmbientOcclusionQuality=100.000000,AmbientOcclusionMipBlend=0.600000,AmbientOcclusionMipScale=1.700000,AmbientOcclusionMipThreshold=0.010000,IndirectLightingColor=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),IndirectLightingIntensity=1.000000,ColorGradingIntensity=0.000000,ColorGradingLUT=Texture2D'/Engine/EditorResources/RGBTable16x1_AssetViewer.RGBTable16x1_AssetViewer',DepthOfFieldMethod=DOFM_BokehDOF,bMobileHQGaussian=False,DepthOfFieldFstop=4.000000,DepthOfFieldSensorWidth=24.576000,DepthOfFieldFocalDistance=1000.000000,DepthOfFieldDepthBlurAmount=1.000000,DepthOfFieldDepthBlurRadius=0.000000,DepthOfFieldFocalRegion=0.000000,DepthOfFieldNearTransitionRegion=300.000000,DepthOfFieldFarTransitionRegion=500.000000,DepthOfFieldScale=0.000000,DepthOfFieldMaxBokehSize=15.000000,DepthOfFieldNearBlurSize=15.000000,DepthOfFieldFarBlurSize=15.000000,DepthOfFieldBokehShape=None,DepthOfFieldOcclusion=0.400000,DepthOfFieldColorThreshold=1.000000,DepthOfFieldSizeThreshold=0.080000,DepthOfFieldSkyFocusDistance=0.000000,DepthOfFieldVignetteSize=200.000000,MotionBlurAmount=0.500000,MotionBlurMax=5.000000,MotionBlurPerObjectSize=0.500000,LPVIntensity=1.000000,LPVVplInjectionBias=0.640000,LPVSize=5312.000000,LPVSecondaryOcclusionIntensity=0.000000,LPVSecondaryBounceIntensity=0.000000,LPVGeometryVolumeBias=0.384000,LPVEmissiveInjectionIntensity=1.000000,LPVDirectionalOcclusionIntensity=0.000000,LPVDirectionalOcclusionRadius=8.000000,LPVDiffuseOcclusionExponent=1.000000,LPVSpecularOcclusionExponent=7.000000,LPVDiffuseOcclusionIntensity=1.000000,LPVSpecularOcclusionIntensity=1.000000,ScreenSpaceReflectionIntensity=100.000000,ScreenSpaceReflectionQuality=100.000000,ScreenSpaceReflectionMaxRoughness=1.000000,ScreenPercentage=100.000000,WeightedBlendables=(Array=),Blendables=),bPostProcessingEnabled=True,LightingRigRotation=109.389069,RotationSpeed=2.000000,DirectionalLightRotation=(Pitch=-39.999985,Yaw=-67.500015,Roll=0.000000))
++Profiles=(ProfileName="Default",DirectionalLightIntensity=2.620000,DirectionalLightColor=(R=0.990000,G=0.839850,B=0.732600,A=1.000000),SkyLightIntensity=0.880000,bRotateLightingRig=False,bShowEnvironment=True,bShowFloor=True,EnvironmentCubeMapPath="/Engine/EditorMaterials/AssetViewer/EpicQuadPanorama_CC+EV1.EpicQuadPanorama_CC+EV1",PostProcessingSettings=(bOverride_WhiteTemp=True,bOverride_WhiteTint=False,bOverride_ColorSaturation=True,bOverride_ColorContrast=True,bOverride_ColorGamma=True,bOverride_ColorGain=True,bOverride_ColorOffset=True,bOverride_ColorSaturationShadows=False,bOverride_ColorContrastShadows=False,bOverride_ColorGammaShadows=False,bOverride_ColorGainShadows=False,bOverride_ColorOffsetShadows=False,bOverride_ColorSaturationMidtones=False,bOverride_ColorContrastMidtones=False,bOverride_ColorGammaMidtones=False,bOverride_ColorGainMidtones=False,bOverride_ColorOffsetMidtones=False,bOverride_ColorSaturationHighlights=False,bOverride_ColorContrastHighlights=False,bOverride_ColorGammaHighlights=False,bOverride_ColorGainHighlights=False,bOverride_ColorOffsetHighlights=False,bOverride_ColorCorrectionShadowsMax=False,bOverride_ColorCorrectionHighlightsMin=False,bOverride_FilmWhitePoint=False,bOverride_FilmSaturation=False,bOverride_FilmChannelMixerRed=False,bOverride_FilmChannelMixerGreen=False,bOverride_FilmChannelMixerBlue=False,bOverride_FilmContrast=False,bOverride_FilmDynamicRange=False,bOverride_FilmHealAmount=False,bOverride_FilmToeAmount=False,bOverride_FilmShadowTint=False,bOverride_FilmShadowTintBlend=False,bOverride_FilmShadowTintAmount=False,bOverride_FilmSlope=True,bOverride_FilmToe=True,bOverride_FilmShoulder=True,bOverride_FilmBlackClip=True,bOverride_FilmWhiteClip=True,bOverride_SceneColorTint=False,bOverride_SceneFringeIntensity=False,bOverride_AmbientCubemapTint=False,bOverride_AmbientCubemapIntensity=False,bOverride_BloomIntensity=True,bOverride_BloomThreshold=False,bOverride_Bloom1Tint=False,bOverride_Bloom1Size=False,bOverride_Bloom2Size=False,bOverride_Bloom2Tint=False,bOverride_Bloom3Tint=False,bOverride_Bloom3Size=False,bOverride_Bloom4Tint=False,bOverride_Bloom4Size=False,bOverride_Bloom5Tint=False,bOverride_Bloom5Size=False,bOverride_Bloom6Tint=False,bOverride_Bloom6Size=False,bOverride_BloomSizeScale=False,bOverride_BloomDirtMaskIntensity=False,bOverride_BloomDirtMaskTint=False,bOverride_BloomDirtMask=False,bOverride_AutoExposureMethod=True,bOverride_AutoExposureLowPercent=False,bOverride_AutoExposureHighPercent=False,bOverride_AutoExposureMinBrightness=True,bOverride_AutoExposureMaxBrightness=True,bOverride_AutoExposureSpeedUp=False,bOverride_AutoExposureSpeedDown=False,bOverride_AutoExposureBias=True,bOverride_HistogramLogMin=True,bOverride_HistogramLogMax=True,bOverride_LensFlareIntensity=False,bOverride_LensFlareTint=False,bOverride_LensFlareTints=False,bOverride_LensFlareBokehSize=False,bOverride_LensFlareBokehShape=False,bOverride_LensFlareThreshold=False,bOverride_VignetteIntensity=True,bOverride_GrainIntensity=False,bOverride_GrainJitter=False,bOverride_AmbientOcclusionIntensity=True,bOverride_AmbientOcclusionStaticFraction=True,bOverride_AmbientOcclusionRadius=True,bOverride_AmbientOcclusionFadeDistance=False,bOverride_AmbientOcclusionFadeRadius=False,bOverride_AmbientOcclusionDistance=False,bOverride_AmbientOcclusionRadiusInWS=False,bOverride_AmbientOcclusionPower=True,bOverride_AmbientOcclusionBias=True,bOverride_AmbientOcclusionQuality=True,bOverride_AmbientOcclusionMipBlend=True,bOverride_AmbientOcclusionMipScale=True,bOverride_AmbientOcclusionMipThreshold=True,bOverride_LPVIntensity=False,bOverride_LPVDirectionalOcclusionIntensity=False,bOverride_LPVDirectionalOcclusionRadius=False,bOverride_LPVDiffuseOcclusionExponent=False,bOverride_LPVSpecularOcclusionExponent=False,bOverride_LPVDiffuseOcclusionIntensity=False,bOverride_LPVSpecularOcclusionIntensity=False,bOverride_LPVSize=False,bOverride_LPVSecondaryOcclusionIntensity=False,bOverride_LPVSecondaryBounceIntensity=False,bOverride_LPVGeometryVolumeBias=False,bOverride_LPVVplInjectionBias=False,bOverride_LPVEmissiveInjectionIntensity=False,bOverride_IndirectLightingColor=False,bOverride_IndirectLightingIntensity=False,bOverride_ColorGradingIntensity=True,bOverride_ColorGradingLUT=True,bOverride_DepthOfFieldFocalDistance=False,bOverride_DepthOfFieldFstop=False,bOverride_DepthOfFieldSensorWidth=False,bOverride_DepthOfFieldDepthBlurRadius=False,bOverride_DepthOfFieldDepthBlurAmount=False,bOverride_DepthOfFieldFocalRegion=False,bOverride_DepthOfFieldNearTransitionRegion=False,bOverride_DepthOfFieldFarTransitionRegion=False,bOverride_DepthOfFieldScale=True,bOverride_DepthOfFieldMaxBokehSize=False,bOverride_DepthOfFieldNearBlurSize=False,bOverride_DepthOfFieldFarBlurSize=False,bOverride_DepthOfFieldMethod=True,bOverride_MobileHQGaussian=False,bOverride_DepthOfFieldBokehShape=False,bOverride_DepthOfFieldOcclusion=False,bOverride_DepthOfFieldColorThreshold=False,bOverride_DepthOfFieldSizeThreshold=False,bOverride_DepthOfFieldSkyFocusDistance=False,bOverride_DepthOfFieldVignetteSize=False,bOverride_MotionBlurAmount=False,bOverride_MotionBlurMax=False,bOverride_MotionBlurPerObjectSize=False,bOverride_ScreenPercentage=False,bOverride_ScreenSpaceReflectionIntensity=True,bOverride_ScreenSpaceReflectionQuality=True,bOverride_ScreenSpaceReflectionMaxRoughness=True,bOverride_ScreenSpaceReflectionRoughnessScale=False,WhiteTemp=6700.000000,WhiteTint=0.000000,ColorSaturation=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrast=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGamma=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGain=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffset=(X=0.005000,Y=0.005000,Z=0.005000,W=0.000000),ColorSaturationShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrastShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGammaShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGainShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffsetShadows=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorCorrectionShadowsMax=0.090000,ColorSaturationMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrastMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGammaMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGainMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffsetMidtones=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorSaturationHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrastHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGammaHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGainHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffsetHighlights=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorCorrectionHighlightsMin=0.500000,FilmWhitePoint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),FilmShadowTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),FilmShadowTintBlend=0.500000,FilmShadowTintAmount=0.000000,FilmSaturation=1.000000,FilmChannelMixerRed=(R=1.000000,G=0.000000,B=0.000000,A=1.000000),FilmChannelMixerGreen=(R=0.000000,G=1.000000,B=0.000000,A=1.000000),FilmChannelMixerBlue=(R=0.000000,G=0.000000,B=1.000000,A=1.000000),FilmContrast=0.030000,FilmToeAmount=1.000000,FilmHealAmount=1.000000,FilmDynamicRange=4.000000,FilmSlope=0.880000,FilmToe=0.550000,FilmShoulder=0.260000,FilmBlackClip=0.000000,FilmWhiteClip=0.040000,SceneColorTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),SceneFringeIntensity=0.000000,BloomIntensity=0.675000,BloomThreshold=-1.000000,BloomSizeScale=4.000000,Bloom1Size=0.300000,Bloom2Size=1.000000,Bloom3Size=2.000000,Bloom4Size=10.000000,Bloom5Size=30.000000,Bloom6Size=64.000000,Bloom1Tint=(R=0.346500,G=0.346500,B=0.346500,A=1.000000),Bloom2Tint=(R=0.138000,G=0.138000,B=0.138000,A=1.000000),Bloom3Tint=(R=0.117600,G=0.117600,B=0.117600,A=1.000000),Bloom4Tint=(R=0.066000,G=0.066000,B=0.066000,A=1.000000),Bloom5Tint=(R=0.066000,G=0.066000,B=0.066000,A=1.000000),Bloom6Tint=(R=0.061000,G=0.061000,B=0.061000,A=1.000000),BloomDirtMaskIntensity=0.000000,BloomDirtMaskTint=(R=0.500000,G=0.500000,B=0.500000,A=1.000000),BloomDirtMask=None,AmbientCubemapTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),AmbientCubemapIntensity=1.000000,AmbientCubemap=None,AutoExposureMethod=AEM_Histogram,AutoExposureLowPercent=80.000000,AutoExposureHighPercent=98.300003,AutoExposureMinBrightness=1.000000,AutoExposureMaxBrightness=1.000000,AutoExposureSpeedUp=3.000000,AutoExposureSpeedDown=1.000000,AutoExposureBias=0.330000,HistogramLogMin=-8.000000,HistogramLogMax=4.000000,LensFlareIntensity=1.000000,LensFlareTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),LensFlareBokehSize=3.000000,LensFlareThreshold=8.000000,LensFlareBokehShape=None,LensFlareTints[0]=(R=1.000000,G=0.800000,B=0.400000,A=0.600000),LensFlareTints[1]=(R=1.000000,G=1.000000,B=0.600000,A=0.530000),LensFlareTints[2]=(R=0.800000,G=0.800000,B=1.000000,A=0.460000),LensFlareTints[3]=(R=0.500000,G=1.000000,B=0.400000,A=0.390000),LensFlareTints[4]=(R=0.500000,G=0.800000,B=1.000000,A=0.310000),LensFlareTints[5]=(R=0.900000,G=1.000000,B=0.800000,A=0.270000),LensFlareTints[6]=(R=1.000000,G=0.800000,B=0.400000,A=0.220000),LensFlareTints[7]=(R=0.900000,G=0.700000,B=0.700000,A=0.150000),VignetteIntensity=0.161468,GrainJitter=0.000000,GrainIntensity=0.000000,AmbientOcclusionIntensity=1.000000,AmbientOcclusionStaticFraction=1.000000,AmbientOcclusionRadius=73.477997,AmbientOcclusionRadiusInWS=False,AmbientOcclusionFadeDistance=8000.000000,AmbientOcclusionFadeRadius=5000.000000,AmbientOcclusionDistance=80.000000,AmbientOcclusionPower=1.200000,AmbientOcclusionBias=3.000000,AmbientOcclusionQuality=100.000000,AmbientOcclusionMipBlend=0.600000,AmbientOcclusionMipScale=1.700000,AmbientOcclusionMipThreshold=0.010000,IndirectLightingColor=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),IndirectLightingIntensity=1.000000,ColorGradingIntensity=0.000000,ColorGradingLUT=Texture2D'/Engine/EditorResources/RGBTable16x1_AssetViewer.RGBTable16x1_AssetViewer',DepthOfFieldMethod=DOFM_BokehDOF,bMobileHQGaussian=False,DepthOfFieldFstop=4.000000,DepthOfFieldSensorWidth=24.576000,DepthOfFieldFocalDistance=1000.000000,DepthOfFieldDepthBlurAmount=1.000000,DepthOfFieldDepthBlurRadius=0.000000,DepthOfFieldFocalRegion=0.000000,DepthOfFieldNearTransitionRegion=300.000000,DepthOfFieldFarTransitionRegion=500.000000,DepthOfFieldScale=0.000000,DepthOfFieldMaxBokehSize=15.000000,DepthOfFieldNearBlurSize=15.000000,DepthOfFieldFarBlurSize=15.000000,DepthOfFieldBokehShape=None,DepthOfFieldOcclusion=0.400000,DepthOfFieldColorThreshold=1.000000,DepthOfFieldSizeThreshold=0.080000,DepthOfFieldSkyFocusDistance=0.000000,DepthOfFieldVignetteSize=200.000000,MotionBlurAmount=0.500000,MotionBlurMax=5.000000,MotionBlurPerObjectSize=0.500000,LPVIntensity=1.000000,LPVVplInjectionBias=0.640000,LPVSize=5312.000000,LPVSecondaryOcclusionIntensity=0.000000,LPVSecondaryBounceIntensity=0.000000,LPVGeometryVolumeBias=0.384000,LPVEmissiveInjectionIntensity=1.000000,LPVDirectionalOcclusionIntensity=0.000000,LPVDirectionalOcclusionRadius=8.000000,LPVDiffuseOcclusionExponent=1.000000,LPVSpecularOcclusionExponent=7.000000,LPVDiffuseOcclusionIntensity=1.000000,LPVSpecularOcclusionIntensity=1.000000,ScreenSpaceReflectionIntensity=100.000000,ScreenSpaceReflectionQuality=100.000000,ScreenSpaceReflectionMaxRoughness=1.000000,ScreenPercentage=100.000000,WeightedBlendables=(Array=),Blendables=),bPostProcessingEnabled=True,LightingRigRotation=109.389069,RotationSpeed=2.000000,DirectionalLightRotation=(Pitch=-39.999985,Yaw=-67.500015,Roll=0.000000))
++Profiles=(ProfileName="Default",DirectionalLightIntensity=2.620000,DirectionalLightColor=(R=0.990000,G=0.839850,B=0.732600,A=1.000000),SkyLightIntensity=0.880000,bRotateLightingRig=False,bShowEnvironment=True,bShowFloor=True,EnvironmentCubeMapPath="",PostProcessingSettings=(bOverride_WhiteTemp=True,bOverride_WhiteTint=False,bOverride_ColorSaturation=True,bOverride_ColorContrast=True,bOverride_ColorGamma=True,bOverride_ColorGain=True,bOverride_ColorOffset=True,bOverride_ColorSaturationShadows=False,bOverride_ColorContrastShadows=False,bOverride_ColorGammaShadows=False,bOverride_ColorGainShadows=False,bOverride_ColorOffsetShadows=False,bOverride_ColorSaturationMidtones=False,bOverride_ColorContrastMidtones=False,bOverride_ColorGammaMidtones=False,bOverride_ColorGainMidtones=False,bOverride_ColorOffsetMidtones=False,bOverride_ColorSaturationHighlights=False,bOverride_ColorContrastHighlights=False,bOverride_ColorGammaHighlights=False,bOverride_ColorGainHighlights=False,bOverride_ColorOffsetHighlights=False,bOverride_ColorCorrectionShadowsMax=False,bOverride_ColorCorrectionHighlightsMin=False,bOverride_FilmWhitePoint=False,bOverride_FilmSaturation=False,bOverride_FilmChannelMixerRed=False,bOverride_FilmChannelMixerGreen=False,bOverride_FilmChannelMixerBlue=False,bOverride_FilmContrast=False,bOverride_FilmDynamicRange=False,bOverride_FilmHealAmount=False,bOverride_FilmToeAmount=False,bOverride_FilmShadowTint=False,bOverride_FilmShadowTintBlend=False,bOverride_FilmShadowTintAmount=False,bOverride_FilmSlope=True,bOverride_FilmToe=True,bOverride_FilmShoulder=True,bOverride_FilmBlackClip=True,bOverride_FilmWhiteClip=True,bOverride_SceneColorTint=False,bOverride_SceneFringeIntensity=False,bOverride_AmbientCubemapTint=False,bOverride_AmbientCubemapIntensity=False,bOverride_BloomIntensity=True,bOverride_BloomThreshold=False,bOverride_Bloom1Tint=False,bOverride_Bloom1Size=False,bOverride_Bloom2Size=False,bOverride_Bloom2Tint=False,bOverride_Bloom3Tint=False,bOverride_Bloom3Size=False,bOverride_Bloom4Tint=False,bOverride_Bloom4Size=False,bOverride_Bloom5Tint=False,bOverride_Bloom5Size=False,bOverride_Bloom6Tint=False,bOverride_Bloom6Size=False,bOverride_BloomSizeScale=False,bOverride_BloomDirtMaskIntensity=False,bOverride_BloomDirtMaskTint=False,bOverride_BloomDirtMask=False,bOverride_AutoExposureMethod=True,bOverride_AutoExposureLowPercent=False,bOverride_AutoExposureHighPercent=False,bOverride_AutoExposureMinBrightness=True,bOverride_AutoExposureMaxBrightness=True,bOverride_AutoExposureSpeedUp=False,bOverride_AutoExposureSpeedDown=False,bOverride_AutoExposureBias=True,bOverride_HistogramLogMin=True,bOverride_HistogramLogMax=True,bOverride_LensFlareIntensity=False,bOverride_LensFlareTint=False,bOverride_LensFlareTints=False,bOverride_LensFlareBokehSize=False,bOverride_LensFlareBokehShape=False,bOverride_LensFlareThreshold=False,bOverride_VignetteIntensity=True,bOverride_GrainIntensity=False,bOverride_GrainJitter=False,bOverride_AmbientOcclusionIntensity=True,bOverride_AmbientOcclusionStaticFraction=True,bOverride_AmbientOcclusionRadius=True,bOverride_AmbientOcclusionFadeDistance=False,bOverride_AmbientOcclusionFadeRadius=False,bOverride_AmbientOcclusionDistance=False,bOverride_AmbientOcclusionRadiusInWS=False,bOverride_AmbientOcclusionPower=True,bOverride_AmbientOcclusionBias=True,bOverride_AmbientOcclusionQuality=True,bOverride_AmbientOcclusionMipBlend=True,bOverride_AmbientOcclusionMipScale=True,bOverride_AmbientOcclusionMipThreshold=True,bOverride_LPVIntensity=False,bOverride_LPVDirectionalOcclusionIntensity=False,bOverride_LPVDirectionalOcclusionRadius=False,bOverride_LPVDiffuseOcclusionExponent=False,bOverride_LPVSpecularOcclusionExponent=False,bOverride_LPVDiffuseOcclusionIntensity=False,bOverride_LPVSpecularOcclusionIntensity=False,bOverride_LPVSize=False,bOverride_LPVSecondaryOcclusionIntensity=False,bOverride_LPVSecondaryBounceIntensity=False,bOverride_LPVGeometryVolumeBias=False,bOverride_LPVVplInjectionBias=False,bOverride_LPVEmissiveInjectionIntensity=False,bOverride_IndirectLightingColor=False,bOverride_IndirectLightingIntensity=False,bOverride_ColorGradingIntensity=True,bOverride_ColorGradingLUT=True,bOverride_DepthOfFieldFocalDistance=False,bOverride_DepthOfFieldFstop=False,bOverride_DepthOfFieldSensorWidth=False,bOverride_DepthOfFieldDepthBlurRadius=False,bOverride_DepthOfFieldDepthBlurAmount=False,bOverride_DepthOfFieldFocalRegion=False,bOverride_DepthOfFieldNearTransitionRegion=False,bOverride_DepthOfFieldFarTransitionRegion=False,bOverride_DepthOfFieldScale=True,bOverride_DepthOfFieldMaxBokehSize=False,bOverride_DepthOfFieldNearBlurSize=False,bOverride_DepthOfFieldFarBlurSize=False,bOverride_DepthOfFieldMethod=True,bOverride_MobileHQGaussian=False,bOverride_DepthOfFieldBokehShape=False,bOverride_DepthOfFieldOcclusion=False,bOverride_DepthOfFieldColorThreshold=False,bOverride_DepthOfFieldSizeThreshold=False,bOverride_DepthOfFieldSkyFocusDistance=False,bOverride_DepthOfFieldVignetteSize=False,bOverride_MotionBlurAmount=False,bOverride_MotionBlurMax=False,bOverride_MotionBlurPerObjectSize=False,bOverride_ScreenPercentage=False,bOverride_ScreenSpaceReflectionIntensity=True,bOverride_ScreenSpaceReflectionQuality=True,bOverride_ScreenSpaceReflectionMaxRoughness=True,bOverride_ScreenSpaceReflectionRoughnessScale=False,WhiteTemp=6700.000000,WhiteTint=0.000000,ColorSaturation=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrast=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGamma=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGain=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffset=(X=0.005000,Y=0.005000,Z=0.005000,W=0.000000),ColorSaturationShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrastShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGammaShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGainShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffsetShadows=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorCorrectionShadowsMax=0.090000,ColorSaturationMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrastMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGammaMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGainMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffsetMidtones=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorSaturationHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrastHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGammaHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGainHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffsetHighlights=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorCorrectionHighlightsMin=0.500000,FilmWhitePoint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),FilmShadowTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),FilmShadowTintBlend=0.500000,FilmShadowTintAmount=0.000000,FilmSaturation=1.000000,FilmChannelMixerRed=(R=1.000000,G=0.000000,B=0.000000,A=1.000000),FilmChannelMixerGreen=(R=0.000000,G=1.000000,B=0.000000,A=1.000000),FilmChannelMixerBlue=(R=0.000000,G=0.000000,B=1.000000,A=1.000000),FilmContrast=0.030000,FilmToeAmount=1.000000,FilmHealAmount=0.180000,FilmDynamicRange=4.000000,FilmSlope=0.880000,FilmToe=0.550000,FilmShoulder=0.260000,FilmBlackClip=0.000000,FilmWhiteClip=0.040000,SceneColorTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),SceneFringeIntensity=0.000000,BloomIntensity=1.000000,BloomThreshold=1.000000,BloomSizeScale=4.000000,Bloom1Size=1.000000,Bloom2Size=4.000000,Bloom3Size=16.000000,Bloom4Size=32.000000,Bloom5Size=64.000000,Bloom6Size=64.000000,Bloom1Tint=(R=0.500000,G=0.500000,B=0.500000,A=1.000000),Bloom2Tint=(R=0.500000,G=0.500000,B=0.500000,A=1.000000),Bloom3Tint=(R=0.500000,G=0.500000,B=0.500000,A=1.000000),Bloom4Tint=(R=0.500000,G=0.500000,B=0.500000,A=1.000000),Bloom5Tint=(R=0.500000,G=0.500000,B=0.500000,A=1.000000),Bloom6Tint=(R=0.500000,G=0.500000,B=0.500000,A=1.000000),BloomDirtMaskIntensity=1.000000,BloomDirtMaskTint=(R=0.500000,G=0.500000,B=0.500000,A=1.000000),BloomDirtMask=None,AmbientCubemapTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),AmbientCubemapIntensity=1.000000,AmbientCubemap=None,AutoExposureMethod=AEM_Histogram,AutoExposureLowPercent=80.000000,AutoExposureHighPercent=98.300003,AutoExposureMinBrightness=1.000000,AutoExposureMaxBrightness=1.000000,AutoExposureSpeedUp=3.000000,AutoExposureSpeedDown=1.000000,AutoExposureBias=0.330000,HistogramLogMin=-8.000000,HistogramLogMax=4.000000,LensFlareIntensity=1.000000,LensFlareTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),LensFlareBokehSize=3.000000,LensFlareThreshold=8.000000,LensFlareBokehShape=None,LensFlareTints[0]=(R=1.000000,G=0.800000,B=0.400000,A=0.600000),LensFlareTints[1]=(R=1.000000,G=1.000000,B=0.600000,A=0.530000),LensFlareTints[2]=(R=0.800000,G=0.800000,B=1.000000,A=0.460000),LensFlareTints[3]=(R=0.500000,G=1.000000,B=0.400000,A=0.390000),LensFlareTints[4]=(R=0.500000,G=0.800000,B=1.000000,A=0.310000),LensFlareTints[5]=(R=0.900000,G=1.000000,B=0.800000,A=0.270000),LensFlareTints[6]=(R=1.000000,G=0.800000,B=0.400000,A=0.220000),LensFlareTints[7]=(R=0.900000,G=0.700000,B=0.700000,A=0.150000),VignetteIntensity=0.161468,GrainJitter=0.000000,GrainIntensity=0.000000,AmbientOcclusionIntensity=1.000000,AmbientOcclusionStaticFraction=1.000000,AmbientOcclusionRadius=73.477997,AmbientOcclusionRadiusInWS=False,AmbientOcclusionFadeDistance=8000.000000,AmbientOcclusionFadeRadius=5000.000000,AmbientOcclusionDistance=80.000000,AmbientOcclusionPower=1.200000,AmbientOcclusionBias=3.000000,AmbientOcclusionQuality=100.000000,AmbientOcclusionMipBlend=0.600000,AmbientOcclusionMipScale=1.700000,AmbientOcclusionMipThreshold=0.010000,IndirectLightingColor=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),IndirectLightingIntensity=1.000000,ColorGradingIntensity=0.000000,ColorGradingLUT=Texture2D'/Engine/EditorResources/RGBTable16x1_AssetViewer.RGBTable16x1_AssetViewer',DepthOfFieldMethod=DOFM_BokehDOF,bMobileHQGaussian=False,DepthOfFieldFstop=4.000000,DepthOfFieldSensorWidth=24.576000,DepthOfFieldFocalDistance=1000.000000,DepthOfFieldDepthBlurAmount=1.000000,DepthOfFieldDepthBlurRadius=0.000000,DepthOfFieldFocalRegion=0.000000,DepthOfFieldNearTransitionRegion=300.000000,DepthOfFieldFarTransitionRegion=500.000000,DepthOfFieldScale=0.000000,DepthOfFieldMaxBokehSize=15.000000,DepthOfFieldNearBlurSize=15.000000,DepthOfFieldFarBlurSize=15.000000,DepthOfFieldBokehShape=None,DepthOfFieldOcclusion=0.400000,DepthOfFieldColorThreshold=1.000000,DepthOfFieldSizeThreshold=0.080000,DepthOfFieldSkyFocusDistance=0.000000,DepthOfFieldVignetteSize=200.000000,MotionBlurAmount=0.500000,MotionBlurMax=5.000000,MotionBlurPerObjectSize=0.500000,LPVIntensity=1.000000,LPVVplInjectionBias=0.640000,LPVSize=5312.000000,LPVSecondaryOcclusionIntensity=0.000000,LPVSecondaryBounceIntensity=0.000000,LPVGeometryVolumeBias=0.384000,LPVEmissiveInjectionIntensity=1.000000,LPVDirectionalOcclusionIntensity=0.000000,LPVDirectionalOcclusionRadius=8.000000,LPVDiffuseOcclusionExponent=1.000000,LPVSpecularOcclusionExponent=7.000000,LPVDiffuseOcclusionIntensity=1.000000,LPVSpecularOcclusionIntensity=1.000000,ScreenSpaceReflectionIntensity=100.000000,ScreenSpaceReflectionQuality=100.000000,ScreenSpaceReflectionMaxRoughness=1.000000,ScreenPercentage=100.000000,WeightedBlendables=(Array=),Blendables=),bPostProcessingEnabled=True,LightingRigRotation=109.389069,RotationSpeed=2.000000,DirectionalLightRotation=(Pitch=-39.999985,Yaw=-67.500015,Roll=0.000000))
+
diff --git a/VIRTUOS_ExpansionPluginTests/Config/DefaultEngine.ini b/VIRTUOS_ExpansionPluginTests/Config/DefaultEngine.ini
new file mode 100644
index 0000000..ce0235f
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Config/DefaultEngine.ini
@@ -0,0 +1,413 @@
+[HMDPluginPriority]
+; Since SteamVR also works with the Oculus Rift and Windows Mixed Reality, give priority to the native Oculus and Windows Mixed Reality plugins before trying SteamVR
+OpenXRHMD=60
+WindowsMixedRealityHMD=40
+OculusHMD=70
+SteamVR=10
+
+[/Script/Engine.GameEngine]
++NetDriverDefinitions=(DefName="GameNetDriver2",DriverClassName="OnlineSubsystemUtils.IpNetDriver",DriverClassNameFallback="OnlineSubsystemUtils.IpNetDriver")
++NetDriverDefinitions=(DefName="GameNetDriver",DriverClassName="OnlineSubsystemSteam.SteamNetDriver",DriverClassNameFallback="OnlineSubsystemUtils.IpNetDriver")
+
+
+[/Script/OnlineSubsystemSteam.SteamNetDriver]
+NetConnectionClassName="OnlineSubsystemSteam.SteamNetConnection"
+
+[Voice]
+bEnabled=true
+
+[OnlineSubsystem]
+bHasVoiceEnabled=true
+DefaultPlatformService=Steam
+
+[OnlineSubsystemSteam]
+bEnabled=True
+SteamDevAppId=480
+
+[/Script/OnlineSubsystemUtils.IpNetDriver]
+MaxClientRate=15000
+MaxInternetClientRate=15000
+
+[/Script/Engine.Player]
+ConfiguredInternetSpeed=15000
+ConfiguredLanSpeed=20000
+
+[/Script/EngineSettings.GameMapsSettings]
+EditorStartupMap=/Game/VRE/ExampleMap/MotionControllerMap.MotionControllerMap
+LocalMapOptions=
+TransitionMap=
+bUseSplitscreen=False
+TwoPlayerSplitscreenLayout=Horizontal
+ThreePlayerSplitscreenLayout=FavorTop
+GameInstanceClass=/Game/VRE/Core/VRGameInstance.VRGameInstance_C
+GameDefaultMap=/Game/VRE/ExampleMap/MotionControllerMap.MotionControllerMap
+ServerDefaultMap=/Engine/Maps/Entry
+GlobalDefaultGameMode=/Game/VRE/Core/SteamVR_GM.SteamVR_GM_C
+GlobalDefaultServerGameMode=None
+
+[/Script/Engine.Engine]
++ActiveGameNameRedirects=(OldGameName="TP_VirtualRealityBP",NewGameName="/Script/VRExpPluginExample")
++ActiveGameNameRedirects=(OldGameName="/Script/TP_VirtualRealityBP",NewGameName="/Script/VRExpPluginExample")
++ActiveGameNameRedirects=(OldGameName="TP_FirstPersonBP",NewGameName="/Script/VRTemplate")
++ActiveGameNameRedirects=(OldGameName="/Script/TP_FirstPersonBP",NewGameName="/Script/VRTemplate")
+bSmoothFrameRate=False
+GameViewportClientClassName=/Script/VRExpansionPlugin.VRGameViewportClient
+NearClipPlane=2.000000
+LocalPlayerClassName=/Game/VRE/Core/CustomLocalPlayer.CustomLocalPlayer_C
+
+[/Script/HardwareTargeting.HardwareTargetingSettings]
+TargetedHardwareClass=Desktop
+AppliedTargetedHardwareClass=Desktop
+DefaultGraphicsPerformance=Maximum
+AppliedDefaultGraphicsPerformance=Maximum
+
+[/Script/HardwareTargeting.HardwareTargetingSettings]
+TargetedHardwareClass=Desktop
+DefaultGraphicsPerformance=Maximum
+
+[/Script/Engine.RendererSettings]
+r.Nanite=0
+r.DefaultFeature.AmbientOcclusion=False
+r.DefaultFeature.AmbientOcclusionStaticFraction=False
+vr.InstancedStereo=True
+r.SeparateTranslucency=0
+r.HZBOcclusion=0
+r.EyeAdaptionQuality=0
+r.SSR.MaxRoughness=0
+r.rhicmdbypass=0
+r.TiledReflectionEnvironmentMinimumCount=10
+r.ForwardShading=True
+r.DefaultFeature.AntiAliasing=3
+r.AllowStaticLighting=True
+r.DefaultFeature.MotionBlur=False
+r.DefaultFeature.Bloom=True
+r.DefaultFeature.AutoExposure=False
+r.CustomDepth=1
+r.CustomDepthTemporalAAJitter=True
+r.EarlyZPass=3
+r.EarlyZPassMovable=True
+r.GBufferFormat=1
+r.Mobile.UseHWsRGBEncoding=True
+r.MobileMSAA=4
+r.MobileHDR=False
+r.AntiAliasingMethod=3
+r.Mobile.AntiAliasing=3
+vr.VRS.HMDFixedFoveationDynamic=False
+vr.VRS.HMDFixedFoveationLevel=0
+vr.MobileMultiView=True
+
+[/Script/NavigationSystem.NavigationSystemV1]
+bAutoCreateNavigationData=True
+bAllowClientSideNavigation=True
+bInitialBuildingLocked=False
+bSkipAgentHeightCheckWhenPickingNavData=False
+DataGatheringMode=Instant
+bGenerateNavigationOnlyAroundNavigationInvokers=False
+ActiveTilesUpdateInterval=1.000000
+DirtyAreasUpdateFreq=60.000000
+
+[/Script/Engine.UserInterfaceSettings]
+RenderFocusRule=NavigationOnly
+DefaultCursor=None
+TextEditBeamCursor=None
+CrosshairsCursor=None
+HandCursor=None
+GrabHandCursor=None
+GrabHandClosedCursor=None
+SlashedCircleCursor=None
+ApplicationScale=1.000000
+UIScaleRule=ShortestSide
+CustomScalingRuleClass=None
+UIScaleCurve=(EditorCurveData=(PreInfinityExtrap=RCCE_Constant,PostInfinityExtrap=RCCE_Constant,Keys=((Time=480.000000,Value=0.444000),(Time=720.000000,Value=0.666000),(Time=1080.000000,Value=1.000000),(Time=8640.000000,Value=8.000000)),DefaultValue=340282346638528859811704183484516925440.000000),ExternalCurve=None)
+
+[/Script/Engine.EndUserSettings]
+bSendAnonymousUsageDataToEpic=False
+
+[/Script/Engine.CollisionProfile]
+-Profiles=(Name="NoCollision",CollisionEnabled=NoCollision,ObjectTypeName="WorldStatic",CustomResponses=((Channel="Visibility",Response=ECR_Ignore),(Channel="Camera",Response=ECR_Ignore)),HelpMessage="No collision",bCanModify=False)
+-Profiles=(Name="BlockAll",CollisionEnabled=QueryAndPhysics,ObjectTypeName="WorldStatic",CustomResponses=,HelpMessage="WorldStatic object that blocks all actors by default. All new custom channels will use its own default response. ",bCanModify=False)
+-Profiles=(Name="OverlapAll",CollisionEnabled=QueryOnly,ObjectTypeName="WorldStatic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Overlap),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldStatic object that overlaps all actors by default. All new custom channels will use its own default response. ",bCanModify=False)
+-Profiles=(Name="BlockAllDynamic",CollisionEnabled=QueryAndPhysics,ObjectTypeName="WorldDynamic",CustomResponses=,HelpMessage="WorldDynamic object that blocks all actors by default. All new custom channels will use its own default response. ",bCanModify=False)
+-Profiles=(Name="OverlapAllDynamic",CollisionEnabled=QueryOnly,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Overlap),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldDynamic object that overlaps all actors by default. All new custom channels will use its own default response. ",bCanModify=False)
+-Profiles=(Name="IgnoreOnlyPawn",CollisionEnabled=QueryOnly,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="Pawn",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore)),HelpMessage="WorldDynamic object that ignores Pawn and Vehicle. All other channels will be set to default.",bCanModify=False)
+-Profiles=(Name="OverlapOnlyPawn",CollisionEnabled=QueryOnly,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="Pawn",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Ignore)),HelpMessage="WorldDynamic object that overlaps Pawn, Camera, and Vehicle. All other channels will be set to default. ",bCanModify=False)
+-Profiles=(Name="Pawn",CollisionEnabled=QueryAndPhysics,ObjectTypeName="Pawn",CustomResponses=((Channel="Visibility",Response=ECR_Ignore)),HelpMessage="Pawn object. Can be used for capsule of any playerable character or AI. ",bCanModify=False)
+-Profiles=(Name="Spectator",CollisionEnabled=QueryOnly,ObjectTypeName="Pawn",CustomResponses=((Channel="WorldStatic",Response=ECR_Block),(Channel="Pawn",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore),(Channel="WorldDynamic",Response=ECR_Ignore),(Channel="Camera",Response=ECR_Ignore),(Channel="PhysicsBody",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore),(Channel="Destructible",Response=ECR_Ignore)),HelpMessage="Pawn object that ignores all other actors except WorldStatic.",bCanModify=False)
+-Profiles=(Name="CharacterMesh",CollisionEnabled=QueryOnly,ObjectTypeName="Pawn",CustomResponses=((Channel="Pawn",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore)),HelpMessage="Pawn object that is used for Character Mesh. All other channels will be set to default.",bCanModify=False)
+-Profiles=(Name="PhysicsActor",CollisionEnabled=QueryAndPhysics,ObjectTypeName="PhysicsBody",CustomResponses=,HelpMessage="Simulating actors",bCanModify=False)
+-Profiles=(Name="Destructible",CollisionEnabled=QueryAndPhysics,ObjectTypeName="Destructible",CustomResponses=,HelpMessage="Destructible actors",bCanModify=False)
+-Profiles=(Name="InvisibleWall",CollisionEnabled=QueryAndPhysics,ObjectTypeName="WorldStatic",CustomResponses=((Channel="Visibility",Response=ECR_Ignore)),HelpMessage="WorldStatic object that is invisible.",bCanModify=False)
+-Profiles=(Name="InvisibleWallDynamic",CollisionEnabled=QueryAndPhysics,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="Visibility",Response=ECR_Ignore)),HelpMessage="WorldDynamic object that is invisible.",bCanModify=False)
+-Profiles=(Name="Trigger",CollisionEnabled=QueryOnly,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Ignore),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldDynamic object that is used for trigger. All other channels will be set to default.",bCanModify=False)
+-Profiles=(Name="Ragdoll",CollisionEnabled=QueryAndPhysics,ObjectTypeName="PhysicsBody",CustomResponses=((Channel="Pawn",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore)),HelpMessage="Simulating Skeletal Mesh Component. All other channels will be set to default.",bCanModify=False)
+-Profiles=(Name="Vehicle",CollisionEnabled=QueryAndPhysics,ObjectTypeName="Vehicle",CustomResponses=,HelpMessage="Vehicle object that blocks Vehicle, WorldStatic, and WorldDynamic. All other channels will be set to default.",bCanModify=False)
+-Profiles=(Name="UI",CollisionEnabled=QueryOnly,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Block),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldStatic object that overlaps all actors by default. All new custom channels will use its own default response. ",bCanModify=False)
++Profiles=(Name="NoCollision",CollisionEnabled=NoCollision,ObjectTypeName="WorldStatic",CustomResponses=((Channel="Visibility",Response=ECR_Ignore),(Channel="Camera",Response=ECR_Ignore)),HelpMessage="No collision",bCanModify=False)
++Profiles=(Name="BlockAll",CollisionEnabled=QueryAndPhysics,ObjectTypeName="WorldStatic",CustomResponses=,HelpMessage="WorldStatic object that blocks all actors by default. All new custom channels will use its own default response. ",bCanModify=False)
++Profiles=(Name="OverlapAll",CollisionEnabled=QueryOnly,ObjectTypeName="WorldStatic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Overlap),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldStatic object that overlaps all actors by default. All new custom channels will use its own default response. ",bCanModify=False)
++Profiles=(Name="BlockAllDynamic",CollisionEnabled=QueryAndPhysics,ObjectTypeName="WorldDynamic",CustomResponses=,HelpMessage="WorldDynamic object that blocks all actors by default. All new custom channels will use its own default response. ",bCanModify=False)
++Profiles=(Name="OverlapAllDynamic",CollisionEnabled=QueryOnly,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Overlap),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldDynamic object that overlaps all actors by default. All new custom channels will use its own default response. ",bCanModify=False)
++Profiles=(Name="IgnoreOnlyPawn",CollisionEnabled=QueryOnly,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="Pawn",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore)),HelpMessage="WorldDynamic object that ignores Pawn and Vehicle. All other channels will be set to default.",bCanModify=False)
++Profiles=(Name="OverlapOnlyPawn",CollisionEnabled=QueryOnly,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="Pawn",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Ignore)),HelpMessage="WorldDynamic object that overlaps Pawn, Camera, and Vehicle. All other channels will be set to default. ",bCanModify=False)
++Profiles=(Name="Pawn",CollisionEnabled=QueryAndPhysics,ObjectTypeName="Pawn",CustomResponses=((Channel="Visibility",Response=ECR_Ignore)),HelpMessage="Pawn object. Can be used for capsule of any playerable character or AI. ",bCanModify=False)
++Profiles=(Name="Spectator",CollisionEnabled=QueryOnly,ObjectTypeName="Pawn",CustomResponses=((Channel="WorldStatic"),(Channel="Pawn",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore),(Channel="WorldDynamic",Response=ECR_Ignore),(Channel="Camera",Response=ECR_Ignore),(Channel="PhysicsBody",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore),(Channel="Destructible",Response=ECR_Ignore)),HelpMessage="Pawn object that ignores all other actors except WorldStatic.",bCanModify=False)
++Profiles=(Name="CharacterMesh",CollisionEnabled=QueryOnly,ObjectTypeName="Pawn",CustomResponses=((Channel="Pawn",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore)),HelpMessage="Pawn object that is used for Character Mesh. All other channels will be set to default.",bCanModify=False)
++Profiles=(Name="PhysicsActor",CollisionEnabled=QueryAndPhysics,ObjectTypeName="PhysicsBody",CustomResponses=,HelpMessage="Simulating actors",bCanModify=False)
++Profiles=(Name="Destructible",CollisionEnabled=QueryAndPhysics,ObjectTypeName="Destructible",CustomResponses=,HelpMessage="Destructible actors",bCanModify=False)
++Profiles=(Name="InvisibleWall",CollisionEnabled=QueryAndPhysics,ObjectTypeName="WorldStatic",CustomResponses=((Channel="Visibility",Response=ECR_Ignore)),HelpMessage="WorldStatic object that is invisible.",bCanModify=False)
++Profiles=(Name="InvisibleWallDynamic",CollisionEnabled=QueryAndPhysics,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="Visibility",Response=ECR_Ignore)),HelpMessage="WorldDynamic object that is invisible.",bCanModify=False)
++Profiles=(Name="Trigger",CollisionEnabled=QueryOnly,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Ignore),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldDynamic object that is used for trigger. All other channels will be set to default.",bCanModify=False)
++Profiles=(Name="Ragdoll",CollisionEnabled=QueryAndPhysics,ObjectTypeName="PhysicsBody",CustomResponses=((Channel="Pawn",Response=ECR_Ignore)),HelpMessage="Simulating Skeletal Mesh Component. All other channels will be set to default.",bCanModify=False)
++Profiles=(Name="Vehicle",CollisionEnabled=QueryAndPhysics,ObjectTypeName="Vehicle",CustomResponses=,HelpMessage="Vehicle object that blocks Vehicle, WorldStatic, and WorldDynamic. All other channels will be set to default.",bCanModify=False)
++Profiles=(Name="UI",CollisionEnabled=QueryOnly,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility"),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldStatic object that overlaps all actors by default. All new custom channels will use its own default response. ",bCanModify=False)
++DefaultChannelResponses=(Channel=ECC_GameTraceChannel1,Name="VRTraceChannel",DefaultResponse=ECR_Block,bTraceType=True,bStaticObject=False)
++DefaultChannelResponses=(Channel=ECC_GameTraceChannel2,Name="FloorTrace",DefaultResponse=ECR_Ignore,bTraceType=True,bStaticObject=False)
++DefaultChannelResponses=(Channel=ECC_GameTraceChannel3,Name="PawnWalking",DefaultResponse=ECR_Ignore,bTraceType=False,bStaticObject=False)
++EditProfiles=(Name="PhysicsActor",CustomResponses=((Channel="VRTraceChannel")))
++EditProfiles=(Name="BlockAllDynamic",CustomResponses=((Channel="VRTraceChannel")))
+-ProfileRedirects=(OldName="BlockingVolume",NewName="InvisibleWall")
+-ProfileRedirects=(OldName="InterpActor",NewName="IgnoreOnlyPawn")
+-ProfileRedirects=(OldName="StaticMeshComponent",NewName="BlockAllDynamic")
+-ProfileRedirects=(OldName="SkeletalMeshActor",NewName="PhysicsActor")
+-ProfileRedirects=(OldName="InvisibleActor",NewName="InvisibleWallDynamic")
++ProfileRedirects=(OldName="BlockingVolume",NewName="InvisibleWall")
++ProfileRedirects=(OldName="InterpActor",NewName="IgnoreOnlyPawn")
++ProfileRedirects=(OldName="StaticMeshComponent",NewName="BlockAllDynamic")
++ProfileRedirects=(OldName="SkeletalMeshActor",NewName="PhysicsActor")
++ProfileRedirects=(OldName="InvisibleActor",NewName="InvisibleWallDynamic")
+-CollisionChannelRedirects=(OldName="Static",NewName="WorldStatic")
+-CollisionChannelRedirects=(OldName="Dynamic",NewName="WorldDynamic")
+-CollisionChannelRedirects=(OldName="VehicleMovement",NewName="Vehicle")
+-CollisionChannelRedirects=(OldName="PawnMovement",NewName="Pawn")
++CollisionChannelRedirects=(OldName="Static",NewName="WorldStatic")
++CollisionChannelRedirects=(OldName="Dynamic",NewName="WorldDynamic")
++CollisionChannelRedirects=(OldName="VehicleMovement",NewName="Vehicle")
++CollisionChannelRedirects=(OldName="PawnMovement",NewName="Pawn")
+
+[/Script/NavigationSystem.RecastNavMesh]
+RuntimeGeneration=Dynamic
+
+[/Script/VRExpansionPlugin.VRGlobalSettings]
+bUseGlobalLerpToHand=False
+MinDistanceForLerp=10.000000
+LerpDuration=0.250000
+MinSpeedForLerp=100.000000
+MaxSpeedForLerp=500.000000
+LerpInterpolationMode=QuatInterp
+bUseCurve=False
+OptionalCurveToFollow=(EditorCurveData=(Keys=,DefaultValue=340282346638528859811704183484516925440.000000,PreInfinityExtrap=RCCE_Constant,PostInfinityExtrap=RCCE_Constant),ExternalCurve=None)
+MaxCCDPasses=1
++MeleeSurfaceSettings=(bSurfaceAllowsPenetration=True,BluntDamageScaler=1.000000,SharpDamageScaler=1.000000,StabVelocityScaler=1.000000,SurfaceType=SurfaceType2)
++MeleeSurfaceSettings=(bSurfaceAllowsPenetration=True,BluntDamageScaler=1.000000,SharpDamageScaler=1.000000,StabVelocityScaler=2.000000,SurfaceType=SurfaceType1)
+VirtualStockSettings=(bUseDistanceBasedStockSnapping=True,StockSnapDistance=35.000000,StockSnapLerpThreshold=20.000000,StockLerpValue=0.000000,StockSnapOffset=(X=0.000000,Y=0.000000,Z=0.000000),bSmoothStockHand=True,SmoothingValueForStock=1.000000,StockHandSmoothing=(MinCutoff=0.100000,DeltaCutoff=10.000000,CutoffSlope=10.000000),bDebugDrawVirtualStock=False)
+OneEuroMinCutoff=0.100000
+OneEuroCutoffSlope=10.000000
+OneEuroDeltaCutoff=10.000000
++ControllerProfiles=(ControllerName="Oculus_Touch_Steam",SocketOffsetTransform=(Rotation=(X=0.000000,Y=-0.000000,Z=0.000000,W=1.000000),Translation=(X=0.000000,Y=0.000000,Z=0.000000),Scale3D=(X=1.000000,Y=1.000000,Z=1.000000)),bUseSeperateHandOffsetTransforms=False,SocketOffsetTransformRightHand=(Rotation=(X=0.000000,Y=0.000000,Z=0.000000,W=1.000000),Translation=(X=0.000000,Y=0.000000,Z=0.000000),Scale3D=(X=1.000000,Y=1.000000,Z=1.000000)))
++ControllerProfiles=(ControllerName="Oculus_Touch_Home",SocketOffsetTransform=(Rotation=(X=0.000000,Y=-0.000000,Z=0.000000,W=1.000000),Translation=(X=0.000000,Y=0.000000,Z=0.000000),Scale3D=(X=1.000000,Y=1.000000,Z=1.000000)),bUseSeperateHandOffsetTransforms=False,SocketOffsetTransformRightHand=(Rotation=(X=0.000000,Y=0.000000,Z=0.000000,W=1.000000),Translation=(X=0.000000,Y=0.000000,Z=0.000000),Scale3D=(X=1.000000,Y=1.000000,Z=1.000000)))
++ControllerProfiles=(ControllerName="Vive_Wands",SocketOffsetTransform=(Rotation=(X=0.000000,Y=0.000000,Z=0.000000,W=1.000000),Translation=(X=0.000000,Y=0.000000,Z=0.000000),Scale3D=(X=1.000000,Y=1.000000,Z=1.000000)),bUseSeperateHandOffsetTransforms=False,SocketOffsetTransformRightHand=(Rotation=(X=0.000000,Y=0.000000,Z=0.000000,W=1.000000),Translation=(X=0.000000,Y=0.000000,Z=0.000000),Scale3D=(X=1.000000,Y=1.000000,Z=1.000000)))
++ControllerProfiles=(ControllerName="Windows_MR",SocketOffsetTransform=(Rotation=(X=0.000000,Y=-0.000000,Z=0.000000,W=1.000000),Translation=(X=0.000000,Y=0.000000,Z=0.000000),Scale3D=(X=1.000000,Y=1.000000,Z=1.000000)),bUseSeperateHandOffsetTransforms=True,SocketOffsetTransformRightHand=(Rotation=(X=0.000000,Y=-0.000000,Z=0.000000,W=1.000000),Translation=(X=0.000000,Y=0.000000,Z=0.000000),Scale3D=(X=1.000000,Y=1.000000,Z=1.000000)))
++ControllerProfiles=(ControllerName="FPS_Pawn",SocketOffsetTransform=(Rotation=(X=0.000000,Y=-0.573576,Z=0.000000,W=0.819152),Translation=(X=0.000000,Y=0.000000,Z=0.000000),Scale3D=(X=1.000000,Y=1.000000,Z=1.000000)),bUseSeperateHandOffsetTransforms=False,SocketOffsetTransformRightHand=(Rotation=(X=0.000000,Y=0.000000,Z=0.000000,W=1.000000),Translation=(X=0.000000,Y=0.000000,Z=0.000000),Scale3D=(X=1.000000,Y=1.000000,Z=1.000000)))
++ControllerProfiles=(ControllerName="KnucklesEV2",SocketOffsetTransform=(Rotation=(X=-0.000000,Y=-0.000000,Z=0.000000,W=1.000000),Translation=(X=0.000000,Y=0.000000,Z=0.000000),Scale3D=(X=1.000000,Y=1.000000,Z=1.000000)),bUseSeperateHandOffsetTransforms=True,SocketOffsetTransformRightHand=(Rotation=(X=0.000000,Y=-0.000000,Z=0.000000,W=1.000000),Translation=(X=0.000000,Y=0.000000,Z=0.000000),Scale3D=(X=1.000000,Y=1.000000,Z=1.000000)))
+AngularDriveDampingScale=0.300000
+LinearDriveDampingScale=0.700000
+LinearDriveStiffnessScale=1.500000
+AngularDriveStiffnessScale=0.300000
+SoftLinearStiffnessScale=1.500000
+SoftLinearDampingScale=1.200000
+JointStiffness=1.000000
+bSetEngineChaosScalers=False
+bLerpHybridWithSweepGrips=True
+bHybridWithSweepUseDistanceBasedLerp=True
+HybridWithSweepLerpDuration=0.660000
+bUseChaosTranslationScalers=False
+
+[/Script/Engine.PhysicsSettings]
+DefaultGravityZ=-980.000000
+DefaultTerminalVelocity=4000.000000
+DefaultFluidFriction=0.300000
+SimulateScratchMemorySize=262144
+RagdollAggregateThreshold=4
+TriangleMeshTriangleMinAreaThreshold=5.000000
+bEnableShapeSharing=False
+bEnablePCM=True
+bEnableStabilization=False
+bWarnMissingLocks=True
+bEnable2DPhysics=False
+PhysicErrorCorrection=(PingExtrapolation=0.100000,PingLimit=100.000000,ErrorPerLinearDifference=1.000000,ErrorPerAngularDifference=1.000000,MaxRestoredStateError=1.000000,MaxLinearHardSnapDistance=400.000000,PositionLerp=0.000000,AngleLerp=0.400000,LinearVelocityCoefficient=100.000000,AngularVelocityCoefficient=10.000000,ErrorAccumulationSeconds=0.500000,ErrorAccumulationDistanceSq=15.000000,ErrorAccumulationSimilarity=100.000000)
+DefaultDegreesOfFreedom=Full3D
+BounceThresholdVelocity=200.000000
+FrictionCombineMode=Average
+RestitutionCombineMode=Average
+MaxAngularVelocity=3600.000000
+MaxDepenetrationVelocity=0.000000
+ContactOffsetMultiplier=0.020000
+MinContactOffset=2.000000
+MaxContactOffset=8.000000
+bSimulateSkeletalMeshOnDedicatedServer=True
+DefaultShapeComplexity=CTF_UseSimpleAndComplex
+bSuppressFaceRemapTable=False
+bSupportUVFromHitResults=False
+bDisableActiveActors=False
+bDisableKinematicStaticPairs=False
+bDisableKinematicKinematicPairs=False
+bDisableCCD=False
+bEnableEnhancedDeterminism=False
+AnimPhysicsMinDeltaTime=0.000000
+bSimulateAnimPhysicsAfterReset=False
+MaxPhysicsDeltaTime=0.033333
+bSubstepping=True
+bSubsteppingAsync=False
+MaxSubstepDeltaTime=0.016667
+MaxSubsteps=6
+SyncSceneSmoothingFactor=0.000000
+InitialAverageFrameRate=0.016667
+PhysXTreeRebuildRate=10
++PhysicalSurfaces=(Type=SurfaceType1,Name="Flesh")
++PhysicalSurfaces=(Type=SurfaceType2,Name="Wood")
+DefaultBroadphaseSettings=(bUseMBPOnClient=False,bUseMBPOnServer=False,bUseMBPOuterBounds=False,MBPBounds=(Min=(X=0.000000,Y=0.000000,Z=0.000000),Max=(X=0.000000,Y=0.000000,Z=0.000000),IsValid=0),MBPOuterBounds=(Min=(X=0.000000,Y=0.000000,Z=0.000000),Max=(X=0.000000,Y=0.000000,Z=0.000000),IsValid=0),MBPNumSubdivs=2)
+ChaosSettings=(DefaultThreadingModel=SingleThread,DedicatedThreadTickMode=VariableCapped,DedicatedThreadBufferMode=Double)
+SolverOptions=(Iterations=8,CollisionPairIterations=1,PushOutIterations=4,CollisionPushOutPairIterations=4,CollisionMarginFraction=0.050000,CollisionMarginMax=10.000000,CollisionCullDistance=3.000000,CollisionMaxPushOutVelocity=100000.000000,JointPairIterations=1,JointPushOutPairIterations=1,ClusterConnectionFactor=1.000000,ClusterUnionConnectionType=DelaunayTriangulation,bGenerateCollisionData=False,CollisionFilterSettings=(FilterEnabled=False,MinMass=0.000000,MinSpeed=0.000000,MinImpulse=0.000000),bGenerateBreakData=False,BreakingFilterSettings=(FilterEnabled=False,MinMass=0.000000,MinSpeed=0.000000,MinVolume=0.000000),bGenerateTrailingData=False,TrailingFilterSettings=(FilterEnabled=False,MinMass=0.000000,MinSpeed=0.000000,MinVolume=0.000000),bGenerateContactGraph=True)
+
+[/Script/AndroidRuntimeSettings.AndroidRuntimeSettings]
+PackageName=com.YourCompany.[PROJECT]
+StoreVersion=1
+StoreVersionOffsetArm64=0
+StoreVersionOffsetX8664=0
+ApplicationDisplayName=
+VersionDisplayName=1.0
+MinSDKVersion=26
+TargetSDKVersion=33
+InstallLocation=InternalOnly
+bEnableLint=False
+bPackageDataInsideApk=False
+bCreateAllPlatformsInstall=False
+bDisableVerifyOBBOnStartUp=False
+bForceSmallOBBFiles=False
+bAllowLargeOBBFiles=False
+bAllowPatchOBBFile=False
+bAllowOverflowOBBFiles=False
+bUseExternalFilesDir=False
+bPublicLogFiles=True
+Orientation=SensorLandscape
+MaxAspectRatio=2.100000
+bUseDisplayCutout=False
+bRestoreNotificationsOnReboot=False
+bFullScreen=True
+bEnableNewKeyboard=True
+DepthBufferPreference=Default
+bValidateTextureFormats=True
+bForceCompressNativeLibs=False
+bEnableAdvancedBinaryCompression=True
+bEnableBundle=False
+bEnableUniversalAPK=False
+bBundleABISplit=True
+bBundleLanguageSplit=True
+bBundleDensitySplit=True
+ExtraApplicationSettings=
+ExtraActivitySettings=
+bAndroidVoiceEnabled=False
+bEnableMulticastSupport=False
+bPackageForMetaQuest=True
+bRemoveOSIG=True
+KeyStore=
+KeyAlias=
+KeyStorePassword=
+KeyPassword=
+bBuildForArm64=True
+bBuildForX8664=False
+bBuildForES31=False
+bSupportsVulkan=True
+bSupportsVulkanSM5=False
+DebugVulkanLayerDirectory=(Path="")
+bAndroidOpenGLSupportsBackbufferSampling=False
+bDetectVulkanByDefault=True
+bBuildWithHiddenSymbolVisibility=False
+bDisableStackProtector=False
+bDisableLibCppSharedDependencyValidation=False
+bSaveSymbols=False
+bStripShaderReflection=True
+bEnableGooglePlaySupport=False
+bUseGetAccounts=False
+GamesAppID=
+bEnableSnapshots=False
+bSupportAdMob=True
+AdMobAppID=
+TagForChildDirectedTreatment=TAG_FOR_CHILD_DIRECTED_TREATMENT_UNSPECIFIED
+TagForUnderAgeOfConsent=TAG_FOR_UNDER_AGE_OF_CONSENT_UNSPECIFIED
+MaxAdContentRating=MAX_AD_CONTENT_RATING_G
+AdMobAdUnitID=
+GooglePlayLicenseKey=
+GCMClientSenderID=
+bShowLaunchImage=True
+bAllowIMU=True
+bAllowControllers=True
+bBlockAndroidKeysOnControllers=False
+bControllersBlockDeviceFeedback=False
+AndroidAudio=Default
+AudioSampleRate=44100
+AudioCallbackBufferFrameSize=1024
+AudioNumBuffersToEnqueue=4
+AudioMaxChannels=0
+AudioNumSourceWorkers=0
+SpatializationPlugin=
+SourceDataOverridePlugin=
+ReverbPlugin=
+OcclusionPlugin=
+CompressionOverrides=(bOverrideCompressionTimes=False,DurationThreshold=5.000000,MaxNumRandomBranches=0,SoundCueQualityIndex=0)
+CacheSizeKB=0
+MaxChunkSizeOverrideKB=0
+bResampleForDevice=False
+SoundCueCookQualityIndex=-1
+MaxSampleRate=0.000000
+HighSampleRate=0.000000
+MedSampleRate=0.000000
+LowSampleRate=0.000000
+MinSampleRate=0.000000
+CompressionQualityModifier=0.000000
+AutoStreamingThreshold=0.000000
+AndroidGraphicsDebugger=None
+MaliGraphicsDebuggerPath=(Path="")
+bEnableMaliPerfCounters=False
+bMultiTargetFormat_ETC2=True
+bMultiTargetFormat_DXT=True
+bMultiTargetFormat_ASTC=True
+TextureFormatPriority_ETC2=0.200000
+TextureFormatPriority_DXT=0.600000
+TextureFormatPriority_ASTC=0.900000
+SDKAPILevelOverride=
+NDKAPILevelOverride=
+BuildToolsOverride=
+bStreamLandscapeMeshLODs=False
+bEnableDomStorage=False
+
+[/Script/OculusHMD.OculusHMDRuntimeSettings]
+FFRLevel=FFR_Medium
+HandTrackingSupport=ControllersAndHands
+
+[/Script/LinuxTargetPlatform.LinuxTargetSettings]
+SpatializationPlugin=
+ReverbPlugin=
+OcclusionPlugin=
+SoundCueCookQualityIndex=-1
+-TargetedRHIs=SF_VULKAN_SM5
++TargetedRHIs=SF_VULKAN_SM5
++TargetedRHIs=SF_VULKAN_ES31
+
+[/Script/AndroidFileServerEditor.AndroidFileServerRuntimeSettings]
+bEnablePlugin=True
+bAllowNetworkConnection=True
+SecurityToken=A20E6C4847E893682C3EDFA6620C12B0
+bIncludeInShipping=False
+bAllowExternalStartInShipping=False
+bCompileAFSProject=False
+bUseCompression=False
+bLogFiles=False
+bReportStats=False
+ConnectionType=USBOnly
+bUseManualIPAddress=False
+ManualIPAddress=
+
+[/Script/WindowsTargetPlatform.WindowsTargetSettings]
+DefaultGraphicsRHI=DefaultGraphicsRHI_DX12
+
diff --git a/VIRTUOS_ExpansionPluginTests/Config/DefaultGame.ini b/VIRTUOS_ExpansionPluginTests/Config/DefaultGame.ini
new file mode 100644
index 0000000..60f5628
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Config/DefaultGame.ini
@@ -0,0 +1,52 @@
+[/Script/EngineSettings.GeneralProjectSettings]
+ProjectID=6652AB584B9973D2EDB8EE93165A687B
+ProjectName=Virtual Reality BP Game Template
+bStartInVR=True
+
+[/Script/Engine.GameNetworkManager]
+TotalNetBandwidth=64000
+MaxDynamicBandwidth=15000
+MinDynamicBandwidth=4000
+
+[/Script/Engine.GameSession]
+bRequiresPushToTalk=true
+
+[/Script/UnrealEd.ProjectPackagingSettings]
+Build=IfProjectHasCode
+BuildConfiguration=PPBC_Development
+BuildTarget=
+StagingDirectory=(Path="E:/DESKTOP/VRExpPluginTestMulti")
+FullRebuild=False
+ForDistribution=False
+IncludeDebugFiles=False
+bExcludeMonolithicEngineHeadersInNativizedCode=False
+UsePakFile=True
+bUseIoStore=False
+bGenerateChunks=False
+bGenerateNoChunks=False
+bChunkHardReferencesOnly=False
+bForceOneChunkPerFile=False
+MaxChunkSize=0
+bBuildHttpChunkInstallData=False
+HttpChunkInstallDataDirectory=(Path="")
+PakFileCompressionFormats=
+PakFileAdditionalCompressionOptions=
+HttpChunkInstallDataVersion=
+IncludePrerequisites=True
+IncludeAppLocalPrerequisites=False
+bShareMaterialShaderCode=False
+bDeterministicShaderCodeOrder=False
+bSharedMaterialNativeLibraries=False
+ApplocalPrerequisitesDirectory=(Path="")
+IncludeCrashReporter=True
+InternationalizationPreset=English
+-CulturesToStage=en
++CulturesToStage=en
+LocalizationTargetCatchAllChunkId=0
+bCookAll=False
+bCookMapsOnly=True
+bCompressed=True
+bSkipEditorContent=False
+bSkipMovies=False
++DirectoriesToAlwaysCook=(Path="/Game/VRE")
+
diff --git a/VIRTUOS_ExpansionPluginTests/Config/DefaultGameplayTags.ini b/VIRTUOS_ExpansionPluginTests/Config/DefaultGameplayTags.ini
new file mode 100644
index 0000000..6b861de
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Config/DefaultGameplayTags.ini
@@ -0,0 +1,11 @@
+
+[/Script/GameplayTags.GameplayTagsSettings]
+ImportTagsFromConfig=False
+WarnOnInvalidTags=True
+FastReplication=True
++GameplayTagTableList=/Game/VRE/Core/DefaultGameplayTags.DefaultGameplayTags
+NumBitsForContainerSize=6
+NetIndexFirstBitSegment=16
+
+
+
diff --git a/VIRTUOS_ExpansionPluginTests/Config/DefaultInput.ini b/VIRTUOS_ExpansionPluginTests/Config/DefaultInput.ini
new file mode 100644
index 0000000..b4e868f
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Config/DefaultInput.ini
@@ -0,0 +1,204 @@
+
+
+[/Script/Engine.InputSettings]
+-AxisConfig=(AxisKeyName="Gamepad_LeftX",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f))
+-AxisConfig=(AxisKeyName="Gamepad_LeftY",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f))
+-AxisConfig=(AxisKeyName="Gamepad_RightX",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f))
+-AxisConfig=(AxisKeyName="Gamepad_RightY",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f))
+-AxisConfig=(AxisKeyName="MouseX",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f))
+-AxisConfig=(AxisKeyName="MouseY",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f))
+-AxisConfig=(AxisKeyName="Mouse2D",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f))
++AxisConfig=(AxisKeyName="Mouse2D",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Gamepad_RightY",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MouseX",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MouseY",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MouseWheelAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Gamepad_LeftTriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Gamepad_RightTriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MotionController_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MotionController_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MotionController_Left_TriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MotionController_Left_Grip1Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MotionController_Left_Grip2Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MotionController_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MotionController_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MotionController_Right_TriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MotionController_Right_Grip1Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MotionController_Right_Grip2Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Gamepad_Special_Left_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Gamepad_Special_Left_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Left_Thumbstick",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Left_FaceButton1",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Left_Trigger",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Left_FaceButton2",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Left_IndexPointing",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Left_ThumbUp",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Right_Thumbstick",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Right_FaceButton1",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Right_Trigger",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Right_FaceButton2",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Right_IndexPointing",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Right_ThumbUp",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Gamepad_LeftX",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Gamepad_LeftY",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Gamepad_RightX",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Daydream_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Daydream_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Daydream_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Daydream_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Vive_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Vive_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Vive_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Vive_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Vive_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Vive_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MixedReality_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MixedReality_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MixedReality_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MixedReality_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MixedReality_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MixedReality_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MixedReality_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MixedReality_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MixedReality_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MixedReality_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusGo_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusGo_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusGo_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusGo_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Left_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Right_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Left_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Left_Grip_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Touch",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Right_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Right_Grip_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+bAltEnterTogglesFullscreen=True
+bF11TogglesFullscreen=True
+bUseMouseForTouch=False
+bEnableMouseSmoothing=True
+bEnableFOVScaling=True
+bCaptureMouseOnLaunch=True
+bEnableLegacyInputScales=True
+bEnableMotionControls=True
+bFilterInputByPlatformUser=False
+bShouldFlushPressedKeysOnViewportFocusLost=True
+bEnableDynamicComponentInputBinding=True
+bAlwaysShowTouchInterface=False
+bShowConsoleOnFourFingerTap=True
+bEnableGestureRecognizer=False
+bUseAutocorrect=False
+DefaultViewportMouseCaptureMode=CapturePermanently_IncludingInitialMouseDown
+DefaultViewportMouseLockMode=LockOnCapture
+FOVScale=0.011110
+DoubleClickTime=0.200000
++ActionMappings=(ActionName="Jump",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=SpaceBar)
++ActionMappings=(ActionName="TeleportLeft",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=T)
++ActionMappings=(ActionName="TeleportRight",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=ValveIndex_Right_Thumbstick_Touch)
++ActionMappings=(ActionName="TeleportRight",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=Vive_Right_Trackpad_Touch)
++ActionMappings=(ActionName="TeleportRight",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=OculusTouch_Right_Thumbstick_Click)
++ActionMappings=(ActionName="TeleportRight",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=MixedReality_Right_Thumbstick_Click)
++ActionMappings=(ActionName="AlternateGripRight",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=MixedReality_Right_Grip_Click)
++ActionMappings=(ActionName="AlternateGripRight",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=OculusTouch_Right_A_Click)
++ActionMappings=(ActionName="AlternateGripRight",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=ValveIndex_Right_A_Click)
++ActionMappings=(ActionName="AlternateGripRight",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=Vive_Right_Grip_Click)
++ActionMappings=(ActionName="AlternateGripLeft",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=Vive_Left_Grip_Click)
++ActionMappings=(ActionName="AlternateGripLeft",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=MixedReality_Left_Grip_Click)
++ActionMappings=(ActionName="AlternateGripLeft",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=OculusTouch_Left_X_Click)
++ActionMappings=(ActionName="AlternateGripLeft",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=ValveIndex_Left_A_Click)
++ActionMappings=(ActionName="PrimaryGripLeft",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=Vive_Left_Trigger_Click)
++ActionMappings=(ActionName="PrimaryGripLeft",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=MixedReality_Left_Trigger_Click)
++ActionMappings=(ActionName="PrimaryGripLeft",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=OculusTouch_Left_Grip_Click)
++ActionMappings=(ActionName="PrimaryGripLeft",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=ValveIndex_Left_Grip_Force)
++ActionMappings=(ActionName="PrimaryGripRight",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=Vive_Right_Trigger_Click)
++ActionMappings=(ActionName="PrimaryGripRight",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=MixedReality_Right_Trigger_Click)
++ActionMappings=(ActionName="PrimaryGripRight",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=OculusTouch_Right_Grip_Click)
++ActionMappings=(ActionName="PrimaryGripRight",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=ValveIndex_Right_Grip_Force)
++ActionMappings=(ActionName="TeleportLeft",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=MixedReality_Left_Thumbstick_Click)
++ActionMappings=(ActionName="TeleportLeft",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=OculusTouch_Left_Thumbstick_Click)
++ActionMappings=(ActionName="TeleportLeft",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=ValveIndex_Left_Thumbstick_Touch)
++ActionMappings=(ActionName="TeleportLeft",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=Vive_Left_Trackpad_Touch)
++ActionMappings=(ActionName="LaserBeamRight",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=Vive_Right_Menu_Click)
++ActionMappings=(ActionName="LaserBeamRight",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=OculusTouch_Right_B_Click)
++ActionMappings=(ActionName="LaserBeamRight",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=MixedReality_Right_Thumbstick_Down)
++ActionMappings=(ActionName="LaserBeamRight",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=ValveIndex_Right_B_Click)
++ActionMappings=(ActionName="LaserBeamLeft",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=Vive_Left_Menu_Click)
++ActionMappings=(ActionName="LaserBeamLeft",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=ValveIndex_Left_B_Click)
++ActionMappings=(ActionName="LaserBeamLeft",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=OculusTouch_Left_Y_Click)
++ActionMappings=(ActionName="LaserBeamLeft",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=MixedReality_Left_Thumbstick_Down)
++ActionMappings=(ActionName="LaserBeamLeft",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=L)
++ActionMappings=(ActionName="LaserBeamRight",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=L)
++ActionMappings=(ActionName="PrimaryGripRight",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=RightMouseButton)
++ActionMappings=(ActionName="PrimaryGripLeft",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=LeftMouseButton)
++ActionMappings=(ActionName="UseHeldObjectLeft",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=Vive_Left_Trigger_Click)
++ActionMappings=(ActionName="UseHeldObjectRight",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=Vive_Right_Trigger_Click)
++ActionMappings=(ActionName="UseHeldObjectLeft",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=MixedReality_Left_Trigger_Click)
++ActionMappings=(ActionName="UseHeldObjectLeft",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=OculusTouch_Left_Trigger_Click)
++ActionMappings=(ActionName="UseHeldObjectLeft",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=ValveIndex_Left_Trigger_Click)
++ActionMappings=(ActionName="UseHeldObjectRight",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=MixedReality_Right_Trigger_Click)
++ActionMappings=(ActionName="UseHeldObjectRight",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=OculusTouch_Right_Trigger_Click)
++ActionMappings=(ActionName="UseHeldObjectRight",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=ValveIndex_Right_Trigger_Click)
++ActionMappings=(ActionName="UseHeldObjectLeft",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=LeftMouseButton)
++ActionMappings=(ActionName="UseHeldObjectRight",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=RightMouseButton)
++ActionMappings=(ActionName="TeleportRight",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=T)
++AxisMappings=(AxisName="MoveForward",Scale=1.000000,Key=W)
++AxisMappings=(AxisName="MoveForward",Scale=-1.000000,Key=S)
++AxisMappings=(AxisName="MoveRight",Scale=1.000000,Key=D)
++AxisMappings=(AxisName="MoveRight",Scale=-1.000000,Key=A)
++AxisMappings=(AxisName="TurnRate",Scale=-1.000000,Key=Left)
++AxisMappings=(AxisName="TurnRate",Scale=1.000000,Key=Right)
++AxisMappings=(AxisName="LookUp",Scale=-1.000000,Key=MouseY)
++AxisMappings=(AxisName="Turn",Scale=1.000000,Key=MouseX)
++AxisMappings=(AxisName="TeleportDirectionUp",Scale=1.000000,Key=Gamepad_LeftY)
++AxisMappings=(AxisName="TeleportDirectionRight",Scale=1.000000,Key=Gamepad_LeftX)
++AxisMappings=(AxisName="MotionControllerThumbLeft_Y",Scale=1.000000,Key=ValveIndex_Left_Thumbstick_Y)
++AxisMappings=(AxisName="MotionControllerThumbLeft_Y",Scale=1.000000,Key=Vive_Left_Trackpad_Y)
++AxisMappings=(AxisName="MotionControllerThumbLeft_Y",Scale=1.000000,Key=OculusTouch_Left_Thumbstick_Y)
++AxisMappings=(AxisName="MotionControllerThumbLeft_Y",Scale=1.000000,Key=MixedReality_Left_Thumbstick_Y)
++AxisMappings=(AxisName="MotionControllerThumbLeft_X",Scale=1.000000,Key=ValveIndex_Left_Thumbstick_X)
++AxisMappings=(AxisName="MotionControllerThumbLeft_X",Scale=1.000000,Key=Vive_Left_Trackpad_X)
++AxisMappings=(AxisName="MotionControllerThumbLeft_X",Scale=1.000000,Key=OculusTouch_Left_Thumbstick_X)
++AxisMappings=(AxisName="MotionControllerThumbLeft_X",Scale=1.000000,Key=MixedReality_Left_Thumbstick_X)
++AxisMappings=(AxisName="MotionControllerThumbRight_Y",Scale=1.000000,Key=ValveIndex_Right_Thumbstick_Y)
++AxisMappings=(AxisName="MotionControllerThumbRight_Y",Scale=1.000000,Key=Vive_Right_Trackpad_Y)
++AxisMappings=(AxisName="MotionControllerThumbRight_Y",Scale=1.000000,Key=OculusTouch_Right_Thumbstick_Y)
++AxisMappings=(AxisName="MotionControllerThumbRight_Y",Scale=1.000000,Key=MixedReality_Right_Thumbstick_Y)
++AxisMappings=(AxisName="MotionControllerThumbRight_X",Scale=1.000000,Key=ValveIndex_Right_Thumbstick_X)
++AxisMappings=(AxisName="MotionControllerThumbRight_X",Scale=1.000000,Key=Vive_Right_Trackpad_X)
++AxisMappings=(AxisName="MotionControllerThumbRight_X",Scale=1.000000,Key=OculusTouch_Right_Thumbstick_X)
++AxisMappings=(AxisName="MotionControllerThumbRight_X",Scale=1.000000,Key=MixedReality_Right_Thumbstick_X)
++AxisMappings=(AxisName="ControllerMovementLeft",Scale=1.000000,Key=Vive_Left_Trackpad_Click)
++AxisMappings=(AxisName="ControllerMovementLeft",Scale=1.000000,Key=ValveIndex_Left_Thumbstick_Touch)
++AxisMappings=(AxisName="ControllerMovementLeft",Scale=1.000000,Key=OculusTouch_Left_Thumbstick_Touch)
++AxisMappings=(AxisName="ControllerMovementLeft",Scale=1.000000,Key=MixedReality_Left_Thumbstick_Click)
++AxisMappings=(AxisName="ControllerMovementRight",Scale=1.000000,Key=ValveIndex_Right_Thumbstick_Touch)
++AxisMappings=(AxisName="ControllerMovementRight",Scale=1.000000,Key=Vive_Right_Trackpad_Click)
++AxisMappings=(AxisName="ControllerMovementRight",Scale=1.000000,Key=OculusTouch_Right_Thumbstick_Touch)
++AxisMappings=(AxisName="ControllerMovementRight",Scale=1.000000,Key=MixedReality_Right_Thumbstick_Click)
+DefaultPlayerInputClass=/Script/EnhancedInput.EnhancedPlayerInput
+DefaultInputComponentClass=/Script/EnhancedInput.EnhancedInputComponent
+DefaultTouchInterface=None
+-ConsoleKeys=Tilde
++ConsoleKeys=Tilde
+
+[/Script/OpenXRInput.OpenXRInputSettings]
+MappableInputConfig=/Game/VRE/Input/VREInputConfig.VREInputConfig
+
diff --git a/VIRTUOS_ExpansionPluginTests/Config/DefaultScalability.ini b/VIRTUOS_ExpansionPluginTests/Config/DefaultScalability.ini
new file mode 100644
index 0000000..1107172
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Config/DefaultScalability.ini
@@ -0,0 +1,90 @@
+
+[AntiAliasingQuality@0]
+r.PostProcessAAQuality=0
+
+[AntiAliasingQuality@1]
+r.PostProcessAAQuality=2
+
+[AntiAliasingQuality@2]
+r.PostProcessAAQuality=3
+
+[AntiAliasingQuality@3]
+r.PostProcessAAQuality=3
+
+;-----------------------------------------------------------------------------------------------------------------
+
+[PostProcessQuality@0]
+r.MotionBlurQuality=0
+r.AmbientOcclusionLevels=0
+r.LensFlareQuality=0
+r.SceneColorFringeQuality=0
+r.DepthOfFieldQuality=0
+r.BloomQuality=1
+r.FastBlurThreshold=0
+
+[PostProcessQuality@1]
+r.MotionBlurQuality=0
+r.AmbientOcclusionLevels=0
+r.LensFlareQuality=0
+r.SceneColorFringeQuality=0
+r.DepthOfFieldQuality=0
+r.BloomQuality=1
+r.FastBlurThreshold=0
+
+[PostProcessQuality@2]
+r.MotionBlurQuality=0
+r.AmbientOcclusionLevels=0
+r.LensFlareQuality=0
+r.SceneColorFringeQuality=0
+r.DepthOfFieldQuality=0
+r.BloomQuality=1
+r.FastBlurThreshold=0
+
+[PostProcessQuality@3]
+r.MotionBlurQuality=0
+r.AmbientOcclusionLevels=0
+r.LensFlareQuality=0
+r.SceneColorFringeQuality=0
+r.DepthOfFieldQuality=0
+r.BloomQuality=1
+r.FastBlurThreshold=0
+
+;-----------------------------------------------------------------------------------------------------------------
+
+[TextureQuality@0]
+r.MaxAnisotropy=0
+
+[TextureQuality@1]
+r.MaxAnisotropy=2
+
+[TextureQuality@2]
+r.MaxAnisotropy=4
+
+[TextureQuality@3]
+r.MaxAnisotropy=8
+
+;-----------------------------------------------------------------------------------------------------------------
+
+[EffectsQuality@0]
+r.TranslucencyVolumeBlur=0
+r.TranslucencyLightingVolumeDim=4
+r.SceneColorFormat=2
+r.SSR.Quality=0
+
+[EffectsQuality@1]
+r.TranslucencyVolumeBlur=0
+r.TranslucencyLightingVolumeDim=4
+r.SceneColorFormat=2
+r.SSR.Quality=0
+
+[EffectsQuality@2]
+r.TranslucencyVolumeBlur=0
+r.TranslucencyLightingVolumeDim=4
+r.SceneColorFormat=2
+r.SSR.Quality=0
+
+[EffectsQuality@3]
+r.TranslucencyVolumeBlur=0
+r.TranslucencyLightingVolumeDim=4
+r.SceneColorFormat=2
+r.SSR.Quality=0
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Config/HoloLens/HoloLensEngine.ini b/VIRTUOS_ExpansionPluginTests/Config/HoloLens/HoloLensEngine.ini
new file mode 100644
index 0000000..fa8a9a8
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Config/HoloLens/HoloLensEngine.ini
@@ -0,0 +1,31 @@
+
+
+[/Script/HoloLensPlatformEditor.HoloLensTargetSettings]
+bBuildForEmulation=False
+bBuildForDevice=True
+bUseNameForLogo=True
+bBuildForRetailWindowsStore=False
+bAutoIncrementVersion=False
+bShouldCreateAppInstaller=False
+AppInstallerInstallationURL=
+HoursBetweenUpdateChecks=0
+bEnablePIXProfiling=False
+TileBackgroundColor=(B=64,G=0,R=0,A=255)
+SplashScreenBackgroundColor=(B=64,G=0,R=0,A=255)
++PerCultureResources=(CultureId="",Strings=(PackageDisplayName="",PublisherDisplayName="",PackageDescription="",ApplicationDisplayName="",ApplicationDescription=""),Images=())
+TargetDeviceFamily=Windows.Holographic
+MinimumPlatformVersion=
+MaximumPlatformVersionTested=10.0.19041.0
+MaxTrianglesPerCubicMeter=500.000000
+SpatialMeshingVolumeSize=20.000000
+CompilerVersion=Default
+Windows10SDKVersion=10.0.18362.0
++CapabilityList=internetClientServer
++CapabilityList=privateNetworkClientServer
++Uap2CapabilityList=spatialPerception
+bSetDefaultCapabilities=False
+SpatializationPlugin=
+ReverbPlugin=
+OcclusionPlugin=
+SoundCueCookQualityIndex=-1
+
diff --git a/VIRTUOS_ExpansionPluginTests/Config/SteamVRBindings/gamepad.json b/VIRTUOS_ExpansionPluginTests/Config/SteamVRBindings/gamepad.json
new file mode 100644
index 0000000..fff7db7
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Config/SteamVRBindings/gamepad.json
@@ -0,0 +1,13 @@
+{
+ "name": "Default bindings for Gamepads",
+ "controller_type": "gamepad",
+ "last_edited_by": "UnrealEngine",
+ "bindings":
+ {
+ "/actions/main":
+ {
+ "sources": []
+ }
+ },
+ "description": "Virtual Reality BP Game Template-11100242"
+}
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Config/SteamVRBindings/holographic_controller.json b/VIRTUOS_ExpansionPluginTests/Config/SteamVRBindings/holographic_controller.json
new file mode 100644
index 0000000..b0267cd
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Config/SteamVRBindings/holographic_controller.json
@@ -0,0 +1,229 @@
+{
+ "name": "Default bindings for MixedReality",
+ "controller_type": "holographic_controller",
+ "last_edited_by": "UnrealEngine",
+ "bindings":
+ {
+ "/actions/main":
+ {
+ "sources": [
+ {
+ "mode": "joystick",
+ "path": "/user/hand/left/input/joystick",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/TeleportLeft"
+ }
+ }
+ },
+ {
+ "mode": "joystick",
+ "path": "/user/hand/right/input/joystick",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/TeleportRight"
+ }
+ }
+ },
+ {
+ "mode": "button",
+ "path": "/user/hand/right/input/grip",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/AlternateGripRight"
+ }
+ }
+ },
+ {
+ "mode": "button",
+ "path": "/user/hand/left/input/grip",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/AlternateGripLeft"
+ }
+ }
+ },
+ {
+ "mode": "button",
+ "path": "/user/hand/left/input/trigger",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/PrimaryGripLeft"
+ }
+ }
+ },
+ {
+ "mode": "button",
+ "path": "/user/hand/right/input/trigger",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/PrimaryGripRight"
+ }
+ }
+ },
+ {
+ "mode": "dpad",
+ "path": "/user/hand/right/input/joystick",
+ "parameters":
+ {
+ "sub_mode": "touch"
+ },
+ "inputs":
+ {
+ "south":
+ {
+ "output": "/actions/main/in/LaserBeamRight"
+ }
+ }
+ },
+ {
+ "mode": "dpad",
+ "path": "/user/hand/left/input/joystick",
+ "parameters":
+ {
+ "sub_mode": "touch"
+ },
+ "inputs":
+ {
+ "south":
+ {
+ "output": "/actions/main/in/LaserBeamLeft"
+ }
+ }
+ },
+ {
+ "mode": "button",
+ "path": "/user/hand/left/input/trigger",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/UseHeldObjectLeft"
+ }
+ }
+ },
+ {
+ "mode": "button",
+ "path": "/user/hand/right/input/trigger",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/UseHeldObjectRight"
+ }
+ }
+ },
+ {
+ "mode": "button",
+ "path": "/user/hand/left/input/trigger",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/TriggerInteractionLEft"
+ }
+ }
+ },
+ {
+ "mode": "button",
+ "path": "/user/hand/right/input/trigger",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/TriggerInteractionRight"
+ }
+ }
+ },
+ {
+ "mode": "joystick",
+ "path": "/user/hand/left/input/joystick",
+ "inputs":
+ {
+ "position":
+ {
+ "output": "/actions/main/in/MotionControllerThumbLeft_X,MotionControllerThumbLeft_Y X Y_axis2d"
+ }
+ }
+ },
+ {
+ "mode": "joystick",
+ "path": "/user/hand/right/input/joystick",
+ "inputs":
+ {
+ "position":
+ {
+ "output": "/actions/main/in/MotionControllerThumbRight_X,MotionControllerThumbRight_Y X Y_axis2d"
+ }
+ }
+ },
+ {
+ "mode": "scalar_constant",
+ "path": "/user/hand/left/input/joystick",
+ "inputs":
+ {
+ "value":
+ {
+ "output": "/actions/main/in/ControllerMovementLeft axis"
+ }
+ }
+ },
+ {
+ "mode": "scalar_constant",
+ "path": "/user/hand/right/input/joystick",
+ "inputs":
+ {
+ "value":
+ {
+ "output": "/actions/main/in/ControllerMovementRight axis"
+ }
+ }
+ }
+ ],
+ "poses": [
+ {
+ "output": "/actions/main/in/controllerleft",
+ "path": "/user/hand/left/pose/raw",
+ "requirement": "optional"
+ },
+ {
+ "output": "/actions/main/in/controllerright",
+ "path": "/user/hand/right/pose/raw"
+ }
+ ],
+ "skeleton": [
+ {
+ "output": "/actions/main/in/skeletonleft",
+ "path": "/user/hand/left/input/skeleton/left"
+ },
+ {
+ "output": "/actions/main/in/skeletonright",
+ "path": "/user/hand/right/input/skeleton/right"
+ }
+ ],
+ "haptics": [
+ {
+ "output": "/actions/main/out/vibrateleft",
+ "path": "/user/hand/left/output/haptic"
+ },
+ {
+ "output": "/actions/main/out/vibrateright",
+ "path": "/user/hand/right/output/haptic"
+ }
+ ]
+ }
+ },
+ "description": "Virtual Reality BP Game Template-11100242"
+}
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Config/SteamVRBindings/indexhmd.json b/VIRTUOS_ExpansionPluginTests/Config/SteamVRBindings/indexhmd.json
new file mode 100644
index 0000000..91f92fd
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Config/SteamVRBindings/indexhmd.json
@@ -0,0 +1,13 @@
+{
+ "name": "Default bindings for Valve Index Headset",
+ "controller_type": "indexhmd",
+ "last_edited_by": "UnrealEngine",
+ "bindings":
+ {
+ "/actions/main":
+ {
+ "sources": []
+ }
+ },
+ "description": "Virtual Reality BP Game Template-11100242"
+}
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Config/SteamVRBindings/knuckles.json b/VIRTUOS_ExpansionPluginTests/Config/SteamVRBindings/knuckles.json
new file mode 100644
index 0000000..efee109
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Config/SteamVRBindings/knuckles.json
@@ -0,0 +1,200 @@
+{
+ "action_manifest_version" : 0,
+ "alias_info" : {},
+ "app_key" : "application.generated.ue.virtual-reality-bp-game-template-14627614.ue4editor-win64-debuggame.exe",
+ "bindings" : {
+ "/actions/main" : {
+ "haptics" : [
+ {
+ "output" : "/actions/main/out/vibrateleft",
+ "path" : "/user/hand/left/output/haptic"
+ },
+ {
+ "output" : "/actions/main/out/vibrateright",
+ "path" : "/user/hand/right/output/haptic"
+ }
+ ],
+ "poses" : [
+ {
+ "output" : "/actions/main/in/controllerleft",
+ "path" : "/user/hand/left/pose/raw",
+ "requirement" : "optional"
+ },
+ {
+ "output" : "/actions/main/in/controllerright",
+ "path" : "/user/hand/right/pose/raw"
+ }
+ ],
+ "skeleton" : [
+ {
+ "output" : "/actions/main/in/skeletonleft",
+ "path" : "/user/hand/left/input/skeleton/left"
+ },
+ {
+ "output" : "/actions/main/in/skeletonright",
+ "path" : "/user/hand/right/input/skeleton/right"
+ }
+ ],
+ "sources" : [
+ {
+ "inputs" : {
+ "east" : {
+ "output" : "/actions/main/in/teleportleft"
+ },
+ "north" : {
+ "output" : "/actions/main/in/teleportleft"
+ },
+ "south" : {
+ "output" : "/actions/main/in/teleportleft"
+ },
+ "west" : {
+ "output" : "/actions/main/in/TeleportLeft"
+ }
+ },
+ "mode" : "dpad",
+ "parameters" : {
+ "sub_mode" : "touch"
+ },
+ "path" : "/user/hand/left/input/thumbstick"
+ },
+ {
+ "inputs" : {
+ "east" : {
+ "output" : "/actions/main/in/teleportright"
+ },
+ "north" : {
+ "output" : "/actions/main/in/teleportright"
+ },
+ "south" : {
+ "output" : "/actions/main/in/teleportright"
+ },
+ "west" : {
+ "output" : "/actions/main/in/teleportright"
+ }
+ },
+ "mode" : "dpad",
+ "parameters" : {
+ "sub_mode" : "touch"
+ },
+ "path" : "/user/hand/right/input/thumbstick"
+ },
+ {
+ "inputs" : {
+ "click" : {
+ "output" : "/actions/main/in/AlternateGripRight"
+ }
+ },
+ "mode" : "button",
+ "path" : "/user/hand/right/input/a"
+ },
+ {
+ "inputs" : {
+ "click" : {
+ "output" : "/actions/main/in/AlternateGripLeft"
+ }
+ },
+ "mode" : "button",
+ "path" : "/user/hand/left/input/a"
+ },
+ {
+ "inputs" : {
+ "click" : {
+ "output" : "/actions/main/in/LaserBeamRight"
+ }
+ },
+ "mode" : "button",
+ "path" : "/user/hand/right/input/b"
+ },
+ {
+ "inputs" : {
+ "click" : {
+ "output" : "/actions/main/in/LaserBeamLeft"
+ }
+ },
+ "mode" : "button",
+ "path" : "/user/hand/left/input/b"
+ },
+ {
+ "inputs" : {
+ "click" : {
+ "output" : "/actions/main/in/UseHeldObjectLeft"
+ }
+ },
+ "mode" : "trigger",
+ "path" : "/user/hand/left/input/trigger"
+ },
+ {
+ "inputs" : {
+ "click" : {
+ "output" : "/actions/main/in/UseHeldObjectRight"
+ }
+ },
+ "mode" : "trigger",
+ "path" : "/user/hand/right/input/trigger"
+ },
+ {
+ "inputs" : {
+ "position" : {
+ "output" : "/actions/main/in/motioncontrollerthumbleft_x,motioncontrollerthumbleft_y x y_axis2d"
+ },
+ "touch" : {
+ "output" : "/actions/main/in/controllermovementleft axis"
+ }
+ },
+ "mode" : "joystick",
+ "parameters" : {
+ "deadzone_pct" : "10"
+ },
+ "path" : "/user/hand/left/input/thumbstick"
+ },
+ {
+ "inputs" : {
+ "position" : {
+ "output" : "/actions/main/in/MotionControllerThumbRight_X,MotionControllerThumbRight_Y X Y_axis2d"
+ },
+ "touch" : {
+ "output" : "/actions/main/in/controllermovementright axis"
+ }
+ },
+ "mode" : "joystick",
+ "parameters" : {
+ "deadzone_pct" : "10"
+ },
+ "path" : "/user/hand/right/input/thumbstick"
+ },
+ {
+ "inputs" : {
+ "click" : {
+ "output" : "/actions/main/in/primarygripleft"
+ }
+ },
+ "mode" : "button",
+ "parameters" : {
+ "click_activate_threshold" : "1",
+ "click_deactivate_threshold" : "1"
+ },
+ "path" : "/user/hand/left/input/grip"
+ },
+ {
+ "inputs" : {
+ "click" : {
+ "output" : "/actions/main/in/primarygripright"
+ }
+ },
+ "mode" : "button",
+ "parameters" : {
+ "click_activate_threshold" : "1",
+ "click_deactivate_threshold" : "1"
+ },
+ "path" : "/user/hand/right/input/grip"
+ }
+ ]
+ }
+ },
+ "category" : "steamvr_input",
+ "controller_type" : "knuckles",
+ "description" : "Virtual Reality BP Game Template-14627614",
+ "name" : "Default bindings for ValveIndex",
+ "options" : {},
+ "simulated_actions" : []
+}
diff --git a/VIRTUOS_ExpansionPluginTests/Config/SteamVRBindings/oculus_touch.json b/VIRTUOS_ExpansionPluginTests/Config/SteamVRBindings/oculus_touch.json
new file mode 100644
index 0000000..22353e3
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Config/SteamVRBindings/oculus_touch.json
@@ -0,0 +1,221 @@
+{
+ "name": "Default bindings for OculusTouch",
+ "controller_type": "oculus_touch",
+ "last_edited_by": "UnrealEngine",
+ "bindings":
+ {
+ "/actions/main":
+ {
+ "sources": [
+ {
+ "mode": "joystick",
+ "path": "/user/hand/left/input/joystick",
+ "inputs":
+ {
+ "touch":
+ {
+ "output": "/actions/main/in/TeleportLeft"
+ }
+ }
+ },
+ {
+ "mode": "joystick",
+ "path": "/user/hand/right/input/joystick",
+ "inputs":
+ {
+ "touch":
+ {
+ "output": "/actions/main/in/TeleportRight"
+ }
+ }
+ },
+ {
+ "mode": "button",
+ "path": "/user/hand/right/input/a",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/AlternateGripRight"
+ }
+ }
+ },
+ {
+ "mode": "button",
+ "path": "/user/hand/left/input/x",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/AlternateGripLeft"
+ }
+ }
+ },
+ {
+ "mode": "button",
+ "path": "/user/hand/left/input/grip",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/PrimaryGripLeft"
+ }
+ }
+ },
+ {
+ "mode": "button",
+ "path": "/user/hand/right/input/grip",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/PrimaryGripRight"
+ }
+ }
+ },
+ {
+ "mode": "button",
+ "path": "/user/hand/right/input/b",
+ "inputs":
+ {
+ "touch":
+ {
+ "output": "/actions/main/in/LaserBeamRight"
+ }
+ }
+ },
+ {
+ "mode": "button",
+ "path": "/user/hand/left/input/y",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/LaserBeamLeft"
+ }
+ }
+ },
+ {
+ "mode": "button",
+ "path": "/user/hand/left/input/trigger",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/UseHeldObjectLeft"
+ }
+ }
+ },
+ {
+ "mode": "button",
+ "path": "/user/hand/right/input/trigger",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/UseHeldObjectRight"
+ }
+ }
+ },
+ {
+ "mode": "button",
+ "path": "/user/hand/left/input/trigger",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/TriggerInteractionLEft"
+ }
+ }
+ },
+ {
+ "mode": "button",
+ "path": "/user/hand/right/input/trigger",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/TriggerInteractionRight"
+ }
+ }
+ },
+ {
+ "mode": "joystick",
+ "path": "/user/hand/left/input/joystick",
+ "inputs":
+ {
+ "position":
+ {
+ "output": "/actions/main/in/MotionControllerThumbLeft_X,MotionControllerThumbLeft_Y X Y_axis2d"
+ }
+ }
+ },
+ {
+ "mode": "joystick",
+ "path": "/user/hand/right/input/joystick",
+ "inputs":
+ {
+ "position":
+ {
+ "output": "/actions/main/in/MotionControllerThumbRight_X,MotionControllerThumbRight_Y X Y_axis2d"
+ }
+ }
+ },
+ {
+ "mode": "scalar_constant",
+ "path": "/user/hand/left/input/joystick",
+ "inputs":
+ {
+ "value":
+ {
+ "output": "/actions/main/in/ControllerMovementLeft axis"
+ }
+ }
+ },
+ {
+ "mode": "scalar_constant",
+ "path": "/user/hand/right/input/joystick",
+ "inputs":
+ {
+ "value":
+ {
+ "output": "/actions/main/in/ControllerMovementRight axis"
+ }
+ }
+ }
+ ],
+ "poses": [
+ {
+ "output": "/actions/main/in/controllerleft",
+ "path": "/user/hand/left/pose/raw",
+ "requirement": "optional"
+ },
+ {
+ "output": "/actions/main/in/controllerright",
+ "path": "/user/hand/right/pose/raw"
+ }
+ ],
+ "skeleton": [
+ {
+ "output": "/actions/main/in/skeletonleft",
+ "path": "/user/hand/left/input/skeleton/left"
+ },
+ {
+ "output": "/actions/main/in/skeletonright",
+ "path": "/user/hand/right/input/skeleton/right"
+ }
+ ],
+ "haptics": [
+ {
+ "output": "/actions/main/out/vibrateleft",
+ "path": "/user/hand/left/output/haptic"
+ },
+ {
+ "output": "/actions/main/out/vibrateright",
+ "path": "/user/hand/right/output/haptic"
+ }
+ ]
+ }
+ },
+ "description": "Virtual Reality BP Game Template-11100242"
+}
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Config/SteamVRBindings/rift.json b/VIRTUOS_ExpansionPluginTests/Config/SteamVRBindings/rift.json
new file mode 100644
index 0000000..d1af9fe
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Config/SteamVRBindings/rift.json
@@ -0,0 +1,13 @@
+{
+ "name": "Default bindings for Rift Headset",
+ "controller_type": "rift",
+ "last_edited_by": "UnrealEngine",
+ "bindings":
+ {
+ "/actions/main":
+ {
+ "sources": []
+ }
+ },
+ "description": "Virtual Reality BP Game Template-11100242"
+}
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Config/SteamVRBindings/steamvr_manifest.json b/VIRTUOS_ExpansionPluginTests/Config/SteamVRBindings/steamvr_manifest.json
new file mode 100644
index 0000000..63a7eb7
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Config/SteamVRBindings/steamvr_manifest.json
@@ -0,0 +1,270 @@
+{
+ "actions": [
+ {
+ "name": "/actions/main/in/controllerleft",
+ "type": "pose",
+ "requirement": "optional"
+ },
+ {
+ "name": "/actions/main/in/controllerright",
+ "type": "pose",
+ "requirement": "optional"
+ },
+ {
+ "name": "/actions/main/in/special1",
+ "type": "pose",
+ "requirement": "optional"
+ },
+ {
+ "name": "/actions/main/in/special2",
+ "type": "pose",
+ "requirement": "optional"
+ },
+ {
+ "name": "/actions/main/in/special3",
+ "type": "pose",
+ "requirement": "optional"
+ },
+ {
+ "name": "/actions/main/in/special4",
+ "type": "pose",
+ "requirement": "optional"
+ },
+ {
+ "name": "/actions/main/in/special5",
+ "type": "pose",
+ "requirement": "optional"
+ },
+ {
+ "name": "/actions/main/in/special6",
+ "type": "pose",
+ "requirement": "optional"
+ },
+ {
+ "name": "/actions/main/in/special7",
+ "type": "pose",
+ "requirement": "optional"
+ },
+ {
+ "name": "/actions/main/in/special8",
+ "type": "pose",
+ "requirement": "optional"
+ },
+ {
+ "name": "/actions/main/in/skeletonleft",
+ "type": "skeleton",
+ "skeleton": "/skeleton/hand/left",
+ "requirement": "optional"
+ },
+ {
+ "name": "/actions/main/in/skeletonright",
+ "type": "skeleton",
+ "skeleton": "/skeleton/hand/right",
+ "requirement": "optional"
+ },
+ {
+ "name": "/actions/main/in/open_console",
+ "type": "boolean",
+ "requirement": "optional"
+ },
+ {
+ "name": "/actions/main/out/vibrateleft",
+ "type": "vibration",
+ "requirement": "optional"
+ },
+ {
+ "name": "/actions/main/out/vibrateright",
+ "type": "vibration",
+ "requirement": "optional"
+ },
+ {
+ "name": "/actions/main/in/TeleportLeft",
+ "type": "boolean"
+ },
+ {
+ "name": "/actions/main/in/TeleportRight",
+ "type": "boolean"
+ },
+ {
+ "name": "/actions/main/in/AlternateGripRight",
+ "type": "boolean"
+ },
+ {
+ "name": "/actions/main/in/AlternateGripLeft",
+ "type": "boolean"
+ },
+ {
+ "name": "/actions/main/in/PrimaryGripLeft",
+ "type": "boolean"
+ },
+ {
+ "name": "/actions/main/in/PrimaryGripRight",
+ "type": "boolean"
+ },
+ {
+ "name": "/actions/main/in/LaserBeamRight",
+ "type": "boolean"
+ },
+ {
+ "name": "/actions/main/in/LaserBeamLeft",
+ "type": "boolean"
+ },
+ {
+ "name": "/actions/main/in/UseHeldObjectLeft",
+ "type": "boolean"
+ },
+ {
+ "name": "/actions/main/in/UseHeldObjectRight",
+ "type": "boolean"
+ },
+ {
+ "name": "/actions/main/in/MotionControllerThumbLeft_X,MotionControllerThumbLeft_Y X Y_axis2d",
+ "type": "vector2"
+ },
+ {
+ "name": "/actions/main/in/MotionControllerThumbRight_X,MotionControllerThumbRight_Y X Y_axis2d",
+ "type": "vector2"
+ },
+ {
+ "name": "/actions/main/in/ControllerMovementLeft axis",
+ "type": "vector1"
+ },
+ {
+ "name": "/actions/main/in/ControllerMovementRight axis",
+ "type": "vector1"
+ }
+ ],
+ "action_sets": [
+ {
+ "name": "/actions/main",
+ "usage": "leftright"
+ }
+ ],
+ "default_bindings": [
+ {
+ "controller_type": "gamepad",
+ "binding_url": "gamepad.json"
+ },
+ {
+ "controller_type": "holographic_controller",
+ "binding_url": "holographic_controller.json"
+ },
+ {
+ "controller_type": "indexhmd",
+ "binding_url": "indexhmd.json"
+ },
+ {
+ "controller_type": "knuckles",
+ "binding_url": "knuckles.json"
+ },
+ {
+ "controller_type": "oculus_touch",
+ "binding_url": "oculus_touch.json"
+ },
+ {
+ "controller_type": "rift",
+ "binding_url": "rift.json"
+ },
+ {
+ "controller_type": "vive",
+ "binding_url": "vive.json"
+ },
+ {
+ "controller_type": "vive_controller",
+ "binding_url": "vive_controller.json"
+ },
+ {
+ "controller_type": "vive_cosmos_controller",
+ "binding_url": "vive_cosmos_controller.json"
+ },
+ {
+ "controller_type": "vive_pro",
+ "binding_url": "vive_pro.json"
+ },
+ {
+ "controller_type": "vive_tracker_camera",
+ "binding_url": "vive_tracker_camera.json"
+ },
+ {
+ "controller_type": "knuckles",
+ "binding_url": "knuckles.json"
+ },
+ {
+ "controller_type": "vive_controller",
+ "binding_url": "vive_controller.json"
+ },
+ {
+ "controller_type": "vive_cosmos_controller",
+ "binding_url": "vive_cosmos_controller.json"
+ },
+ {
+ "controller_type": "oculus_touch",
+ "binding_url": "oculus_touch.json"
+ },
+ {
+ "controller_type": "holographic_controller",
+ "binding_url": "holographic_controller.json"
+ },
+ {
+ "controller_type": "indexhmd",
+ "binding_url": "indexhmd.json"
+ },
+ {
+ "controller_type": "vive",
+ "binding_url": "vive.json"
+ },
+ {
+ "controller_type": "vive_pro",
+ "binding_url": "vive_pro.json"
+ },
+ {
+ "controller_type": "rift",
+ "binding_url": "rift.json"
+ },
+ {
+ "controller_type": "vive_tracker_camera",
+ "binding_url": "vive_tracker_camera.json"
+ },
+ {
+ "controller_type": "gamepad",
+ "binding_url": "gamepad.json"
+ }
+ ],
+ "localization": [
+ {
+ "language_tag": "en_us",
+ "/actions/main/in/controllerleft": "Left Controller [Pose]",
+ "/actions/main/in/controllerright": "Right Controller [Pose]",
+ "/actions/main/in/special1": "Special 1 [Tracker]",
+ "/actions/main/in/special2": "Special 2 [Tracker]",
+ "/actions/main/in/special3": "Special 3 [Tracker]",
+ "/actions/main/in/special4": "Special 4 [Tracker]",
+ "/actions/main/in/special5": "Special 5 [Tracker]",
+ "/actions/main/in/special6": "Special 6 [Tracker]",
+ "/actions/main/in/special7": "Special 7 [Tracker]",
+ "/actions/main/in/special8": "Special 8 [Tracker]",
+ "/actions/main/in/skeletonleft": "Skeleton (Left)",
+ "/actions/main/in/skeletonright": "Skeleton (Right)",
+ "/actions/main/in/open_console": "Open Console",
+ "/actions/main/out/vibrateleft": "Haptic (Left)",
+ "/actions/main/out/vibrateright": "Haptic (Right)",
+ "/actions/main/in/TeleportLeft": "TeleportLeft",
+ "/actions/main/in/TeleportRight": "TeleportRight",
+ "/actions/main/in/AlternateGripRight": "AlternateGripRight",
+ "/actions/main/in/AlternateGripLeft": "AlternateGripLeft",
+ "/actions/main/in/PrimaryGripLeft": "PrimaryGripLeft",
+ "/actions/main/in/PrimaryGripRight": "PrimaryGripRight",
+ "/actions/main/in/LaserBeamRight": "LaserBeamRight",
+ "/actions/main/in/LaserBeamLeft": "LaserBeamLeft",
+ "/actions/main/in/UseHeldObjectLeft": "UseHeldObjectLeft",
+ "/actions/main/in/UseHeldObjectRight": "UseHeldObjectRight",
+ "/actions/main/in/TriggerInteractionLEft": "TriggerInteractionLEft",
+ "/actions/main/in/TriggerInteractionRight": "TriggerInteractionRight",
+ "/actions/main/in/MotionControllerThumbLeft_X,MotionControllerThumbLeft_Y X Y_axis2d": "MotionControllerThumbLeft",
+ "/actions/main/in/MotionControllerThumbRight_X,MotionControllerThumbRight_Y X Y_axis2d": "MotionControllerThumbRight",
+ "/actions/main/in/ControllerMovementLeft axis": "ControllerMovementLeft",
+ "/actions/main/in/ControllerMovementRight axis": "ControllerMovementRight",
+ "/actions/main": "Main Game Actions"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Config/SteamVRBindings/vive.json b/VIRTUOS_ExpansionPluginTests/Config/SteamVRBindings/vive.json
new file mode 100644
index 0000000..2f8cb4e
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Config/SteamVRBindings/vive.json
@@ -0,0 +1,13 @@
+{
+ "name": "Default bindings for Vive Headset",
+ "controller_type": "vive",
+ "last_edited_by": "UnrealEngine",
+ "bindings":
+ {
+ "/actions/main":
+ {
+ "sources": []
+ }
+ },
+ "description": "Virtual Reality BP Game Template-11100242"
+}
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Config/SteamVRBindings/vive_controller.json b/VIRTUOS_ExpansionPluginTests/Config/SteamVRBindings/vive_controller.json
new file mode 100644
index 0000000..f87239d
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Config/SteamVRBindings/vive_controller.json
@@ -0,0 +1,221 @@
+{
+ "name": "Default bindings for Vive",
+ "controller_type": "vive_controller",
+ "last_edited_by": "UnrealEngine",
+ "bindings":
+ {
+ "/actions/main":
+ {
+ "sources": [
+ {
+ "mode": "trackpad",
+ "path": "/user/hand/left/input/trackpad",
+ "inputs":
+ {
+ "touch":
+ {
+ "output": "/actions/main/in/TeleportLeft"
+ }
+ }
+ },
+ {
+ "mode": "trackpad",
+ "path": "/user/hand/right/input/trackpad",
+ "inputs":
+ {
+ "touch":
+ {
+ "output": "/actions/main/in/TeleportRight"
+ }
+ }
+ },
+ {
+ "mode": "button",
+ "path": "/user/hand/right/input/grip",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/AlternateGripRight"
+ }
+ }
+ },
+ {
+ "mode": "button",
+ "path": "/user/hand/left/input/grip",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/AlternateGripLeft"
+ }
+ }
+ },
+ {
+ "mode": "button",
+ "path": "/user/hand/left/input/trigger",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/PrimaryGripLeft"
+ }
+ }
+ },
+ {
+ "mode": "button",
+ "path": "/user/hand/right/input/trigger",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/PrimaryGripRight"
+ }
+ }
+ },
+ {
+ "mode": "button",
+ "path": "/user/hand/right/input/application_menu",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/LaserBeamRight"
+ }
+ }
+ },
+ {
+ "mode": "button",
+ "path": "/user/hand/left/input/application_menu",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/LaserBeamLeft"
+ }
+ }
+ },
+ {
+ "mode": "button",
+ "path": "/user/hand/left/input/trigger",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/UseHeldObjectLeft"
+ }
+ }
+ },
+ {
+ "mode": "button",
+ "path": "/user/hand/right/input/trigger",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/UseHeldObjectRight"
+ }
+ }
+ },
+ {
+ "mode": "button",
+ "path": "/user/hand/left/input/trigger",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/TriggerInteractionLEft"
+ }
+ }
+ },
+ {
+ "mode": "button",
+ "path": "/user/hand/right/input/trigger",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/TriggerInteractionRight"
+ }
+ }
+ },
+ {
+ "mode": "trackpad",
+ "path": "/user/hand/left/input/trackpad",
+ "inputs":
+ {
+ "position":
+ {
+ "output": "/actions/main/in/MotionControllerThumbLeft_X,MotionControllerThumbLeft_Y X Y_axis2d"
+ }
+ }
+ },
+ {
+ "mode": "trackpad",
+ "path": "/user/hand/right/input/trackpad",
+ "inputs":
+ {
+ "position":
+ {
+ "output": "/actions/main/in/MotionControllerThumbRight_X,MotionControllerThumbRight_Y X Y_axis2d"
+ }
+ }
+ },
+ {
+ "mode": "scalar_constant",
+ "path": "/user/hand/left/input/trackpad",
+ "inputs":
+ {
+ "value":
+ {
+ "output": "/actions/main/in/ControllerMovementLeft axis"
+ }
+ }
+ },
+ {
+ "mode": "scalar_constant",
+ "path": "/user/hand/right/input/trackpad",
+ "inputs":
+ {
+ "value":
+ {
+ "output": "/actions/main/in/ControllerMovementRight axis"
+ }
+ }
+ }
+ ],
+ "poses": [
+ {
+ "output": "/actions/main/in/controllerleft",
+ "path": "/user/hand/left/pose/raw",
+ "requirement": "optional"
+ },
+ {
+ "output": "/actions/main/in/controllerright",
+ "path": "/user/hand/right/pose/raw"
+ }
+ ],
+ "skeleton": [
+ {
+ "output": "/actions/main/in/skeletonleft",
+ "path": "/user/hand/left/input/skeleton/left"
+ },
+ {
+ "output": "/actions/main/in/skeletonright",
+ "path": "/user/hand/right/input/skeleton/right"
+ }
+ ],
+ "haptics": [
+ {
+ "output": "/actions/main/out/vibrateleft",
+ "path": "/user/hand/left/output/haptic"
+ },
+ {
+ "output": "/actions/main/out/vibrateright",
+ "path": "/user/hand/right/output/haptic"
+ }
+ ]
+ }
+ },
+ "description": "Virtual Reality BP Game Template-11100242"
+}
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Config/SteamVRBindings/vive_cosmos_controller.json b/VIRTUOS_ExpansionPluginTests/Config/SteamVRBindings/vive_cosmos_controller.json
new file mode 100644
index 0000000..1b2ac33
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Config/SteamVRBindings/vive_cosmos_controller.json
@@ -0,0 +1,221 @@
+{
+ "name": "Default bindings for Cosmos",
+ "controller_type": "vive_cosmos_controller",
+ "last_edited_by": "UnrealEngine",
+ "bindings":
+ {
+ "/actions/main":
+ {
+ "sources": [
+ {
+ "mode": "joystick",
+ "path": "/user/hand/left/input/joystick",
+ "inputs":
+ {
+ "touch":
+ {
+ "output": "/actions/main/in/TeleportLeft"
+ }
+ }
+ },
+ {
+ "mode": "joystick",
+ "path": "/user/hand/right/input/joystick",
+ "inputs":
+ {
+ "touch":
+ {
+ "output": "/actions/main/in/TeleportRight"
+ }
+ }
+ },
+ {
+ "mode": "button",
+ "path": "/user/hand/right/input/grip",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/AlternateGripRight"
+ }
+ }
+ },
+ {
+ "mode": "button",
+ "path": "/user/hand/left/input/grip",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/AlternateGripLeft"
+ }
+ }
+ },
+ {
+ "mode": "button",
+ "path": "/user/hand/left/input/trigger",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/PrimaryGripLeft"
+ }
+ }
+ },
+ {
+ "mode": "button",
+ "path": "/user/hand/right/input/trigger",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/PrimaryGripRight"
+ }
+ }
+ },
+ {
+ "mode": "button",
+ "path": "/user/hand/right/input/b",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/LaserBeamRight"
+ }
+ }
+ },
+ {
+ "mode": "button",
+ "path": "/user/hand/left/input/y",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/LaserBeamLeft"
+ }
+ }
+ },
+ {
+ "mode": "button",
+ "path": "/user/hand/left/input/trigger",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/UseHeldObjectLeft"
+ }
+ }
+ },
+ {
+ "mode": "button",
+ "path": "/user/hand/right/input/trigger",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/UseHeldObjectRight"
+ }
+ }
+ },
+ {
+ "mode": "button",
+ "path": "/user/hand/left/input/trigger",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/TriggerInteractionLEft"
+ }
+ }
+ },
+ {
+ "mode": "button",
+ "path": "/user/hand/right/input/trigger",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/TriggerInteractionRight"
+ }
+ }
+ },
+ {
+ "mode": "joystick",
+ "path": "/user/hand/left/input/joystick",
+ "inputs":
+ {
+ "position":
+ {
+ "output": "/actions/main/in/MotionControllerThumbLeft_X,MotionControllerThumbLeft_Y X Y_axis2d"
+ }
+ }
+ },
+ {
+ "mode": "joystick",
+ "path": "/user/hand/right/input/joystick",
+ "inputs":
+ {
+ "position":
+ {
+ "output": "/actions/main/in/MotionControllerThumbRight_X,MotionControllerThumbRight_Y X Y_axis2d"
+ }
+ }
+ },
+ {
+ "mode": "scalar_constant",
+ "path": "/user/hand/left/input/joystick",
+ "inputs":
+ {
+ "value":
+ {
+ "output": "/actions/main/in/ControllerMovementLeft axis"
+ }
+ }
+ },
+ {
+ "mode": "scalar_constant",
+ "path": "/user/hand/right/input/joystick",
+ "inputs":
+ {
+ "value":
+ {
+ "output": "/actions/main/in/ControllerMovementRight axis"
+ }
+ }
+ }
+ ],
+ "poses": [
+ {
+ "output": "/actions/main/in/controllerleft",
+ "path": "/user/hand/left/pose/raw",
+ "requirement": "optional"
+ },
+ {
+ "output": "/actions/main/in/controllerright",
+ "path": "/user/hand/right/pose/raw"
+ }
+ ],
+ "skeleton": [
+ {
+ "output": "/actions/main/in/skeletonleft",
+ "path": "/user/hand/left/input/skeleton/left"
+ },
+ {
+ "output": "/actions/main/in/skeletonright",
+ "path": "/user/hand/right/input/skeleton/right"
+ }
+ ],
+ "haptics": [
+ {
+ "output": "/actions/main/out/vibrateleft",
+ "path": "/user/hand/left/output/haptic"
+ },
+ {
+ "output": "/actions/main/out/vibrateright",
+ "path": "/user/hand/right/output/haptic"
+ }
+ ]
+ }
+ },
+ "description": "Virtual Reality BP Game Template-11100242"
+}
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Config/SteamVRBindings/vive_pro.json b/VIRTUOS_ExpansionPluginTests/Config/SteamVRBindings/vive_pro.json
new file mode 100644
index 0000000..5a7ce10
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Config/SteamVRBindings/vive_pro.json
@@ -0,0 +1,13 @@
+{
+ "name": "Default bindings for Vive Pro Headset",
+ "controller_type": "vive_pro",
+ "last_edited_by": "UnrealEngine",
+ "bindings":
+ {
+ "/actions/main":
+ {
+ "sources": []
+ }
+ },
+ "description": "Virtual Reality BP Game Template-11100242"
+}
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Config/SteamVRBindings/vive_tracker_camera.json b/VIRTUOS_ExpansionPluginTests/Config/SteamVRBindings/vive_tracker_camera.json
new file mode 100644
index 0000000..d25fce8
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Config/SteamVRBindings/vive_tracker_camera.json
@@ -0,0 +1,232 @@
+{
+ "name": "Default bindings for Vive Trackers",
+ "controller_type": "vive_tracker_camera",
+ "last_edited_by": "UnrealEngine",
+ "bindings":
+ {
+ "/actions/main":
+ {
+ "sources": [
+ {
+ "mode": "trackpad",
+ "path": "/user/hand/left/input/trackpad",
+ "inputs":
+ {
+ "touch":
+ {
+ "output": "/actions/main/in/TeleportLeft"
+ }
+ }
+ },
+ {
+ "mode": "trackpad",
+ "path": "/user/hand/right/input/trackpad",
+ "inputs":
+ {
+ "touch":
+ {
+ "output": "/actions/main/in/TeleportRight"
+ }
+ }
+ },
+ {
+ "mode": "button",
+ "path": "/user/hand/right/input/grip",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/AlternateGripRight"
+ }
+ }
+ },
+ {
+ "mode": "button",
+ "path": "/user/hand/left/input/grip",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/AlternateGripLeft"
+ }
+ }
+ },
+ {
+ "mode": "button",
+ "path": "/user/hand/left/input/trigger",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/PrimaryGripLeft"
+ }
+ }
+ },
+ {
+ "mode": "button",
+ "path": "/user/hand/right/input/trigger",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/PrimaryGripRight"
+ }
+ }
+ },
+ {
+ "mode": "button",
+ "path": "/user/hand/right/input/application_menu",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/LaserBeamRight"
+ }
+ }
+ },
+ {
+ "mode": "button",
+ "path": "/user/hand/left/input/application_menu",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/LaserBeamLeft"
+ }
+ }
+ },
+ {
+ "mode": "button",
+ "path": "/user/hand/left/input/trigger",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/UseHeldObjectLeft"
+ }
+ }
+ },
+ {
+ "mode": "button",
+ "path": "/user/hand/right/input/trigger",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/UseHeldObjectRight"
+ }
+ }
+ },
+ {
+ "mode": "button",
+ "path": "/user/hand/left/input/trigger",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/TriggerInteractionLEft"
+ }
+ }
+ },
+ {
+ "mode": "button",
+ "path": "/user/hand/right/input/trigger",
+ "inputs":
+ {
+ "click":
+ {
+ "output": "/actions/main/in/TriggerInteractionRight"
+ }
+ }
+ },
+ {
+ "mode": "trackpad",
+ "path": "/user/hand/left/input/trackpad",
+ "inputs":
+ {
+ "position":
+ {
+ "output": "/actions/main/in/MotionControllerThumbLeft_X,MotionControllerThumbLeft_Y X Y_axis2d"
+ }
+ }
+ },
+ {
+ "mode": "trackpad",
+ "path": "/user/hand/right/input/trackpad",
+ "inputs":
+ {
+ "position":
+ {
+ "output": "/actions/main/in/MotionControllerThumbRight_X,MotionControllerThumbRight_Y X Y_axis2d"
+ }
+ }
+ },
+ {
+ "mode": "scalar_constant",
+ "path": "/user/hand/left/input/trackpad",
+ "inputs":
+ {
+ "value":
+ {
+ "output": "/actions/main/in/ControllerMovementLeft axis"
+ }
+ }
+ },
+ {
+ "mode": "scalar_constant",
+ "path": "/user/hand/right/input/trackpad",
+ "inputs":
+ {
+ "value":
+ {
+ "output": "/actions/main/in/ControllerMovementRight axis"
+ }
+ }
+ }
+ ],
+ "poses": [
+ {
+ "output": "/actions/main/in/special1",
+ "path": "/user/hand/left/pose/back",
+ "requirement": "optional"
+ },
+ {
+ "output": "/actions/main/in/special2",
+ "path": "/user/hand/right/pose/back",
+ "requirement": "optional"
+ },
+ {
+ "output": "/actions/main/in/special3",
+ "path": "/user/hand/left/pose/front",
+ "requirement": "optional"
+ },
+ {
+ "output": "/actions/main/in/special4",
+ "path": "/user/hand/right/pose/front",
+ "requirement": "optional"
+ },
+ {
+ "output": "/actions/main/in/special5",
+ "path": "/user/hand/left/pose/frontandrolled",
+ "requirement": "optional"
+ },
+ {
+ "output": "/actions/main/in/special6",
+ "path": "/user/hand/right/pose/frontandrolled",
+ "requirement": "optional"
+ },
+ {
+ "output": "/actions/main/in/special7",
+ "path": "/user/hand/left/pose/pistolgrip",
+ "requirement": "optional"
+ },
+ {
+ "output": "/actions/main/in/special8",
+ "path": "/user/hand/right/pose/pistolgrip",
+ "requirement": "optional"
+ }
+ ]
+ }
+ },
+ "description": "Virtual Reality BP Game Template-11100242"
+}
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/BP_Teleport_Controller.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/BP_Teleport_Controller.uasset
new file mode 100644
index 0000000..684f55a
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/BP_Teleport_Controller.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8f1ed57763e5651c2e37eecb9d5ebb96212e22f8e33780bf9a6389da3537af2d
+size 1187934
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/BP_VRCharacter.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/BP_VRCharacter.uasset
new file mode 100644
index 0000000..39fdbf7
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/BP_VRCharacter.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f65066d1020d466d37a6e178305693752f8de27d2ff857cb62dfd53363f67e6d
+size 5196567
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/BasicShapeMaterialTrans.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/BasicShapeMaterialTrans.uasset
new file mode 100644
index 0000000..3c801d6
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/BasicShapeMaterialTrans.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:58f1201bcbc50d575bdffa78d222f112da953bcfbc80856857a2f91165047a63
+size 31685
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/DefaultTextMaterialOpaque2.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/DefaultTextMaterialOpaque2.uasset
new file mode 100644
index 0000000..ba3b229
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/DefaultTextMaterialOpaque2.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:635c9b00ebe64ad76ce2128397d65c75469984f909f1047a0f6571cbebcb61ed
+size 53693
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/FPS_VRCharacter.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/FPS_VRCharacter.uasset
new file mode 100644
index 0000000..602c8c8
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/FPS_VRCharacter.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:18b301fc5f0b1876baa0845e45e395d2a17c176174580d0ea95a530791653b17
+size 317409
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/GripEnum.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/GripEnum.uasset
new file mode 100644
index 0000000..d26bf51
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/GripEnum.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:beff711e061a3bc2b8d308504cd1ccb6b9e44c702afdbfdbe18cc9297835a02c
+size 2693
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/LaserBeamSplineMat.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/LaserBeamSplineMat.uasset
new file mode 100644
index 0000000..993462c
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/LaserBeamSplineMat.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7ce59b72a756fba9cacba821f446f5ad3776046c9c0875d06183811ce5a71c09
+size 21948
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Animations/MannequinHand_Right_CanGrab.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Animations/MannequinHand_Right_CanGrab.uasset
new file mode 100644
index 0000000..9b78d92
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Animations/MannequinHand_Right_CanGrab.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2e2da68c5ea6e7a3ef662a7ac8c30c5d843549bd17e858c6a0fee4f7c6d38f04
+size 182385
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Animations/MannequinHand_Right_Grab.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Animations/MannequinHand_Right_Grab.uasset
new file mode 100644
index 0000000..d0350f3
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Animations/MannequinHand_Right_Grab.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e64c362bad08b3cf6807bdf7ddf3bd8d09fcdd6c8d5f4c677d08ed65d9b659cc
+size 179613
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Animations/MannequinHand_Right_Open.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Animations/MannequinHand_Right_Open.uasset
new file mode 100644
index 0000000..52520d2
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Animations/MannequinHand_Right_Open.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:df5662a655d4a1ed8c8b53eadc0efb7c10641d1addf84f9d18faa0ee71697de0
+size 181442
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Animations/RightGrip_BS.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Animations/RightGrip_BS.uasset
new file mode 100644
index 0000000..fe07cb3
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Animations/RightGrip_BS.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:97afb117c4d48760302fe617cbb826dbf122c798235f2260e084c90b94104e4a
+size 78621
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Animations/RightHand_AnimBP.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Animations/RightHand_AnimBP.uasset
new file mode 100644
index 0000000..7766563
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Animations/RightHand_AnimBP.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7ace9b2a7b2461c05cf617151fe17d0e0c077772a18f2dc36c127373faa300d9
+size 89078
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Materials/M_HandMat.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Materials/M_HandMat.uasset
new file mode 100644
index 0000000..2143b3c
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Materials/M_HandMat.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5408d88d682b1d8ac4a19f308d8763b8b6831e082656a9610799d2f997c46b28
+size 126834
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Materials/M_UE4Man_Body.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Materials/M_UE4Man_Body.uasset
new file mode 100644
index 0000000..dd0a558
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Materials/M_UE4Man_Body.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:fb1221b071760d9dcfba75f0571f49608c921310cfa2f488002a243198b8f1a5
+size 29782
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Materials/M_UE4Man_ChestLogo.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Materials/M_UE4Man_ChestLogo.uasset
new file mode 100644
index 0000000..dc87ca7
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Materials/M_UE4Man_ChestLogo.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cfc7020cafbeee6767b0e78ed2d03cb0a5fa133357149120ed5c8bfe394e57df
+size 9894
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Materials/MaterialLayers/ML_GlossyBlack_Latex_UE4.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Materials/MaterialLayers/ML_GlossyBlack_Latex_UE4.uasset
new file mode 100644
index 0000000..d10ffb4
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Materials/MaterialLayers/ML_GlossyBlack_Latex_UE4.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7ab6d666fd644894fa774fa9dda8eb352fd1bce6d6c31f7a30d1b117bb7aef33
+size 87003
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Materials/MaterialLayers/ML_Plastic_Shiny_Beige.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Materials/MaterialLayers/ML_Plastic_Shiny_Beige.uasset
new file mode 100644
index 0000000..464c92a
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Materials/MaterialLayers/ML_Plastic_Shiny_Beige.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:fc6c2715548627e400f396bbb3b84ac8df814822e2f07a35cb1617e760d8dc76
+size 100213
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Materials/MaterialLayers/ML_Plastic_Shiny_Beige_LOGO.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Materials/MaterialLayers/ML_Plastic_Shiny_Beige_LOGO.uasset
new file mode 100644
index 0000000..8470a0a
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Materials/MaterialLayers/ML_Plastic_Shiny_Beige_LOGO.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:290cf62fb29f6dfe8881596cf940b3ca128c45d56fe81707c08bb3c034ebd781
+size 11203
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Materials/MaterialLayers/ML_SoftMetal_UE4.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Materials/MaterialLayers/ML_SoftMetal_UE4.uasset
new file mode 100644
index 0000000..af20b4f
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Materials/MaterialLayers/ML_SoftMetal_UE4.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bd107a851b46f797ae0f07e68119092f47006e0a1b3a74fde58aac1baa45fc06
+size 87500
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Materials/MaterialLayers/T_ML_Aluminum01.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Materials/MaterialLayers/T_ML_Aluminum01.uasset
new file mode 100644
index 0000000..e7d7ef1
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Materials/MaterialLayers/T_ML_Aluminum01.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cb1c60928051c5bf2e48e56c6f6bf5d9664e1e0fa977d8968c946bfb8531ae5d
+size 395367
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Materials/MaterialLayers/T_ML_Aluminum01_N.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Materials/MaterialLayers/T_ML_Aluminum01_N.uasset
new file mode 100644
index 0000000..a749813
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Materials/MaterialLayers/T_ML_Aluminum01_N.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d5d6d0c10c263d396cbe623561dd9f590378f4219f13256929aba78f2e7f9259
+size 404256
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Materials/MaterialLayers/T_ML_Rubber_Blue_01_D.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Materials/MaterialLayers/T_ML_Rubber_Blue_01_D.uasset
new file mode 100644
index 0000000..933e548
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Materials/MaterialLayers/T_ML_Rubber_Blue_01_D.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f6f7fa9ef137740de52f259ea1d53453e6d739a74e5cb77b6f110812d176e90a
+size 428993
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Materials/MaterialLayers/T_ML_Rubber_Blue_01_N.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Materials/MaterialLayers/T_ML_Rubber_Blue_01_N.uasset
new file mode 100644
index 0000000..79c8dce
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Materials/MaterialLayers/T_ML_Rubber_Blue_01_N.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:58ced35291d53058126de2e835460a0cde4993d2dd3cb364eeb026fb944cb1d9
+size 358491
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Mesh/MannequinHand_Right.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Mesh/MannequinHand_Right.uasset
new file mode 100644
index 0000000..518d701
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Mesh/MannequinHand_Right.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:444309d9a0f79a3ecb48e66d13e9ab08d83daecd9d28a34fd92c34935cbff7f7
+size 555575
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Mesh/MannequinHand_Right_PhysicsAsset.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Mesh/MannequinHand_Right_PhysicsAsset.uasset
new file mode 100644
index 0000000..8dae0fa
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Mesh/MannequinHand_Right_PhysicsAsset.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:384dbce181235b77e56d1915661c6a35002b1fa284c312708272c2270f0efda7
+size 8200
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Mesh/MannequinHand_Right_Skeleton.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Mesh/MannequinHand_Right_Skeleton.uasset
new file mode 100644
index 0000000..f76f0fa
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Mesh/MannequinHand_Right_Skeleton.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0e5e73f9c213323ace75dd9b87f27e79a7c739abb44d2885c0b4e916a25e9519
+size 10699
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Mesh/SK_Mannequin.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Mesh/SK_Mannequin.uasset
new file mode 100644
index 0000000..ccf6e3a
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Mesh/SK_Mannequin.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7dc6c0ff7dbed333463595c609151c34154a746146649e69e943c8178f54b76d
+size 5583019
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Mesh/SK_Mannequin_PhysicsAsset.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Mesh/SK_Mannequin_PhysicsAsset.uasset
new file mode 100644
index 0000000..3eb1695
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Mesh/SK_Mannequin_PhysicsAsset.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1ed75a8aaee902d169ddb980125d9dbeb929851ef33009693a136dc3928211e6
+size 46351
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Mesh/UE4_Mannequin_Skeleton.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Mesh/UE4_Mannequin_Skeleton.uasset
new file mode 100644
index 0000000..c0b6225
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Mesh/UE4_Mannequin_Skeleton.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9012982f60619c314d67bd9477e6bc3c98d90b1864e5532ca7dd9f3147c8bf6b
+size 21571
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Mesh/models_hands_vr_glove_vmat.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Mesh/models_hands_vr_glove_vmat.uasset
new file mode 100644
index 0000000..19f514a
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Mesh/models_hands_vr_glove_vmat.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a11e85324d5097e5a93563d4d706ec763ad4e6c8a9a0006be35776185476cf4f
+size 122076
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Mesh/vr_glove_color.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Mesh/vr_glove_color.uasset
new file mode 100644
index 0000000..7930bf9
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Mesh/vr_glove_color.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6f8c0dc8bc0cb9187b2af5b39ad5ffa58054e02d90954b15d7db5be165ce1a69
+size 1561204
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Mesh/vr_glove_color_red.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Mesh/vr_glove_color_red.uasset
new file mode 100644
index 0000000..834142c
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Mesh/vr_glove_color_red.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6f6bc42a6e8c95b19afef0529046d803ad106cae5390790a0a0f2bd9f466b795
+size 1444461
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Mesh/vr_glove_left_model.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Mesh/vr_glove_left_model.uasset
new file mode 100644
index 0000000..4850151
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Mesh/vr_glove_left_model.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e4dad86a8d893b632636892641163cf90ffb6f39733269b15b28b2c4d1697c45
+size 599145
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Mesh/vr_glove_left_model_Skeleton.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Mesh/vr_glove_left_model_Skeleton.uasset
new file mode 100644
index 0000000..6e081c9
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Mesh/vr_glove_left_model_Skeleton.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0ea51de7b170b0ef094cbfc17cbc576507fcce7697e8e16c8733ba856b68103a
+size 10570
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Mesh/vr_glove_normal.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Mesh/vr_glove_normal.uasset
new file mode 100644
index 0000000..92f4fd6
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Mesh/vr_glove_normal.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:959ae0ad037205b36f8521a70359b45ae6ed51875cec9eb5f61036a5dbd0a754
+size 1825248
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Mesh/vr_glove_right_model.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Mesh/vr_glove_right_model.uasset
new file mode 100644
index 0000000..433a14c
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Mesh/vr_glove_right_model.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:add21dfee93998d0e96661bdd12ae44d51b042c54344348c96e524d9444b4da1
+size 598535
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Mesh/vr_glove_right_model_Skeleton.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Mesh/vr_glove_right_model_Skeleton.uasset
new file mode 100644
index 0000000..78be17e
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Mesh/vr_glove_right_model_Skeleton.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:29f881745fdf7f236e29dcc7558ba940108c415a982e134a7f31f11df6b9b78a
+size 10574
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Textures/UE4Man_Logo_N.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Textures/UE4Man_Logo_N.uasset
new file mode 100644
index 0000000..7a2a6dd
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Textures/UE4Man_Logo_N.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d9492b25939ff1d478e15d1b7d0f1234ff4237a440705fd8b60288c0dd16f22e
+size 137426
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Textures/UE4_LOGO_CARD.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Textures/UE4_LOGO_CARD.uasset
new file mode 100644
index 0000000..f783b6f
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Textures/UE4_LOGO_CARD.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c6badd78cd1054104de8a0acef1aec56fdefd0c57ca56f89802bf8f4581f33d9
+size 70878
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Textures/UE4_MAT_MASKA.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Textures/UE4_MAT_MASKA.uasset
new file mode 100644
index 0000000..0dd9b8d
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Textures/UE4_MAT_MASKA.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:07842eb857d733ec1ba3ac29949afab7307f76f48051503a07d10e04219a1546
+size 211715
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Textures/UE4_Mannequin_MAT_MASKA.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Textures/UE4_Mannequin_MAT_MASKA.uasset
new file mode 100644
index 0000000..edd80a9
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Textures/UE4_Mannequin_MAT_MASKA.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:470dd9cda45e6e2c75a57a851a8748ac3be1e8e313460fc3cba1b49874a0f3f4
+size 211755
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Textures/UE4_Mannequin__norm.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Textures/UE4_Mannequin__norm.uasset
new file mode 100644
index 0000000..24eb036
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Textures/UE4_Mannequin__norm.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c2ed6047f58c4cd64dc8942ceeaab7d315fa94906148d5d6785b005b6b9f923a
+size 5427379
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Textures/UE4_Mannequin__normals.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Textures/UE4_Mannequin__normals.uasset
new file mode 100644
index 0000000..5d5dff8
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequin/Character/Textures/UE4_Mannequin__normals.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d4707f8ef7073631612811dbd4a21beeadf7bb5c2a7809961d8dc3509de1dac0
+size 5427391
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Animations/ABP_Manny.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Animations/ABP_Manny.uasset
new file mode 100644
index 0000000..63d77ff
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Animations/ABP_Manny.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f3ffd71bccec69abff163356446a7980111452b030a7a3dfee461424c2cde74d
+size 370976
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Animations/ABP_Quinn.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Animations/ABP_Quinn.uasset
new file mode 100644
index 0000000..1639c5a
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Animations/ABP_Quinn.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:138a910497e5c70007ac3d8a0e4993c11398c7c6fd9d2544f4189f9f15f5cda2
+size 40139
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Animations/Manny/BS_MM_WalkRun.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Animations/Manny/BS_MM_WalkRun.uasset
new file mode 100644
index 0000000..4bc0329
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Animations/Manny/BS_MM_WalkRun.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8acae4dcedf36a5f5b136e061077a1c3ed992abbde86d358577dd4c3fca45dad
+size 9153
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Animations/Manny/MM_Fall_Loop.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Animations/Manny/MM_Fall_Loop.uasset
new file mode 100644
index 0000000..3e40e51
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Animations/Manny/MM_Fall_Loop.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d51e888c1eefd69b5232e4cc6334ed6686956de6ac17e69ae6e4d1f57b7e6350
+size 659645
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Animations/Manny/MM_Idle.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Animations/Manny/MM_Idle.uasset
new file mode 100644
index 0000000..5accf35
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Animations/Manny/MM_Idle.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b7aaf7ac0774daebe07de85da640c6507245067737734f2ba6bb12a62949789c
+size 876005
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Animations/Manny/MM_Jump.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Animations/Manny/MM_Jump.uasset
new file mode 100644
index 0000000..f20cbf5
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Animations/Manny/MM_Jump.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a3540f4d3626cdc7e93dca2a3b867f395ceebb963f99d57b73dd7ae445e1c684
+size 252393
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Animations/Manny/MM_Land.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Animations/Manny/MM_Land.uasset
new file mode 100644
index 0000000..a22edbe
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Animations/Manny/MM_Land.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7bb628219d7e653767b30cf86df62e227f1b8daf2dbfa6b852b8b905c87dab50
+size 261766
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Animations/Manny/MM_Run_Fwd.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Animations/Manny/MM_Run_Fwd.uasset
new file mode 100644
index 0000000..93f8183
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Animations/Manny/MM_Run_Fwd.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5c93ea1786347f2d3b52014f3e4d6f7a691726c333cc572721c740b91336380f
+size 271611
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Animations/Manny/MM_Walk_Fwd.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Animations/Manny/MM_Walk_Fwd.uasset
new file mode 100644
index 0000000..3fd08d2
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Animations/Manny/MM_Walk_Fwd.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:243e8534ffb8348f257e1e390b0acaffbced581987771c0564f01b5c4da241f5
+size 361158
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Animations/Manny/MM_Walk_InPlace.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Animations/Manny/MM_Walk_InPlace.uasset
new file mode 100644
index 0000000..6983ba1
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Animations/Manny/MM_Walk_InPlace.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3c4f94a99357d4b8b8efb1ff23769a39932518b3a4e343db9222e9a29b63149a
+size 570800
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Animations/Quinn/BS_MF_Unarmed_WalkRun.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Animations/Quinn/BS_MF_Unarmed_WalkRun.uasset
new file mode 100644
index 0000000..9352b9e
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Animations/Quinn/BS_MF_Unarmed_WalkRun.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:28fef93862e8758129b5f7ee2997d2e71d27cf5cabace52eb5d6e228343b916f
+size 9193
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Animations/Quinn/MF_Idle.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Animations/Quinn/MF_Idle.uasset
new file mode 100644
index 0000000..df3b1fe
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Animations/Quinn/MF_Idle.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:505d8e71cbe6761c0bb62af210ebf534ec253c8356d3984e23febf80329d2716
+size 880797
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Animations/Quinn/MF_Run_Fwd.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Animations/Quinn/MF_Run_Fwd.uasset
new file mode 100644
index 0000000..809e666
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Animations/Quinn/MF_Run_Fwd.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:29b35e9ceeb7cca02d9f920db519472b758acccd78e6f2a846ebbbd1a3c0dcac
+size 392562
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Animations/Quinn/MF_Walk_Fwd.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Animations/Quinn/MF_Walk_Fwd.uasset
new file mode 100644
index 0000000..29dbfce
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Animations/Quinn/MF_Walk_Fwd.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a52b767af2e9a9f8cee083466a068f7b29a4d30a0e7428b14b61dbd1d04263f5
+size 411429
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Materials/Functions/CA_Mannequin.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Materials/Functions/CA_Mannequin.uasset
new file mode 100644
index 0000000..c2cc1f0
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Materials/Functions/CA_Mannequin.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9f845106db23168ccf6f1638185bc7b5c1e458722cf1a757d14e550ca81f6f85
+size 4968
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Materials/Functions/ChromaticCurve.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Materials/Functions/ChromaticCurve.uasset
new file mode 100644
index 0000000..102734e
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Materials/Functions/ChromaticCurve.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e16b429037def385b9477be63f9c1dbe44eba465228660450b089bf8d60d259f
+size 6435
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Materials/Functions/MF_Diffraction.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Materials/Functions/MF_Diffraction.uasset
new file mode 100644
index 0000000..4dc7f3b
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Materials/Functions/MF_Diffraction.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:23f1ece80ac62814a694cf50fd7854b8476cb6f2242cfc265012b1201d350c85
+size 32059
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Materials/Functions/MF_logo3layers.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Materials/Functions/MF_logo3layers.uasset
new file mode 100644
index 0000000..593cf82
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Materials/Functions/MF_logo3layers.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:913352fe76b0c0c44217ca76babf8ecae15e590e315470d98815a2e00f98173f
+size 56015
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Materials/Functions/ML_BaseColorFallOff.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Materials/Functions/ML_BaseColorFallOff.uasset
new file mode 100644
index 0000000..01b6b66
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Materials/Functions/ML_BaseColorFallOff.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d54d6bc865c113b78654dc8497173ff83863fbd483f4cd91c1e933cdbf016e82
+size 13739
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Materials/Instances/Manny/MI_Manny_01.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Materials/Instances/Manny/MI_Manny_01.uasset
new file mode 100644
index 0000000..dc46ed4
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Materials/Instances/Manny/MI_Manny_01.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:14719191a97abe54593ff2c8ac936121055d3fdd5f3c645e5b7100fbb0ab396c
+size 21533
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Materials/Instances/Manny/MI_Manny_02.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Materials/Instances/Manny/MI_Manny_02.uasset
new file mode 100644
index 0000000..f29dbb0
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Materials/Instances/Manny/MI_Manny_02.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:71e67d413b3eecbadd402ce3f24759cbb1d08367194c7cf7a977764218a8690d
+size 24564
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Materials/Instances/Quinn/MI_Quinn_01.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Materials/Instances/Quinn/MI_Quinn_01.uasset
new file mode 100644
index 0000000..486c014
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Materials/Instances/Quinn/MI_Quinn_01.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c66658b6032f46d60bb4271571b0954022a336a78c55cd02af4f7ebaeeae7fe3
+size 19578
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Materials/Instances/Quinn/MI_Quinn_02.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Materials/Instances/Quinn/MI_Quinn_02.uasset
new file mode 100644
index 0000000..7d2b736
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Materials/Instances/Quinn/MI_Quinn_02.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0aa8f6da31dd346b9e237f3221707d210a3a3df61b1e47b5a0f92b7844afa32d
+size 24923
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Materials/M_Mannequin.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Materials/M_Mannequin.uasset
new file mode 100644
index 0000000..c02c488
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Materials/M_Mannequin.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:102c18dfb1698b567b0822a51c733ed6fd2c24b93bec07a6e9d9b940a5e38558
+size 83288
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Meshes/Mannequin_LODSettings.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Meshes/Mannequin_LODSettings.uasset
new file mode 100644
index 0000000..c1b9fac
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Meshes/Mannequin_LODSettings.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:77d9c5e7fccd2887c53cdecf10234b85d2ba500e184b8c10774b4ade25edae3a
+size 19131
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Meshes/SKM_Manny.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Meshes/SKM_Manny.uasset
new file mode 100644
index 0000000..183b2c3
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Meshes/SKM_Manny.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2872a0f2c074351aa06902e958a88acbbad3cb8bf392abb3c1e263ad0e746dd1
+size 34534742
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Meshes/SKM_Manny_Simple.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Meshes/SKM_Manny_Simple.uasset
new file mode 100644
index 0000000..af9abb0
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Meshes/SKM_Manny_Simple.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9fd6d74751113f7121ed50b0186e08eb173e2cb5d01b0f617d59cf67b00f6ef8
+size 18526982
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Meshes/SKM_Quinn.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Meshes/SKM_Quinn.uasset
new file mode 100644
index 0000000..2dfcd8a
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Meshes/SKM_Quinn.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cc51eccd57660addda0e480d3db60e57e623e2e0cfb0af5e867a1d53b361b238
+size 36495758
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Meshes/SKM_Quinn_Simple.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Meshes/SKM_Quinn_Simple.uasset
new file mode 100644
index 0000000..91d07a3
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Meshes/SKM_Quinn_Simple.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2febd11df2ab7e875dbc44172e360e3779e84952954b5ff5a3a8d19bfd481d7b
+size 19371456
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Meshes/SK_Mannequin.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Meshes/SK_Mannequin.uasset
new file mode 100644
index 0000000..2fb9db8
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Meshes/SK_Mannequin.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:64245b653cdcbdf1c36f1f24bc2eea411505bf68f2ceb430a362bf4a9263630c
+size 160772
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/ABP_Manny_PostProcess.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/ABP_Manny_PostProcess.uasset
new file mode 100644
index 0000000..0003778
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/ABP_Manny_PostProcess.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:76c17aacee9867a44c09c5ccbd7c7e618a7aac55152ceec8b9a2405e8925c565
+size 463186
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/ABP_Quinn_PostProcess.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/ABP_Quinn_PostProcess.uasset
new file mode 100644
index 0000000..eb951eb
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/ABP_Quinn_PostProcess.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4d805c548607edd93e1525f32f62be35fc0bc3b49288f0f28f75d475d733aa25
+size 462984
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/CR_Mannequin_BasicFootIK.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/CR_Mannequin_BasicFootIK.uasset
new file mode 100644
index 0000000..4d3a1f4
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/CR_Mannequin_BasicFootIK.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:63c0417f3b6045558911f27b41c79baa7338a2ee96d54bb67eee399ed8c89710
+size 590741
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/CR_Mannequin_Body.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/CR_Mannequin_Body.uasset
new file mode 100644
index 0000000..b3fd00c
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/CR_Mannequin_Body.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5292dd182fba746bb222ee8ca0bad62684478259185ec288325309b28617b95d
+size 12653677
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/CR_Mannequin_Procedural.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/CR_Mannequin_Procedural.uasset
new file mode 100644
index 0000000..e12ec6d
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/CR_Mannequin_Procedural.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4d85bba6546413f9e1b1cab62a290afcbd4e478c9e47ab02cd3e0578de1a8403
+size 2336132
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/IK_Mannequin.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/IK_Mannequin.uasset
new file mode 100644
index 0000000..0411e13
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/IK_Mannequin.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:66e8025f539146e688c3cb69af621514f295765cf4e0d30312ec8bcaf93274b1
+size 143511
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/PA_Mannequin.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/PA_Mannequin.uasset
new file mode 100644
index 0000000..9e57443
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/PA_Mannequin.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:32d8ce58623c077e25c0f260cc549377d4df1e81662e07bb96cc48113b78ae6d
+size 289317
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_calf_l_anim.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_calf_l_anim.uasset
new file mode 100644
index 0000000..c59e631
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_calf_l_anim.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2ce02adaae5946a30bfc0f348815218640229bc92e34561509b14a84fbc38cb0
+size 105206
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_calf_l_pose.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_calf_l_pose.uasset
new file mode 100644
index 0000000..65ee716
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_calf_l_pose.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:217815a8fbb02209d8ab7a27815067e8eec7c1c31544215cb9314863ee323166
+size 207531
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_calf_r_anim.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_calf_r_anim.uasset
new file mode 100644
index 0000000..4f45d23
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_calf_r_anim.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c95783ce9cfb7ca8bfd429e2c9007bf01b266c2ffe6fbb23cdf8741b7dbe1c53
+size 105216
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_calf_r_pose.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_calf_r_pose.uasset
new file mode 100644
index 0000000..72fb9e6
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_calf_r_pose.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a96b8c6935bf170cdef21b026148f07ecacfa3af22651f9b463fbdb8fc52e6df
+size 207531
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_clavicle_l_anim.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_clavicle_l_anim.uasset
new file mode 100644
index 0000000..97f005f
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_clavicle_l_anim.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a04dd3997ef95806234d2df5d287aa41f5715d1eae056de5a7e037e2f1aa803d
+size 105242
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_clavicle_l_pose.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_clavicle_l_pose.uasset
new file mode 100644
index 0000000..05a78aa
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_clavicle_l_pose.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:eb547ba1ee69dd4171187abb5dbc0f79d10844049ac78d881b6cb00aadd46cb2
+size 205837
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_clavicle_r_anim.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_clavicle_r_anim.uasset
new file mode 100644
index 0000000..e9853af
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_clavicle_r_anim.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4707a92febba42851f293c526a373843fa181ab0d9071601c5e823e18b126952
+size 105244
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_clavicle_r_pose.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_clavicle_r_pose.uasset
new file mode 100644
index 0000000..bce7832
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_clavicle_r_pose.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:efcbfde96d0c561d4f2737354d992561d6eed0855468dd77977275ccaeb22b59
+size 206439
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_foot_l_anim.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_foot_l_anim.uasset
new file mode 100644
index 0000000..a6f1e9d
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_foot_l_anim.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:21cafd1a5ee408867a834fa06245f6d074d98e180f78edd1b9affd69b407277f
+size 93214
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_foot_l_pose.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_foot_l_pose.uasset
new file mode 100644
index 0000000..9581023
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_foot_l_pose.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:fcad5bad764e842adfc6088f1c054fd1db3eee86236ce421f345e2cda31b9210
+size 132277
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_foot_r_anim.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_foot_r_anim.uasset
new file mode 100644
index 0000000..489b0b4
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_foot_r_anim.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:34368aa5b3857b5d827d8b1706f4432e834823afa6159a0c4c4ec126dfa28d60
+size 93216
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_foot_r_pose.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_foot_r_pose.uasset
new file mode 100644
index 0000000..84dfebf
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_foot_r_pose.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ac5b5b1f6aa71eef22067ec547533c8d45de8d65f0808ecb59bddbc1cd92dc8a
+size 132277
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_hand_l_anim.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_hand_l_anim.uasset
new file mode 100644
index 0000000..5a82929
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_hand_l_anim.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4ff41b8e57ca295e00365dc7a1c6ebc364f376d7cdde9ae0378badda8b30f3f1
+size 99214
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_hand_l_pose.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_hand_l_pose.uasset
new file mode 100644
index 0000000..8ca1d0a
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_hand_l_pose.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c31dafc62605422678e3d2b2401ce8db9f41689d292184d96d5a164a492f8d16
+size 169307
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_hand_r_anim.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_hand_r_anim.uasset
new file mode 100644
index 0000000..7af6ceb
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_hand_r_anim.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:52c13d7f7a1b328aec3234a8ffa386b12c14069fe7217446c9c2080fb7c89b10
+size 99216
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_hand_r_pose.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_hand_r_pose.uasset
new file mode 100644
index 0000000..fee8a06
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_hand_r_pose.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:75052755245be0feb091c8834f6b611c75d6780cec4bb525c91d42e7be51b5a4
+size 169307
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_lowerarm_l_anim.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_lowerarm_l_anim.uasset
new file mode 100644
index 0000000..b7281fb
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_lowerarm_l_anim.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3b23f1f90d33078cb5c0e1cb0b2f066491daa7a95cc4c19e0ba3e2e7b1ec6db6
+size 129596
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_lowerarm_l_pose.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_lowerarm_l_pose.uasset
new file mode 100644
index 0000000..1661689
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_lowerarm_l_pose.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8af73466c4cfcf6fb805ac23e7e72b6b06297d592c4445a77ef72e8dbf057d02
+size 361749
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_lowerarm_r_anim.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_lowerarm_r_anim.uasset
new file mode 100644
index 0000000..dc5af61
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_lowerarm_r_anim.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:84ac4e43287f2167d1d10a786eb3b09a7ae5178db58b370617e38fcfe384173d
+size 129244
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_lowerarm_r_pose.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_lowerarm_r_pose.uasset
new file mode 100644
index 0000000..d2083f2
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_lowerarm_r_pose.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5c371e1e54db220b98992146383a757748eda80701ce9e743048a7209385666d
+size 361749
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_thigh_l_anim.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_thigh_l_anim.uasset
new file mode 100644
index 0000000..c249f39
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_thigh_l_anim.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:626f16b8aa0b278034ad93354f36f340c5eedb8dc0f9e1eb2d2ec6840e0ae1f0
+size 159223
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_thigh_l_pose.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_thigh_l_pose.uasset
new file mode 100644
index 0000000..e85bf2e
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_thigh_l_pose.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a500c9e6709807db106a20a4ce9a1b6f3197c5ae5e493535f7424908f6368454
+size 558074
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_thigh_r_anim.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_thigh_r_anim.uasset
new file mode 100644
index 0000000..0f01e9e
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_thigh_r_anim.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bbce93d680b6e54608205212e7cf59eccbe21f129ff8bbf5d6cfcbe56478b900
+size 159225
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_thigh_r_pose.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_thigh_r_pose.uasset
new file mode 100644
index 0000000..00e6374
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_thigh_r_pose.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:102b63287bd6c6c8a6bda114c9cef7bed39086c34438a8fe84cbfc0270f6a5f0
+size 558074
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_upperarm_l_anim.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_upperarm_l_anim.uasset
new file mode 100644
index 0000000..efc5d1f
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_upperarm_l_anim.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a54532246126cd1d7207cffc9f8ce45de791c7eba4aab45e7905dd526ac7717e
+size 171244
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_upperarm_l_pose.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_upperarm_l_pose.uasset
new file mode 100644
index 0000000..4e66f77
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_upperarm_l_pose.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:34094764ce886228c6093d2f583f4fafa22aaa3b387c9b811d36da8602573460
+size 641738
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_upperarm_r_anim.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_upperarm_r_anim.uasset
new file mode 100644
index 0000000..dee9f14
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_upperarm_r_anim.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:90c6fcad7188756cdda1fe49a1ce75b698ecfef27c8b88f0d2ff3527adf0dcbc
+size 171246
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_upperarm_r_pose.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_upperarm_r_pose.uasset
new file mode 100644
index 0000000..2ea225c
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Manny/Manny_upperarm_r_pose.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:fc2fca5f70a43c8c98ac21acd711b2436de7d7e42cf321b30450a20686dcf020
+size 630300
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_calf_l_anim.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_calf_l_anim.uasset
new file mode 100644
index 0000000..0519f06
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_calf_l_anim.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b540e31e20573fb60b5293bc5850e26d933f791c04695133ab4f902670725679
+size 153784
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_calf_l_pose.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_calf_l_pose.uasset
new file mode 100644
index 0000000..46bf9c5
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_calf_l_pose.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a30d62bd32f58e232b30519721808fd49070f67e3ecbe101520661a6dde7e5e4
+size 245262
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_calf_r_anim.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_calf_r_anim.uasset
new file mode 100644
index 0000000..e394a3b
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_calf_r_anim.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f91f63f68351873f8ef00dba1ef9aef6c334c90454da4460e9455332fb715389
+size 153775
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_calf_r_pose.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_calf_r_pose.uasset
new file mode 100644
index 0000000..32532f4
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_calf_r_pose.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1aaa44d06f750c8ea5435bc42f2387ff650dd2453137f2fcdffe356228220611
+size 245343
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_clavicle_l_anim.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_clavicle_l_anim.uasset
new file mode 100644
index 0000000..83eaf73
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_clavicle_l_anim.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cd4c1f05ea456162e35c61d31b0c9c5034531c4940fa857d30f93889b18ab1d5
+size 147743
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_clavicle_l_pose.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_clavicle_l_pose.uasset
new file mode 100644
index 0000000..39c4e5b
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_clavicle_l_pose.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:76e3889df4ee68657936ddbb13d8db6de9f8b4818128d4882fb292f1a577566f
+size 206014
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_clavicle_r_anim.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_clavicle_r_anim.uasset
new file mode 100644
index 0000000..710f59a
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_clavicle_r_anim.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f79419b8a761026ffaae77fd07703404c92681fec9df9ec45819bcb9a9794f7b
+size 147492
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_clavicle_r_pose.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_clavicle_r_pose.uasset
new file mode 100644
index 0000000..b05efff
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_clavicle_r_pose.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:277f4410f9b80780972698064e0f4cf192b4375610506efa5d510ae0e8404f1a
+size 206115
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_foot_l_anim.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_foot_l_anim.uasset
new file mode 100644
index 0000000..c2b2a80
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_foot_l_anim.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8208edc319e609d4e6b1d326e62a877063d79cf503158a4bc1878fd02a93ed58
+size 135462
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_foot_l_pose.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_foot_l_pose.uasset
new file mode 100644
index 0000000..909c41a
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_foot_l_pose.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6371fa829ebf962697f5be5b5650ceb195803537302f0ae3d1a0cd4111d74584
+size 132254
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_foot_r_anim.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_foot_r_anim.uasset
new file mode 100644
index 0000000..558cac0
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_foot_r_anim.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3a6c53687aef19e408c06f41ee035835ca209af4ad8ff679152c47d0daf29600
+size 135464
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_foot_r_pose.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_foot_r_pose.uasset
new file mode 100644
index 0000000..d131e90
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_foot_r_pose.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d83f07adb29976a36ddf99f245f24b5d3868ad3fdd5f0025acc9231346a014bc
+size 132254
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_hand_l_anim.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_hand_l_anim.uasset
new file mode 100644
index 0000000..f4b66e0
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_hand_l_anim.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ed9e80e359140fb44037acbb066826198c27233bdd7c253cfb391ed6dc30d07b
+size 141462
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_hand_l_pose.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_hand_l_pose.uasset
new file mode 100644
index 0000000..ace6e4b
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_hand_l_pose.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1b1aaee1c184ab23f77d462372726b9be294b25dc77eccb2f444e54ce72be812
+size 169284
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_hand_r_anim.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_hand_r_anim.uasset
new file mode 100644
index 0000000..a414ba1
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_hand_r_anim.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:83e66651a64a65e141384f67f3f036d82647c1c3024437b0b0ee0cc59533f6db
+size 122362
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_hand_r_pose.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_hand_r_pose.uasset
new file mode 100644
index 0000000..dc73bac
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_hand_r_pose.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ac2d942e4fc64538231e1c921ec9d008a6d1bdecf76dc130bd372c687f781c08
+size 169284
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_lowerarm_l_anim.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_lowerarm_l_anim.uasset
new file mode 100644
index 0000000..1f49344
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_lowerarm_l_anim.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9976a76b917eac5bdc0e4296a91d9816a198b8548d18594c3f623826726fe496
+size 158389
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_lowerarm_l_pose.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_lowerarm_l_pose.uasset
new file mode 100644
index 0000000..2c8178d
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_lowerarm_l_pose.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ba6822ed243f8452e0392cf4f198e636de9c868053e2261d63d8292ef96319c7
+size 399664
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_lowerarm_r_anim.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_lowerarm_r_anim.uasset
new file mode 100644
index 0000000..e2e3009
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_lowerarm_r_anim.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:892f16dfdfa0d0d9957d427c7d620867adc3a33f3e7814fd92f91c7d696bf027
+size 158391
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_lowerarm_r_pose.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_lowerarm_r_pose.uasset
new file mode 100644
index 0000000..f933f99
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_lowerarm_r_pose.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:dee51a08a93109dec908d4cdffeba2be2ccc48d84cbed47048f99b12f43e7ddc
+size 399664
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_thigh_l_anim.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_thigh_l_anim.uasset
new file mode 100644
index 0000000..5566b31
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_thigh_l_anim.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:381b95e60bfc26a64d8595e047ea6a007e66adeeca20331d23127a82cdda783b
+size 182369
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_thigh_l_pose.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_thigh_l_pose.uasset
new file mode 100644
index 0000000..90ff7c6
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_thigh_l_pose.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cbcbe04fcc189470ee8567f1e4363ebdd6b978cd018af41df6d6e849335dbaea
+size 557750
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_thigh_r_anim.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_thigh_r_anim.uasset
new file mode 100644
index 0000000..5c41404
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_thigh_r_anim.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b6c4a3386b121b0da667ca82a62986fb54575533bcaaf8797ee2ffb49e4b2629
+size 182371
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_thigh_r_pose.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_thigh_r_pose.uasset
new file mode 100644
index 0000000..c7cc3ae
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_thigh_r_pose.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6e4b3f86720ae78ec16ebcd7dcebef72bb89ad5216dc524cd193b6396faddf6a
+size 557750
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_upperarm_l_anim.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_upperarm_l_anim.uasset
new file mode 100644
index 0000000..1e3518a
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_upperarm_l_anim.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cedcccc8e20190a00ff514c5467ed6781748deb2178f856221f833b99c89f1ac
+size 170390
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_upperarm_l_pose.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_upperarm_l_pose.uasset
new file mode 100644
index 0000000..15ec31f
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_upperarm_l_pose.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:910546553703424dadce708ffb02ba320b60abff1b4bffa7ba637a1f76d8b5a3
+size 485575
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_upperarm_r_anim.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_upperarm_r_anim.uasset
new file mode 100644
index 0000000..28b6e76
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_upperarm_r_anim.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:02a0a2a388445683c68518c0062f7aff5baa45d8b490b8a0b7a0571a5977751a
+size 170392
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_upperarm_r_pose.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_upperarm_r_pose.uasset
new file mode 100644
index 0000000..886ec5e
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/Poses/Quinn/Quinn_upperarm_r_pose.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:167ccaa713eaa481437b4128bab282dd8e46a56c403d925f7a95ec8706289b70
+size 488585
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/RTG_Mannequin.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/RTG_Mannequin.uasset
new file mode 100644
index 0000000..0f1e5b1
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Rigs/RTG_Mannequin.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c7f55f2e0aacf145904d8cbd2459af8739f043c928fbdc0b48bc8b92efda0fba
+size 23761
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Manny/T_Manny_01_ASAOPMASK_MSK.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Manny/T_Manny_01_ASAOPMASK_MSK.uasset
new file mode 100644
index 0000000..1b2b3ca
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Manny/T_Manny_01_ASAOPMASK_MSK.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:adcfc2e6fb97e6625a6b358c6a2d4a96ac7f3cf7dea0551c486344291efa8295
+size 3612711
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Manny/T_Manny_01_BN.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Manny/T_Manny_01_BN.uasset
new file mode 100644
index 0000000..2ee75cc
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Manny/T_Manny_01_BN.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:32a5d96fb2745b0d2c2a96aa6825a64ea177c9a1af64cbe7156a5a80852e60be
+size 18514263
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Manny/T_Manny_01_CCRCCPlastic_MSK.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Manny/T_Manny_01_CCRCCPlastic_MSK.uasset
new file mode 100644
index 0000000..daabd36
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Manny/T_Manny_01_CCRCCPlastic_MSK.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a6571dd4986df8ac46bbbba1f736ada982706ab0e5502a161142e368f4d7f52d
+size 10272081
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Manny/T_Manny_01_D.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Manny/T_Manny_01_D.uasset
new file mode 100644
index 0000000..8b78bd3
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Manny/T_Manny_01_D.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1fbb07cb01e9eb29f8ceabbae27ede593bd4ede98957f492f197208e8db5dab2
+size 5740449
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Manny/T_Manny_01_MSR_MSK.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Manny/T_Manny_01_MSR_MSK.uasset
new file mode 100644
index 0000000..88f75ce
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Manny/T_Manny_01_MSR_MSK.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1751ca6b12e656c02c5e0fd50e1856d731c0304b81a6740a9847b2de30021547
+size 11038284
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Manny/T_Manny_01_N.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Manny/T_Manny_01_N.uasset
new file mode 100644
index 0000000..9aeb54f
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Manny/T_Manny_01_N.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a51fe0604dcb201b32ba900006f565b0330d826d3f6f891fb7e884c3cc7235c9
+size 7198345
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Manny/T_Manny_01_Tan.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Manny/T_Manny_01_Tan.uasset
new file mode 100644
index 0000000..e016ef0
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Manny/T_Manny_01_Tan.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0bdc44109c7e4891fc8c8b15a98d06300f2c4e2b55f5513fb15a380de31a28d1
+size 1357866
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Manny/T_Manny_02_ASAOPMASK_MSK.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Manny/T_Manny_02_ASAOPMASK_MSK.uasset
new file mode 100644
index 0000000..1824c76
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Manny/T_Manny_02_ASAOPMASK_MSK.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cb0e8f5e63df11c269dbffaf6a8a9c9ccf00261e985dfd6a42499ec78ce816e3
+size 8341487
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Manny/T_Manny_02_BN.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Manny/T_Manny_02_BN.uasset
new file mode 100644
index 0000000..7573bb3
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Manny/T_Manny_02_BN.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d6cc567ea332550b67761d3c8afface743880d9ca2d7d701a328de16b4c9a0c1
+size 21139622
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Manny/T_Manny_02_CCRCCPlastic_MSK.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Manny/T_Manny_02_CCRCCPlastic_MSK.uasset
new file mode 100644
index 0000000..e3a6b16
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Manny/T_Manny_02_CCRCCPlastic_MSK.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:acceca89b72d45be61b978671e8634e40b3439da0565565424bbb75bc491771a
+size 13144152
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Manny/T_Manny_02_D.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Manny/T_Manny_02_D.uasset
new file mode 100644
index 0000000..cb90248
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Manny/T_Manny_02_D.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7f7e39f621fd09895097c8ef9be45b4c9114ff5434d41ff4875640377d90c048
+size 9033617
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Manny/T_Manny_02_MSR_MSK.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Manny/T_Manny_02_MSR_MSK.uasset
new file mode 100644
index 0000000..a0cc5b4
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Manny/T_Manny_02_MSR_MSK.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1cc57bbf89333f7053772d9d3ce220d5185af3b7b98e2b1be3710736ed81e385
+size 13677710
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Manny/T_Manny_02_N.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Manny/T_Manny_02_N.uasset
new file mode 100644
index 0000000..02a6a68
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Manny/T_Manny_02_N.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:774b5f12e83544218ad818e58a00e0651627e2a2139c6ae81740c32188b88790
+size 7269429
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Manny/T_Manny_02_Tan.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Manny/T_Manny_02_Tan.uasset
new file mode 100644
index 0000000..1c513fc
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Manny/T_Manny_02_Tan.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cb1d7c621fdcf8b600bcfb7ac7f10f5e1b1c9016a73997f4e024e94535a0e073
+size 2153886
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Quinn/T_Quinn_01ID_BN.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Quinn/T_Quinn_01ID_BN.uasset
new file mode 100644
index 0000000..4fa1e57
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Quinn/T_Quinn_01ID_BN.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e399a20c77dcf93205859660f6fc938fa18c7eb0a7eef84fe11be57f44333f8f
+size 16108696
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Quinn/T_Quinn_01ID_D.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Quinn/T_Quinn_01ID_D.uasset
new file mode 100644
index 0000000..7e730a1
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Quinn/T_Quinn_01ID_D.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ba3a9f3c53be7bcf473543fd7db586ddb25aeaa3654f2b334a42d97403e4cb4e
+size 4711231
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Quinn/T_Quinn_01ID_MSR_MSK.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Quinn/T_Quinn_01ID_MSR_MSK.uasset
new file mode 100644
index 0000000..bf17e43
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Quinn/T_Quinn_01ID_MSR_MSK.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2fe6e4d0e9dfe6b37fcfff10fd7828e57e303552d58d5097032c8d88e3f5ef01
+size 11656243
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Quinn/T_Quinn_01ID_N.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Quinn/T_Quinn_01ID_N.uasset
new file mode 100644
index 0000000..dbe9183
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Quinn/T_Quinn_01ID_N.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:09b0a749d38658217ee32ff0d7cf875b8360eae15d10b2771a66edd8f49568dc
+size 5217772
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Quinn/T_Quinn_01ID_Tan.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Quinn/T_Quinn_01ID_Tan.uasset
new file mode 100644
index 0000000..abb1683
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Quinn/T_Quinn_01ID_Tan.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3a304782fd7992f18692fc5dc9b6fd121ec15d8e4088c09496cbfa73e5ebb466
+size 1104725
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Quinn/T_Quinn_01_ASAOMASK_MSK.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Quinn/T_Quinn_01_ASAOMASK_MSK.uasset
new file mode 100644
index 0000000..a658a84
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Quinn/T_Quinn_01_ASAOMASK_MSK.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d1d294824a3393a5b7abbef09dbbd08cea1eb248ca935b6727792a624d839f51
+size 5834939
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Quinn/T_Quinn_01_CCRCCPlastic_MSK.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Quinn/T_Quinn_01_CCRCCPlastic_MSK.uasset
new file mode 100644
index 0000000..2ca4e1b
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Quinn/T_Quinn_01_CCRCCPlastic_MSK.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d7a0c808b5e0ea524c53e9be00e25bfa2bd2261b4f0ddfde14f3823e6c34f7ae
+size 12400035
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Quinn/T_Quinn_02ID_BN.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Quinn/T_Quinn_02ID_BN.uasset
new file mode 100644
index 0000000..5b55852
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Quinn/T_Quinn_02ID_BN.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ae64881694d7d129425271e9749ae310de06e4f6b44b431048263a0bdd79269b
+size 19706980
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Quinn/T_Quinn_02ID_D.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Quinn/T_Quinn_02ID_D.uasset
new file mode 100644
index 0000000..9037825
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Quinn/T_Quinn_02ID_D.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ff3848b1eba0eea285b8ed3f0bf32b3d06fcab26877eb30f56e55a1fbadf8f24
+size 6732599
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Quinn/T_Quinn_02ID_MSR_MSK.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Quinn/T_Quinn_02ID_MSR_MSK.uasset
new file mode 100644
index 0000000..2ad72e2
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Quinn/T_Quinn_02ID_MSR_MSK.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f07854549aed14b0a08a810536fd3f92c7004f8cf325b6c229e199b77a70d210
+size 13169873
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Quinn/T_Quinn_02ID_N.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Quinn/T_Quinn_02ID_N.uasset
new file mode 100644
index 0000000..610e4bd
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Quinn/T_Quinn_02ID_N.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:291193c31c0afedc313659a9712f91d771c5721af47dd1a074be93d6c97bf2a0
+size 5217772
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Quinn/T_Quinn_02ID_Tan.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Quinn/T_Quinn_02ID_Tan.uasset
new file mode 100644
index 0000000..a82b78a
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Quinn/T_Quinn_02ID_Tan.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:52e073a5286c21223e535aaa14196064e32da69a412bafaff889e45f1100f2cc
+size 1758592
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Quinn/T_Quinn_02_ASAOMASK_MSK.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Quinn/T_Quinn_02_ASAOMASK_MSK.uasset
new file mode 100644
index 0000000..45c6ee6
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Quinn/T_Quinn_02_ASAOMASK_MSK.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5a8a3b6407fdeb750079ccfbd392598eb7ed13de5eebe3b3e02c4f1905021601
+size 6902078
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Quinn/T_Quinn_02_CCRCCPlastic_MSK.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Quinn/T_Quinn_02_CCRCCPlastic_MSK.uasset
new file mode 100644
index 0000000..4fdb67a
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Quinn/T_Quinn_02_CCRCCPlastic_MSK.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d6a1d60f0eabcd7d2655fb7619c7141f44ac7f2670f9f4113de67c5943c6c0db
+size 13427967
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Shared/T_UE_Logo_M.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Shared/T_UE_Logo_M.uasset
new file mode 100644
index 0000000..4bc8f96
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Mannequins/Textures/Shared/T_UE_Logo_M.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:47dae3eb7ff30f4c72bec58a775ee73523014520406e32c110a28f0ab1e092a2
+size 70054
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Materials/MF_OccludedPixels.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Materials/MF_OccludedPixels.uasset
new file mode 100644
index 0000000..6ab6003
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Materials/MF_OccludedPixels.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:54a0408daab81a30343ab5a6eebbb7341858fd4327eea5a98bedd3703386fa26
+size 101110
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Materials/MI_ChaperoneOutline.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Materials/MI_ChaperoneOutline.uasset
new file mode 100644
index 0000000..442c544
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Materials/MI_ChaperoneOutline.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f16e14b4ebea466a0b9df780a72ba103a1c6673f7388c1dd2baa13dc18c70a47
+size 83176
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Materials/MI_SmallCubes.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Materials/MI_SmallCubes.uasset
new file mode 100644
index 0000000..3063540
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Materials/MI_SmallCubes.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:62bcc58df01d0360bc1a9f0f15ca64e9c190b8bb68e1d577c4cf1e437ad8509a
+size 93815
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Materials/MI_TeleportCylinderPreview.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Materials/MI_TeleportCylinderPreview.uasset
new file mode 100644
index 0000000..dc1b7b5
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Materials/MI_TeleportCylinderPreview.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d9dc5a4c5036697c43ca19e908b333a5957af0f3fdcb519ab55afd01a51c97fe
+size 86595
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Materials/M_ArcEndpoint.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Materials/M_ArcEndpoint.uasset
new file mode 100644
index 0000000..45bd2e7
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Materials/M_ArcEndpoint.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:df74d0c1f039f45bf279012ec85336ff35ac6615c0b11b4fdebdcb8d675ab1b9
+size 39968
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Materials/M_BaseMaterial.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Materials/M_BaseMaterial.uasset
new file mode 100644
index 0000000..cf9c7bf
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Materials/M_BaseMaterial.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4bba49da44c24411de48a207a27be18cd99c687e3c3bd5bf3a54ef2acdb6f74f
+size 84767
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Materials/M_SplineArcMat.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Materials/M_SplineArcMat.uasset
new file mode 100644
index 0000000..6d84d80
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Materials/M_SplineArcMat.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5ed3c39989e69ab1d5b009cbbcdb162a079fed671c93dccd8e1576d8d96fbb7b
+size 96939
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Materials/M_TeleportPreviews.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Materials/M_TeleportPreviews.uasset
new file mode 100644
index 0000000..644ddf6
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Materials/M_TeleportPreviews.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:73a23e29e515999882607de2e1893ebd9049959d58e00b0b30f0fa3965383140
+size 98377
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Materials/TeleportMCP.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Materials/TeleportMCP.uasset
new file mode 100644
index 0000000..7bc999d
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Materials/TeleportMCP.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8e64974c83716a0270f5502fd90bd9a25a9228d9a8681f3fc94f0a8d8e929524
+size 1778
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Meshes/1x1_cube.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Meshes/1x1_cube.uasset
new file mode 100644
index 0000000..f7bb0c7
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Meshes/1x1_cube.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:93348fe619da2f377eb7a1f9abb2083c15a15004d032f433ad6b7d04fd4cbd58
+size 66048
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Meshes/BeaconDirection.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Meshes/BeaconDirection.uasset
new file mode 100644
index 0000000..2e61cec
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Meshes/BeaconDirection.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1e8320ab60f49afe35ae68ea9ec1c0f086751c9b1071360234d2d1c1e874b61f
+size 23071
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Meshes/BeamMesh.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Meshes/BeamMesh.uasset
new file mode 100644
index 0000000..6a91c9f
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Meshes/BeamMesh.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8fec217aa37eb7a505128347b5af8a3ccbf1c5337b18d0b10d2b45d89e15f443
+size 25004
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Meshes/SM_FatCylinder.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Meshes/SM_FatCylinder.uasset
new file mode 100644
index 0000000..e3a33aa
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/Meshes/SM_FatCylinder.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:856efe12362b5c9d2c2dc382bc1288200833381f07da2392409357d619d682c3
+size 23281
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/MovementMode.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/MovementMode.uasset
new file mode 100644
index 0000000..556cfec
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/MovementMode.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:01c270fa3a06b43ac3906b8373466bbf5c0fcaa6a1efe1679afecbb55f79a804
+size 5307
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/NewForceFeedbackEffect2.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/NewForceFeedbackEffect2.uasset
new file mode 100644
index 0000000..656953c
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/NewForceFeedbackEffect2.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d9e956a394bfe6a02e1101caee08e9f82779ef95bba5dd861e96a7b4f0c9bba0
+size 2482
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/VOIPAtten.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/VOIPAtten.uasset
new file mode 100644
index 0000000..ee5efbf
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Character/VOIPAtten.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:30b8f0721f0f7186cf161fbebeb3f633f53b36cd80fa756b5fa902c838de2d94
+size 1368
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/CustomLocalPlayer.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/CustomLocalPlayer.uasset
new file mode 100644
index 0000000..2ec26f4
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/CustomLocalPlayer.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9d1dd04c7829b6a1f8c3495acb74dfd3f1ddb8a89c830f549c55e9bd58acd674
+size 6206
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/DefaultGameplayTags.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/DefaultGameplayTags.uasset
new file mode 100644
index 0000000..0895ccd
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/DefaultGameplayTags.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0ae95b01c3b9586c8865a8608f5a283501868837b6f2ed8b4449bf68a05c8355
+size 4870
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/Gestures.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/Gestures.uasset
new file mode 100644
index 0000000..71ae424
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/Gestures.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:24137f097a0cf0e3cc12bee282c3e692e035efc8df1bf17e1657f6e6278ee599
+size 2821
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/GraspAnimBP.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/GraspAnimBP.uasset
new file mode 100644
index 0000000..0ac6882
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/GraspAnimBP.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:30f6cf32bcdb82d86b3a1c4279764c7010d29a1e0110a0a9cee61eed816489f5
+size 592475
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/GraspAnimBPManny.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/GraspAnimBPManny.uasset
new file mode 100644
index 0000000..b672e63
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/GraspAnimBPManny.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:113b070c95cabd0083641039ba8b9eea8513e343fc4243dd9f30ed12d14664e4
+size 604888
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/GraspingBlend.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/GraspingBlend.uasset
new file mode 100644
index 0000000..65ee1f5
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/GraspingBlend.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a6a701a4008fcfde07775abfb54f0df35b4318316afd2f9eb8a2a6b6777d41af
+size 7885
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/GraspingBlendManny.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/GraspingBlendManny.uasset
new file mode 100644
index 0000000..4792a35
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/GraspingBlendManny.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:888b3957bbb2c7423619b921b6de9c9b3f1ebed7eb9250967a16ac2179a5d70e
+size 8868
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/GraspingHand.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/GraspingHand.uasset
new file mode 100644
index 0000000..e7924ea
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/GraspingHand.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2dfebe27b025e487366129436fbc9a2fd1e10b66b31746fbe3d6d3a05b4daf98
+size 2399836
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/GraspingHandManny.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/GraspingHandManny.uasset
new file mode 100644
index 0000000..322988f
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/GraspingHandManny.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:54effd14dde85d288320028e5f51ae07183ece445c8da502765046669bbab9f3
+size 2380291
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/GraspingPhysics.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/GraspingPhysics.uasset
new file mode 100644
index 0000000..b030e54
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/GraspingPhysics.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c749c5c6ed2962234b07a3f5e01febce333d125eead42610649556c8a69692f8
+size 126272
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/GraspingPhysicsManny.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/GraspingPhysicsManny.uasset
new file mode 100644
index 0000000..3622fd8
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/GraspingPhysicsManny.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:058754dc3612df66d55038a7ff765967ce894f7b85f06376a20a6f20ce86ef4c
+size 69933
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/HandAnimState.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/HandAnimState.uasset
new file mode 100644
index 0000000..c399504
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/HandAnimState.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b19e695b3faa89d8093b9b0b5eef960298d0b71d340459d5411e9ed0384b1d9f
+size 3023
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/PhysicsTossManager.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/PhysicsTossManager.uasset
new file mode 100644
index 0000000..004c10c
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/PhysicsTossManager.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:11d92c3fdb9ad0f88ccc631bcf239dff5a6341f79711b3eca311c6962c3e5292
+size 285951
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/TriggerIndexs.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/TriggerIndexs.uasset
new file mode 100644
index 0000000..4f65169
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/TriggerIndexs.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f230faef606a51133b4b90f52b6a3faeb10bea4e451bb5f15a27637d05880aea
+size 5143
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Animations/A_MannequinsXR_Grasp_Right.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Animations/A_MannequinsXR_Grasp_Right.uasset
new file mode 100644
index 0000000..470b2db
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Animations/A_MannequinsXR_Grasp_Right.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0c265c4a5179cbc49b00368235635ed1d8335dfca016bc78ff8e4948a095beb6
+size 29844
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Animations/A_MannequinsXR_Idle_Right.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Animations/A_MannequinsXR_Idle_Right.uasset
new file mode 100644
index 0000000..8c0b03c
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Animations/A_MannequinsXR_Idle_Right.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9b8e07d4a99b3dc2ced7b8d4f05d986f6fdf2797eafcb50d525ae89c4935c9df
+size 47127
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Animations/A_MannequinsXR_IndexCurl_Right.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Animations/A_MannequinsXR_IndexCurl_Right.uasset
new file mode 100644
index 0000000..d4b7472
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Animations/A_MannequinsXR_IndexCurl_Right.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:04e0970c4cbc59c044ccf98219b746a974d8c71572d4ebcf35c894fd01d09264
+size 30163
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Animations/A_MannequinsXR_Point_Right.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Animations/A_MannequinsXR_Point_Right.uasset
new file mode 100644
index 0000000..137e9ad
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Animations/A_MannequinsXR_Point_Right.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:136de5f21df470e3322645d54b10ce70ddb1fbf24fe1758964caa61fb188223b
+size 30075
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Animations/A_MannequinsXR_ThumbUp_Right.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Animations/A_MannequinsXR_ThumbUp_Right.uasset
new file mode 100644
index 0000000..5ef3f67
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Animations/A_MannequinsXR_ThumbUp_Right.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:170953d0519e3e7b9230a1e4347136fc1f102786f6cdeb6f73ca1b858f61ac8a
+size 30136
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Animations/GrabAnimation.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Animations/GrabAnimation.uasset
new file mode 100644
index 0000000..7b5b519
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Animations/GrabAnimation.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:29d60663acdcf836b1f18af8160e55605eed835f7860998f77bf8c5c64b935e7
+size 18098
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Animations/MDT_MannequinsXR.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Animations/MDT_MannequinsXR.uasset
new file mode 100644
index 0000000..c1c81d8
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Animations/MDT_MannequinsXR.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:12e935fb2b9c993c1fea32f460877f99625f5ac9dc66e45713cafbf494c69c1c
+size 12698
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/B_MannequinsXR.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/B_MannequinsXR.uasset
new file mode 100644
index 0000000..a8aff4c
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/B_MannequinsXR.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c018cfaa312a441aeb0c3e3adfdce495c1da70d99effb180b75d8c6ab36b58cf
+size 13722
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Materials/Functions/CA_Mannequin.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Materials/Functions/CA_Mannequin.uasset
new file mode 100644
index 0000000..98e7dff
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Materials/Functions/CA_Mannequin.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5b30315c5268c4c6f2bf734d3b92e2775cd157d159f4bbc5caf9caf8e6fa107d
+size 4986
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Materials/Functions/ChromaticCurve.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Materials/Functions/ChromaticCurve.uasset
new file mode 100644
index 0000000..7bd3825
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Materials/Functions/ChromaticCurve.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:92f3173629d4560f25529f361141d58ac6fa21a8d6191c537f703b13ee929b73
+size 6447
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Materials/Functions/MF_Diffraction.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Materials/Functions/MF_Diffraction.uasset
new file mode 100644
index 0000000..c815496
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Materials/Functions/MF_Diffraction.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d0926a93a1f8fc963c4f1ea8a3938e268aac97d8637602351aa2a24214f76889
+size 32083
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Materials/Functions/MF_logo3layers.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Materials/Functions/MF_logo3layers.uasset
new file mode 100644
index 0000000..ef5ad5d
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Materials/Functions/MF_logo3layers.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ec0f6fc2eedc7fccfcf37ea6ea4179896bc99cb556dcd05a1b467f1a0ed64e4f
+size 54856
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Materials/Functions/ML_BaseColorFallOff.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Materials/Functions/ML_BaseColorFallOff.uasset
new file mode 100644
index 0000000..e1f8e51
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Materials/Functions/ML_BaseColorFallOff.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bd8135c90a2ee5475a026fda4d521aa5587c7cb37ecff60b5918ffbcbee74ad1
+size 13751
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Materials/Instances/Manny/MI_Manny_01.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Materials/Instances/Manny/MI_Manny_01.uasset
new file mode 100644
index 0000000..8a6b70a
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Materials/Instances/Manny/MI_Manny_01.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d5ababd4023308c31b036cbcb7807007169945e2be7811b495edc82405933917
+size 15549
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Materials/Instances/Manny/MI_Manny_02.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Materials/Instances/Manny/MI_Manny_02.uasset
new file mode 100644
index 0000000..2dcd1f5
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Materials/Instances/Manny/MI_Manny_02.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cc3290f2fdab2392cdfe63ca1f39963eb6a3ab53e94c614b2b227cbebad8fd7b
+size 20126
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Materials/M_Mannequin.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Materials/M_Mannequin.uasset
new file mode 100644
index 0000000..4dbd090
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Materials/M_Mannequin.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9d0de5cf3c470c1ea90877e3518ff9dc4b96545b56f0b0e8045519907c7bfeaa
+size 79412
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Meshes/ABP_MannequinsXR.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Meshes/ABP_MannequinsXR.uasset
new file mode 100644
index 0000000..20db8ae
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Meshes/ABP_MannequinsXR.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:01de832d0c6c993b0f4a9d01605080af5cc8e11c625003c814e63a4e5955f999
+size 226849
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Meshes/SKM_MannyXR_left.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Meshes/SKM_MannyXR_left.uasset
new file mode 100644
index 0000000..37ff094
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Meshes/SKM_MannyXR_left.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:49761abd36fc336199f308b808a05edf93c531bc7e5de50e0a1a7277ebed5759
+size 2341113
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Meshes/SKM_MannyXR_right.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Meshes/SKM_MannyXR_right.uasset
new file mode 100644
index 0000000..d2c4d9d
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Meshes/SKM_MannyXR_right.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a900ce60070e4f6a8286c3a25de9e2f55dc50863fbc89e95824c07d97f2a9973
+size 2340333
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Meshes/SK_MannequinsXR.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Meshes/SK_MannequinsXR.uasset
new file mode 100644
index 0000000..778c06b
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Meshes/SK_MannequinsXR.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:aa6834d20b534c4919ed5d11564c65f3a8dc46462b12ec1e63ebdf019a46f7fc
+size 53130
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Textures/Manny/T_Manny_01_ASAOPMASK_MSK.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Textures/Manny/T_Manny_01_ASAOPMASK_MSK.uasset
new file mode 100644
index 0000000..89c075f
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Textures/Manny/T_Manny_01_ASAOPMASK_MSK.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ce32d32ba29bc7f506de3cecd6fca0f2f5890530ca3df2ad4028aacd5b3848ed
+size 3612723
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Textures/Manny/T_Manny_01_BN.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Textures/Manny/T_Manny_01_BN.uasset
new file mode 100644
index 0000000..23a94da
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Textures/Manny/T_Manny_01_BN.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1cab472105172ff531e7d4b581d49a0b0267c3a01192a9b346d167227a01cafb
+size 18514275
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Textures/Manny/T_Manny_01_CCRCCPlastic_MSK.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Textures/Manny/T_Manny_01_CCRCCPlastic_MSK.uasset
new file mode 100644
index 0000000..f235d70
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Textures/Manny/T_Manny_01_CCRCCPlastic_MSK.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:663d2b0ae07554ec74f1476f758828be6723bb4539974f02b9e06b445e25de82
+size 10272093
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Textures/Manny/T_Manny_01_D.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Textures/Manny/T_Manny_01_D.uasset
new file mode 100644
index 0000000..1267d9e
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Textures/Manny/T_Manny_01_D.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4d3d691c0808c6378042935fd497fa6328fe6a0f65fc14ede6ef74bd897896fd
+size 5740461
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Textures/Manny/T_Manny_01_MSR_MSK.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Textures/Manny/T_Manny_01_MSR_MSK.uasset
new file mode 100644
index 0000000..6209c01
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Textures/Manny/T_Manny_01_MSR_MSK.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:594997242a9e782913f417bf7b66bfdd4e973f61d37f19517946b00f8330fdcd
+size 11038296
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Textures/Manny/T_Manny_01_N.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Textures/Manny/T_Manny_01_N.uasset
new file mode 100644
index 0000000..da667a0
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Textures/Manny/T_Manny_01_N.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6d855986290da65aeb62f194871eccb3345b756a16392b74f08e484de1187cb1
+size 7198357
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Textures/Manny/T_Manny_01_Tan.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Textures/Manny/T_Manny_01_Tan.uasset
new file mode 100644
index 0000000..cc74e2e
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Textures/Manny/T_Manny_01_Tan.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f5198cf31123bfc90086843c08469c7446089a9c02e4730ac5a237770b4bcc38
+size 1357878
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Textures/Manny/T_Manny_02_ASAOPMASK_MSK.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Textures/Manny/T_Manny_02_ASAOPMASK_MSK.uasset
new file mode 100644
index 0000000..815b6d6
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Textures/Manny/T_Manny_02_ASAOPMASK_MSK.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:799d88e7e15925be4b7ebc9bd36a0e4a757cdf46e0cb2c7d3d09ebc9259c0f53
+size 8341499
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Textures/Manny/T_Manny_02_BN.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Textures/Manny/T_Manny_02_BN.uasset
new file mode 100644
index 0000000..8bea4d6
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Textures/Manny/T_Manny_02_BN.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ec720ed86dd6f43709a1da7edeb4a84adcefc17ee1417fbe58577ebf61adb580
+size 21139634
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Textures/Manny/T_Manny_02_CCRCCPlastic_MSK.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Textures/Manny/T_Manny_02_CCRCCPlastic_MSK.uasset
new file mode 100644
index 0000000..6b381a5
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Textures/Manny/T_Manny_02_CCRCCPlastic_MSK.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7cbed690c700c91f50e0869f80afc58d5cd23827937560acfc509840ea9567a4
+size 13144001
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Textures/Manny/T_Manny_02_D.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Textures/Manny/T_Manny_02_D.uasset
new file mode 100644
index 0000000..752f2ce
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Textures/Manny/T_Manny_02_D.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c352d07a48e228219b32dee09b7668594655cfca59d97b15dc8cb1a49e1eadd9
+size 9033629
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Textures/Manny/T_Manny_02_MSR_MSK.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Textures/Manny/T_Manny_02_MSR_MSK.uasset
new file mode 100644
index 0000000..dfd2fda
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Textures/Manny/T_Manny_02_MSR_MSK.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ec66618f04ba83fca769b035feb5c006ff10a3e96e661484bb1dcd6b5d810dac
+size 13677722
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Textures/Manny/T_Manny_02_N.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Textures/Manny/T_Manny_02_N.uasset
new file mode 100644
index 0000000..c5c838b
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Textures/Manny/T_Manny_02_N.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8966f6bab5c41e69e5145b4f17e95ab65fb31e917c74eef271051a566da80c7c
+size 7269441
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Textures/Manny/T_Manny_02_Tan.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Textures/Manny/T_Manny_02_Tan.uasset
new file mode 100644
index 0000000..558b417
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Textures/Manny/T_Manny_02_Tan.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:38467e22cf6c7828771d7f8184c9d6fed06b6711b35614f9760b64403e3c4945
+size 2153898
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Textures/Shared/T_UE_Logo_M.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Textures/Shared/T_UE_Logo_M.uasset
new file mode 100644
index 0000000..906fae9
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/GraspingHands/VRHandMeshes/Textures/Shared/T_UE_Logo_M.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f07764fd1a0041865c1cf996725714a2ba3baf0df51a24e58f0adebdd99d5d8f
+size 70066
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/HandSockets/HandSocketBase.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/HandSockets/HandSocketBase.uasset
new file mode 100644
index 0000000..3143dac
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/HandSockets/HandSocketBase.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e9afbf8d9f9e4bbd7cb6db658f61a41bffe5c58c0cda80b9bf40bce72b13c216
+size 16993
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/HandSockets/HandSocketMat.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/HandSockets/HandSocketMat.uasset
new file mode 100644
index 0000000..6f7bc67
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/HandSockets/HandSocketMat.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:60d11e9fd42cd8cf37367a275aa7c98a8b428cc98942013db797bca364e2e287
+size 114250
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/PendingPC.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/PendingPC.uasset
new file mode 100644
index 0000000..008bcb0
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/PendingPC.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6b2167e82facf92adbeb93c08a63b213ea320776aa96d89e31d0bc35642710b2
+size 27086
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/PhysicalMats/PM_Alum.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/PhysicalMats/PM_Alum.uasset
new file mode 100644
index 0000000..99a6a72
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/PhysicalMats/PM_Alum.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a021ff228a4ae98acdcfcca3b75825059bd652798dc000eb5e76f5dc8f4e798a
+size 1379
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/PhysicalMats/PM_Hardwood.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/PhysicalMats/PM_Hardwood.uasset
new file mode 100644
index 0000000..05822fc
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/PhysicalMats/PM_Hardwood.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:99d0b46af625ca3c6feba9a8fe1fcce90e7e3fd972c2389cb4b82deb4384ab88
+size 1477
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/PhysicalMats/PM_Steel.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/PhysicalMats/PM_Steel.uasset
new file mode 100644
index 0000000..9111aaa
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/PhysicalMats/PM_Steel.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b359ecff9c2e3953ce83e8066ccf15a5ceb63f8ab5346bd15039eb6896b0b19b
+size 1383
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Template_VR_Player_Controller.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Template_VR_Player_Controller.uasset
new file mode 100644
index 0000000..db7358e
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/Template_VR_Player_Controller.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7c8d0e7108076c15c4f87b88ea83abd84b50226f91a2c83c7007f867365aeec1
+size 238162
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/VRGameInstance.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/VRGameInstance.uasset
new file mode 100644
index 0000000..553aa52
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/VRGameInstance.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:71935ffebc175ba938413214c8fc35a1401603b164ee247025ecaf5dd9c44e4a
+size 9094
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/VRGameMode.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/VRGameMode.uasset
new file mode 100644
index 0000000..ed10686
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/VRGameMode.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ff2a09974e04b0cb5e29b828479eab8d9eeb50a464db264533519340783ebcf1
+size 26008
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/VRViewportClass.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/VRViewportClass.uasset
new file mode 100644
index 0000000..aa56d16
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Core/VRViewportClass.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:dff16568830426dbf60838ea138a16bfe440db8055e1ed3f15943229c1816c07
+size 6006
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/ExampleMap/LevelSMs/Box_Brush13_StaticMesh.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/ExampleMap/LevelSMs/Box_Brush13_StaticMesh.uasset
new file mode 100644
index 0000000..5910f8c
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/ExampleMap/LevelSMs/Box_Brush13_StaticMesh.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:10391fea96ab434d99d1d23737016a268a2c42440c7fad7d07f12c446d0f13a0
+size 18467
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/ExampleMap/LevelSMs/Box_Brush14_StaticMesh.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/ExampleMap/LevelSMs/Box_Brush14_StaticMesh.uasset
new file mode 100644
index 0000000..cbfebc2
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/ExampleMap/LevelSMs/Box_Brush14_StaticMesh.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b4e4a7867f411e848f08c9170a12920a9500666027432e8ef6c233a7dbf41c55
+size 24257
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/ExampleMap/LevelSMs/Box_Brush22_StaticMesh.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/ExampleMap/LevelSMs/Box_Brush22_StaticMesh.uasset
new file mode 100644
index 0000000..0896e49
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/ExampleMap/LevelSMs/Box_Brush22_StaticMesh.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ebd328ddb48f91e11e4573b8357152ce511bdd249a2cdd8c629a770026651045
+size 17077
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/ExampleMap/LevelSMs/Box_Brush2_StaticMesh.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/ExampleMap/LevelSMs/Box_Brush2_StaticMesh.uasset
new file mode 100644
index 0000000..ce850f7
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/ExampleMap/LevelSMs/Box_Brush2_StaticMesh.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e8250f324987ba218861508e39eec873fde808e69e570e10547bad10df042a0c
+size 15733
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/ExampleMap/LevelSMs/Box_Brush3_StaticMesh.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/ExampleMap/LevelSMs/Box_Brush3_StaticMesh.uasset
new file mode 100644
index 0000000..6e3dd00
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/ExampleMap/LevelSMs/Box_Brush3_StaticMesh.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6e4653a80131ccccae3c410b5ac3c21b6e8575ef19b950e1fed0226a57aba5cd
+size 15450
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/ExampleMap/LevelSMs/Box_Brush4_StaticMesh.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/ExampleMap/LevelSMs/Box_Brush4_StaticMesh.uasset
new file mode 100644
index 0000000..3f23cd5
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/ExampleMap/LevelSMs/Box_Brush4_StaticMesh.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c2bc60803baf2e8b20485b5b0ec2facdc2384794f93c66754b2cabc5d64cfcf4
+size 15912
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/ExampleMap/LevelSMs/Box_Brush6_StaticMesh.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/ExampleMap/LevelSMs/Box_Brush6_StaticMesh.uasset
new file mode 100644
index 0000000..828d009
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/ExampleMap/LevelSMs/Box_Brush6_StaticMesh.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4e90fe931bea94598a40a5f072e81f0c40c05ae18375267915c5dffda2af0570
+size 16340
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/ExampleMap/LevelSMs/Box_Brush7_StaticMesh.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/ExampleMap/LevelSMs/Box_Brush7_StaticMesh.uasset
new file mode 100644
index 0000000..2747418
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/ExampleMap/LevelSMs/Box_Brush7_StaticMesh.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:91a7bde3205120e3ce15cea8b3ade316a2c3d6db86f7f3833fb7ba4834f15529
+size 15585
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/ExampleMap/LevelSMs/Box_Brush8_StaticMesh.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/ExampleMap/LevelSMs/Box_Brush8_StaticMesh.uasset
new file mode 100644
index 0000000..ca5f886
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/ExampleMap/LevelSMs/Box_Brush8_StaticMesh.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f6f64c3beeac932365b4a9e55db67408a28afd156c5a4a24ff107881c7304f4b
+size 16785
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/ExampleMap/LevelSMs/Box_Brush_StaticMesh.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/ExampleMap/LevelSMs/Box_Brush_StaticMesh.uasset
new file mode 100644
index 0000000..6c62f89
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/ExampleMap/LevelSMs/Box_Brush_StaticMesh.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7fc221df0d911f5870dd3b1c23d01399a86b89612b69be58ec3fc9f65220a19f
+size 20563
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/ExampleMap/LevelSMs/Curved_Stair_Brush_StaticMesh.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/ExampleMap/LevelSMs/Curved_Stair_Brush_StaticMesh.uasset
new file mode 100644
index 0000000..7353f24
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/ExampleMap/LevelSMs/Curved_Stair_Brush_StaticMesh.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:66b277c3d6d61654ce7d50678eeb8eb3be4d3f7d70e88b141197024405606a7f
+size 22965
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/ExampleMap/MotionControllerMap.umap b/VIRTUOS_ExpansionPluginTests/Content/VRE/ExampleMap/MotionControllerMap.umap
new file mode 100644
index 0000000..c65a3ae
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/ExampleMap/MotionControllerMap.umap
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5d015c184eb3a1c25f797831cf80c2709992944e896cfe693d981f3766235714
+size 785879
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/BP_PickupCube.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/BP_PickupCube.uasset
new file mode 100644
index 0000000..b6f7678
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/BP_PickupCube.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bd1a07bf541a81e9cce6b581bf7cdadb1020431e5e3c32c8d456b15f392e8e92
+size 150174
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Button/ButtonActor.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Button/ButtonActor.uasset
new file mode 100644
index 0000000..464ec13
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Button/ButtonActor.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b9dfb7afff84b77ce2fb54b930025a7c10995cf582ddda533a578458ece2cf75
+size 101811
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Button/ButtonBase.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Button/ButtonBase.uasset
new file mode 100644
index 0000000..526d831
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Button/ButtonBase.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:64cc2164c96336613f8fe61a818946638eeb7b0bb21402e1fd63ed6fb61d1648
+size 35104
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Button/ButtonComponent.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Button/ButtonComponent.uasset
new file mode 100644
index 0000000..894caf2
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Button/ButtonComponent.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:95bff05325a75be2a611e3470e106230ac6cb2bacc602f184ac6a88ea5e5b11b
+size 379820
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Button/ButtonTop.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Button/ButtonTop.uasset
new file mode 100644
index 0000000..c1a6218
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Button/ButtonTop.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a81a6225e3093b7eb97a204fcdf6500ad2b9c2dea96dea3f55c576a8bc59986d
+size 66155
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Button/Button_Mat_Inst.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Button/Button_Mat_Inst.uasset
new file mode 100644
index 0000000..e2ed19e
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Button/Button_Mat_Inst.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:80150b19456873f2b10d0e4b9b989640bc09f71baeead650a50f3beb306d875b
+size 66333
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Button/ColorChangingButtonComponent.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Button/ColorChangingButtonComponent.uasset
new file mode 100644
index 0000000..ef0a800
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Button/ColorChangingButtonComponent.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:69830f818201f1723d8983d309044fd0f41227fc256509527e072f3747012e14
+size 62983
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Button/EButtonType.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Button/EButtonType.uasset
new file mode 100644
index 0000000..cdb8760
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Button/EButtonType.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5fb8ec1bd634dd924cf1c44fd8ee5e0aa39229e875137f63d6181938be77cef5
+size 2304
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Button/ElectricalObject.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Button/ElectricalObject.uasset
new file mode 100644
index 0000000..4159efb
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Button/ElectricalObject.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cf4cc3e58aec9a98fa0bfe9f03ada09ce59d8d711734f053d9996c8b93bbd13d
+size 12597
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Button/Gray.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Button/Gray.uasset
new file mode 100644
index 0000000..4967146
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Button/Gray.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a23deb87e91daf4031a23e2c72bc6c55c61dbf997b498c969f1b735d74dabea9
+size 6069
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Button/Green.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Button/Green.uasset
new file mode 100644
index 0000000..8a8d315
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Button/Green.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:61cd829c1307abb54d7865e377f7ca8f11eb73496809713d1695055985d14a6b
+size 17999
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Cabinet/Cabinet.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Cabinet/Cabinet.uasset
new file mode 100644
index 0000000..e1c5ebb
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Cabinet/Cabinet.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:23dfe23b8227529cf453cd66af662b3443346125efcf7f9ffdd1566421bff6aa
+size 56771
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Cabinet/CabinetBase.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Cabinet/CabinetBase.uasset
new file mode 100644
index 0000000..18ae603
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Cabinet/CabinetBase.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:90a1b2da8d54bd1cbec340ba64c6d9bf50118348e67a090ce036f35d8d96f1e6
+size 20834
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Cabinet/CabinetDoor.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Cabinet/CabinetDoor.uasset
new file mode 100644
index 0000000..ce7ccae
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Cabinet/CabinetDoor.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a69477ea30c64b3a4c2427fa6f83c14135dd8db699a08024fa7f0edeb952ebdd
+size 17717
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Cabinet/Gray.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Cabinet/Gray.uasset
new file mode 100644
index 0000000..4906592
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Cabinet/Gray.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5522aa366ce2d692e0012d2440f74795abe1ae374e7c38a87c805642c1ce71f9
+size 81208
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Cabinet/Green.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Cabinet/Green.uasset
new file mode 100644
index 0000000..de6b2c7
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Cabinet/Green.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2a8838140790a8f0c9b45a47fe6b690c968fefa14881ca723ca28fe28cd383fa
+size 94280
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Cabinet/Lime.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Cabinet/Lime.uasset
new file mode 100644
index 0000000..3e00508
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Cabinet/Lime.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:085d6332bf28450397ebbc9bd8fce6a136cec6db508843e0a3b436a10e087463
+size 94098
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Camera/Black.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Camera/Black.uasset
new file mode 100644
index 0000000..07912f0
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Camera/Black.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ba8324a8a46e6155cb2a24ada3e678b52e17ec6d8066d0b98fe035815c37cc87
+size 25458
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Camera/CameraRecorder.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Camera/CameraRecorder.uasset
new file mode 100644
index 0000000..ced8b0a
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Camera/CameraRecorder.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:79702223e28a0f8b0251e0bd4e4511483c0ecf9c1a6b5c1255ebb68ec3057284
+size 268529
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Camera/CameraRenderTarget.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Camera/CameraRenderTarget.uasset
new file mode 100644
index 0000000..e39807d
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Camera/CameraRenderTarget.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2f32afb2d3f030b63aa27285f455371b0191343097d1d98f1a4abd79860fccfd
+size 3641
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Camera/CameraScreen.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Camera/CameraScreen.uasset
new file mode 100644
index 0000000..f339cde
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Camera/CameraScreen.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e8a978d8e277ac685477e77140a5f6b05fb3bfcaeffbf32fb69e7299c1204d7c
+size 41802
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Camera/CameraScreenMat.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Camera/CameraScreenMat.uasset
new file mode 100644
index 0000000..41d0331
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Camera/CameraScreenMat.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:903272c3b4530e07619493df888b6f6e37b152ff9d8102fad6e3cecffd942677
+size 10873
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Camera/Camerabody.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Camera/Camerabody.uasset
new file mode 100644
index 0000000..8399df8
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Camera/Camerabody.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6426aa8965d838f8d7e3687ede63be7ba029c866339b43fa42cba0984a108767
+size 75902
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Camera/ForeGroundRenderTarget.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Camera/ForeGroundRenderTarget.uasset
new file mode 100644
index 0000000..b39d757
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Camera/ForeGroundRenderTarget.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f909b074d1c940bbdcb9c9802758d09d6041b228899315a89826249089675c88
+size 3492
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Camera/GreenScreen_PP.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Camera/GreenScreen_PP.uasset
new file mode 100644
index 0000000..f708984
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Camera/GreenScreen_PP.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:334a096855afefda3acaa1bcf70eb2bf7e820e81d8bfb4afc39f66d8b9bc576d
+size 65017
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Camera/MixedOutput.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Camera/MixedOutput.uasset
new file mode 100644
index 0000000..5f0bd9d
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Camera/MixedOutput.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a988c604e1874c549b15b7c8453cb95c167cef32360216ddee3167d028176b9f
+size 3377
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Camera/MixedOutput_Mat.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Camera/MixedOutput_Mat.uasset
new file mode 100644
index 0000000..8781aa0
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Camera/MixedOutput_Mat.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:041a1ea331c26a214ac6732716105ff8dab91bdd795972b470299eca9225f493
+size 95569
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Camera/MixedReality_MPC.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Camera/MixedReality_MPC.uasset
new file mode 100644
index 0000000..004fee2
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Camera/MixedReality_MPC.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:559661367178dcdca61fb03f772e60fc6cb6018940c5c391335de3a1f9628b92
+size 2150
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Camera/White.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Camera/White.uasset
new file mode 100644
index 0000000..733fbfc
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Camera/White.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0e575c89ace5e2d09c5bd99780b595cf63284e352497b74015a5fb4cbc03705f
+size 74950
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DialIndicator/DialComponent.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DialIndicator/DialComponent.uasset
new file mode 100644
index 0000000..d4a89ed
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DialIndicator/DialComponent.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8e1a99dd8b3ca2b77246336f1d27da028e603b27eac7f771784f68fe25e3afd2
+size 131681
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DialIndicator/DialIndicator.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DialIndicator/DialIndicator.uasset
new file mode 100644
index 0000000..5504cec
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DialIndicator/DialIndicator.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1af2ccaa51feb7292eab50373fd1c638172ef7bc2862fe88f0f5eb5f0013300d
+size 196537
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DialIndicator/DialIndicatorBase.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DialIndicator/DialIndicatorBase.uasset
new file mode 100644
index 0000000..5e4d882
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DialIndicator/DialIndicatorBase.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:35474a9568c69f94180bcb1123c25a7e4aeda891aa15c7fa65c4b0172e8faea4
+size 16474
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DialIndicator/DialIndicatorTop.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DialIndicator/DialIndicatorTop.uasset
new file mode 100644
index 0000000..b46d0e3
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DialIndicator/DialIndicatorTop.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:46741e029d1ec16ed7c83772456430b084feb98914228afb72a5a1918848b2de
+size 119906
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DialIndicator/Green.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DialIndicator/Green.uasset
new file mode 100644
index 0000000..0ad2666
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DialIndicator/Green.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:45dce261f7182c28d550ceb3a520de2d91c582c6972fff0c1d0be43302cfd43b
+size 36251
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DialIndicator/NativeDialIndicator.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DialIndicator/NativeDialIndicator.uasset
new file mode 100644
index 0000000..c81658f
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DialIndicator/NativeDialIndicator.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:802e00e2bd13dc44b65b6622cab36622cd7a2d81b8953d101b84baf34751344b
+size 145751
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DialIndicator/Turquoise.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DialIndicator/Turquoise.uasset
new file mode 100644
index 0000000..f1a2768
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DialIndicator/Turquoise.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2a6cd83380bf55b9abde582cd88ef63374cdcde8a2f936bc9d4fc04ca3a7d747
+size 39285
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/DeadBolt.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/DeadBolt.uasset
new file mode 100644
index 0000000..f60b36a
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/DeadBolt.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d6cf5a73b1099be46fc9a148dfb2763094df9f0b64763d56c0aee985ff0ae85b
+size 30756
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/DeadboltKnob.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/DeadboltKnob.uasset
new file mode 100644
index 0000000..a2087d5
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/DeadboltKnob.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b661932077b81f5914b0d14503944aee305c467ccbc59df2404b22fb4e57fef5
+size 20444
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/DeadboltKnobBase.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/DeadboltKnobBase.uasset
new file mode 100644
index 0000000..484650b
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/DeadboltKnobBase.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a5ed7dac671a4a05a230b858c5054c1941201a2d5192a8a888c5bda08192942e
+size 37396
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/DoorActor.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/DoorActor.uasset
new file mode 100644
index 0000000..5e16d72
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/DoorActor.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ce17327cd3d5f8a2a24afd737e596de392359fecc7e613930d57d293e3a8baa9
+size 482616
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/DoorHandle.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/DoorHandle.uasset
new file mode 100644
index 0000000..d084f8a
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/DoorHandle.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ef34824e65f8fcc98cbc0b8a38062f592e2601e52b4d3d004d14d4a5bc16de1b
+size 27188
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/DoorHandleBase.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/DoorHandleBase.uasset
new file mode 100644
index 0000000..05afe5a
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/DoorHandleBase.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:890004a21ec396b826fb1278c0666608f78736c089e47b7885af438d21e10932
+size 42483
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/DoorHandleLever.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/DoorHandleLever.uasset
new file mode 100644
index 0000000..c23ef09
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/DoorHandleLever.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:eb8d56160493cd0a40c091068c5b492bfaed26c102e435449754cf7d95fa225e
+size 65367
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/DoorKey.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/DoorKey.uasset
new file mode 100644
index 0000000..696d794
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/DoorKey.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:00e2b7ebe5483e565299514705089198b9d04fd4a76bed5a9ede8792d62fb0e6
+size 408094
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/Key.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/Key.uasset
new file mode 100644
index 0000000..11e5242
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/Key.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:dd857ca55f15461848584b49987fdfb1131681c62be2b5a3440b0fef2f466764
+size 43253
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/KeyBase.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/KeyBase.uasset
new file mode 100644
index 0000000..201b05d
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/KeyBase.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6a24c3814a4bb15da918c05eba3728a94898ba82527bb36dd1f2db583ab8dcbe
+size 43831
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/Keycore.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/Keycore.uasset
new file mode 100644
index 0000000..c7312f8
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/Keycore.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c14d750494e0099b614637dfecfe27116eea9e8e5cfe7ec7c95231d3964dbdc2
+size 23166
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/defaultMat.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/defaultMat.uasset
new file mode 100644
index 0000000..d40dcb8
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/defaultMat.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1aacfe5a0b4bc6f62d18c26e216bda6ee8e4c683256878385c7c2cfef7f9f7c5
+size 110540
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/door1_aldoor.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/door1_aldoor.uasset
new file mode 100644
index 0000000..f6c8903
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/door1_aldoor.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a5260874df1dde108b2931d679350eb6e821cb645d7747649cb07c3d2ee77a3c
+size 21908
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/door1_door.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/door1_door.uasset
new file mode 100644
index 0000000..6d43d3d
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/door1_door.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4263e50843e1888f724b10d1f4420ee4dc3d1fb8b7ac94ef12b404250ec53c09
+size 24094
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/glas.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/glas.uasset
new file mode 100644
index 0000000..8719abd
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/glas.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:194000a2f547acfa85ed79129f880db150a504b26246552e62f92a511e5056d6
+size 28970
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/wood.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/wood.uasset
new file mode 100644
index 0000000..863d589
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/wood.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a1e195f085561bbc017273590f789c4b5e9adca817f7836aba89152d2efc903c
+size 31512
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/wood2.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/wood2.uasset
new file mode 100644
index 0000000..9eab57d
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/wood2.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:fe79c61941651446755e024ce6a47ea35d3d0f58598a5e3f4e02c35f13035a76
+size 31516
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/woodteak.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/woodteak.uasset
new file mode 100644
index 0000000..7ee30cb
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Door/woodteak.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:91b701891d8e479854277c3a4dbe0f4c521ba2df1dc32ad1a7bd01cbac1624ab
+size 2433033
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DrawerBase/DrawerBaseActor.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DrawerBase/DrawerBaseActor.uasset
new file mode 100644
index 0000000..db9dff4
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DrawerBase/DrawerBaseActor.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b22af8781853e2c43144ac2c17cb5a3a43cda1a23151cdb66d8aed2665ad7b9b
+size 77028
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DrawerBase/temp_desk.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DrawerBase/temp_desk.uasset
new file mode 100644
index 0000000..c7bf0cb
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DrawerBase/temp_desk.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:87acc845c797c51c77d36f249df27f50ca137ca4e2fbdad94334292ea880fa55
+size 34344
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DrawerBase/temp_desk_drawer1.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DrawerBase/temp_desk_drawer1.uasset
new file mode 100644
index 0000000..14718e1
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DrawerBase/temp_desk_drawer1.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:052e9fa2d2d7844857b79951dfff6dc228fe2c96194c944939bf660772221d50
+size 22293
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DrawerBase/temp_desk_drawer2.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DrawerBase/temp_desk_drawer2.uasset
new file mode 100644
index 0000000..5e7ce1c
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DrawerBase/temp_desk_drawer2.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:346c24d947e981e760abe6c888a1ec69eec72382d862ea2ae698300c7a34f883
+size 22608
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DrawingBoard/BaseWhiteBoardTool.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DrawingBoard/BaseWhiteBoardTool.uasset
new file mode 100644
index 0000000..b206c1e
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DrawingBoard/BaseWhiteBoardTool.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e1b5385b120dae1c65a3b9b1260cd87fcd68d2e45949bb5b3648aa3e145ce611
+size 270932
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DrawingBoard/DrawingBoard.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DrawingBoard/DrawingBoard.uasset
new file mode 100644
index 0000000..5e31cad
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DrawingBoard/DrawingBoard.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0e5f476146385a2fcb8a1ea30be8c211a18246a38bf3bb7bad7a6f35b7c082e7
+size 125384
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DrawingBoard/Eraser.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DrawingBoard/Eraser.uasset
new file mode 100644
index 0000000..ee1d1c2
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DrawingBoard/Eraser.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1ebb1e920500f60c3603fc5c4ea4ee6b9bb8583471c3d5f3d2d71f4fdaaef147
+size 153827
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DrawingBoard/MERGED_Eraser.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DrawingBoard/MERGED_Eraser.uasset
new file mode 100644
index 0000000..aac71cc
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DrawingBoard/MERGED_Eraser.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:97e35d483072b22159307911c038852fad0b03662f9692614157623bbc8a0f18
+size 15301
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DrawingBoard/MERGED_Marker.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DrawingBoard/MERGED_Marker.uasset
new file mode 100644
index 0000000..ba476df
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DrawingBoard/MERGED_Marker.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:088d9e4a46d0410b8ec1de2f0ec286a018f472537429ba39aa316698eef3f951
+size 31118
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DrawingBoard/MERGED_SprayCan.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DrawingBoard/MERGED_SprayCan.uasset
new file mode 100644
index 0000000..8cee56d
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DrawingBoard/MERGED_SprayCan.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5c83a20b95e4f03a274b29badf291d9df18823fb9e23f69332654b10efc80ad2
+size 58204
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DrawingBoard/Marker.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DrawingBoard/Marker.uasset
new file mode 100644
index 0000000..ea7d2cf
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DrawingBoard/Marker.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6e3a7dec1935eb88950bd50ce7893644aca8287f0d0db45dd28de4bf707d8ef0
+size 151638
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DrawingBoard/MarkerBoardMat.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DrawingBoard/MarkerBoardMat.uasset
new file mode 100644
index 0000000..fee99be
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DrawingBoard/MarkerBoardMat.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8a6d31a42a324dd4e72d005062383eb028b68aaacb27139df5a712d59a028139
+size 9469
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DrawingBoard/MarkerMat.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DrawingBoard/MarkerMat.uasset
new file mode 100644
index 0000000..d63f422
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DrawingBoard/MarkerMat.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e3c5a7a4236ea97fcc5be4c3c595dad5bfd2588857020a6bcbe830c3fbf3c215
+size 94918
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DrawingBoard/Mat_Eraser.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DrawingBoard/Mat_Eraser.uasset
new file mode 100644
index 0000000..3ad5b0a
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DrawingBoard/Mat_Eraser.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4f380476ebb6ea8b12ea9b8d2b9cc4e696212e39107b2b74e7107714228c4116
+size 85981
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DrawingBoard/SprayCan.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DrawingBoard/SprayCan.uasset
new file mode 100644
index 0000000..9ee4d52
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/DrawingBoard/SprayCan.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:df42bf574eaa21c3b4788bb5453bf55b69c5fd7f7eba1a102aeeb5d5ef866efe
+size 152211
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Gestures/GestureViewer.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Gestures/GestureViewer.uasset
new file mode 100644
index 0000000..32a6e37
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Gestures/GestureViewer.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:952c0bde0af4effcc991bbc910dae104918d5cabd1e2c82af091e871d2dec210
+size 171940
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Gestures/GestureWand.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Gestures/GestureWand.uasset
new file mode 100644
index 0000000..f8b847b
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Gestures/GestureWand.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f39b3b35bf1867075c1861faf5c36fc09e53209693f48e985f25d6441bf088ba
+size 187040
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Gestures/VRGestures.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Gestures/VRGestures.uasset
new file mode 100644
index 0000000..ee529cc
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Gestures/VRGestures.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:58529d9b47718196ede8460d3e80a9d319af36b502de104f0e970b52c6624c49
+size 10528
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/GrippableChar/GrippableChar.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/GrippableChar/GrippableChar.uasset
new file mode 100644
index 0000000..c7f5243
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/GrippableChar/GrippableChar.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6096c920fbcec3f2a7a87f0eb757f6db906eebe236a32b92b0a8c30be2407d0a
+size 50949
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/GrippableChar/GrippableManniquin.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/GrippableChar/GrippableManniquin.uasset
new file mode 100644
index 0000000..fe5f4ba
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/GrippableChar/GrippableManniquin.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cc29ab5ef52d0eb01c7863e443bc4c1ba5e1fee229f7031539219f4bfc5fa01d
+size 94552
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/HandTruck/HandTruck.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/HandTruck/HandTruck.uasset
new file mode 100644
index 0000000..a87804c
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/HandTruck/HandTruck.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2e50b5b22fb83eea948452c2e776b1e057316c067af2736da2ad18f9dde59caf
+size 48570
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/HandTruck/MI_HandTruck.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/HandTruck/MI_HandTruck.uasset
new file mode 100644
index 0000000..9e9d95f
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/HandTruck/MI_HandTruck.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:44a8465deb9a30bfe437f77414213a197080177c58e9a1801583c016c62cf4a7
+size 13945
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/HandTruck/SM_HandTruck_Body.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/HandTruck/SM_HandTruck_Body.uasset
new file mode 100644
index 0000000..7633858
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/HandTruck/SM_HandTruck_Body.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:55dceb9c905187abbb630ae206ca2398fce1d154fe7f1047b2d88bbc4c67733a
+size 131100
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/HandTruck/SM_HandTruck_Wheel.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/HandTruck/SM_HandTruck_Wheel.uasset
new file mode 100644
index 0000000..fafb830
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/HandTruck/SM_HandTruck_Wheel.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:05b34d87202a0e97df3a1dd0998191d1612aa1d3e3e0058ed25b2787f578fbf4
+size 71160
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/HandTruck/T_HandTruck_AliOweidah_BaseColor.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/HandTruck/T_HandTruck_AliOweidah_BaseColor.uasset
new file mode 100644
index 0000000..93a4f0f
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/HandTruck/T_HandTruck_AliOweidah_BaseColor.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e26c48e0e746ce733c7772a3101f1092cd18b26995cf6cd31f990d150c74e125
+size 4370558
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/HandTruck/T_HandTruck_AliOweidah_MetallicRoughnessAO.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/HandTruck/T_HandTruck_AliOweidah_MetallicRoughnessAO.uasset
new file mode 100644
index 0000000..90b5d28
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/HandTruck/T_HandTruck_AliOweidah_MetallicRoughnessAO.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:83f7dacb84501221919aefa415fd647560835c33cbd7dd72bcf80d3380d2684c
+size 4949633
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/HandTruck/T_HandTruck_AliOweidah_Normal.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/HandTruck/T_HandTruck_AliOweidah_Normal.uasset
new file mode 100644
index 0000000..760ce57
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/HandTruck/T_HandTruck_AliOweidah_Normal.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:47b08848526ab3fff7b97dcdff9d64540f46de6f49b8ad3fb7764ffa80c2f507
+size 7035954
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/InteractibleTut/TutorialSlider.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/InteractibleTut/TutorialSlider.uasset
new file mode 100644
index 0000000..cc5be48
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/InteractibleTut/TutorialSlider.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9ef355113eccb855e9ffac1cb2506f3b359caf156893d5bf2f8b535b0d62ce94
+size 252172
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Lever/Gray.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Lever/Gray.uasset
new file mode 100644
index 0000000..ce69622
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Lever/Gray.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f500399c20585f9041847650342b73a32df68dd9fc4eeea74aa6f80b768056d7
+size 32808
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Lever/Green.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Lever/Green.uasset
new file mode 100644
index 0000000..c825617
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Lever/Green.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:582c09c7a5b6b10b2db4d0ab62cf1a123eae396406cc68e79b26bc4843719511
+size 36243
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Lever/LeverActor.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Lever/LeverActor.uasset
new file mode 100644
index 0000000..9f6a7b4
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Lever/LeverActor.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9dfa6ff0b0de5084fe99620f2fbf997891d914b5254b37b88be27a9eb30bb6b9
+size 222796
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Lever/LeverBase.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Lever/LeverBase.uasset
new file mode 100644
index 0000000..379e507
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Lever/LeverBase.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:42a3bb07b48e9a7d3f936b7e686429341c96e06650c95c3b54d4aa1faa8bddac
+size 17163
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Lever/LeverComponent.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Lever/LeverComponent.uasset
new file mode 100644
index 0000000..3a6209e
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Lever/LeverComponent.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:438604f263cfe5e15e41eeecb95dee988b09f1678eedbcb7144d9e6717e9e832
+size 245397
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Lever/LeverTop.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Lever/LeverTop.uasset
new file mode 100644
index 0000000..e05db9c
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Lever/LeverTop.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2e8b02589009be42fac9ed88b160195d6dbbc22ad20b87d1d5bf55998c58a234
+size 77004
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Lever/NativeLeverActor.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Lever/NativeLeverActor.uasset
new file mode 100644
index 0000000..127358f
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Lever/NativeLeverActor.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3a17a4ef9754e7c8c1b3dd8f963efc63bafe68c8c63f99bb5838b645765851e5
+size 87973
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Lever/NativeLeverActor_ThrowSwitch.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Lever/NativeLeverActor_ThrowSwitch.uasset
new file mode 100644
index 0000000..669b455
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Lever/NativeLeverActor_ThrowSwitch.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0c0b143a789a7c12743c7a0677aaeb9cd63c869f6bd997710023a24ef818ca5d
+size 131241
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Microphone/aluminium.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Microphone/aluminium.uasset
new file mode 100644
index 0000000..e827cc3
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Microphone/aluminium.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6e5f45a7f810a4a94b8455fe4652d245e31d71d30378ca4505877721d7d7ba3c
+size 29654
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Microphone/black_mate_plastic.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Microphone/black_mate_plastic.uasset
new file mode 100644
index 0000000..4cbc444
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Microphone/black_mate_plastic.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:54369a67837f6d040b25cfa951e999d05a17124736b65c2b6897653606230d4a
+size 10786
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Microphone/black_poli_plastic.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Microphone/black_poli_plastic.uasset
new file mode 100644
index 0000000..49d9a73
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Microphone/black_poli_plastic.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:761abe2dd65aa06e948ce2a388d1914d5f1bdd096ed334ff28182e51eb697123
+size 10675
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Microphone/fabric.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Microphone/fabric.uasset
new file mode 100644
index 0000000..f675c5f
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Microphone/fabric.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:33c0118c19b766a04ce2221316160a1ad5b55dc993e125a084ee3a37cee77d0d
+size 41501
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Microphone/net.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Microphone/net.uasset
new file mode 100644
index 0000000..9ba2a96
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Microphone/net.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7dc56ef826ad8f792fcf1a164dc40361aa5f8a1c1327f2cdab4e6e2aac91e1b1
+size 37417
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Microphone/wireless_vocal_microphone.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Microphone/wireless_vocal_microphone.uasset
new file mode 100644
index 0000000..f1bcd49
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Microphone/wireless_vocal_microphone.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:fa8c1bb3639baa0add6a8b0dc37002da24d8ed7e4503fb9be0c1a9358b07c225
+size 3734774
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/PPOutline/PP_OutlineCustomDepthOcclusion.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/PPOutline/PP_OutlineCustomDepthOcclusion.uasset
new file mode 100644
index 0000000..e497447
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/PPOutline/PP_OutlineCustomDepthOcclusion.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a6f76aceabfbf35f0acd3c0d1c7193472fa8b75e3c9da7827d4ee4208ec6a3ca
+size 70071
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/PPOutline/PP_OutlineCustomDepthOcclusion_Inst.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/PPOutline/PP_OutlineCustomDepthOcclusion_Inst.uasset
new file mode 100644
index 0000000..cbcd874
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/PPOutline/PP_OutlineCustomDepthOcclusion_Inst.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e6e1239e4c895b808e8dd2bb2be41d0814d82a3e5ad832e199e65b3fb865fe10
+size 24471
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/PickupCube.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/PickupCube.uasset
new file mode 100644
index 0000000..8a90b37
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/PickupCube.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c2def2681e56880ae4a57c5059c4dbe614d2ccd61f636567526124246bd090ea
+size 31105
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Potion/Potion.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Potion/Potion.uasset
new file mode 100644
index 0000000..eb74bee
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Potion/Potion.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2c83598eb5ca29960b40b59fb57e4ff9c5f93d3c375c6e81e2c5a6b839f30e1b
+size 73721
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Potion/PotionActor.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Potion/PotionActor.uasset
new file mode 100644
index 0000000..348da12
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Potion/PotionActor.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e8ea4d400a5353c6deaaef479e751bb8ad4637529c0d624334dae075082bb5ee
+size 287635
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Potion/PotionMat.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Potion/PotionMat.uasset
new file mode 100644
index 0000000..9063951
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Potion/PotionMat.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f152c24befbe476276a7ad62a7d0d36ca359e93337ad4212b6c816e8df53a801
+size 10279
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Potion/Stopper.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Potion/Stopper.uasset
new file mode 100644
index 0000000..a7d9849
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/Potion/Stopper.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d0672aa3b1222e3cc5bfe2ccc381f2cfa5fc69c67caa6b789727ee8a9a6b9a40
+size 33960
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/SelfGraspActor/SelfGraspExample.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/SelfGraspActor/SelfGraspExample.uasset
new file mode 100644
index 0000000..ddb6a34
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/SelfGraspActor/SelfGraspExample.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:73668b1ac902a37543284fa7b3cbf3adc2b0ad444eff101370a45867e6403d51
+size 138631
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/SnapPoint/SnapActor.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/SnapPoint/SnapActor.uasset
new file mode 100644
index 0000000..564dfe2
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/SnapPoint/SnapActor.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7369ef195fb24315f8040a71d8183a122d7554ff4583f7a9641e35f76908d52e
+size 174114
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/SnapPoint/SnapPointMat.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/SnapPoint/SnapPointMat.uasset
new file mode 100644
index 0000000..509c299
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Misc/Examples/SnapPoint/SnapPointMat.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:fb1df872f07f671841aa1679f62038b5c62a380627cd8818eb15edb552474ccd
+size 74020
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Materials/Functions/MF_MicroDetail_01a.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Materials/Functions/MF_MicroDetail_01a.uasset
new file mode 100644
index 0000000..3e3dfcd
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Materials/Functions/MF_MicroDetail_01a.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:46de3f6d9eda13a259e2ce89e43509e9e2bbaa2e348b18351084caa581b9ae10
+size 130061
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Materials/Functions/MF_VertexBlend_01a.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Materials/Functions/MF_VertexBlend_01a.uasset
new file mode 100644
index 0000000..56fd432
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Materials/Functions/MF_VertexBlend_01a.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2eb1a3a7be5ef4967b926fcd0438b067d007d36adca9c1bde966ca02832812e2
+size 53424
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Materials/Functions/MI_VertexBlend_02a.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Materials/Functions/MI_VertexBlend_02a.uasset
new file mode 100644
index 0000000..865299b
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Materials/Functions/MI_VertexBlend_02a.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1dfa4ba4f6e7f4468bb26eb5bc081529e6901a923d55b5054736bc7bcec928b5
+size 101357
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Materials/Instances/MI_Cinderstack_01a.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Materials/Instances/MI_Cinderstack_01a.uasset
new file mode 100644
index 0000000..60f79e0
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Materials/Instances/MI_Cinderstack_01a.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3f2c6c9ce1f1e5201bd89b74ca29bc67d735902d6f33ed901af562b187ddb7a2
+size 130236
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Materials/Instances/MI_OilBarrel_01a.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Materials/Instances/MI_OilBarrel_01a.uasset
new file mode 100644
index 0000000..fc56967
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Materials/Instances/MI_OilBarrel_01a.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:237757931ae10a9178c92453c455aad9a514beabb2a36c650e55bec6ad004123
+size 113923
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Materials/Instances/MI_WoodenPalette_01a.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Materials/Instances/MI_WoodenPalette_01a.uasset
new file mode 100644
index 0000000..4149d6b
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Materials/Instances/MI_WoodenPalette_01a.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8b18ff4d445db1dd7afe92ac44c4c9cb6054b4a99d37ca31803cbe6a1d6637ef
+size 117766
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Materials/Masters/MM_Background_01a.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Materials/Masters/MM_Background_01a.uasset
new file mode 100644
index 0000000..0e319ea
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Materials/Masters/MM_Background_01a.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:aab16f3516b9f21762acd32f5b741a4332026fe5b19e0586202bce373c85f291
+size 160378
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Materials/Masters/MM_Decal_01.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Materials/Masters/MM_Decal_01.uasset
new file mode 100644
index 0000000..9ca51b8
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Materials/Masters/MM_Decal_01.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:56b5d41d00b73ca83606fc1a20821b572c464085807de61199ef537800e28ff3
+size 92078
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Materials/Masters/MM_MasterMaterial_01.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Materials/Masters/MM_MasterMaterial_01.uasset
new file mode 100644
index 0000000..121d8bd
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Materials/Masters/MM_MasterMaterial_01.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a41595af710b517ff91f9fe236ab3ac6925b1016df38fa902a6d57944aa8d76f
+size 152973
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Materials/Masters/MM_PP_Sharpen.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Materials/Masters/MM_PP_Sharpen.uasset
new file mode 100644
index 0000000..81aa4bb
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Materials/Masters/MM_PP_Sharpen.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3bae9c42636782d7be44a833b5696bd0d1c09c47cf23bd89c536cdcc50d457c4
+size 97284
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Meshes/SM_Barrel_01.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Meshes/SM_Barrel_01.uasset
new file mode 100644
index 0000000..8529cfc
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Meshes/SM_Barrel_01.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ee089330d04e55771019df07689a86a66c1bc1fed294ce1480d1becd5037463f
+size 53719
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Meshes/SM_CinderStack_Single_01a.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Meshes/SM_CinderStack_Single_01a.uasset
new file mode 100644
index 0000000..18404af
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Meshes/SM_CinderStack_Single_01a.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7350a8a119cf53cf3de4e15b06bebfa8f6c93bdbb335949a4a10f891e4cf2c2c
+size 22573
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Meshes/SM_woodenpalette_01.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Meshes/SM_woodenpalette_01.uasset
new file mode 100644
index 0000000..32eaad9
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Meshes/SM_woodenpalette_01.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5f87ea3210fe0d335d0d36f93b53b06d4398880aedd846e9db8817c54d29a1f1
+size 67207
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Tileable/TX_DebrisTile_ALB.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Tileable/TX_DebrisTile_ALB.uasset
new file mode 100644
index 0000000..a9a9934
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Tileable/TX_DebrisTile_ALB.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:168d5ef3074c9c661e459c699daddbd5c583fe93a0a62d27cd074da5ae312e80
+size 2543556
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Tileable/TX_DebrisTile_NRM.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Tileable/TX_DebrisTile_NRM.uasset
new file mode 100644
index 0000000..29a77e5
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Tileable/TX_DebrisTile_NRM.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c428772f2f3ca07677e5a50c64c2d3db5342d312b93135aa72d40dd3ac2f6e4a
+size 3155023
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Tileable/TX_DebrisTile_RMA.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Tileable/TX_DebrisTile_RMA.uasset
new file mode 100644
index 0000000..1d52381
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Tileable/TX_DebrisTile_RMA.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:009179a5932b69793956fd253e44932b5b1b99821dce2a6fd3c4c74f42c2e2a2
+size 2240008
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Tileable/TX_Dirt_01_ALB.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Tileable/TX_Dirt_01_ALB.uasset
new file mode 100644
index 0000000..c8b4b55
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Tileable/TX_Dirt_01_ALB.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:357531740d3fdd72f7e6be6396b9ed30333a2a5198a4d7554bd8e6211b9696f7
+size 2866443
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Tileable/TX_Micro_MASK.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Tileable/TX_Micro_MASK.uasset
new file mode 100644
index 0000000..e74e684
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Tileable/TX_Micro_MASK.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f2b02bd7ba793d75f52a6e05e13c3e0ff0f391b3cbc3be418c34d85cbb9116cf
+size 564567
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Tileable/TX_Noise_NRM.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Tileable/TX_Noise_NRM.uasset
new file mode 100644
index 0000000..83d1950
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Tileable/TX_Noise_NRM.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:864e9a4b2fede4c864c1dbdfae0af968544381e52dcdc7fa083509288737ac27
+size 660264
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Tileable/TX_RockyMud_01_ALB.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Tileable/TX_RockyMud_01_ALB.uasset
new file mode 100644
index 0000000..f345efa
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Tileable/TX_RockyMud_01_ALB.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d8714f0cbb121f613b39461bafb3d09d66307ba072e70f40aaef18f2510148e5
+size 8390576
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Tileable/TX_RockyMud_01_NRM.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Tileable/TX_RockyMud_01_NRM.uasset
new file mode 100644
index 0000000..7e26d17
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Tileable/TX_RockyMud_01_NRM.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:47dbb52bd3eec831e7d501e841f908770964d44784756130aa817efa741c9aa1
+size 10932150
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Tileable/TX_RockyMud_01_RHA.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Tileable/TX_RockyMud_01_RHA.uasset
new file mode 100644
index 0000000..7829a74
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Tileable/TX_RockyMud_01_RHA.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:dcfac76b178dc5355495ad83b1f19909174e1d869cd189eebf3aa19785232a4d
+size 7534879
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Tileable/TX_RockyMud_02_ALB.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Tileable/TX_RockyMud_02_ALB.uasset
new file mode 100644
index 0000000..d7573fd
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Tileable/TX_RockyMud_02_ALB.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:816d326b9e476fdb06364a6d10243e9b2a2caa0560feb4e691c6e77faaa47b66
+size 7202171
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Tileable/TX_RockyMud_02_NRM.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Tileable/TX_RockyMud_02_NRM.uasset
new file mode 100644
index 0000000..ef620d6
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Tileable/TX_RockyMud_02_NRM.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a1fa9075b4b85acec31277ac058b8503e453c862744d0033aba76a80832066dd
+size 11008277
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Tileable/TX_RockyMud_02_RHA.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Tileable/TX_RockyMud_02_RHA.uasset
new file mode 100644
index 0000000..b8a0d67
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Tileable/TX_RockyMud_02_RHA.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:48e7a11f5e330dc29c7f1dbd9bc38678ca1da39d1a7dd64ba2c36242a91c540b
+size 7413211
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Unique/TX_CinderStack_ALB.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Unique/TX_CinderStack_ALB.uasset
new file mode 100644
index 0000000..5735871
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Unique/TX_CinderStack_ALB.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f2cbb7c7de684ec63939ab6b94aaed76fc7b15dd66e37f4c65202f43928b594a
+size 5091292
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Unique/TX_CinderStack_NRM.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Unique/TX_CinderStack_NRM.uasset
new file mode 100644
index 0000000..a4c0bdf
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Unique/TX_CinderStack_NRM.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:87e5010e03419020639deafb5370c430a545f36e76fcd6d50f1f3ecb6d06af22
+size 6858453
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Unique/TX_CinderStack_RMA.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Unique/TX_CinderStack_RMA.uasset
new file mode 100644
index 0000000..b89fba7
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Unique/TX_CinderStack_RMA.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5dd9080c43668d00a4f4331dd2182ea677f081bd344618a02b24046fbec17dc5
+size 3624022
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Unique/TX_OilBarrel_01a_ALB.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Unique/TX_OilBarrel_01a_ALB.uasset
new file mode 100644
index 0000000..fc98634
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Unique/TX_OilBarrel_01a_ALB.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:13b4702228bd042b016f87904dcb6014a6c0c9dea23a2a4ad33b996c8ebca385
+size 3661816
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Unique/TX_OilBarrel_01a_NRM.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Unique/TX_OilBarrel_01a_NRM.uasset
new file mode 100644
index 0000000..49b84ab
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Unique/TX_OilBarrel_01a_NRM.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a9a2ea530056a112fda331213d76faf4dbcc5dae2038164abd627b7200b164e7
+size 6255274
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Unique/TX_OilBarrel_01a_RMA.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Unique/TX_OilBarrel_01a_RMA.uasset
new file mode 100644
index 0000000..7d8c000
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Unique/TX_OilBarrel_01a_RMA.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0d61bd788260e36eb91619246519745837fe90bd3f501916d57b3a6a71cb87b8
+size 4853191
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Unique/TX_WoodenPalette_01_ALB.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Unique/TX_WoodenPalette_01_ALB.uasset
new file mode 100644
index 0000000..31e27ae
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Unique/TX_WoodenPalette_01_ALB.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e8252f39e2986b4c9cd3cf9ef868867bfd192680799ea67fa40ffa0b116d4a21
+size 8132717
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Unique/TX_WoodenPalette_01_NRM.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Unique/TX_WoodenPalette_01_NRM.uasset
new file mode 100644
index 0000000..461c99b
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Unique/TX_WoodenPalette_01_NRM.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:01d00655b7100c23df0a97c9d225cda5362118d51b8feedba4a2fe60685e4f41
+size 7648965
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Unique/TX_WoodenPalette_01_RMA.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Unique/TX_WoodenPalette_01_RMA.uasset
new file mode 100644
index 0000000..014c3fd
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Unique/TX_WoodenPalette_01_RMA.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:dd149ef586ec4d096fed5818ebcf22f573ab3da822555a4f1e708558b4d94fc2
+size 5332763
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Utility/TX_Backdrop_01_ALB.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Utility/TX_Backdrop_01_ALB.uasset
new file mode 100644
index 0000000..d467fb5
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Utility/TX_Backdrop_01_ALB.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9e724c834c9f2bb6e66836eab618145664979312615acf36eab6c9ea1609c2b8
+size 5323994
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Utility/TX_Backdrop_01_NRM.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Utility/TX_Backdrop_01_NRM.uasset
new file mode 100644
index 0000000..62e011a
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Utility/TX_Backdrop_01_NRM.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ecfd969052860ea9ffd796813294496cbf5ae851d58a543797d8748a7f8ffbf0
+size 5096528
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Utility/TX_Cubemap_Stairs.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Utility/TX_Cubemap_Stairs.uasset
new file mode 100644
index 0000000..9ddb474
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Utility/TX_Cubemap_Stairs.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:306378ec3fa39fa39e98c4df608aa75b7e451b3bc461ebecfcb5f2051bb61a55
+size 1344026
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Utility/TX_Fill_01_ALB.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Utility/TX_Fill_01_ALB.uasset
new file mode 100644
index 0000000..09509a1
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Utility/TX_Fill_01_ALB.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a659af51fb2279a433998785e28c10f59def76b27e954fd40ca25d80dd43116c
+size 4630
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Utility/TX_Fill_01_NRM.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Utility/TX_Fill_01_NRM.uasset
new file mode 100644
index 0000000..2a45970
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Utility/TX_Fill_01_NRM.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:24787f2f3893798b33f605d491ab1911c1d49629e47343def2a6091cee78450a
+size 4971
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Utility/TX_Fill_01_RMA.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Utility/TX_Fill_01_RMA.uasset
new file mode 100644
index 0000000..516056a
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Utility/TX_Fill_01_RMA.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:78f0959e9ea208f1ed78d4b04f9ce8ce6485889c632ff53f606571a57ee9efcb
+size 4791
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Utility/TX_LUT_01.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Utility/TX_LUT_01.uasset
new file mode 100644
index 0000000..f34d476
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/Construction_VOL1/Textures/Utility/TX_LUT_01.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:dac0ed2527081ae69997a0494d54893218beea1282af9b10319881781c364006
+size 9379
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/PhysicsProp.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/PhysicsProp.uasset
new file mode 100644
index 0000000..3052b27
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/PhysicsProps/PhysicsProp.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2cd253b479208691a09327dab304cb56c9947d0c684ec157338c366130505049
+size 26825
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/BaseMaterial.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/BaseMaterial.uasset
new file mode 100644
index 0000000..7944bb2
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/BaseMaterial.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:79fa7371413bf0625027b539f441ce9d07dd32b70bd1dcf92c159e3ed558edac
+size 37288
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/Materials/M_FPGun.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/Materials/M_FPGun.uasset
new file mode 100644
index 0000000..69030a3
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/Materials/M_FPGun.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:88069a4e4ddaed5a467d821cae74ab0cf59eed443b75fb98172fcbe6cf9b3b32
+size 42891
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/Materials/MaterialLayers/ML_GlossyBlack_Latex_UE4.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/Materials/MaterialLayers/ML_GlossyBlack_Latex_UE4.uasset
new file mode 100644
index 0000000..dd89f69
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/Materials/MaterialLayers/ML_GlossyBlack_Latex_UE4.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:fa1a7361d2e8419ce214d353423f6dc2f75b0a2fe94494454cf0f234be295b83
+size 11035
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/Materials/MaterialLayers/ML_Plastic_Shiny_Beige.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/Materials/MaterialLayers/ML_Plastic_Shiny_Beige.uasset
new file mode 100644
index 0000000..55f57a2
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/Materials/MaterialLayers/ML_Plastic_Shiny_Beige.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ecca6588006834428240b3da4420d1d4bb38b065d50c4fa169a5cfb31de7dcbb
+size 16411
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/Materials/MaterialLayers/ML_Plastic_Shiny_Beige_LOGO.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/Materials/MaterialLayers/ML_Plastic_Shiny_Beige_LOGO.uasset
new file mode 100644
index 0000000..4ae8002
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/Materials/MaterialLayers/ML_Plastic_Shiny_Beige_LOGO.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:02b4ba08e261bc3f8d711c1bb4f1d0581fc8714838500a19db047f9663f31918
+size 9346
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/Materials/MaterialLayers/ML_Screen.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/Materials/MaterialLayers/ML_Screen.uasset
new file mode 100644
index 0000000..fac8e9e
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/Materials/MaterialLayers/ML_Screen.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1408f030db090d64b88848af82bdd4c7143933c643b1869b7c3aeb0474abe42e
+size 5637
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/Materials/MaterialLayers/ML_SoftMetal_UE4.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/Materials/MaterialLayers/ML_SoftMetal_UE4.uasset
new file mode 100644
index 0000000..001cc7f
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/Materials/MaterialLayers/ML_SoftMetal_UE4.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9cbacdbbfe28e1a92b5550db757a640f0ed8afa8a7d61b0e1b588ba12e2ae1d3
+size 20834
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/Materials/MaterialLayers/T_ML_Aluminum01.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/Materials/MaterialLayers/T_ML_Aluminum01.uasset
new file mode 100644
index 0000000..2588b4e
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/Materials/MaterialLayers/T_ML_Aluminum01.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e74702634949dc793b5faf3362cc480ab86b35e4cd1933df639399110085bec9
+size 8630797
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/Materials/MaterialLayers/T_ML_Aluminum01_N.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/Materials/MaterialLayers/T_ML_Aluminum01_N.uasset
new file mode 100644
index 0000000..a27a4f8
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/Materials/MaterialLayers/T_ML_Aluminum01_N.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1c96051a4f0f700cf03578b2c132cfc01af83bb842e58937abef2ea8f9f5a742
+size 4395348
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/Materials/MaterialLayers/T_ML_FineRubber.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/Materials/MaterialLayers/T_ML_FineRubber.uasset
new file mode 100644
index 0000000..bd184a7
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/Materials/MaterialLayers/T_ML_FineRubber.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0638dbd563e69ca689c8406ad9ef1ec699a511ddf6d160303f5135d4f5ffaf39
+size 6312755
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/Materials/MaterialLayers/T_ML_Rubber_Blue_01_D.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/Materials/MaterialLayers/T_ML_Rubber_Blue_01_D.uasset
new file mode 100644
index 0000000..b0221a7
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/Materials/MaterialLayers/T_ML_Rubber_Blue_01_D.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:41d350d266f5b5524e89f6442adb74532902201e8a78b43024bafa833f1a5e72
+size 5404974
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/Materials/MaterialLayers/T_ML_Rubber_Blue_01_N.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/Materials/MaterialLayers/T_ML_Rubber_Blue_01_N.uasset
new file mode 100644
index 0000000..f655151
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/Materials/MaterialLayers/T_ML_Rubber_Blue_01_N.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5d9df78b7114730c1f6aeba1f3cf33f2cbf6c4af1ab932b8169d8cc6445fb1c4
+size 4850545
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/Mesh/SK_FPGun.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/Mesh/SK_FPGun.uasset
new file mode 100644
index 0000000..9c7d0ee
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/Mesh/SK_FPGun.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a3e47ace89b14dfe90e808c925f5f226ddebbfd64fa4c3c83ea29a71c41d45e9
+size 1272029
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/Mesh/SK_FPGun_PhysicsAsset.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/Mesh/SK_FPGun_PhysicsAsset.uasset
new file mode 100644
index 0000000..71dd339
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/Mesh/SK_FPGun_PhysicsAsset.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:822e4aecb276c98ae7ff96a36762c6a48540f883a36ba5ed2364bce0d6d8bddc
+size 7543
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/Mesh/SK_FPGun_Skeleton.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/Mesh/SK_FPGun_Skeleton.uasset
new file mode 100644
index 0000000..e28638a
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/Mesh/SK_FPGun_Skeleton.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3341d663057a271a84a00e1a28615d826c51e56f77587611ccd149e5fee1a73c
+size 6626
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/SK_FPGun.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/SK_FPGun.uasset
new file mode 100644
index 0000000..14f3daa
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/SK_FPGun.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5494698f9b66bb69ea99a4aaae9fa48de02a38606722b0976f5fc317f1197b8a
+size 396510
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/Textures/T_FPGun_M.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/Textures/T_FPGun_M.uasset
new file mode 100644
index 0000000..c6f904a
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/Textures/T_FPGun_M.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:197e0dc9e1bb8a5fec6101157aae681370f2169f2f0344cdfe31568a02599581
+size 227728
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/Textures/T_FPGun_N.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/Textures/T_FPGun_N.uasset
new file mode 100644
index 0000000..9ebb3f6
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FPWeapon/Textures/T_FPGun_N.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c0b556925a72756d6bcb23f1e18bc141321515297ccbfd3ac33d6d4e59a55c19
+size 2044097
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FirstPersonProjectile.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FirstPersonProjectile.uasset
new file mode 100644
index 0000000..b3dc820
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FirstPersonProjectile.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:750523053f57c80de4ef91f018b8c64ac00a34763df68bcaaab8d88a152333f9
+size 54602
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FirstPersonProjectileMaterial.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FirstPersonProjectileMaterial.uasset
new file mode 100644
index 0000000..5e23e32
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FirstPersonProjectileMaterial.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:090191e27cddafbf08ad68b2c5c9490f93610e27901ec457ca7b29a7e80b219a
+size 39588
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FirstPersonProjectileMesh.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FirstPersonProjectileMesh.uasset
new file mode 100644
index 0000000..e2241c1
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FirstPersonProjectileMesh.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:775f9c72d050928647dae6357e1e66b6bfe05614c070d3aff70a70863a7df31c
+size 32065
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FirstPersonTemplateWeaponFire02.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FirstPersonTemplateWeaponFire02.uasset
new file mode 100644
index 0000000..bfa3993
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/FirstPersonTemplateWeaponFire02.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1c08f6393f81a69f838aa1411fa245a9eb3ab8ee60951231772e9d67454e290d
+size 306470
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/GrappleGun.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/GrappleGun.uasset
new file mode 100644
index 0000000..6a02069
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/GrappleGun.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:001e2ee5be3a783ad1c3798a034bcb9f25ce91be78edb5cfad15164ff7c92777
+size 540085
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/GunBase.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/GunBase.uasset
new file mode 100644
index 0000000..666a0b4
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/GunBase.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c5f4552703e45ded80a16d305a181f7638a2b68d5d60ef2e7156f5114a19f9c5
+size 818943
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/M_FPGunGrapple.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/M_FPGunGrapple.uasset
new file mode 100644
index 0000000..963e6c0
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Guns/M_FPGunGrapple.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2643ac81668975dc59ed047333ddb39d3be54898134c4e9fa297eabc83650d6b
+size 25721
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Blade_HeroSword11/HeroSword.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Blade_HeroSword11/HeroSword.uasset
new file mode 100644
index 0000000..3a862a3
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Blade_HeroSword11/HeroSword.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ffc54e3fbe51f9f0cd1552bc7d2de49005aa6f0d79bfb3a8523ad661222bce2a
+size 298673
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Blade_HeroSword11/HeroSword_Phys.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Blade_HeroSword11/HeroSword_Phys.uasset
new file mode 100644
index 0000000..fef389e
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Blade_HeroSword11/HeroSword_Phys.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1e2e7dc0f58196b8fd57fa46f4b3737a537a3029c209e3d95c4009f74d230d94
+size 95578
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Blade_HeroSword11/M_Blade_HeroSword11.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Blade_HeroSword11/M_Blade_HeroSword11.uasset
new file mode 100644
index 0000000..6d962d4
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Blade_HeroSword11/M_Blade_HeroSword11.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c91cc03d4a17c922e55388505f2abd289e45081e8d925357ee768877ed1249c8
+size 107825
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Blade_HeroSword11/PhysMat_MetalSmooth.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Blade_HeroSword11/PhysMat_MetalSmooth.uasset
new file mode 100644
index 0000000..f251b74
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Blade_HeroSword11/PhysMat_MetalSmooth.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5219da2c8ab4269c5234d11e76e7b5d793c39ba8eb7da2c5dec12bd5b6ae2d19
+size 1522
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Blade_HeroSword11/SK_Blade_HeroSword11.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Blade_HeroSword11/SK_Blade_HeroSword11.uasset
new file mode 100644
index 0000000..f590fdb
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Blade_HeroSword11/SK_Blade_HeroSword11.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a03c3f0e0f3de7d898c6ea025d720c7b904db99cb4fae52b867aebf973ba3b9c
+size 196668
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Blade_HeroSword11/SK_Blade_HeroSword_01_Skeleton.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Blade_HeroSword11/SK_Blade_HeroSword_01_Skeleton.uasset
new file mode 100644
index 0000000..d8ec25f
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Blade_HeroSword11/SK_Blade_HeroSword_01_Skeleton.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:815ea9b78e76cfb1b4348ae3a97cb7bf640c4fbd4e71d1e69bfb7e8369491c38
+size 5625
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Blade_HeroSword11/T_Blade_HeroSword_011_D.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Blade_HeroSword11/T_Blade_HeroSword_011_D.uasset
new file mode 100644
index 0000000..8b4fffc
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Blade_HeroSword11/T_Blade_HeroSword_011_D.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f9d43f1ada74559bafe97e50bef7ea39b0215155a4d9a89621522bb499ca5ca6
+size 198578
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Blade_HeroSword11/T_Blade_HeroSword_011_N.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Blade_HeroSword11/T_Blade_HeroSword_011_N.uasset
new file mode 100644
index 0000000..eb26f19
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Blade_HeroSword11/T_Blade_HeroSword_011_N.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0faac10cadc67807ce2653e847769c7675955566c5feb932195802d4c8e52693
+size 5443
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Blade_HeroSword11/T_Blade_HeroSword_012_D.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Blade_HeroSword11/T_Blade_HeroSword_012_D.uasset
new file mode 100644
index 0000000..3913837
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Blade_HeroSword11/T_Blade_HeroSword_012_D.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:86459d73ffac44b0dde0fcac8bac5cddc9280e3c3e700763e16277c39b4d61c2
+size 208578
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Dagger.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Dagger.uasset
new file mode 100644
index 0000000..c0089c3
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Dagger.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:840ca0e72c6aa513712ff9304b3c47ebe7aec55e7e09cb1f0abb44b098b527d2
+size 52177
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Mace.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Mace.uasset
new file mode 100644
index 0000000..9009ccb
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Mace.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:85a8e4a238d39976d637c001e248e1ca994e6432e1055b4c52027df0ea1c9db8
+size 51924
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/MeleeBase.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/MeleeBase.uasset
new file mode 100644
index 0000000..aef5ed9
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/MeleeBase.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:85a8f370b1c18dfaeb64365eea8477f366c29cf41229d96fa1414b7fe71171d3
+size 611254
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Shield.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Shield.uasset
new file mode 100644
index 0000000..2e844a3
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Shield.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:960db53865588c3a3896544a9c3a82286dbe7d15f8cca3ad2fd7f9656ab80fb5
+size 167573
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Sickle.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Sickle.uasset
new file mode 100644
index 0000000..9d3b338
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Sickle.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:30814a4a78240dbb3dc9489a4e97b896f42ad19d60ad8ad758db02316806de90
+size 62253
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Spear.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Spear.uasset
new file mode 100644
index 0000000..1a3e729
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Spear.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b02f64e4c172901175aa697f0dff10befaae01da8b14542677225371361b370f
+size 54722
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Sword.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Sword.uasset
new file mode 100644
index 0000000..197ed02
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Sword.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:498812318079cda021d0746cc0cc48bc51d0717a9f22c4a6070525bcc468737d
+size 52842
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Blueprints/Lightshaft/BP_GodRay.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Blueprints/Lightshaft/BP_GodRay.uasset
new file mode 100644
index 0000000..6e3d012
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Blueprints/Lightshaft/BP_GodRay.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a2a46851c9932dd8d26ee0716b257d373c5172e1042279f1deccff8a2c9c1574
+size 183248
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Blueprints/Lightshaft/Lightshaft/M_GodRay.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Blueprints/Lightshaft/Lightshaft/M_GodRay.uasset
new file mode 100644
index 0000000..f341bf0
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Blueprints/Lightshaft/Lightshaft/M_GodRay.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2ee58fc59456ce4bb6e5fa0111232980474d4ffec3c0aa8571241f93f3f41849
+size 114319
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Blueprints/Lightshaft/Lightshaft/M_GodRay_Inst_Animated.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Blueprints/Lightshaft/Lightshaft/M_GodRay_Inst_Animated.uasset
new file mode 100644
index 0000000..b2c102a
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Blueprints/Lightshaft/Lightshaft/M_GodRay_Inst_Animated.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:475e9d6c1c455ae69a9ee605fb75e4d5c33897142ae08f791c57ed17dbe15abd
+size 87108
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Blueprints/Lightshaft/Lightshaft/SM_GodRay_Plane.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Blueprints/Lightshaft/Lightshaft/SM_GodRay_Plane.uasset
new file mode 100644
index 0000000..9ac7995
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Blueprints/Lightshaft/Lightshaft/SM_GodRay_Plane.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5d196272fdce50a2f28f8c84ab1783ff598acaa8f7055399d0024e16a520fce9
+size 13530
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Blueprints/Lightshaft/Lightshaft/T_GodRay_Mask01.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Blueprints/Lightshaft/Lightshaft/T_GodRay_Mask01.uasset
new file mode 100644
index 0000000..4c2feae
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Blueprints/Lightshaft/Lightshaft/T_GodRay_Mask01.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b7626534fadbc2771a3aa9b9d4e5053cc9ecc06a1756ffdf21b4a4704928c794
+size 300670
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Blueprints/Lightshaft/Lightshaft/T_GodRay_Mask02.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Blueprints/Lightshaft/Lightshaft/T_GodRay_Mask02.uasset
new file mode 100644
index 0000000..401423a
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Blueprints/Lightshaft/Lightshaft/T_GodRay_Mask02.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:06b2668cde292942d34ad8ab348419b9b1467a55cd419a15ea384523177744a8
+size 92433
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Effects/Materials/Fire/M_Fire_SubUV_11X11_Noise.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Effects/Materials/Fire/M_Fire_SubUV_11X11_Noise.uasset
new file mode 100644
index 0000000..78487ee
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Effects/Materials/Fire/M_Fire_SubUV_11X11_Noise.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a5404e7a40c21f0941e2c0eec547e5b211e7afc7e8a0b5c0f79dc10f3644a7d7
+size 111655
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Effects/Materials/Fire/M_HeatDistort.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Effects/Materials/Fire/M_HeatDistort.uasset
new file mode 100644
index 0000000..3b8f45f
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Effects/Materials/Fire/M_HeatDistort.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7fd51c9653bd123a8dd16b169497bfc8a4f9ee802f2919429ab7a687cd2ec214
+size 96107
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Effects/Materials/Flares/M_FlareRound.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Effects/Materials/Flares/M_FlareRound.uasset
new file mode 100644
index 0000000..916197d
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Effects/Materials/Flares/M_FlareRound.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:766c8e34dbc04de89a7fdc9fcf6f7e5bf4cebc9b94e82ca1928574a8e28c2646
+size 98203
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Effects/Materials/Sparks/M_Spark_VelocityLerp.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Effects/Materials/Sparks/M_Spark_VelocityLerp.uasset
new file mode 100644
index 0000000..5f67139
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Effects/Materials/Sparks/M_Spark_VelocityLerp.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0af6253a894c43bcc8bc87af60fc41cf6e6b8ac7dcbdee0bcc75e1219b3bae38
+size 117146
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Effects/Particles/Fire/P_TorchFire.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Effects/Particles/Fire/P_TorchFire.uasset
new file mode 100644
index 0000000..9bdf9a4
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Effects/Particles/Fire/P_TorchFire.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:254759d62fe73fb2c58112f65980653bb8039d31ccdabe2764c124dfd806e407
+size 191472
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Effects/Textures/Fire/T_FireSubUV_11X11_32.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Effects/Textures/Fire/T_FireSubUV_11X11_32.uasset
new file mode 100644
index 0000000..881845b
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Effects/Textures/Fire/T_FireSubUV_11X11_32.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:550111515d289af09c05a3415074c6a9c8199262f5a749698e78e5f7f87da302
+size 557282
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Effects/Textures/Flares/T_FlareRound_Noise.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Effects/Textures/Flares/T_FlareRound_Noise.uasset
new file mode 100644
index 0000000..2a4261c
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Effects/Textures/Flares/T_FlareRound_Noise.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:989c52dec7176c3d608127b383fab088d65cb6343d225cd9129c40f343ddb654
+size 208151
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Effects/Textures/Noise/T_HeatTile3_N.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Effects/Textures/Noise/T_HeatTile3_N.uasset
new file mode 100644
index 0000000..3d8f564
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Effects/Textures/Noise/T_HeatTile3_N.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:11b0683b7f46609d570f48f7deaebb062261e0f51084f65b280e8e1af8c8f7c8
+size 856610
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Effects/Textures/Noise/T_TilingNoise03.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Effects/Textures/Noise/T_TilingNoise03.uasset
new file mode 100644
index 0000000..9ccd9fb
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Effects/Textures/Noise/T_TilingNoise03.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d75511ea6c1cff25e9f5ace144dd4a5979700f54999053f0ee3bdb80b373cd1c
+size 3405393
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Effects/Textures/Noise/T_TilingNoise12.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Effects/Textures/Noise/T_TilingNoise12.uasset
new file mode 100644
index 0000000..2dacd6f
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Effects/Textures/Noise/T_TilingNoise12.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:378f11161767fa352819e7379e629444818d705455799b55f177ed082a392a03
+size 4373838
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Effects/Textures/Noise/T_TilingNoise16.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Effects/Textures/Noise/T_TilingNoise16.uasset
new file mode 100644
index 0000000..225767d
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Effects/Textures/Noise/T_TilingNoise16.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d464fccf7965a757438468b30a0284ad304d3286482680d818f89a6680acd485
+size 406330
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Effects/Textures/Snow/T_Snow01_Packed.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Effects/Textures/Snow/T_Snow01_Packed.uasset
new file mode 100644
index 0000000..80db722
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Effects/Textures/Snow/T_Snow01_Packed.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a6d9b5978040bf798bae763b407706b33d80799112cd0a289338cd214a4be071
+size 71605
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Effects/VectorFields/VF_RandomNoise_01_90.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Effects/VectorFields/VF_RandomNoise_01_90.uasset
new file mode 100644
index 0000000..5caf55d
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Effects/VectorFields/VF_RandomNoise_01_90.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:26f05d94150b20d5243039e8a131f8247b3815a90c0d3eb8f09810a836ff17d2
+size 2099406
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Materials/M_Stained_Glass.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Materials/M_Stained_Glass.uasset
new file mode 100644
index 0000000..dceaf95
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Materials/M_Stained_Glass.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2fb023fe018378ff1a51b7e6e466f83d1fcc2a7d1af4223f16ab04bcf9091d66
+size 108387
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Materials/Weapons/M_WeaponSet_1.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Materials/Weapons/M_WeaponSet_1.uasset
new file mode 100644
index 0000000..f7d8b24
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Materials/Weapons/M_WeaponSet_1.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:59e46a0f80b43bb4a932358a636677c731f230d4b1e84c20d4c25823d7ccedce
+size 153667
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Materials/Weapons/M_WeaponSet_1Dirty.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Materials/Weapons/M_WeaponSet_1Dirty.uasset
new file mode 100644
index 0000000..76c494c
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Materials/Weapons/M_WeaponSet_1Dirty.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:dd36ff8ce6303a0f01569b0d85953a46005f369cb6cbefb7e5c0b1c06691bbca
+size 167960
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Materials/Weapons/M_WeaponSet_2.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Materials/Weapons/M_WeaponSet_2.uasset
new file mode 100644
index 0000000..d7b9048
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Materials/Weapons/M_WeaponSet_2.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:097117c1a29ff25bbcd612d34b4c5af58fd06b28da930763d6e6f27bb526557b
+size 141726
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Materials/Weapons/M_WeaponSet_2Dirty.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Materials/Weapons/M_WeaponSet_2Dirty.uasset
new file mode 100644
index 0000000..3cb63dc
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Materials/Weapons/M_WeaponSet_2Dirty.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:fc591651531e983b85ab41dfb80c4b2a14983226122b51b28ab0900e3778a75e
+size 159444
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Mesh/Weapons/Weapons_Kit/SM_Axe.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Mesh/Weapons/Weapons_Kit/SM_Axe.uasset
new file mode 100644
index 0000000..07bbfc3
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Mesh/Weapons/Weapons_Kit/SM_Axe.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:94187dc99d52e92126399a465c9eb439e6c4e878f2c1c124432e59dbaf71344f
+size 128989
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Mesh/Weapons/Weapons_Kit/SM_Dagger_1.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Mesh/Weapons/Weapons_Kit/SM_Dagger_1.uasset
new file mode 100644
index 0000000..e0ebc4c
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Mesh/Weapons/Weapons_Kit/SM_Dagger_1.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5e3b6a414c337179dd48c59d919a707a9918eee407cd96c6ceea467f760481dc
+size 124408
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Mesh/Weapons/Weapons_Kit/SM_Dagger_2.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Mesh/Weapons/Weapons_Kit/SM_Dagger_2.uasset
new file mode 100644
index 0000000..3f937e5
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Mesh/Weapons/Weapons_Kit/SM_Dagger_2.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7d56bc1f8bc9f88cf44dad20744972f6b7de11d6180240d1209bcb0a5d84d8ff
+size 51054
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Mesh/Weapons/Weapons_Kit/SM_GreatAxe.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Mesh/Weapons/Weapons_Kit/SM_GreatAxe.uasset
new file mode 100644
index 0000000..f14773f
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Mesh/Weapons/Weapons_Kit/SM_GreatAxe.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7f89f5bb96bef13e5258c43d99834ad396bad2bf08cbd5913dff95bb67be2865
+size 143225
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Mesh/Weapons/Weapons_Kit/SM_GreatHammer.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Mesh/Weapons/Weapons_Kit/SM_GreatHammer.uasset
new file mode 100644
index 0000000..aabb4e9
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Mesh/Weapons/Weapons_Kit/SM_GreatHammer.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7481a49577f6e195a74ee5fc7bb51e4d72e422bbfc812f04ad18a648c7588ac1
+size 139673
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Mesh/Weapons/Weapons_Kit/SM_H2H.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Mesh/Weapons/Weapons_Kit/SM_H2H.uasset
new file mode 100644
index 0000000..cf387dd
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Mesh/Weapons/Weapons_Kit/SM_H2H.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e490bdae2c930f9b91f20f70c0b1a097dd1548c81e38f5afde78782d34357cb8
+size 125392
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Mesh/Weapons/Weapons_Kit/SM_Mace.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Mesh/Weapons/Weapons_Kit/SM_Mace.uasset
new file mode 100644
index 0000000..19d2eb9
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Mesh/Weapons/Weapons_Kit/SM_Mace.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b5a09b7b8f984e2a6417cf46c0dd13a9205b72bd7463710396fbca42f534f947
+size 44908
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Mesh/Weapons/Weapons_Kit/SM_Shield.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Mesh/Weapons/Weapons_Kit/SM_Shield.uasset
new file mode 100644
index 0000000..a136b85
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Mesh/Weapons/Weapons_Kit/SM_Shield.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:860e01527b514d992288b9d246950454461a910e9c8a3110b2e5b8ea0faf8e1a
+size 97265
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Mesh/Weapons/Weapons_Kit/SM_Sickle.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Mesh/Weapons/Weapons_Kit/SM_Sickle.uasset
new file mode 100644
index 0000000..8f76040
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Mesh/Weapons/Weapons_Kit/SM_Sickle.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:14b188a7e76b7070aaeb079d57cfdf8ca773c9b6be50d55602abd840b0d67942
+size 42760
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Mesh/Weapons/Weapons_Kit/SM_Spear.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Mesh/Weapons/Weapons_Kit/SM_Spear.uasset
new file mode 100644
index 0000000..9c8a943
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Mesh/Weapons/Weapons_Kit/SM_Spear.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:dff7bfe5730fba4cdac3157941d7e112e6d97911770b9e1cd06fb677569776a5
+size 32777
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Mesh/Weapons/Weapons_Kit/SM_Sword.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Mesh/Weapons/Weapons_Kit/SM_Sword.uasset
new file mode 100644
index 0000000..378ed58
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Mesh/Weapons/Weapons_Kit/SM_Sword.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bb798aaae1c576f72e26eb1da88d16d27cefe5cf956e6a5b6f1cd2bc941413b7
+size 45619
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Mesh/Weapons/Weapons_Kit/SM_Throwing.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Mesh/Weapons/Weapons_Kit/SM_Throwing.uasset
new file mode 100644
index 0000000..e76eeb1
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Mesh/Weapons/Weapons_Kit/SM_Throwing.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5ab8a56f350b2d6fca2ca70799fa8d7d221f50ec91886c343603488128ef33d4
+size 124904
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Textures/Weapons/T_Dirty_Weapon_Set1_BaseColor.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Textures/Weapons/T_Dirty_Weapon_Set1_BaseColor.uasset
new file mode 100644
index 0000000..9812cf4
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Textures/Weapons/T_Dirty_Weapon_Set1_BaseColor.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f27231b3a3f97e9e2b1c7a2527f963ece0d03a533e19e12a1148af4eca911b76
+size 22201596
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Textures/Weapons/T_Dirty_Weapon_Set1_Normal.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Textures/Weapons/T_Dirty_Weapon_Set1_Normal.uasset
new file mode 100644
index 0000000..f0ead8b
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Textures/Weapons/T_Dirty_Weapon_Set1_Normal.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b315359708284f52047eb6e035476c8e8118dbb0fb87aab102195b5635a31091
+size 27731639
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Textures/Weapons/T_Dirty_Weapon_Set1_OcclusionRoughnessMetallic.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Textures/Weapons/T_Dirty_Weapon_Set1_OcclusionRoughnessMetallic.uasset
new file mode 100644
index 0000000..ba2fea6
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Textures/Weapons/T_Dirty_Weapon_Set1_OcclusionRoughnessMetallic.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bc57a2e130aa460d49e4f091be7186e7e94e09dc2a3d03c42f9ebc8ed8316e80
+size 21668041
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Textures/Weapons/T_Dirty_Weapon_Set2_BaseColor.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Textures/Weapons/T_Dirty_Weapon_Set2_BaseColor.uasset
new file mode 100644
index 0000000..f19494e
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Textures/Weapons/T_Dirty_Weapon_Set2_BaseColor.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6757304bf199367136e78bef88c47552e969c43618a6baedd3bac2ca548960b8
+size 19537489
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Textures/Weapons/T_Dirty_Weapon_Set2_Normal.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Textures/Weapons/T_Dirty_Weapon_Set2_Normal.uasset
new file mode 100644
index 0000000..41f23c1
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Textures/Weapons/T_Dirty_Weapon_Set2_Normal.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:eeb8f00cf65eb3a673d6d12fa86d16b778ef3696fde482e19f2c319d2870f5ce
+size 28237997
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Textures/Weapons/T_Dirty_Weapon_Set2_OcclusionRoughnessMetallic.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Textures/Weapons/T_Dirty_Weapon_Set2_OcclusionRoughnessMetallic.uasset
new file mode 100644
index 0000000..cd867e0
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Textures/Weapons/T_Dirty_Weapon_Set2_OcclusionRoughnessMetallic.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a6926ed6959a55f20091c3c7693705f73f17a431168fc697a35f08b93d00e453
+size 21919418
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Textures/Weapons/T_Weapon_Set1_BaseColor.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Textures/Weapons/T_Weapon_Set1_BaseColor.uasset
new file mode 100644
index 0000000..e2646d2
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Textures/Weapons/T_Weapon_Set1_BaseColor.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e96bd7a5ffb230b013ce3a73491411323ddc32e99a5646b0fa177aa55f68eafa
+size 16301026
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Textures/Weapons/T_Weapon_Set1_Normal.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Textures/Weapons/T_Weapon_Set1_Normal.uasset
new file mode 100644
index 0000000..6931fca
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Textures/Weapons/T_Weapon_Set1_Normal.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3820cbf36d6cef92cb5414d316bc6a90e2c01ee4b2d4b8adee885a2c9c118d35
+size 24691345
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Textures/Weapons/T_Weapon_Set1_OcclusionRoughnessMetallic.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Textures/Weapons/T_Weapon_Set1_OcclusionRoughnessMetallic.uasset
new file mode 100644
index 0000000..4eceaec
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Textures/Weapons/T_Weapon_Set1_OcclusionRoughnessMetallic.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a3eaf8634a3d32fa06a9440dcd4dfaf13e2dac678d2dda181e94d4f1ba7edd83
+size 18813193
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Textures/Weapons/T_Weapon_Set2_BaseColor.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Textures/Weapons/T_Weapon_Set2_BaseColor.uasset
new file mode 100644
index 0000000..0a0753e
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Textures/Weapons/T_Weapon_Set2_BaseColor.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:210eb6bd9c4f3120a7f97616b1033cdbbd6cea346739448b18997f9f2064f201
+size 16278172
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Textures/Weapons/T_Weapon_Set2_Normal.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Textures/Weapons/T_Weapon_Set2_Normal.uasset
new file mode 100644
index 0000000..0488c33
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Textures/Weapons/T_Weapon_Set2_Normal.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6887a707b1c9defcf84ed80247061f23a53d3931789cb1f36f1fed1397d4c8bd
+size 22453854
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Textures/Weapons/T_Weapon_Set2_OcclusionRoughnessMetallic.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Textures/Weapons/T_Weapon_Set2_OcclusionRoughnessMetallic.uasset
new file mode 100644
index 0000000..6dbdc00
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Melee/Weapon_Pack/Textures/Weapons/T_Weapon_Set2_OcclusionRoughnessMetallic.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:abd37e4193e561e39c3377ce3529db87a93b577bece881b952eef0704aa76bdc
+size 22522560
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Misc/Cylinder1.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Misc/Cylinder1.uasset
new file mode 100644
index 0000000..c7c00b0
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Misc/Cylinder1.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:768fbb4f48589b55c7260e17e2c9403eda7b754b70894221f086d76df6e7e2bf
+size 33387
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Misc/Dummy.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Misc/Dummy.uasset
new file mode 100644
index 0000000..6a0288f
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Misc/Dummy.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3f46b9fa8ed6fc037539117502b6a6926f0a35ce2923bbc8572abfa618a8ce52
+size 42603
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Misc/Slicer/01_-_Default.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Misc/Slicer/01_-_Default.uasset
new file mode 100644
index 0000000..e401673
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Misc/Slicer/01_-_Default.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:932cdd3bb9fc0a6a2e2c7859bc24e10ac79943769e7585a42e3fa09892970f7b
+size 101948
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Misc/Slicer/01_-_Defaultg.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Misc/Slicer/01_-_Defaultg.uasset
new file mode 100644
index 0000000..1c1f60f
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Misc/Slicer/01_-_Defaultg.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c2af53bc782c41a75638f767cc0e425e395f71221c8766e31395ae8c5bcfc5db
+size 95583
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Misc/Slicer/01_-_Defaultgw.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Misc/Slicer/01_-_Defaultgw.uasset
new file mode 100644
index 0000000..5fb009b
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Misc/Slicer/01_-_Defaultgw.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f49205106745bca0b66a4fa65fc9dc896477ab180a014df7f7b423db84b34ea8
+size 51417
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Misc/Slicer/Sandra_s_Sword.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Misc/Slicer/Sandra_s_Sword.uasset
new file mode 100644
index 0000000..a18f9cf
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Misc/Slicer/Sandra_s_Sword.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e08448fd62c79e3e4f6ba35e0d0f06aa3ea00ba8a4a099132bc1ec12ef873b7f
+size 251353
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Misc/Slicer/SliceDummy.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Misc/Slicer/SliceDummy.uasset
new file mode 100644
index 0000000..eacf3d6
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Misc/Slicer/SliceDummy.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:951117b745ace00e670c2a02075f91b1fd6d494563ccf9b3e8a35b86bc2c40a1
+size 26921
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Misc/Slicer/Slicer.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Misc/Slicer/Slicer.uasset
new file mode 100644
index 0000000..1a43812
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Misc/Slicer/Slicer.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8b7b75445dd797b1c0a197c952063eb6055403ec66707f1a4d204764854cfeac
+size 162371
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Misc/Slicer/SlicingDummy.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Misc/Slicer/SlicingDummy.uasset
new file mode 100644
index 0000000..82f2485
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Misc/Slicer/SlicingDummy.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ae36e55d206fb080cce3ba8d816e28d891ce507c8fc4b4ade937acfb2a1dacc4
+size 216357
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Misc/Slicer/defaultMat.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Misc/Slicer/defaultMat.uasset
new file mode 100644
index 0000000..e9382ba
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Grippables/Weapons/Misc/Slicer/defaultMat.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b61bb60f00f4342daeb6da43b2e73f8d0b3b25411d59638ccc419151fcc39ff4
+size 59089
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/AlternateGripLeft.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/AlternateGripLeft.uasset
new file mode 100644
index 0000000..a3ecb0f
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/AlternateGripLeft.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1e838ac7a16a9abfbff653a00a2b541db9d5039649b8962d867bd271f673354b
+size 1733
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/AlternateGripRight.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/AlternateGripRight.uasset
new file mode 100644
index 0000000..4a50b4b
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/AlternateGripRight.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b709af759ede67d0f1be1236ea9d32414abd47c8aecdf0af30d81518712544e5
+size 1742
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/ControllerMovementLeft.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/ControllerMovementLeft.uasset
new file mode 100644
index 0000000..7434073
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/ControllerMovementLeft.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:efb29e43077a95e58dc4701bac014adaeaa5a6aa59bbfbe67558023152a4b08a
+size 1925
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/ControllerMovementRight.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/ControllerMovementRight.uasset
new file mode 100644
index 0000000..6cc857a
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/ControllerMovementRight.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:47cbe9c2325bfce551802aece2a33050ab71b31ad010a0a4c03fed01ebf4403e
+size 1934
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/FPSActions/Jump.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/FPSActions/Jump.uasset
new file mode 100644
index 0000000..de27f0f
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/FPSActions/Jump.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7a1a5b728c4a1fb21be95307d7ce48439155eae8ff6b791f877d3309b8a62b29
+size 1645
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/FPSActions/LookUp.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/FPSActions/LookUp.uasset
new file mode 100644
index 0000000..dc3e867
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/FPSActions/LookUp.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3be15eb111e1907782872e423d45b0b6f441e710d0c144b4fdfbb0b380d1e8ec
+size 1810
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/FPSActions/MoveForward.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/FPSActions/MoveForward.uasset
new file mode 100644
index 0000000..3e3270d
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/FPSActions/MoveForward.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2c52c5253dfbb4a1ff7ba50eb675b199b0999f5b704c34a6a4440114763e2426
+size 1857
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/FPSActions/MoveRight.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/FPSActions/MoveRight.uasset
new file mode 100644
index 0000000..d44788b
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/FPSActions/MoveRight.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:fd6d29db7763b629a4e8588db43f677b0cae53855cee720530d918b0339556df
+size 1837
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/FPSActions/Turn.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/FPSActions/Turn.uasset
new file mode 100644
index 0000000..0a7106f
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/FPSActions/Turn.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2cef6554d211f2da6161b97663aeb2369c648f9ede8db34c919575257c025baf
+size 1792
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/LaserBeamLeft.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/LaserBeamLeft.uasset
new file mode 100644
index 0000000..65cb30a
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/LaserBeamLeft.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9197c15df99db7cf86c31af6b948278ddc6d228ed314aa15b97acb41d9009273
+size 1697
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/LaserBeamRight.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/LaserBeamRight.uasset
new file mode 100644
index 0000000..3435283
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/LaserBeamRight.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4eb9f86043920401304778a1247f7ccf600cfe2b62c8e78d4b5921d7575b5bf2
+size 1706
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/MotionControllerThumbLeft_X.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/MotionControllerThumbLeft_X.uasset
new file mode 100644
index 0000000..76bfaff
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/MotionControllerThumbLeft_X.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f9514284ca54c4019335be42544a9c29de3e2ea179616f932b575a8ae927b0fd
+size 1972
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/MotionControllerThumbLeft_Y.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/MotionControllerThumbLeft_Y.uasset
new file mode 100644
index 0000000..65f005c
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/MotionControllerThumbLeft_Y.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4dc37821f7f0f5f745b527f6eb5ef09846a144a745ec9e970e4a54fe35a352c8
+size 1972
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/MotionControllerThumbRight_X.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/MotionControllerThumbRight_X.uasset
new file mode 100644
index 0000000..7eb201d
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/MotionControllerThumbRight_X.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c809975f9f3d5b164dd94b1c5a13359035a3aa55a1201d28ef694eb8dcca694b
+size 1981
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/MotionControllerThumbRight_Y.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/MotionControllerThumbRight_Y.uasset
new file mode 100644
index 0000000..4fb4d3f
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/MotionControllerThumbRight_Y.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:08334d0a62c0064bcbe385e62e42f18fd5394b8de87935dbf191c09b37d0703e
+size 1981
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/PrimaryGripLeft.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/PrimaryGripLeft.uasset
new file mode 100644
index 0000000..0a2ad1a
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/PrimaryGripLeft.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2118297fb54c03b90d3f600dc2359ae504078a606720a5b02a4504cff322153a
+size 1725
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/PrimaryGripRight.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/PrimaryGripRight.uasset
new file mode 100644
index 0000000..f78e572
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/PrimaryGripRight.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:45c451a28c509a1f29e80ee9d05df72e40267f376734866cd4c60f6a80ee0e8f
+size 1734
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/TeleportLeft.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/TeleportLeft.uasset
new file mode 100644
index 0000000..9177e19
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/TeleportLeft.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7a5639edfcc13491b91cc89d033361dbb8b92776c278a871d13ae51837ae9c82
+size 1686
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/TeleportRight.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/TeleportRight.uasset
new file mode 100644
index 0000000..054131e
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/TeleportRight.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bca00b14c3c592ae8c8f97963794904c5718f02c62e59161e845b35305c62547
+size 1695
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/UseHeldObjectLeft.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/UseHeldObjectLeft.uasset
new file mode 100644
index 0000000..c55059d
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/UseHeldObjectLeft.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:de78a3a8cf9cb1ccd1f1a67814856637922e53c76dab8e5675f3f044d985d21d
+size 1729
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/UseHeldObjectRight.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/UseHeldObjectRight.uasset
new file mode 100644
index 0000000..f93d245
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/Actions/UseHeldObjectRight.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8fb7e74eb8d0e577d5dd293a3e23bc7d850f43938a0f97ac16c8ba4cdd751863
+size 1738
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/FPSInputConfig.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/FPSInputConfig.uasset
new file mode 100644
index 0000000..7320fc5
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/FPSInputConfig.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8a3dbd80f436cfb88b32ad754672a97aeb7831d9b27f0afd118dbb2e76a902f4
+size 2238
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/FPSInputContext.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/FPSInputContext.uasset
new file mode 100644
index 0000000..97f2e4a
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/FPSInputContext.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5d736f494febc8d56fbc8752af45f9f260547664bbe483db52c40e6960dc817b
+size 11478
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/VREInputConfig.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/VREInputConfig.uasset
new file mode 100644
index 0000000..e57a916
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/VREInputConfig.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a93b6b73d5169be5db2c22c81bda929bb9a40900382197dac7f10f15ab59b694
+size 2252
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/VREInputMappingContext.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/VREInputMappingContext.uasset
new file mode 100644
index 0000000..c217feb
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Input/VREInputMappingContext.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0779907ddaccc85ddc7e510b3daa296045c10d8343c75b727493ce05e15b17bc
+size 54466
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/Vehicle/Sedan/FrontWheel.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/Vehicle/Sedan/FrontWheel.uasset
new file mode 100644
index 0000000..2aaa864
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/Vehicle/Sedan/FrontWheel.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4ff5226c6d823f8dabf9a119e500c94c38f73e712ea4f7f7f6afca2ba07937cb
+size 6125
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/Vehicle/Sedan/Materials/M_Vehicle_Sedan.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/Vehicle/Sedan/Materials/M_Vehicle_Sedan.uasset
new file mode 100644
index 0000000..eac566c
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/Vehicle/Sedan/Materials/M_Vehicle_Sedan.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ce2cd364db5148595af16df814568e853c4795cdb9a85c73ebf6dcc1d0324c2e
+size 80345
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/Vehicle/Sedan/Materials/M_Vehicle_Sedan_Inst_Glass.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/Vehicle/Sedan/Materials/M_Vehicle_Sedan_Inst_Glass.uasset
new file mode 100644
index 0000000..516d9bc
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/Vehicle/Sedan/Materials/M_Vehicle_Sedan_Inst_Glass.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f824bb413380bd1e4fdc134dd483fd4507bb1433e196affe9a8135f1ab42fd0c
+size 9253
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/Vehicle/Sedan/Materials/M_Vehicle_Sedan_Inst_Headlights.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/Vehicle/Sedan/Materials/M_Vehicle_Sedan_Inst_Headlights.uasset
new file mode 100644
index 0000000..6812515
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/Vehicle/Sedan/Materials/M_Vehicle_Sedan_Inst_Headlights.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ca34d3c81f602c0171bc6487ad2ee5d9d61e367d724d5901468a6ef6f68f6df9
+size 9320
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/Vehicle/Sedan/Materials/M_Vehicle_Sedan_Inst_Plastic.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/Vehicle/Sedan/Materials/M_Vehicle_Sedan_Inst_Plastic.uasset
new file mode 100644
index 0000000..7b2b6d4
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/Vehicle/Sedan/Materials/M_Vehicle_Sedan_Inst_Plastic.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:684a9e971512c9cfea6d34958b067e94882547e593da269f763f4af9fdedb8ec
+size 7897
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/Vehicle/Sedan/Materials/M_Vehicle_Sedan_Inst_TailLights.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/Vehicle/Sedan/Materials/M_Vehicle_Sedan_Inst_TailLights.uasset
new file mode 100644
index 0000000..9d7e7a7
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/Vehicle/Sedan/Materials/M_Vehicle_Sedan_Inst_TailLights.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4ce9249e77130d60f498e04affb1dc188fbd53d51fecc40a7bfc4b3b9c97e24b
+size 9352
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/Vehicle/Sedan/Materials/M_Vehicle_Sedan_Inst_Tires.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/Vehicle/Sedan/Materials/M_Vehicle_Sedan_Inst_Tires.uasset
new file mode 100644
index 0000000..26ea8c8
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/Vehicle/Sedan/Materials/M_Vehicle_Sedan_Inst_Tires.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e457133da6737a691055d244332179173ea12542159c0205154e22471e0f4291
+size 7559
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/Vehicle/Sedan/Materials/M_Vehicle_Sedan_Inst_Wheels.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/Vehicle/Sedan/Materials/M_Vehicle_Sedan_Inst_Wheels.uasset
new file mode 100644
index 0000000..643dd84
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/Vehicle/Sedan/Materials/M_Vehicle_Sedan_Inst_Wheels.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5f3a4c897de984f16e4e94646a0d0934d5aff79fbc8edf106b1800797afb9043
+size 9385
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/Vehicle/Sedan/RearWheel.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/Vehicle/Sedan/RearWheel.uasset
new file mode 100644
index 0000000..7e0949a
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/Vehicle/Sedan/RearWheel.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bb171705409ab03549023f637889281c864af540a4cd2cf9e2bee11e98466391
+size 6057
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/Vehicle/Sedan/Sedan_AnimBP.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/Vehicle/Sedan/Sedan_AnimBP.uasset
new file mode 100644
index 0000000..a77d01d
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/Vehicle/Sedan/Sedan_AnimBP.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:69f58b972446fdac5fa06ba68227f995d22a1a4a6d4235fcfa63b6e7f8fbb868
+size 140414
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/Vehicle/Sedan/Sedan_PhysMat.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/Vehicle/Sedan/Sedan_PhysMat.uasset
new file mode 100644
index 0000000..5cf59a8
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/Vehicle/Sedan/Sedan_PhysMat.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0c3d7abb5517c9c5c43f78cc4c4a83762ca33852d92232b62125a8986c54e9bb
+size 1310
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/Vehicle/Sedan/Sedan_PhysicsAsset.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/Vehicle/Sedan/Sedan_PhysicsAsset.uasset
new file mode 100644
index 0000000..f2dbc68
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/Vehicle/Sedan/Sedan_PhysicsAsset.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b6c1fd3bf8b4ff23436a824fd2340580e15017dc0c095f1f36907e77ae3b8514
+size 361660
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/Vehicle/Sedan/Sedan_SkelMesh.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/Vehicle/Sedan/Sedan_SkelMesh.uasset
new file mode 100644
index 0000000..3b2a848
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/Vehicle/Sedan/Sedan_SkelMesh.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cc5f67c9ba2f5295d2018f68b5e5f047db15b0bb3924aa87448bfbbe6ca8067b
+size 3665493
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/Vehicle/Sedan/Sedan_SkelMesh_Physics.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/Vehicle/Sedan/Sedan_SkelMesh_Physics.uasset
new file mode 100644
index 0000000..ce2991d
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/Vehicle/Sedan/Sedan_SkelMesh_Physics.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0999149ddb59656da7236c8ef4fa0d81d3bce047e10574ecd71d752f5a46e07b
+size 361365
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/Vehicle/Sedan/Sedan_Skeleton.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/Vehicle/Sedan/Sedan_Skeleton.uasset
new file mode 100644
index 0000000..ee232d9
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/Vehicle/Sedan/Sedan_Skeleton.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9a5fbcaa9e0b4dfb6f7d4095853718252887d767cdc00fe52b13621374049e70
+size 8338
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/VehicleAdv/ArenaMesh/LoopBig.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/VehicleAdv/ArenaMesh/LoopBig.uasset
new file mode 100644
index 0000000..190d20a
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/VehicleAdv/ArenaMesh/LoopBig.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:dbce931b614a6e6ea567c99dfca6c9f8f4a943a35bf03556212baa77ebccab2a
+size 45221
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/VehicleAdv/Materials/MasterSolidColor.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/VehicleAdv/Materials/MasterSolidColor.uasset
new file mode 100644
index 0000000..ad3870b
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/VehicleAdv/Materials/MasterSolidColor.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2d9afa56344449a31b7b84692c028610c0fd1ed112063fd6c99acd57fa9843b7
+size 112697
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/VehicleAdv/Materials/MaterialInstances/SolidOrange.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/VehicleAdv/Materials/MaterialInstances/SolidOrange.uasset
new file mode 100644
index 0000000..c6470f8
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/VehicleAdv/Materials/MaterialInstances/SolidOrange.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:eb32a9e8a5294f86fea30d174b40f94f73354afe86918c1277f738d9d641306c
+size 113444
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/VehicleBP/Sedan/PassengerInfo.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/VehicleBP/Sedan/PassengerInfo.uasset
new file mode 100644
index 0000000..6153d28
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/VehicleBP/Sedan/PassengerInfo.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7c363ef9c2d2737fae7f94fc100580601f146bb54e81e85f41aa048146609f3a
+size 5053
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/VehicleBP/Sedan/Sedan.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/VehicleBP/Sedan/Sedan.uasset
new file mode 100644
index 0000000..e4610db
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/VehicleBP/Sedan/Sedan.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:106197b135fa8ffa3a2ce3bf80b112fe6608cc48b815fd0373b3fdd39dd6f823
+size 874870
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/VehicleBP/Sedan/SteeringWheel.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/VehicleBP/Sedan/SteeringWheel.uasset
new file mode 100644
index 0000000..d98b232
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/VehicleBP/Sedan/SteeringWheel.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:876735fa638611f892f65c692b440f7f73c39a17595a45370c6ebe4f317dc149
+size 30710
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/VehicleBP/Sedan/ThrottleLever.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/VehicleBP/Sedan/ThrottleLever.uasset
new file mode 100644
index 0000000..d563275
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/VehicleBP/Sedan/ThrottleLever.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:635cb356d1c5527efce63067594d1b5d3ef86311ca935b3505104c33fe28ca5c
+size 47382
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/VehicleBP/Sedan/Turquoise.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/VehicleBP/Sedan/Turquoise.uasset
new file mode 100644
index 0000000..cecc2af
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/VehicleBP/Sedan/Turquoise.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3f958dc04dfec97c6c276e0758bf14efce612c5f7ea3efc9c86d95456248e2e1
+size 77363
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/VehicleBP/Sedan/Yellow.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/VehicleBP/Sedan/Yellow.uasset
new file mode 100644
index 0000000..698ea36
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Car/VehicleBP/Sedan/Yellow.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:56e63af559bfc33c426375085614864c582541a394c3594978fef13dd26cd1dc
+size 75748
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Console/ConsoleOutput.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Console/ConsoleOutput.uasset
new file mode 100644
index 0000000..9cfcadd
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Console/ConsoleOutput.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8509d96aef98eee98867e8c1e46bf4436d2343600848756448166cc3f2ff8ea2
+size 789315
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Console/VRConsoleMaterial.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Console/VRConsoleMaterial.uasset
new file mode 100644
index 0000000..0ac27cf
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Console/VRConsoleMaterial.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a0862ca33e347ace106d392902a6329c9c51b6e08176e0485417c7dc0301a6d0
+size 12326
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Keyboard/2DKeyboard.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Keyboard/2DKeyboard.uasset
new file mode 100644
index 0000000..9879206
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Keyboard/2DKeyboard.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:44661d761195da9d072b67bb113f1838cfd1d098cae2db645e4624ccd4afb4fe
+size 321128
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Keyboard/3DKeyboard.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Keyboard/3DKeyboard.uasset
new file mode 100644
index 0000000..a9af119
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Keyboard/3DKeyboard.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e22f99d5e56041b779772be7a63ba5dadd4dafd3d601b8769a01b38eadd2da57
+size 407554
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Keyboard/3DKeyboardKey.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Keyboard/3DKeyboardKey.uasset
new file mode 100644
index 0000000..e45f3e7
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Keyboard/3DKeyboardKey.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8e8c4f29f92e9a26ca55edd6cf2ae5137d0685eaa464b9e80dd4bb0fb8f3b1d8
+size 240589
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Keyboard/BaseKeyboardKey.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Keyboard/BaseKeyboardKey.uasset
new file mode 100644
index 0000000..6749129
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Keyboard/BaseKeyboardKey.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7ce1c750fc1377956a31649b38f8eb7f28f312e61dae4d70cc64e94c7403280b
+size 86027
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Keyboard/GreenFaceKeyboardKey.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Keyboard/GreenFaceKeyboardKey.uasset
new file mode 100644
index 0000000..12d6c2d
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Keyboard/GreenFaceKeyboardKey.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:301c68d773c5dcfe727c53f17bb05e2e2c5023b0998487de2d1744add834ff4c
+size 127284
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Keyboard/KeyboardDataTable.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Keyboard/KeyboardDataTable.uasset
new file mode 100644
index 0000000..c15b51b
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Keyboard/KeyboardDataTable.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d5399edf9f2aeaa23649658320ff63462de49dc6c00a915725b136487fc6c7e9
+size 38731
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Keyboard/KeyboardKeyTableRow.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Keyboard/KeyboardKeyTableRow.uasset
new file mode 100644
index 0000000..3031dd7
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Keyboard/KeyboardKeyTableRow.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6e541ec061bc7cd0984ab9b194dab84d030f32f7b2d9e079462ebe9a7643893e
+size 10438
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Keyboard/KeyboardTestActor.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Keyboard/KeyboardTestActor.uasset
new file mode 100644
index 0000000..f65b11c
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Keyboard/KeyboardTestActor.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d2b243e1cd3c289f7854c4cf467e2955c62caaa01d44881b61e58842417c4e77
+size 76187
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Keyboard/KeypadDataTable.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Keyboard/KeypadDataTable.uasset
new file mode 100644
index 0000000..9596ccb
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Keyboard/KeypadDataTable.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2d1ef2e7d8fde8a332cec51c4632c939422f78a28fdf7f06b515279c1b978956
+size 12056
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Keyboard/SpecialKeyType.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Keyboard/SpecialKeyType.uasset
new file mode 100644
index 0000000..c481253
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Keyboard/SpecialKeyType.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c07e1ed35deb9ac19ca322384ff3d5a3a4d9cc3432188ae22244584126e1b402
+size 8721
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Keyboard/TextKeyboardKey.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Keyboard/TextKeyboardKey.uasset
new file mode 100644
index 0000000..34eabb1
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Keyboard/TextKeyboardKey.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:26a31e4ac534a8aded5536364121a35b45c62ce2f298ae3dc5b25605f2c2b856
+size 85254
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Mirror/MirrorActor.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Mirror/MirrorActor.uasset
new file mode 100644
index 0000000..b0dadf0
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Mirror/MirrorActor.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:30054a1224d11629877aec1b8a038bf079abfc84b9bb5230901e8d48b0f95c27
+size 51272
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Mirror/MirrorFeed.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Mirror/MirrorFeed.uasset
new file mode 100644
index 0000000..9019636
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Mirror/MirrorFeed.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3702ec3cb3cbf0a574e3253078c333a04dfdde1609ba81ef9faf6b8f9ab65fff
+size 38762
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Mirror/MirrorMat.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Mirror/MirrorMat.uasset
new file mode 100644
index 0000000..0393cc4
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Mirror/MirrorMat.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9b21a4b79201347ca5135e003c04995f1d1704275ae5ab2f9cb0d0e28e437375
+size 28408
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Misc/ClimbWallActor.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Misc/ClimbWallActor.uasset
new file mode 100644
index 0000000..e9f878c
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Misc/ClimbWallActor.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ce2739b0b9d3d45b096bcd07e52493d9f704075337f94d033dd7a0a037193325
+size 41320
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Misc/ConsoleCommandSliderActor.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Misc/ConsoleCommandSliderActor.uasset
new file mode 100644
index 0000000..97fc719
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Misc/ConsoleCommandSliderActor.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f4a9fe524691dd02d2d6baed4b0c7193cbf24d84812493ddff6621c2cafcd5e3
+size 218357
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Misc/Floor_StaticMesh.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Misc/Floor_StaticMesh.uasset
new file mode 100644
index 0000000..e1316a2
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Misc/Floor_StaticMesh.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a1221e832c3e593122a34f1a2af1323ead3dd2c1c531ea7e76c086f00cba42c4
+size 15023
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Misc/Rotator.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Misc/Rotator.uasset
new file mode 100644
index 0000000..8ef4d2a
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/Misc/Rotator.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1a73a922a3e8c6651a9338c16fb071fab3620521ee934e0d2ee5117d403aa1a1
+size 50905
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/OptionsMenu/OptionsMenu.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/OptionsMenu/OptionsMenu.uasset
new file mode 100644
index 0000000..f4c1ec0
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/OptionsMenu/OptionsMenu.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:139496f4ef272cb1d13084015f9b96287296a91246acc5cfa3f7b57cc92d0c78
+size 161477
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/OptionsMenu/OptionsMenuActor.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/OptionsMenu/OptionsMenuActor.uasset
new file mode 100644
index 0000000..7f052b2
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/OptionsMenu/OptionsMenuActor.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:748e0fcff1e1e348dd08bd825b04f97aab7f3bf7299e2b5a140b55ab4b1e6c51
+size 23156
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/ServerMenu/3DMenu.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/ServerMenu/3DMenu.uasset
new file mode 100644
index 0000000..7b7bd50
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/ServerMenu/3DMenu.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:71e8e33ea948f9b6ef3b99cef1e3e0ce2377d19bce9953abb1094adb51cc9327
+size 276488
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/ServerMenu/3DMenuActor.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/ServerMenu/3DMenuActor.uasset
new file mode 100644
index 0000000..590c8a3
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/ServerMenu/3DMenuActor.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d48fd0a8a60b6dfa0aa072bc57d190f423ce1501139ab5d97e66f44372b96ea2
+size 23395
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/ServerMenu/ServerInfo.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/ServerMenu/ServerInfo.uasset
new file mode 100644
index 0000000..d37c5d7
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/ServerMenu/ServerInfo.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:142cb376e836f3b6ebd3da84ca963e2e2400beac1e1532e773b6e23eb5790879
+size 77987
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/WallWalking/WallWalkingMat.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/WallWalking/WallWalkingMat.uasset
new file mode 100644
index 0000000..b7ffd3f
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/WallWalking/WallWalkingMat.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8b4d1001ff45b75964b0cbe3ae97e457a70e01aa5da965b0abbbe0d3358eb08d
+size 8963
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/WallWalking/WallWalkingRamp.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/WallWalking/WallWalkingRamp.uasset
new file mode 100644
index 0000000..f3dddf0
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/WallWalking/WallWalkingRamp.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:607f2e7b5e5e574c7c9701289fd283466a381affa1bf40c2eea16ce6ff418cfb
+size 22517
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/WallWalking/WallWalkingVolume.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/WallWalking/WallWalkingVolume.uasset
new file mode 100644
index 0000000..1be9b61
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/WallWalking/WallWalkingVolume.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:60b46b6b8e21d6860ba5bedb096c1d2fc608d4529e4739cbba0fb0c4472bdd9a
+size 62508
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/WallWalking/WallWalkingWall.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/WallWalking/WallWalkingWall.uasset
new file mode 100644
index 0000000..6f7f212
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Level/WallWalking/WallWalkingWall.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e1089fee314fe843d0c9edb06f4de9c886147412063dff81505580e012b3d5d8
+size 14964
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Misc/Meshes/1M_Cube.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Misc/Meshes/1M_Cube.uasset
new file mode 100644
index 0000000..2895a52
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Misc/Meshes/1M_Cube.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1ec84195d01bfbac3fc8b0ab497c0c33fc6bae4254b91cec65d8a4e6e1904989
+size 15666
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Misc/Meshes/1M_Cube_Chamfer.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Misc/Meshes/1M_Cube_Chamfer.uasset
new file mode 100644
index 0000000..5ec7c86
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Misc/Meshes/1M_Cube_Chamfer.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1126f5d84f073db0a0b171757f6c82d6cc5535e48e762ba9f88328f73168a1bf
+size 89045
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Misc/Meshes/CubeMaterial.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Misc/Meshes/CubeMaterial.uasset
new file mode 100644
index 0000000..67f9fa5
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Misc/Meshes/CubeMaterial.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:afb6e4332e8e6adbc6371511f9e464e91c9b866368fcb07e076e510da8dd5465
+size 93059
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Misc/Meshes/TemplateFloor.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Misc/Meshes/TemplateFloor.uasset
new file mode 100644
index 0000000..21c8e48
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Misc/Meshes/TemplateFloor.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:deffbbd85fa4b8b5744f2f4dc1db5921e1993716acfcc1c1f18ab37d8a5bb49a
+size 95181
diff --git a/VIRTUOS_ExpansionPluginTests/Content/VRE/Misc/PostProcess/PP_StereoTexture.uasset b/VIRTUOS_ExpansionPluginTests/Content/VRE/Misc/PostProcess/PP_StereoTexture.uasset
new file mode 100644
index 0000000..a297522
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Content/VRE/Misc/PostProcess/PP_StereoTexture.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:eafc13b2fd952c0ee10ea95a98a6e718d3ac9cb25992560a3395ee4f8c812191
+size 79674
diff --git a/VIRTUOS_ExpansionPluginTests/LICENSE.txt b/VIRTUOS_ExpansionPluginTests/LICENSE.txt
new file mode 100644
index 0000000..986c999
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/LICENSE.txt
@@ -0,0 +1,19 @@
+Copyright Joshua Statzer
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/.gitattributes b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/.gitattributes
new file mode 100644
index 0000000..3373152
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/.gitattributes
@@ -0,0 +1,2 @@
+* text=auto
+*.bat eol=crlf
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/.gitignore b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/.gitignore
new file mode 100644
index 0000000..3cdb673
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/.gitignore
@@ -0,0 +1,10 @@
+
+.hg/
+binaries/
+deriveddatacache/
+.vs/
+build/
+intermediate/
+PACKPLUGIN/
+saved/
+*.orig
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/AdvancedSessions.uplugin b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/AdvancedSessions.uplugin
new file mode 100644
index 0000000..594ee8f
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/AdvancedSessions.uplugin
@@ -0,0 +1,34 @@
+{
+ "FileVersion": 3,
+ "FriendlyName": "Advanced Sessions",
+ "Version": 5.1,
+ "VersionName": "5.1",
+ "Description": "Adds new blueprint functions to handle more advanced session operations.",
+ "Category": "Advanced Sessions Plugin",
+ "CreatedBy": "Joshua Statzer",
+ "CreatedByURL": "N/A",
+ "Modules": [
+ {
+ "Name": "AdvancedSessions",
+ "Type": "Runtime",
+ "LoadingPhase": "PreDefault"
+ }
+ ],
+ "Plugins": [
+ {
+ "Name": "OnlineSubsystem",
+ "Enabled": true
+ },
+ {
+ "Name": "OnlineSubsystemUtils",
+ "Enabled": true
+ }
+ ],
+ "DocsURL": "",
+ "MarketplaceURL": "",
+ "SupportURL": "",
+ "CanContainContent": false,
+ "IsBetaVersion": false,
+ "IsExperimentalVersion": false,
+ "Installed": false
+}
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Config/FilterPlugin.ini b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Config/FilterPlugin.ini
new file mode 100644
index 0000000..ccebca2
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Config/FilterPlugin.ini
@@ -0,0 +1,8 @@
+[FilterPlugin]
+; This section lists additional files which will be packaged along with your plugin. Paths should be listed relative to the root plugin directory, and
+; may include "...", "*", and "?" wildcards to match directories, files, and individual characters respectively.
+;
+; Examples:
+; /README.txt
+; /Extras/...
+; /Binaries/ThirdParty/*.dll
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Resources/Icon128.png b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Resources/Icon128.png
new file mode 100644
index 0000000..bc2b90d
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Resources/Icon128.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9a4c9fd3d85a99b80d3008e8c9ede55943cc16b79a382a8200b3fa4883d5468b
+size 3371
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/AdvancedSessions.Build.cs b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/AdvancedSessions.Build.cs
new file mode 100644
index 0000000..a305151
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/AdvancedSessions.Build.cs
@@ -0,0 +1,17 @@
+using UnrealBuildTool;
+using System.IO;
+
+public class AdvancedSessions : ModuleRules
+{
+ public AdvancedSessions(ReadOnlyTargetRules Target) : base(Target)
+ {
+ PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
+ //bEnforceIWYU = true;
+
+ PublicDefinitions.Add("WITH_ADVANCED_SESSIONS=1");
+
+ // PrivateIncludePaths.AddRange(new string[] { "AdvancedSessions/Private"/*, "OnlineSubsystemSteam/Private"*/ });
+ // PublicIncludePaths.AddRange(new string[] { "AdvancedSessions/Public" });
+ PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "OnlineSubsystem", "CoreUObject", "OnlineSubsystemUtils", "Networking", "Sockets"/*"Voice", "OnlineSubsystemSteam"*/ });
+ }
+}
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/AdvancedExternalUILibrary.h b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/AdvancedExternalUILibrary.h
new file mode 100644
index 0000000..968f623
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/AdvancedExternalUILibrary.h
@@ -0,0 +1,63 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+#include "CoreMinimal.h"
+#include "BlueprintDataDefinitions.h"
+#include "Kismet/BlueprintFunctionLibrary.h"
+#include "Online.h"
+#include "Engine/LocalPlayer.h"
+#include "OnlineSubsystem.h"
+#include "BlueprintDataDefinitions.h"
+//#include "OnlineFriendsInterface.h"
+//#include "OnlineUserInterface.h"
+//#include "OnlineMessageInterface.h"
+//#include "OnlinePresenceInterface.h"
+//#include "Engine/GameInstance.h"
+#include "Interfaces/OnlineSessionInterface.h"
+
+//#include "UObjectIterator.h"
+
+#include "AdvancedExternalUILibrary.generated.h"
+
+
+//General Advanced Sessions Log
+DECLARE_LOG_CATEGORY_EXTERN(AdvancedExternalUILog, Log, All);
+
+UCLASS()
+class UAdvancedExternalUILibrary : public UBlueprintFunctionLibrary
+{
+ GENERATED_BODY()
+public:
+
+ //********* External UI Functions *************//
+
+ // Show the UI that handles the Friends list
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedExternalUI", meta = (ExpandEnumAsExecs = "Result"))
+ static void ShowFriendsUI(APlayerController *PlayerController, EBlueprintResultSwitch &Result);
+
+ // Show the UI that handles inviting people to your game
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedExternalUI", meta = (ExpandEnumAsExecs = "Result"))
+ static void ShowInviteUI(APlayerController *PlayerController, EBlueprintResultSwitch &Result);
+
+ // Show the UI that shows the leaderboard (doesn't work with steam)
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedExternalUI", meta = (ExpandEnumAsExecs = "Result"))
+ static void ShowLeaderBoardUI(FString LeaderboardName, EBlueprintResultSwitch &Result);
+
+ // Show the UI that shows a web URL
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedExternalUI", meta = (ExpandEnumAsExecs = "Result", AutoCreateRefTerm = "AllowedDomains"))
+ static void ShowWebURLUI(FString URLToShow, EBlueprintResultSwitch &Result, TArray& AllowedDomains, bool bEmbedded = false , bool bShowBackground = false, bool bShowCloseButton = false, int32 OffsetX = 0, int32 OffsetY = 0, int32 SizeX = 0, int32 SizeY = 0);
+
+ // Show the UI that shows a web URL
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedExternalUI")
+ static void CloseWebURLUI();
+
+
+ // Show the UI that shows the profile of a uniquenetid
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedExternalUI", meta = (ExpandEnumAsExecs = "Result"))
+ static void ShowProfileUI(const FBPUniqueNetId PlayerViewingProfile, const FBPUniqueNetId PlayerToViewProfileOf, EBlueprintResultSwitch &Result);
+
+ // Show the UI that shows the account upgrade UI (doesn't work with steam)
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedExternalUI", meta = (ExpandEnumAsExecs = "Result"))
+ static void ShowAccountUpgradeUI(const FBPUniqueNetId PlayerRequestingAccountUpgradeUI, EBlueprintResultSwitch &Result);
+
+};
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/AdvancedFriendsGameInstance.h b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/AdvancedFriendsGameInstance.h
new file mode 100644
index 0000000..be1def9
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/AdvancedFriendsGameInstance.h
@@ -0,0 +1,145 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+#include "CoreMinimal.h"
+#include "Engine/Engine.h"
+#include "BlueprintDataDefinitions.h"
+#include "Kismet/BlueprintFunctionLibrary.h"
+#include "Online.h"
+#include "OnlineSubsystem.h"
+#include "Interfaces/OnlineFriendsInterface.h"
+#include "Interfaces/OnlineUserInterface.h"
+#include "Interfaces/OnlineMessageInterface.h"
+#include "Interfaces/OnlinePresenceInterface.h"
+#include "Engine/GameInstance.h"
+#include "Engine/LocalPlayer.h"
+#include "Interfaces/OnlineSessionInterface.h"
+#include "OnlineSessionSettings.h"
+#include "UObject/UObjectIterator.h"
+#include "AdvancedFriendsInterface.h"
+
+#include "AdvancedFriendsGameInstance.generated.h"
+
+
+//General Advanced Sessions Log
+DECLARE_LOG_CATEGORY_EXTERN(AdvancedFriendsInterfaceLog, Log, All);
+
+UCLASS()
+class ADVANCEDSESSIONS_API UAdvancedFriendsGameInstance : public UGameInstance
+{
+ GENERATED_BODY()
+public:
+
+ UAdvancedFriendsGameInstance(const FObjectInitializer& ObjectInitializer);
+
+ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AdvancedFriendsInterface)
+ bool bCallFriendInterfaceEventsOnPlayerControllers;
+
+ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AdvancedFriendsInterface)
+ bool bCallIdentityInterfaceEventsOnPlayerControllers;
+
+ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AdvancedFriendsInterface)
+ bool bCallVoiceInterfaceEventsOnPlayerControllers;
+
+ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AdvancedVoiceInterface)
+ bool bEnableTalkingStatusDelegate;
+
+ //virtual void PostLoad() override;
+ virtual void Shutdown() override;
+ virtual void Init() override;
+
+ //*** Session invite received by local ***//
+ FOnSessionInviteReceivedDelegate SessionInviteReceivedDelegate;
+ FDelegateHandle SessionInviteReceivedDelegateHandle;
+
+ //const FUniqueNetId& /*UserId*/, const FUniqueNetId& /*FromId*/, const FString& /*AppId*/, const FOnlineSessionSearchResult& /*InviteResult*/
+ void OnSessionInviteReceivedMaster(const FUniqueNetId & PersonInvited, const FUniqueNetId & PersonInviting, const FString & AppId, const FOnlineSessionSearchResult& SessionToJoin);
+
+ // After a session invite has been accepted by the local player this event is triggered, call JoinSession on the session result to join it
+ UFUNCTION(BlueprintImplementableEvent, Category = "AdvancedFriends")
+ void OnSessionInviteReceived(int32 LocalPlayerNum, FBPUniqueNetId PersonInviting, const FString& AppId, const FBlueprintSessionResult& SessionToJoin);
+
+ //*** Session invite accepted by local ***//
+ FOnSessionUserInviteAcceptedDelegate SessionInviteAcceptedDelegate;
+ FDelegateHandle SessionInviteAcceptedDelegateHandle;
+
+ void OnSessionInviteAcceptedMaster(const bool bWasSuccessful, int32 LocalPlayer, TSharedPtr PersonInviting, const FOnlineSessionSearchResult& SessionToJoin);
+
+ // After a session invite has been accepted by the local player this event is triggered, call JoinSession on the session result to join it
+ // This function is currently not hooked up in any of Epics default subsystems, it is here for custom subsystems
+ UFUNCTION(BlueprintImplementableEvent, Category = "AdvancedFriends")
+ void OnSessionInviteAccepted(int32 LocalPlayerNum, FBPUniqueNetId PersonInvited, const FBlueprintSessionResult& SessionToJoin);
+
+
+ // After a voice status has changed this event is triggered if the bEnableTalkingStatusDelegate property is true
+ UFUNCTION(BlueprintImplementableEvent, Category = "AdvancedVoice")
+ void OnPlayerTalkingStateChanged(FBPUniqueNetId PlayerId, bool bIsTalking);
+
+ void OnPlayerTalkingStateChangedMaster(TSharedRef PlayerId, bool bIsTalking);
+
+ FOnPlayerTalkingStateChangedDelegate PlayerTalkingStateChangedDelegate;
+ FDelegateHandle PlayerTalkingStateChangedDelegateHandle;
+
+
+ // Called when the designated LocalUser has changed login state
+ UFUNCTION(BlueprintImplementableEvent , Category = "AdvancedIdentity", meta = (DisplayName = "OnPlayerLoginChanged"))
+ void OnPlayerLoginChanged(int32 PlayerNum);
+
+ void OnPlayerLoginChangedMaster(int32 PlayerNum);
+ FOnLoginChangedDelegate PlayerLoginChangedDelegate;
+ FDelegateHandle PlayerLoginChangedDelegateHandle;
+
+ // Called when the designated LocalUser has changed login status
+ UFUNCTION(BlueprintImplementableEvent, Category = "AdvancedIdentity", meta = (DisplayName = "OnPlayerLoginStatusChanged"))
+ void OnPlayerLoginStatusChanged(int32 PlayerNum, EBPLoginStatus PreviousStatus, EBPLoginStatus NewStatus, FBPUniqueNetId NewPlayerUniqueNetID);
+
+ void OnPlayerLoginStatusChangedMaster(int32 PlayerNum, ELoginStatus::Type PreviousStatus, ELoginStatus::Type NewStatus, const FUniqueNetId & NewPlayerUniqueNetID);
+ FOnLoginStatusChangedDelegate PlayerLoginStatusChangedDelegate;
+ FDelegateHandle PlayerLoginStatusChangedDelegateHandle;
+
+
+ //*** Session Invite Received From Friend ***//
+ // REMOVED BECAUSE IT NEVER GETS CALLED
+ /*FOnSessionInviteReceivedDelegate SessionInviteReceivedDelegate;
+ FDelegateHandle SessionInviteReceivedDelegateHandle;
+
+ void OnSessionInviteReceivedMaster(const FUniqueNetId &InvitedPlayer, const FUniqueNetId &FriendInviting, const FOnlineSessionSearchResult& Session);
+
+ // After a session invite has been sent from a friend this event is triggered, call JoinSession on the session result to join it
+ UFUNCTION(BlueprintImplementableEvent, Category = "AdvancedFriends")
+ void OnSessionInviteReceived(const FBPUniqueNetId &InvitedPlayer, const FBPUniqueNetId &FriendInviting, const FBlueprintSessionResult &Session);
+ */
+
+ //*** Friend Invite Accepted ***//
+ /*FOnInviteAcceptedDelegate FriendInviteAcceptedDelegate;
+ FDelegateHandle FriendInviteAcceptedDelegateHandle;
+
+ void OnFriendInviteAcceptedDelegateMaster(const FUniqueNetId& LocalPlayer, const FUniqueNetId &PlayerInvited);
+
+ // After a session invite has been accepted by a friend this event is triggered
+ UFUNCTION(BlueprintImplementableEvent, Category = "AdvancedFriends")
+ void OnFriendInviteAccepted(const FBPUniqueNetId &InvitedPlayer, const FBPUniqueNetId &PlayerInvited);
+ */
+
+ //*** Friend Invite Rejected ***//
+ /*FOnInviteRejectedDelegate SessionInviteRejectedByFriendDelegate;
+ FDelegateHandle InviteRejectedByFriendDelegateHandle;
+
+ void OnFriendInviteRejectedDelegateMaster(const FUniqueNetId& LocalPlayer, const FUniqueNetId &PlayerDeclined);
+
+ // After a friend invite has been rejected this event is triggered
+ UFUNCTION(BlueprintImplementableEvent, Category = "AdvancedFriends")
+ void OnFriendInviteRejected(const FBPUniqueNetId &InvitedPlayer, const FBPUniqueNetId &PlayerDeclined);
+ */
+
+ //*** Removed By Friend ***//
+ /*FOnFriendRemovedDelegate RemovedByFriendDelegate;
+ FDelegateHandle RemovedByFriendDelegateHandle;
+
+ void OnRemovedByFriendDelegateMaster(const FUniqueNetId& LocalPlayer, const FUniqueNetId &FriendRemoved);
+
+ // After a friend removed the player this event is triggered
+ UFUNCTION(BlueprintImplementableEvent, Category = "AdvancedFriends")
+ void OnRemovedByFriend(const FBPUniqueNetId &InvitedPlayer, const FBPUniqueNetId &FriendRemoved);*/
+};
+
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/AdvancedFriendsInterface.h b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/AdvancedFriendsInterface.h
new file mode 100644
index 0000000..44cfc0b
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/AdvancedFriendsInterface.h
@@ -0,0 +1,56 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+#include "CoreMinimal.h"
+#include "Kismet/BlueprintFunctionLibrary.h"
+#include "Online.h"
+#include "OnlineSubsystem.h"
+#include "Interfaces/OnlineFriendsInterface.h"
+#include "Interfaces/OnlineUserInterface.h"
+#include "Interfaces/OnlineMessageInterface.h"
+#include "Interfaces/OnlinePresenceInterface.h"
+#include "Engine/GameInstance.h"
+#include "Interfaces/OnlineSessionInterface.h"
+#include "OnlineSessionSettings.h"
+#include "UObject/UObjectIterator.h"
+#include "BlueprintDataDefinitions.h"
+#include "AdvancedFriendsInterface.generated.h"
+
+
+UINTERFACE(MinimalAPI)
+class UAdvancedFriendsInterface : public UInterface
+{
+ GENERATED_UINTERFACE_BODY()
+};
+
+class IAdvancedFriendsInterface
+{
+ GENERATED_IINTERFACE_BODY()
+public:
+
+ // Called when the designated LocalUser has accepted a session invite, use JoinSession on result to connect
+ UFUNCTION(BlueprintImplementableEvent, meta = (DisplayName = "OnSessionInviteReceived"))
+ void OnSessionInviteReceived(FBPUniqueNetId PersonInviting, const FBlueprintSessionResult& SearchResult);
+
+ // Called when the designated LocalUser has accepted a session invite, use JoinSession on result to connect
+ UFUNCTION(BlueprintImplementableEvent, meta = (DisplayName = "OnSessionInviteAccepted"))
+ void OnSessionInviteAccepted(FBPUniqueNetId PersonInvited, const FBlueprintSessionResult& SearchResult);
+
+ // Called when the designated LocalUser has accepted a session invite, use JoinSession on result to connect
+ UFUNCTION(BlueprintImplementableEvent, meta = (DisplayName = "OnPlayerVoiceStateChanged"))
+ void OnPlayerVoiceStateChanged(FBPUniqueNetId PlayerId, bool bIsTalking);
+
+ // Called when the designated LocalUser has changed login state
+ UFUNCTION(BlueprintImplementableEvent, meta = (DisplayName = "OnPlayerLoginChanged"))
+ void OnPlayerLoginChanged(int32 PlayerNum);
+
+ // Called when the designated LocalUser has changed login state
+ UFUNCTION(BlueprintImplementableEvent, meta = (DisplayName = "OnPlayerLoginStatusChanged"))
+ void OnPlayerLoginStatusChanged(EBPLoginStatus PreviousStatus, EBPLoginStatus NewStatus, FBPUniqueNetId PlayerUniqueNetID);
+
+ // REMOVED BECAUSE IT WAS NEVER BEING CALLED
+ // Called when the designated LocalUser has received a session invite, use JoinSession on result to connect
+ //UFUNCTION(BlueprintImplementableEvent, meta = (FriendlyName = "OnSessionInviteReceived"))
+ //void OnSessionInviteReceived(const FBPUniqueNetId &FriendInviting, const FBlueprintSessionResult &Session);
+
+};
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/AdvancedFriendsLibrary.h b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/AdvancedFriendsLibrary.h
new file mode 100644
index 0000000..fedf2c1
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/AdvancedFriendsLibrary.h
@@ -0,0 +1,56 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+#include "CoreMinimal.h"
+#include "BlueprintDataDefinitions.h"
+#include "Kismet/BlueprintFunctionLibrary.h"
+#include "Online.h"
+#include "Engine/LocalPlayer.h"
+#include "OnlineSubsystem.h"
+#include "Interfaces/OnlineFriendsInterface.h"
+#include "Interfaces/OnlineUserInterface.h"
+#include "Interfaces/OnlineMessageInterface.h"
+#include "Interfaces/OnlinePresenceInterface.h"
+#include "Engine/GameInstance.h"
+#include "Interfaces/OnlineSessionInterface.h"
+
+#include "UObject/UObjectIterator.h"
+
+#include "AdvancedFriendsLibrary.generated.h"
+
+
+//General Advanced Sessions Log
+DECLARE_LOG_CATEGORY_EXTERN(AdvancedFriendsLog, Log, All);
+
+UCLASS()
+class UAdvancedFriendsLibrary : public UBlueprintFunctionLibrary
+{
+ GENERATED_BODY()
+public:
+
+ //********* Friend List Functions *************//
+
+ // Sends an Invite to the current online session to a list of friends
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedFriends|FriendsList", meta = (ExpandEnumAsExecs = "Result"))
+ static void SendSessionInviteToFriends(APlayerController *PlayerController, const TArray &Friends, EBlueprintResultSwitch &Result);
+
+ // Sends an Invite to the current online session to a friend
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedFriends|FriendsList", meta = (ExpandEnumAsExecs = "Result"))
+ static void SendSessionInviteToFriend(APlayerController *PlayerController, const FBPUniqueNetId &FriendUniqueNetId, EBlueprintResultSwitch &Result);
+
+ // Get a friend from the previously read/saved friends list (Must Call GetFriends first for this to return anything)
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedFriends|FriendsList")
+ static void GetFriend(APlayerController *PlayerController, const FBPUniqueNetId FriendUniqueNetId, FBPFriendInfo &Friend);
+
+ // Get the previously read/saved friends list (Must Call GetFriends first for this to return anything)
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedFriends|FriendsList")
+ static void GetStoredFriendsList(APlayerController *PlayerController, TArray &FriendsList);
+
+ // Get the previously read/saved recent players list (Must Call GetRecentPlayers first for this to return anything)
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedFriends|RecentPlayersList")
+ static void GetStoredRecentPlayersList(FBPUniqueNetId UniqueNetId, TArray &PlayersList);
+
+ // Check if a UniqueNetId is a friend
+ UFUNCTION(BlueprintPure, Category = "Online|AdvancedFriends|FriendsList")
+ static void IsAFriend(APlayerController *PlayerController, const FBPUniqueNetId UniqueNetId, bool &IsFriend);
+};
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/AdvancedGameSession.h b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/AdvancedGameSession.h
new file mode 100644
index 0000000..179d857
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/AdvancedGameSession.h
@@ -0,0 +1,71 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+#include "CoreMinimal.h"
+#include "Engine/Engine.h"
+#include "Online.h"
+#include "OnlineSubsystem.h"
+#include "Engine/GameInstance.h"
+#include "GameFramework/GameModeBase.h"
+#include "GameFramework/GameSession.h"
+#include "GameFramework/PlayerState.h"
+
+//#include "UObjectIterator.h"
+
+#include "AdvancedGameSession.generated.h"
+
+
+
+
+/**
+ A quick wrapper around the game session to add a partial ban implementation. Just bans for the duration of the current session
+*/
+UCLASS(config = Game, notplaceable)
+class AAdvancedGameSession : public AGameSession
+{
+ GENERATED_UCLASS_BODY()
+
+public:
+
+ UPROPERTY(Transient)
+ TMap BanList;
+
+ virtual bool BanPlayer(class APlayerController* BannedPlayer, const FText& BanReason)
+ {
+
+ if (APlayerState* PlayerState = (BannedPlayer != NULL) ? BannedPlayer->PlayerState : NULL)
+ {
+ FUniqueNetIdRepl UniqueNetID = PlayerState->GetUniqueId();
+ bool bWasKicked = KickPlayer(BannedPlayer, BanReason);
+
+ if (bWasKicked)
+ {
+ BanList.Add(UniqueNetID, BanReason);
+ }
+
+ return bWasKicked;
+ }
+
+ return false;
+ }
+
+ // This should really be handled in the game mode asking game session, but I didn't want to force a custom game session AND game mode
+ // If done in the game mode, we could check prior to actually spooling up any player information in ApproveLogin
+ virtual void PostLogin(APlayerController* NewPlayer) override
+ {
+ if (APlayerState* PlayerState = (NewPlayer != NULL) ? NewPlayer->PlayerState : NULL)
+ {
+ FUniqueNetIdRepl UniqueNetID = PlayerState->GetUniqueId();
+
+ if (BanList.Contains(UniqueNetID))
+ {
+ KickPlayer(NewPlayer, BanList[UniqueNetID]);
+ }
+ }
+ }
+};
+
+AAdvancedGameSession::AAdvancedGameSession(const FObjectInitializer& ObjectInitializer)
+ : Super(ObjectInitializer)
+{
+}
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/AdvancedIdentityLibrary.h b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/AdvancedIdentityLibrary.h
new file mode 100644
index 0000000..392ab8c
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/AdvancedIdentityLibrary.h
@@ -0,0 +1,81 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+#include "CoreMinimal.h"
+#include "BlueprintDataDefinitions.h"
+#include "Kismet/BlueprintFunctionLibrary.h"
+#include "Online.h"
+#include "OnlineSubsystem.h"
+#include "Interfaces/OnlineIdentityInterface.h"
+#include "Interfaces/OnlineUserInterface.h"
+#include "Interfaces/OnlinePresenceInterface.h"
+#include "Engine/GameInstance.h"
+#include "Engine/LocalPlayer.h"
+
+#include "UObject/UObjectIterator.h"
+
+#include "AdvancedIdentityLibrary.generated.h"
+
+
+//General Advanced Sessions Log
+DECLARE_LOG_CATEGORY_EXTERN(AdvancedIdentityLog, Log, All);
+
+UCLASS()
+class UAdvancedIdentityLibrary : public UBlueprintFunctionLibrary
+{
+ GENERATED_BODY()
+public:
+ //********* Identity Functions *************//
+
+ // Get the login status of a local player
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedIdentity", meta = (ExpandEnumAsExecs = "Result"))
+ static void GetLoginStatus(const FBPUniqueNetId & UniqueNetID, EBPLoginStatus & LoginStatus, EBlueprintResultSwitch &Result);
+
+ // Get the auth token for a local player
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedIdentity", meta = (ExpandEnumAsExecs = "Result"))
+ static void GetPlayerAuthToken(APlayerController * PlayerController, FString & AuthToken, EBlueprintResultSwitch &Result);
+
+ // Get a players nickname
+ UFUNCTION(BlueprintPure, Category = "Online|AdvancedIdentity")
+ static void GetPlayerNickname(const FBPUniqueNetId & UniqueNetID, FString & PlayerNickname);
+
+ //********* User Account Functions *************//
+
+ // Get a users account
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedIdentity|UserAccount", meta = (ExpandEnumAsExecs = "Result"))
+ static void GetUserAccount(const FBPUniqueNetId & UniqueNetId, FBPUserOnlineAccount & AccountInfo, EBlueprintResultSwitch &Result);
+
+ // Get all known users accounts
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedIdentity|UserAccount", meta = (ExpandEnumAsExecs = "Result"))
+ static void GetAllUserAccounts(TArray & AccountInfos, EBlueprintResultSwitch &Result);
+
+ // Get a user account access token
+ UFUNCTION(BlueprintPure, Category = "Online|AdvancedIdentity|UserAccount")
+ static void GetUserAccountAccessToken(const FBPUserOnlineAccount & AccountInfo, FString & AccessToken);
+
+ // Get a user account Auth attribute (depends on subsystem)
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedIdentity|UserAccount", meta = (ExpandEnumAsExecs = "Result"))
+ static void GetUserAccountAuthAttribute(const FBPUserOnlineAccount & AccountInfo, const FString & AttributeName, FString & AuthAttribute, EBlueprintResultSwitch &Result);
+
+ // Set a user account attribute (depends on subsystem)
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedIdentity|UserAccount", meta = (ExpandEnumAsExecs = "Result"))
+ static void SetUserAccountAttribute(const FBPUserOnlineAccount & AccountInfo, const FString & AttributeName, const FString & NewAttributeValue, EBlueprintResultSwitch &Result);
+
+ // Get user ID
+ UFUNCTION(BlueprintPure, Category = "Online|AdvancedIdentity|UserAccount")
+ static void GetUserID(const FBPUserOnlineAccount & AccountInfo, FBPUniqueNetId & UniqueNetID);
+
+ // Get user accounts real name if possible
+ UFUNCTION(BlueprintPure, Category = "Online|AdvancedIdentity|UserAccount")
+ static void GetUserAccountRealName(const FBPUserOnlineAccount & AccountInfo, FString & UserName);
+
+ // Get user account display name if possible
+ UFUNCTION(BlueprintPure, Category = "Online|AdvancedIdentity|UserAccount")
+ static void GetUserAccountDisplayName(const FBPUserOnlineAccount & AccountInfo, FString & DisplayName);
+
+ // Get user account attribute (depends on subsystem)
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedIdentity|UserAccount", meta = (ExpandEnumAsExecs = "Result"))
+ static void GetUserAccountAttribute(const FBPUserOnlineAccount & AccountInfo, const FString & AttributeName, FString & AttributeValue, EBlueprintResultSwitch &Result);
+
+
+};
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/AdvancedSessions.h b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/AdvancedSessions.h
new file mode 100644
index 0000000..6218cd0
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/AdvancedSessions.h
@@ -0,0 +1,11 @@
+#pragma once
+
+#include "Modules/ModuleManager.h"
+
+class AdvancedSessions : public IModuleInterface
+{
+public:
+ /** IModuleInterface implementation */
+ void StartupModule();
+ void ShutdownModule();
+};
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/AdvancedSessionsLibrary.h b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/AdvancedSessionsLibrary.h
new file mode 100644
index 0000000..54153ae
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/AdvancedSessionsLibrary.h
@@ -0,0 +1,209 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+#include "CoreMinimal.h"
+#include "Engine/Engine.h"
+#include "BlueprintDataDefinitions.h"
+#include "Kismet/BlueprintFunctionLibrary.h"
+#include "Online.h"
+#include "OnlineSubsystem.h"
+#include "Interfaces/OnlineFriendsInterface.h"
+#include "Interfaces/OnlineUserInterface.h"
+#include "Interfaces/OnlineMessageInterface.h"
+#include "Interfaces/OnlinePresenceInterface.h"
+#include "Engine/GameInstance.h"
+#include "Interfaces/OnlineSessionInterface.h"
+
+#include "GameFramework/GameModeBase.h"
+#include "GameFramework/GameSession.h"
+
+//#include "UObjectIterator.h"
+
+#include "AdvancedSessionsLibrary.generated.h"
+
+
+//General Advanced Sessions Log
+DECLARE_LOG_CATEGORY_EXTERN(AdvancedSessionsLog, Log, All);
+
+
+UCLASS()
+class UAdvancedSessionsLibrary : public UBlueprintFunctionLibrary
+{
+ GENERATED_BODY()
+public:
+ //********* Session Admin Functions *************//
+
+ // Kick a player from the currently active game session, only available on the server
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedSessions", meta = (WorldContext = "WorldContextObject"))
+ static bool KickPlayer(UObject* WorldContextObject, APlayerController* PlayerToKick, FText KickReason);
+
+ // Ban a player from the currently active game session, only available on the server
+ // Note that the default gamesession class does not implement an actual ban list and just kicks when this is called
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedSessions", meta = (WorldContext = "WorldContextObject"))
+ static bool BanPlayer(UObject* WorldContextObject, APlayerController* PlayerToBan, FText BanReason);
+
+ //********* Session Search Functions *************//
+
+ // Adds or modifies session settings in an existing array depending on if they exist already or not
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedSessions|SessionInfo")
+ static void AddOrModifyExtraSettings(UPARAM(ref) TArray & SettingsArray, UPARAM(ref) TArray & NewOrChangedSettings, TArray & ModifiedSettingsArray);
+
+ // Get an array of the session settings from a session search result
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedSessions|SessionInfo")
+ static void GetExtraSettings(FBlueprintSessionResult SessionResult, TArray & ExtraSettings);
+
+ // Get the current session state
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedSessions|SessionInfo", meta = (WorldContext = "WorldContextObject"))
+ static void GetSessionState(UObject* WorldContextObject, EBPOnlineSessionState &SessionState);
+
+ // Get the current session settings
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedSessions|SessionInfo", meta = (ExpandEnumAsExecs = "Result", WorldContext = "WorldContextObject"))
+ static void GetSessionSettings(UObject* WorldContextObject, int32 &NumConnections, int32 &NumPrivateConnections, bool &bIsLAN, bool &bIsDedicated, bool &bAllowInvites, bool &bAllowJoinInProgress, bool &bIsAnticheatEnabled, int32 &BuildUniqueID, TArray &ExtraSettings, EBlueprintResultSwitch &Result);
+
+ // Check if someone is in the current session
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedSessions|SessionInfo", meta = (WorldContext = "WorldContextObject"))
+ static void IsPlayerInSession(UObject* WorldContextObject, const FBPUniqueNetId &PlayerToCheck, bool &bIsInSession);
+
+ // Make a literal session search parameter
+ UFUNCTION(BlueprintPure, Category = "Online|AdvancedSessions|SessionInfo|Literals")
+ static FSessionsSearchSetting MakeLiteralSessionSearchProperty(FSessionPropertyKeyPair SessionSearchProperty, EOnlineComparisonOpRedux ComparisonOp);
+
+
+ //********* Session Information Functions ***********//
+
+ // Check if a session result is valid or not
+ UFUNCTION(BlueprintPure, Category = "Online|AdvancedSessions|SessionInfo")
+ static bool IsValidSession(const FBlueprintSessionResult & SessionResult);
+
+ // Get a string copy of a session ID
+ UFUNCTION(BlueprintPure, Category = "Online|AdvancedSessions|SessionInfo")
+ static void GetSessionID_AsString(const FBlueprintSessionResult & SessionResult, FString& SessionID);
+
+ // Get a string copy of the current session ID
+ UFUNCTION(BlueprintPure, Category = "Online|AdvancedSessions|SessionInfo", meta = (WorldContext = "WorldContextObject"))
+ static void GetCurrentSessionID_AsString(UObject* WorldContextObject, FString& SessionID);
+
+ // Get the Unique Current Build ID
+ UFUNCTION(BlueprintPure, Category = "Online|AdvancedSessions|SessionInfo")
+ static void GetCurrentUniqueBuildID(int32 &UniqueBuildId);
+
+ // Get the Unique Build ID from a session search result
+ UFUNCTION(BlueprintPure, Category = "Online|AdvancedSessions|SessionInfo")
+ static void GetUniqueBuildID(FBlueprintSessionResult SessionResult, int32 &UniqueBuildId);
+
+
+ // Thanks CriErr for submission
+
+
+ // Get session property Key Name value
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedSessions|SessionInfo")
+ static FName GetSessionPropertyKey(const FSessionPropertyKeyPair& SessionProperty);
+
+ // Find session property by Name
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedSessions|SessionInfo", meta = (ExpandEnumAsExecs = "Result"))
+ static void FindSessionPropertyByName(const TArray& ExtraSettings, FName SettingsName, EBlueprintResultSwitch &Result, FSessionPropertyKeyPair& OutProperty);
+
+ // Find session property index by Name
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedSessions|SessionInfo", meta = (ExpandEnumAsExecs = "Result"))
+ static void FindSessionPropertyIndexByName(const TArray& ExtraSettings, FName SettingName, EBlueprintResultSwitch &Result, int32& OutIndex);
+
+ /// Removed the Index_None part of the last function, that isn't accessible in blueprint, better to return success/failure
+ // End Thanks CriErr :p
+
+ // Get session custom information key/value as Byte (For Enums)
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedSessions|SessionInfo", meta = (ExpandEnumAsExecs = "SearchResult"))
+ static void GetSessionPropertyByte(const TArray & ExtraSettings, FName SettingName, ESessionSettingSearchResult &SearchResult, uint8 &SettingValue);
+
+ // Get session custom information key/value as Bool
+ // Steam only currently supports Int,Float,String,BYTE values for search filtering!!!
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedSessions|SessionInfo", meta = (ExpandEnumAsExecs = "SearchResult"))
+ static void GetSessionPropertyBool(const TArray & ExtraSettings, FName SettingName, ESessionSettingSearchResult &SearchResult, bool &SettingValue);
+
+ // Get session custom information key/value as String
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedSessions|SessionInfo", meta = (ExpandEnumAsExecs = "SearchResult"))
+ static void GetSessionPropertyString(const TArray & ExtraSettings, FName SettingName, ESessionSettingSearchResult &SearchResult, FString &SettingValue);
+
+ // Get session custom information key/value as Int
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedSessions|SessionInfo", meta = (ExpandEnumAsExecs = "SearchResult"))
+ static void GetSessionPropertyInt(const TArray & ExtraSettings, FName SettingName, ESessionSettingSearchResult &SearchResult, int32 &SettingValue);
+
+ // Get session custom information key/value as Float
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedSessions|SessionInfo", meta = (ExpandEnumAsExecs = "SearchResult"))
+ static void GetSessionPropertyFloat(const TArray & ExtraSettings, FName SettingName, ESessionSettingSearchResult &SearchResult, float &SettingValue);
+
+
+ // Make a literal session custom information key/value pair from Byte (For Enums)
+ UFUNCTION(BlueprintPure, Category = "Online|AdvancedSessions|SessionInfo|Literals")
+ static FSessionPropertyKeyPair MakeLiteralSessionPropertyByte(FName Key, uint8 Value);
+
+ // Make a literal session custom information key/value pair from Bool
+ // Steam only currently supports Int,Float,String,BYTE values for search filtering!
+ UFUNCTION(BlueprintPure, Category = "Online|AdvancedSessions|SessionInfo|Literals")
+ static FSessionPropertyKeyPair MakeLiteralSessionPropertyBool(FName Key, bool Value);
+
+ // Make a literal session custom information key/value pair from String
+ UFUNCTION(BlueprintPure, Category = "Online|AdvancedSessions|SessionInfo|Literals")
+ static FSessionPropertyKeyPair MakeLiteralSessionPropertyString(FName Key, FString Value);
+
+ // Make a literal session custom information key/value pair from Int
+ UFUNCTION(BlueprintPure, Category = "Online|AdvancedSessions|SessionInfo|Literals")
+ static FSessionPropertyKeyPair MakeLiteralSessionPropertyInt(FName Key, int32 Value);
+
+ // Make a literal session custom information key/value pair from Float
+ UFUNCTION(BlueprintPure, Category = "Online|AdvancedSessions|SessionInfo|Literals")
+ static FSessionPropertyKeyPair MakeLiteralSessionPropertyFloat(FName Key, float Value);
+
+
+ //******* Player ID functions *********//
+
+ // Get the unique net id of a network player attached to the given controller
+ UFUNCTION(BlueprintPure, Category = "Online|AdvancedSessions|PlayerInfo|PlayerID")
+ static void GetUniqueNetID(APlayerController *PlayerController, FBPUniqueNetId &UniqueNetId);
+
+ // Get the unique net id of a network player who is assigned the the given player state
+ UFUNCTION(BlueprintPure, Category = "Online|AdvancedSessions|PlayerInfo|PlayerID")
+ static void GetUniqueNetIDFromPlayerState(APlayerState *PlayerState, FBPUniqueNetId &UniqueNetId);
+
+ // Return True if Unique Net ID is valid
+ UFUNCTION(BlueprintPure, Category = "Online|AdvancedSessions|PlayerInfo|PlayerID")
+ static bool IsValidUniqueNetID(const FBPUniqueNetId &UniqueNetId);
+
+ /* Returns true if the values are equal (A == B) */
+ UFUNCTION(BlueprintPure, meta = (DisplayName = "Equal Unique Net ID", CompactNodeTitle = "==", Keywords = "== equal"), Category = "Online|AdvancedSessions|PlayerInfo|PlayerID")
+ static bool EqualEqual_UNetIDUnetID(const FBPUniqueNetId &A, const FBPUniqueNetId &B);
+
+ // Check if a UniqueNetId is a friend
+ UFUNCTION(BlueprintPure, Category = "Online|AdvancedSessions|UniqueNetId")
+ static void UniqueNetIdToString(const FBPUniqueNetId &UniqueNetId, FString &String);
+
+ //******** Player Name Functions **********//
+
+ // Get the player name of a network player attached to the given controller
+ UFUNCTION(BlueprintPure, Category = "Online|AdvancedSessions|PlayerInfo|PlayerName")
+ static void GetPlayerName(APlayerController *PlayerController, FString &PlayerName);
+
+ // Set the player name of a network player attached to the given controller
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedSessions|PlayerInfo|PlayerName")
+ static void SetPlayerName(APlayerController *PlayerController, FString PlayerName);
+
+ //********** Misc Player Info Functions *********//
+
+ // Get the number of network players
+ UFUNCTION(BlueprintPure, Category = "Online|AdvancedSessions|PlayerInfo|Misc", meta = (bIgnoreSelf = "true", WorldContext = "WorldContextObject", DisplayName = "GetNumNetworkPlayers"))
+ static void GetNumberOfNetworkPlayers(UObject* WorldContextObject, int32 &NumNetPlayers);
+
+ // Get the network player index of the given controller
+ UFUNCTION(BlueprintPure, Category = "Online|AdvancedSessions|PlayerInfo|Misc")
+ static void GetNetPlayerIndex(APlayerController *PlayerController, int32 &NetPlayerIndex);
+
+ // Checks if the stated session subsystem is active
+ UFUNCTION(BlueprintPure, Category = "Online|AdvancedSessions|Misc")
+ static bool HasOnlineSubsystem(FName SubSystemName);
+
+ //**** Seamless travel Functions ****//
+
+ //Exposes Server travel to blueprint
+ UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category = "Online|AdvancedSessions|Seamless", meta = (HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"))
+ static bool ServerTravel(UObject* WorldContextObject, const FString& InURL, bool bAbsolute, bool bShouldSkipGameNotify);
+
+};
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/AdvancedVoiceLibrary.h b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/AdvancedVoiceLibrary.h
new file mode 100644
index 0000000..7f75b6c
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/AdvancedVoiceLibrary.h
@@ -0,0 +1,99 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+#include "CoreMinimal.h"
+#include "Kismet/BlueprintFunctionLibrary.h"
+#include "BlueprintDataDefinitions.h"
+#include "Online.h"
+#include "OnlineSubsystem.h"
+#include "Interfaces/VoiceInterface.h"
+//#include "OnlineFriendsInterface.h"
+//#include "OnlineUserInterface.h"
+//#include "OnlineMessageInterface.h"
+//#include "OnlinePresenceInterface.h"
+#include "Engine/GameInstance.h"
+//#include "OnlineSessionInterface.h"
+
+#include "UObject/UObjectIterator.h"
+
+#include "AdvancedVoiceLibrary.generated.h"
+
+
+//General Advanced Sessions Log
+DECLARE_LOG_CATEGORY_EXTERN(AdvancedVoiceLog, Log, All);
+
+
+UCLASS()
+class UAdvancedVoiceLibrary : public UBlueprintFunctionLibrary
+{
+ GENERATED_BODY()
+public:
+
+ //********* Voice Library Functions *************//
+
+ // Get if a headset is present for the specified local user
+ UFUNCTION(BlueprintPure, Category = "Online|AdvancedVoice|VoiceInfo")
+ static void IsHeadsetPresent(bool & bHasHeadset, uint8 LocalPlayerNum = 0);
+
+ // Starts networked voice, allows push to talk in coordination with StopNetworkedVoice
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedVoice")
+ static void StartNetworkedVoice(uint8 LocalPlayerNum = 0);
+
+ // Stops networked voice, allows push to talk in coordination with StartNetworkedVoice
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedVoice")
+ static void StopNetworkedVoice(uint8 LocalPlayerNum = 0);
+
+ // Registers a local player as someone interested in voice data
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedVoice")
+ static bool RegisterLocalTalker(uint8 LocalPlayerNum = 0);
+
+ // Registers all signed in players as local talkers
+ // This is already done automatically, only do it manually if you unregistered someone
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedVoice")
+ static void RegisterAllLocalTalkers();
+
+ // UnRegisters local player as a local talker
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedVoice")
+ static void UnRegisterLocalTalker(uint8 LocalPlayerNum = 0);
+
+ // UnRegisters all signed in players as local talkers
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedVoice")
+ static void UnRegisterAllLocalTalkers();
+
+ // Registers a remote player as a talker
+ // This is already done automatically, only do it manually if you unregistered someone
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedVoice")
+ static bool RegisterRemoteTalker(const FBPUniqueNetId& UniqueNetId);
+
+ // UnRegisters a remote player as a talker
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedVoice")
+ static bool UnRegisterRemoteTalker(const FBPUniqueNetId& UniqueNetId);
+
+ // UnRegisters all remote players as talkers
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedVoice")
+ static void RemoveAllRemoteTalkers();
+
+ // Returns whether a local player is currently talking
+ UFUNCTION(BlueprintPure, Category = "Online|AdvancedVoice|VoiceInfo")
+ static bool IsLocalPlayerTalking(uint8 LocalPlayerNum);
+
+ // Returns whether a remote player is currently talking
+ UFUNCTION(BlueprintPure, Category = "Online|AdvancedVoice|VoiceInfo")
+ static bool IsRemotePlayerTalking(const FBPUniqueNetId& UniqueNetId);
+
+ // Returns whether a player is muted for the specified local player
+ UFUNCTION(BlueprintPure, Category = "Online|AdvancedVoice|VoiceInfo")
+ static bool IsPlayerMuted(uint8 LocalUserNumChecking, const FBPUniqueNetId& UniqueNetId);
+
+ // Mutes the player associated with the uniquenetid for the specified local player, if IsSystemWide is true then it will attempt to mute globally for the player
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedVoice")
+ static bool MuteRemoteTalker(uint8 LocalUserNum, const FBPUniqueNetId& UniqueNetId, bool bIsSystemWide = false);
+
+ // UnMutes the player associated with the uniquenetid for the specified local player, if IsSystemWide is true then it will attempt to unmute globally for the player
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedVoice")
+ static bool UnMuteRemoteTalker(uint8 LocalUserNum, const FBPUniqueNetId& UniqueNetId, bool bIsSystemWide = false);
+
+ // Gets the number of local talkers for this system
+ UFUNCTION(BlueprintPure, Category = "Online|AdvancedVoice|VoiceInfo")
+ static void GetNumLocalTalkers(int32 & NumLocalTalkers);
+};
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/AutoLoginUserCallbackProxy.h b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/AutoLoginUserCallbackProxy.h
new file mode 100644
index 0000000..dc91cff
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/AutoLoginUserCallbackProxy.h
@@ -0,0 +1,55 @@
+// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
+#pragma once
+
+#include "CoreMinimal.h"
+#include "BlueprintDataDefinitions.h"
+#include "Interfaces/OnlineIdentityInterface.h"
+#include "Engine/LocalPlayer.h"
+#include "AutoLoginUserCallbackProxy.generated.h"
+
+UCLASS(MinimalAPI)
+class UAutoLoginUserCallbackProxy : public UOnlineBlueprintCallProxyBase
+{
+ GENERATED_UCLASS_BODY()
+
+ // Called when there is a successful destroy
+ UPROPERTY(BlueprintAssignable)
+ FEmptyOnlineDelegate OnSuccess;
+
+ // Called when there is an unsuccessful destroy
+ UPROPERTY(BlueprintAssignable)
+ FEmptyOnlineDelegate OnFailure;
+
+ /**
+ * Logs the player into the online service using parameters passed on the
+ * command line. Expects -AUTH_LOGIN= -AUTH_PASSWORD=. If either
+ * are missing, the function returns false and doesn't start the login
+ * process
+ *
+ * @param LocalUserNum the controller number of the associated user
+ *
+ */
+ UFUNCTION(BlueprintCallable, meta=(BlueprintInternalUseOnly = "true", WorldContext="WorldContextObject"), Category = "Online|AdvancedIdentity")
+ static UAutoLoginUserCallbackProxy* AutoLoginUser(UObject* WorldContextObject, int32 LocalUserNum);
+
+ // UOnlineBlueprintCallProxyBase interface
+ virtual void Activate() override;
+ // End of UOnlineBlueprintCallProxyBase interface
+
+private:
+ // Internal callback when the operation completes, calls out to the public success/failure callbacks
+ void OnCompleted(int32 LocalUserNum, bool bWasSuccessful, const FUniqueNetId& UserId, const FString& ErrorVal);
+
+private:
+ // The controller number of the associated user
+ int32 LocalUserNumber;
+
+ // The delegate executed by the online subsystem
+ FOnLoginCompleteDelegate Delegate;
+
+ // Handle to the registered OnDestroySessionComplete delegate
+ FDelegateHandle DelegateHandle;
+
+ // The world context object in which this call is taking place
+ UObject* WorldContextObject;
+};
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/BlueprintDataDefinitions.h b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/BlueprintDataDefinitions.h
new file mode 100644
index 0000000..b5dc438
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/BlueprintDataDefinitions.h
@@ -0,0 +1,435 @@
+#pragma once
+#include "CoreMinimal.h"
+//#include "EngineMinimal.h"
+#include "Engine/Engine.h"
+#include "GameFramework/PlayerState.h"
+//#include "Core.h"
+#include "Interfaces/OnlineSessionInterface.h"
+#include "OnlineSessionSettings.h"
+#include "OnlineDelegateMacros.h"
+#include "OnlineSubsystem.h"
+#include "OnlineSubsystemImpl.h"
+#include "OnlineSubsystemUtils.h"
+#include "OnlineSubsystemUtilsModule.h"
+#include "GameFramework/PlayerController.h"
+#include "Modules/ModuleManager.h"
+#include "OnlineSubsystemUtilsClasses.h"
+#include "BlueprintDataDefinitions.generated.h"
+
+UENUM(BlueprintType)
+enum class EBPUserPrivileges : uint8
+{
+ /** Whether the user can play at all, online or offline - may be age restricted */
+ CanPlay,
+ /** Whether the user can play in online modes */
+ CanPlayOnline,
+ /** Whether the user can use voice and text chat */
+ CanCommunicateOnline,
+ /** Whether the user can use content generated by other users */
+ CanUseUserGeneratedContent
+};
+
+
+UENUM(BlueprintType)
+enum class EBPLoginStatus : uint8
+{
+ /** Player has not logged in or chosen a local profile */
+ NotLoggedIn,
+ /** Player is using a local profile but is not logged in */
+ UsingLocalProfile,
+ /** Player has been validated by the platform specific authentication service */
+ LoggedIn
+};
+
+
+USTRUCT(BlueprintType)
+struct FBPUserOnlineAccount
+{
+ GENERATED_USTRUCT_BODY()
+
+public:
+ TSharedPtr UserAccountInfo;
+
+ FBPUserOnlineAccount()
+ {
+
+ }
+
+ FBPUserOnlineAccount(TSharedPtr UserAccount)
+ {
+ UserAccountInfo = UserAccount;
+ }
+};
+
+UENUM()
+enum class ESessionSettingSearchResult : uint8
+{
+ // Found the setting
+ Found,
+
+ // Did not find the setting
+ NotFound,
+
+ // Was not the correct type
+ WrongType
+};
+
+// This makes a lot of the blueprint functions cleaner
+UENUM()
+enum class EBlueprintResultSwitch : uint8
+{
+ // On Success
+ OnSuccess,
+
+ // On Failure
+ OnFailure
+};
+
+// This makes a lot of the blueprint functions cleaner
+UENUM()
+enum class EBlueprintAsyncResultSwitch : uint8
+{
+ // On Success
+ OnSuccess,
+
+ // Still loading
+ AsyncLoading,
+ // On Failure
+ OnFailure
+};
+
+// This is to define server type searches
+UENUM(BlueprintType)
+enum class EBPServerPresenceSearchType : uint8
+{
+ AllServers,
+ ClientServersOnly,
+ DedicatedServersOnly
+};
+
+// Wanted this to be switchable in the editor
+UENUM(BlueprintType)
+enum class EBPOnlinePresenceState : uint8
+{
+ Online,
+ Offline,
+ Away,
+ ExtendedAway,
+ DoNotDisturb,
+ Chat
+};
+
+UENUM(BlueprintType)
+enum class EBPOnlineSessionState : uint8
+{
+ /** An online session has not been created yet */
+ NoSession,
+ /** An online session is in the process of being created */
+ Creating,
+ /** Session has been created but the session hasn't started (pre match lobby) */
+ Pending,
+ /** Session has been asked to start (may take time due to communication with backend) */
+ Starting,
+ /** The current session has started. Sessions with join in progress disabled are no longer joinable */
+ InProgress,
+ /** The session is still valid, but the session is no longer being played (post match lobby) */
+ Ending,
+ /** The session is closed and any stats committed */
+ Ended,
+ /** The session is being destroyed */
+ Destroying
+};
+
+// Boy oh boy is this a dirty hack, but I can't figure out a good way to do it otherwise at the moment
+// The UniqueNetId is an abstract class so I can't exactly re-initialize it to make a shared pointer on some functions
+// So I made the blueprintable UniqueNetID into a dual variable struct with access functions and I am converting the const var for the pointer
+// I really need to re-think this later
+USTRUCT(BlueprintType)
+struct FBPUniqueNetId
+{
+ GENERATED_USTRUCT_BODY()
+
+private:
+ bool bUseDirectPointer;
+
+
+public:
+ TSharedPtr UniqueNetId;
+ const FUniqueNetId * UniqueNetIdPtr;
+
+ void SetUniqueNetId(const TSharedPtr &ID)
+ {
+ bUseDirectPointer = false;
+ UniqueNetIdPtr = nullptr;
+ UniqueNetId = ID;
+ }
+
+ void SetUniqueNetId(const FUniqueNetId *ID)
+ {
+ bUseDirectPointer = true;
+ UniqueNetIdPtr = ID;
+ }
+
+ bool IsValid() const
+ {
+ if (bUseDirectPointer && UniqueNetIdPtr != nullptr && UniqueNetIdPtr->IsValid())
+ {
+ return true;
+ }
+ else if (UniqueNetId.IsValid())
+ {
+ return true;
+ }
+ else
+ return false;
+
+ }
+
+ const FUniqueNetId* GetUniqueNetId() const
+ {
+ if (bUseDirectPointer && UniqueNetIdPtr != nullptr)
+ {
+ // No longer converting to non const as all functions now pass const UniqueNetIds
+ return /*const_cast*/(UniqueNetIdPtr);
+ }
+ else if (UniqueNetId.IsValid())
+ {
+ return UniqueNetId.Get();
+ }
+ else
+ return nullptr;
+ }
+
+ // Adding in a compare operator so that std functions will work with this struct
+ FORCEINLINE bool operator==(const FBPUniqueNetId& Other) const
+ {
+ return (IsValid() && Other.IsValid() && (*GetUniqueNetId() == *Other.GetUniqueNetId()));
+ }
+
+ FORCEINLINE bool operator!=(const FBPUniqueNetId& Other) const
+ {
+ return !(IsValid() && Other.IsValid() && (*GetUniqueNetId() == *Other.GetUniqueNetId()));
+ }
+
+ FBPUniqueNetId()
+ {
+ bUseDirectPointer = false;
+ UniqueNetIdPtr = nullptr;
+ }
+};
+
+USTRUCT(BluePrintType)
+struct FBPOnlineUser
+{
+ GENERATED_USTRUCT_BODY()
+
+public:
+ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Online|Friend")
+ FBPUniqueNetId UniqueNetId;
+ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Online|Friend")
+ FString DisplayName;
+ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Online|Friend")
+ FString RealName;
+};
+
+USTRUCT(BluePrintType)
+struct FBPOnlineRecentPlayer : public FBPOnlineUser
+{
+ GENERATED_USTRUCT_BODY()
+
+public:
+ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Online|Friend")
+ FString LastSeen;
+};
+
+
+USTRUCT(BlueprintType)
+struct FBPFriendPresenceInfo
+{
+ GENERATED_USTRUCT_BODY()
+
+public:
+
+ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Online|Friend")
+ bool bIsOnline = false;
+ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Online|Friend")
+ bool bIsPlaying = false;
+ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Online|Friend")
+ bool bIsPlayingThisGame = false;
+ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Online|Friend")
+ bool bIsJoinable = false;
+ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Online|Friend")
+ bool bHasVoiceSupport = false;
+ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Online|Friend")
+ EBPOnlinePresenceState PresenceState = EBPOnlinePresenceState::Offline;
+ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Online|Friend")
+ FString StatusString;
+
+ FBPFriendPresenceInfo()
+ {
+ bIsOnline = false;
+ bIsPlaying = false;
+ bIsPlayingThisGame = false;
+ bIsJoinable = false;
+ bHasVoiceSupport = false;
+ PresenceState = EBPOnlinePresenceState::Offline;
+ }
+};
+
+USTRUCT(BlueprintType)
+struct FBPFriendInfo
+{
+ GENERATED_USTRUCT_BODY()
+
+public:
+
+ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Online|Friend")
+ FString DisplayName;
+ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Online|Friend")
+ FString RealName;
+ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Online|Friend")
+ EBPOnlinePresenceState OnlineState = EBPOnlinePresenceState::Offline;
+ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Online|Friend")
+ FBPUniqueNetId UniqueNetId;
+ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Online|Friend")
+ bool bIsPlayingSameGame = false;
+ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Online|Friend")
+ FBPFriendPresenceInfo PresenceInfo;
+
+ FBPFriendInfo()
+ {
+ OnlineState = EBPOnlinePresenceState::Offline;
+ bIsPlayingSameGame = false;
+ }
+};
+
+
+/** The types of comparison operations for a given search query */
+// Used to compare session properties
+UENUM(BlueprintType)
+enum class EOnlineComparisonOpRedux : uint8
+{
+ Equals,
+ NotEquals,
+ GreaterThan,
+ GreaterThanEquals,
+ LessThan,
+ LessThanEquals,
+};
+
+
+// Used to store session properties before converting to FVariantData
+USTRUCT(BlueprintType)
+struct FSessionPropertyKeyPair
+{
+ GENERATED_USTRUCT_BODY()
+
+ FName Key;
+ FVariantData Data;
+};
+
+
+// Sent to the FindSessionsAdvanced to filter the end results
+USTRUCT(BlueprintType)
+struct FSessionsSearchSetting
+{
+ GENERATED_USTRUCT_BODY()
+ //UPROPERTY()
+
+
+ // Had to make a copy of this to account for the original not being exposed to blueprints
+ /** How is this session setting compared on the backend searches */
+ EOnlineComparisonOpRedux ComparisonOp;
+
+ // The key pair to search for
+ FSessionPropertyKeyPair PropertyKeyPair;
+};
+
+// Couldn't use the default one as it is not exposed to other modules, had to re-create it here
+// Helper class for various methods to reduce the call hierarchy
+struct FOnlineSubsystemBPCallHelperAdvanced
+{
+public:
+ FOnlineSubsystemBPCallHelperAdvanced(const TCHAR* CallFunctionContext, UWorld* World, FName SystemName = NAME_None)
+ : OnlineSub(Online::GetSubsystem(World, SystemName))
+ , FunctionContext(CallFunctionContext)
+ {
+ if (OnlineSub == nullptr)
+ {
+ FFrame::KismetExecutionMessage(*FString::Printf(TEXT("%s - Invalid or uninitialized OnlineSubsystem"), FunctionContext), ELogVerbosity::Warning);
+ }
+ }
+
+ void QueryIDFromPlayerController(APlayerController* PlayerController)
+ {
+ UserID.Reset();
+ //return const_cast(UniqueNetIdPtr);
+ if (APlayerState* PlayerState = (PlayerController != NULL) ? PlayerController->PlayerState : NULL)
+ {
+ UserID = PlayerState->GetUniqueId().GetUniqueNetId();
+ if (!UserID.IsValid())
+ {
+ FFrame::KismetExecutionMessage(*FString::Printf(TEXT("%s - Cannot map local player to unique net ID"), FunctionContext), ELogVerbosity::Warning);
+ }
+ }
+ else
+ {
+ FFrame::KismetExecutionMessage(*FString::Printf(TEXT("%s - Invalid player state"), FunctionContext), ELogVerbosity::Warning);
+ }
+ }
+
+
+ bool IsValid() const
+ {
+ return UserID.IsValid() && (OnlineSub != nullptr);
+ }
+
+public:
+ //TSharedPtr& GetUniqueNetId()
+ TSharedPtr*class*/ const FUniqueNetId> UserID;
+ IOnlineSubsystem* const OnlineSub;
+ const TCHAR* FunctionContext;
+};
+class FOnlineSearchSettingsEx : public FOnlineSearchSettings
+{
+ /**
+ * Sets a key value pair combination that defines a search parameter
+ *
+ * @param Key key for the setting
+ * @param Value value of the setting
+ * @param InType type of comparison
+ */
+public:
+
+ void HardSet(FName Key, const FVariantData& Value, EOnlineComparisonOpRedux CompOp)
+ {
+ FOnlineSessionSearchParam* SearchParam = SearchParams.Find(Key);
+
+ TEnumAsByte op;
+
+ switch (CompOp)
+ {
+ case EOnlineComparisonOpRedux::Equals: op = EOnlineComparisonOp::Equals; break;
+ case EOnlineComparisonOpRedux::GreaterThan: op = EOnlineComparisonOp::GreaterThan; break;
+ case EOnlineComparisonOpRedux::GreaterThanEquals: op = EOnlineComparisonOp::GreaterThanEquals; break;
+ case EOnlineComparisonOpRedux::LessThan: op = EOnlineComparisonOp::LessThan; break;
+ case EOnlineComparisonOpRedux::LessThanEquals: op = EOnlineComparisonOp::LessThanEquals; break;
+ case EOnlineComparisonOpRedux::NotEquals: op = EOnlineComparisonOp::NotEquals; break;
+ default: op = EOnlineComparisonOp::Equals; break;
+ }
+
+ if (SearchParam)
+ {
+ SearchParam->Data = Value;
+ SearchParam->ComparisonOp = op;
+ }
+ else
+ {
+ FOnlineSessionSearchParam searchSetting((int)0, op);
+ searchSetting.Data = Value;
+ SearchParams.Add(Key, searchSetting);
+ }
+ }
+};
+
+#define INVALID_INDEX -1
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/CancelFindSessionsCallbackProxy.h b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/CancelFindSessionsCallbackProxy.h
new file mode 100644
index 0000000..59ebff3
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/CancelFindSessionsCallbackProxy.h
@@ -0,0 +1,46 @@
+// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
+#pragma once
+#include "CoreMinimal.h"
+#include "Engine/Engine.h"
+#include "Interfaces/OnlineSessionInterface.h"
+#include "BlueprintDataDefinitions.h"
+#include "CancelFindSessionsCallbackProxy.generated.h"
+
+UCLASS(MinimalAPI)
+class UCancelFindSessionsCallbackProxy : public UOnlineBlueprintCallProxyBase
+{
+ GENERATED_UCLASS_BODY()
+
+ // Called when there is a successful destroy
+ UPROPERTY(BlueprintAssignable)
+ FEmptyOnlineDelegate OnSuccess;
+
+ // Called when there is an unsuccessful destroy
+ UPROPERTY(BlueprintAssignable)
+ FEmptyOnlineDelegate OnFailure;
+
+ // Cancels finding sessions
+ UFUNCTION(BlueprintCallable, meta=(BlueprintInternalUseOnly = "true", WorldContext="WorldContextObject"), Category = "Online|AdvancedSessions")
+ static UCancelFindSessionsCallbackProxy* CancelFindSessions(UObject* WorldContextObject, class APlayerController* PlayerController);
+
+ // UOnlineBlueprintCallProxyBase interface
+ virtual void Activate() override;
+ // End of UOnlineBlueprintCallProxyBase interface
+
+private:
+ // Internal callback when the operation completes, calls out to the public success/failure callbacks
+ void OnCompleted(bool bWasSuccessful);
+
+private:
+ // The player controller triggering things
+ TWeakObjectPtr PlayerControllerWeakPtr;
+
+ // The delegate executed by the online subsystem
+ FOnCancelFindSessionsCompleteDelegate Delegate;
+
+ // Handle to the registered OnDestroySessionComplete delegate
+ FDelegateHandle DelegateHandle;
+
+ // The world context object in which this call is taking place
+ UObject* WorldContextObject;
+};
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/CreateSessionCallbackProxyAdvanced.h b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/CreateSessionCallbackProxyAdvanced.h
new file mode 100644
index 0000000..013ff0b
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/CreateSessionCallbackProxyAdvanced.h
@@ -0,0 +1,107 @@
+// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
+#pragma once
+
+#include "CoreMinimal.h"
+#include "Engine/Engine.h"
+#include "BlueprintDataDefinitions.h"
+#include "CreateSessionCallbackProxyAdvanced.generated.h"
+
+UCLASS(MinimalAPI)
+class UCreateSessionCallbackProxyAdvanced : public UOnlineBlueprintCallProxyBase
+{
+ GENERATED_UCLASS_BODY()
+
+ // Called when the session was created successfully
+ UPROPERTY(BlueprintAssignable)
+ FEmptyOnlineDelegate OnSuccess;
+
+ // Called when there was an error creating the session
+ UPROPERTY(BlueprintAssignable)
+ FEmptyOnlineDelegate OnFailure;
+
+ /**
+ * Creates a session with the default online subsystem with advanced optional inputs, for dedicated servers leave UsePresence as false and set IsDedicatedServer to true. Dedicated servers don't use presence.
+ * @param PublicConnections When doing a 'listen' server, this must be >=2 (ListenServer itself counts as a connection)
+ * @param bUseLAN When you want to play LAN, the level to play on must be loaded with option 'bIsLanMatch'
+ * @param bUsePresence Must be true for a 'listen' server (Map must be loaded with option 'listen'), false for a 'dedicated' server.
+ * @param bUseLobbiesIfAvailable Used to flag the subsystem to use a lobby api instead of general hosting if the API supports it, generally true on steam for listen servers and false for dedicated
+ * @param bShouldAdvertise Set to true when the OnlineSubsystem should list your server when someone is searching for servers. Otherwise the server is hidden and only join via invite is possible.
+ * @param bUseLobbiesVoiceChatIfAvailable Set to true to setup voice chat lobbies if the API supports it
+ * @param bStartAfterCreate Set to true to start the session after it's created. If false you need to manually call StartSession when ready.
+ */
+ UFUNCTION(BlueprintCallable, meta=(BlueprintInternalUseOnly = "true", WorldContext="WorldContextObject",AutoCreateRefTerm="ExtraSettings"), Category = "Online|AdvancedSessions")
+ static UCreateSessionCallbackProxyAdvanced* CreateAdvancedSession(UObject* WorldContextObject, const TArray& ExtraSettings, class APlayerController* PlayerController = NULL, int32 PublicConnections = 100, int32 PrivateConnections = 0, bool bUseLAN = false, bool bAllowInvites = true, bool bIsDedicatedServer = false, bool bUsePresence = true, bool bUseLobbiesIfAvailable = true, bool bAllowJoinViaPresence = true, bool bAllowJoinViaPresenceFriendsOnly = false, bool bAntiCheatProtected = false, bool bUsesStats = false, bool bShouldAdvertise = true, bool bUseLobbiesVoiceChatIfAvailable = false, bool bStartAfterCreate = true);
+
+ // UOnlineBlueprintCallProxyBase interface
+ virtual void Activate() override;
+ // End of UOnlineBlueprintCallProxyBase interface
+
+private:
+ // Internal callback when session creation completes, optionally calls StartSession
+ void OnCreateCompleted(FName SessionName, bool bWasSuccessful);
+
+ // Internal callback when session start completes
+ void OnStartCompleted(FName SessionName, bool bWasSuccessful);
+
+ // The player controller triggering things
+ TWeakObjectPtr PlayerControllerWeakPtr;
+
+ // The delegate executed by the online subsystem
+ FOnCreateSessionCompleteDelegate CreateCompleteDelegate;
+
+ // The delegate executed by the online subsystem
+ FOnStartSessionCompleteDelegate StartCompleteDelegate;
+
+ // Handles to the registered delegates above
+ FDelegateHandle CreateCompleteDelegateHandle;
+ FDelegateHandle StartCompleteDelegateHandle;
+
+ // Number of public connections
+ int NumPublicConnections;
+
+ // Number of private connections
+ int NumPrivateConnections;
+
+ // Whether or not to search LAN
+ bool bUseLAN;
+
+ // Whether or not to allow invites
+ bool bAllowInvites;
+
+ // Whether this is a dedicated server or not
+ bool bDedicatedServer;
+
+ // Whether to use the presence option
+ bool bUsePresence;
+
+ // Whether to prefer the use of lobbies for hosting if the api supports them
+ bool bUseLobbiesIfAvailable;
+
+ // Whether to allow joining via presence
+ bool bAllowJoinViaPresence;
+
+ // Allow joining via presence for friends only
+ bool bAllowJoinViaPresenceFriendsOnly;
+
+ // Delcare the server to be anti cheat protected
+ bool bAntiCheatProtected;
+
+ // Record Stats
+ bool bUsesStats;
+
+ // Should advertise server?
+ bool bShouldAdvertise;
+
+ // Whether to prefer the use of voice chat lobbies if the api supports them
+ bool bUseLobbiesVoiceChatIfAvailable;
+
+ // Whether to start the session automatically after it is created
+ bool bStartAfterCreate;
+
+ // Store extra settings
+ TArray ExtraSettings;
+
+ // The world context object in which this call is taking place
+ UObject* WorldContextObject;
+};
+
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/EndSessionCallbackProxy.h b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/EndSessionCallbackProxy.h
new file mode 100644
index 0000000..756c645
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/EndSessionCallbackProxy.h
@@ -0,0 +1,49 @@
+// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
+#pragma once
+#include "CoreMinimal.h"
+#include "Engine/Engine.h"
+#include "Interfaces/OnlineSessionInterface.h"
+#include "BlueprintDataDefinitions.h"
+#include "EndSessionCallbackProxy.generated.h"
+
+UCLASS(MinimalAPI)
+class UEndSessionCallbackProxy : public UOnlineBlueprintCallProxyBase
+{
+ GENERATED_UCLASS_BODY()
+
+ // Called when there is a successful destroy
+ UPROPERTY(BlueprintAssignable)
+ FEmptyOnlineDelegate OnSuccess;
+
+ // Called when there is an unsuccessful destroy
+ UPROPERTY(BlueprintAssignable)
+ FEmptyOnlineDelegate OnFailure;
+
+ /**
+ * Ends the current sessions, Generally for almost all uses you should be using the engines native Destroy Session node instead.
+ * This exists for people using StartSession and optionally hand managing the session state.
+ */
+ UFUNCTION(BlueprintCallable, meta=(BlueprintInternalUseOnly = "true", WorldContext="WorldContextObject"), Category = "Online|AdvancedSessions")
+ static UEndSessionCallbackProxy* EndSession(UObject* WorldContextObject, class APlayerController* PlayerController);
+
+ // UOnlineBlueprintCallProxyBase interface
+ virtual void Activate() override;
+ // End of UOnlineBlueprintCallProxyBase interface
+
+private:
+ // Internal callback when the operation completes, calls out to the public success/failure callbacks
+ void OnCompleted(FName SessionName, bool bWasSuccessful);
+
+private:
+ // The player controller triggering things
+ TWeakObjectPtr PlayerControllerWeakPtr;
+
+ // The delegate executed by the online subsystem
+ FOnEndSessionCompleteDelegate Delegate;
+
+ // Handle to the registered OnDestroySessionComplete delegate
+ FDelegateHandle DelegateHandle;
+
+ // The world context object in which this call is taking place
+ UObject* WorldContextObject;
+};
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/FindFriendSessionCallbackProxy.h b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/FindFriendSessionCallbackProxy.h
new file mode 100644
index 0000000..6876995
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/FindFriendSessionCallbackProxy.h
@@ -0,0 +1,51 @@
+// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
+#pragma once
+
+#include "CoreMinimal.h"
+#include "BlueprintDataDefinitions.h"
+#include "Engine/LocalPlayer.h"
+#include "FindFriendSessionCallbackProxy.generated.h"
+
+DECLARE_LOG_CATEGORY_EXTERN(AdvancedFindFriendSessionLog, Log, All);
+
+DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FBlueprintFindFriendSessionDelegate, const TArray &, SessionInfo);
+
+UCLASS(MinimalAPI)
+class UFindFriendSessionCallbackProxy : public UOnlineBlueprintCallProxyBase
+{
+ GENERATED_UCLASS_BODY()
+
+ // Called when the friends list successfully was retrieved
+ UPROPERTY(BlueprintAssignable)
+ FBlueprintFindFriendSessionDelegate OnSuccess;
+
+ // Called when there was an error retrieving the friends list
+ UPROPERTY(BlueprintAssignable)
+ FBlueprintFindFriendSessionDelegate OnFailure;
+
+ // Attempts to get the current session that a friend is in
+ UFUNCTION(BlueprintCallable, meta=(BlueprintInternalUseOnly = "true", WorldContext="WorldContextObject"), Category = "Online|AdvancedFriends")
+ static UFindFriendSessionCallbackProxy* FindFriendSession(UObject* WorldContextObject, APlayerController *PlayerController, const FBPUniqueNetId &FriendUniqueNetId);
+
+ virtual void Activate() override;
+
+private:
+ // Internal callback when the friends list is retrieved
+ void OnFindFriendSessionCompleted(int32 LocalPlayer, bool bWasSuccessful, const TArray& SessionInfo);
+
+ // The player controller triggering things
+ TWeakObjectPtr PlayerControllerWeakPtr;
+
+ // The UniqueNetID of the person to invite
+ FBPUniqueNetId cUniqueNetId;
+
+ // The delegate to call on completion
+ FOnFindFriendSessionCompleteDelegate OnFindFriendSessionCompleteDelegate;
+
+ // Handles to the registered delegates above
+ FDelegateHandle FindFriendSessionCompleteDelegateHandle;
+
+ // The world context object in which this call is taking place
+ UObject* WorldContextObject;
+};
+
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/FindSessionsCallbackProxyAdvanced.h b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/FindSessionsCallbackProxyAdvanced.h
new file mode 100644
index 0000000..a998ba3
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/FindSessionsCallbackProxyAdvanced.h
@@ -0,0 +1,109 @@
+// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
+#pragma once
+#include "CoreMinimal.h"
+#include "Engine/Engine.h"
+#include "Interfaces/OnlineSessionInterface.h"
+#include "FindSessionsCallbackProxy.h"
+#include "BlueprintDataDefinitions.h"
+#include "FindSessionsCallbackProxyAdvanced.generated.h"
+
+
+FORCEINLINE bool operator==(const FBlueprintSessionResult& A, const FBlueprintSessionResult& B)
+{
+ return (A.OnlineResult.IsValid() == B.OnlineResult.IsValid() && (A.OnlineResult.GetSessionIdStr() == B.OnlineResult.GetSessionIdStr()));
+}
+
+UCLASS(MinimalAPI)
+class UFindSessionsCallbackProxyAdvanced : public UOnlineBlueprintCallProxyBase
+{
+ GENERATED_UCLASS_BODY()
+
+ // Called when there is a successful query
+ UPROPERTY(BlueprintAssignable)
+ FBlueprintFindSessionsResultDelegate OnSuccess;
+
+ // Called when there is an unsuccessful query
+ UPROPERTY(BlueprintAssignable)
+ FBlueprintFindSessionsResultDelegate OnFailure;
+
+ // Searches for advertised sessions with the default online subsystem and includes an array of filters
+ UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true", WorldContext = "WorldContextObject", AutoCreateRefTerm="Filters"), Category = "Online|AdvancedSessions")
+ static UFindSessionsCallbackProxyAdvanced* FindSessionsAdvanced(UObject* WorldContextObject, class APlayerController* PlayerController, int32 MaxResults, bool bUseLAN, EBPServerPresenceSearchType ServerTypeToSearch, const TArray &Filters, bool bEmptyServersOnly = false, bool bNonEmptyServersOnly = false, bool bSecureServersOnly = false, bool bSearchLobbies = true, int MinSlotsAvailable = 0);
+
+ static bool CompareVariants(const FVariantData &A, const FVariantData &B, EOnlineComparisonOpRedux Comparator);
+
+ // Filters an array of session results by the given search parameters, returns a new array with the filtered results
+ UFUNCTION(BluePrintCallable, meta = (Category = "Online|AdvancedSessions"))
+ static void FilterSessionResults(const TArray &SessionResults, const TArray &Filters, TArray &FilteredResults);
+
+ // Removed, the default built in versions work fine in the normal FindSessionsCallbackProxy
+ /*UFUNCTION(BlueprintPure, Category = "Online|Session")
+ static int32 GetPingInMs(const FBlueprintSessionResult& Result);
+
+ UFUNCTION(BlueprintPure, Category = "Online|Session")
+ static FString GetServerName(const FBlueprintSessionResult& Result);
+
+ UFUNCTION(BlueprintPure, Category = "Online|Session")
+ static int32 GetCurrentPlayers(const FBlueprintSessionResult& Result);
+
+ UFUNCTION(BlueprintPure, Category = "Online|Session")
+ static int32 GetMaxPlayers(const FBlueprintSessionResult& Result);*/
+
+
+ // UOnlineBlueprintCallProxyBase interface
+ virtual void Activate() override;
+ // End of UOnlineBlueprintCallProxyBase interface
+
+private:
+ // Internal callback when the session search completes, calls out to the public success/failure callbacks
+ void OnCompleted(bool bSuccess);
+
+ bool bRunSecondSearch;
+ bool bIsOnSecondSearch;
+
+ TArray SessionSearchResults;
+
+private:
+ // The player controller triggering things
+ TWeakObjectPtr PlayerControllerWeakPtr;
+
+ // The delegate executed by the online subsystem
+ FOnFindSessionsCompleteDelegate Delegate;
+
+ // Handle to the registered OnFindSessionsComplete delegate
+ FDelegateHandle DelegateHandle;
+
+ // Object to track search results
+ TSharedPtr SearchObject;
+ TSharedPtr SearchObjectDedicated;
+
+ // Whether or not to search LAN
+ bool bUseLAN;
+
+ // Whether or not to search for dedicated servers
+ EBPServerPresenceSearchType ServerSearchType;
+
+ // Maximum number of results to return
+ int MaxResults;
+
+ // Store extra settings
+ TArray SearchSettings;
+
+ // Search for empty servers only
+ bool bEmptyServersOnly;
+
+ // Search for non empty servers only
+ bool bNonEmptyServersOnly;
+
+ // Search for secure servers only
+ bool bSecureServersOnly;
+
+ // Search through lobbies
+ bool bSearchLobbies;
+
+ // Min slots requires to search
+ int MinSlotsAvailable;
+
+ // The world context object in which this call is taking place
+ UObject* WorldContextObject;
+};
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/GetFriendsCallbackProxy.h b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/GetFriendsCallbackProxy.h
new file mode 100644
index 0000000..57be8f7
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/GetFriendsCallbackProxy.h
@@ -0,0 +1,49 @@
+// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
+#pragma once
+
+#include "CoreMinimal.h"
+#include "BlueprintDataDefinitions.h"
+#include "Engine/LocalPlayer.h"
+#include "GetFriendsCallbackProxy.generated.h"
+
+DECLARE_LOG_CATEGORY_EXTERN(AdvancedGetFriendsLog, Log, All);
+
+DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FBlueprintGetFriendsListDelegate, const TArray&, Results);
+
+UCLASS(MinimalAPI)
+class UGetFriendsCallbackProxy : public UOnlineBlueprintCallProxyBase
+{
+ GENERATED_UCLASS_BODY()
+
+ // Called when the friends list successfully was retrieved
+ UPROPERTY(BlueprintAssignable)
+ FBlueprintGetFriendsListDelegate OnSuccess;
+
+ // Called when there was an error retrieving the friends list
+ UPROPERTY(BlueprintAssignable)
+ FBlueprintGetFriendsListDelegate OnFailure;
+
+ // Gets the players list of friends from the OnlineSubsystem and returns it, can be retrieved later with GetStoredFriendsList
+ UFUNCTION(BlueprintCallable, meta=(BlueprintInternalUseOnly = "true", WorldContext="WorldContextObject"), Category = "Online|AdvancedFriends")
+ static UGetFriendsCallbackProxy* GetAndStoreFriendsList(UObject* WorldContextObject, class APlayerController* PlayerController);
+
+ virtual void Activate() override;
+
+private:
+ // Internal callback when the friends list is retrieved
+ void OnReadFriendsListCompleted(int32 LocalUserNum, bool bWasSuccessful, const FString& ListName, const FString& ErrorString);
+
+ // The player controller triggering things
+ TWeakObjectPtr PlayerControllerWeakPtr;
+
+ // The delegate executed
+ FOnReadFriendsListComplete FriendListReadCompleteDelegate;
+
+ // The Type of friends list to get
+ // Removed because all but the facebook interfaces don't even currently support anything but the default friends list.
+ //EBPFriendsLists FriendListToGet;
+
+ // The world context object in which this call is taking place
+ UObject* WorldContextObject;
+};
+
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/GetRecentPlayersCallbackProxy.h b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/GetRecentPlayersCallbackProxy.h
new file mode 100644
index 0000000..d0d7af4
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/GetRecentPlayersCallbackProxy.h
@@ -0,0 +1,49 @@
+// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
+#pragma once
+
+#include "CoreMinimal.h"
+#include "BlueprintDataDefinitions.h"
+#include "GetRecentPlayersCallbackProxy.generated.h"
+
+DECLARE_LOG_CATEGORY_EXTERN(AdvancedGetRecentPlayersLog, Log, All);
+
+DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FBlueprintGetRecentPlayersDelegate, const TArray&, Results);
+
+UCLASS(MinimalAPI)
+class UGetRecentPlayersCallbackProxy : public UOnlineBlueprintCallProxyBase
+{
+ GENERATED_UCLASS_BODY()
+
+ // Called when the friends list successfully was retrieved
+ UPROPERTY(BlueprintAssignable)
+ FBlueprintGetRecentPlayersDelegate OnSuccess;
+
+ // Called when there was an error retrieving the friends list
+ UPROPERTY(BlueprintAssignable)
+ FBlueprintGetRecentPlayersDelegate OnFailure;
+
+ // Gets the list of recent players from the OnlineSubsystem and returns it, can be retrieved later with GetStoredRecentPlayersList, can fail if no recent players are found
+ UFUNCTION(BlueprintCallable, meta=(BlueprintInternalUseOnly = "true", WorldContext="WorldContextObject"), Category = "Online|AdvancedFriends")
+ static UGetRecentPlayersCallbackProxy* GetAndStoreRecentPlayersList(UObject* WorldContextObject, const FBPUniqueNetId &UniqueNetId);
+
+ virtual void Activate() override;
+
+private:
+ // Internal callback when the friends list is retrieved
+ void OnQueryRecentPlayersCompleted(const FUniqueNetId &UserID, const FString &Namespace, bool bWasSuccessful, const FString& ErrorString);
+ // Handle to the registered OnFindSessionsComplete delegate
+ FDelegateHandle DelegateHandle;
+
+ // The player controller triggering things
+ //TWeakObjectPtr PlayerControllerWeakPtr;
+
+ // The UniqueNetID of the person to get recent players for
+ FBPUniqueNetId cUniqueNetId;
+
+ // The delegate executed
+ FOnQueryRecentPlayersCompleteDelegate QueryRecentPlayersCompleteDelegate;
+
+ // The world context object in which this call is taking place
+ UObject* WorldContextObject;
+};
+
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/GetUserPrivilegeCallbackProxy.h b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/GetUserPrivilegeCallbackProxy.h
new file mode 100644
index 0000000..448ab59
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/GetUserPrivilegeCallbackProxy.h
@@ -0,0 +1,45 @@
+// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
+#pragma once
+
+#include "CoreMinimal.h"
+#include "BlueprintDataDefinitions.h"
+#include "Interfaces/OnlineIdentityInterface.h"
+#include "GetUserPrivilegeCallbackProxy.generated.h"
+
+DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FBlueprintGetUserPrivilegeDelegate,/* const &FBPUniqueNetId, PlayerID,*/ EBPUserPrivileges, QueriedPrivilege, bool, HadPrivilege);
+
+UCLASS(MinimalAPI)
+class UGetUserPrivilegeCallbackProxy : public UOnlineBlueprintCallProxyBase
+{
+ GENERATED_UCLASS_BODY()
+
+ // Called when there is a successful destroy
+ UPROPERTY(BlueprintAssignable)
+ FBlueprintGetUserPrivilegeDelegate OnSuccess;
+
+ // Called when there is an unsuccessful destroy
+ UPROPERTY(BlueprintAssignable)
+ FEmptyOnlineDelegate OnFailure;
+
+ // Gets the privilage of the user
+ UFUNCTION(BlueprintCallable, meta=(BlueprintInternalUseOnly = "true", WorldContext="WorldContextObject"), Category = "Online|AdvancedIdentity")
+ static UGetUserPrivilegeCallbackProxy* GetUserPrivilege(UObject* WorldContextObject, const EBPUserPrivileges & PrivilegeToCheck, const FBPUniqueNetId & PlayerUniqueNetID);
+
+ // UOnlineBlueprintCallProxyBase interface
+ virtual void Activate() override;
+ // End of UOnlineBlueprintCallProxyBase interface
+
+private:
+ // Internal callback when the operation completes, calls out to the public success/failure callbacks
+ void OnCompleted(const FUniqueNetId& PlayerID, EUserPrivileges::Type Privilege, uint32 Result);
+
+private:
+ // The player controller triggering things
+ FBPUniqueNetId PlayerUniqueNetID;
+
+ // Privilege to check
+ EBPUserPrivileges UserPrivilege;
+
+ // The world context object in which this call is taking place
+ UObject* WorldContextObject;
+};
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/LoginUserCallbackProxy.h b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/LoginUserCallbackProxy.h
new file mode 100644
index 0000000..08bfd4c
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/LoginUserCallbackProxy.h
@@ -0,0 +1,55 @@
+// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
+#pragma once
+
+#include "CoreMinimal.h"
+#include "BlueprintDataDefinitions.h"
+#include "Interfaces/OnlineIdentityInterface.h"
+#include "Engine/LocalPlayer.h"
+#include "LoginUserCallbackProxy.generated.h"
+
+UCLASS(MinimalAPI)
+class ULoginUserCallbackProxy : public UOnlineBlueprintCallProxyBase
+{
+ GENERATED_UCLASS_BODY()
+
+ // Called when there is a successful destroy
+ UPROPERTY(BlueprintAssignable)
+ FEmptyOnlineDelegate OnSuccess;
+
+ // Called when there is an unsuccessful destroy
+ UPROPERTY(BlueprintAssignable)
+ FEmptyOnlineDelegate OnFailure;
+
+ // Logs into the identity interface
+ UFUNCTION(BlueprintCallable, meta=(BlueprintInternalUseOnly = "true", WorldContext="WorldContextObject", AdvancedDisplay = "AuthType"), Category = "Online|AdvancedIdentity")
+ static ULoginUserCallbackProxy* LoginUser(UObject* WorldContextObject, class APlayerController* PlayerController, FString UserID, FString UserToken, FString AuthType);
+
+ // UOnlineBlueprintCallProxyBase interface
+ virtual void Activate() override;
+ // End of UOnlineBlueprintCallProxyBase interface
+
+private:
+ // Internal callback when the operation completes, calls out to the public success/failure callbacks
+ void OnCompleted(int32 LocalUserNum, bool bWasSuccessful, const FUniqueNetId& UserId, const FString& ErrorVal);
+
+private:
+ // The player controller triggering things
+ TWeakObjectPtr PlayerControllerWeakPtr;
+
+ // The user ID
+ FString UserID;
+
+ // The user pass / token
+ FString UserToken;
+
+ FString AuthType;
+
+ // The delegate executed by the online subsystem
+ FOnLoginCompleteDelegate Delegate;
+
+ // Handle to the registered OnDestroySessionComplete delegate
+ FDelegateHandle DelegateHandle;
+
+ // The world context object in which this call is taking place
+ UObject* WorldContextObject;
+};
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/LogoutUserCallbackProxy.h b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/LogoutUserCallbackProxy.h
new file mode 100644
index 0000000..9907095
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/LogoutUserCallbackProxy.h
@@ -0,0 +1,47 @@
+// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
+#pragma once
+
+#include "CoreMinimal.h"
+#include "BlueprintDataDefinitions.h"
+#include "Interfaces/OnlineIdentityInterface.h"
+#include "Engine/LocalPlayer.h"
+#include "LogoutUserCallbackProxy.generated.h"
+
+UCLASS(MinimalAPI)
+class ULogoutUserCallbackProxy : public UOnlineBlueprintCallProxyBase
+{
+ GENERATED_UCLASS_BODY()
+
+ // Called when there is a successful destroy
+ UPROPERTY(BlueprintAssignable)
+ FEmptyOnlineDelegate OnSuccess;
+
+ // Called when there is an unsuccessful destroy
+ UPROPERTY(BlueprintAssignable)
+ FEmptyOnlineDelegate OnFailure;
+
+ // Logs out of the identity interface
+ UFUNCTION(BlueprintCallable, meta=(BlueprintInternalUseOnly = "true", WorldContext="WorldContextObject"), Category = "Online|AdvancedIdentity")
+ static ULogoutUserCallbackProxy* LogoutUser(UObject* WorldContextObject, class APlayerController* PlayerController);
+
+ // UOnlineBlueprintCallProxyBase interface
+ virtual void Activate() override;
+ // End of UOnlineBlueprintCallProxyBase interface
+
+private:
+ // Internal callback when the operation completes, calls out to the public success/failure callbacks
+ void OnCompleted(int LocalUserNum, bool bWasSuccessful);
+
+private:
+ // The player controller triggering things
+ TWeakObjectPtr PlayerControllerWeakPtr;
+
+ // The delegate executed by the online subsystem
+ FOnLogoutCompleteDelegate Delegate;
+
+ // Handle to the registered OnDestroySessionComplete delegate
+ FDelegateHandle DelegateHandle;
+
+ // The world context object in which this call is taking place
+ UObject* WorldContextObject;
+};
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/OnlineSubSystemHeader.h b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/OnlineSubSystemHeader.h
new file mode 100644
index 0000000..ad4b18f
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/OnlineSubSystemHeader.h
@@ -0,0 +1,27 @@
+#pragma once
+
+//#include "EngineMinimal.h"
+//#include "Core.h"
+//#include "OnlineSessionInterface.h"
+//#include "OnlineSessionSettings.h"
+//#include "OnlineDelegateMacros.h"
+//#include "OnlineSubsystem.h"
+//#include "OnlineSubsystemImpl.h"
+//#include "OnlineSubsystemUtils.h"
+//#include "OnlineSubsystemUtilsModule.h"
+//#include "ModuleManager.h"
+//#include "OnlineSubsystemUtilsClasses.h"
+//#include "BlueprintDataDefinitions.h"
+
+
+/*#include "VoiceEngineImpl.h"
+#include "VoiceInterfaceImpl.h"
+#include "Voice.h""
+*/
+
+// Found this in the steam controller, seems like a nice thought since steam is throwing errors
+// Disable crazy warnings that claim that standard C library is "deprecated".
+//#ifdef _MSC_VER
+//#pragma warning(push)
+//#pragma warning(disable:4996)
+//#endif
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/SendFriendInviteCallbackProxy.h b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/SendFriendInviteCallbackProxy.h
new file mode 100644
index 0000000..ca5c352
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/SendFriendInviteCallbackProxy.h
@@ -0,0 +1,49 @@
+// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
+#pragma once
+
+#include "CoreMinimal.h"
+#include "BlueprintDataDefinitions.h"
+#include "Engine/LocalPlayer.h"
+#include "SendFriendInviteCallbackProxy.generated.h"
+
+DECLARE_LOG_CATEGORY_EXTERN(AdvancedSendFriendInviteLog, Log, All);
+
+DECLARE_DYNAMIC_MULTICAST_DELEGATE(FBlueprintSendFriendInviteDelegate);
+
+UCLASS(MinimalAPI)
+class USendFriendInviteCallbackProxy : public UOnlineBlueprintCallProxyBase
+{
+ GENERATED_UCLASS_BODY()
+
+ // Called when the friends list successfully was retrieved
+ UPROPERTY(BlueprintAssignable)
+ FBlueprintSendFriendInviteDelegate OnSuccess;
+
+ // Called when there was an error retrieving the friends list
+ UPROPERTY(BlueprintAssignable)
+ FBlueprintSendFriendInviteDelegate OnFailure;
+
+ // Adds a friend who is using the defined UniqueNetId, some interfaces do now allow this function to be called (INCLUDING STEAM)
+ UFUNCTION(BlueprintCallable, meta=(BlueprintInternalUseOnly = "true", WorldContext="WorldContextObject"), Category = "Online|AdvancedFriends")
+ static USendFriendInviteCallbackProxy* SendFriendInvite(UObject* WorldContextObject, APlayerController *PlayerController, const FBPUniqueNetId &UniqueNetIDInvited);
+
+ virtual void Activate() override;
+
+private:
+ // Internal callback when the friends list is retrieved
+ void OnSendInviteComplete(int32 LocalPlayerNum, bool bWasSuccessful, const FUniqueNetId &InvitedPlayer, const FString &ListName, const FString &ErrorString);
+
+
+ // The player controller triggering things
+ TWeakObjectPtr PlayerControllerWeakPtr;
+
+ // The UniqueNetID of the person to invite
+ FBPUniqueNetId cUniqueNetId;
+
+ // The delegate to call on completion
+ FOnSendInviteComplete OnSendInviteCompleteDelegate;
+
+ // The world context object in which this call is taking place
+ UObject* WorldContextObject;
+};
+
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/StartSessionCallbackProxyAdvanced.h b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/StartSessionCallbackProxyAdvanced.h
new file mode 100644
index 0000000..246fc50
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/StartSessionCallbackProxyAdvanced.h
@@ -0,0 +1,46 @@
+#pragma once
+
+#include "CoreMinimal.h"
+#include "BlueprintDataDefinitions.h"
+#include "StartSessionCallbackProxyAdvanced.generated.h"
+
+UCLASS(MinimalAPI)
+class UStartSessionCallbackProxyAdvanced : public UOnlineBlueprintCallProxyBase
+{
+ GENERATED_UCLASS_BODY()
+ // Called when the session starts successfully
+ UPROPERTY(BlueprintAssignable)
+ FEmptyOnlineDelegate OnSuccess;
+
+ // Called when there is an error starting the session
+ UPROPERTY(BlueprintAssignable)
+ FEmptyOnlineDelegate OnFailure;
+
+ /**
+ * Starts a session with the default online subsystem. The session needs to be previously created by calling the "CreateAdvancedSession" node.
+ * @param WorldContextObject
+ */
+ UFUNCTION(
+ BlueprintCallable
+ , meta=(BlueprintInternalUseOnly = "true", WorldContext="WorldContextObject")
+ , Category = "Online|AdvancedSessions"
+ )
+ static UStartSessionCallbackProxyAdvanced* StartAdvancedSession(const UObject* WorldContextObject);
+
+ // UOnlineBlueprintCallProxyBase interface
+ virtual void Activate() override;
+ // End of UOnlineBlueprintCallProxyBase interface
+
+private:
+ // Internal callback when session start completes
+ void OnStartCompleted(FName SessionName, bool bWasSuccessful);
+
+ // The delegate executed by the online subsystem
+ FOnStartSessionCompleteDelegate StartCompleteDelegate;
+
+ // Handles to the registered delegates above
+ FDelegateHandle StartCompleteDelegateHandle;
+
+ // The world context object in which this call is taking place
+ const UObject* WorldContextObject;
+};
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/UpdateSessionCallbackProxyAdvanced.h b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/UpdateSessionCallbackProxyAdvanced.h
new file mode 100644
index 0000000..bd549a9
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Classes/UpdateSessionCallbackProxyAdvanced.h
@@ -0,0 +1,69 @@
+// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
+#pragma once
+
+#include "CoreMinimal.h"
+#include "Engine/Engine.h"
+#include "BlueprintDataDefinitions.h"
+#include "UpdateSessionCallbackProxyAdvanced.generated.h"
+
+UCLASS(MinimalAPI)
+class UUpdateSessionCallbackProxyAdvanced : public UOnlineBlueprintCallProxyBase
+{
+ GENERATED_UCLASS_BODY()
+
+ // Called when the session was updated successfully
+ UPROPERTY(BlueprintAssignable)
+ FEmptyOnlineDelegate OnSuccess;
+
+ // Called when there was an error updating the session
+ UPROPERTY(BlueprintAssignable)
+ FEmptyOnlineDelegate OnFailure;
+
+ // Creates a session with the default online subsystem with advanced optional inputs, you MUST fill in all categories or it will pass in values that you didn't want as default values
+ UFUNCTION(BlueprintCallable, meta=(BlueprintInternalUseOnly = "true", WorldContext="WorldContextObject",AutoCreateRefTerm="ExtraSettings"), Category = "Online|AdvancedSessions")
+ static UUpdateSessionCallbackProxyAdvanced* UpdateSession(UObject* WorldContextObject, const TArray &ExtraSettings, int32 PublicConnections = 100, int32 PrivateConnections = 0, bool bUseLAN = false, bool bAllowInvites = false, bool bAllowJoinInProgress = false, bool bRefreshOnlineData = true, bool bIsDedicatedServer = false, bool bShouldAdvertise = true);
+
+ // UOnlineBlueprintCallProxyBase interface
+ virtual void Activate() override;
+ // End of UOnlineBlueprintCallProxyBase interface
+
+private:
+ // Internal callback when session creation completes, calls StartSession
+ void OnUpdateCompleted(FName SessionName, bool bWasSuccessful);
+
+ // The delegate executed by the online subsystem
+ FOnUpdateSessionCompleteDelegate OnUpdateSessionCompleteDelegate;
+
+ // Handles to the registered delegates above
+ FDelegateHandle OnUpdateSessionCompleteDelegateHandle;
+
+ // Number of public connections
+ int NumPublicConnections = 100;
+
+ // Number of private connections
+ int NumPrivateConnections = 0;
+
+ // Whether or not to search LAN
+ bool bUseLAN = false;
+
+ // Whether or not to allow invites
+ bool bAllowInvites = true;
+
+ // Store extra settings
+ TArray ExtraSettings;
+
+ // Whether to update the online data
+ bool bRefreshOnlineData = true;
+
+ // Allow joining in progress
+ bool bAllowJoinInProgress = true;
+
+ // Update whether this is a dedicated server or not
+ bool bDedicatedServer = false;
+
+ bool bShouldAdvertise = true;
+
+ // The world context object in which this call is taking place
+ UObject* WorldContextObject;
+};
+
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/AdvancedExternalUILibrary.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/AdvancedExternalUILibrary.cpp
new file mode 100644
index 0000000..f4b1766
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/AdvancedExternalUILibrary.cpp
@@ -0,0 +1,160 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+#include "AdvancedExternalUILibrary.h"
+#include "Engine/LocalPlayer.h"
+
+
+//General Log
+DEFINE_LOG_CATEGORY(AdvancedExternalUILog);
+
+void UAdvancedExternalUILibrary::ShowAccountUpgradeUI(const FBPUniqueNetId PlayerRequestingAccountUpgradeUI, EBlueprintResultSwitch &Result)
+{
+ IOnlineExternalUIPtr ExternalUIInterface = Online::GetExternalUIInterface();
+
+ if (!ExternalUIInterface.IsValid())
+ {
+ UE_LOG(AdvancedExternalUILog, Warning, TEXT("ShowAccountUpgradeUI Failed to get External UI interface!"));
+ Result = EBlueprintResultSwitch::OnFailure;
+ return;
+ }
+
+ ExternalUIInterface->ShowAccountUpgradeUI(*PlayerRequestingAccountUpgradeUI.GetUniqueNetId());
+ Result = EBlueprintResultSwitch::OnSuccess;
+}
+
+void UAdvancedExternalUILibrary::ShowProfileUI(const FBPUniqueNetId PlayerViewingProfile, const FBPUniqueNetId PlayerToViewProfileOf, EBlueprintResultSwitch &Result)
+{
+
+ IOnlineExternalUIPtr ExternalUIInterface = Online::GetExternalUIInterface();
+
+ if (!ExternalUIInterface.IsValid())
+ {
+ UE_LOG(AdvancedExternalUILog, Warning, TEXT("ShowProfileUI Failed to get External UI interface!"));
+ Result = EBlueprintResultSwitch::OnFailure;
+ return;
+ }
+
+ ExternalUIInterface->ShowProfileUI(*PlayerViewingProfile.GetUniqueNetId(), *PlayerToViewProfileOf.GetUniqueNetId(), NULL);
+ Result = EBlueprintResultSwitch::OnSuccess;
+}
+
+
+
+void UAdvancedExternalUILibrary::ShowWebURLUI(FString URLToShow, EBlueprintResultSwitch &Result, TArray& AllowedDomains, bool bEmbedded, bool bShowBackground, bool bShowCloseButton, int32 OffsetX, int32 OffsetY, int32 SizeX, int32 SizeY)
+{
+ IOnlineExternalUIPtr ExternalUIInterface = Online::GetExternalUIInterface();
+
+ if (!ExternalUIInterface.IsValid())
+ {
+ UE_LOG(AdvancedExternalUILog, Warning, TEXT("ShowWebURLUI Failed to get External UI interface!"));
+ Result = EBlueprintResultSwitch::OnFailure;
+ return;
+ }
+
+ URLToShow = URLToShow.Replace(TEXT("http://"), TEXT(""));
+ URLToShow = URLToShow.Replace(TEXT("https://"), TEXT(""));
+
+ FShowWebUrlParams Params;
+ Params.AllowedDomains = AllowedDomains;
+ Params.bEmbedded = bEmbedded;
+ Params.bShowBackground = bShowBackground;
+ Params.bShowCloseButton = bShowCloseButton;
+ Params.OffsetX = OffsetX;
+ Params.OffsetY = OffsetY;
+ Params.SizeX = SizeX;
+ Params.SizeY = SizeY;
+
+ ExternalUIInterface->ShowWebURL(URLToShow, Params);
+ Result = EBlueprintResultSwitch::OnSuccess;
+}
+
+void UAdvancedExternalUILibrary::CloseWebURLUI()
+{
+ IOnlineExternalUIPtr ExternalUIInterface = Online::GetExternalUIInterface();
+
+ if (!ExternalUIInterface.IsValid())
+ {
+ UE_LOG(AdvancedExternalUILog, Warning, TEXT("CloseWebURLUI Failed to get External UI interface!"));
+ return;
+ }
+
+ ExternalUIInterface->CloseWebURL();
+}
+
+void UAdvancedExternalUILibrary::ShowLeaderBoardUI(FString LeaderboardName, EBlueprintResultSwitch &Result)
+{
+ IOnlineExternalUIPtr ExternalUIInterface = Online::GetExternalUIInterface();
+
+ if (!ExternalUIInterface.IsValid())
+ {
+ UE_LOG(AdvancedExternalUILog, Warning, TEXT("ShowLeaderboardsUI Failed to get External UI interface!"));
+ Result = EBlueprintResultSwitch::OnFailure;
+ return;
+ }
+
+ ExternalUIInterface->ShowLeaderboardUI(LeaderboardName);
+ Result = EBlueprintResultSwitch::OnSuccess;
+
+}
+
+
+void UAdvancedExternalUILibrary::ShowInviteUI(APlayerController *PlayerController, EBlueprintResultSwitch &Result)
+{
+ if (!PlayerController)
+ {
+ UE_LOG(AdvancedExternalUILog, Warning, TEXT("ShowInviteUI Had a bad Player Controller!"));
+ Result = EBlueprintResultSwitch::OnFailure;
+ return;
+ }
+
+ IOnlineExternalUIPtr ExternalUIInterface = Online::GetExternalUIInterface();
+
+ if (!ExternalUIInterface.IsValid())
+ {
+ UE_LOG(AdvancedExternalUILog, Warning, TEXT("ShowInviteUI Failed to get External UI interface!"));
+ Result = EBlueprintResultSwitch::OnFailure;
+ return;
+ }
+
+ ULocalPlayer* Player = Cast(PlayerController->Player);
+
+ if (!Player)
+ {
+ UE_LOG(AdvancedExternalUILog, Warning, TEXT("ShowInviteUI Failed to get ULocalPlayer for the given PlayerController!"));
+ Result = EBlueprintResultSwitch::OnFailure;
+ return;
+ }
+
+ ExternalUIInterface->ShowInviteUI(Player->GetControllerId(), NAME_GameSession);
+ Result = EBlueprintResultSwitch::OnSuccess;
+}
+
+void UAdvancedExternalUILibrary::ShowFriendsUI(APlayerController *PlayerController, EBlueprintResultSwitch &Result)
+{
+ if (!PlayerController)
+ {
+ UE_LOG(AdvancedExternalUILog, Warning, TEXT("ShowFriendsUI Had a bad Player Controller!"));
+ Result = EBlueprintResultSwitch::OnFailure;
+ return;
+ }
+
+ IOnlineExternalUIPtr ExternalUIInterface = Online::GetExternalUIInterface();
+
+ if (!ExternalUIInterface.IsValid())
+ {
+ UE_LOG(AdvancedExternalUILog, Warning, TEXT("ShowFriendsUI Failed to get External UI interface!"));
+ Result = EBlueprintResultSwitch::OnFailure;
+ return;
+ }
+
+ ULocalPlayer* Player = Cast(PlayerController->Player);
+
+ if (!Player)
+ {
+ UE_LOG(AdvancedExternalUILog, Warning, TEXT("ShowFriendsUI Failed to get ULocalPlayer for the given PlayerController!"));
+ Result = EBlueprintResultSwitch::OnFailure;
+ return;
+ }
+
+ ExternalUIInterface->ShowFriendsUI(Player->GetControllerId());
+ Result = EBlueprintResultSwitch::OnSuccess;
+}
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/AdvancedFriendsGameInstance.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/AdvancedFriendsGameInstance.cpp
new file mode 100644
index 0000000..47afb6a
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/AdvancedFriendsGameInstance.cpp
@@ -0,0 +1,334 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+#include "AdvancedFriendsGameInstance.h"
+#include "Kismet/GameplayStatics.h"
+#include "GameFramework/PlayerController.h"
+
+//General Log
+DEFINE_LOG_CATEGORY(AdvancedFriendsInterfaceLog);
+
+UAdvancedFriendsGameInstance::UAdvancedFriendsGameInstance(const FObjectInitializer& ObjectInitializer)
+ : Super(ObjectInitializer)
+ , bCallFriendInterfaceEventsOnPlayerControllers(true)
+ , bCallIdentityInterfaceEventsOnPlayerControllers(true)
+ , bCallVoiceInterfaceEventsOnPlayerControllers(true)
+ , bEnableTalkingStatusDelegate(true)
+ , SessionInviteReceivedDelegate(FOnSessionInviteReceivedDelegate::CreateUObject(this, &ThisClass::OnSessionInviteReceivedMaster))
+ , SessionInviteAcceptedDelegate(FOnSessionUserInviteAcceptedDelegate::CreateUObject(this, &ThisClass::OnSessionInviteAcceptedMaster))
+ , PlayerTalkingStateChangedDelegate(FOnPlayerTalkingStateChangedDelegate::CreateUObject(this, &ThisClass::OnPlayerTalkingStateChangedMaster))
+ , PlayerLoginChangedDelegate(FOnLoginChangedDelegate::CreateUObject(this, &ThisClass::OnPlayerLoginChangedMaster))
+ , PlayerLoginStatusChangedDelegate(FOnLoginStatusChangedDelegate::CreateUObject(this, &ThisClass::OnPlayerLoginStatusChangedMaster))
+{
+}
+
+void UAdvancedFriendsGameInstance::Shutdown()
+{
+ IOnlineSessionPtr SessionInterface = Online::GetSessionInterface(GetWorld());
+
+ if (!SessionInterface.IsValid())
+ {
+ UE_LOG(AdvancedFriendsInterfaceLog, Warning, TEXT("UAdvancedFriendsGameInstance Failed to get session system!"));
+ //return;
+ }
+ else
+ {
+ // Clear all of the delegate handles here
+ SessionInterface->ClearOnSessionUserInviteAcceptedDelegate_Handle(SessionInviteAcceptedDelegateHandle);
+ SessionInterface->ClearOnSessionInviteReceivedDelegate_Handle(SessionInviteReceivedDelegateHandle);
+ }
+
+
+ if (bEnableTalkingStatusDelegate)
+ {
+ IOnlineVoicePtr VoiceInterface = Online::GetVoiceInterface(GetWorld());
+
+ if (VoiceInterface.IsValid())
+ {
+ VoiceInterface->ClearOnPlayerTalkingStateChangedDelegate_Handle(PlayerTalkingStateChangedDelegateHandle);
+ }
+ else
+ {
+
+ UE_LOG(AdvancedFriendsInterfaceLog, Warning, TEXT("UAdvancedFriendsInstance Failed to get voice interface!"));
+ }
+ }
+
+ IOnlineIdentityPtr IdentityInterface = Online::GetIdentityInterface(GetWorld());
+
+ if (IdentityInterface.IsValid())
+ {
+ IdentityInterface->ClearOnLoginChangedDelegate_Handle(PlayerLoginChangedDelegateHandle);
+
+
+ // I am just defaulting to player 1
+ IdentityInterface->ClearOnLoginStatusChangedDelegate_Handle(0, PlayerLoginStatusChangedDelegateHandle);
+ }
+
+
+ Super::Shutdown();
+}
+
+void UAdvancedFriendsGameInstance::Init()
+{
+ IOnlineSessionPtr SessionInterface = Online::GetSessionInterface(GetWorld());//OnlineSub->GetSessionInterface();
+
+ if (SessionInterface.IsValid())
+ {
+ // Currently doesn't store a handle or assign a delegate to any local player beyond the first.....should handle?
+ // Thought about directly handling it but friends for multiple players probably isn't required
+ // Iterating through the local player TArray only works if it has had players assigned to it, most of the online interfaces don't support
+ // Multiple logins either (IE: Steam)
+ SessionInviteAcceptedDelegateHandle = SessionInterface->AddOnSessionUserInviteAcceptedDelegate_Handle(SessionInviteAcceptedDelegate);
+
+ SessionInviteReceivedDelegateHandle = SessionInterface->AddOnSessionInviteReceivedDelegate_Handle(SessionInviteReceivedDelegate);
+ }
+ else
+ {
+ UE_LOG(AdvancedFriendsInterfaceLog, Warning, TEXT("UAdvancedFriendsInstance Failed to get session interface!"));
+ //return;
+ }
+
+ // Beginning work on the voice interface
+ if (bEnableTalkingStatusDelegate)
+ {
+ IOnlineVoicePtr VoiceInterface = Online::GetVoiceInterface(GetWorld());
+
+ if (VoiceInterface.IsValid())
+ {
+ PlayerTalkingStateChangedDelegateHandle = VoiceInterface->AddOnPlayerTalkingStateChangedDelegate_Handle(PlayerTalkingStateChangedDelegate);
+ }
+ else
+ {
+
+ UE_LOG(AdvancedFriendsInterfaceLog, Warning, TEXT("UAdvancedFriendsInstance Failed to get voice interface!"));
+ }
+ }
+
+ IOnlineIdentityPtr IdentityInterface = Online::GetIdentityInterface(GetWorld());
+
+ if (IdentityInterface.IsValid())
+ {
+ PlayerLoginChangedDelegateHandle = IdentityInterface->AddOnLoginChangedDelegate_Handle(PlayerLoginChangedDelegate);
+
+ // Just defaulting to player 1
+ PlayerLoginStatusChangedDelegateHandle = IdentityInterface->AddOnLoginStatusChangedDelegate_Handle(0, PlayerLoginStatusChangedDelegate);
+ }
+ else
+ {
+ UE_LOG(AdvancedFriendsInterfaceLog, Warning, TEXT("UAdvancedFriendsInstance Failed to get identity interface!"));
+ }
+
+
+ Super::Init();
+}
+
+/*void UAdvancedFriendsGameInstance::PostLoad()
+{
+ Super::PostLoad();
+}*/
+
+
+// Removed because it never gets called by the online subsystems
+/*void UAdvancedFriendsGameInstance::OnSessionInviteReceivedMaster(const FUniqueNetId &InvitedPlayer, const FUniqueNetId &FriendInviting, const FOnlineSessionSearchResult& Session)
+{
+ // Just call the blueprint event to let the user handle this
+
+ FBPUniqueNetId IP, FI;
+
+ IP.SetUniqueNetId(&InvitedPlayer);
+
+ FI.SetUniqueNetId(&FriendInviting);
+
+ FBlueprintSessionResult BPS;
+ BPS.OnlineResult = Session;
+ OnSessionInviteReceived(IP,FI,BPS);
+
+ TArray& PlayerArray = GetWorld()->GetGameState()->PlayerArray;
+ const TArray&ControllerArray = this->GetLocalPlayers();
+
+ for (int i = 0; i < ControllerArray.Num(); i++)
+ {
+ if (*PlayerArray[ControllerArray[i]->PlayerController->NetPlayerIndex]->UniqueId.GetUniqueNetId().Get() == InvitedPlayer)
+ {
+ //Run the Event specific to the actor, if the actor has the interface, otherwise ignore
+ if (ControllerArray[i]->PlayerController->GetClass()->ImplementsInterface(UAdvancedFriendsInterface::StaticClass()))
+ {
+ IAdvancedFriendsInterface::Execute_OnSessionInviteReceived(ControllerArray[i]->PlayerController, FI, BPS);
+ }
+ break;
+ }
+ }
+}*/
+
+void UAdvancedFriendsGameInstance::OnPlayerLoginStatusChangedMaster(int32 PlayerNum, ELoginStatus::Type PreviousStatus, ELoginStatus::Type NewStatus, const FUniqueNetId & NewPlayerUniqueNetID)
+{
+ EBPLoginStatus OrigStatus = (EBPLoginStatus)PreviousStatus;
+ EBPLoginStatus CurrentStatus = (EBPLoginStatus)NewStatus;
+ FBPUniqueNetId PlayerID;
+ PlayerID.SetUniqueNetId(&NewPlayerUniqueNetID);
+
+ OnPlayerLoginStatusChanged(PlayerNum, OrigStatus,CurrentStatus,PlayerID);
+
+
+ if (bCallIdentityInterfaceEventsOnPlayerControllers)
+ {
+ APlayerController* Player = UGameplayStatics::GetPlayerController(GetWorld(), PlayerNum);
+
+ if (Player != NULL)
+ {
+ //Run the Event specific to the actor, if the actor has the interface, otherwise ignore
+ if (Player->GetClass()->ImplementsInterface(UAdvancedFriendsInterface::StaticClass()))
+ {
+ IAdvancedFriendsInterface::Execute_OnPlayerLoginStatusChanged(Player, OrigStatus, CurrentStatus, PlayerID);
+ }
+ }
+ else
+ {
+ UE_LOG(AdvancedFriendsInterfaceLog, Warning, TEXT("UAdvancedFriendsInstance Failed to get a controller with the specified index in OnPlayerLoginStatusChangedMaster!"));
+ }
+ }
+}
+
+void UAdvancedFriendsGameInstance::OnPlayerLoginChangedMaster(int32 PlayerNum)
+{
+ OnPlayerLoginChanged(PlayerNum);
+
+ if (bCallIdentityInterfaceEventsOnPlayerControllers)
+ {
+ APlayerController* Player = UGameplayStatics::GetPlayerController(GetWorld(), PlayerNum);
+
+ if (Player != NULL)
+ {
+ //Run the Event specific to the actor, if the actor has the interface, otherwise ignore
+ if (Player->GetClass()->ImplementsInterface(UAdvancedFriendsInterface::StaticClass()))
+ {
+ IAdvancedFriendsInterface::Execute_OnPlayerLoginChanged(Player, PlayerNum);
+ }
+ }
+ else
+ {
+ UE_LOG(AdvancedFriendsInterfaceLog, Warning, TEXT("UAdvancedFriendsInstance Failed to get a controller with the specified index in OnPlayerLoginChanged!"));
+ }
+ }
+}
+
+void UAdvancedFriendsGameInstance::OnPlayerTalkingStateChangedMaster(TSharedRef PlayerId, bool bIsTalking)
+{
+ FBPUniqueNetId PlayerTalking;
+ PlayerTalking.SetUniqueNetId(PlayerId);
+ OnPlayerTalkingStateChanged(PlayerTalking, bIsTalking);
+
+ if (bCallVoiceInterfaceEventsOnPlayerControllers)
+ {
+ APlayerController* Player = NULL;
+
+ for (const ULocalPlayer* LPlayer : LocalPlayers)
+ {
+ Player = UGameplayStatics::GetPlayerController(GetWorld(), LPlayer->GetControllerId());
+
+ if (Player != NULL)
+ {
+ //Run the Event specific to the actor, if the actor has the interface, otherwise ignore
+ if (Player->GetClass()->ImplementsInterface(UAdvancedFriendsInterface::StaticClass()))
+ {
+ IAdvancedFriendsInterface::Execute_OnPlayerVoiceStateChanged(Player, PlayerTalking, bIsTalking);
+ }
+ }
+ else
+ {
+ UE_LOG(AdvancedFriendsInterfaceLog, Warning, TEXT("UAdvancedFriendsInstance Failed to get a controller with the specified index in OnVoiceStateChanged!"));
+ }
+ }
+ }
+}
+
+void UAdvancedFriendsGameInstance::OnSessionInviteReceivedMaster(const FUniqueNetId & PersonInvited, const FUniqueNetId & PersonInviting, const FString& AppId, const FOnlineSessionSearchResult& SessionToJoin)
+{
+ if (SessionToJoin.IsValid())
+ {
+ FBlueprintSessionResult BluePrintResult;
+ BluePrintResult.OnlineResult = SessionToJoin;
+
+ FBPUniqueNetId PInvited;
+ PInvited.SetUniqueNetId(&PersonInvited);
+
+ FBPUniqueNetId PInviting;
+ PInviting.SetUniqueNetId(&PersonInviting);
+
+
+ TArray PlayerList;
+ GEngine->GetAllLocalPlayerControllers(PlayerList);
+
+ APlayerController* Player = NULL;
+
+ int32 LocalPlayer = 0;
+ for (int i = 0; i < PlayerList.Num(); i++)
+ {
+ if (*PlayerList[i]->PlayerState->GetUniqueId().GetUniqueNetId() == PersonInvited)
+ {
+ LocalPlayer = i;
+ Player = PlayerList[i];
+ break;
+ }
+ }
+
+ OnSessionInviteReceived(LocalPlayer, PInviting, AppId, BluePrintResult);
+
+ //IAdvancedFriendsInterface* TheInterface = NULL;
+
+ if (Player != NULL)
+ {
+ //Run the Event specific to the actor, if the actor has the interface, otherwise ignore
+ if (Player->GetClass()->ImplementsInterface(UAdvancedFriendsInterface::StaticClass()))
+ {
+ IAdvancedFriendsInterface::Execute_OnSessionInviteReceived(Player, PInviting, BluePrintResult);
+ }
+ }
+ else
+ {
+ UE_LOG(AdvancedFriendsInterfaceLog, Warning, TEXT("UAdvancedFriendsInstance Failed to get a controller with the specified index in OnSessionInviteReceived!"));
+ }
+ }
+ else
+ {
+ UE_LOG(AdvancedFriendsInterfaceLog, Warning, TEXT("UAdvancedFriendsInstance Return a bad search result in OnSessionInviteReceived!"));
+ }
+}
+
+void UAdvancedFriendsGameInstance::OnSessionInviteAcceptedMaster(const bool bWasSuccessful, int32 LocalPlayer, TSharedPtr PersonInvited, const FOnlineSessionSearchResult& SessionToJoin)
+{
+ if (bWasSuccessful)
+ {
+ if (SessionToJoin.IsValid())
+ {
+
+ FBlueprintSessionResult BluePrintResult;
+ BluePrintResult.OnlineResult = SessionToJoin;
+
+ FBPUniqueNetId PInvited;
+ PInvited.SetUniqueNetId(PersonInvited);
+
+ OnSessionInviteAccepted(LocalPlayer,PInvited, BluePrintResult);
+
+ APlayerController* Player = UGameplayStatics::GetPlayerController(GetWorld(), LocalPlayer);
+
+ //IAdvancedFriendsInterface* TheInterface = NULL;
+
+ if (Player != NULL)
+ {
+ //Run the Event specific to the actor, if the actor has the interface, otherwise ignore
+ if (Player->GetClass()->ImplementsInterface(UAdvancedFriendsInterface::StaticClass()))
+ {
+ IAdvancedFriendsInterface::Execute_OnSessionInviteAccepted(Player,PInvited, BluePrintResult);
+ }
+ }
+ else
+ {
+ UE_LOG(AdvancedFriendsInterfaceLog, Warning, TEXT("UAdvancedFriendsInstance Failed to get a controller with the specified index in OnSessionInviteAccepted!"));
+ }
+ }
+ else
+ {
+ UE_LOG(AdvancedFriendsInterfaceLog, Warning, TEXT("UAdvancedFriendsInstance Return a bad search result in OnSessionInviteAccepted!"));
+ }
+ }
+}
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/AdvancedFriendsInterface.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/AdvancedFriendsInterface.cpp
new file mode 100644
index 0000000..92e5138
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/AdvancedFriendsInterface.cpp
@@ -0,0 +1,9 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+#include "AdvancedFriendsInterface.h"
+
+
+
+UAdvancedFriendsInterface::UAdvancedFriendsInterface(const class FObjectInitializer& ObjectInitializer)
+ : Super(ObjectInitializer)
+{
+}
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/AdvancedFriendsLibrary.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/AdvancedFriendsLibrary.cpp
new file mode 100644
index 0000000..1af7a5b
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/AdvancedFriendsLibrary.cpp
@@ -0,0 +1,274 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+#include "AdvancedFriendsLibrary.h"
+
+
+
+// This is taken directly from UE4 - OnlineSubsystemSteamPrivatePCH.h as a fix for the array_count macro
+
+//General Log
+DEFINE_LOG_CATEGORY(AdvancedFriendsLog);
+
+void UAdvancedFriendsLibrary::SendSessionInviteToFriends(APlayerController *PlayerController, const TArray &Friends, EBlueprintResultSwitch &Result)
+{
+ if (!PlayerController)
+ {
+ UE_LOG(AdvancedFriendsLog, Warning, TEXT("SendSessionInviteToFriend Had a bad Player Controller!"));
+ Result = EBlueprintResultSwitch::OnFailure;
+ return;
+ }
+
+ if (Friends.Num() < 1)
+ {
+ UE_LOG(AdvancedFriendsLog, Warning, TEXT("SendSessionInviteToFriend Had no friends in invitation array!"));
+ Result = EBlueprintResultSwitch::OnFailure;
+ return;
+ }
+
+ IOnlineSessionPtr SessionInterface = Online::GetSessionInterface();
+
+ if (!SessionInterface.IsValid())
+ {
+ UE_LOG(AdvancedFriendsLog, Warning, TEXT("SendSessionInviteToFriend Failed to get session interface!"));
+ Result = EBlueprintResultSwitch::OnFailure;
+ return;
+ }
+
+ ULocalPlayer* Player = Cast(PlayerController->Player);
+
+ if (!Player)
+ {
+ UE_LOG(AdvancedFriendsLog, Warning, TEXT("SendSessionInviteToFriend failed to get LocalPlayer!"));
+ Result = EBlueprintResultSwitch::OnFailure;
+ return;
+ }
+
+ TArray> List;
+ for (int i = 0; i < Friends.Num(); i++)
+ {
+ TSharedRef val(Friends[i].UniqueNetId.ToSharedRef());
+ //TSharedRef val(Friends[i].GetUniqueNetId());
+ List.Add(val);
+ }
+
+ if (SessionInterface->SendSessionInviteToFriends(Player->GetControllerId(), NAME_GameSession, List))
+ {
+ Result = EBlueprintResultSwitch::OnSuccess;
+ return;
+ }
+
+ Result = EBlueprintResultSwitch::OnFailure;
+ return;
+}
+
+void UAdvancedFriendsLibrary::SendSessionInviteToFriend(APlayerController *PlayerController, const FBPUniqueNetId &FriendUniqueNetId, EBlueprintResultSwitch &Result)
+{
+ if (!PlayerController)
+ {
+ UE_LOG(AdvancedFriendsLog, Warning, TEXT("SendSessionInviteToFriend Had a bad Player Controller!"));
+ Result = EBlueprintResultSwitch::OnFailure;
+ return;
+ }
+
+ if (!FriendUniqueNetId.IsValid())
+ {
+ UE_LOG(AdvancedFriendsLog, Warning, TEXT("SendSessionInviteToFriend Had a bad UniqueNetId!"));
+ Result = EBlueprintResultSwitch::OnFailure;
+ return;
+ }
+
+ IOnlineSessionPtr SessionInterface = Online::GetSessionInterface();
+
+ if (!SessionInterface.IsValid())
+ {
+ UE_LOG(AdvancedFriendsLog, Warning, TEXT("SendSessionInviteToFriend Failed to get session interface!"));
+ Result = EBlueprintResultSwitch::OnFailure;
+ return;
+ }
+
+ ULocalPlayer* Player = Cast(PlayerController->Player);
+
+ if (!Player)
+ {
+ UE_LOG(AdvancedFriendsLog, Warning, TEXT("SendSessionInviteToFriend failed to get LocalPlayer!"));
+ Result = EBlueprintResultSwitch::OnFailure;
+ return;
+ }
+
+ if (SessionInterface->SendSessionInviteToFriend(Player->GetControllerId(), NAME_GameSession, *FriendUniqueNetId.GetUniqueNetId()))
+ {
+ Result = EBlueprintResultSwitch::OnSuccess;
+ return;
+ }
+
+ Result = EBlueprintResultSwitch::OnFailure;
+ return;
+}
+
+void UAdvancedFriendsLibrary::GetFriend(APlayerController *PlayerController, const FBPUniqueNetId FriendUniqueNetId, FBPFriendInfo &Friend)
+{
+
+ if (!PlayerController)
+ {
+ UE_LOG(AdvancedFriendsLog, Warning, TEXT("GetFriend Had a bad Player Controller!"));
+ return;
+ }
+
+ if (!FriendUniqueNetId.IsValid())
+ {
+ UE_LOG(AdvancedFriendsLog, Warning, TEXT("GetFriend Had a bad UniqueNetId!"));
+ return;
+ }
+
+ IOnlineFriendsPtr FriendsInterface = Online::GetFriendsInterface();
+
+ if (!FriendsInterface.IsValid())
+ {
+ UE_LOG(AdvancedFriendsLog, Warning, TEXT("GetFriend Failed to get friends interface!"));
+ return;
+ }
+
+ ULocalPlayer* Player = Cast(PlayerController->Player);
+
+ if (!Player)
+ {
+ UE_LOG(AdvancedFriendsLog, Warning, TEXT("GetFriend failed to get LocalPlayer!"));
+ return;
+ }
+
+ TSharedPtr fr = FriendsInterface->GetFriend(Player->GetControllerId(), *FriendUniqueNetId.GetUniqueNetId(), EFriendsLists::ToString(EFriendsLists::Default));
+ if (fr.IsValid())
+ {
+ FOnlineUserPresence pres = fr->GetPresence();
+ Friend.DisplayName = fr->GetDisplayName();
+ Friend.OnlineState = ((EBPOnlinePresenceState)((int32)pres.Status.State));
+ Friend.RealName = fr->GetRealName();
+ Friend.UniqueNetId.SetUniqueNetId(fr->GetUserId());
+ Friend.bIsPlayingSameGame = pres.bIsPlayingThisGame;
+
+ Friend.PresenceInfo.bHasVoiceSupport = pres.bHasVoiceSupport;
+ Friend.PresenceInfo.bIsJoinable = pres.bIsJoinable;
+ Friend.PresenceInfo.bIsOnline = pres.bIsOnline;
+ Friend.PresenceInfo.bIsPlaying = pres.bIsPlaying;
+ Friend.PresenceInfo.bIsPlayingThisGame = pres.bIsPlayingThisGame;
+ Friend.PresenceInfo.PresenceState = ((EBPOnlinePresenceState)((int32)pres.Status.State));
+ Friend.PresenceInfo.StatusString = pres.Status.StatusStr;
+ }
+}
+
+void UAdvancedFriendsLibrary::IsAFriend(APlayerController *PlayerController, const FBPUniqueNetId UniqueNetId, bool &IsFriend)
+{
+ if (!PlayerController)
+ {
+ UE_LOG(AdvancedFriendsLog, Warning, TEXT("IsAFriend Had a bad Player Controller!"));
+ return;
+ }
+
+ if (!UniqueNetId.IsValid())
+ {
+ UE_LOG(AdvancedFriendsLog, Warning, TEXT("IsAFriend Had a bad UniqueNetId!"));
+ return;
+ }
+
+ IOnlineFriendsPtr FriendsInterface = Online::GetFriendsInterface();
+
+ if (!FriendsInterface.IsValid())
+ {
+ UE_LOG(AdvancedFriendsLog, Warning, TEXT("IsAFriend Failed to get friends interface!"));
+ return;
+ }
+
+ ULocalPlayer* Player = Cast(PlayerController->Player);
+
+ if (!Player)
+ {
+ UE_LOG(AdvancedFriendsLog, Warning, TEXT("IsAFriend Failed to get LocalPlayer!"));
+ return;
+ }
+
+ IsFriend = FriendsInterface->IsFriend(Player->GetControllerId(), *UniqueNetId.GetUniqueNetId(), EFriendsLists::ToString(EFriendsLists::Default));
+}
+
+void UAdvancedFriendsLibrary::GetStoredRecentPlayersList(FBPUniqueNetId UniqueNetId, TArray &PlayersList)
+{
+ IOnlineFriendsPtr FriendsInterface = Online::GetFriendsInterface();
+
+ if (!FriendsInterface.IsValid())
+ {
+ UE_LOG(AdvancedFriendsLog, Warning, TEXT("GetRecentPlayersList Failed to get friends interface!"));
+ return;
+ }
+
+ if (!UniqueNetId.IsValid())
+ {
+ UE_LOG(AdvancedFriendsLog, Warning, TEXT("GetRecentPlayersList Failed was given an invalid UniqueNetId!"));
+ return;
+ }
+
+ TArray< TSharedRef > PlayerList;
+
+ // For now getting all namespaces
+ FriendsInterface->GetRecentPlayers(*(UniqueNetId.GetUniqueNetId()),"", PlayerList);
+
+ for (int32 i = 0; i < PlayerList.Num(); i++)
+ {
+ TSharedRef Player = PlayerList[i];
+ FBPOnlineRecentPlayer BPF;
+ BPF.DisplayName = Player->GetDisplayName();
+ BPF.RealName = Player->GetRealName();
+ BPF.UniqueNetId.SetUniqueNetId(Player->GetUserId());
+ PlayersList.Add(BPF);
+ }
+}
+
+void UAdvancedFriendsLibrary::GetStoredFriendsList(APlayerController *PlayerController, TArray &FriendsList)
+{
+
+ if (!PlayerController)
+ {
+ UE_LOG(AdvancedFriendsLog, Warning, TEXT("GetFriendsList Had a bad Player Controller!"));
+ return;
+ }
+
+ IOnlineFriendsPtr FriendsInterface = Online::GetFriendsInterface();
+
+ if (!FriendsInterface.IsValid())
+ {
+ UE_LOG(AdvancedFriendsLog, Warning, TEXT("GetFriendsList Failed to get friends interface!"));
+ return;
+ }
+
+ ULocalPlayer* Player = Cast(PlayerController->Player);
+
+ if (!Player)
+ {
+ UE_LOG(AdvancedFriendsLog, Warning, TEXT("GetFriendsList Failed to get LocalPlayer!"));
+ return;
+ }
+
+
+ TArray< TSharedRef > FriendList;
+ FriendsInterface->GetFriendsList(Player->GetControllerId(), EFriendsLists::ToString((EFriendsLists::Default)), FriendList);
+
+ for (int32 i = 0; i < FriendList.Num(); i++)
+ {
+ TSharedRef Friend = FriendList[i];
+ FBPFriendInfo BPF;
+ FOnlineUserPresence pres = Friend->GetPresence();
+
+ BPF.OnlineState = ((EBPOnlinePresenceState)((int32)pres.Status.State));
+ BPF.DisplayName = Friend->GetDisplayName();
+ BPF.RealName = Friend->GetRealName();
+ BPF.UniqueNetId.SetUniqueNetId(Friend->GetUserId());
+ BPF.bIsPlayingSameGame = pres.bIsPlayingThisGame;
+
+ BPF.PresenceInfo.bIsOnline = pres.bIsOnline;
+ BPF.PresenceInfo.bHasVoiceSupport = pres.bHasVoiceSupport;
+ BPF.PresenceInfo.bIsPlaying = pres.bIsPlaying;
+ BPF.PresenceInfo.PresenceState = ((EBPOnlinePresenceState)((int32)pres.Status.State));
+ BPF.PresenceInfo.StatusString = pres.Status.StatusStr;
+ BPF.PresenceInfo.bIsJoinable = pres.bIsJoinable;
+ BPF.PresenceInfo.bIsPlayingThisGame = pres.bIsPlayingThisGame;
+
+ FriendsList.Add(BPF);
+ }
+}
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/AdvancedIdentityLibrary.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/AdvancedIdentityLibrary.cpp
new file mode 100644
index 0000000..a44d5ae
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/AdvancedIdentityLibrary.cpp
@@ -0,0 +1,235 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+#include "AdvancedIdentityLibrary.h"
+
+//General Log
+DEFINE_LOG_CATEGORY(AdvancedIdentityLog);
+
+
+void UAdvancedIdentityLibrary::GetPlayerAuthToken(APlayerController * PlayerController, FString & AuthToken, EBlueprintResultSwitch &Result)
+{
+ if (!PlayerController)
+ {
+ UE_LOG(AdvancedIdentityLog, Warning, TEXT("GetPlayerAuthToken was passed a bad player controller!"));
+ Result = EBlueprintResultSwitch::OnFailure;
+ return;
+ }
+
+ ULocalPlayer* Player = Cast(PlayerController->Player);
+
+ if (!Player)
+ {
+ UE_LOG(AdvancedIdentityLog, Warning, TEXT("GetPlayerAuthToken failed to get LocalPlayer!"));
+ Result = EBlueprintResultSwitch::OnFailure;
+ return;
+ }
+
+ IOnlineIdentityPtr IdentityInterface = Online::GetIdentityInterface();
+
+ if (!IdentityInterface.IsValid())
+ {
+ UE_LOG(AdvancedIdentityLog, Warning, TEXT("GetPlayerAuthToken Failed to get identity interface!"));
+ Result = EBlueprintResultSwitch::OnFailure;
+ return;
+ }
+
+ AuthToken = IdentityInterface->GetAuthToken(Player->GetControllerId());
+ Result = EBlueprintResultSwitch::OnSuccess;
+}
+
+void UAdvancedIdentityLibrary::GetPlayerNickname(const FBPUniqueNetId & UniqueNetID, FString & PlayerNickname)
+{
+ if (!UniqueNetID.IsValid())
+ {
+ UE_LOG(AdvancedIdentityLog, Warning, TEXT("GetPlayerNickname was passed a bad player uniquenetid!"));
+ return;
+ }
+
+ IOnlineIdentityPtr IdentityInterface = Online::GetIdentityInterface();
+
+ if (!IdentityInterface.IsValid())
+ {
+ UE_LOG(AdvancedIdentityLog, Warning, TEXT("GetPlayerNickname Failed to get identity interface!"));
+ return;
+ }
+ PlayerNickname = IdentityInterface->GetPlayerNickname(*UniqueNetID.GetUniqueNetId());
+}
+
+
+void UAdvancedIdentityLibrary::GetLoginStatus(const FBPUniqueNetId & UniqueNetID, EBPLoginStatus & LoginStatus, EBlueprintResultSwitch &Result)
+{
+ if (!UniqueNetID.IsValid())
+ {
+ UE_LOG(AdvancedIdentityLog, Warning, TEXT("GetLoginStatus was passed a bad player uniquenetid!"));
+ Result = EBlueprintResultSwitch::OnFailure;
+ return;
+ }
+
+ IOnlineIdentityPtr IdentityInterface = Online::GetIdentityInterface();
+
+ if (!IdentityInterface.IsValid())
+ {
+ UE_LOG(AdvancedIdentityLog, Warning, TEXT("GetLoginStatus Failed to get identity interface!"));
+ Result = EBlueprintResultSwitch::OnFailure;
+ return;
+ }
+
+ LoginStatus = (EBPLoginStatus)IdentityInterface->GetLoginStatus(*UniqueNetID.GetUniqueNetId());
+ Result = EBlueprintResultSwitch::OnSuccess;
+}
+
+
+void UAdvancedIdentityLibrary::GetAllUserAccounts(TArray & AccountInfos, EBlueprintResultSwitch &Result)
+{
+ IOnlineIdentityPtr IdentityInterface = Online::GetIdentityInterface();
+
+ if (!IdentityInterface.IsValid())
+ {
+ UE_LOG(AdvancedIdentityLog, Warning, TEXT("GetAllUserAccounts Failed to get identity interface!"));
+ Result = EBlueprintResultSwitch::OnFailure;
+ return;
+ }
+
+ TArray> accountInfos = IdentityInterface->GetAllUserAccounts();
+
+ for (int i = 0; i < accountInfos.Num(); ++i)
+ {
+ AccountInfos.Add(FBPUserOnlineAccount(accountInfos[i]));
+ }
+
+ Result = EBlueprintResultSwitch::OnSuccess;
+}
+
+void UAdvancedIdentityLibrary::GetUserAccount(const FBPUniqueNetId & UniqueNetId, FBPUserOnlineAccount & AccountInfo, EBlueprintResultSwitch &Result)
+{
+ IOnlineIdentityPtr IdentityInterface = Online::GetIdentityInterface();
+
+ if(!UniqueNetId.IsValid())
+ {
+ UE_LOG(AdvancedIdentityLog, Warning, TEXT("GetUserAccount was passed a bad unique net id!"));
+ Result = EBlueprintResultSwitch::OnFailure;
+ return;
+ }
+
+ if (!IdentityInterface.IsValid())
+ {
+ UE_LOG(AdvancedIdentityLog, Warning, TEXT("GetUserAccount Failed to get identity interface!"));
+ Result = EBlueprintResultSwitch::OnFailure;
+ return;
+ }
+
+ TSharedPtr accountInfo = IdentityInterface->GetUserAccount(*UniqueNetId.GetUniqueNetId());
+
+ if (!accountInfo.IsValid())
+ {
+ UE_LOG(AdvancedIdentityLog, Warning, TEXT("GetUserAccount Failed to get the account!"));
+ Result = EBlueprintResultSwitch::OnFailure;
+ return;
+ }
+
+ AccountInfo.UserAccountInfo = accountInfo;
+ Result = EBlueprintResultSwitch::OnSuccess;
+}
+
+void UAdvancedIdentityLibrary::GetUserAccountAccessToken(const FBPUserOnlineAccount & AccountInfo, FString & AccessToken)
+{
+ if (!AccountInfo.UserAccountInfo.IsValid())
+ {
+ UE_LOG(AdvancedIdentityLog, Warning, TEXT("GetUserAccountAccessToken was passed an invalid account!"));
+ return;
+ }
+
+ AccessToken = AccountInfo.UserAccountInfo->GetAccessToken();
+}
+
+void UAdvancedIdentityLibrary::GetUserAccountAuthAttribute(const FBPUserOnlineAccount & AccountInfo, const FString & AttributeName, FString & AuthAttribute, EBlueprintResultSwitch &Result)
+{
+ if (!AccountInfo.UserAccountInfo.IsValid())
+ {
+ UE_LOG(AdvancedIdentityLog, Warning, TEXT("GetUserAccountAuthAttribute was passed an invalid account!"));
+ Result = EBlueprintResultSwitch::OnFailure;
+ return;
+ }
+
+ if (!AccountInfo.UserAccountInfo->GetAuthAttribute(AttributeName, AuthAttribute))
+ {
+ UE_LOG(AdvancedIdentityLog, Warning, TEXT("GetUserAccountAuthAttribute couldn't find the attribute!"));
+ Result = EBlueprintResultSwitch::OnFailure;
+ return;
+ }
+
+ Result = EBlueprintResultSwitch::OnSuccess;
+}
+
+void UAdvancedIdentityLibrary::SetUserAccountAttribute(const FBPUserOnlineAccount & AccountInfo, const FString & AttributeName, const FString & NewAttributeValue, EBlueprintResultSwitch &Result)
+{
+ if (!AccountInfo.UserAccountInfo.IsValid())
+ {
+ UE_LOG(AdvancedIdentityLog, Warning, TEXT("SetUserAccountAuthAttribute was passed an invalid account!"));
+ Result = EBlueprintResultSwitch::OnFailure;
+ return;
+ }
+
+ if (!AccountInfo.UserAccountInfo->SetUserAttribute(AttributeName, NewAttributeValue))
+ {
+ UE_LOG(AdvancedIdentityLog, Warning, TEXT("SetUserAccountAuthAttribute was unable to set the attribute!"));
+ Result = EBlueprintResultSwitch::OnFailure;
+ return;
+ }
+
+ Result = EBlueprintResultSwitch::OnSuccess;
+}
+
+void UAdvancedIdentityLibrary::GetUserID(const FBPUserOnlineAccount & AccountInfo, FBPUniqueNetId & UniqueNetID)
+{
+ if (!AccountInfo.UserAccountInfo.IsValid())
+ {
+ UE_LOG(AdvancedIdentityLog, Warning, TEXT("GetUserID was passed an invalid account!"));
+ return;
+ }
+
+
+ UniqueNetID.SetUniqueNetId(AccountInfo.UserAccountInfo->GetUserId());
+}
+
+void UAdvancedIdentityLibrary::GetUserAccountRealName(const FBPUserOnlineAccount & AccountInfo, FString & UserName)
+{
+ if (!AccountInfo.UserAccountInfo.IsValid())
+ {
+ UE_LOG(AdvancedIdentityLog, Warning, TEXT("GetUserAccountRealName was passed an invalid account!"));
+ return;
+ }
+
+
+ UserName = AccountInfo.UserAccountInfo->GetRealName();
+}
+
+void UAdvancedIdentityLibrary::GetUserAccountDisplayName(const FBPUserOnlineAccount & AccountInfo, FString & DisplayName)
+{
+ if (!AccountInfo.UserAccountInfo.IsValid())
+ {
+ UE_LOG(AdvancedIdentityLog, Warning, TEXT("GetUserAccountDisplayName was passed an invalid account!"));
+ return;
+ }
+
+
+ DisplayName = AccountInfo.UserAccountInfo->GetDisplayName();
+}
+
+void UAdvancedIdentityLibrary::GetUserAccountAttribute(const FBPUserOnlineAccount & AccountInfo, const FString & AttributeName, FString & AttributeValue, EBlueprintResultSwitch &Result)
+{
+ if (!AccountInfo.UserAccountInfo.IsValid())
+ {
+ UE_LOG(AdvancedIdentityLog, Warning, TEXT("GetUserAccountAttribute was passed an invalid account!"));
+ Result = EBlueprintResultSwitch::OnFailure;
+ return;
+ }
+
+ if (!AccountInfo.UserAccountInfo->GetUserAttribute(AttributeName, AttributeValue))
+ {
+ UE_LOG(AdvancedIdentityLog, Warning, TEXT("GetUserAccountAttribute failed to get user attribute!"));
+ Result = EBlueprintResultSwitch::OnFailure;
+ return;
+ }
+
+ Result = EBlueprintResultSwitch::OnSuccess;
+}
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/AdvancedSessions.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/AdvancedSessions.cpp
new file mode 100644
index 0000000..8a12271
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/AdvancedSessions.cpp
@@ -0,0 +1,12 @@
+//#include "StandAlonePrivatePCH.h"
+#include "AdvancedSessions.h"
+
+void AdvancedSessions::StartupModule()
+{
+}
+
+void AdvancedSessions::ShutdownModule()
+{
+}
+
+IMPLEMENT_MODULE(AdvancedSessions, AdvancedSessions)
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/AdvancedSessionsLibrary.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/AdvancedSessionsLibrary.cpp
new file mode 100644
index 0000000..9b2b6bf
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/AdvancedSessionsLibrary.cpp
@@ -0,0 +1,547 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+#include "AdvancedSessionsLibrary.h"
+#include "GameFramework/PlayerState.h"
+#include "GameFramework/GameStateBase.h"
+
+//General Log
+DEFINE_LOG_CATEGORY(AdvancedSessionsLog);
+
+
+bool UAdvancedSessionsLibrary::KickPlayer(UObject* WorldContextObject, APlayerController* PlayerToKick, FText KickReason)
+{
+ UWorld* const World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
+
+ if (World)
+ {
+ if (AGameModeBase* GameMode = World->GetAuthGameMode())
+ {
+ if (GameMode->GameSession)
+ {
+ return GameMode->GameSession->KickPlayer(PlayerToKick, KickReason);
+ }
+ }
+ }
+
+ return false;
+}
+
+bool UAdvancedSessionsLibrary::BanPlayer(UObject* WorldContextObject, APlayerController* PlayerToBan, FText BanReason)
+{
+ UWorld* const World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
+
+ if (World)
+ {
+ if (AGameModeBase* GameMode = World->GetAuthGameMode())
+ {
+ if (GameMode->GameSession)
+ {
+ return GameMode->GameSession->BanPlayer(PlayerToBan, BanReason);
+ }
+ }
+ }
+
+ return false;
+}
+
+bool UAdvancedSessionsLibrary::IsValidSession(const FBlueprintSessionResult & SessionResult)
+{
+ return SessionResult.OnlineResult.IsValid();
+}
+
+void UAdvancedSessionsLibrary::GetSessionID_AsString(const FBlueprintSessionResult & SessionResult, FString& SessionID)
+{
+ const TSharedPtr SessionInfo = SessionResult.OnlineResult.Session.SessionInfo;
+ if (SessionInfo.IsValid() && SessionInfo->IsValid() && SessionInfo->GetSessionId().IsValid())
+ {
+ SessionID = SessionInfo->GetSessionId().ToString();
+ return;
+ }
+
+ // Zero the string out if we didn't have a valid one, in case this is called in c++
+ SessionID.Empty();
+}
+
+void UAdvancedSessionsLibrary::GetCurrentSessionID_AsString(UObject* WorldContextObject, FString& SessionID)
+{
+ UWorld* const World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
+ IOnlineSessionPtr SessionInterface = Online::GetSessionInterface(World);
+
+ if (!SessionInterface.IsValid())
+ {
+ UE_LOG(AdvancedSessionsLog, Warning, TEXT("GetCurrentSessionID_AsString couldn't get the session interface!"));
+ SessionID.Empty();
+ return;
+ }
+
+ const FNamedOnlineSession* Session = SessionInterface->GetNamedSession(NAME_GameSession);
+ if (Session != nullptr)
+ {
+ const TSharedPtr SessionInfo = Session->SessionInfo;
+ if (SessionInfo.IsValid() && SessionInfo->IsValid() && SessionInfo->GetSessionId().IsValid())
+ {
+ SessionID = SessionInfo->GetSessionId().ToString();
+ return;
+ }
+ }
+
+ // Zero the string out if we didn't have a valid one, in case this is called in c++
+ SessionID.Empty();
+}
+
+void UAdvancedSessionsLibrary::GetCurrentUniqueBuildID(int32 &UniqueBuildId)
+{
+ UniqueBuildId = GetBuildUniqueId();
+}
+
+void UAdvancedSessionsLibrary::GetUniqueBuildID(FBlueprintSessionResult SessionResult, int32 &UniqueBuildId)
+{
+ UniqueBuildId = SessionResult.OnlineResult.Session.SessionSettings.BuildUniqueId;
+}
+
+FName UAdvancedSessionsLibrary::GetSessionPropertyKey(const FSessionPropertyKeyPair& SessionProperty)
+{
+ return SessionProperty.Key;
+}
+
+void UAdvancedSessionsLibrary::FindSessionPropertyByName(const TArray& ExtraSettings, FName SettingName, EBlueprintResultSwitch &Result, FSessionPropertyKeyPair& OutProperty)
+{
+ const FSessionPropertyKeyPair* prop = ExtraSettings.FindByPredicate([&](const FSessionPropertyKeyPair& it) {return it.Key == SettingName; });
+ if (prop)
+ {
+ Result = EBlueprintResultSwitch::OnSuccess;
+ OutProperty = *prop;
+ return;
+ }
+
+ Result = EBlueprintResultSwitch::OnFailure;
+}
+
+void UAdvancedSessionsLibrary::FindSessionPropertyIndexByName(const TArray& ExtraSettings, FName SettingName, EBlueprintResultSwitch &Result, int32& OutIndex)
+{
+ OutIndex = ExtraSettings.IndexOfByPredicate([&](const FSessionPropertyKeyPair& it) {return it.Key == SettingName; });
+
+ Result = OutIndex != INDEX_NONE ? EBlueprintResultSwitch::OnSuccess : EBlueprintResultSwitch::OnFailure;
+}
+
+void UAdvancedSessionsLibrary::AddOrModifyExtraSettings(UPARAM(ref) TArray & SettingsArray, UPARAM(ref) TArray & NewOrChangedSettings, TArray & ModifiedSettingsArray)
+{
+ ModifiedSettingsArray = SettingsArray;
+
+ bool bFoundSetting = false;
+ // For each new setting
+ for (const FSessionPropertyKeyPair& Setting : NewOrChangedSettings)
+ {
+ bFoundSetting = false;
+
+ for (FSessionPropertyKeyPair & itr : ModifiedSettingsArray)
+ {
+ // Manually comparing the keys
+ if (itr.Key == Setting.Key)
+ {
+ bFoundSetting = true;
+ itr.Data = Setting.Data;
+ }
+ }
+
+ // If it was not found, add to the array instead
+ if (!bFoundSetting)
+ {
+ ModifiedSettingsArray.Add(Setting);
+ }
+ }
+
+}
+
+void UAdvancedSessionsLibrary::GetExtraSettings(FBlueprintSessionResult SessionResult, TArray & ExtraSettings)
+{
+ FSessionPropertyKeyPair NewSetting;
+ for (auto& Elem : SessionResult.OnlineResult.Session.SessionSettings.Settings)
+ {
+ NewSetting.Key = Elem.Key;
+ NewSetting.Data = Elem.Value.Data;
+ ExtraSettings.Add(NewSetting);
+ }
+}
+
+void UAdvancedSessionsLibrary::GetSessionState(UObject* WorldContextObject, EBPOnlineSessionState &SessionState)
+{
+ UWorld* const World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
+ IOnlineSessionPtr SessionInterface = Online::GetSessionInterface(World);
+
+ if (!SessionInterface.IsValid())
+ {
+ UE_LOG(AdvancedSessionsLog, Warning, TEXT("GetSessionState couldn't get the session interface!"));
+ return;
+ }
+
+ SessionState = ((EBPOnlineSessionState)SessionInterface->GetSessionState(NAME_GameSession));
+}
+
+void UAdvancedSessionsLibrary::GetSessionSettings(UObject* WorldContextObject, int32 &NumConnections, int32 &NumPrivateConnections, bool &bIsLAN, bool &bIsDedicated, bool &bAllowInvites, bool &bAllowJoinInProgress, bool &bIsAnticheatEnabled, int32 &BuildUniqueID, TArray &ExtraSettings, EBlueprintResultSwitch &Result)
+{
+ UWorld* const World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
+ IOnlineSessionPtr SessionInterface = Online::GetSessionInterface(World);
+
+ if (!SessionInterface.IsValid())
+ {
+ UE_LOG(AdvancedSessionsLog, Warning, TEXT("GetSessionSettings couldn't get the session interface!"));
+ Result = EBlueprintResultSwitch::OnFailure;
+ return;
+ }
+
+ FOnlineSessionSettings* settings = SessionInterface->GetSessionSettings(NAME_GameSession);
+ if (!settings)
+ {
+ UE_LOG(AdvancedSessionsLog, Warning, TEXT("GetSessionSettings couldn't get the session settings!"));
+ Result = EBlueprintResultSwitch::OnFailure;
+ return;
+ }
+
+ BuildUniqueID = settings->BuildUniqueId;
+ NumConnections = settings->NumPublicConnections;
+ NumPrivateConnections = settings->NumPrivateConnections;
+ bIsLAN = settings->bIsLANMatch;
+ bIsDedicated = settings->bIsDedicated;
+ bIsAnticheatEnabled = settings->bAntiCheatProtected;
+ bAllowInvites = settings->bAllowInvites;
+ bAllowJoinInProgress = settings->bAllowJoinInProgress;
+
+ FSessionPropertyKeyPair NewSetting;
+
+ for (auto& Elem : settings->Settings)
+ {
+ NewSetting.Key = Elem.Key;
+ NewSetting.Data = Elem.Value.Data;
+ ExtraSettings.Add(NewSetting);
+ }
+
+ Result = EBlueprintResultSwitch::OnSuccess;
+}
+
+void UAdvancedSessionsLibrary::IsPlayerInSession(UObject* WorldContextObject, const FBPUniqueNetId &PlayerToCheck, bool &bIsInSession)
+{
+ UWorld* const World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
+ IOnlineSessionPtr SessionInterface = Online::GetSessionInterface(World);
+
+ if (!SessionInterface.IsValid())
+ {
+ UE_LOG(AdvancedSessionsLog, Warning, TEXT("IsPlayerInSession couldn't get the session interface!"));
+ bIsInSession = false;
+ return;
+ }
+
+ bIsInSession = SessionInterface->IsPlayerInSession(NAME_GameSession, *PlayerToCheck.GetUniqueNetId());
+}
+
+FSessionsSearchSetting UAdvancedSessionsLibrary::MakeLiteralSessionSearchProperty(FSessionPropertyKeyPair SessionSearchProperty, EOnlineComparisonOpRedux ComparisonOp)
+{
+ FSessionsSearchSetting setting;
+ setting.PropertyKeyPair = SessionSearchProperty;
+ setting.ComparisonOp = ComparisonOp;
+
+ return setting;
+}
+
+FSessionPropertyKeyPair UAdvancedSessionsLibrary::MakeLiteralSessionPropertyByte(FName Key, uint8 Value)
+{
+ FSessionPropertyKeyPair Prop;
+ Prop.Key = Key;
+ Prop.Data.SetValue((int32)Value);
+ return Prop;
+}
+
+FSessionPropertyKeyPair UAdvancedSessionsLibrary::MakeLiteralSessionPropertyBool(FName Key, bool Value)
+{
+ FSessionPropertyKeyPair Prop;
+ Prop.Key = Key;
+ Prop.Data.SetValue(Value);
+ return Prop;
+}
+
+FSessionPropertyKeyPair UAdvancedSessionsLibrary::MakeLiteralSessionPropertyString(FName Key, FString Value)
+{
+ FSessionPropertyKeyPair Prop;
+ Prop.Key = Key;
+ Prop.Data.SetValue(Value);
+ return Prop;
+}
+
+FSessionPropertyKeyPair UAdvancedSessionsLibrary::MakeLiteralSessionPropertyInt(FName Key, int32 Value)
+{
+ FSessionPropertyKeyPair Prop;
+ Prop.Key = Key;
+ Prop.Data.SetValue(Value);
+ return Prop;
+}
+
+FSessionPropertyKeyPair UAdvancedSessionsLibrary::MakeLiteralSessionPropertyFloat(FName Key, float Value)
+{
+ FSessionPropertyKeyPair Prop;
+ Prop.Key = Key;
+ Prop.Data.SetValue(Value);
+ return Prop;
+}
+
+void UAdvancedSessionsLibrary::GetSessionPropertyByte(const TArray & ExtraSettings, FName SettingName, ESessionSettingSearchResult &SearchResult, uint8 &SettingValue)
+{
+ for (FSessionPropertyKeyPair itr : ExtraSettings)
+ {
+ if (itr.Key == SettingName)
+ {
+ if (itr.Data.GetType() == EOnlineKeyValuePairDataType::Int32)
+ {
+ int32 Val;
+ itr.Data.GetValue(Val);
+ SettingValue = (uint8)(Val);
+ SearchResult = ESessionSettingSearchResult::Found;
+ }
+ else
+ {
+ SearchResult = ESessionSettingSearchResult::WrongType;
+ }
+ return;
+ }
+ }
+
+ SearchResult = ESessionSettingSearchResult::NotFound;
+ return;
+}
+
+void UAdvancedSessionsLibrary::GetSessionPropertyBool(const TArray & ExtraSettings, FName SettingName, ESessionSettingSearchResult &SearchResult, bool &SettingValue)
+{
+ for (FSessionPropertyKeyPair itr : ExtraSettings)
+ {
+ if (itr.Key == SettingName)
+ {
+ if (itr.Data.GetType() == EOnlineKeyValuePairDataType::Bool)
+ {
+ itr.Data.GetValue(SettingValue);
+ SearchResult = ESessionSettingSearchResult::Found;
+ }
+ else
+ {
+ SearchResult = ESessionSettingSearchResult::WrongType;
+ }
+ return;
+ }
+ }
+
+ SearchResult = ESessionSettingSearchResult::NotFound;
+ return;
+}
+
+void UAdvancedSessionsLibrary::GetSessionPropertyString(const TArray & ExtraSettings, FName SettingName, ESessionSettingSearchResult &SearchResult, FString &SettingValue)
+{
+ for (FSessionPropertyKeyPair itr : ExtraSettings)
+ {
+ if (itr.Key == SettingName)
+ {
+ if (itr.Data.GetType() == EOnlineKeyValuePairDataType::String)
+ {
+ itr.Data.GetValue(SettingValue);
+ SearchResult = ESessionSettingSearchResult::Found;
+ }
+ else
+ {
+ SearchResult = ESessionSettingSearchResult::WrongType;
+ }
+ return;
+ }
+ }
+
+ SearchResult = ESessionSettingSearchResult::NotFound;
+ return;
+}
+
+void UAdvancedSessionsLibrary::GetSessionPropertyInt(const TArray & ExtraSettings, FName SettingName, ESessionSettingSearchResult &SearchResult, int32 &SettingValue)
+{
+ for (FSessionPropertyKeyPair itr : ExtraSettings)
+ {
+ if (itr.Key == SettingName)
+ {
+ if (itr.Data.GetType() == EOnlineKeyValuePairDataType::Int32)
+ {
+ itr.Data.GetValue(SettingValue);
+ SearchResult = ESessionSettingSearchResult::Found;
+ }
+ else
+ {
+ SearchResult = ESessionSettingSearchResult::WrongType;
+ }
+ return;
+ }
+ }
+
+ SearchResult = ESessionSettingSearchResult::NotFound;
+ return;
+}
+
+void UAdvancedSessionsLibrary::GetSessionPropertyFloat(const TArray & ExtraSettings, FName SettingName, ESessionSettingSearchResult &SearchResult, float &SettingValue)
+{
+ for (FSessionPropertyKeyPair itr : ExtraSettings)
+ {
+ if (itr.Key == SettingName)
+ {
+ if (itr.Data.GetType() == EOnlineKeyValuePairDataType::Float)
+ {
+ itr.Data.GetValue(SettingValue);
+ SearchResult = ESessionSettingSearchResult::Found;
+ }
+ else
+ {
+ SearchResult = ESessionSettingSearchResult::WrongType;
+ }
+ return;
+ }
+ }
+
+ SearchResult = ESessionSettingSearchResult::NotFound;
+ return;
+}
+
+
+bool UAdvancedSessionsLibrary::HasOnlineSubsystem(FName SubSystemName)
+{
+ return IOnlineSubsystem::DoesInstanceExist(SubSystemName);
+}
+
+void UAdvancedSessionsLibrary::GetNetPlayerIndex(APlayerController *PlayerController, int32 &NetPlayerIndex)
+{
+ if (!PlayerController)
+ {
+ UE_LOG(AdvancedSessionsLog, Warning, TEXT("GetNetPlayerIndex received a bad PlayerController!"));
+ NetPlayerIndex = 0;
+ return;
+ }
+
+ NetPlayerIndex = PlayerController->NetPlayerIndex;
+ return;
+}
+
+void UAdvancedSessionsLibrary::UniqueNetIdToString(const FBPUniqueNetId& UniqueNetId, FString &String)
+{
+ const FUniqueNetId * ID = UniqueNetId.GetUniqueNetId();
+
+ if ( !ID )
+ {
+ UE_LOG(AdvancedSessionsLog, Warning, TEXT("UniqueNetIdToString received a bad UniqueNetId!"));
+ String = "ERROR, BAD UNIQUE NET ID";
+ }
+ else
+ String = ID->ToString();
+}
+
+
+void UAdvancedSessionsLibrary::GetUniqueNetID(APlayerController *PlayerController, FBPUniqueNetId &UniqueNetId)
+{
+ if (!PlayerController)
+ {
+ UE_LOG(AdvancedSessionsLog, Warning, TEXT("GetUniqueNetIdFromController received a bad PlayerController!"));
+ return;
+ }
+
+ if (APlayerState* PlayerState = (PlayerController != NULL) ? PlayerController->PlayerState : NULL)
+ {
+ UniqueNetId.SetUniqueNetId(PlayerState->GetUniqueId().GetUniqueNetId());
+ if (!UniqueNetId.IsValid())
+ {
+ UE_LOG(AdvancedSessionsLog, Warning, TEXT("GetUniqueNetIdFromController couldn't get the player uniquenetid!"));
+ }
+ return;
+ }
+}
+
+void UAdvancedSessionsLibrary::GetUniqueNetIDFromPlayerState(APlayerState *PlayerState, FBPUniqueNetId &UniqueNetId)
+{
+ if (!PlayerState)
+ {
+ UE_LOG(AdvancedSessionsLog, Warning, TEXT("GetUniqueNetIdFromPlayerState received a bad PlayerState!"));
+ return;
+ }
+
+ UniqueNetId.SetUniqueNetId(PlayerState->GetUniqueId().GetUniqueNetId());
+ if (!UniqueNetId.IsValid())
+ {
+ UE_LOG(AdvancedSessionsLog, Warning, TEXT("GetUniqueNetIdFromPlayerState couldn't get the player uniquenetid!"));
+ }
+ return;
+}
+
+bool UAdvancedSessionsLibrary::IsValidUniqueNetID(const FBPUniqueNetId &UniqueNetId)
+{
+ return UniqueNetId.IsValid();
+}
+
+bool UAdvancedSessionsLibrary::EqualEqual_UNetIDUnetID(const FBPUniqueNetId &A, const FBPUniqueNetId &B)
+{
+ return ((A.IsValid() && B.IsValid()) && (*A.GetUniqueNetId() == *B.GetUniqueNetId()));
+}
+
+void UAdvancedSessionsLibrary::SetPlayerName(APlayerController *PlayerController, FString PlayerName)
+{
+ if (!PlayerController)
+ {
+ UE_LOG(AdvancedSessionsLog, Warning, TEXT("SetLocalPlayerNameFromController Bad Player Controller!"));
+ return;
+ }
+
+ if (APlayerState* PlayerState = (PlayerController != NULL) ? PlayerController->PlayerState : NULL)
+ {
+ PlayerState->SetPlayerName(PlayerName);
+ return;
+ }
+ else
+ {
+ UE_LOG(AdvancedSessionsLog, Warning, TEXT("SetLocalPlayerNameFromController had a bad player state!"));
+ }
+}
+
+void UAdvancedSessionsLibrary::GetPlayerName(APlayerController *PlayerController, FString &PlayerName)
+{
+ if (!PlayerController)
+ {
+ UE_LOG(AdvancedSessionsLog, Warning, TEXT("GetLocalPlayerNameFromController Bad Player Controller!"));
+ return;
+ }
+
+ if (APlayerState* PlayerState = (PlayerController != NULL) ? PlayerController->PlayerState : NULL)
+ {
+ PlayerName = PlayerState->GetPlayerName();
+ return;
+ }
+ else
+ {
+ UE_LOG(AdvancedSessionsLog, Warning, TEXT("GetLocalPlayerNameFromController had a bad player state!"));
+ }
+}
+
+void UAdvancedSessionsLibrary::GetNumberOfNetworkPlayers(UObject* WorldContextObject, int32 &NumNetPlayers)
+{
+ //Get World
+ UWorld* TheWorld = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
+
+ if (!TheWorld)
+ {
+ UE_LOG(AdvancedSessionsLog, Warning, TEXT("GetNumberOfNetworkPlayers Failed to get World()!"));
+ return;
+ }
+
+ NumNetPlayers = TheWorld->GetGameState()->PlayerArray.Num();
+}
+
+bool UAdvancedSessionsLibrary::ServerTravel(UObject* WorldContextObject, const FString& FURL, bool bAbsolute, bool bShouldSkipGameNotify)
+{
+ if (!WorldContextObject)
+ {
+ return false;
+ }
+
+ //using a context object to get the world
+ UWorld* const World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::ReturnNull);
+ if (World)
+ {
+ return World->ServerTravel(FURL, bAbsolute, bShouldSkipGameNotify);
+ }
+
+ return false;
+}
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/AdvancedVoiceLibrary.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/AdvancedVoiceLibrary.cpp
new file mode 100644
index 0000000..f491524
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/AdvancedVoiceLibrary.cpp
@@ -0,0 +1,254 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+#include "AdvancedVoiceLibrary.h"
+
+
+//General Log
+DEFINE_LOG_CATEGORY(AdvancedVoiceLog);
+
+void UAdvancedVoiceLibrary::IsHeadsetPresent(bool & bHasHeadset, uint8 LocalPlayerNum)
+{
+ IOnlineVoicePtr VoiceInterface = Online::GetVoiceInterface();
+
+ if (!VoiceInterface.IsValid())
+ {
+ bHasHeadset = false;
+ UE_LOG(AdvancedVoiceLog, Warning, TEXT("Check For Headset couldn't get the voice interface!"));
+ return;
+ }
+
+ bHasHeadset = VoiceInterface->IsHeadsetPresent(LocalPlayerNum);
+}
+
+void UAdvancedVoiceLibrary::StartNetworkedVoice(uint8 LocalPlayerNum)
+{
+ IOnlineVoicePtr VoiceInterface = Online::GetVoiceInterface();
+
+ if (!VoiceInterface.IsValid())
+ {
+ UE_LOG(AdvancedVoiceLog, Warning, TEXT("Start Networked Voice couldn't get the voice interface!"));
+ return;
+ }
+
+ VoiceInterface->StartNetworkedVoice(LocalPlayerNum);
+}
+
+void UAdvancedVoiceLibrary::StopNetworkedVoice(uint8 LocalPlayerNum)
+{
+ IOnlineVoicePtr VoiceInterface = Online::GetVoiceInterface();
+
+ if (!VoiceInterface.IsValid())
+ {
+ UE_LOG(AdvancedVoiceLog, Warning, TEXT("Start Networked Voice couldn't get the voice interface!"));
+ return;
+ }
+
+ VoiceInterface->StopNetworkedVoice(LocalPlayerNum);
+}
+
+bool UAdvancedVoiceLibrary::RegisterLocalTalker(uint8 LocalPlayerNum)
+{
+ IOnlineVoicePtr VoiceInterface = Online::GetVoiceInterface();
+
+ if (!VoiceInterface.IsValid())
+ {
+ UE_LOG(AdvancedVoiceLog, Warning, TEXT("Register Local Talker couldn't get the voice interface!"));
+ return false;
+ }
+
+ return VoiceInterface->RegisterLocalTalker(LocalPlayerNum);
+}
+
+void UAdvancedVoiceLibrary::RegisterAllLocalTalkers()
+{
+ IOnlineVoicePtr VoiceInterface = Online::GetVoiceInterface();
+
+ if (!VoiceInterface.IsValid())
+ {
+ UE_LOG(AdvancedVoiceLog, Warning, TEXT("Register Local Talkers couldn't get the voice interface!"));
+ return;
+ }
+
+ VoiceInterface->RegisterLocalTalkers();
+}
+
+
+void UAdvancedVoiceLibrary::UnRegisterLocalTalker(uint8 LocalPlayerNum)
+{
+ IOnlineVoicePtr VoiceInterface = Online::GetVoiceInterface();
+
+ if (!VoiceInterface.IsValid())
+ {
+ UE_LOG(AdvancedVoiceLog, Warning, TEXT("Unregister Local Talker couldn't get the voice interface!"));
+ return;
+ }
+
+ VoiceInterface->UnregisterLocalTalker(LocalPlayerNum);
+}
+
+void UAdvancedVoiceLibrary::UnRegisterAllLocalTalkers()
+{
+ IOnlineVoicePtr VoiceInterface = Online::GetVoiceInterface();
+
+ if (!VoiceInterface.IsValid())
+ {
+ UE_LOG(AdvancedVoiceLog, Warning, TEXT("UnRegister All Local Talkers couldn't get the voice interface!"));
+ return;
+ }
+
+ VoiceInterface->UnregisterLocalTalkers();
+}
+
+bool UAdvancedVoiceLibrary::RegisterRemoteTalker(const FBPUniqueNetId& UniqueNetId)
+{
+ if (!UniqueNetId.IsValid())
+ {
+ UE_LOG(AdvancedVoiceLog, Warning, TEXT("Register Remote Talker was passed an invalid unique net id!"));
+ return false;
+ }
+
+ IOnlineVoicePtr VoiceInterface = Online::GetVoiceInterface();
+
+ if (!VoiceInterface.IsValid())
+ {
+ UE_LOG(AdvancedVoiceLog, Warning, TEXT("Register Remote Talker couldn't get the voice interface!"));
+ return false;
+ }
+
+ return VoiceInterface->RegisterRemoteTalker(*UniqueNetId.GetUniqueNetId());
+}
+
+bool UAdvancedVoiceLibrary::UnRegisterRemoteTalker(const FBPUniqueNetId& UniqueNetId)
+{
+ if (!UniqueNetId.IsValid())
+ {
+ UE_LOG(AdvancedVoiceLog, Warning, TEXT("UnRegister Remote Talker was passed an invalid unique net id!"));
+ return false;
+ }
+
+ IOnlineVoicePtr VoiceInterface = Online::GetVoiceInterface();
+
+ if (!VoiceInterface.IsValid())
+ {
+ UE_LOG(AdvancedVoiceLog, Warning, TEXT("UnRegister Remote Talker couldn't get the voice interface!"));
+ return false;
+ }
+
+ return VoiceInterface->UnregisterRemoteTalker(*UniqueNetId.GetUniqueNetId());
+}
+
+void UAdvancedVoiceLibrary::RemoveAllRemoteTalkers()
+{
+ IOnlineVoicePtr VoiceInterface = Online::GetVoiceInterface();
+
+ if (!VoiceInterface.IsValid())
+ {
+ UE_LOG(AdvancedVoiceLog, Warning, TEXT("Remove All Remote Talkers couldn't get the voice interface!"));
+ return;
+ }
+
+ VoiceInterface->RemoveAllRemoteTalkers();
+}
+
+bool UAdvancedVoiceLibrary::IsLocalPlayerTalking(uint8 LocalPlayerNum)
+{
+ IOnlineVoicePtr VoiceInterface = Online::GetVoiceInterface();
+
+ if (!VoiceInterface.IsValid())
+ {
+ UE_LOG(AdvancedVoiceLog, Warning, TEXT("Is Local Player Talking couldn't get the voice interface!"));
+ return false;
+ }
+
+ return VoiceInterface->IsLocalPlayerTalking(LocalPlayerNum);
+}
+
+bool UAdvancedVoiceLibrary::IsRemotePlayerTalking(const FBPUniqueNetId& UniqueNetId)
+{
+ if (!UniqueNetId.IsValid())
+ {
+ UE_LOG(AdvancedVoiceLog, Warning, TEXT("Is Remote Player Talking was passed an invalid unique net id!"));
+ return false;
+ }
+
+ IOnlineVoicePtr VoiceInterface = Online::GetVoiceInterface();
+
+ if (!VoiceInterface.IsValid())
+ {
+ UE_LOG(AdvancedVoiceLog, Warning, TEXT("Is Remote Player Talking couldn't get the voice interface!"));
+ return false;
+ }
+
+ return VoiceInterface->IsRemotePlayerTalking(*UniqueNetId.GetUniqueNetId());
+}
+
+bool UAdvancedVoiceLibrary::IsPlayerMuted(uint8 LocalUserNumChecking, const FBPUniqueNetId& UniqueNetId)
+{
+ if (!UniqueNetId.IsValid())
+ {
+ UE_LOG(AdvancedVoiceLog, Warning, TEXT("Is Player Muted was passed an invalid unique net id!"));
+ return false;
+ }
+
+ IOnlineVoicePtr VoiceInterface = Online::GetVoiceInterface();
+
+ if (!VoiceInterface.IsValid())
+ {
+ UE_LOG(AdvancedVoiceLog, Warning, TEXT("Is Player Muted couldn't get the voice interface!"));
+ return false;
+ }
+
+ return VoiceInterface->IsMuted(LocalUserNumChecking, *UniqueNetId.GetUniqueNetId());
+}
+
+bool UAdvancedVoiceLibrary::MuteRemoteTalker(uint8 LocalUserNum, const FBPUniqueNetId& UniqueNetId, bool bIsSystemWide)
+{
+ if (!UniqueNetId.IsValid())
+ {
+ UE_LOG(AdvancedVoiceLog, Warning, TEXT("Mute Remote Talker was passed an invalid unique net id!"));
+ return false;
+ }
+
+ IOnlineVoicePtr VoiceInterface = Online::GetVoiceInterface();
+
+ if (!VoiceInterface.IsValid())
+ {
+ UE_LOG(AdvancedVoiceLog, Warning, TEXT("Mute Remote Talker couldn't get the voice interface!"));
+ return false;
+ }
+
+ return VoiceInterface->MuteRemoteTalker(LocalUserNum, *UniqueNetId.GetUniqueNetId(), bIsSystemWide);
+}
+
+bool UAdvancedVoiceLibrary::UnMuteRemoteTalker(uint8 LocalUserNum, const FBPUniqueNetId& UniqueNetId, bool bIsSystemWide)
+{
+ if (!UniqueNetId.IsValid())
+ {
+ UE_LOG(AdvancedVoiceLog, Warning, TEXT("Unmute Remote Talker was passed an invalid unique net id!"));
+ return false;
+ }
+
+ IOnlineVoicePtr VoiceInterface = Online::GetVoiceInterface();
+
+ if (!VoiceInterface.IsValid())
+ {
+ UE_LOG(AdvancedVoiceLog, Warning, TEXT("Unmute Remote Talker couldn't get the voice interface!"));
+ return false;
+ }
+
+ return VoiceInterface->UnmuteRemoteTalker(LocalUserNum, *UniqueNetId.GetUniqueNetId(), bIsSystemWide);
+}
+
+
+void UAdvancedVoiceLibrary::GetNumLocalTalkers(int32 & NumLocalTalkers)
+{
+ IOnlineVoicePtr VoiceInterface = Online::GetVoiceInterface();
+
+ if (!VoiceInterface.IsValid())
+ {
+ NumLocalTalkers = 0;
+ UE_LOG(AdvancedVoiceLog, Warning, TEXT("Unmute Remote Talker couldn't get the voice interface!"));
+ return;
+ }
+
+ NumLocalTalkers = VoiceInterface->GetNumLocalTalkers();
+}
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/AutoLoginUserCallbackProxy.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/AutoLoginUserCallbackProxy.cpp
new file mode 100644
index 0000000..0c29769
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/AutoLoginUserCallbackProxy.cpp
@@ -0,0 +1,76 @@
+// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
+
+#include "AutoLoginUserCallbackProxy.h"
+#include "Kismet/GameplayStatics.h"
+
+#include "Online.h"
+
+//////////////////////////////////////////////////////////////////////////
+// ULoginUserCallbackProxy
+
+UAutoLoginUserCallbackProxy::UAutoLoginUserCallbackProxy(const FObjectInitializer& ObjectInitializer)
+ : Super(ObjectInitializer)
+ , Delegate(FOnLoginCompleteDelegate::CreateUObject(this, &ThisClass::OnCompleted))
+{
+}
+
+UAutoLoginUserCallbackProxy* UAutoLoginUserCallbackProxy::AutoLoginUser(UObject* WorldContextObject, int32 LocalUserNum)
+{
+ UAutoLoginUserCallbackProxy* Proxy = NewObject();
+ Proxy->LocalUserNumber = LocalUserNum;
+ Proxy->WorldContextObject = WorldContextObject;
+ return Proxy;
+}
+
+void UAutoLoginUserCallbackProxy::Activate()
+{
+ auto Identity = Online::GetIdentityInterface();
+
+ if (Identity.IsValid())
+ {
+ DelegateHandle = Identity->AddOnLoginCompleteDelegate_Handle(LocalUserNumber, Delegate);
+ Identity->AutoLogin(LocalUserNumber);
+ return;
+ }
+
+ // Fail immediately
+ OnFailure.Broadcast();
+}
+
+void UAutoLoginUserCallbackProxy::OnCompleted(int32 LocalUserNum, bool bWasSuccessful, const FUniqueNetId& UserId, const FString& ErrorVal)
+{
+ auto Identity = Online::GetIdentityInterface();
+
+ if (Identity.IsValid())
+ {
+ Identity->ClearOnLoginCompleteDelegate_Handle(LocalUserNum, DelegateHandle);
+ }
+
+ if(APlayerController* PController = UGameplayStatics::GetPlayerController(WorldContextObject->GetWorld(), LocalUserNum))
+ {
+ ULocalPlayer* Player = Cast(PController->Player);
+
+ FUniqueNetIdRepl uniqueId(UserId.AsShared());
+
+ if (Player)
+ {
+ Player->SetCachedUniqueNetId(uniqueId);
+ }
+
+ if (APlayerState* State = PController->PlayerState)
+ {
+ // Update UniqueId. See also ShowLoginUICallbackProxy.cpp
+ State->SetUniqueId(uniqueId);
+ }
+ }
+
+
+ if (bWasSuccessful)
+ {
+ OnSuccess.Broadcast();
+ }
+ else
+ {
+ OnFailure.Broadcast();
+ }
+}
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/CancelFindSessionsCallbackProxy.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/CancelFindSessionsCallbackProxy.cpp
new file mode 100644
index 0000000..7d29f5f
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/CancelFindSessionsCallbackProxy.cpp
@@ -0,0 +1,70 @@
+// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
+#include "CancelFindSessionsCallbackProxy.h"
+
+
+//////////////////////////////////////////////////////////////////////////
+// UCancelFindSessionsCallbackProxy
+
+UCancelFindSessionsCallbackProxy::UCancelFindSessionsCallbackProxy(const FObjectInitializer& ObjectInitializer)
+ : Super(ObjectInitializer)
+ , Delegate(FOnCancelFindSessionsCompleteDelegate::CreateUObject(this, &ThisClass::OnCompleted))
+{
+}
+
+UCancelFindSessionsCallbackProxy* UCancelFindSessionsCallbackProxy::CancelFindSessions(UObject* WorldContextObject, class APlayerController* PlayerController)
+{
+ UCancelFindSessionsCallbackProxy* Proxy = NewObject();
+ Proxy->PlayerControllerWeakPtr = PlayerController;
+ Proxy->WorldContextObject = WorldContextObject;
+ return Proxy;
+}
+
+void UCancelFindSessionsCallbackProxy::Activate()
+{
+ FOnlineSubsystemBPCallHelperAdvanced Helper(TEXT("CancelFindSessions"), GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull));
+ Helper.QueryIDFromPlayerController(PlayerControllerWeakPtr.Get());
+
+ if (Helper.IsValid())
+ {
+ auto Sessions = Helper.OnlineSub->GetSessionInterface();
+ if (Sessions.IsValid())
+ {
+ DelegateHandle = Sessions->AddOnCancelFindSessionsCompleteDelegate_Handle(Delegate);
+ Sessions->CancelFindSessions();
+
+ // OnCompleted will get called, nothing more to do now
+ return;
+ }
+ else
+ {
+ FFrame::KismetExecutionMessage(TEXT("Sessions not supported by Online Subsystem"), ELogVerbosity::Warning);
+ }
+ }
+
+ // Fail immediately
+ OnFailure.Broadcast();
+}
+
+void UCancelFindSessionsCallbackProxy::OnCompleted(bool bWasSuccessful)
+{
+ FOnlineSubsystemBPCallHelperAdvanced Helper(TEXT("CancelFindSessionsCallback"), GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull));
+ Helper.QueryIDFromPlayerController(PlayerControllerWeakPtr.Get());
+
+ if (Helper.IsValid())
+ {
+ auto Sessions = Helper.OnlineSub->GetSessionInterface();
+ if (Sessions.IsValid())
+ {
+ Sessions->ClearOnCancelFindSessionsCompleteDelegate_Handle(DelegateHandle);
+ }
+ }
+
+ if (bWasSuccessful)
+ {
+ OnSuccess.Broadcast();
+ }
+ else
+ {
+ OnFailure.Broadcast();
+ }
+}
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/CreateSessionCallbackProxyAdvanced.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/CreateSessionCallbackProxyAdvanced.cpp
new file mode 100644
index 0000000..71a7d85
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/CreateSessionCallbackProxyAdvanced.cpp
@@ -0,0 +1,182 @@
+// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
+#include "CreateSessionCallbackProxyAdvanced.h"
+
+
+//////////////////////////////////////////////////////////////////////////
+// UCreateSessionCallbackProxyAdvanced
+
+UCreateSessionCallbackProxyAdvanced::UCreateSessionCallbackProxyAdvanced(const FObjectInitializer& ObjectInitializer)
+ : Super(ObjectInitializer)
+ , CreateCompleteDelegate(FOnCreateSessionCompleteDelegate::CreateUObject(this, &ThisClass::OnCreateCompleted))
+ , StartCompleteDelegate(FOnStartSessionCompleteDelegate::CreateUObject(this, &ThisClass::OnStartCompleted))
+ , NumPublicConnections(1)
+{
+}
+
+UCreateSessionCallbackProxyAdvanced* UCreateSessionCallbackProxyAdvanced::CreateAdvancedSession(UObject* WorldContextObject, const TArray& ExtraSettings, class APlayerController* PlayerController, int32 PublicConnections, int32 PrivateConnections, bool bUseLAN, bool bAllowInvites, bool bIsDedicatedServer, bool bUsePresence, bool bUseLobbiesIfAvailable, bool bAllowJoinViaPresence, bool bAllowJoinViaPresenceFriendsOnly, bool bAntiCheatProtected, bool bUsesStats, bool bShouldAdvertise, bool bUseLobbiesVoiceChatIfAvailable, bool bStartAfterCreate)
+{
+ UCreateSessionCallbackProxyAdvanced* Proxy = NewObject();
+ Proxy->PlayerControllerWeakPtr = PlayerController;
+ Proxy->NumPublicConnections = PublicConnections;
+ Proxy->NumPrivateConnections = PrivateConnections;
+ Proxy->bUseLAN = bUseLAN;
+ Proxy->WorldContextObject = WorldContextObject;
+ Proxy->bAllowInvites = bAllowInvites;
+ Proxy->ExtraSettings = ExtraSettings;
+ Proxy->bDedicatedServer = bIsDedicatedServer;
+ Proxy->bUsePresence = bUsePresence;
+ Proxy->bUseLobbiesIfAvailable = bUseLobbiesIfAvailable;
+ Proxy->bAllowJoinViaPresence = bAllowJoinViaPresence;
+ Proxy->bAllowJoinViaPresenceFriendsOnly = bAllowJoinViaPresenceFriendsOnly;
+ Proxy->bAntiCheatProtected = bAntiCheatProtected;
+ Proxy->bUsesStats = bUsesStats;
+ Proxy->bShouldAdvertise = bShouldAdvertise;
+ Proxy->bUseLobbiesVoiceChatIfAvailable = bUseLobbiesVoiceChatIfAvailable;
+ Proxy->bStartAfterCreate = bStartAfterCreate;
+ return Proxy;
+}
+
+void UCreateSessionCallbackProxyAdvanced::Activate()
+{
+ FOnlineSubsystemBPCallHelperAdvanced Helper(TEXT("CreateSession"), GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull));
+
+ if (PlayerControllerWeakPtr.IsValid() )
+ Helper.QueryIDFromPlayerController(PlayerControllerWeakPtr.Get());
+
+ if (Helper.OnlineSub != nullptr)
+ {
+ auto Sessions = Helper.OnlineSub->GetSessionInterface();
+ if (Sessions.IsValid())
+ {
+ CreateCompleteDelegateHandle = Sessions->AddOnCreateSessionCompleteDelegate_Handle(CreateCompleteDelegate);
+
+ FOnlineSessionSettings Settings;
+ Settings.NumPublicConnections = NumPublicConnections;
+ Settings.NumPrivateConnections = NumPrivateConnections;
+ Settings.bShouldAdvertise = bShouldAdvertise;
+ Settings.bAllowJoinInProgress = true;
+ Settings.bIsLANMatch = bUseLAN;
+ Settings.bAllowJoinViaPresence = bAllowJoinViaPresence;
+ Settings.bIsDedicated = bDedicatedServer;
+
+ if (bDedicatedServer)
+ {
+ Settings.bUsesPresence = false;
+ Settings.bUseLobbiesIfAvailable = false;
+ }
+ else
+ {
+ Settings.bUsesPresence = bUsePresence;
+ Settings.bUseLobbiesIfAvailable = bUseLobbiesIfAvailable;
+ }
+
+ Settings.bUseLobbiesVoiceChatIfAvailable = bUseLobbiesIfAvailable ? bUseLobbiesVoiceChatIfAvailable : false;
+ Settings.bAllowJoinViaPresenceFriendsOnly = bAllowJoinViaPresenceFriendsOnly;
+ Settings.bAntiCheatProtected = bAntiCheatProtected;
+ Settings.bUsesStats = bUsesStats;
+
+ // These are about the only changes over the standard Create Sessions Node
+ Settings.bAllowInvites = bAllowInvites;
+
+ FOnlineSessionSetting ExtraSetting;
+ for (int i = 0; i < ExtraSettings.Num(); i++)
+ {
+ ExtraSetting.Data = ExtraSettings[i].Data;
+ // ViaOnlineServiceAndPing
+ ExtraSetting.AdvertisementType = EOnlineDataAdvertisementType::ViaOnlineService;
+ Settings.Settings.Add(ExtraSettings[i].Key, ExtraSetting);
+ }
+
+
+ if (!bDedicatedServer )
+ {
+ if (PlayerControllerWeakPtr.IsValid() && Helper.UserID.IsValid())
+ {
+ Sessions->CreateSession(*Helper.UserID, NAME_GameSession, Settings);
+ }
+ else
+ {
+ FFrame::KismetExecutionMessage(TEXT("Invalid Player controller when attempting to start a session"), ELogVerbosity::Warning);
+ Sessions->ClearOnCreateSessionCompleteDelegate_Handle(CreateCompleteDelegateHandle);
+
+ // Fail immediately
+ OnFailure.Broadcast();
+ }
+ }
+ else
+ Sessions->CreateSession(0, NAME_GameSession, Settings);
+
+ // OnCreateCompleted will get called, nothing more to do now
+ return;
+ }
+ else
+ {
+ FFrame::KismetExecutionMessage(TEXT("Sessions not supported by Online Subsystem"), ELogVerbosity::Warning);
+ }
+ }
+
+ // Fail immediately
+ OnFailure.Broadcast();
+}
+
+void UCreateSessionCallbackProxyAdvanced::OnCreateCompleted(FName SessionName, bool bWasSuccessful)
+{
+ FOnlineSubsystemBPCallHelperAdvanced Helper(TEXT("CreateSessionCallback"), GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull));
+ //Helper.QueryIDFromPlayerController(PlayerControllerWeakPtr.Get());
+
+ if (Helper.OnlineSub != nullptr)
+ {
+ auto Sessions = Helper.OnlineSub->GetSessionInterface();
+ if (Sessions.IsValid())
+ {
+ Sessions->ClearOnCreateSessionCompleteDelegate_Handle(CreateCompleteDelegateHandle);
+
+ if (bWasSuccessful)
+ {
+ if (this->bStartAfterCreate)
+ {
+ UE_LOG_ONLINE_SESSION(Display, TEXT("Session creation completed. Automatic start is turned on, starting session now."));
+ StartCompleteDelegateHandle = Sessions->AddOnStartSessionCompleteDelegate_Handle(StartCompleteDelegate);
+ Sessions->StartSession(NAME_GameSession); // We'll call `OnSuccess.Broadcast()` when start succeeds.
+ }
+ else
+ {
+ UE_LOG_ONLINE_SESSION(Display, TEXT("Session creation completed. Automatic start is turned off, to start the session call 'StartSession'."));
+ OnSuccess.Broadcast();
+ }
+
+ // OnStartCompleted will get called, nothing more to do now
+ return;
+ }
+ }
+ }
+
+ if (!bWasSuccessful)
+ {
+ OnFailure.Broadcast();
+ }
+}
+
+void UCreateSessionCallbackProxyAdvanced::OnStartCompleted(FName SessionName, bool bWasSuccessful)
+{
+ FOnlineSubsystemBPCallHelperAdvanced Helper(TEXT("StartSessionCallback"), GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull));
+ //Helper.QueryIDFromPlayerController(PlayerControllerWeakPtr.Get());
+
+ if (Helper.OnlineSub != nullptr)
+ {
+ auto Sessions = Helper.OnlineSub->GetSessionInterface();
+ if (Sessions.IsValid())
+ {
+ Sessions->ClearOnStartSessionCompleteDelegate_Handle(StartCompleteDelegateHandle);
+ }
+ }
+
+ if (bWasSuccessful)
+ {
+ OnSuccess.Broadcast();
+ }
+ else
+ {
+ OnFailure.Broadcast();
+ }
+}
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/EndSessionCallbackProxy.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/EndSessionCallbackProxy.cpp
new file mode 100644
index 0000000..0276399
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/EndSessionCallbackProxy.cpp
@@ -0,0 +1,78 @@
+// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
+#include "EndSessionCallbackProxy.h"
+
+
+//////////////////////////////////////////////////////////////////////////
+// UEndSessionCallbackProxy
+
+UEndSessionCallbackProxy::UEndSessionCallbackProxy(const FObjectInitializer& ObjectInitializer)
+ : Super(ObjectInitializer)
+ , Delegate(FOnEndSessionCompleteDelegate::CreateUObject(this, &ThisClass::OnCompleted))
+{
+}
+
+UEndSessionCallbackProxy* UEndSessionCallbackProxy::EndSession(UObject* WorldContextObject, class APlayerController* PlayerController)
+{
+ UEndSessionCallbackProxy* Proxy = NewObject();
+ Proxy->PlayerControllerWeakPtr = PlayerController;
+ Proxy->WorldContextObject = WorldContextObject;
+ return Proxy;
+}
+
+void UEndSessionCallbackProxy::Activate()
+{
+ FOnlineSubsystemBPCallHelperAdvanced Helper(TEXT("EndSession"), GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull));
+ Helper.QueryIDFromPlayerController(PlayerControllerWeakPtr.Get());
+
+ if (Helper.IsValid())
+ {
+ auto Sessions = Helper.OnlineSub->GetSessionInterface();
+ if (Sessions.IsValid())
+ {
+ FNamedOnlineSession* Session = Sessions->GetNamedSession(NAME_GameSession);
+ if (Session &&
+ Session->SessionState == EOnlineSessionState::InProgress)
+ {
+ DelegateHandle = Sessions->AddOnEndSessionCompleteDelegate_Handle(Delegate);
+ Sessions->EndSession(NAME_GameSession);
+ }
+ else
+ {
+ OnSuccess.Broadcast();
+ }
+ // OnCompleted will get called, nothing more to do now
+ return;
+ }
+ else
+ {
+ FFrame::KismetExecutionMessage(TEXT("Sessions not supported by Online Subsystem"), ELogVerbosity::Warning);
+ }
+ }
+
+ // Fail immediately
+ OnFailure.Broadcast();
+}
+
+void UEndSessionCallbackProxy::OnCompleted(FName SessionName, bool bWasSuccessful)
+{
+ FOnlineSubsystemBPCallHelperAdvanced Helper(TEXT("EndSessionCallback"), GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull));
+ Helper.QueryIDFromPlayerController(PlayerControllerWeakPtr.Get());
+
+ if (Helper.IsValid())
+ {
+ auto Sessions = Helper.OnlineSub->GetSessionInterface();
+ if (Sessions.IsValid())
+ {
+ Sessions->ClearOnEndSessionCompleteDelegate_Handle(DelegateHandle);
+ }
+ }
+
+ if (bWasSuccessful)
+ {
+ OnSuccess.Broadcast();
+ }
+ else
+ {
+ OnFailure.Broadcast();
+ }
+}
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/FindFriendSessionCallbackProxy.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/FindFriendSessionCallbackProxy.cpp
new file mode 100644
index 0000000..79b25de
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/FindFriendSessionCallbackProxy.cpp
@@ -0,0 +1,107 @@
+// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
+#include "FindFriendSessionCallbackProxy.h"
+
+
+//////////////////////////////////////////////////////////////////////////
+// UGetRecentPlayersCallbackProxy
+DEFINE_LOG_CATEGORY(AdvancedFindFriendSessionLog);
+
+UFindFriendSessionCallbackProxy::UFindFriendSessionCallbackProxy(const FObjectInitializer& ObjectInitializer)
+ : Super(ObjectInitializer)
+ , OnFindFriendSessionCompleteDelegate(FOnFindFriendSessionCompleteDelegate::CreateUObject(this, &ThisClass::OnFindFriendSessionCompleted))
+{
+}
+
+UFindFriendSessionCallbackProxy* UFindFriendSessionCallbackProxy::FindFriendSession(UObject* WorldContextObject, APlayerController *PlayerController, const FBPUniqueNetId &FriendUniqueNetId)
+{
+ UFindFriendSessionCallbackProxy* Proxy = NewObject();
+ Proxy->PlayerControllerWeakPtr = PlayerController;
+ Proxy->cUniqueNetId = FriendUniqueNetId;
+ Proxy->WorldContextObject = WorldContextObject;
+ return Proxy;
+}
+
+void UFindFriendSessionCallbackProxy::Activate()
+{
+ if (!cUniqueNetId.IsValid())
+ {
+ // Fail immediately
+ UE_LOG(AdvancedFindFriendSessionLog, Warning, TEXT("FindFriendSession Failed received a bad UniqueNetId!"));
+ TArray EmptyResult;
+ OnFailure.Broadcast(EmptyResult);
+ return;
+ }
+
+ if (!PlayerControllerWeakPtr.IsValid())
+ {
+ // Fail immediately
+ UE_LOG(AdvancedFindFriendSessionLog, Warning, TEXT("FindFriendSession Failed received a bad playercontroller!"));
+ TArray EmptyResult;
+ OnFailure.Broadcast(EmptyResult);
+ return;
+ }
+
+ IOnlineSessionPtr Sessions = Online::GetSessionInterface(GetWorld());
+
+ if (Sessions.IsValid())
+ {
+ ULocalPlayer* Player = Cast(PlayerControllerWeakPtr->Player);
+
+ if (!Player)
+ {
+ // Fail immediately
+ UE_LOG(AdvancedFindFriendSessionLog, Warning, TEXT("FindFriendSession Failed couldn't cast to ULocalPlayer!"));
+ TArray EmptyResult;
+ OnFailure.Broadcast(EmptyResult);
+ return;
+ }
+
+ FindFriendSessionCompleteDelegateHandle = Sessions->AddOnFindFriendSessionCompleteDelegate_Handle(Player->GetControllerId(), OnFindFriendSessionCompleteDelegate);
+
+ Sessions->FindFriendSession(Player->GetControllerId(), *cUniqueNetId.GetUniqueNetId());
+
+ return;
+ }
+
+ // Fail immediately
+ TArray EmptyResult;
+ OnFailure.Broadcast(EmptyResult);
+}
+
+
+void UFindFriendSessionCallbackProxy::OnFindFriendSessionCompleted(int32 LocalPlayer, bool bWasSuccessful, const TArray& SessionInfo)
+{
+ IOnlineSessionPtr Sessions = Online::GetSessionInterface(GetWorld());
+
+ if (Sessions.IsValid())
+ Sessions->ClearOnFindFriendSessionCompleteDelegate_Handle(LocalPlayer, FindFriendSessionCompleteDelegateHandle);
+
+ if ( bWasSuccessful )
+ {
+ TArray Result;
+
+ for (auto& Sesh : SessionInfo)
+ {
+ if (Sesh.IsValid())
+ {
+ FBlueprintSessionResult BSesh;
+ BSesh.OnlineResult = Sesh;
+ Result.Add(BSesh);
+ }
+ }
+
+ if(Result.Num() > 0)
+ OnSuccess.Broadcast(Result);
+ else
+ {
+ UE_LOG(AdvancedFindFriendSessionLog, Warning, TEXT("FindFriendSession Failed, returned an invalid session."));
+ OnFailure.Broadcast(Result);
+ }
+ }
+ else
+ {
+ UE_LOG(AdvancedFindFriendSessionLog, Warning, TEXT("FindFriendSession Failed"));
+ TArray EmptyResult;
+ OnFailure.Broadcast(EmptyResult);
+ }
+}
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/FindSessionsCallbackProxyAdvanced.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/FindSessionsCallbackProxyAdvanced.cpp
new file mode 100644
index 0000000..56bb813
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/FindSessionsCallbackProxyAdvanced.cpp
@@ -0,0 +1,437 @@
+// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
+#include "FindSessionsCallbackProxyAdvanced.h"
+
+#include "Online/OnlineSessionNames.h"
+
+//////////////////////////////////////////////////////////////////////////
+// UFindSessionsCallbackProxyAdvanced
+
+
+UFindSessionsCallbackProxyAdvanced::UFindSessionsCallbackProxyAdvanced(const FObjectInitializer& ObjectInitializer)
+ : Super(ObjectInitializer)
+ , Delegate(FOnFindSessionsCompleteDelegate::CreateUObject(this, &ThisClass::OnCompleted))
+ , bUseLAN(false)
+{
+ bRunSecondSearch = false;
+ bIsOnSecondSearch = false;
+}
+
+UFindSessionsCallbackProxyAdvanced* UFindSessionsCallbackProxyAdvanced::FindSessionsAdvanced(UObject* WorldContextObject, class APlayerController* PlayerController, int MaxResults, bool bUseLAN, EBPServerPresenceSearchType ServerTypeToSearch, const TArray &Filters, bool bEmptyServersOnly, bool bNonEmptyServersOnly, bool bSecureServersOnly, bool bSearchLobbies, int MinSlotsAvailable)
+{
+ UFindSessionsCallbackProxyAdvanced* Proxy = NewObject();
+ Proxy->PlayerControllerWeakPtr = PlayerController;
+ Proxy->bUseLAN = bUseLAN;
+ Proxy->MaxResults = MaxResults;
+ Proxy->WorldContextObject = WorldContextObject;
+ Proxy->SearchSettings = Filters;
+ Proxy->ServerSearchType = ServerTypeToSearch;
+ Proxy->bEmptyServersOnly = bEmptyServersOnly,
+ Proxy->bNonEmptyServersOnly = bNonEmptyServersOnly;
+ Proxy->bSecureServersOnly = bSecureServersOnly;
+ Proxy->bSearchLobbies = bSearchLobbies;
+ Proxy->MinSlotsAvailable = MinSlotsAvailable;
+ return Proxy;
+}
+
+void UFindSessionsCallbackProxyAdvanced::Activate()
+{
+ FOnlineSubsystemBPCallHelperAdvanced Helper(TEXT("FindSessions"), GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull));
+ Helper.QueryIDFromPlayerController(PlayerControllerWeakPtr.Get());
+
+ if (Helper.IsValid())
+ {
+ auto Sessions = Helper.OnlineSub->GetSessionInterface();
+ if (Sessions.IsValid())
+ {
+ // Re-initialize here, otherwise I think there might be issues with people re-calling search for some reason before it is destroyed
+ bRunSecondSearch = false;
+ bIsOnSecondSearch = false;
+
+ DelegateHandle = Sessions->AddOnFindSessionsCompleteDelegate_Handle(Delegate);
+
+ SearchObject = MakeShareable(new FOnlineSessionSearch);
+ SearchObject->MaxSearchResults = MaxResults;
+ SearchObject->bIsLanQuery = bUseLAN;
+ //SearchObject->QuerySettings.Set(SEARCH_PRESENCE, true, EOnlineComparisonOp::Equals);
+
+ // Create temp filter variable, because I had to re-define a blueprint version of this, it is required.
+ FOnlineSearchSettingsEx tem;
+
+ /* // Search only for dedicated servers (value is true/false)
+ #define SEARCH_DEDICATED_ONLY FName(TEXT("DEDICATEDONLY"))
+ // Search for empty servers only (value is true/false)
+ #define SEARCH_EMPTY_SERVERS_ONLY FName(TEXT("EMPTYONLY"))
+ // Search for non empty servers only (value is true/false)
+ #define SEARCH_NONEMPTY_SERVERS_ONLY FName(TEXT("NONEMPTYONLY"))
+ // Search for secure servers only (value is true/false)
+ #define SEARCH_SECURE_SERVERS_ONLY FName(TEXT("SECUREONLY"))
+ // Search for presence sessions only (value is true/false)
+ #define SEARCH_PRESENCE FName(TEXT("PRESENCESEARCH"))
+ // Search for a match with min player availability (value is int)
+ #define SEARCH_MINSLOTSAVAILABLE FName(TEXT("MINSLOTSAVAILABLE"))
+ // Exclude all matches where any unique ids in a given array are present (value is string of the form "uniqueid1;uniqueid2;uniqueid3")
+ #define SEARCH_EXCLUDE_UNIQUEIDS FName(TEXT("EXCLUDEUNIQUEIDS"))
+ // User ID to search for session of
+ #define SEARCH_USER FName(TEXT("SEARCHUSER"))
+ // Keywords to match in session search
+ #define SEARCH_KEYWORDS FName(TEXT("SEARCHKEYWORDS"))*/
+ /** Keywords to match in session search */
+ /** The matchmaking queue name to matchmake in, e.g. "TeamDeathmatch" (value is string) */
+ /** #define SEARCH_MATCHMAKING_QUEUE FName(TEXT("MATCHMAKINGQUEUE"))*/
+ /** If set, use the named Xbox Live hopper to find a session via matchmaking (value is a string) */
+ /** #define SEARCH_XBOX_LIVE_HOPPER_NAME FName(TEXT("LIVEHOPPERNAME"))*/
+ /** Which session template from the service configuration to use */
+ /** #define SEARCH_XBOX_LIVE_SESSION_TEMPLATE_NAME FName(TEXT("LIVESESSIONTEMPLATE"))*/
+ /** Selection method used to determine which match to join when multiple are returned (valid only on Switch) */
+ /** #define SEARCH_SWITCH_SELECTION_METHOD FName(TEXT("SWITCHSELECTIONMETHOD"))*/
+ /** Whether to use lobbies vs sessions */
+ /** #define SEARCH_LOBBIES FName(TEXT("LOBBYSEARCH"))*/
+
+ if (bEmptyServersOnly)
+ tem.Set(SEARCH_EMPTY_SERVERS_ONLY, true, EOnlineComparisonOp::Equals);
+
+ if (bNonEmptyServersOnly)
+ tem.Set(SEARCH_NONEMPTY_SERVERS_ONLY, true, EOnlineComparisonOp::Equals);
+
+ if (bSecureServersOnly)
+ tem.Set(SEARCH_SECURE_SERVERS_ONLY, true, EOnlineComparisonOp::Equals);
+
+ if (MinSlotsAvailable != 0)
+ tem.Set(SEARCH_MINSLOTSAVAILABLE, MinSlotsAvailable, EOnlineComparisonOp::GreaterThanEquals);
+
+ // Filter results
+ if (SearchSettings.Num() > 0)
+ {
+ for (int i = 0; i < SearchSettings.Num(); i++)
+ {
+ // Function that was added to make directly adding a FVariant possible
+ tem.HardSet(SearchSettings[i].PropertyKeyPair.Key, SearchSettings[i].PropertyKeyPair.Data, SearchSettings[i].ComparisonOp);
+ }
+ }
+
+ switch (ServerSearchType)
+ {
+
+ case EBPServerPresenceSearchType::ClientServersOnly:
+ {
+ tem.Set(SEARCH_PRESENCE, true, EOnlineComparisonOp::Equals);
+
+ if (bSearchLobbies)
+ tem.Set(SEARCH_LOBBIES, true, EOnlineComparisonOp::Equals);
+ }
+ break;
+
+ case EBPServerPresenceSearchType::DedicatedServersOnly:
+ {
+ //tem.Set(SEARCH_DEDICATED_ONLY, true, EOnlineComparisonOp::Equals);
+ }
+ break;
+
+ case EBPServerPresenceSearchType::AllServers:
+ default:
+ {
+ //if (IOnlineSubsystem::DoesInstanceExist("STEAM"))
+ //{
+ bRunSecondSearch = true;
+
+ SearchObjectDedicated = MakeShareable(new FOnlineSessionSearch);
+ SearchObjectDedicated->MaxSearchResults = MaxResults;
+ SearchObjectDedicated->bIsLanQuery = bUseLAN;
+
+ FOnlineSearchSettingsEx DedicatedOnly = tem;
+
+ tem.Set(SEARCH_PRESENCE, true, EOnlineComparisonOp::Equals);
+
+ if (bSearchLobbies)
+ tem.Set(SEARCH_LOBBIES, true, EOnlineComparisonOp::Equals);
+
+ //DedicatedOnly.Set(SEARCH_DEDICATED_ONLY, true, EOnlineComparisonOp::Equals);
+ SearchObjectDedicated->QuerySettings = DedicatedOnly;
+ //}
+ }
+ break;
+ }
+
+ // Copy the derived temp variable over to it's base class
+ SearchObject->QuerySettings = tem;
+
+ Sessions->FindSessions(*Helper.UserID, SearchObject.ToSharedRef());
+
+ // OnQueryCompleted will get called, nothing more to do now
+ return;
+ }
+ else
+ {
+ FFrame::KismetExecutionMessage(TEXT("Sessions not supported by Online Subsystem"), ELogVerbosity::Warning);
+ }
+ }
+
+ // Fail immediately
+ OnFailure.Broadcast(SessionSearchResults);
+}
+
+void UFindSessionsCallbackProxyAdvanced::OnCompleted(bool bSuccess)
+{
+ FOnlineSubsystemBPCallHelperAdvanced Helper(TEXT("FindSessionsCallback"), GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull));
+ Helper.QueryIDFromPlayerController(PlayerControllerWeakPtr.Get());
+
+ if (!bRunSecondSearch && Helper.IsValid())
+ {
+ auto Sessions = Helper.OnlineSub->GetSessionInterface();
+ if (Sessions.IsValid())
+ {
+ Sessions->ClearOnFindSessionsCompleteDelegate_Handle(DelegateHandle);
+ }
+ }
+
+ if (bSuccess)
+ {
+ if (bIsOnSecondSearch)
+ {
+ if (SearchObjectDedicated.IsValid())
+ {
+ // Just log the results for now, will need to add a blueprint-compatible search result struct
+ for (auto& Result : SearchObjectDedicated->SearchResults)
+ {
+ FString ResultText = FString::Printf(TEXT("Found a session. Ping is %d"), Result.PingInMs);
+
+ FFrame::KismetExecutionMessage(*ResultText, ELogVerbosity::Log);
+
+ FBlueprintSessionResult BPResult;
+ BPResult.OnlineResult = Result;
+ SessionSearchResults.AddUnique(BPResult);
+ }
+ OnSuccess.Broadcast(SessionSearchResults);
+ return;
+ }
+ }
+ else
+ {
+ if (SearchObject.IsValid())
+ {
+ // Just log the results for now, will need to add a blueprint-compatible search result struct
+ for (auto& Result : SearchObject->SearchResults)
+ {
+ FString ResultText = FString::Printf(TEXT("Found a session. Ping is %d"), Result.PingInMs);
+
+ FFrame::KismetExecutionMessage(*ResultText, ELogVerbosity::Log);
+
+ FBlueprintSessionResult BPResult;
+ BPResult.OnlineResult = Result;
+ SessionSearchResults.AddUnique(BPResult);
+ }
+ if (!bRunSecondSearch)
+ {
+ OnSuccess.Broadcast(SessionSearchResults);
+ return;
+ }
+ }
+ }
+ }
+ else
+ {
+ if (!bRunSecondSearch)
+ {
+ // Need to account for only one of the searches failing
+ if (SessionSearchResults.Num() > 0)
+ OnSuccess.Broadcast(SessionSearchResults);
+ else
+ OnFailure.Broadcast(SessionSearchResults);
+ return;
+ }
+ }
+
+ if (Helper.IsValid() && bRunSecondSearch && ServerSearchType == EBPServerPresenceSearchType::AllServers)
+ {
+ bRunSecondSearch = false;
+ bIsOnSecondSearch = true;
+ auto Sessions = Helper.OnlineSub->GetSessionInterface();
+ Sessions->FindSessions(*Helper.UserID, SearchObjectDedicated.ToSharedRef());
+ }
+ else // We lost our player controller
+ {
+ if (bSuccess && SessionSearchResults.Num() > 0)
+ OnSuccess.Broadcast(SessionSearchResults);
+ else
+ OnFailure.Broadcast(SessionSearchResults);
+ }
+}
+
+
+void UFindSessionsCallbackProxyAdvanced::FilterSessionResults(const TArray &SessionResults, const TArray &Filters, TArray &FilteredResults)
+{
+ for (int j = 0; j < SessionResults.Num(); j++)
+ {
+ bool bAddResult = true;
+
+ // Filter results
+ if (Filters.Num() > 0)
+ {
+ const FOnlineSessionSetting * setting;
+ for (int i = 0; i < Filters.Num(); i++)
+ {
+ setting = SessionResults[j].OnlineResult.Session.SessionSettings.Settings.Find(Filters[i].PropertyKeyPair.Key);
+
+ // Couldn't find this key
+ if (!setting)
+ continue;
+
+ if (!CompareVariants(setting->Data, Filters[i].PropertyKeyPair.Data, Filters[i].ComparisonOp))
+ {
+ bAddResult = false;
+ break;
+ }
+ }
+ }
+
+ if (bAddResult)
+ FilteredResults.Add(SessionResults[j]);
+ }
+
+ return;
+}
+
+
+bool UFindSessionsCallbackProxyAdvanced::CompareVariants(const FVariantData &A, const FVariantData &B, EOnlineComparisonOpRedux Comparator)
+{
+ if (A.GetType() != B.GetType())
+ return false;
+
+ switch (A.GetType())
+ {
+ case EOnlineKeyValuePairDataType::Bool:
+ {
+ bool bA, bB;
+ A.GetValue(bA);
+ B.GetValue(bB);
+ switch (Comparator)
+ {
+ case EOnlineComparisonOpRedux::Equals:
+ return bA == bB; break;
+ case EOnlineComparisonOpRedux::NotEquals:
+ return bA != bB; break;
+ default:
+ return false;break;
+ }
+ }
+ case EOnlineKeyValuePairDataType::Double:
+ {
+ double bA, bB;
+ A.GetValue(bA);
+ B.GetValue(bB);
+ switch (Comparator)
+ {
+ case EOnlineComparisonOpRedux::Equals:
+ return bA == bB; break;
+ case EOnlineComparisonOpRedux::NotEquals:
+ return bA != bB; break;
+ case EOnlineComparisonOpRedux::GreaterThanEquals:
+ return (bA == bB || bA > bB); break;
+ case EOnlineComparisonOpRedux::LessThanEquals:
+ return (bA == bB || bA < bB); break;
+ case EOnlineComparisonOpRedux::GreaterThan:
+ return bA > bB; break;
+ case EOnlineComparisonOpRedux::LessThan:
+ return bA < bB; break;
+ default:
+ return false; break;
+ }
+ }
+ case EOnlineKeyValuePairDataType::Float:
+ {
+ float tbA, tbB;
+ double bA, bB;
+ A.GetValue(tbA);
+ B.GetValue(tbB);
+ bA = (double)tbA;
+ bB = (double)tbB;
+ switch (Comparator)
+ {
+ case EOnlineComparisonOpRedux::Equals:
+ return bA == bB; break;
+ case EOnlineComparisonOpRedux::NotEquals:
+ return bA != bB; break;
+ case EOnlineComparisonOpRedux::GreaterThanEquals:
+ return (bA == bB || bA > bB); break;
+ case EOnlineComparisonOpRedux::LessThanEquals:
+ return (bA == bB || bA < bB); break;
+ case EOnlineComparisonOpRedux::GreaterThan:
+ return bA > bB; break;
+ case EOnlineComparisonOpRedux::LessThan:
+ return bA < bB; break;
+ default:
+ return false; break;
+ }
+ }
+ case EOnlineKeyValuePairDataType::Int32:
+ {
+ int32 bA, bB;
+ A.GetValue(bA);
+ B.GetValue(bB);
+ switch (Comparator)
+ {
+ case EOnlineComparisonOpRedux::Equals:
+ return bA == bB; break;
+ case EOnlineComparisonOpRedux::NotEquals:
+ return bA != bB; break;
+ case EOnlineComparisonOpRedux::GreaterThanEquals:
+ return (bA == bB || bA > bB); break;
+ case EOnlineComparisonOpRedux::LessThanEquals:
+ return (bA == bB || bA < bB); break;
+ case EOnlineComparisonOpRedux::GreaterThan:
+ return bA > bB; break;
+ case EOnlineComparisonOpRedux::LessThan:
+ return bA < bB; break;
+ default:
+ return false; break;
+ }
+ }
+ case EOnlineKeyValuePairDataType::Int64:
+ {
+ uint64 bA, bB;
+ A.GetValue(bA);
+ B.GetValue(bB);
+ switch (Comparator)
+ {
+ case EOnlineComparisonOpRedux::Equals:
+ return bA == bB; break;
+ case EOnlineComparisonOpRedux::NotEquals:
+ return bA != bB; break;
+ case EOnlineComparisonOpRedux::GreaterThanEquals:
+ return (bA == bB || bA > bB); break;
+ case EOnlineComparisonOpRedux::LessThanEquals:
+ return (bA == bB || bA < bB); break;
+ case EOnlineComparisonOpRedux::GreaterThan:
+ return bA > bB; break;
+ case EOnlineComparisonOpRedux::LessThan:
+ return bA < bB; break;
+ default:
+ return false; break;
+ }
+ }
+
+ case EOnlineKeyValuePairDataType::String:
+ {
+ FString bA, bB;
+ A.GetValue(bA);
+ B.GetValue(bB);
+ switch (Comparator)
+ {
+ case EOnlineComparisonOpRedux::Equals:
+ return bA == bB; break;
+ case EOnlineComparisonOpRedux::NotEquals:
+ return bA != bB; break;
+ default:
+ return false; break;
+ }
+ }
+
+ case EOnlineKeyValuePairDataType::Empty:
+ case EOnlineKeyValuePairDataType::Blob:
+ default:
+ return false; break;
+ }
+
+
+
+}
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/GetFriendsCallbackProxy.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/GetFriendsCallbackProxy.cpp
new file mode 100644
index 0000000..80fc481
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/GetFriendsCallbackProxy.cpp
@@ -0,0 +1,96 @@
+// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
+#include "GetFriendsCallbackProxy.h"
+
+#include "Online.h"
+#include "Interfaces/OnlinePresenceInterface.h"
+
+//////////////////////////////////////////////////////////////////////////
+// UGetFriendsCallbackProxy
+DEFINE_LOG_CATEGORY(AdvancedGetFriendsLog);
+
+UGetFriendsCallbackProxy::UGetFriendsCallbackProxy(const FObjectInitializer& ObjectInitializer)
+ : Super(ObjectInitializer)
+ , FriendListReadCompleteDelegate(FOnReadFriendsListComplete::CreateUObject(this, &ThisClass::OnReadFriendsListCompleted))
+{
+}
+
+UGetFriendsCallbackProxy* UGetFriendsCallbackProxy::GetAndStoreFriendsList(UObject* WorldContextObject, class APlayerController* PlayerController)
+{
+ UGetFriendsCallbackProxy* Proxy = NewObject();
+ Proxy->PlayerControllerWeakPtr = PlayerController;
+ Proxy->WorldContextObject = WorldContextObject;
+ return Proxy;
+}
+
+void UGetFriendsCallbackProxy::Activate()
+{
+ if (!PlayerControllerWeakPtr.IsValid())
+ {
+ // Fail immediately
+ UE_LOG(AdvancedGetFriendsLog, Warning, TEXT("GetFriends Failed received a bad player controller!"));
+ TArray EmptyArray;
+ OnFailure.Broadcast(EmptyArray);
+ return;
+ }
+
+ IOnlineFriendsPtr Friends = Online::GetFriendsInterface();
+ if (Friends.IsValid())
+ {
+ ULocalPlayer* Player = Cast(PlayerControllerWeakPtr->Player);
+
+ Friends->ReadFriendsList(Player->GetControllerId(), EFriendsLists::ToString((EFriendsLists::Default)), FriendListReadCompleteDelegate);
+ return;
+ }
+
+ // Fail immediately
+ TArray EmptyArray;
+
+ OnFailure.Broadcast(EmptyArray);
+}
+
+void UGetFriendsCallbackProxy::OnReadFriendsListCompleted(int32 LocalUserNum, bool bWasSuccessful, const FString& ListName, const FString& ErrorString)
+{
+ if (bWasSuccessful)
+ {
+ IOnlineFriendsPtr Friends = Online::GetFriendsInterface();
+ if (Friends.IsValid())
+ {
+ // Not actually needed anymore, plus was not being validated and causing a crash
+ //ULocalPlayer* Player = Cast(PlayerControllerWeakPtr->Player);
+
+ TArray FriendsListOut;
+ TArray< TSharedRef > FriendList;
+ Friends->GetFriendsList(LocalUserNum, ListName, FriendList);
+
+ for (int32 i = 0; i < FriendList.Num(); i++)
+ {
+ TSharedRef Friend = FriendList[i];
+ FBPFriendInfo BPF;
+ FOnlineUserPresence pres = Friend->GetPresence();
+ BPF.OnlineState = ((EBPOnlinePresenceState)((int32)pres.Status.State));
+ BPF.DisplayName = Friend->GetDisplayName();
+ BPF.RealName = Friend->GetRealName();
+ BPF.UniqueNetId.SetUniqueNetId(Friend->GetUserId());
+ BPF.bIsPlayingSameGame = pres.bIsPlayingThisGame;
+
+ BPF.PresenceInfo.bIsOnline = pres.bIsOnline;
+ BPF.PresenceInfo.bHasVoiceSupport = pres.bHasVoiceSupport;
+ BPF.PresenceInfo.bIsPlaying = pres.bIsPlaying;
+ BPF.PresenceInfo.PresenceState = ((EBPOnlinePresenceState)((int32)pres.Status.State));
+ BPF.PresenceInfo.StatusString = pres.Status.StatusStr;
+ BPF.PresenceInfo.bIsJoinable = pres.bIsJoinable;
+ BPF.PresenceInfo.bIsPlayingThisGame = pres.bIsPlayingThisGame;
+
+
+ FriendsListOut.Add(BPF);
+ }
+
+ OnSuccess.Broadcast(FriendsListOut);
+ }
+ }
+ else
+ {
+ TArray EmptyArray;
+ OnFailure.Broadcast(EmptyArray);
+ }
+}
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/GetRecentPlayersCallbackProxy.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/GetRecentPlayersCallbackProxy.cpp
new file mode 100644
index 0000000..8eb3a3d
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/GetRecentPlayersCallbackProxy.cpp
@@ -0,0 +1,86 @@
+// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
+#include "GetRecentPlayersCallbackProxy.h"
+
+#include "Online.h"
+
+//////////////////////////////////////////////////////////////////////////
+// UGetRecentPlayersCallbackProxy
+DEFINE_LOG_CATEGORY(AdvancedGetRecentPlayersLog);
+
+UGetRecentPlayersCallbackProxy::UGetRecentPlayersCallbackProxy(const FObjectInitializer& ObjectInitializer)
+ : Super(ObjectInitializer)
+ , QueryRecentPlayersCompleteDelegate(FOnQueryRecentPlayersCompleteDelegate::CreateUObject(this, &ThisClass::OnQueryRecentPlayersCompleted))
+{
+}
+
+UGetRecentPlayersCallbackProxy* UGetRecentPlayersCallbackProxy::GetAndStoreRecentPlayersList(UObject* WorldContextObject, const FBPUniqueNetId& UniqueNetId)
+{
+ UGetRecentPlayersCallbackProxy* Proxy = NewObject();
+ Proxy->cUniqueNetId = UniqueNetId;
+ Proxy->WorldContextObject = WorldContextObject;
+ return Proxy;
+}
+
+void UGetRecentPlayersCallbackProxy::Activate()
+{
+ if (!cUniqueNetId.IsValid())
+ {
+ // Fail immediately
+ UE_LOG(AdvancedGetRecentPlayersLog, Warning, TEXT("GetRecentPlayers Failed received a bad UniqueNetId!"));
+ TArray EmptyArray;
+ OnFailure.Broadcast(EmptyArray);
+ return;
+ }
+
+ IOnlineFriendsPtr Friends = Online::GetFriendsInterface();
+ if (Friends.IsValid())
+ {
+ DelegateHandle = Friends->AddOnQueryRecentPlayersCompleteDelegate_Handle(QueryRecentPlayersCompleteDelegate);
+
+ // Testing with null namespace
+ Friends->QueryRecentPlayers(*(cUniqueNetId.GetUniqueNetId()), "");
+ return;
+ }
+ // Fail immediately
+ TArray EmptyArray;
+ OnFailure.Broadcast(EmptyArray);
+}
+
+void UGetRecentPlayersCallbackProxy::OnQueryRecentPlayersCompleted(const FUniqueNetId &UserID, const FString &Namespace, bool bWasSuccessful, const FString& ErrorString)
+{
+
+ IOnlineFriendsPtr Friends = Online::GetFriendsInterface();
+ if (Friends.IsValid())
+ Friends->ClearOnQueryRecentPlayersCompleteDelegate_Handle(DelegateHandle);
+
+
+ if (bWasSuccessful)
+ {
+ // WHOOPS
+ //IOnlineFriendsPtr Friends = Online::GetFriendsInterface();
+ if (Friends.IsValid())
+ {
+ TArray PlayersListOut;
+ TArray< TSharedRef > PlayerList;
+
+ Friends->GetRecentPlayers(*(cUniqueNetId.GetUniqueNetId()), "", PlayerList);
+
+ for (int32 i = 0; i < PlayerList.Num(); i++)
+ {
+ TSharedRef Player = PlayerList[i];
+ FBPOnlineRecentPlayer BPF;
+ BPF.DisplayName = Player->GetDisplayName();
+ BPF.RealName = Player->GetRealName();
+ BPF.UniqueNetId.SetUniqueNetId(Player->GetUserId());
+ PlayersListOut.Add(BPF);
+ }
+
+ OnSuccess.Broadcast(PlayersListOut);
+ }
+ }
+ else
+ {
+ TArray EmptyArray;
+ OnFailure.Broadcast(EmptyArray);
+ }
+}
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/GetUserPrivilegeCallbackProxy.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/GetUserPrivilegeCallbackProxy.cpp
new file mode 100644
index 0000000..cec618f
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/GetUserPrivilegeCallbackProxy.cpp
@@ -0,0 +1,41 @@
+// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
+
+#include "GetUserPrivilegeCallbackProxy.h"
+
+#include "Online.h"
+
+//////////////////////////////////////////////////////////////////////////
+// UGetUserPrivilegeCallbackProxy
+
+UGetUserPrivilegeCallbackProxy::UGetUserPrivilegeCallbackProxy(const FObjectInitializer& ObjectInitializer)
+ : Super(ObjectInitializer)
+{
+}
+
+UGetUserPrivilegeCallbackProxy* UGetUserPrivilegeCallbackProxy::GetUserPrivilege(UObject* WorldContextObject, const EBPUserPrivileges & PrivilegeToCheck, const FBPUniqueNetId & PlayerUniqueNetID)
+{
+ UGetUserPrivilegeCallbackProxy* Proxy = NewObject();
+ Proxy->PlayerUniqueNetID.SetUniqueNetId(PlayerUniqueNetID.GetUniqueNetId());
+ Proxy->UserPrivilege = PrivilegeToCheck;
+ Proxy->WorldContextObject = WorldContextObject;
+ return Proxy;
+}
+
+void UGetUserPrivilegeCallbackProxy::Activate()
+{
+ auto Identity = Online::GetIdentityInterface();
+
+ if (Identity.IsValid())
+ {
+ Identity->GetUserPrivilege(*PlayerUniqueNetID.GetUniqueNetId(), (EUserPrivileges::Type)UserPrivilege, IOnlineIdentity::FOnGetUserPrivilegeCompleteDelegate::CreateUObject(this, &ThisClass::OnCompleted));
+ return;
+ }
+
+ // Fail immediately
+ OnFailure.Broadcast();
+}
+
+void UGetUserPrivilegeCallbackProxy::OnCompleted(const FUniqueNetId& PlayerID, EUserPrivileges::Type Privilege, uint32 PrivilegeResult)
+{
+ OnSuccess.Broadcast(/*PlayerID,*/ (EBPUserPrivileges)Privilege, PrivilegeResult == 0);
+}
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/LoginUserCallbackProxy.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/LoginUserCallbackProxy.cpp
new file mode 100644
index 0000000..3bb211d
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/LoginUserCallbackProxy.cpp
@@ -0,0 +1,97 @@
+// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
+
+#include "LoginUserCallbackProxy.h"
+
+#include "Online.h"
+
+//////////////////////////////////////////////////////////////////////////
+// ULoginUserCallbackProxy
+
+ULoginUserCallbackProxy::ULoginUserCallbackProxy(const FObjectInitializer& ObjectInitializer)
+ : Super(ObjectInitializer)
+ , Delegate(FOnLoginCompleteDelegate::CreateUObject(this, &ThisClass::OnCompleted))
+{
+}
+
+ULoginUserCallbackProxy* ULoginUserCallbackProxy::LoginUser(UObject* WorldContextObject, class APlayerController* PlayerController, FString UserID, FString UserToken, FString AuthType)
+{
+ ULoginUserCallbackProxy* Proxy = NewObject();
+ Proxy->PlayerControllerWeakPtr = PlayerController;
+ Proxy->UserID = UserID;
+ Proxy->UserToken = UserToken;
+ Proxy->AuthType = AuthType;
+ Proxy->WorldContextObject = WorldContextObject;
+ return Proxy;
+}
+
+void ULoginUserCallbackProxy::Activate()
+{
+
+ if (!PlayerControllerWeakPtr.IsValid())
+ {
+ OnFailure.Broadcast();
+ return;
+ }
+
+ ULocalPlayer* Player = Cast(PlayerControllerWeakPtr->Player);
+
+ if (!Player)
+ {
+ OnFailure.Broadcast();
+ return;
+ }
+
+ auto Identity = Online::GetIdentityInterface();
+
+ if (Identity.IsValid())
+ {
+ // Fallback to default AuthType if nothing is specified
+ if (AuthType.IsEmpty())
+ {
+ AuthType = Identity->GetAuthType();
+ }
+ DelegateHandle = Identity->AddOnLoginCompleteDelegate_Handle(Player->GetControllerId(), Delegate);
+ FOnlineAccountCredentials AccountCreds(AuthType, UserID, UserToken);
+ Identity->Login(Player->GetControllerId(), AccountCreds);
+ return;
+ }
+
+ // Fail immediately
+ OnFailure.Broadcast();
+}
+
+void ULoginUserCallbackProxy::OnCompleted(int32 LocalUserNum, bool bWasSuccessful, const FUniqueNetId& UserId, const FString& ErrorVal)
+{
+ if (PlayerControllerWeakPtr.IsValid())
+ {
+ ULocalPlayer* Player = Cast(PlayerControllerWeakPtr->Player);
+
+ FUniqueNetIdRepl UniqueID(UserId.AsShared());
+
+ if (Player)
+ {
+ auto Identity = Online::GetIdentityInterface();
+
+ if (Identity.IsValid())
+ {
+ Identity->ClearOnLoginCompleteDelegate_Handle(Player->GetControllerId(), DelegateHandle);
+ }
+ Player->SetCachedUniqueNetId(UniqueID);
+ }
+
+ if (APlayerState* State = PlayerControllerWeakPtr->PlayerState)
+ {
+ // Update UniqueId. See also ShowLoginUICallbackProxy.cpp
+ State->SetUniqueId(UniqueID);
+ }
+ }
+
+ if (bWasSuccessful)
+ {
+ OnSuccess.Broadcast();
+ }
+ else
+ {
+ OnFailure.Broadcast();
+ }
+}
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/LogoutUserCallbackProxy.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/LogoutUserCallbackProxy.cpp
new file mode 100644
index 0000000..29b1b53
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/LogoutUserCallbackProxy.cpp
@@ -0,0 +1,81 @@
+// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
+
+#include "LogoutUserCallbackProxy.h"
+
+#include "Online.h"
+
+//////////////////////////////////////////////////////////////////////////
+// ULogoutUserCallbackProxy
+
+ULogoutUserCallbackProxy::ULogoutUserCallbackProxy(const FObjectInitializer& ObjectInitializer)
+ : Super(ObjectInitializer)
+ , Delegate(FOnLogoutCompleteDelegate::CreateUObject(this, &ThisClass::OnCompleted))
+{
+}
+
+ULogoutUserCallbackProxy* ULogoutUserCallbackProxy::LogoutUser(UObject* WorldContextObject, class APlayerController* PlayerController)
+{
+ ULogoutUserCallbackProxy* Proxy = NewObject();
+ Proxy->PlayerControllerWeakPtr = PlayerController;
+ Proxy->WorldContextObject = WorldContextObject;
+ return Proxy;
+}
+
+void ULogoutUserCallbackProxy::Activate()
+{
+
+ if (!PlayerControllerWeakPtr.IsValid())
+ {
+ OnFailure.Broadcast();
+ return;
+ }
+
+
+ ULocalPlayer* Player = Cast(PlayerControllerWeakPtr->Player);
+
+ if (!Player)
+ {
+ OnFailure.Broadcast();
+ return;
+ }
+
+ auto Identity = Online::GetIdentityInterface();
+
+ if (Identity.IsValid())
+ {
+ DelegateHandle = Identity->AddOnLogoutCompleteDelegate_Handle(Player->GetControllerId(), Delegate);
+ Identity->Logout(Player->GetControllerId());
+ return;
+ }
+
+ // Fail immediately
+ OnFailure.Broadcast();
+}
+
+void ULogoutUserCallbackProxy::OnCompleted(int LocalUserNum, bool bWasSuccessful)
+{
+
+ if (PlayerControllerWeakPtr.IsValid())
+ {
+ ULocalPlayer* Player = Cast(PlayerControllerWeakPtr->Player);
+
+ if (Player)
+ {
+ auto Identity = Online::GetIdentityInterface();
+
+ if (Identity.IsValid())
+ {
+ Identity->ClearOnLogoutCompleteDelegate_Handle(Player->GetControllerId(), DelegateHandle);
+ }
+ }
+ }
+
+ if (bWasSuccessful)
+ {
+ OnSuccess.Broadcast();
+ }
+ else
+ {
+ OnFailure.Broadcast();
+ }
+}
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/SendFriendInviteCallbackProxy.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/SendFriendInviteCallbackProxy.cpp
new file mode 100644
index 0000000..71fed1e
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/SendFriendInviteCallbackProxy.cpp
@@ -0,0 +1,74 @@
+// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
+#include "SendFriendInviteCallbackProxy.h"
+
+#include "Online.h"
+
+//////////////////////////////////////////////////////////////////////////
+// UGetRecentPlayersCallbackProxy
+DEFINE_LOG_CATEGORY(AdvancedSendFriendInviteLog);
+
+USendFriendInviteCallbackProxy::USendFriendInviteCallbackProxy(const FObjectInitializer& ObjectInitializer)
+ : Super(ObjectInitializer)
+ , OnSendInviteCompleteDelegate(FOnSendInviteComplete::CreateUObject(this, &ThisClass::OnSendInviteComplete))
+{
+}
+
+USendFriendInviteCallbackProxy* USendFriendInviteCallbackProxy::SendFriendInvite(UObject* WorldContextObject, APlayerController *PlayerController, const FBPUniqueNetId &UniqueNetIDInvited)
+{
+ USendFriendInviteCallbackProxy* Proxy = NewObject();
+ Proxy->PlayerControllerWeakPtr = PlayerController;
+ Proxy->cUniqueNetId = UniqueNetIDInvited;
+ Proxy->WorldContextObject = WorldContextObject;
+ return Proxy;
+}
+
+void USendFriendInviteCallbackProxy::Activate()
+{
+ if (!cUniqueNetId.IsValid())
+ {
+ // Fail immediately
+ UE_LOG(AdvancedSendFriendInviteLog, Warning, TEXT("SendFriendInvite Failed received a bad UniqueNetId!"));
+ OnFailure.Broadcast();
+ return;
+ }
+
+ if (!PlayerControllerWeakPtr.IsValid())
+ {
+ // Fail immediately
+ UE_LOG(AdvancedSendFriendInviteLog, Warning, TEXT("SendFriendInvite Failed received a bad playercontroller!"));
+ OnFailure.Broadcast();
+ return;
+ }
+
+ IOnlineFriendsPtr Friends = Online::GetFriendsInterface();
+ if (Friends.IsValid())
+ {
+ ULocalPlayer* Player = Cast(PlayerControllerWeakPtr->Player);
+
+ if (!Player)
+ {
+ // Fail immediately
+ UE_LOG(AdvancedSendFriendInviteLog, Warning, TEXT("SendFriendInvite Failed couldn't cast to ULocalPlayer!"));
+ OnFailure.Broadcast();
+ return;
+ }
+
+ Friends->SendInvite(Player->GetControllerId(), *cUniqueNetId.GetUniqueNetId(), EFriendsLists::ToString((EFriendsLists::Default)), OnSendInviteCompleteDelegate);
+ return;
+ }
+ // Fail immediately
+ OnFailure.Broadcast();
+}
+
+void USendFriendInviteCallbackProxy::OnSendInviteComplete(int32 LocalPlayerNum, bool bWasSuccessful, const FUniqueNetId &InvitedPlayer, const FString &ListName, const FString &ErrorString)
+{
+ if ( bWasSuccessful )
+ {
+ OnSuccess.Broadcast();
+ }
+ else
+ {
+ UE_LOG(AdvancedSendFriendInviteLog, Warning, TEXT("SendFriendInvite Failed with error: %s"), *ErrorString);
+ OnFailure.Broadcast();
+ }
+}
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/StartSessionCallbackProxyAdvanced.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/StartSessionCallbackProxyAdvanced.cpp
new file mode 100644
index 0000000..3831384
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/StartSessionCallbackProxyAdvanced.cpp
@@ -0,0 +1,62 @@
+#include "StartSessionCallbackProxyAdvanced.h"
+
+UStartSessionCallbackProxyAdvanced::UStartSessionCallbackProxyAdvanced(const FObjectInitializer& ObjectInitializer)
+ : Super(ObjectInitializer)
+ , StartCompleteDelegate(FOnStartSessionCompleteDelegate::CreateUObject(this, &ThisClass::OnStartCompleted))
+{
+}
+
+UStartSessionCallbackProxyAdvanced* UStartSessionCallbackProxyAdvanced::StartAdvancedSession(
+ const UObject* WorldContextObject)
+{
+ UStartSessionCallbackProxyAdvanced* Proxy = NewObject();
+ Proxy->WorldContextObject = WorldContextObject;
+ return Proxy;
+}
+
+void UStartSessionCallbackProxyAdvanced::Activate()
+{
+ const FOnlineSubsystemBPCallHelperAdvanced Helper(
+ TEXT("StartSession"),
+ GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull));
+
+ if (Helper.OnlineSub != nullptr)
+ {
+ const auto Sessions = Helper.OnlineSub->GetSessionInterface();
+ if (Sessions.IsValid())
+ {
+ StartCompleteDelegateHandle = Sessions->AddOnStartSessionCompleteDelegate_Handle(StartCompleteDelegate);
+ Sessions->StartSession(NAME_GameSession);
+ return;
+ }
+ FFrame::KismetExecutionMessage(TEXT("Sessions not supported by Online Subsystem"), ELogVerbosity::Warning);
+ }
+
+ // Fail immediately
+ OnFailure.Broadcast();
+}
+
+void UStartSessionCallbackProxyAdvanced::OnStartCompleted(FName SessionName, bool bWasSuccessful)
+{
+ const FOnlineSubsystemBPCallHelperAdvanced Helper(
+ TEXT("StartSessionCallback"),
+ GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull));
+
+ if (Helper.OnlineSub != nullptr)
+ {
+ const auto Sessions = Helper.OnlineSub->GetSessionInterface();
+ if (Sessions.IsValid())
+ {
+ Sessions->ClearOnStartSessionCompleteDelegate_Handle(StartCompleteDelegateHandle);
+ }
+ }
+
+ if (bWasSuccessful)
+ {
+ OnSuccess.Broadcast();
+ }
+ else
+ {
+ OnFailure.Broadcast();
+ }
+}
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/UpdateSessionCallbackProxyAdvanced.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/UpdateSessionCallbackProxyAdvanced.cpp
new file mode 100644
index 0000000..7fa1ea0
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSessions/Source/AdvancedSessions/Private/UpdateSessionCallbackProxyAdvanced.cpp
@@ -0,0 +1,130 @@
+// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
+#include "UpdateSessionCallbackProxyAdvanced.h"
+
+
+//////////////////////////////////////////////////////////////////////////
+// UUpdateSessionCallbackProxyAdvanced
+
+UUpdateSessionCallbackProxyAdvanced::UUpdateSessionCallbackProxyAdvanced(const FObjectInitializer& ObjectInitializer)
+ : Super(ObjectInitializer)
+ , OnUpdateSessionCompleteDelegate(FOnUpdateSessionCompleteDelegate::CreateUObject(this, &ThisClass::OnUpdateCompleted))
+ , NumPublicConnections(1)
+{
+}
+
+UUpdateSessionCallbackProxyAdvanced* UUpdateSessionCallbackProxyAdvanced::UpdateSession(UObject* WorldContextObject, const TArray &ExtraSettings, int32 PublicConnections, int32 PrivateConnections, bool bUseLAN, bool bAllowInvites, bool bAllowJoinInProgress, bool bRefreshOnlineData, bool bIsDedicatedServer, bool bShouldAdvertise)
+{
+ UUpdateSessionCallbackProxyAdvanced* Proxy = NewObject();
+ Proxy->NumPublicConnections = PublicConnections;
+ Proxy->NumPrivateConnections = PrivateConnections;
+ Proxy->bUseLAN = bUseLAN;
+ Proxy->WorldContextObject = WorldContextObject;
+ Proxy->bAllowInvites = bAllowInvites;
+ Proxy->ExtraSettings = ExtraSettings;
+ Proxy->bRefreshOnlineData = bRefreshOnlineData;
+ Proxy->bAllowJoinInProgress = bAllowJoinInProgress;
+ Proxy->bDedicatedServer = bIsDedicatedServer;
+ Proxy->bShouldAdvertise = bShouldAdvertise;
+ return Proxy;
+}
+
+void UUpdateSessionCallbackProxyAdvanced::Activate()
+{
+ const FOnlineSubsystemBPCallHelperAdvanced Helper(TEXT("UpdateSession"), GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull));
+
+ if (Helper.OnlineSub != nullptr)
+ {
+ const auto Sessions = Helper.OnlineSub->GetSessionInterface();
+ if (Sessions.IsValid())
+ {
+ if (Sessions->GetNumSessions() < 1)
+ {
+ OnFailure.Broadcast();
+ GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("NO REGISTERED SESSIONS!"));
+ return;
+ }
+
+ // This gets the actual session itself
+ //FNamedOnlineSession * curSession = Sessions->GetNamedSession(NAME_GameSession);
+ FOnlineSessionSettings* Settings = Sessions->GetSessionSettings(NAME_GameSession);
+
+ if (!Settings)
+ {
+ // Fail immediately
+ OnFailure.Broadcast();
+ return;
+ }
+
+ OnUpdateSessionCompleteDelegateHandle = Sessions->AddOnUpdateSessionCompleteDelegate_Handle(OnUpdateSessionCompleteDelegate);
+
+ // FOnlineSessionSettings Settings;
+ //Settings->BuildUniqueId = GetBuildUniqueId();
+ Settings->NumPublicConnections = NumPublicConnections;
+ Settings->NumPrivateConnections = NumPrivateConnections;
+ Settings->bShouldAdvertise = bShouldAdvertise;
+ Settings->bAllowJoinInProgress = bAllowJoinInProgress;
+ Settings->bIsLANMatch = bUseLAN;
+ //Settings->bUsesPresence = true;
+ //Settings->bAllowJoinViaPresence = true;
+ Settings->bAllowInvites = bAllowInvites;
+ Settings->bAllowJoinInProgress = bAllowJoinInProgress;
+ Settings->bIsDedicated = bDedicatedServer;
+
+ FOnlineSessionSetting * fSetting = NULL;
+ FOnlineSessionSetting ExtraSetting;
+ for (int i = 0; i < ExtraSettings.Num(); i++)
+ {
+ fSetting = Settings->Settings.Find(ExtraSettings[i].Key);
+
+ if (fSetting)
+ {
+ fSetting->Data = ExtraSettings[i].Data;
+ }
+ else
+ {
+ ExtraSetting.Data = ExtraSettings[i].Data;
+ ExtraSetting.AdvertisementType = EOnlineDataAdvertisementType::ViaOnlineService;
+ Settings->Settings.Add(ExtraSettings[i].Key, ExtraSetting);
+ }
+ }
+
+ Sessions->UpdateSession(NAME_GameSession, *Settings, bRefreshOnlineData);
+
+ // OnUpdateCompleted will get called, nothing more to do now
+ return;
+ }
+ else
+ {
+ FFrame::KismetExecutionMessage(TEXT("Sessions not supported by Online Subsystem"), ELogVerbosity::Warning);
+ }
+ }
+ // Fail immediately
+ OnFailure.Broadcast();
+ GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("Sessions not supported"));
+}
+
+void UUpdateSessionCallbackProxyAdvanced::OnUpdateCompleted(FName SessionName, bool bWasSuccessful)
+{
+ const FOnlineSubsystemBPCallHelperAdvanced Helper(TEXT("UpdateSessionCallback"), GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull));
+
+ if (Helper.OnlineSub != nullptr)
+ {
+ const auto Sessions = Helper.OnlineSub->GetSessionInterface();
+ if (Sessions.IsValid())
+ {
+ Sessions->ClearOnUpdateSessionCompleteDelegate_Handle(OnUpdateSessionCompleteDelegateHandle);
+
+ if (bWasSuccessful)
+ {
+ OnSuccess.Broadcast();
+ return;
+ }
+ }
+ }
+
+ if (!bWasSuccessful)
+ {
+ OnFailure.Broadcast();
+ GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("WAS NOT SUCCESSFUL"));
+ }
+}
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSteamSessions/AdvancedSteamSessions.uplugin b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSteamSessions/AdvancedSteamSessions.uplugin
new file mode 100644
index 0000000..fa63cb3
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSteamSessions/AdvancedSteamSessions.uplugin
@@ -0,0 +1,38 @@
+{
+ "FileVersion" : 3,
+
+ "FriendlyName" : "Advanced Steam Sessions",
+ "Version" : 5.1,
+ "VersionName": "5.1",
+ "Description" : "Adds new blueprint functions to handle more advanced session operations in Steam. REQUIRES ADVANCED SESSIONS",
+ "Category" : "Advanced Sessions Plugin",
+ "CreatedBy" : "Joshua Statzer",
+ "CreatedByURL" : "N/A",
+
+ "Modules" :
+ [
+ {
+ "Name" : "AdvancedSteamSessions",
+ "Type" : "RunTime",
+ "LoadingPhase" : "PreDefault"
+ }
+ ],
+ "Plugins": [
+ {
+ "Name": "AdvancedSessions",
+ "Enabled": true
+ },
+ {
+ "Name": "OnlineSubsystem",
+ "Enabled": true
+ },
+ {
+ "Name": "OnlineSubsystemUtils",
+ "Enabled": true
+ },
+ {
+ "Name": "SteamShared",
+ "Enabled": true
+ }
+ ]
+}
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSteamSessions/Config/FilterPlugin.ini b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSteamSessions/Config/FilterPlugin.ini
new file mode 100644
index 0000000..ccebca2
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSteamSessions/Config/FilterPlugin.ini
@@ -0,0 +1,8 @@
+[FilterPlugin]
+; This section lists additional files which will be packaged along with your plugin. Paths should be listed relative to the root plugin directory, and
+; may include "...", "*", and "?" wildcards to match directories, files, and individual characters respectively.
+;
+; Examples:
+; /README.txt
+; /Extras/...
+; /Binaries/ThirdParty/*.dll
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSteamSessions/Resources/Icon128.png b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSteamSessions/Resources/Icon128.png
new file mode 100644
index 0000000..fee08fc
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSteamSessions/Resources/Icon128.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:eac09a19149b472680abd8bcb38513edbe29083a34edb4bbdbfbebec68540029
+size 5879
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSteamSessions/Source/AdvancedSteamSessions/AdvancedSteamSessions.Build.cs b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSteamSessions/Source/AdvancedSteamSessions/AdvancedSteamSessions.Build.cs
new file mode 100644
index 0000000..609d7b5
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSteamSessions/Source/AdvancedSteamSessions/AdvancedSteamSessions.Build.cs
@@ -0,0 +1,22 @@
+using UnrealBuildTool;
+using System.IO;
+
+public class AdvancedSteamSessions : ModuleRules
+{
+ public AdvancedSteamSessions(ReadOnlyTargetRules Target) : base(Target)
+ {
+ PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
+ //bEnforceIWYU = true;
+
+ PublicDefinitions.Add("WITH_ADVANCED_STEAM_SESSIONS=1");
+
+ PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "OnlineSubsystem", "CoreUObject", "OnlineSubsystemUtils", "Networking", "Sockets", "AdvancedSessions"/*"Voice", "OnlineSubsystemSteam"*/ });
+ PrivateDependencyModuleNames.AddRange(new string[] { "OnlineSubsystem", "Sockets", "Networking", "OnlineSubsystemUtils" /*"Voice", "Steamworks","OnlineSubsystemSteam"*/});
+
+ if ((Target.Platform == UnrealTargetPlatform.Win64) || (Target.Platform == UnrealTargetPlatform.Linux) || (Target.Platform == UnrealTargetPlatform.Mac))
+ {
+ PublicDependencyModuleNames.AddRange(new string[] { "Steamworks"/*, "OnlineSubsystemSteam"*/ });
+ //PublicIncludePaths.AddRange(new string[] { "../Plugins/Online/OnlineSubsystemSteam/Source/Private" });// This is dumb but it isn't very open
+ }
+ }
+}
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSteamSessions/Source/AdvancedSteamSessions/Classes/AdvancedSteamFriendsLibrary.h b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSteamSessions/Source/AdvancedSteamSessions/Classes/AdvancedSteamFriendsLibrary.h
new file mode 100644
index 0000000..7d8cead
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSteamSessions/Source/AdvancedSteamSessions/Classes/AdvancedSteamFriendsLibrary.h
@@ -0,0 +1,387 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+#include "CoreMinimal.h"
+#include "BlueprintDataDefinitions.h"
+#include "Kismet/BlueprintFunctionLibrary.h"
+#include "Online.h"
+#include "OnlineSubsystem.h"
+#include "Interfaces/OnlineFriendsInterface.h"
+#include "Interfaces/OnlineUserInterface.h"
+#include "Interfaces/OnlineMessageInterface.h"
+#include "Interfaces/OnlinePresenceInterface.h"
+#include "Engine/GameInstance.h"
+#include "Interfaces/OnlineSessionInterface.h"
+#include "BlueprintDataDefinitions.h"
+#include "UObject/UObjectIterator.h"
+
+// This is taken directly from UE4 - OnlineSubsystemSteamPrivatePCH.h as a fix for the array_count macro
+// @todo Steam: Steam headers trigger secure-C-runtime warnings in Visual C++. Rather than mess with _CRT_SECURE_NO_WARNINGS, we'll just
+// disable the warnings locally. Remove when this is fixed in the SDK
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:4996)
+// #TODO check back on this at some point
+#pragma warning(disable:4265) // SteamAPI CCallback< specifically, this warning is off by default but 4.17 turned it on....
+#endif
+
+#if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX
+
+#pragma push_macro("ARRAY_COUNT")
+#undef ARRAY_COUNT
+
+#if USING_CODE_ANALYSIS
+MSVC_PRAGMA(warning(push))
+MSVC_PRAGMA(warning(disable : ALL_CODE_ANALYSIS_WARNINGS))
+#endif // USING_CODE_ANALYSIS
+
+#include
+
+#if USING_CODE_ANALYSIS
+MSVC_PRAGMA(warning(pop))
+#endif // USING_CODE_ANALYSIS
+
+#include
+#include
+//#include
+#pragma pop_macro("ARRAY_COUNT")
+
+// @todo Steam: See above
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+// Making a copy of this here since the original is still in a private folder and is screwing with things
+/**
+* Steam specific implementation of the unique net id
+*/
+class FUniqueNetIdSteam2 :
+ public FUniqueNetId
+{
+PACKAGE_SCOPE:
+ /** Holds the net id for a player */
+ uint64 UniqueNetId;
+
+ /** Hidden on purpose */
+ FUniqueNetIdSteam2() :
+ UniqueNetId(0)
+ {
+ }
+
+ /**
+ * Copy Constructor
+ *
+ * @param Src the id to copy
+ */
+ explicit FUniqueNetIdSteam2(const FUniqueNetIdSteam2& Src) :
+ UniqueNetId(Src.UniqueNetId)
+ {
+ }
+
+public:
+ /**
+ * Constructs this object with the specified net id
+ *
+ * @param InUniqueNetId the id to set ours to
+ */
+ explicit FUniqueNetIdSteam2(uint64 InUniqueNetId) :
+ UniqueNetId(InUniqueNetId)
+ {
+ }
+
+ /**
+ * Constructs this object with the steam id
+ *
+ * @param InUniqueNetId the id to set ours to
+ */
+ explicit FUniqueNetIdSteam2(CSteamID InSteamId) :
+ UniqueNetId(InSteamId.ConvertToUint64())
+ {
+ }
+
+ /**
+ * Constructs this object with the specified net id
+ *
+ * @param String textual representation of an id
+ */
+ explicit FUniqueNetIdSteam2(const FString& Str) :
+ UniqueNetId(FCString::Atoi64(*Str))
+ {
+ }
+
+
+ /**
+ * Constructs this object with the specified net id
+ *
+ * @param InUniqueNetId the id to set ours to (assumed to be FUniqueNetIdSteam in fact)
+ */
+ explicit FUniqueNetIdSteam2(const FUniqueNetId& InUniqueNetId) :
+ UniqueNetId(*(uint64*)InUniqueNetId.GetBytes())
+ {
+ }
+
+ virtual FName GetType() const override
+ {
+ return STEAM_SUBSYSTEM;
+ }
+
+ /**
+ * Get the raw byte representation of this net id
+ * This data is platform dependent and shouldn't be manipulated directly
+ *
+ * @return byte array of size GetSize()
+ */
+ virtual const uint8* GetBytes() const override
+ {
+ return (uint8*)&UniqueNetId;
+ }
+
+ /**
+ * Get the size of the id
+ *
+ * @return size in bytes of the id representation
+ */
+ virtual int32 GetSize() const override
+ {
+ return sizeof(uint64);
+ }
+
+ /**
+ * Check the validity of the id
+ *
+ * @return true if this is a well formed ID, false otherwise
+ */
+ virtual bool IsValid() const override
+ {
+ return UniqueNetId != 0 && CSteamID(UniqueNetId).IsValid();
+ }
+
+ /**
+ * Platform specific conversion to string representation of data
+ *
+ * @return data in string form
+ */
+ virtual FString ToString() const override
+ {
+ return FString::Printf(TEXT("%llu"), UniqueNetId);
+ }
+
+ /**
+ * Get a human readable representation of the net id
+ * Shouldn't be used for anything other than logging/debugging
+ *
+ * @return id in string form
+ */
+ virtual FString ToDebugString() const override
+ {
+ CSteamID SteamID(UniqueNetId);
+ if (SteamID.IsLobby())
+ {
+ return FString::Printf(TEXT("Lobby [0x%llX]"), UniqueNetId);
+ }
+ else if (SteamID.BAnonGameServerAccount())
+ {
+ return FString::Printf(TEXT("Server [0x%llX]"), UniqueNetId);
+ }
+ else if (SteamID.IsValid())
+ {
+ const FString NickName(SteamFriends() ? UTF8_TO_TCHAR(SteamFriends()->GetFriendPersonaName(UniqueNetId)) : TEXT("UNKNOWN"));
+ return FString::Printf(TEXT("%s [0x%llX]"), *NickName, UniqueNetId);
+ }
+ else
+ {
+ return FString::Printf(TEXT("INVALID [0x%llX]"), UniqueNetId);
+ }
+ }
+
+
+ virtual uint32 GetTypeHash() const override
+ {
+ return ::GetTypeHash(UniqueNetId);
+ }
+
+ /** Convenience cast to CSteamID */
+ operator CSteamID()
+ {
+ return UniqueNetId;
+ }
+
+ /** Convenience cast to CSteamID */
+ operator const CSteamID() const
+ {
+ return UniqueNetId;
+ }
+
+ /** Convenience cast to CSteamID pointer */
+ operator CSteamID*()
+ {
+ return (CSteamID*)&UniqueNetId;
+ }
+
+ /** Convenience cast to CSteamID pointer */
+ operator const CSteamID*() const
+ {
+ return (const CSteamID*)&UniqueNetId;
+ }
+
+ friend FArchive& operator<<(FArchive& Ar, FUniqueNetIdSteam2& UserId)
+ {
+ return Ar << UserId.UniqueNetId;
+ }
+};
+
+#endif
+
+#include "AdvancedSteamFriendsLibrary.generated.h"
+
+
+//General Advanced Sessions Log
+DECLARE_LOG_CATEGORY_EXTERN(AdvancedSteamFriendsLog, Log, All);
+
+UENUM(Blueprintable)
+enum class SteamAvatarSize : uint8
+{
+ SteamAvatar_INVALID = 0,
+ SteamAvatar_Small = 1,
+ SteamAvatar_Medium = 2,
+ SteamAvatar_Large = 3
+};
+
+UENUM(Blueprintable)
+enum class ESteamUserOverlayType : uint8
+{
+ /*Opens the overlay web browser to the specified user or groups profile.*/
+ steamid,
+ /*Opens a chat window to the specified user, or joins the group chat.*/
+ chat,
+ /*Opens a window to a Steam Trading session that was started with the ISteamEconomy / StartTrade Web API.*/
+ jointrade,
+ /*Opens the overlay web browser to the specified user's stats.*/
+ stats,
+ /*Opens the overlay web browser to the specified user's achievements.*/
+ achievements,
+ /*Opens the overlay in minimal mode prompting the user to add the target user as a friend.*/
+ friendadd,
+ /*Opens the overlay in minimal mode prompting the user to remove the target friend.*/
+ friendremove,
+ /*Opens the overlay in minimal mode prompting the user to accept an incoming friend invite.*/
+ friendrequestaccept,
+ /*Opens the overlay in minimal mode prompting the user to ignore an incoming friend invite.*/
+ friendrequestignore
+};
+
+static FString EnumToString(const FString& enumName, uint8 value)
+{
+
+ const UEnum* EnumPtr = FindFirstObject(*enumName, EFindFirstObjectOptions::None, ELogVerbosity::Warning, TEXT("EumtoString"));
+
+ if (!EnumPtr)
+ return FString();
+
+ FString EnumName = EnumPtr->GetNameStringByIndex(value);
+ return EnumName;
+}
+
+
+USTRUCT(BlueprintType, Category = "Online|SteamAPI|SteamGroups")
+struct FBPSteamGroupInfo
+{
+ GENERATED_USTRUCT_BODY()
+
+public:
+
+ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|SteamAPI|SteamGroups")
+ FBPUniqueNetId GroupID; // Uint64 representation
+ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|SteamAPI|SteamGroups")
+ FString GroupName;
+ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|SteamAPI|SteamGroups")
+ FString GroupTag;
+ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|SteamAPI|SteamGroups")
+ int32 numOnline = 0;
+ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|SteamAPI|SteamGroups")
+ int32 numInGame = 0;
+ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|SteamAPI|SteamGroups")
+ int32 numChatting = 0;
+
+};
+
+UENUM(Blueprintable)
+enum class EBPTextFilteringContext : uint8
+{
+ /*Unknown context.*/
+ FContext_Unknown = 0,
+ /*Game content, only legally required filtering is performed.*/
+ FContext_GameContent = 1,
+ /*Char from another player.*/
+ FContext_Chat = 2,
+ /*Character or item name.*/
+ FContext_Name = 3
+};
+
+UCLASS()
+class UAdvancedSteamFriendsLibrary : public UBlueprintFunctionLibrary
+{
+ GENERATED_BODY()
+public:
+
+ //********* Friend List Functions *************//
+
+ // Get a texture of a valid friends avatar, STEAM ONLY, Returns invalid texture if the subsystem hasn't loaded that size of avatar yet
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedFriends|SteamAPI", meta = (ExpandEnumAsExecs = "Result"))
+ static UTexture2D * GetSteamFriendAvatar(const FBPUniqueNetId UniqueNetId, EBlueprintAsyncResultSwitch &Result, SteamAvatarSize AvatarSize = SteamAvatarSize::SteamAvatar_Medium);
+
+ // Preloads the avatar and name of a steam friend, return whether it is already available or not, STEAM ONLY, Takes time to actually load everything after this is called.
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedFriends|SteamAPI")
+ static bool RequestSteamFriendInfo(const FBPUniqueNetId UniqueNetId, bool bRequireNameOnly = false);
+
+ // Opens the steam overlay to go to the specified user dialog
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedFriends|SteamAPI")
+ static bool OpenSteamUserOverlay(const FBPUniqueNetId UniqueNetId, ESteamUserOverlayType DialogType);
+
+ // Returns if the steam overlay is currently active (this can return false during initial overlay hooking)
+ UFUNCTION(BlueprintPure, Category = "Online|AdvancedFriends|SteamAPI")
+ static bool IsOverlayEnabled();
+
+ // Gets the level of a friends steam account, STEAM ONLY, Returns -1 if the steam level is not known, might need RequestSteamFriendInfo called first.
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedFriends|SteamAPI")
+ static int32 GetFriendSteamLevel(const FBPUniqueNetId UniqueNetId);
+
+ // Gets the persona name of a steam ID, STEAM ONLY, Returns empty if no result, might need RequestSteamFriendInfo called first.
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedFriends|SteamAPI")
+ static FString GetSteamPersonaName(const FBPUniqueNetId UniqueNetId);
+
+ // Creates a unique steam id directly from a string holding a uint64 value, useful for testing
+ UFUNCTION(BlueprintPure, Category = "Online|AdvancedFriends|SteamAPI")
+ static FBPUniqueNetId CreateSteamIDFromString(const FString SteamID64);
+
+ // Retreives the local steam ID from steam
+ UFUNCTION(BlueprintPure, Category = "Online|AdvancedFriends|SteamAPI")
+ static FBPUniqueNetId GetLocalSteamIDFromSteam();
+
+ /* Gets the current game played by a friend - AppID is int32 even though steam ids are uint32, can't be helped in blueprint currently
+ * can use the AppID with the WebAPI GetAppList request.
+ */
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedFriends|SteamAPI", meta = (ExpandEnumAsExecs = "Result"))
+ static void GetSteamFriendGamePlayed(const FBPUniqueNetId UniqueNetId, EBlueprintResultSwitch &Result/*, FString & GameName*/, int32 & AppID);
+
+ // Get a full list of steam groups
+ UFUNCTION(BlueprintCallable, Category = "Online|SteamAPI|SteamGroups")
+ static void GetSteamGroups(TArray & SteamGroups);
+
+ // Initializes text filtering (pre-loading dictonaries)
+ // Returns if it succeeded, false if filtering is unavailable for the games language
+ UFUNCTION(BlueprintCallable, Category = "Online|SteamAPI|TextFiltering")
+ static bool InitTextFiltering();
+
+ // Attempts to filter a string with the given filtering context
+ // Returns true if the text has been filtered, false if it hasn't (no filtering required or operation failed)
+ // If false it will still output the original text
+ // Textsource is the steam id that is the source of the text (player name / chat)
+ // Requires that InitTextFiltering be called first!!
+ UFUNCTION(BlueprintCallable, Category = "Online|SteamAPI|TextFiltering")
+ static bool FilterText(FString TextToFilter, EBPTextFilteringContext Context, const FBPUniqueNetId TextSourceID, FString& FilteredText);
+
+ // Returns if steam is running in big picture mode
+ UFUNCTION(BlueprintPure, Category = "Online|SteamAPI")
+ static bool IsSteamInBigPictureMode();
+};
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSteamSessions/Source/AdvancedSteamSessions/Classes/AdvancedSteamSessions.h b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSteamSessions/Source/AdvancedSteamSessions/Classes/AdvancedSteamSessions.h
new file mode 100644
index 0000000..4a49251
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSteamSessions/Source/AdvancedSteamSessions/Classes/AdvancedSteamSessions.h
@@ -0,0 +1,12 @@
+#pragma once
+
+#include "CoreMinimal.h"
+#include "Modules/ModuleManager.h"
+
+class AdvancedSteamSessions : public IModuleInterface
+{
+public:
+ /** IModuleInterface implementation */
+ void StartupModule();
+ void ShutdownModule();
+};
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSteamSessions/Source/AdvancedSteamSessions/Classes/AdvancedSteamWorkshopLibrary.h b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSteamSessions/Source/AdvancedSteamSessions/Classes/AdvancedSteamWorkshopLibrary.h
new file mode 100644
index 0000000..64c5d76
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSteamSessions/Source/AdvancedSteamSessions/Classes/AdvancedSteamWorkshopLibrary.h
@@ -0,0 +1,351 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+#include "CoreMinimal.h"
+#include "BlueprintDataDefinitions.h"
+#include "Kismet/BlueprintFunctionLibrary.h"
+#include "Online.h"
+#include "OnlineSubsystem.h"
+#if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX
+#include "steam/isteamugc.h"
+#include "steam/isteamremotestorage.h"
+#endif
+#include "Interfaces/OnlineSessionInterface.h"
+
+// @todo Steam: Steam headers trigger secure-C-runtime warnings in Visual C++. Rather than mess with _CRT_SECURE_NO_WARNINGS, we'll just
+// disable the warnings locally. Remove when this is fixed in the SDK
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:4996)
+// #TODO check back on this at some point
+#pragma warning(disable:4265) // SteamAPI CCallback< specifically, this warning is off by default but 4.17 turned it on....
+#endif
+
+#if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX
+
+#pragma push_macro("ARRAY_COUNT")
+#undef ARRAY_COUNT
+
+#if USING_CODE_ANALYSIS
+MSVC_PRAGMA(warning(push))
+MSVC_PRAGMA(warning(disable : ALL_CODE_ANALYSIS_WARNINGS))
+#endif // USING_CODE_ANALYSIS
+
+#include
+
+#if USING_CODE_ANALYSIS
+MSVC_PRAGMA(warning(pop))
+#endif // USING_CODE_ANALYSIS
+
+
+#pragma pop_macro("ARRAY_COUNT")
+
+#endif
+
+// @todo Steam: See above
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+
+#include "AdvancedSteamWorkshopLibrary.generated.h"
+
+
+//General Advanced Sessions Log
+DECLARE_LOG_CATEGORY_EXTERN(AdvancedSteamWorkshopLog, Log, All);
+
+
+// Using a custom struct because uint32 isn't blueprint supported and I don't want to cast to int32
+// due to the size of the workshop it could end up overflowing?
+USTRUCT(BlueprintType)
+struct FBPSteamWorkshopID
+{
+ GENERATED_USTRUCT_BODY()
+
+public:
+
+ uint64 SteamWorkshopID;
+
+ FBPSteamWorkshopID()
+ {
+
+ }
+
+ FBPSteamWorkshopID(uint64 ID)
+ {
+ SteamWorkshopID = ID;
+ }
+};
+
+
+// General result codes - Copying steams version over
+// Check these to future proof
+UENUM(BlueprintType)
+enum class FBPSteamResult : uint8
+{
+ K_EResultInvalid = 0,
+ k_EResultOK = 1, // success
+ k_EResultFail = 2, // generic failure
+ k_EResultNoConnection = 3, // no/failed network connection
+ // k_EResultNoConnectionRetry = 4, // OBSOLETE - removed
+ k_EResultInvalidPassword = 5, // password/ticket is invalid
+ k_EResultLoggedInElsewhere = 6, // same user logged in elsewhere
+ k_EResultInvalidProtocolVer = 7, // protocol version is incorrect
+ k_EResultInvalidParam = 8, // a parameter is incorrect
+ k_EResultFileNotFound = 9, // file was not found
+ k_EResultBusy = 10, // called method busy - action not taken
+ k_EResultInvalidState = 11, // called object was in an invalid state
+ k_EResultInvalidName = 12, // name is invalid
+ k_EResultInvalidEmail = 13, // email is invalid
+ k_EResultDuplicateName = 14, // name is not unique
+ k_EResultAccessDenied = 15, // access is denied
+ k_EResultTimeout = 16, // operation timed out
+ k_EResultBanned = 17, // VAC2 banned
+ k_EResultAccountNotFound = 18, // account not found
+ k_EResultInvalidSteamID = 19, // steamID is invalid
+ k_EResultServiceUnavailable = 20, // The requested service is currently unavailable
+ k_EResultNotLoggedOn = 21, // The user is not logged on
+ k_EResultPending = 22, // Request is pending (may be in process, or waiting on third party)
+ k_EResultEncryptionFailure = 23, // Encryption or Decryption failed
+ k_EResultInsufficientPrivilege = 24, // Insufficient privilege
+ k_EResultLimitExceeded = 25, // Too much of a good thing
+ k_EResultRevoked = 26, // Access has been revoked (used for revoked guest passes)
+ k_EResultExpired = 27, // License/Guest pass the user is trying to access is expired
+ k_EResultAlreadyRedeemed = 28, // Guest pass has already been redeemed by account, cannot be acked again
+ k_EResultDuplicateRequest = 29, // The request is a duplicate and the action has already occurred in the past, ignored this time
+ k_EResultAlreadyOwned = 30, // All the games in this guest pass redemption request are already owned by the user
+ k_EResultIPNotFound = 31, // IP address not found
+ k_EResultPersistFailed = 32, // failed to write change to the data store
+ k_EResultLockingFailed = 33, // failed to acquire access lock for this operation
+ k_EResultLogonSessionReplaced = 34,
+ k_EResultConnectFailed = 35,
+ k_EResultHandshakeFailed = 36,
+ k_EResultIOFailure = 37,
+ k_EResultRemoteDisconnect = 38,
+ k_EResultShoppingCartNotFound = 39, // failed to find the shopping cart requested
+ k_EResultBlocked = 40, // a user didn't allow it
+ k_EResultIgnored = 41, // target is ignoring sender
+ k_EResultNoMatch = 42, // nothing matching the request found
+ k_EResultAccountDisabled = 43,
+ k_EResultServiceReadOnly = 44, // this service is not accepting content changes right now
+ k_EResultAccountNotFeatured = 45, // account doesn't have value, so this feature isn't available
+ k_EResultAdministratorOK = 46, // allowed to take this action, but only because requester is admin
+ k_EResultContentVersion = 47, // A Version mismatch in content transmitted within the Steam protocol.
+ k_EResultTryAnotherCM = 48, // The current CM can't service the user making a request, user should try another.
+ k_EResultPasswordRequiredToKickSession = 49,// You are already logged in elsewhere, this cached credential login has failed.
+ k_EResultAlreadyLoggedInElsewhere = 50, // You are already logged in elsewhere, you must wait
+ k_EResultSuspended = 51, // Long running operation (content download) suspended/paused
+ k_EResultCancelled = 52, // Operation canceled (typically by user: content download)
+ k_EResultDataCorruption = 53, // Operation canceled because data is ill formed or unrecoverable
+ k_EResultDiskFull = 54, // Operation canceled - not enough disk space.
+ k_EResultRemoteCallFailed = 55, // an remote call or IPC call failed
+ k_EResultPasswordUnset = 56, // Password could not be verified as it's unset server side
+ k_EResultExternalAccountUnlinked = 57, // External account (PSN, Facebook...) is not linked to a Steam account
+ k_EResultPSNTicketInvalid = 58, // PSN ticket was invalid
+ k_EResultExternalAccountAlreadyLinked = 59, // External account (PSN, Facebook...) is already linked to some other account, must explicitly request to replace/delete the link first
+ k_EResultRemoteFileConflict = 60, // The sync cannot resume due to a conflict between the local and remote files
+ k_EResultIllegalPassword = 61, // The requested new password is not legal
+ k_EResultSameAsPreviousValue = 62, // new value is the same as the old one ( secret question and answer )
+ k_EResultAccountLogonDenied = 63, // account login denied due to 2nd factor authentication failure
+ k_EResultCannotUseOldPassword = 64, // The requested new password is not legal
+ k_EResultInvalidLoginAuthCode = 65, // account login denied due to auth code invalid
+ k_EResultAccountLogonDeniedNoMail = 66, // account login denied due to 2nd factor auth failure - and no mail has been sent
+ k_EResultHardwareNotCapableOfIPT = 67, //
+ k_EResultIPTInitError = 68, //
+ k_EResultParentalControlRestricted = 69, // operation failed due to parental control restrictions for current user
+ k_EResultFacebookQueryError = 70, // Facebook query returned an error
+ k_EResultExpiredLoginAuthCode = 71, // account login denied due to auth code expired
+ k_EResultIPLoginRestrictionFailed = 72,
+ k_EResultAccountLockedDown = 73,
+ k_EResultAccountLogonDeniedVerifiedEmailRequired = 74,
+ k_EResultNoMatchingURL = 75,
+ k_EResultBadResponse = 76, // parse failure, missing field, etc.
+ k_EResultRequirePasswordReEntry = 77, // The user cannot complete the action until they re-enter their password
+ k_EResultValueOutOfRange = 78, // the value entered is outside the acceptable range
+ k_EResultUnexpectedError = 79, // something happened that we didn't expect to ever happen
+ k_EResultDisabled = 80, // The requested service has been configured to be unavailable
+ k_EResultInvalidCEGSubmission = 81, // The set of files submitted to the CEG server are not valid !
+ k_EResultRestrictedDevice = 82, // The device being used is not allowed to perform this action
+ k_EResultRegionLocked = 83, // The action could not be complete because it is region restricted
+ k_EResultRateLimitExceeded = 84, // Temporary rate limit exceeded, try again later, different from k_EResultLimitExceeded which may be permanent
+ k_EResultAccountLoginDeniedNeedTwoFactor = 85, // Need two-factor code to login
+ k_EResultItemDeleted = 86, // The thing we're trying to access has been deleted
+ k_EResultAccountLoginDeniedThrottle = 87, // login attempt failed, try to throttle response to possible attacker
+ k_EResultTwoFactorCodeMismatch = 88, // two factor code mismatch
+ k_EResultTwoFactorActivationCodeMismatch = 89, // activation code for two-factor didn't match
+ k_EResultAccountAssociatedToMultiplePartners = 90, // account has been associated with multiple partners
+ k_EResultNotModified = 91, // data not modified
+};
+
+// Check these to future proof
+UENUM(BlueprintType)
+enum class FBPWorkshopFileType : uint8
+{
+ k_EWorkshopFileTypeCommunity = 0,
+ k_EWorkshopFileTypeMicrotransaction = 1,
+ k_EWorkshopFileTypeCollection = 2,
+ k_EWorkshopFileTypeArt = 3,
+ k_EWorkshopFileTypeVideo = 4,
+ k_EWorkshopFileTypeScreenshot = 5,
+ k_EWorkshopFileTypeGame = 6,
+ k_EWorkshopFileTypeSoftware = 7,
+ k_EWorkshopFileTypeConcept = 8,
+ k_EWorkshopFileTypeWebGuide = 9,
+ k_EWorkshopFileTypeIntegratedGuide = 10,
+ k_EWorkshopFileTypeMerch = 11,
+ k_EWorkshopFileTypeControllerBinding = 12,
+ k_EWorkshopFileTypeSteamworksAccessInvite = 13,
+ k_EWorkshopFileTypeSteamVideo = 14,
+
+ // Update k_EWorkshopFileTypeMax if you add values.
+ k_EWorkshopFileTypeMax = 15
+};
+
+// WorkshopItemDetails Struct
+USTRUCT(BlueprintType)
+struct FBPSteamWorkshopItemDetails
+{
+ GENERATED_USTRUCT_BODY()
+
+public:
+
+ FBPSteamWorkshopItemDetails()
+ {
+ ResultOfRequest = FBPSteamResult::k_EResultOK;
+ FileType = FBPWorkshopFileType::k_EWorkshopFileTypeMax;
+ CreatorAppID = 0;
+ ConsumerAppID = 0;
+ VotesUp = 0;
+ VotesDown = 0;
+ CalculatedScore = 0.f;
+ bBanned = false;
+ bAcceptedForUse = false;
+ bTagsTruncated = false;
+ }
+
+#if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX
+ FBPSteamWorkshopItemDetails(SteamUGCDetails_t &hUGCDetails)
+ {
+ ResultOfRequest = (FBPSteamResult)hUGCDetails.m_eResult;
+ FileType = (FBPWorkshopFileType)hUGCDetails.m_eFileType;
+ CreatorAppID = (int32)hUGCDetails.m_nCreatorAppID;
+ ConsumerAppID = (int32)hUGCDetails.m_nConsumerAppID;
+ Title = FString(hUGCDetails.m_rgchTitle, k_cchPublishedDocumentTitleMax);
+ Description = FString(hUGCDetails.m_rgchDescription, k_cchPublishedDocumentDescriptionMax);
+ ItemUrl = FString(hUGCDetails.m_rgchURL, k_cchPublishedFileURLMax);
+ VotesUp = (int32)hUGCDetails.m_unVotesUp;
+ VotesDown = (int32)hUGCDetails.m_unVotesDown;
+ CalculatedScore = hUGCDetails.m_flScore;
+ bBanned = hUGCDetails.m_bBanned;
+ bAcceptedForUse = hUGCDetails.m_bAcceptedForUse;
+ bTagsTruncated = hUGCDetails.m_bTagsTruncated;
+
+ CreatorSteamID = FString::Printf(TEXT("%llu"), hUGCDetails.m_ulSteamIDOwner);
+ }
+
+ FBPSteamWorkshopItemDetails(const SteamUGCDetails_t &hUGCDetails)
+ {
+ ResultOfRequest = (FBPSteamResult)hUGCDetails.m_eResult;
+ FileType = (FBPWorkshopFileType)hUGCDetails.m_eFileType;
+ CreatorAppID = (int32)hUGCDetails.m_nCreatorAppID;
+ ConsumerAppID = (int32)hUGCDetails.m_nConsumerAppID;
+ Title = FString(hUGCDetails.m_rgchTitle, k_cchPublishedDocumentTitleMax);
+ Description = FString(hUGCDetails.m_rgchDescription, k_cchPublishedDocumentDescriptionMax);
+ ItemUrl = FString(hUGCDetails.m_rgchURL, k_cchPublishedFileURLMax);
+ VotesUp = (int32)hUGCDetails.m_unVotesUp;
+ VotesDown = (int32)hUGCDetails.m_unVotesDown;
+ CalculatedScore = hUGCDetails.m_flScore;
+ bBanned = hUGCDetails.m_bBanned;
+ bAcceptedForUse = hUGCDetails.m_bAcceptedForUse;
+ bTagsTruncated = hUGCDetails.m_bTagsTruncated;
+
+ CreatorSteamID = FString::Printf(TEXT("%llu"), hUGCDetails.m_ulSteamIDOwner);
+ }
+#endif
+
+ // Result of obtaining the details
+ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|AdvancedSteamWorkshop")
+ FBPSteamResult ResultOfRequest;
+
+ // Type of file
+ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|AdvancedSteamWorkshop")
+ FBPWorkshopFileType FileType;
+
+ // These two are listed as baked to an int, but is stored as a uint, think its safe to keep int
+ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|AdvancedSteamWorkshop")
+ int32 CreatorAppID;
+ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|AdvancedSteamWorkshop")
+ int32 ConsumerAppID;
+
+ // Title of item
+ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|AdvancedSteamWorkshop")
+ FString Title;
+
+ // Description of item
+ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|AdvancedSteamWorkshop")
+ FString Description;
+
+ //Url for a video of website
+ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|AdvancedSteamWorkshop")
+ FString ItemUrl;
+
+ // Votes will be unlikely to go above signed limited
+ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|AdvancedSteamWorkshop")
+ int32 VotesUp;
+ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|AdvancedSteamWorkshop")
+ int32 VotesDown;
+
+ // Calculated score
+ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|AdvancedSteamWorkshop")
+ float CalculatedScore;
+
+ // whether the file was banned
+ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|AdvancedSteamWorkshop")
+ bool bBanned;
+
+ // developer has specifically flagged this item as accepted in the Workshop
+ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|AdvancedSteamWorkshop")
+ bool bAcceptedForUse;
+
+ // whether the list of tags was too long to be returned in the provided buffer
+ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|AdvancedSteamWorkshop")
+ bool bTagsTruncated;
+
+ // Steam ID of the user who created this content.
+ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|AdvancedSteamWorkshop")
+ FString CreatorSteamID;
+
+ /*
+ PublishedFileId_t m_nPublishedFileId;
+ uint32 m_rtimeCreated; // time when the published file was created
+ uint32 m_rtimeUpdated; // time when the published file was last updated
+ uint32 m_rtimeAddedToUserList; // time when the user added the published file to their list (not always applicable)
+ ERemoteStoragePublishedFileVisibility m_eVisibility; // visibility
+ char m_rgchTags[k_cchTagListMax]; // comma separated list of all tags associated with this file
+ // file/url information
+ UGCHandle_t m_hFile; // The handle of the primary file
+ UGCHandle_t m_hPreviewFile; // The handle of the preview file
+ char m_pchFileName[k_cchFilenameMax]; // The cloud filename of the primary file
+ int32 m_nFileSize; // Size of the primary file
+ int32 m_nPreviewFileSize; // Size of the preview file
+ uint32 m_unNumChildren; // if m_eFileType == k_EWorkshopFileTypeCollection, then this number will be the number of children contained within the collection
+ */
+
+};
+
+UCLASS()
+class UAdvancedSteamWorkshopLibrary : public UBlueprintFunctionLibrary
+{
+ GENERATED_BODY()
+public:
+
+ //********* Steam Functions *************//
+
+ // Returns IDs for subscribed workshop items, TArray length dictates how many
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedSteamWorkshop")
+ static TArray GetSubscribedWorkshopItems(int32 & NumberOfItems);
+
+ UFUNCTION(BlueprintCallable, Category = "Online|AdvancedSteamWorkshop")
+ static void GetNumSubscribedWorkshopItems(int32 & NumberOfItems);
+
+};
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSteamSessions/Source/AdvancedSteamSessions/Classes/SteamNotificationsSubsystem.h b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSteamSessions/Source/AdvancedSteamSessions/Classes/SteamNotificationsSubsystem.h
new file mode 100644
index 0000000..a1b547c
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSteamSessions/Source/AdvancedSteamSessions/Classes/SteamNotificationsSubsystem.h
@@ -0,0 +1,90 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "Subsystems/GameInstanceSubsystem.h"
+
+#if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX
+
+//#include "OnlineSubsystemSteam.h"
+//#include "OnlineSubsystemSteamPrivate.h"
+
+#include
+
+#endif
+
+#include "SteamNotificationsSubsystem.generated.h"
+
+DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnSteamOverlayActivated, bool, bOverlayState);
+
+UCLASS()
+class ADVANCEDSTEAMSESSIONS_API USteamNotificationsSubsystem : public UGameInstanceSubsystem
+{
+ GENERATED_BODY()
+
+public:
+
+ // Event thrown when the steam overlay switches states
+ UPROPERTY(BlueprintAssignable, Category = "SteamEvents")
+ FOnSteamOverlayActivated OnSteamOverlayActivated_Bind;
+
+ USteamNotificationsSubsystem() : Super()
+ {
+
+ }
+
+ class cSteamEventsStore
+ {
+ public:
+ USteamNotificationsSubsystem* ParentSubsystem = nullptr;
+ void Initialize(USteamNotificationsSubsystem* MyParent)
+ {
+ ParentSubsystem = MyParent;
+ }
+
+#if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX
+ cSteamEventsStore() :
+ OnExternalUITriggeredCallback(this, &cSteamEventsStore::OnExternalUITriggered)
+ {
+
+ }
+#else
+ //cSteamEventsStore()
+ //{
+
+ //}
+#endif
+
+ //~cSteamEventsStore(){}
+
+ private:
+#if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX
+ STEAM_CALLBACK(cSteamEventsStore, OnExternalUITriggered, GameOverlayActivated_t, OnExternalUITriggeredCallback);
+#endif
+ };
+
+ cSteamEventsStore MyEvents;
+
+ /** Implement this for initialization of instances of the system */
+ virtual void Initialize(FSubsystemCollectionBase& Collection) override
+ {
+ MyEvents.Initialize(this);
+ }
+
+ /** Implement this for deinitialization of instances of the system */
+ virtual void Deinitialize() override
+ {
+
+ }
+};
+
+#if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX
+void USteamNotificationsSubsystem::cSteamEventsStore::OnExternalUITriggered(GameOverlayActivated_t* CallbackData)
+{
+ if (ParentSubsystem)
+ {
+ ParentSubsystem->OnSteamOverlayActivated_Bind.Broadcast((bool)CallbackData->m_bActive);
+ }
+}
+#endif
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSteamSessions/Source/AdvancedSteamSessions/Classes/SteamRequestGroupOfficersCallbackProxy.h b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSteamSessions/Source/AdvancedSteamSessions/Classes/SteamRequestGroupOfficersCallbackProxy.h
new file mode 100644
index 0000000..9c73a23
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSteamSessions/Source/AdvancedSteamSessions/Classes/SteamRequestGroupOfficersCallbackProxy.h
@@ -0,0 +1,100 @@
+// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
+#pragma once
+
+#include "CoreMinimal.h"
+#include "BlueprintDataDefinitions.h"
+
+// This is taken directly from UE4 - OnlineSubsystemSteamPrivatePCH.h as a fix for the array_count macro
+
+// @todo Steam: Steam headers trigger secure-C-runtime warnings in Visual C++. Rather than mess with _CRT_SECURE_NO_WARNINGS, we'll just
+// disable the warnings locally. Remove when this is fixed in the SDK
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:4996)
+// #TODO check back on this at some point
+#pragma warning(disable:4265) // SteamAPI CCallback< specifically, this warning is off by default but 4.17 turned it on....
+#endif
+
+#if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX
+
+//#include "OnlineSubsystemSteam.h"
+
+#pragma push_macro("ARRAY_COUNT")
+#undef ARRAY_COUNT
+
+#if USING_CODE_ANALYSIS
+MSVC_PRAGMA(warning(push))
+MSVC_PRAGMA(warning(disable : ALL_CODE_ANALYSIS_WARNINGS))
+#endif // USING_CODE_ANALYSIS
+
+#include
+
+#if USING_CODE_ANALYSIS
+MSVC_PRAGMA(warning(pop))
+#endif // USING_CODE_ANALYSIS
+
+
+#pragma pop_macro("ARRAY_COUNT")
+
+#endif
+
+// @todo Steam: See above
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+#include "SteamRequestGroupOfficersCallbackProxy.generated.h"
+
+USTRUCT(BlueprintType, Category = "Online|SteamAPI|SteamGroups")
+struct FBPSteamGroupOfficer
+{
+ GENERATED_USTRUCT_BODY()
+
+public:
+
+ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|SteamAPI|SteamGroups")
+ FBPUniqueNetId OfficerUniqueNetID; // Uint64 representation
+ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Online|SteamAPI|SteamGroups")
+ bool bIsOwner = false;
+
+};
+
+
+DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FBlueprintGroupOfficerDetailsDelegate, const TArray &, OfficerList);
+
+UCLASS(MinimalAPI)
+class USteamRequestGroupOfficersCallbackProxy : public UOnlineBlueprintCallProxyBase
+{
+ GENERATED_UCLASS_BODY()
+
+ virtual ~USteamRequestGroupOfficersCallbackProxy();
+
+ // Called when there is a successful results return
+ UPROPERTY(BlueprintAssignable)
+ FBlueprintGroupOfficerDetailsDelegate OnSuccess;
+
+ // Called when there is an unsuccessful results return
+ UPROPERTY(BlueprintAssignable)
+ FBlueprintGroupOfficerDetailsDelegate OnFailure;
+
+ // Returns a list of steam group officers
+ UFUNCTION(BlueprintCallable, meta=(BlueprintInternalUseOnly = "true", WorldContext="WorldContextObject"), Category = "Online|SteamAPI|SteamGroups")
+ static USteamRequestGroupOfficersCallbackProxy* GetSteamGroupOfficerList(UObject* WorldContextObject, FBPUniqueNetId GroupUniqueNetID);
+
+ // UOnlineBlueprintCallProxyBase interface
+ virtual void Activate() override;
+ // End of UOnlineBlueprintCallProxyBase interface
+
+private:
+
+#if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX
+ void OnRequestGroupOfficerDetails( ClanOfficerListResponse_t *pResult, bool bIOFailure);
+ CCallResult m_callResultGroupOfficerRequestDetails;
+
+#endif
+
+private:
+
+ FBPUniqueNetId GroupUniqueID;
+ UObject* WorldContextObject;
+};
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSteamSessions/Source/AdvancedSteamSessions/Classes/SteamWSRequestUGCDetailsCallbackProxy.h b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSteamSessions/Source/AdvancedSteamSessions/Classes/SteamWSRequestUGCDetailsCallbackProxy.h
new file mode 100644
index 0000000..63d8c80
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSteamSessions/Source/AdvancedSteamSessions/Classes/SteamWSRequestUGCDetailsCallbackProxy.h
@@ -0,0 +1,87 @@
+// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
+#pragma once
+
+#include "CoreMinimal.h"
+#include "AdvancedSteamWorkshopLibrary.h"
+#include "BlueprintDataDefinitions.h"
+
+// This is taken directly from UE4 - OnlineSubsystemSteamPrivatePCH.h as a fix for the array_count macro
+
+// @todo Steam: Steam headers trigger secure-C-runtime warnings in Visual C++. Rather than mess with _CRT_SECURE_NO_WARNINGS, we'll just
+// disable the warnings locally. Remove when this is fixed in the SDK
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:4996)
+// #TODO check back on this at some point
+#pragma warning(disable:4265) // SteamAPI CCallback< specifically, this warning is off by default but 4.17 turned it on....
+#endif
+
+#if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX
+
+//#include "OnlineSubsystemSteam.h"
+
+#pragma push_macro("ARRAY_COUNT")
+#undef ARRAY_COUNT
+
+#if USING_CODE_ANALYSIS
+MSVC_PRAGMA(warning(push))
+MSVC_PRAGMA(warning(disable : ALL_CODE_ANALYSIS_WARNINGS))
+#endif // USING_CODE_ANALYSIS
+
+#include
+
+#if USING_CODE_ANALYSIS
+MSVC_PRAGMA(warning(pop))
+#endif // USING_CODE_ANALYSIS
+
+
+#pragma pop_macro("ARRAY_COUNT")
+
+#endif
+
+// @todo Steam: See above
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+
+#include "SteamWSRequestUGCDetailsCallbackProxy.generated.h"
+
+DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FBlueprintWorkshopDetailsDelegate, const FBPSteamWorkshopItemDetails&, WorkShopDetails);
+
+UCLASS(MinimalAPI)
+class USteamWSRequestUGCDetailsCallbackProxy : public UOnlineBlueprintCallProxyBase
+{
+ GENERATED_UCLASS_BODY()
+
+ // Called when there is a successful results return
+ UPROPERTY(BlueprintAssignable)
+ FBlueprintWorkshopDetailsDelegate OnSuccess;
+
+ // Called when there is an unsuccessful results return
+ UPROPERTY(BlueprintAssignable)
+ FBlueprintWorkshopDetailsDelegate OnFailure;
+
+ // Ends the current session
+ UFUNCTION(BlueprintCallable, meta=(BlueprintInternalUseOnly = "true", WorldContext="WorldContextObject"), Category = "Online|AdvancedSteamWorkshop")
+ static USteamWSRequestUGCDetailsCallbackProxy* GetWorkshopItemDetails(UObject* WorldContextObject, FBPSteamWorkshopID WorkShopID);
+
+ // UOnlineBlueprintCallProxyBase interface
+ virtual void Activate() override;
+ // End of UOnlineBlueprintCallProxyBase interface
+
+private:
+
+#if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX
+ // Internal callback when the operation completes, calls out to the public success/failure callbacks
+
+ void OnUGCRequestUGCDetails(SteamUGCQueryCompleted_t *pResult, bool bIOFailure);
+ CCallResult m_callResultUGCRequestDetails;
+
+#endif
+
+private:
+
+ FBPSteamWorkshopID WorkShopID;
+ UObject* WorldContextObject;
+};
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSteamSessions/Source/AdvancedSteamSessions/Private/AdvancedSteamFriendsLibrary.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSteamSessions/Source/AdvancedSteamSessions/Private/AdvancedSteamFriendsLibrary.cpp
new file mode 100644
index 0000000..91f822e
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSteamSessions/Source/AdvancedSteamSessions/Private/AdvancedSteamFriendsLibrary.cpp
@@ -0,0 +1,432 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+#include "AdvancedSteamFriendsLibrary.h"
+#include "OnlineSubSystemHeader.h"
+
+//General Log
+DEFINE_LOG_CATEGORY(AdvancedSteamFriendsLog);
+
+
+// Clan functions, add in soon
+/*int32 UAdvancedSteamFriendsLibrary::GetFriendSteamLevel(const FBPUniqueNetId UniqueNetId)
+{
+
+#if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX
+ if (!UniqueNetId.IsValid() || !UniqueNetId.UniqueNetId->IsValid() || UniqueNetId.UniqueNetId->GetType() != STEAM_SUBSYSTEM)
+ {
+ UE_LOG(AdvancedSteamFriendsLog, Warning, TEXT("IsAFriend Had a bad UniqueNetId!"));
+ return 0;
+ }
+
+ if (SteamAPI_Init())
+ {
+ uint64 id = *((uint64*)UniqueNetId.UniqueNetId->GetBytes());
+
+
+ // clan (group) iteration and access functions
+ //virtual int GetClanCount() = 0;
+ //virtual CSteamID GetClanByIndex(int iClan) = 0;
+ //virtual const char *GetClanName(CSteamID steamIDClan) = 0;
+ //virtual const char *GetClanTag(CSteamID steamIDClan) = 0;
+ // returns the most recent information we have about what's happening in a clan
+ //virtual bool GetClanActivityCounts(CSteamID steamIDClan, int *pnOnline, int *pnInGame, int *pnChatting) = 0;
+ // for clans a user is a member of, they will have reasonably up-to-date information, but for others you'll have to download the info to have the latest
+ //virtual SteamAPICall_t DownloadClanActivityCounts(ARRAY_COUNT(cClansToRequest) CSteamID *psteamIDClans, int cClansToRequest) = 0;
+
+ // requests information about a clan officer list
+ // when complete, data is returned in ClanOfficerListResponse_t call result
+ // this makes available the calls below
+ // you can only ask about clans that a user is a member of
+ // note that this won't download avatars automatically; if you get an officer,
+ // and no avatar image is available, call RequestUserInformation( steamID, false ) to download the avatar
+ //virtual SteamAPICall_t RequestClanOfficerList(CSteamID steamIDClan) = 0;
+
+
+ // returns the steamID of the clan owner
+ //virtual CSteamID GetClanOwner(CSteamID steamIDClan) = 0;
+ // returns the number of officers in a clan (including the owner)
+ //virtual int GetClanOfficerCount(CSteamID steamIDClan) = 0;
+ // returns the steamID of a clan officer, by index, of range [0,GetClanOfficerCount)
+ //virtual CSteamID GetClanOfficerByIndex(CSteamID steamIDClan, int iOfficer) = 0;
+
+
+ return SteamFriends()->GetFriendSteamLevel(id);
+ }
+#endif
+
+ return 0;
+}*/
+
+void UAdvancedSteamFriendsLibrary::GetSteamGroups(TArray & SteamGroups)
+{
+
+#if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX
+
+ if (SteamAPI_Init())
+ {
+ int numClans = SteamFriends()->GetClanCount();
+
+ for (int i = 0; i < numClans; i++)
+ {
+ CSteamID SteamGroupID = SteamFriends()->GetClanByIndex(i);
+
+ if(!SteamGroupID.IsValid())
+ continue;
+
+ FBPSteamGroupInfo GroupInfo;
+
+ TSharedPtr ValueID(new const FUniqueNetIdSteam2(SteamGroupID));
+ GroupInfo.GroupID.SetUniqueNetId(ValueID);
+ SteamFriends()->GetClanActivityCounts(SteamGroupID, &GroupInfo.numOnline, &GroupInfo.numInGame, &GroupInfo.numChatting);
+ GroupInfo.GroupName = FString(UTF8_TO_TCHAR(SteamFriends()->GetClanName(SteamGroupID)));
+ GroupInfo.GroupTag = FString(UTF8_TO_TCHAR(SteamFriends()->GetClanTag(SteamGroupID)));
+
+ SteamGroups.Add(GroupInfo);
+ }
+ }
+#endif
+
+}
+
+void UAdvancedSteamFriendsLibrary::GetSteamFriendGamePlayed(const FBPUniqueNetId UniqueNetId, EBlueprintResultSwitch &Result/*, FString & GameName*/, int32 & AppID)
+{
+
+#if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX
+ if (!UniqueNetId.IsValid() || !UniqueNetId.UniqueNetId->IsValid() || UniqueNetId.UniqueNetId->GetType() != STEAM_SUBSYSTEM)
+ {
+ UE_LOG(AdvancedSteamFriendsLog, Warning, TEXT("GetSteamFriendGamePlayed Had a bad UniqueNetId!"));
+ Result = EBlueprintResultSwitch::OnFailure;
+ return;
+ }
+
+ if (SteamAPI_Init())
+ {
+ uint64 id = *((uint64*)UniqueNetId.UniqueNetId->GetBytes());
+
+ FriendGameInfo_t GameInfo;
+ bool bIsInGame = SteamFriends()->GetFriendGamePlayed(id, &GameInfo);
+
+ if (bIsInGame && GameInfo.m_gameID.IsValid())
+ {
+ AppID = GameInfo.m_gameID.AppID();
+
+ // Forgot this test and left it in, it is incorrect, you would need restricted access
+ // And it would only find games in the local library anyway
+ /*char NameBuffer[512];
+ int Len = SteamAppList()->GetAppName(GameInfo.m_gameID.AppID(), NameBuffer, 512);
+
+ if (Len != -1) // Invalid
+ {
+ GameName = FString(UTF8_TO_TCHAR(NameBuffer));
+ }*/
+
+ Result = EBlueprintResultSwitch::OnSuccess;
+ return;
+ }
+
+ }
+#endif
+
+ Result = EBlueprintResultSwitch::OnFailure;
+}
+
+int32 UAdvancedSteamFriendsLibrary::GetFriendSteamLevel(const FBPUniqueNetId UniqueNetId)
+{
+
+#if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX
+ if (!UniqueNetId.IsValid() || !UniqueNetId.UniqueNetId->IsValid() || UniqueNetId.UniqueNetId->GetType() != STEAM_SUBSYSTEM)
+ {
+ UE_LOG(AdvancedSteamFriendsLog, Warning, TEXT("IsAFriend Had a bad UniqueNetId!"));
+ return 0;
+ }
+
+ if (SteamAPI_Init())
+ {
+ uint64 id = *((uint64*)UniqueNetId.UniqueNetId->GetBytes());
+
+ return SteamFriends()->GetFriendSteamLevel(id);
+ }
+#endif
+
+ return 0;
+}
+
+FString UAdvancedSteamFriendsLibrary::GetSteamPersonaName(const FBPUniqueNetId UniqueNetId)
+{
+
+#if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX
+ if (!UniqueNetId.IsValid() || !UniqueNetId.UniqueNetId->IsValid() || UniqueNetId.UniqueNetId->GetType() != STEAM_SUBSYSTEM)
+ {
+ UE_LOG(AdvancedSteamFriendsLog, Warning, TEXT("GetSteamPersonaName Had a bad UniqueNetId!"));
+ return FString(TEXT(""));
+ }
+
+ if (SteamAPI_Init())
+ {
+ uint64 id = *((uint64*)UniqueNetId.UniqueNetId->GetBytes());
+ const char* PersonaName = SteamFriends()->GetFriendPersonaName(id);
+ return FString(UTF8_TO_TCHAR(PersonaName));
+ }
+#endif
+
+ return FString(TEXT(""));
+}
+
+FBPUniqueNetId UAdvancedSteamFriendsLibrary::CreateSteamIDFromString(const FString SteamID64)
+{
+ FBPUniqueNetId netId;
+
+#if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX
+ if (!(SteamID64.Len() > 0))
+ {
+ UE_LOG(AdvancedSteamFriendsLog, Warning, TEXT("CreateSteamIDFromString Had a bad UniqueNetId!"));
+ return netId;
+ }
+
+ if (SteamAPI_Init())
+ {
+ // Already does the conversion
+ TSharedPtr ValueID(new const FUniqueNetIdSteam2(SteamID64));
+ //FCString::Atoi64(*SteamID64));
+
+ netId.SetUniqueNetId(ValueID);
+ return netId;
+ }
+#endif
+
+ return netId;
+}
+
+FBPUniqueNetId UAdvancedSteamFriendsLibrary::GetLocalSteamIDFromSteam()
+{
+ FBPUniqueNetId netId;
+
+#if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX
+ if (SteamAPI_Init())
+ {
+ TSharedPtr SteamID(new const FUniqueNetIdSteam2(SteamUser()->GetSteamID()));
+ netId.SetUniqueNetId(SteamID);
+ }
+#endif
+
+ return netId;
+}
+
+bool UAdvancedSteamFriendsLibrary::RequestSteamFriendInfo(const FBPUniqueNetId UniqueNetId, bool bRequireNameOnly)
+{
+#if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX
+ if (!UniqueNetId.IsValid() || !UniqueNetId.UniqueNetId->IsValid() || UniqueNetId.UniqueNetId->GetType() != STEAM_SUBSYSTEM)
+ {
+ UE_LOG(AdvancedSteamFriendsLog, Warning, TEXT("RequestSteamFriendInfo Had a bad UniqueNetId!"));
+ return false;
+ }
+
+ if (SteamAPI_Init())
+ {
+ uint64 id = *((uint64*)UniqueNetId.UniqueNetId->GetBytes());
+
+ return !SteamFriends()->RequestUserInformation(id, bRequireNameOnly);
+ }
+#endif
+
+ UE_LOG(AdvancedSteamFriendsLog, Warning, TEXT("RequestSteamFriendInfo Couldn't init steamAPI!"));
+ return false;
+}
+
+
+bool UAdvancedSteamFriendsLibrary::OpenSteamUserOverlay(const FBPUniqueNetId UniqueNetId, ESteamUserOverlayType DialogType)
+{
+#if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX
+ if (!UniqueNetId.IsValid() || !UniqueNetId.UniqueNetId->IsValid() || UniqueNetId.UniqueNetId->GetType() != STEAM_SUBSYSTEM)
+ {
+ UE_LOG(AdvancedSteamFriendsLog, Warning, TEXT("OpenSteamUserOverlay Had a bad UniqueNetId!"));
+ return false;
+ }
+
+ if (SteamAPI_Init())
+ {
+ uint64 id = *((uint64*)UniqueNetId.UniqueNetId->GetBytes());
+ FString DialogName = EnumToString("ESteamUserOverlayType", (uint8)DialogType);
+ SteamFriends()->ActivateGameOverlayToUser(TCHAR_TO_ANSI(*DialogName), id);
+ return true;
+ }
+#endif
+
+ UE_LOG(AdvancedSteamFriendsLog, Warning, TEXT("OpenSteamUserOverlay Couldn't init steamAPI!"));
+ return false;
+}
+
+bool UAdvancedSteamFriendsLibrary::IsOverlayEnabled()
+{
+#if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX
+ if (SteamAPI_Init())
+ {
+ return SteamUtils()->IsOverlayEnabled();
+ }
+#endif
+
+ UE_LOG(AdvancedSteamFriendsLog, Warning, TEXT("OpenSteamUserOverlay Couldn't init steamAPI!"));
+ return false;
+}
+
+UTexture2D * UAdvancedSteamFriendsLibrary::GetSteamFriendAvatar(const FBPUniqueNetId UniqueNetId, EBlueprintAsyncResultSwitch &Result, SteamAvatarSize AvatarSize)
+{
+#if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX
+ if (!UniqueNetId.IsValid() || !UniqueNetId.UniqueNetId->IsValid() || UniqueNetId.UniqueNetId->GetType() != STEAM_SUBSYSTEM)
+ {
+ UE_LOG(AdvancedSteamFriendsLog, Warning, TEXT("GetSteamFriendAvatar Had a bad UniqueNetId!"));
+ Result = EBlueprintAsyncResultSwitch::OnFailure;
+ return nullptr;
+ }
+
+ uint32 Width = 0;
+ uint32 Height = 0;
+
+ if (SteamAPI_Init())
+ {
+ //Getting the PictureID from the SteamAPI and getting the Size with the ID
+ //virtual bool RequestUserInformation( CSteamID steamIDUser, bool bRequireNameOnly ) = 0;
+
+
+ uint64 id = *((uint64*)UniqueNetId.UniqueNetId->GetBytes());
+ int Picture = 0;
+
+ switch(AvatarSize)
+ {
+ case SteamAvatarSize::SteamAvatar_Small: Picture = SteamFriends()->GetSmallFriendAvatar(id); break;
+ case SteamAvatarSize::SteamAvatar_Medium: Picture = SteamFriends()->GetMediumFriendAvatar(id); break;
+ case SteamAvatarSize::SteamAvatar_Large: Picture = SteamFriends()->GetLargeFriendAvatar(id); break;
+ default: break;
+ }
+
+ if (Picture == -1)
+ {
+ Result = EBlueprintAsyncResultSwitch::AsyncLoading;
+ return NULL;
+ }
+
+ SteamUtils()->GetImageSize(Picture, &Width, &Height);
+
+ // STOLEN FROM ANSWERHUB :p, then fixed because answerhub wasn't releasing the memory O.o
+ // Also fixed image pixel format and switched to a memcpy instead of manual iteration.
+ // At some point I should probably reply to that answerhub post with these fixes to prevent people killing their games.....
+
+ if (Width > 0 && Height > 0)
+ {
+ //Creating the buffer "oAvatarRGBA" and then filling it with the RGBA Stream from the Steam Avatar
+ uint8 *oAvatarRGBA = new uint8[Width * Height * 4];
+
+
+ //Filling the buffer with the RGBA Stream from the Steam Avatar and creating a UTextur2D to parse the RGBA Steam in
+ SteamUtils()->GetImageRGBA(Picture, (uint8*)oAvatarRGBA, 4 * Height * Width * sizeof(char));
+
+
+ // Removed as I changed the image bit code to be RGB, I think the original author was unaware that there were different pixel formats
+ /*
+ //Swap R and B channels because for some reason the games whack
+ for (uint32 i = 0; i < (Width * Height * 4); i += 4)
+ {
+ uint8 Temp = oAvatarRGBA[i + 0];
+ oAvatarRGBA[i + 0] = oAvatarRGBA[i + 2];
+ oAvatarRGBA[i + 2] = Temp;
+ }*/
+
+ UTexture2D* Avatar = UTexture2D::CreateTransient(Width, Height, PF_R8G8B8A8);
+ // Switched to a Memcpy instead of byte by byte transer
+
+ if (FTexturePlatformData* PlatformData = Avatar->GetPlatformData())
+ {
+ uint8* MipData = (uint8*)PlatformData->Mips[0].BulkData.Lock(LOCK_READ_WRITE);
+ FMemory::Memcpy(MipData, (void*)oAvatarRGBA, Height * Width * 4);
+ PlatformData->Mips[0].BulkData.Unlock();
+
+ // Original implementation was missing this!!
+ // the hell man......
+ delete[] oAvatarRGBA;
+
+ //Setting some Parameters for the Texture and finally returning it
+ PlatformData->SetNumSlices(1);
+ Avatar->NeverStream = true;
+ //Avatar->CompressionSettings = TC_EditorIcon;
+ }
+
+ Avatar->UpdateResource();
+
+ Result = EBlueprintAsyncResultSwitch::OnSuccess;
+ return Avatar;
+ }
+ else
+ {
+ UE_LOG(AdvancedSteamFriendsLog, Warning, TEXT("Bad Height / Width with steam avatar!"));
+ }
+
+ Result = EBlueprintAsyncResultSwitch::OnFailure;
+ return nullptr;
+ }
+#endif
+
+ UE_LOG(AdvancedSteamFriendsLog, Warning, TEXT("STEAM Couldn't be verified as initialized"));
+ Result = EBlueprintAsyncResultSwitch::OnFailure;
+ return nullptr;
+}
+
+bool UAdvancedSteamFriendsLibrary::InitTextFiltering()
+{
+#if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX
+
+ if (SteamAPI_Init())
+ {
+ return SteamUtils()->InitFilterText();
+ }
+
+#endif
+
+ return false;
+}
+
+bool UAdvancedSteamFriendsLibrary::FilterText(FString TextToFilter, EBPTextFilteringContext Context, const FBPUniqueNetId TextSourceID, FString& FilteredText)
+{
+#if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX
+
+ if (SteamAPI_Init())
+ {
+ uint32 BufferLen = TextToFilter.Len() + 10; // Docs say 1 byte excess min, going with 10
+ char* OutText = new char[BufferLen];
+
+ uint64 id = 0;
+
+ if (TextSourceID.IsValid())
+ {
+ id = *((uint64*)TextSourceID.UniqueNetId->GetBytes());
+ }
+
+ int FilterCount = SteamUtils()->FilterText((ETextFilteringContext)Context, id, TCHAR_TO_ANSI(*TextToFilter), OutText, BufferLen);
+
+ if (FilterCount > 0)
+ {
+ FilteredText = FString(UTF8_TO_TCHAR(OutText));
+ delete[] OutText;
+ return true;
+ }
+
+ delete[] OutText;
+ }
+
+#endif
+
+ FilteredText = TextToFilter;
+ return false;
+}
+
+bool UAdvancedSteamFriendsLibrary::IsSteamInBigPictureMode()
+{
+#if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX
+
+ if (SteamAPI_Init())
+ {
+ return SteamUtils()->IsSteamInBigPictureMode();
+ }
+
+#endif
+
+ return false;
+}
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSteamSessions/Source/AdvancedSteamSessions/Private/AdvancedSteamSessions.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSteamSessions/Source/AdvancedSteamSessions/Private/AdvancedSteamSessions.cpp
new file mode 100644
index 0000000..34d9ff5
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSteamSessions/Source/AdvancedSteamSessions/Private/AdvancedSteamSessions.cpp
@@ -0,0 +1,12 @@
+//#include "StandAlonePrivatePCH.h"
+#include "AdvancedSteamSessions.h"
+
+void AdvancedSteamSessions::StartupModule()
+{
+}
+
+void AdvancedSteamSessions::ShutdownModule()
+{
+}
+
+IMPLEMENT_MODULE(AdvancedSteamSessions, AdvancedSteamSessions)
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSteamSessions/Source/AdvancedSteamSessions/Private/AdvancedSteamWorkshopLibrary.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSteamSessions/Source/AdvancedSteamSessions/Private/AdvancedSteamWorkshopLibrary.cpp
new file mode 100644
index 0000000..497f2b0
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSteamSessions/Source/AdvancedSteamSessions/Private/AdvancedSteamWorkshopLibrary.cpp
@@ -0,0 +1,69 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+#include "AdvancedSteamWorkshopLibrary.h"
+#include "OnlineSubSystemHeader.h"
+//General Log
+DEFINE_LOG_CATEGORY(AdvancedSteamWorkshopLog);
+
+
+void UAdvancedSteamWorkshopLibrary::GetNumSubscribedWorkshopItems(int32 & NumberOfItems)
+{
+ NumberOfItems = 0;
+#if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX
+
+ if (SteamAPI_Init())
+ {
+ NumberOfItems = SteamUGC()->GetNumSubscribedItems();
+ return;
+ }
+ else
+ {
+ UE_LOG(AdvancedSteamWorkshopLog, Warning, TEXT("Error in GetNumSubscribedWorkshopItemCount : SteamAPI is not Inited!"));
+ return;
+ }
+#endif
+
+ UE_LOG(AdvancedSteamWorkshopLog, Warning, TEXT("Error in GetNumSubscribedWorkshopItemCount : Called on an incompatible platform"));
+ return;
+}
+
+TArray UAdvancedSteamWorkshopLibrary::GetSubscribedWorkshopItems(int32 & NumberOfItems)
+{
+ TArray outArray;
+ NumberOfItems = 0;
+
+#if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX
+
+ if (SteamAPI_Init())
+ {
+ uint32 NumItems = SteamUGC()->GetNumSubscribedItems();
+
+ if (NumItems == 0)
+ return outArray;
+
+ // Not using the actual variable above in case someone somehow goes past int32 limits
+ // Don't want to go negative on the iteration.
+ NumberOfItems = NumItems;
+
+ PublishedFileId_t *fileIds = new PublishedFileId_t[NumItems];
+
+ uint32 subItems = SteamUGC()->GetSubscribedItems(fileIds, NumItems);
+
+ for (uint32 i = 0; i < subItems; ++i)
+ {
+ outArray.Add(FBPSteamWorkshopID(fileIds[i]));
+ }
+
+ delete[] fileIds;
+
+ return outArray;
+ }
+ else
+ {
+ UE_LOG(AdvancedSteamWorkshopLog, Warning, TEXT("Error in GetSubscribedWorkshopItemCount : SteamAPI is not Inited!"));
+ return outArray;
+ }
+#endif
+
+ UE_LOG(AdvancedSteamWorkshopLog, Warning, TEXT("Error in GetSubscribedWorkshopItemCount : Called on an incompatible platform"));
+ return outArray;
+}
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSteamSessions/Source/AdvancedSteamSessions/Private/SteamRequestGroupOfficersCallbackProxy.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSteamSessions/Source/AdvancedSteamSessions/Private/SteamRequestGroupOfficersCallbackProxy.cpp
new file mode 100644
index 0000000..337239c
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSteamSessions/Source/AdvancedSteamSessions/Private/SteamRequestGroupOfficersCallbackProxy.cpp
@@ -0,0 +1,121 @@
+// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
+
+#include "SteamRequestGroupOfficersCallbackProxy.h"
+#include "Online/CoreOnline.h"
+#include "AdvancedSteamFriendsLibrary.h"
+#include "OnlineSubSystemHeader.h"
+#if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX
+#include "steam/isteamfriends.h"
+#endif
+//#include "OnlineSubsystemSteamTypes.h"
+
+//////////////////////////////////////////////////////////////////////////
+// UEndSessionCallbackProxy
+
+USteamRequestGroupOfficersCallbackProxy::USteamRequestGroupOfficersCallbackProxy(const FObjectInitializer& ObjectInitializer)
+ : Super(ObjectInitializer)
+{
+}
+
+USteamRequestGroupOfficersCallbackProxy::~USteamRequestGroupOfficersCallbackProxy()
+{
+}
+
+USteamRequestGroupOfficersCallbackProxy* USteamRequestGroupOfficersCallbackProxy::GetSteamGroupOfficerList(UObject* WorldContextObject, FBPUniqueNetId GroupUniqueNetID)
+{
+ USteamRequestGroupOfficersCallbackProxy* Proxy = NewObject();
+
+ Proxy->GroupUniqueID = GroupUniqueNetID;
+ return Proxy;
+}
+
+void USteamRequestGroupOfficersCallbackProxy::Activate()
+{
+#if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX
+ if (SteamAPI_Init())
+ {
+ uint64 id = *((uint64*)GroupUniqueID.UniqueNetId->GetBytes());
+ SteamAPICall_t hSteamAPICall = SteamFriends()->RequestClanOfficerList(id);
+
+ m_callResultGroupOfficerRequestDetails.Set(hSteamAPICall, this, &USteamRequestGroupOfficersCallbackProxy::OnRequestGroupOfficerDetails);
+ return;
+ }
+#endif
+ TArray EmptyArray;
+ OnFailure.Broadcast(EmptyArray);
+}
+
+#if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX
+void USteamRequestGroupOfficersCallbackProxy::OnRequestGroupOfficerDetails(ClanOfficerListResponse_t *pResult, bool bIOFailure)
+{
+ TArray OfficerArray;
+
+ //FOnlineSubsystemSteam* SteamSubsystem = (FOnlineSubsystemSteam*)(IOnlineSubsystem::Get(STEAM_SUBSYSTEM));
+
+ if (bIOFailure || !pResult || !pResult->m_bSuccess)
+ {
+ //if (SteamSubsystem != nullptr)
+ {
+ // SteamSubsystem->ExecuteNextTick([this]()
+ //{
+ TArray FailureArray;
+ OnFailure.Broadcast(FailureArray);
+ //});
+ }
+ //OnFailure.Broadcast(OfficerArray);
+ return;
+ }
+
+ if (SteamAPI_Init())
+ {
+ uint64 id = *((uint64*)GroupUniqueID.UniqueNetId->GetBytes());
+
+ FBPSteamGroupOfficer Officer;
+ CSteamID ClanOwner = SteamFriends()->GetClanOwner(id);
+
+ Officer.bIsOwner = true;
+
+ TSharedPtr ValueID(new const FUniqueNetIdSteam2(ClanOwner));
+ Officer.OfficerUniqueNetID.SetUniqueNetId(ValueID);
+ OfficerArray.Add(Officer);
+
+ for (int i = 0; i < pResult->m_cOfficers; i++)
+ {
+ CSteamID OfficerSteamID = SteamFriends()->GetClanOfficerByIndex(id, i);
+
+ Officer.bIsOwner = false;
+
+ TSharedPtr newValueID(new const FUniqueNetIdSteam2(OfficerSteamID));
+ Officer.OfficerUniqueNetID.SetUniqueNetId(newValueID);
+
+ OfficerArray.Add(Officer);
+ }
+
+ //if (SteamSubsystem != nullptr)
+ //{
+ //SteamSubsystem->ExecuteNextTick([OfficerArray, this]()
+ //{
+ OnSuccess.Broadcast(OfficerArray);
+ //});
+ //}
+
+ //OnSuccess.Broadcast(OfficerArray);
+ return;
+ }
+ else
+ {
+ //if (SteamSubsystem != nullptr)
+ {
+ //SteamSubsystem->ExecuteNextTick([this]()
+ //{
+ TArray FailureArray;
+ OnFailure.Broadcast(FailureArray);
+ //});
+ }
+ }
+
+ // Should never hit this anyway
+ //OnFailure.Broadcast(OfficerArray);
+}
+#endif
+
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSteamSessions/Source/AdvancedSteamSessions/Private/SteamWSRequestUGCDetailsCallbackProxy.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSteamSessions/Source/AdvancedSteamSessions/Private/SteamWSRequestUGCDetailsCallbackProxy.cpp
new file mode 100644
index 0000000..95bee5c
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/AdvancedSteamSessions/Source/AdvancedSteamSessions/Private/SteamWSRequestUGCDetailsCallbackProxy.cpp
@@ -0,0 +1,101 @@
+// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
+
+#include "SteamWSRequestUGCDetailsCallbackProxy.h"
+#include "OnlineSubSystemHeader.h"
+#if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX
+#include "steam/isteamugc.h"
+#endif
+
+//////////////////////////////////////////////////////////////////////////
+// UEndSessionCallbackProxy
+
+USteamWSRequestUGCDetailsCallbackProxy::USteamWSRequestUGCDetailsCallbackProxy(const FObjectInitializer& ObjectInitializer)
+ : Super(ObjectInitializer)
+{
+}
+
+
+USteamWSRequestUGCDetailsCallbackProxy* USteamWSRequestUGCDetailsCallbackProxy::GetWorkshopItemDetails(UObject* WorldContextObject, FBPSteamWorkshopID WorkShopID/*, int32 NumSecondsBeforeTimeout*/)
+{
+ USteamWSRequestUGCDetailsCallbackProxy* Proxy = NewObject();
+
+ Proxy->WorkShopID = WorkShopID;
+ return Proxy;
+}
+
+void USteamWSRequestUGCDetailsCallbackProxy::Activate()
+{
+#if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX
+ if (SteamAPI_Init())
+ {
+ // #TODO: Support arrays instead in the future?
+ UGCQueryHandle_t hQueryHandle = SteamUGC()->CreateQueryUGCDetailsRequest((PublishedFileId_t *)&WorkShopID.SteamWorkshopID, 1);
+ // #TODO: add search settings here by calling into the handle?
+ SteamAPICall_t hSteamAPICall = SteamUGC()->SendQueryUGCRequest(hQueryHandle);
+
+ // Need to release the query
+ SteamUGC()->ReleaseQueryUGCRequest(hQueryHandle);
+
+ if (hSteamAPICall == k_uAPICallInvalid)
+ {
+ OnFailure.Broadcast(FBPSteamWorkshopItemDetails());
+ return;
+ }
+
+ m_callResultUGCRequestDetails.Set(hSteamAPICall, this, &USteamWSRequestUGCDetailsCallbackProxy::OnUGCRequestUGCDetails);
+ return;
+ }
+#endif
+ OnFailure.Broadcast(FBPSteamWorkshopItemDetails());
+}
+
+#if PLATFORM_WINDOWS || PLATFORM_MAC || PLATFORM_LINUX
+void USteamWSRequestUGCDetailsCallbackProxy::OnUGCRequestUGCDetails(SteamUGCQueryCompleted_t *pResult, bool bIOFailure)
+{
+ //FOnlineSubsystemSteam* SteamSubsystem = (FOnlineSubsystemSteam*)(IOnlineSubsystem::Get(STEAM_SUBSYSTEM));
+
+ if (bIOFailure || !pResult || pResult->m_unNumResultsReturned <= 0)
+ {
+ //if (SteamSubsystem != nullptr)
+ {
+ // SteamSubsystem->ExecuteNextTick([this]()
+ //{
+ OnFailure.Broadcast(FBPSteamWorkshopItemDetails());
+ //});
+ }
+ //OnFailure.Broadcast(FBPSteamWorkshopItemDetails());
+ return;
+ }
+ if (SteamAPI_Init())
+ {
+ SteamUGCDetails_t Details;
+ if (SteamUGC()->GetQueryUGCResult(pResult->m_handle, 0, &Details))
+ {
+ //if (SteamSubsystem != nullptr)
+ {
+ //SteamSubsystem->ExecuteNextTick([Details, this]()
+ //{
+ OnSuccess.Broadcast(FBPSteamWorkshopItemDetails(Details));
+ //});
+ }
+
+ //OnSuccess.Broadcast(FBPSteamWorkshopItemDetails(Details));
+ return;
+ }
+ }
+ else
+ {
+ //if (SteamSubsystem != nullptr)
+ {
+ //SteamSubsystem->ExecuteNextTick([this]()
+ //{
+ OnFailure.Broadcast(FBPSteamWorkshopItemDetails());
+ //});
+ }
+ }
+
+ // Not needed, should never hit here
+ //OnFailure.Broadcast(FBPSteamWorkshopItemDetails());
+}
+#endif
+
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/LICENSE.txt b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/LICENSE.txt
new file mode 100644
index 0000000..986c999
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/LICENSE.txt
@@ -0,0 +1,19 @@
+Copyright Joshua Statzer
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/README.md b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/README.md
new file mode 100644
index 0000000..c83724b
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/AdvancedSessions/README.md
@@ -0,0 +1,7 @@
+### How do I use it? ###
+
+**KantanDocGen Automatic Documentation ([KantanDocGen](http://kantandev.com/free/kantan-doc-gen))**
+
+**[AdvancedSessions](https://vreue4.com/generated-node-documentation?section=advanced-sessions-plugin)**
+
+**[AdvancedSteamSessions](https://vreue4.com/generated-node-documentation?section=advanced-steam-sessions-plugin)**
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/.gitattributes b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/.gitattributes
new file mode 100644
index 0000000..3373152
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/.gitattributes
@@ -0,0 +1,2 @@
+* text=auto
+*.bat eol=crlf
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/.gitignore b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/.gitignore
new file mode 100644
index 0000000..3cdb673
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/.gitignore
@@ -0,0 +1,10 @@
+
+.hg/
+binaries/
+deriveddatacache/
+.vs/
+build/
+intermediate/
+PACKPLUGIN/
+saved/
+*.orig
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/LICENSE.txt b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/LICENSE.txt
new file mode 100644
index 0000000..986c999
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/LICENSE.txt
@@ -0,0 +1,19 @@
+Copyright Joshua Statzer
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Config/FilterPlugin.ini b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Config/FilterPlugin.ini
new file mode 100644
index 0000000..ccebca2
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Config/FilterPlugin.ini
@@ -0,0 +1,8 @@
+[FilterPlugin]
+; This section lists additional files which will be packaged along with your plugin. Paths should be listed relative to the root plugin directory, and
+; may include "...", "*", and "?" wildcards to match directories, files, and individual characters respectively.
+;
+; Examples:
+; /README.txt
+; /Extras/...
+; /Binaries/ThirdParty/*.dll
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/OpenXRExpansionPlugin.uplugin b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/OpenXRExpansionPlugin.uplugin
new file mode 100644
index 0000000..ef2e2b0
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/OpenXRExpansionPlugin.uplugin
@@ -0,0 +1,45 @@
+{
+ "FileVersion": 3,
+ "Version": 5.3,
+ "VersionName": "5.3",
+ "FriendlyName": "OpenXRExpansionPlugin",
+ "Description": "An set of utility functions for OpenXR",
+ "Category": "Virtual Reality",
+ "CreatedBy": "Joshua (MordenTral) Statzer",
+ "CreatedByURL": "http://www.vreue4.com",
+ "DocsURL": "http://www.vreue4.com",
+ "MarketplaceURL": "",
+ "SupportURL": "http://www.vreue4.com",
+ "EnabledByDefault": false,
+ "CanContainContent": false,
+ "IsBetaVersion": false,
+ "Installed": true,
+ "SupportedTargetPlatforms": [
+ "Win64",
+ "Linux",
+ "Android"
+ ],
+ "Modules": [
+ {
+ "Name": "OpenXRExpansionPlugin",
+ "Type": "Runtime",
+ "LoadingPhase": "PreDefault"
+ }
+ ,
+ {
+ "Name": "OpenXRExpansionEditor",
+ "Type": "UnCookedOnly",
+ "LoadingPhase": "PostEngineInit"
+ }
+ ],
+ "Plugins": [
+ {
+ "Name": "OpenXR",
+ "Enabled": true
+ },
+ {
+ "Name": "XRBase",
+ "Enabled": true
+ }
+ ]
+}
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Resources/Icon128.png b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Resources/Icon128.png
new file mode 100644
index 0000000..23fec31
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Resources/Icon128.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bb8f329acc9d0c2815d28349d5a4144ebd337f7e2a93819cf1a7c3cc9b06ecea
+size 16085
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionEditor/OpenXRExpansionEditor.Build.cs b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionEditor/OpenXRExpansionEditor.Build.cs
new file mode 100644
index 0000000..36288b9
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionEditor/OpenXRExpansionEditor.Build.cs
@@ -0,0 +1,61 @@
+// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
+
+using System.IO;
+
+namespace UnrealBuildTool.Rules
+{
+ public class OpenXRExpansionEditor : ModuleRules
+ {
+
+ public OpenXRExpansionEditor(ReadOnlyTargetRules Target) : base(Target)
+ {
+ PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
+
+ PublicIncludePaths.AddRange(
+ new string[] {
+ // ... add public include paths required here ...
+ }
+ );
+
+ PrivateIncludePaths.AddRange(
+ new string[] {
+ // ... add other private include paths required here ...
+ }
+ );
+
+ PublicDependencyModuleNames.AddRange(
+ new string[]
+ {
+ // ... add other public dependencies that you statically link with here ...
+ "Engine",
+ "Core",
+ "CoreUObject"
+ }
+ );
+
+ PrivateDependencyModuleNames.AddRange(
+ new string[]
+ {
+ "UnrealEd",
+ "BlueprintGraph",
+ "AnimGraph",
+ "AnimGraphRuntime",
+ "SlateCore",
+ "Slate",
+ "InputCore",
+ "Engine",
+ "EditorStyle",
+ "AssetRegistry",
+ "OpenXRExpansionPlugin"
+ }
+ );
+
+ DynamicallyLoadedModuleNames.AddRange(
+ new string[]
+ {
+ // ... add any modules that your module loads dynamically here ...
+ }
+ );
+ }
+ }
+}
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionEditor/Private/AnimGraphNode_ApplyOpenXRHandPose.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionEditor/Private/AnimGraphNode_ApplyOpenXRHandPose.cpp
new file mode 100644
index 0000000..1a109c9
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionEditor/Private/AnimGraphNode_ApplyOpenXRHandPose.cpp
@@ -0,0 +1,34 @@
+// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
+
+#include "AnimGraphNode_ApplyOpenXRHandPose.h"
+#include UE_INLINE_GENERATED_CPP_BY_NAME(AnimGraphNode_ApplyOpenXRHandPose)
+
+/////////////////////////////////////////////////////
+// UAnimGraphNode_ModifyBSHand
+
+UAnimGraphNode_ApplyOpenXRHandPose::UAnimGraphNode_ApplyOpenXRHandPose(const FObjectInitializer& Initializer)
+ : Super(Initializer)
+{
+}
+
+//Title Color!
+FLinearColor UAnimGraphNode_ApplyOpenXRHandPose::GetNodeTitleColor() const
+{
+ return FLinearColor(12, 12, 0, 1);
+}
+
+//Node Category
+FString UAnimGraphNode_ApplyOpenXRHandPose::GetNodeCategory() const
+{
+ return FString("OpenXR");
+}
+FText UAnimGraphNode_ApplyOpenXRHandPose::GetControllerDescription() const
+{
+ return FText::FromString("Apply OpenXR Hand Pose");
+}
+
+FText UAnimGraphNode_ApplyOpenXRHandPose::GetNodeTitle(ENodeTitleType::Type TitleType) const
+{
+ FText Result = GetControllerDescription();
+ return Result;
+}
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionEditor/Private/OpenXRExpansionEditor.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionEditor/Private/OpenXRExpansionEditor.cpp
new file mode 100644
index 0000000..f22c5fc
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionEditor/Private/OpenXRExpansionEditor.cpp
@@ -0,0 +1,16 @@
+// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
+
+#include "OpenXRExpansionEditor.h"
+#include "Editor/UnrealEdEngine.h"
+#include "UnrealEdGlobals.h"
+
+
+IMPLEMENT_MODULE(FOpenXRExpansionEditorModule, OpenXRExpansionEditor);
+
+void FOpenXRExpansionEditorModule::StartupModule()
+{
+}
+
+void FOpenXRExpansionEditorModule::ShutdownModule()
+{
+}
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionEditor/Public/AnimGraphNode_ApplyOpenXRHandPose.h b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionEditor/Public/AnimGraphNode_ApplyOpenXRHandPose.h
new file mode 100644
index 0000000..b9d5db3
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionEditor/Public/AnimGraphNode_ApplyOpenXRHandPose.h
@@ -0,0 +1,34 @@
+#pragma once
+
+#include "AnimGraphDefinitions.h"
+#include "Kismet2/BlueprintEditorUtils.h"
+#include "Editor/AnimGraph/Public/AnimGraphNode_SkeletalControlBase.h"
+
+#include "AnimNode_ApplyOpenXRHandPose.h"
+// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
+
+#include "AnimGraphNode_ApplyOpenXRHandPose.generated.h"
+
+UCLASS(MinimalAPI)
+class UAnimGraphNode_ApplyOpenXRHandPose : public UAnimGraphNode_SkeletalControlBase
+{
+ GENERATED_UCLASS_BODY()
+
+ UPROPERTY(EditAnywhere, Category = Settings)
+ FAnimNode_ApplyOpenXRHandPose Node;
+
+public:
+ // UEdGraphNode interface
+ virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override;
+ virtual FLinearColor GetNodeTitleColor() const override;
+ virtual FString GetNodeCategory() const override;
+ // End of UEdGraphNode interface
+
+protected:
+
+ // UAnimGraphNode_SkeletalControlBase protected interface
+ virtual FText GetControllerDescription() const;
+ virtual const FAnimNode_SkeletalControlBase* GetNode() const override { return &Node; }
+ // End of UAnimGraphNode_SkeletalControlBase protected interface
+
+};
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionEditor/Public/OpenXRExpansionEditor.h b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionEditor/Public/OpenXRExpansionEditor.h
new file mode 100644
index 0000000..92ed6bc
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionEditor/Public/OpenXRExpansionEditor.h
@@ -0,0 +1,13 @@
+// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
+
+#pragma once
+
+#include "Runtime/Core/Public/Modules/ModuleInterface.h"
+
+class FOpenXRExpansionEditorModule : public IModuleInterface
+{
+public:
+
+ virtual void StartupModule() override;
+ virtual void ShutdownModule() override;
+};
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionPlugin/OpenXRExpansionPlugin.Build.cs b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionPlugin/OpenXRExpansionPlugin.Build.cs
new file mode 100644
index 0000000..5e4b5d1
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionPlugin/OpenXRExpansionPlugin.Build.cs
@@ -0,0 +1,70 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+
+using UnrealBuildTool;
+using System.IO;
+
+namespace UnrealBuildTool.Rules
+{
+ public class OpenXRExpansionPlugin: ModuleRules
+ {
+ public OpenXRExpansionPlugin(ReadOnlyTargetRules Target)
+ : base(Target)
+ {
+ PublicDependencyModuleNames.AddRange(
+ new string[]
+ {
+ //"InputDevice",
+ //"LiveLink",
+ //"LiveLinkInterface"
+ }
+ );
+
+ var EngineDir = Path.GetFullPath(Target.RelativeEnginePath);
+ PrivateIncludePaths.AddRange(
+ new string[] {
+ EngineDir + "Plugins/Runtime/OpenXR/Source/OpenXRHMD/Private",
+ EngineDir + "/Source/ThirdParty/OpenXR/include",
+ // ... add other private include paths required here ...
+ }
+ );
+
+ PrivateDependencyModuleNames.AddRange(
+ new string[]
+ {
+ "Core",
+ "NetCore",
+ "CoreUObject",
+ //"ApplicationCore",
+ "Engine",
+ //"InputDevice",
+ "InputCore",
+ "Slate",
+ "HeadMountedDisplay",
+ //"AnimGraph",
+ "AnimGraphRuntime",
+ "SlateCore",
+ "XRBase"
+ //"LiveLink",
+ //"LiveLinkInterface",
+ }
+ );
+
+ if (Target.Platform != UnrealTargetPlatform.Mac)
+ {
+ PrivateDependencyModuleNames.AddRange(
+ new string[]
+ {
+ "OpenXRHMD"
+ }
+ );
+ PrivateDefinitions.AddRange(new string[] { "OPENXR_SUPPORTED" });
+ AddEngineThirdPartyPrivateStaticDependencies(Target, "OpenXR");
+ }
+
+ // if (Target.bBuildEditor == true)
+ // {
+ // PrivateDependencyModuleNames.Add("UnrealEd");
+ // }
+ }
+ }
+}
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionPlugin/Private/AnimNode_ApplyOpenXRHandPose.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionPlugin/Private/AnimNode_ApplyOpenXRHandPose.cpp
new file mode 100644
index 0000000..6a14619
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionPlugin/Private/AnimNode_ApplyOpenXRHandPose.cpp
@@ -0,0 +1,463 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+#include "AnimNode_ApplyOpenXRHandPose.h"
+#include UE_INLINE_GENERATED_CPP_BY_NAME(AnimNode_ApplyOpenXRHandPose)
+
+//#include "EngineMinimal.h"
+//#include "Engine/Engine.h"
+//#include "CoreMinimal.h"
+#include "OpenXRExpansionFunctionLibrary.h"
+#include "AnimNode_ApplyOpenXRHandPose.h"
+#include "AnimationRuntime.h"
+#include "DrawDebugHelpers.h"
+#include "OpenXRHandPoseComponent.h"
+#include "Runtime/Engine/Public/Animation/AnimInstanceProxy.h"
+#include "BoneControllers/AnimNode_SkeletalControlBase.h"
+
+FAnimNode_ApplyOpenXRHandPose::FAnimNode_ApplyOpenXRHandPose()
+ : FAnimNode_SkeletalControlBase()
+{
+ WorldIsGame = false;
+ Alpha = 1.f;
+ SkeletonType = EVROpenXRSkeletonType::OXR_SkeletonType_UE4Default_Right;
+ bIsOpenInputAnimationInstance = false;
+ bSkipRootBone = false;
+ bOnlyApplyWristTransform = false;
+ //WristAdjustment = FQuat::Identity;
+}
+
+void FAnimNode_ApplyOpenXRHandPose::OnInitializeAnimInstance(const FAnimInstanceProxy* InProxy, const UAnimInstance* InAnimInstance)
+{
+ Super::OnInitializeAnimInstance(InProxy, InAnimInstance);
+
+ if (const UOpenXRAnimInstance * animInst = Cast(InAnimInstance))
+ {
+ bIsOpenInputAnimationInstance = true;
+ }
+}
+
+void FAnimNode_ApplyOpenXRHandPose::Initialize_AnyThread(const FAnimationInitializeContext& Context)
+{
+ Super::Initialize_AnyThread(Context);
+}
+
+void FAnimNode_ApplyOpenXRHandPose::CacheBones_AnyThread(const FAnimationCacheBonesContext& Context)
+{
+ Super::CacheBones_AnyThread(Context);
+}
+
+void FAnimNode_ApplyOpenXRHandPose::InitializeBoneReferences(const FBoneContainer& RequiredBones)
+{
+ UObject* OwningAsset = RequiredBones.GetAsset();
+ if (!OwningAsset)
+ return;
+
+ USkeleton* AssetSkeleton = RequiredBones.GetSkeletonAsset();
+
+ if (!AssetSkeleton)
+ return;
+
+ if (!MappedBonePairs.bInitialized || OwningAsset->GetFName() != MappedBonePairs.LastInitializedName || SkeletonType != MappedBonePairs.LastInitializedSkeleton)
+ {
+
+ // Trigger a full re-build if our asset changed
+ if (MappedBonePairs.bInitialized && (OwningAsset->GetFName() != MappedBonePairs.LastInitializedName || SkeletonType != MappedBonePairs.LastInitializedSkeleton))
+ {
+ MappedBonePairs.ClearMapping();
+ }
+
+ MappedBonePairs.LastInitializedName = OwningAsset->GetFName();
+ MappedBonePairs.LastInitializedSkeleton = SkeletonType;
+ MappedBonePairs.bInitialized = false;
+
+ if (AssetSkeleton)
+ {
+ // If our bone pairs are empty, then setup our sane defaults
+ if (!MappedBonePairs.BonePairs.Num())
+ {
+
+ MappedBonePairs.ConstructDefaultMappings(SkeletonType, bSkipRootBone);
+ }
+
+ // Construct a reverse map of our joints
+ MappedBonePairs.ConstructReverseMapping();
+
+ TArray RefBones = AssetSkeleton->GetReferenceSkeleton().GetRefBonePose();
+ TArray RefBonesInfo = AssetSkeleton->GetReferenceSkeleton().GetRefBoneInfo();
+
+ for (FBPOpenXRSkeletalPair& BonePair : MappedBonePairs.BonePairs)
+ {
+ // Fill in the bone name for the reference
+ BonePair.ReferenceToConstruct.BoneName = BonePair.BoneToTarget;
+
+ // Init the reference
+ BonePair.ReferenceToConstruct.Initialize(AssetSkeleton);
+
+ BonePair.ReferenceToConstruct.CachedCompactPoseIndex = BonePair.ReferenceToConstruct.GetCompactPoseIndex(RequiredBones);
+
+ if ((BonePair.ReferenceToConstruct.CachedCompactPoseIndex != INDEX_NONE))
+ {
+ // Get our parent bones index
+ BonePair.ParentReference = RequiredBones.GetParentBoneIndex(BonePair.ReferenceToConstruct.CachedCompactPoseIndex);
+ }
+ }
+
+ MappedBonePairs.bInitialized = true;
+
+ if (SkeletonType == EVROpenXRSkeletonType::OXR_SkeletonType_OpenVRDefault_Left || SkeletonType == EVROpenXRSkeletonType::OXR_SkeletonType_OpenVRDefault_Right)
+ {
+ // We hard code this for now because I don't like their wrist being a different transform
+ MappedBonePairs.AdjustmentQuat = FRotator(0.f, 90.f, 180.f).Quaternion(); // Current one is incorrect without wrist
+ // Maybe do it in relative space?
+ }
+ else
+ {
+ CalculateSkeletalAdjustment(AssetSkeleton);
+ }
+
+ }
+ }
+}
+
+void FAnimNode_ApplyOpenXRHandPose::CalculateSkeletalAdjustment(USkeleton* AssetSkeleton)
+{
+
+ TArray RefBones = AssetSkeleton->GetReferenceSkeleton().GetRefBonePose();
+ TArray RefBonesInfo = AssetSkeleton->GetReferenceSkeleton().GetRefBoneInfo();
+
+ if (!MappedBonePairs.bInitialized || MappedBonePairs.BonePairs.Num() < 4 || !RefBones.Num())
+ {
+ UE_LOG(LogTemp, Error, TEXT("Empty or incorrect mapping or skeleton data when calculating skeletal adjustment!"));
+ return;
+ }
+
+ FBPOpenXRSkeletalPair KnuckleIndexPair = MappedBonePairs.BonePairs[MappedBonePairs.ReverseBonePairMap[(int8)EXRHandJointType::OXR_HAND_JOINT_INDEX_PROXIMAL_EXT]];
+ FBPOpenXRSkeletalPair KnuckleMiddlePair = MappedBonePairs.BonePairs[MappedBonePairs.ReverseBonePairMap[(int8)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_PROXIMAL_EXT]];
+ FBPOpenXRSkeletalPair KnuckleRingPair = MappedBonePairs.BonePairs[MappedBonePairs.ReverseBonePairMap[(int8)EXRHandJointType::OXR_HAND_JOINT_RING_PROXIMAL_EXT]];
+ FBPOpenXRSkeletalPair KnucklePinkyPair = MappedBonePairs.BonePairs[MappedBonePairs.ReverseBonePairMap[(int8)EXRHandJointType::OXR_HAND_JOINT_LITTLE_PROXIMAL_EXT]];
+
+ FBPOpenXRSkeletalPair WristPair = MappedBonePairs.BonePairs[MappedBonePairs.ReverseBonePairMap[(int8)EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT]];
+
+ FVector KnuckleAverage = GetRefBoneInCS(RefBones, RefBonesInfo, KnuckleIndexPair.ReferenceToConstruct.BoneIndex).GetTranslation();
+ KnuckleAverage += GetRefBoneInCS(RefBones, RefBonesInfo, KnuckleMiddlePair.ReferenceToConstruct.BoneIndex).GetTranslation();
+ KnuckleAverage += GetRefBoneInCS(RefBones, RefBonesInfo, KnuckleRingPair.ReferenceToConstruct.BoneIndex).GetTranslation();
+ KnuckleAverage += GetRefBoneInCS(RefBones, RefBonesInfo, KnucklePinkyPair.ReferenceToConstruct.BoneIndex).GetTranslation();
+
+ // Get our average across the knuckles
+ KnuckleAverage /= 4.f;
+
+ // Obtain the UE4 wrist Side & Forward directions from first animation frame and place in cache
+ FTransform WristTransform_UE = GetRefBoneInCS(RefBones, RefBonesInfo, WristPair.ReferenceToConstruct.BoneIndex);
+ FVector ToKnuckleAverage_UE = KnuckleAverage - WristTransform_UE.GetTranslation();
+ ToKnuckleAverage_UE.Normalize();
+
+
+ WristForwardLS_UE = WristTransform_UE.GetRotation().UnrotateVector(ToKnuckleAverage_UE);
+ SetVectorToMaxElement(WristForwardLS_UE);
+ WristSideDirectionLS = FVector::CrossProduct(WristForwardLS_UE, FVector::RightVector);
+ SetVectorToMaxElement(WristSideDirectionLS);
+
+ CalculateOpenXRAdjustment();
+}
+
+void FAnimNode_ApplyOpenXRHandPose::CalculateOpenXRAdjustment()
+{
+ // Base implementation is like valves
+
+ // Forward direction
+ static FVector OpenXRForwardDirection = FVector(1.0f, 0.f, 0.f);
+
+ // Side direction
+ // Do I need to flip this for left hand?
+ static FVector OpenXRSideDirection = FVector(0.f, 1.f, 0.f);
+
+ // Align forward vectors, openXR once in engine is X+ forward
+ FQuat AlignmentRot = FQuat::FindBetweenNormals(WristForwardLS_UE, OpenXRForwardDirection);
+
+ // Rotate about the aligned forward direction to make the side directions align
+ FVector WristSideDirectionMS_UE = AlignmentRot * WristSideDirectionLS;
+
+ // Rotate around, should the Side direction flip for openXR if its the left hand?
+ FQuat TwistRotation = CalcRotationAboutAxis(WristSideDirectionMS_UE, OpenXRSideDirection, OpenXRForwardDirection);
+
+ FRotator Difference = (TwistRotation * AlignmentRot).Rotator();
+
+ MappedBonePairs.AdjustmentQuat = (TwistRotation * AlignmentRot).GetNormalized();
+
+}
+
+void FAnimNode_ApplyOpenXRHandPose::ConvertHandTransformsSpace(TArray& OutTransforms, const TArray& WorldTransforms, FTransform AddTrans, bool bMirrorLeftRight, bool bMergeMissingUE4Bones)
+{
+ // Fail if the count is too low
+ if (WorldTransforms.Num() < EHandKeypointCount)
+ return;
+
+ if (OutTransforms.Num() < WorldTransforms.Num())
+ {
+ OutTransforms.Empty(WorldTransforms.Num());
+ OutTransforms.AddUninitialized(WorldTransforms.Num());
+ }
+
+ TArray TempWorldTransforms = WorldTransforms;
+
+ // Ensure add trans is normalized
+ AddTrans.NormalizeRotation();
+
+ // Bone/Parent map
+ int32 BoneParents[26] =
+ {
+ // Manually build the parent hierarchy starting at the wrist which has no parent (-1)
+ 1, // Palm -> Wrist
+ -1, // Wrist -> None
+ 1, // ThumbMetacarpal -> Wrist
+ 2, // ThumbProximal -> ThumbMetacarpal
+ 3, // ThumbDistal -> ThumbProximal
+ 4, // ThumbTip -> ThumbDistal
+
+ 1, // IndexMetacarpal -> Wrist
+ 6, // IndexProximal -> IndexMetacarpal
+ 7, // IndexIntermediate -> IndexProximal
+ 8, // IndexDistal -> IndexIntermediate
+ 9, // IndexTip -> IndexDistal
+
+ 1, // MiddleMetacarpal -> Wrist
+ 11, // MiddleProximal -> MiddleMetacarpal
+ 12, // MiddleIntermediate -> MiddleProximal
+ 13, // MiddleDistal -> MiddleIntermediate
+ 14, // MiddleTip -> MiddleDistal
+
+ 1, // RingMetacarpal -> Wrist
+ 16, // RingProximal -> RingMetacarpal
+ 17, // RingIntermediate -> RingProximal
+ 18, // RingDistal -> RingIntermediate
+ 19, // RingTip -> RingDistal
+
+ 1, // LittleMetacarpal -> Wrist
+ 21, // LittleProximal -> LittleMetacarpal
+ 22, // LittleIntermediate -> LittleProximal
+ 23, // LittleDistal -> LittleIntermediate
+ 24, // LittleTip -> LittleDistal
+ };
+
+ bool bUseAutoCalculatedRetarget = AddTrans.Equals(FTransform::Identity);
+
+ // Convert transforms to parent space
+ // The hand tracking transforms are in world space.
+
+ for (int32 Index = 0; Index < EHandKeypointCount; ++Index)
+ {
+ if (TempWorldTransforms[Index].ContainsNaN() || TempWorldTransforms[Index].Equals(FTransform::Identity))
+ {
+ OutTransforms[Index] = FTransform::Identity;
+ //continue;
+ }
+
+ // Ensure normalization
+ TempWorldTransforms[Index].NormalizeRotation();
+
+ if (bMirrorLeftRight)
+ {
+ TempWorldTransforms[Index].Mirror(EAxis::Y, EAxis::Y);
+ }
+
+ if (bUseAutoCalculatedRetarget)
+ {
+ TempWorldTransforms[Index].ConcatenateRotation(MappedBonePairs.AdjustmentQuat);
+ //WorldTransforms[Index].ConcatenateRotation(MappedBonePairs.BonePairs[0].RetargetRot);
+ }
+ else
+ {
+ TempWorldTransforms[Index].ConcatenateRotation(AddTrans.GetRotation());
+ }
+ }
+
+ // Make this into a single loop, their structure always has children after parent
+ for (int32 Index = 0; Index < EHandKeypointCount; ++Index)
+ {
+ FTransform& BoneTransform = TempWorldTransforms[Index];
+ //BoneTransform.NormalizeRotation();
+
+ int32 ParentIndex = BoneParents[Index];
+ int32 ParentParent = -1;
+
+ // Thumb keeps the metacarpal intact, we don't skip it
+ if (bMergeMissingUE4Bones)
+ {
+ if (Index != (int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_PROXIMAL_EXT && ParentIndex > 0)
+ {
+ ParentParent = BoneParents[ParentIndex];
+ }
+ }
+
+ if (ParentIndex < 0)
+ {
+ // We are at the root, so use it.
+ OutTransforms[Index] = BoneTransform;
+ }
+ else
+ {
+ FTransform ParentTransform = FTransform::Identity;
+
+ // Merging missing metacarpal bone into the transform
+ if (bMergeMissingUE4Bones && ParentParent == 1) // Wrist
+ {
+ ParentTransform = TempWorldTransforms[ParentParent];
+ }
+ else
+ {
+ ParentTransform = TempWorldTransforms[ParentIndex];
+ }
+
+ //ParentTransform.NormalizeRotation();
+ OutTransforms[Index] = BoneTransform.GetRelativeTransform(ParentTransform);
+ }
+ }
+}
+
+void FAnimNode_ApplyOpenXRHandPose::EvaluateSkeletalControl_AnyThread(FComponentSpacePoseContext& Output, TArray& OutBoneTransforms)
+{
+ if (!MappedBonePairs.bInitialized)
+ return;
+
+
+ const FBoneContainer& BoneContainer = Output.Pose.GetPose().GetBoneContainer();
+
+ UObject* OwningAsset = BoneContainer.GetAsset();
+ if (!OwningAsset)
+ return;
+
+ // Trigger a full re-build if our asset or target skeleton changed, do it up here before finding the correct hand
+ if ((OwningAsset->GetFName() != MappedBonePairs.LastInitializedName || SkeletonType != MappedBonePairs.LastInitializedSkeleton))
+ {
+ InitializeBoneReferences(BoneContainer);
+ }
+
+
+ /*const */FBPOpenXRActionSkeletalData *StoredActionInfoPtr = nullptr;
+ if (bIsOpenInputAnimationInstance)
+ {
+ /*const*/ FOpenXRAnimInstanceProxy* OpenXRAnimInstance = (FOpenXRAnimInstanceProxy*)Output.AnimInstanceProxy;
+ if (OpenXRAnimInstance->HandSkeletalActionData.Num())
+ {
+ for (int i = 0; i HandSkeletalActionData.Num(); ++i)
+ {
+ EVRSkeletalHandIndex TargetHand = OpenXRAnimInstance->HandSkeletalActionData[i].TargetHand;
+
+ if (OpenXRAnimInstance->HandSkeletalActionData[i].bMirrorLeftRight)
+ {
+ TargetHand = (TargetHand == EVRSkeletalHandIndex::EActionHandIndex_Left) ? EVRSkeletalHandIndex::EActionHandIndex_Right : EVRSkeletalHandIndex::EActionHandIndex_Left;
+ }
+
+ if (TargetHand == MappedBonePairs.TargetHand)
+ {
+ StoredActionInfoPtr = &OpenXRAnimInstance->HandSkeletalActionData[i];
+ break;
+ }
+ }
+ }
+ }
+
+ // If we have an empty hand pose but have a passed in custom one then use that
+ if (StoredActionInfoPtr == nullptr || !StoredActionInfoPtr->SkeletalTransforms.Num())
+ {
+ StoredActionInfoPtr = &OptionalStoredActionInfo;
+ }
+
+ if (!StoredActionInfoPtr->bHasValidData)
+ {
+ return;
+ }
+
+ //MappedBonePairs.AdjustmentQuat = WristAdjustment;
+
+ // Currently not blending correctly
+ const float BlendWeight = FMath::Clamp(ActualAlpha, 0.f, 1.f);
+ uint8 BoneTransIndex = 0;
+ uint8 NumBones = StoredActionInfoPtr ? StoredActionInfoPtr->SkeletalTransforms.Num() : 0;
+
+ if (NumBones < 1)
+ {
+ // Early out, we don't have a valid data to work with
+ return;
+ }
+
+
+
+
+ FTransform trans = FTransform::Identity;
+ OutBoneTransforms.Reserve(MappedBonePairs.BonePairs.Num());
+ TArray TransBones;
+ FTransform AdditionTransform = StoredActionInfoPtr->AdditionTransform;
+
+ FTransform TempTrans = FTransform::Identity;
+ FTransform ParentTrans = FTransform::Identity;
+ FTransform * ParentTransPtr = nullptr;
+
+ //AdditionTransform.SetRotation(MappedBonePairs.AdjustmentQuat);
+
+ TArray HandTransforms;
+ ConvertHandTransformsSpace(HandTransforms, StoredActionInfoPtr->SkeletalTransforms, AdditionTransform, StoredActionInfoPtr->bMirrorLeftRight, MappedBonePairs.bMergeMissingBonesUE4);
+
+ for (const FBPOpenXRSkeletalPair& BonePair : MappedBonePairs.BonePairs)
+ {
+ BoneTransIndex = (int8)BonePair.OpenXRBone;
+ ParentTrans = FTransform::Identity;
+
+ if (bSkipRootBone && BonePair.OpenXRBone == EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT)
+ continue;
+
+ if (BoneTransIndex >= NumBones || BonePair.ReferenceToConstruct.CachedCompactPoseIndex == INDEX_NONE)
+ continue;
+
+ if (!BonePair.ReferenceToConstruct.IsValidToEvaluate(BoneContainer))
+ {
+ continue;
+ }
+
+ trans = Output.Pose.GetComponentSpaceTransform(BonePair.ReferenceToConstruct.CachedCompactPoseIndex);
+
+ if (BonePair.ParentReference != INDEX_NONE)
+ {
+ ParentTrans = Output.Pose.GetComponentSpaceTransform(BonePair.ParentReference);
+ ParentTrans.SetScale3D(FVector(1.f));
+ }
+
+ EXRHandJointType CurrentBone = (EXRHandJointType)BoneTransIndex;
+ TempTrans = (HandTransforms[BoneTransIndex]);
+ //TempTrans.ConcatenateRotation(BonePair.RetargetRot);
+
+ /*if (StoredActionInfoPtr->bMirrorHand)
+ {
+ FMatrix M = TempTrans.ToMatrixWithScale();
+ M.Mirror(EAxis::Z, EAxis::X);
+ M.Mirror(EAxis::X, EAxis::Z);
+ TempTrans.SetFromMatrix(M);
+ }*/
+
+ TempTrans = TempTrans * ParentTrans;
+
+ if (StoredActionInfoPtr->bAllowDeformingMesh || bOnlyApplyWristTransform)
+ trans.SetTranslation(TempTrans.GetTranslation());
+
+ trans.SetRotation(TempTrans.GetRotation());
+
+ TransBones.Add(FBoneTransform(BonePair.ReferenceToConstruct.CachedCompactPoseIndex, trans));
+
+ // Need to do it per bone so future bones are correct
+ // Only if in parent space though, can do it all at the end in component space
+ if (TransBones.Num())
+ {
+ Output.Pose.LocalBlendCSBoneTransforms(TransBones, BlendWeight);
+ TransBones.Reset();
+ }
+
+ if (bOnlyApplyWristTransform && CurrentBone == EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT)
+ {
+ break; // Early out of the loop, we only wanted to apply the wrist
+ }
+ }
+}
+
+bool FAnimNode_ApplyOpenXRHandPose::IsValidToEvaluate(const USkeleton* Skeleton, const FBoneContainer& RequiredBones)
+{
+ return(/*MappedBonePairs.bInitialized && */MappedBonePairs.BonePairs.Num() > 0);
+}
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionPlugin/Private/OpenXRExpansionFunctionLibrary.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionPlugin/Private/OpenXRExpansionFunctionLibrary.cpp
new file mode 100644
index 0000000..89c9aa1
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionPlugin/Private/OpenXRExpansionFunctionLibrary.cpp
@@ -0,0 +1,587 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+#include "OpenXRExpansionFunctionLibrary.h"
+#include UE_INLINE_GENERATED_CPP_BY_NAME(OpenXRExpansionFunctionLibrary)
+
+//#include "EngineMinimal.h"
+#include "Engine/Engine.h"
+#include
+#include "CoreMinimal.h"
+#include "IXRTrackingSystem.h"
+
+//General Log
+DEFINE_LOG_CATEGORY(OpenXRExpansionFunctionLibraryLog);
+
+UOpenXRExpansionFunctionLibrary::UOpenXRExpansionFunctionLibrary(const FObjectInitializer& ObjectInitializer)
+ : Super(ObjectInitializer)
+{
+}
+
+//=============================================================================
+UOpenXRExpansionFunctionLibrary::~UOpenXRExpansionFunctionLibrary()
+{
+
+}
+
+void UOpenXRExpansionFunctionLibrary::GetXRMotionControllerType(FString& TrackingSystemName, EBPOpenXRControllerDeviceType& DeviceType, EBPXRResultSwitch& Result)
+{
+#if defined(OPENXR_SUPPORTED)
+ DeviceType = EBPOpenXRControllerDeviceType::DT_UnknownController;
+ Result = EBPXRResultSwitch::OnFailed;
+
+ if (FOpenXRHMD* pOpenXRHMD = GetOpenXRHMD())
+ {
+ XrInstance XRInstance = pOpenXRHMD->GetInstance();
+ XrSystemId XRSysID = pOpenXRHMD->GetSystem();
+
+ if (XRSysID && XRInstance)
+ {
+ XrSystemProperties systemProperties{ XR_TYPE_SYSTEM_PROPERTIES };
+ systemProperties.next = nullptr;
+
+ if (xrGetSystemProperties(XRInstance, XRSysID, &systemProperties) == XR_SUCCESS)
+ {
+ XrSession XRSesh = pOpenXRHMD->GetSession();
+
+ if (XRSesh)
+ {
+ XrPath myPath;
+ XrResult PathResult = xrStringToPath(XRInstance, "/user/hand/left", &myPath);
+ XrInteractionProfileState interactionProfile{ XR_TYPE_INTERACTION_PROFILE_STATE };
+ interactionProfile.next = nullptr;
+
+ XrResult QueryResult = xrGetCurrentInteractionProfile(XRSesh, myPath, &interactionProfile);
+ if (QueryResult == XR_SUCCESS)
+ {
+ char myPathy[XR_MAX_SYSTEM_NAME_SIZE];
+ uint32_t outputsize;
+ xrPathToString(XRInstance, interactionProfile.interactionProfile, XR_MAX_SYSTEM_NAME_SIZE, &outputsize, myPathy);
+
+ if (interactionProfile.interactionProfile == XR_NULL_PATH || outputsize < 1)
+ return;
+
+ FString InteractionName(ANSI_TO_TCHAR(myPathy));
+ if (InteractionName.Len() < 1)
+ return;
+
+ /*
+ * Interaction profile paths [6.4]
+ An interaction profile identifies a collection of buttons and
+ other input sources, and is of the form:
+ /interaction_profiles//
+ Paths supported in the core 1.0 release
+ /interaction_profiles/khr/simple_control
+ /interaction_profiles/khr/simple_controller
+ /interaction_profiles/google/daydream_controller
+ /interaction_profiles/htc/vive_controller
+ /interaction_profiles/htc/vive_pro
+ /interaction_profiles/microsoft/motion_controller
+ /interaction_profiles/microsoft/xbox_controller
+ /interaction_profiles/oculus/go_controller
+ /interaction_profiles/oculus/touch_controller
+ /interaction_profiles/valve/index_controller
+ */
+
+ // Not working currently?
+ /*XrInputSourceLocalizedNameGetInfo InputSourceInfo{ XR_TYPE_INPUT_SOURCE_LOCALIZED_NAME_GET_INFO };
+ InputSourceInfo.next = nullptr;
+ InputSourceInfo.whichComponents = XR_INPUT_SOURCE_LOCALIZED_NAME_INTERACTION_PROFILE_BIT;
+ InputSourceInfo.sourcePath = interactionProfile.interactionProfile;
+
+ char buffer[XR_MAX_SYSTEM_NAME_SIZE];
+ uint32_t usedBufferCount = 0;
+ if (xrGetInputSourceLocalizedName(XRSesh, &InputSourceInfo, XR_MAX_SYSTEM_NAME_SIZE, &usedBufferCount, (char*)&buffer) == XR_SUCCESS)
+ {
+ int g = 0;
+ }*/
+
+
+ if (InteractionName.Find("touch_controller", ESearchCase::IgnoreCase) != INDEX_NONE)
+ {
+ DeviceType = EBPOpenXRControllerDeviceType::DT_OculusTouchController;
+ }
+ else if (InteractionName.Contains("index_controller", ESearchCase::IgnoreCase))
+ {
+ DeviceType = EBPOpenXRControllerDeviceType::DT_ValveIndexController;
+ }
+ else if (InteractionName.Find("vive_controller", ESearchCase::IgnoreCase) != INDEX_NONE)
+ {
+ DeviceType = EBPOpenXRControllerDeviceType::DT_ViveController;
+ }
+ else if (InteractionName.Find("vive_pro", ESearchCase::IgnoreCase) != INDEX_NONE)
+ {
+ DeviceType = EBPOpenXRControllerDeviceType::DT_ViveProController;
+ }
+ else if (InteractionName.Find("simple_controller", ESearchCase::IgnoreCase) != INDEX_NONE)
+ {
+ DeviceType = EBPOpenXRControllerDeviceType::DT_SimpleController;
+ }
+ else if (InteractionName.Find("daydream_controller", ESearchCase::IgnoreCase) != INDEX_NONE)
+ {
+ DeviceType = EBPOpenXRControllerDeviceType::DT_DaydreamController;
+ }
+ else if (InteractionName.Find("motion_controller", ESearchCase::IgnoreCase) != INDEX_NONE)
+ {
+ DeviceType = EBPOpenXRControllerDeviceType::DT_MicrosoftMotionController;
+ }
+ else if (InteractionName.Find("xbox_controller", ESearchCase::IgnoreCase) != INDEX_NONE)
+ {
+ DeviceType = EBPOpenXRControllerDeviceType::DT_MicrosoftXboxController;
+ }
+ else if (InteractionName.Find("go_controller", ESearchCase::IgnoreCase) != INDEX_NONE)
+ {
+ DeviceType = EBPOpenXRControllerDeviceType::DT_OculusGoController;
+ }
+ else if (InteractionName.Find("neo3_controller", ESearchCase::IgnoreCase) != INDEX_NONE)
+ {
+ DeviceType = EBPOpenXRControllerDeviceType::DT_PicoNeo3Controller;
+ }
+ else if (InteractionName.Find("mixed_reality_controller", ESearchCase::IgnoreCase) != INDEX_NONE)
+ {
+ DeviceType = EBPOpenXRControllerDeviceType::DT_WMRController;
+ }
+ else
+ {
+ UE_LOG(OpenXRExpansionFunctionLibraryLog, Warning, TEXT("UNKNOWN OpenXR Interaction profile detected!!!: %s"), *InteractionName);
+ DeviceType = EBPOpenXRControllerDeviceType::DT_UnknownController;
+ }
+
+ Result = EBPXRResultSwitch::OnSucceeded;
+ }
+
+ TrackingSystemName = FString(ANSI_TO_TCHAR(systemProperties.systemName));// , XR_MAX_SYSTEM_NAME_SIZE);
+ //VendorID = systemProperties.vendorId;
+ return;
+ }
+ }
+ }
+ }
+#endif
+
+ TrackingSystemName.Empty();
+ return;
+}
+
+bool UOpenXRExpansionFunctionLibrary::GetOpenXRHandPose(FBPOpenXRActionSkeletalData& HandPoseContainer, UOpenXRHandPoseComponent* HandPoseComponent, bool bGetMockUpPose)
+{
+ FXRMotionControllerData MotionControllerData;
+
+ if (bGetMockUpPose)
+ {
+ GetMockUpControllerData(MotionControllerData, HandPoseContainer);
+ return true;
+ }
+
+ UHeadMountedDisplayFunctionLibrary::GetMotionControllerData((UObject*)HandPoseComponent, HandPoseContainer.TargetHand == EVRSkeletalHandIndex::EActionHandIndex_Left ? EControllerHand::Left : EControllerHand::Right, MotionControllerData);
+
+ if (MotionControllerData.bValid)
+ {
+ HandPoseContainer.SkeletalTransforms.Empty(MotionControllerData.HandKeyPositions.Num());
+ FTransform ParentTrans = FTransform::Identity;
+
+ if (MotionControllerData.DeviceVisualType == EXRVisualType::Controller)
+ {
+ ParentTrans = FTransform(MotionControllerData.GripRotation, MotionControllerData.GripPosition, FVector(1.f));
+ }
+ else // EXRVisualType::Hand visual type
+ {
+ ParentTrans = FTransform(MotionControllerData.HandKeyRotations[(uint8)EHandKeypoint::Palm], MotionControllerData.HandKeyPositions[(uint8)EHandKeypoint::Palm], FVector(1.f));
+ }
+
+ for (int i = 0; i < MotionControllerData.HandKeyPositions.Num(); ++i)
+ {
+ // Convert to component space, we convert then to parent space later when applying it
+ HandPoseContainer.SkeletalTransforms.Add(FTransform(MotionControllerData.HandKeyRotations[i].GetNormalized(), MotionControllerData.HandKeyPositions[i], FVector(1.f)).GetRelativeTransform(ParentTrans));
+ }
+
+ //if (bGetCurlValues)
+ {
+ GetFingerCurlValues(HandPoseContainer.SkeletalTransforms, HandPoseContainer.FingerCurls);
+ }
+
+ HandPoseContainer.bHasValidData = (HandPoseContainer.SkeletalTransforms.Num() == EHandKeypointCount);
+ return true;
+ }
+
+ HandPoseContainer.bHasValidData = false;
+ return false;
+}
+
+void UOpenXRExpansionFunctionLibrary::GetFingerCurlValues(TArray& TransformArray, TArray& CurlArray)
+{
+ // Fail if the count is too low
+ if (TransformArray.Num() < EHandKeypointCount)
+ return;
+
+ if (CurlArray.Num() < 5)
+ {
+ CurlArray.AddZeroed(5);
+ }
+
+ CurlArray[0] = GetCurlValueForBoneRoot(TransformArray, EHandKeypoint::ThumbMetacarpal);
+ CurlArray[1] = GetCurlValueForBoneRoot(TransformArray, EHandKeypoint::IndexProximal);
+ CurlArray[2] = GetCurlValueForBoneRoot(TransformArray, EHandKeypoint::MiddleProximal);
+ CurlArray[3] = GetCurlValueForBoneRoot(TransformArray, EHandKeypoint::RingProximal);
+ CurlArray[4] = GetCurlValueForBoneRoot(TransformArray, EHandKeypoint::LittleProximal);
+}
+
+bool UOpenXRExpansionFunctionLibrary::GetOpenXRFingerCurlValuesForHand(
+ UObject* WorldContextObject,
+ EControllerHand TargetHand,
+ float& ThumbCurl,
+ float& IndexCurl,
+ float& MiddleCurl,
+ float& RingCurl,
+ float& PinkyCurl)
+{
+ FXRMotionControllerData MotionControllerData;
+ UHeadMountedDisplayFunctionLibrary::GetMotionControllerData(WorldContextObject, TargetHand, MotionControllerData);
+
+ // Fail if the count is too low
+ if (MotionControllerData.HandKeyPositions.Num() < EHandKeypointCount)
+ return false;
+
+ FTransform ParentTrans = FTransform::Identity;
+
+ if (MotionControllerData.DeviceVisualType == EXRVisualType::Controller)
+ {
+ ParentTrans = FTransform(MotionControllerData.GripRotation, MotionControllerData.GripPosition, FVector(1.f));
+ }
+ else // EXRVisualType::Hand visual type
+ {
+ ParentTrans = FTransform(MotionControllerData.HandKeyRotations[(uint8)EHandKeypoint::Palm], MotionControllerData.HandKeyPositions[(uint8)EHandKeypoint::Palm], FVector(1.f));
+ }
+
+ TArray TransformArray;
+ TransformArray.AddUninitialized(MotionControllerData.HandKeyPositions.Num());
+
+ for (int i = 0; i < MotionControllerData.HandKeyPositions.Num(); ++i)
+ {
+ // Convert to component space, we convert then to parent space later when applying it
+ TransformArray[i] = FTransform(MotionControllerData.HandKeyRotations[i].GetNormalized(), MotionControllerData.HandKeyPositions[i], FVector(1.f)).GetRelativeTransform(ParentTrans);
+ }
+
+ ThumbCurl = GetCurlValueForBoneRoot(TransformArray, EHandKeypoint::ThumbMetacarpal);
+ IndexCurl = GetCurlValueForBoneRoot(TransformArray, EHandKeypoint::IndexProximal);
+ MiddleCurl = GetCurlValueForBoneRoot(TransformArray, EHandKeypoint::MiddleProximal);
+ RingCurl = GetCurlValueForBoneRoot(TransformArray, EHandKeypoint::RingProximal);
+ PinkyCurl = GetCurlValueForBoneRoot(TransformArray, EHandKeypoint::LittleProximal);
+
+ return true;
+}
+
+float UOpenXRExpansionFunctionLibrary::GetCurlValueForBoneRoot(TArray& TransformArray, EHandKeypoint RootBone)
+{
+ float Angle1 = 0.0f;
+ float Angle2 = 0.0f;
+ float Angle1Curl = 0.0f;
+ float Angle2Curl = 0.0f;
+
+ if (RootBone == EHandKeypoint::ThumbMetacarpal)
+ {
+ FVector Prox = TransformArray[(uint8)RootBone].GetRotation().GetForwardVector();
+ FVector Inter = TransformArray[(uint8)RootBone + 1].GetRotation().GetForwardVector();
+ FVector Distal = TransformArray[(uint8)RootBone + 2].GetRotation().GetForwardVector();
+
+ Prox = FVector::VectorPlaneProject(Prox, FVector::UpVector);
+ Inter = FVector::VectorPlaneProject(Inter, FVector::UpVector);
+ Distal = FVector::VectorPlaneProject(Distal, FVector::UpVector);
+
+ Angle1 = FMath::RadiansToDegrees(FMath::Acos(FVector::DotProduct(Inter, Distal)));
+ Angle2 = FMath::RadiansToDegrees(FMath::Acos(FVector::DotProduct(Prox, Inter)));
+
+ Angle1Curl = (Angle1 - 10.0f) / 64.0f;
+ Angle2Curl = (Angle2 - 20.0f) / 42.0f;
+ }
+ else
+ {
+ FVector Prox = TransformArray[(uint8)RootBone].GetRotation().GetForwardVector();
+ FVector Inter = TransformArray[(uint8)RootBone + 1].GetRotation().GetForwardVector();
+ FVector Distal = TransformArray[(uint8)RootBone + 2].GetRotation().GetForwardVector();
+
+
+ // We don't use the Y (splay) value, only X and Z plane
+
+ Prox = FVector::VectorPlaneProject(Prox, FVector::RightVector);
+ Inter = FVector::VectorPlaneProject(Inter, FVector::RightVector);
+ Distal = FVector::VectorPlaneProject(Distal, FVector::RightVector);
+
+ Angle1 = FMath::RadiansToDegrees(FMath::Acos(FVector::DotProduct(Inter, Distal)));
+ Angle2 = FMath::RadiansToDegrees(FMath::Acos(FVector::DotProduct(Prox, Inter)));
+
+ Angle1Curl = (Angle1 - 10.0f) / 60.0f;
+ Angle2Curl = (Angle2 - 10.0f) / 100.0f;
+ }
+
+ // Can lower number of variables by doing these
+
+ float FinalAngleAvg = FMath::Clamp((Angle1Curl + Angle2Curl) / 2.0f, 0.0f, 1.0f);
+
+ //GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Yellow, FString::Printf(TEXT("Finger Curl %f"), IndexCurl));
+ return FinalAngleAvg;
+
+}
+
+void UOpenXRExpansionFunctionLibrary::ConvertHandTransformsSpaceAndBack(TArray& OutTransforms, const TArray& WorldTransforms)
+{
+ // Fail if the count is too low
+ if (WorldTransforms.Num() < EHandKeypointCount)
+ return;
+
+ if (OutTransforms.Num() < WorldTransforms.Num())
+ {
+ OutTransforms.Empty(WorldTransforms.Num());
+ OutTransforms.AddUninitialized(WorldTransforms.Num());
+ }
+
+ // Bone/Parent map
+ int32 BoneParents[26] =
+ {
+ // Manually build the parent hierarchy starting at the wrist which has no parent (-1)
+ 1, // Palm -> Wrist
+ -1, // Wrist -> None
+ 1, // ThumbMetacarpal -> Wrist
+ 2, // ThumbProximal -> ThumbMetacarpal
+ 3, // ThumbDistal -> ThumbProximal
+ 4, // ThumbTip -> ThumbDistal
+
+ 1, // IndexMetacarpal -> Wrist
+ 6, // IndexProximal -> IndexMetacarpal
+ 7, // IndexIntermediate -> IndexProximal
+ 8, // IndexDistal -> IndexIntermediate
+ 9, // IndexTip -> IndexDistal
+
+ 1, // MiddleMetacarpal -> Wrist
+ 11, // MiddleProximal -> MiddleMetacarpal
+ 12, // MiddleIntermediate -> MiddleProximal
+ 13, // MiddleDistal -> MiddleIntermediate
+ 14, // MiddleTip -> MiddleDistal
+
+ 1, // RingMetacarpal -> Wrist
+ 16, // RingProximal -> RingMetacarpal
+ 17, // RingIntermediate -> RingProximal
+ 18, // RingDistal -> RingIntermediate
+ 19, // RingTip -> RingDistal
+
+ 1, // LittleMetacarpal -> Wrist
+ 21, // LittleProximal -> LittleMetacarpal
+ 22, // LittleIntermediate -> LittleProximal
+ 23, // LittleDistal -> LittleIntermediate
+ 24, // LittleTip -> LittleDistal
+ };
+
+ // Convert transforms to parent space
+ // The hand tracking transforms are in world space.
+ for (int32 Index = 0; Index < EHandKeypointCount; ++Index)
+ {
+ FTransform BoneTransform = WorldTransforms[Index];
+ BoneTransform.NormalizeRotation();
+ int32 ParentIndex = BoneParents[Index];
+ int32 ParentParent = -1;
+
+ if (ParentIndex > 0)
+ {
+ ParentParent = BoneParents[ParentIndex];
+ }
+
+ if (ParentIndex < 0)
+ {
+ // We are at the root, so use it.
+ OutTransforms[Index] = BoneTransform;
+ }
+ else
+ {
+
+ FTransform ParentTransform = FTransform::Identity;
+
+ // Merging missing metacarpal bone into the transform
+ if (ParentParent == 1) // Wrist
+ {
+ ParentTransform = WorldTransforms[ParentParent];
+ }
+ else
+ {
+ ParentTransform = WorldTransforms[ParentIndex];
+ }
+
+ ParentTransform.NormalizeRotation();
+ OutTransforms[Index] = BoneTransform.GetRelativeTransform(ParentTransform);
+ }
+ }
+
+ // Check on the easy component space conversion first
+ {
+ for (int32 Index = 0; Index < EHandKeypointCount; ++Index)
+ {
+ const FTransform& BoneTransform = WorldTransforms[Index];
+ int32 ParentIndex = BoneParents[Index];
+ int32 ParentParent = -1;
+
+ if (ParentIndex > 0)
+ {
+ ParentParent = BoneParents[ParentIndex];
+ }
+
+ if (ParentIndex > 0)
+ {
+ if (ParentParent == 1)
+ {
+ OutTransforms[Index] = OutTransforms[Index] * OutTransforms[ParentParent];
+ }
+ else
+ {
+ OutTransforms[Index] = OutTransforms[Index] * OutTransforms[ParentIndex];
+ }
+ }
+ }
+ }
+}
+
+void UOpenXRExpansionFunctionLibrary::GetMockUpControllerData(FXRMotionControllerData& MotionControllerData, FBPOpenXRActionSkeletalData& SkeletalMappingData, bool bOpenHand)
+{
+
+
+ TArray HandRotationsClosed = {
+ // Closed palm
+ FQuat(-6.9388939039072284e-18f,-2.7755575615628914e-17f,-5.5511151231257827e-17f,1.0000000181623150),
+ FQuat(0.0010158333104005046f,-0.031842494413126823f,0.0082646248419453450f,-0.99945823120983279),
+ FQuat(0.49284713488980170f,-0.22607130273287540f,-0.44054329019960731f,0.71548243409346490),
+ FQuat(-0.60572821120045273f,-0.017497510794223320f,0.10943807503774633f,-0.78791529705201735),
+ FQuat(-0.61281359708005512f,-0.23245447006924613f,-0.058766632856478873f,-0.75297472319193537),
+ FQuat(-0.61281359708005512f,-0.23245447006924613f,-0.058766632856478873f,-0.75297472319193537),
+ FQuat(-0.11514346379358367f,-0.029229397612833233f,-0.076351329575539195f,0.98997885626789228),
+ FQuat(0.079333339113826437f,-0.72590009974051883f,0.050346047334316274f,-0.68135202233808378),
+ FQuat(0.0032539550806410245f,-0.97123336736010268f,0.098921247251489333f,0.21658665949113542),
+ FQuat(-0.064585069112672477f,-0.57963374972897053f,0.075445998290538954f,0.80880246209406348),
+ FQuat(0.064585069112672477f,0.57963374972897053f,-0.075445998290538954f,-0.80880246209406348),
+ FQuat(-7.7702472130181111e-08f,9.8656527815210726e-08f,1.3838491007001075e-06f,1.0000000181613493),
+ FQuat(0.085300231549708214f,-0.74048187833139134f,0.058532016219761618f,-0.66406663653752407),
+ FQuat(0.011595964719175678f,-0.98786834549923641f,0.099110835214894707f,0.11899052159928070),
+ FQuat(-0.063530326287074640f,-0.56012988021281451f,0.076145543493668810f,0.82244775363868539),
+ FQuat(0.030477923994508188f,0.55662337489051250f,-0.098559802475379960f,-0.82433459003921772),
+ FQuat(-0.025910339872061101f,0.052311401725670628f,0.042953782692297438f,0.99737013210455761),
+ FQuat(0.11635892750396379f,-0.74717191584145570f,-0.026909776929005647f,-0.65381238012001353),
+ FQuat(0.098078041656806447f,-0.98532297068866348f,0.071135591008198620f,0.12024601945419003),
+ FQuat(0.0028091467060491482f,-0.52817558741956572f,0.11864668261714340f,0.84080060571895254),
+ FQuat(-0.0028091467060491482f,0.52817558741956572f,-0.11864668261714340f,-0.84080060571895254),
+ FQuat(-0.11913260527111892f,0.10934177100849747f,0.11664955310670821f,0.97992077106196507),
+ FQuat(-0.18696185363314571f,0.78123174637415782f,0.043590203318890380f,0.59398834520762267),
+ FQuat(0.15903913486884827f,-0.97287570092949460f,0.091728860868847406f,0.14073122087670015),
+ FQuat(0.035141532005084519f,-0.48251853052338572f,0.18910886397987722f,0.85450501128943579),
+ FQuat(0.035141532005084519f,-0.48251853052338572f,0.18910886397987722f,0.85450501128943579)
+ };
+ TArray HandRotationsOpen = {
+ // Open Hand
+ FQuat(0.167000905f,-0.670308471f,0.304047525f,-0.656011939f),
+ FQuat(-0.129862994f,0.736467659f,-0.315623045f,0.584065497f),
+ FQuat(-0.030090153f,0.121532254f,-0.840178490f,0.527659237f),
+ FQuat(-0.126470163f,-0.262596279f,0.816956878f,-0.497623593f),
+ FQuat(-0.102322638f,-0.249194950f,0.821705163f,-0.502227187f),
+ FQuat(-0.102322638f,-0.249194950f,0.821705163f,-0.502227187f),
+ FQuat(-0.277370781f,0.686735749f,-0.258646101f,0.620130479f),
+ FQuat(0.193366051f,-0.808131576f,0.260262072f,-0.491728455f),
+ FQuat(0.145547777f,-0.854364336f,0.317562312f,-0.384749293f),
+ FQuat(0.107193023f,-0.879853010f,0.321882188f,-0.332806766f),
+ FQuat(0.107193023f,-0.879853010f,0.321882188f,-0.332806766f),
+ FQuat(0.166999936f,-0.670307159f,0.304047883f,-0.656013489f),
+ FQuat(0.206125781f,-0.815250278f,0.203173012f,-0.501596987f),
+ FQuat(0.164493740f,-0.890369833f,0.257293820f,-0.337613612f),
+ FQuat(0.114019498f,-0.937856555f,0.283619940f,-0.164267495f),
+ FQuat(0.107336335f,-0.925720870f,0.321023613f,-0.168710276f),
+ FQuat(0.156629071f,-0.719596088f,0.210793152f,-0.642817795f),
+ FQuat(0.194258988f,-0.858762920f,0.127883837f,-0.456546605f),
+ FQuat(0.166189745f,-0.930981100f,0.161785051f,-0.281922638f),
+ FQuat(0.119936436f,-0.970744252f,0.189072192f,-0.086731322f),
+ FQuat(0.119936436f,-0.970744252f,0.189072192f,-0.086731322f),
+ FQuat(0.160288095f,-0.812664807f,0.100923792f,-0.551087677f),
+ FQuat(0.207311243f,-0.870556056f,0.056644741f,-0.442656904f),
+ FQuat(0.191506147f,-0.944826961f,0.096772499f,-0.247511998f),
+ FQuat(0.116890728f,-0.981477261f,0.138804480f,-0.061412390f),
+ FQuat(0.116890728f,-0.981477261f,0.138804480f,-0.061412390f)
+ };
+
+ MotionControllerData.HandKeyRotations = /*SkeletalMappingData.TargetHand != EVRSkeletalHandIndex::EActionHandIndex_Left ? HandRotationsOpen :*/ HandRotationsClosed;
+
+ TArray HandPositionsClosed = {
+ // Closed palm - Left
+ FVector(0.0000000000000000f,0.0000000000000000f,0.0000000000000000f),
+ FVector(-2.8690212431406792f,0.70708009295073815f,-0.47404338536985718f),
+ FVector(-1.1322360817697272f,-2.1125772981974671f,-1.2278703475596775f),
+ FVector(0.92697682070144727f,-5.5601677377459957f,-1.6753327187355360f),
+ FVector(4.0987554778339428f,-6.0520138168462640f,-2.1960898756852747f),
+ FVector(5.5854053809842918f,-5.4247506634349065f,-2.6631245417791525f),
+ FVector(-1.6026417502203387f,-1.4646945203797794f,-0.17057236434820122f),
+ FVector(5.7352432311721007f,-2.5389617545260998f,0.39061644722637634f),
+ FVector(5.4801464829170561f,-3.3344912297783416f,-3.8566611419550343f),
+ FVector(2.9179605371815693f,-3.2311985822561073f,-2.6652727318443148f),
+ FVector(3.2708935578922342f,-3.0117453368521279f,-1.6311186720587312f),
+ FVector(-1.1935619377191149f,-1.8034793735494103e-05f,1.4147048846974153e-06f),
+ FVector(5.9028526610092893f,1.3513666817788206e-05f,-4.3170989212359956e-06f),
+ FVector(5.4567759872551527f,-0.87968929643487392f,-4.1965100581882382f),
+ FVector(2.2252652348065158f,-0.87742006725177069f,-3.4067970851427791f),
+ FVector(2.6916877869696085f,-0.62360084690574125f,-2.2285708116727738f),
+ FVector(-1.1796822503165614f,1.3653411443775685f,-0.17694011615865479f),
+ FVector(5.3502831208188670f,1.9121382570769896f,-0.87930289382919313f),
+ FVector(4.8743654830862742f,1.3526757302541959f,-4.8457258101076217f),
+ FVector(2.1622015314362244f,0.85068796311660544f,-4.1307752690132205f),
+ FVector(2.6021369184528194f,1.0596020074600654f,-3.1860412064934174f),
+ FVector(-1.2905361603753163f,2.6108535683555365f,-0.46423293549223010f),
+ FVector(4.6820094577722964f,3.8858425146699327f,-1.9880098921962746f),
+ FVector(4.0115118130532306f,3.1678700881616777f,-4.8092930847360869f),
+ FVector(2.3757993445967389f,2.6579252395479291f,-4.2645319235961239f),
+ FVector(2.7329133227289351f,2.8811366857469527f,-3.6179750674182261f)
+ };
+
+ // Open Hand
+ TArray HandPositionsOpen = {
+ FVector(-1014.001f,-478.278f,212.902f),
+ FVector(-1013.516f,-476.006f,214.688f),
+ FVector(-1016.362f,-479.642f,215.119f),
+ FVector(-1018.145f,-483.254f,214.805f),
+ FVector(-1019.682f,-485.682f,213.284f),
+ FVector(-1020.480f,-486.982f,212.581f),
+ FVector(-1014.360f,-478.927f,215.169f),
+ FVector(-1014.932f,-484.146f,209.902f),
+ FVector(-1016.872f,-486.643f,206.852f),
+ FVector(-1018.771f,-488.058f,205.231f),
+ FVector(-1019.613f,-488.507f,204.655f),
+ FVector(-1013.901f,-477.534f,213.831f),
+ FVector(-1014.494f,-481.954f,208.310f),
+ FVector(-1016.269f,-484.282f,205.146f),
+ FVector(-1018.657f,-485.834f,203.427f),
+ FVector(-1019.846f,-486.231f,203.113f),
+ FVector(-1013.816f,-476.436f,213.006f),
+ FVector(-1014.637f,-479.707f,207.344f),
+ FVector(-1016.703f,-481.540f,204.355f),
+ FVector(-1018.962f,-482.692f,203.000f),
+ FVector(-1019.978f,-482.975f,202.870f),
+ FVector(-1013.845f,-475.325f,212.363f),
+ FVector(-1015.993f,-477.665f,206.928f),
+ FVector(-1017.571f,-478.907f,204.670f),
+ FVector(-1019.033f,-479.652f,203.887f),
+ FVector(-1019.778f,-479.842f,203.819f)
+ };
+
+ MotionControllerData.HandKeyPositions = /*SkeletalMappingData.TargetHand != EVRSkeletalHandIndex::EActionHandIndex_Left ? HandPositionsOpen : */HandPositionsClosed;
+
+ if (SkeletalMappingData.TargetHand != EVRSkeletalHandIndex::EActionHandIndex_Left)
+ {
+ MotionControllerData.GripPosition = FVector(-1018.305f, -478.019f, 209.872f);
+ MotionControllerData.GripRotation = FQuat(-0.116352126f, 0.039430488f, -0.757644236f, 0.641001403f);
+ }
+ else
+ {
+ MotionControllerData.GripPosition = FVector(-1202.619f, -521.077f, 283.076f);
+ MotionControllerData.GripRotation = FQuat(0.040843058f, 0.116659224f, 0.980030060f, -0.155767411f);
+ }
+
+ MotionControllerData.DeviceName = TEXT("OpenXR");
+
+ SkeletalMappingData.SkeletalTransforms.Empty(SkeletalMappingData.SkeletalTransforms.Num());
+ FTransform ParentTrans = FTransform(MotionControllerData.GripRotation, MotionControllerData.GripPosition, FVector(1.f));
+ for (int i = 0; i < MotionControllerData.HandKeyPositions.Num(); i++)
+ {
+ SkeletalMappingData.SkeletalTransforms.Add(FTransform(MotionControllerData.HandKeyRotations[i], MotionControllerData.HandKeyPositions[i], FVector(1.f)).GetRelativeTransform(ParentTrans));
+ }
+
+ SkeletalMappingData.bHasValidData = (SkeletalMappingData.SkeletalTransforms.Num() == EHandKeypointCount);
+}
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionPlugin/Private/OpenXRExpansionPlugin.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionPlugin/Private/OpenXRExpansionPlugin.cpp
new file mode 100644
index 0000000..47fac71
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionPlugin/Private/OpenXRExpansionPlugin.cpp
@@ -0,0 +1,24 @@
+// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
+
+#include "OpenXRExpansionPlugin.h"
+#include "OpenXRExpansionFunctionLibrary.h"
+
+#define LOCTEXT_NAMESPACE "FXRExpansionPluginModule"
+
+void FOpenXRExpansionPluginModule::StartupModule()
+{
+ // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
+ //LoadOpenVRModule();
+}
+
+void FOpenXRExpansionPluginModule::ShutdownModule()
+{
+ // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading,
+ // we call this function before unloading the module.
+// UnloadOpenVRModule();
+}
+
+
+#undef LOCTEXT_NAMESPACE
+
+IMPLEMENT_MODULE(FOpenXRExpansionPluginModule, OpenXRExpansionPlugin)
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionPlugin/Private/OpenXRHandPoseComponent.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionPlugin/Private/OpenXRHandPoseComponent.cpp
new file mode 100644
index 0000000..0912c8d
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionPlugin/Private/OpenXRHandPoseComponent.cpp
@@ -0,0 +1,884 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+#include "OpenXRHandPoseComponent.h"
+#include UE_INLINE_GENERATED_CPP_BY_NAME(OpenXRHandPoseComponent)
+
+#include "Net/UnrealNetwork.h"
+#include "MotionControllerComponent.h"
+#include "OpenXRExpansionFunctionLibrary.h"
+#include "Engine/NetSerialization.h"
+
+#include "XRMotionControllerBase.h" // for GetHandEnumForSourceName()
+//#include "EngineMinimal.h"
+
+UOpenXRHandPoseComponent::UOpenXRHandPoseComponent(const FObjectInitializer& ObjectInitializer)
+ : Super(ObjectInitializer)
+{
+ PrimaryComponentTick.bCanEverTick = true;
+ PrimaryComponentTick.bStartWithTickEnabled = true;
+
+ ReplicationRateForSkeletalAnimations = 10.f;
+ bReplicateSkeletalData = false;
+ bSmoothReplicatedSkeletalData = true;
+ SkeletalNetUpdateCount = 0.f;
+ bDetectGestures = true;
+ SetIsReplicatedByDefault(true);
+ bGetMockUpPoseForDebugging = false;
+}
+
+void UOpenXRHandPoseComponent::GetLifetimeReplicatedProps(TArray< class FLifetimeProperty > & OutLifetimeProps) const
+{
+ Super::GetLifetimeReplicatedProps(OutLifetimeProps);
+
+ // Skipping the owner with this as the owner will use the controllers location directly
+ DOREPLIFETIME_CONDITION(UOpenXRHandPoseComponent, LeftHandRep, COND_SkipOwner);
+ DOREPLIFETIME_CONDITION(UOpenXRHandPoseComponent, RightHandRep, COND_SkipOwner);
+}
+
+void UOpenXRHandPoseComponent::Server_SendSkeletalTransforms_Implementation(const FBPXRSkeletalRepContainer& SkeletalInfo)
+{
+ for (int i = 0; i < HandSkeletalActions.Num(); i++)
+ {
+ if (HandSkeletalActions[i].TargetHand == SkeletalInfo.TargetHand)
+ {
+ if (SkeletalInfo.TargetHand == EVRSkeletalHandIndex::EActionHandIndex_Left)
+ {
+ if (bSmoothReplicatedSkeletalData)
+ {
+ LeftHandRepManager.PreCopyNewData(HandSkeletalActions[i], ReplicationRateForSkeletalAnimations, bUseExponentialSmoothing);
+ }
+
+ FBPXRSkeletalRepContainer::CopyReplicatedTo(SkeletalInfo, HandSkeletalActions[i]);
+ LeftHandRep = SkeletalInfo;
+
+ if (bSmoothReplicatedSkeletalData)
+ {
+ LeftHandRepManager.NotifyNewData(HandSkeletalActions[i], ReplicationRateForSkeletalAnimations, bUseExponentialSmoothing);
+ }
+ }
+ else
+ {
+ if (bSmoothReplicatedSkeletalData)
+ {
+ RightHandRepManager.PreCopyNewData(HandSkeletalActions[i], ReplicationRateForSkeletalAnimations, bUseExponentialSmoothing);
+ }
+
+ FBPXRSkeletalRepContainer::CopyReplicatedTo(SkeletalInfo, HandSkeletalActions[i]);
+ RightHandRep = SkeletalInfo;
+
+ if (bSmoothReplicatedSkeletalData)
+ {
+ RightHandRepManager.NotifyNewData(HandSkeletalActions[i], ReplicationRateForSkeletalAnimations, bUseExponentialSmoothing);
+ }
+ }
+
+ break;
+ }
+ }
+}
+
+bool UOpenXRHandPoseComponent::Server_SendSkeletalTransforms_Validate(const FBPXRSkeletalRepContainer& SkeletalInfo)
+{
+ return true;
+}
+
+void FOpenXRAnimInstanceProxy::PreUpdate(UAnimInstance* InAnimInstance, float DeltaSeconds)
+{
+ Super::PreUpdate(InAnimInstance, DeltaSeconds);
+
+ if (UOpenXRAnimInstance* OwningInstance = Cast(InAnimInstance))
+ {
+ if (OwningInstance->OwningPoseComp)
+ {
+ if (HandSkeletalActionData.Num() != OwningInstance->OwningPoseComp->HandSkeletalActions.Num())
+ {
+ HandSkeletalActionData.Empty(OwningInstance->OwningPoseComp->HandSkeletalActions.Num());
+
+ for(FBPOpenXRActionSkeletalData& actionInfo : OwningInstance->OwningPoseComp->HandSkeletalActions)
+ {
+ HandSkeletalActionData.Add(actionInfo);
+ }
+ }
+ else
+ {
+ for (int i = 0; i < OwningInstance->OwningPoseComp->HandSkeletalActions.Num(); ++i)
+ {
+ HandSkeletalActionData[i] = OwningInstance->OwningPoseComp->HandSkeletalActions[i];
+ }
+ }
+ }
+ }
+}
+
+FOpenXRAnimInstanceProxy::FOpenXRAnimInstanceProxy(UAnimInstance* InAnimInstance)
+ : FAnimInstanceProxy(InAnimInstance)
+{
+}
+
+void UOpenXRHandPoseComponent::BeginPlay()
+{
+ /*if (UMotionControllerComponent * MotionParent = Cast(GetAttachParent()))
+ {
+ EControllerHand HandType;
+ if (!FXRMotionControllerBase::GetHandEnumForSourceName(MotionParent->MotionSource, HandType))
+ {
+ HandType = EControllerHand::Left;
+ }
+
+ for (int i = 0; i < HandSkeletalActions.Num(); i++)
+ {
+ if (HandType == EControllerHand::Left || HandType == EControllerHand::AnyHand)
+ HandSkeletalActions[i].SkeletalData.TargetHand = EVRSkeletalHandIndex::EActionHandIndex_Left;
+ else
+ HandSkeletalActions[i].SkeletalData.TargetHand = EVRSkeletalHandIndex::EActionHandIndex_Right;
+ }
+
+ }*/
+
+ Super::BeginPlay();
+}
+
+void UOpenXRHandPoseComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)
+{
+ if (!IsLocallyControlled())
+ {
+ if (bReplicateSkeletalData)
+ {
+ // Handle bone lerping here if we are replicating
+ for (FBPOpenXRActionSkeletalData& actionInfo : HandSkeletalActions)
+ {
+ if (bSmoothReplicatedSkeletalData)
+ {
+ if (actionInfo.TargetHand == EVRSkeletalHandIndex::EActionHandIndex_Left)
+ {
+ LeftHandRepManager.UpdateManager(DeltaTime, actionInfo, this);
+ }
+ else
+ {
+ RightHandRepManager.UpdateManager(DeltaTime, actionInfo, this);
+ }
+ }
+ }
+ }
+ }
+ else // Get data and process
+ {
+ bool bGetCompressedTransforms = false;
+ if (bReplicateSkeletalData && HandSkeletalActions.Num() > 0)
+ {
+ SkeletalNetUpdateCount += DeltaTime;
+ if (SkeletalNetUpdateCount >= (1.0f / ReplicationRateForSkeletalAnimations))
+ {
+ SkeletalNetUpdateCount = 0.0f;
+ bGetCompressedTransforms = true;
+ }
+ }
+
+ for (FBPOpenXRActionSkeletalData& actionInfo : HandSkeletalActions)
+ {
+ if (UOpenXRExpansionFunctionLibrary::GetOpenXRHandPose(actionInfo, this, bGetMockUpPoseForDebugging))
+ {
+ if (bGetCompressedTransforms)
+ {
+ if (GetNetMode() == NM_Client)
+ {
+ if (actionInfo.bHasValidData)
+ {
+ FBPXRSkeletalRepContainer ContainerSend;
+ ContainerSend.CopyForReplication(actionInfo);
+ Server_SendSkeletalTransforms(ContainerSend);
+ }
+ }
+ else
+ {
+ if (actionInfo.bHasValidData)
+ {
+ if (actionInfo.TargetHand == EVRSkeletalHandIndex::EActionHandIndex_Left)
+ LeftHandRep.CopyForReplication(actionInfo);
+ else
+ RightHandRep.CopyForReplication(actionInfo);
+ }
+ }
+ }
+ }
+
+ if (bDetectGestures && actionInfo.bHasValidData && actionInfo.SkeletalTransforms.Num() > 0 && GesturesDB != nullptr && GesturesDB->Gestures.Num() > 0)
+ {
+ DetectCurrentPose(actionInfo);
+ }
+ }
+ }
+
+ Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
+}
+
+bool UOpenXRHandPoseComponent::SaveCurrentPose(FName RecordingName, EVRSkeletalHandIndex HandToSave)
+{
+
+ if (!HandSkeletalActions.Num())
+ return false;
+
+ // Default to the first hand element so that single length arrays work as is.
+ FBPOpenXRActionSkeletalData* HandSkeletalAction = nullptr;
+
+ // Now check for the specific passed in hand if this is a multi hand
+ for (int i = 0; i < HandSkeletalActions.Num(); ++i)
+ {
+ if (HandSkeletalActions[i].TargetHand == HandToSave)
+ {
+ HandSkeletalAction = &HandSkeletalActions[i];
+ break;
+ }
+ }
+
+ if (!HandSkeletalAction || !HandSkeletalAction->bHasValidData || HandSkeletalAction->SkeletalTransforms.Num() < EHandKeypointCount)
+ return false;
+
+ if (GesturesDB)
+ {
+ FOpenXRGesture NewGesture;
+
+ int32 FingerMap[5] =
+ {
+ (int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_TIP_EXT,
+ (int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_TIP_EXT,
+ (int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_TIP_EXT,
+ (int32)EXRHandJointType::OXR_HAND_JOINT_RING_TIP_EXT,
+ (int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_TIP_EXT
+ };
+
+ FVector WristLoc = FVector::ZeroVector;
+
+ if (HandToSave == EVRSkeletalHandIndex::EActionHandIndex_Left)
+ {
+ WristLoc = HandSkeletalAction->SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT].GetLocation().MirrorByVector(FVector::RightVector);
+ }
+ else
+ {
+ WristLoc = HandSkeletalAction->SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT].GetLocation();
+ }
+
+ for (int i = 0; i < 5; ++i)
+ {
+ if (HandToSave == EVRSkeletalHandIndex::EActionHandIndex_Left)
+ {
+ NewGesture.FingerValues[i] = FOpenXRGestureFingerPosition(HandSkeletalAction->SkeletalTransforms[FingerMap[i]].GetLocation().MirrorByVector(FVector::RightVector) - WristLoc, (EXRHandJointType)FingerMap[i]);
+ }
+ else
+ {
+ NewGesture.FingerValues[i] = FOpenXRGestureFingerPosition(HandSkeletalAction->SkeletalTransforms[FingerMap[i]].GetLocation() - WristLoc, (EXRHandJointType)FingerMap[i]);
+ }
+ }
+
+ NewGesture.Name = RecordingName;
+ GesturesDB->Gestures.Add(NewGesture);
+
+ return true;
+ }
+
+ return false;
+}
+
+
+bool UOpenXRHandPoseComponent::K2_DetectCurrentPose(UPARAM(ref) FBPOpenXRActionSkeletalData& SkeletalAction, FOpenXRGesture & GestureOut)
+{
+ if (!GesturesDB || GesturesDB->Gestures.Num() < 1)
+ return false;
+
+ int32 FingerMap[5] =
+ {
+ (int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_TIP_EXT,
+ (int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_TIP_EXT,
+ (int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_TIP_EXT,
+ (int32)EXRHandJointType::OXR_HAND_JOINT_RING_TIP_EXT,
+ (int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_TIP_EXT
+ };
+
+ FVector WristLoc = FVector::ZeroVector;
+
+ if (SkeletalAction.TargetHand == EVRSkeletalHandIndex::EActionHandIndex_Left)
+ {
+ WristLoc = SkeletalAction.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT].GetLocation().MirrorByVector(FVector::RightVector);
+ }
+ else
+ {
+ WristLoc = SkeletalAction.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT].GetLocation();
+ }
+
+ // Early fill in an array to keep from performing math for each gesture
+ TArray CurrentTips;
+ CurrentTips.AddUninitialized(5);
+ for (int i = 0; i < 5; ++i)
+ {
+ if (SkeletalAction.TargetHand == EVRSkeletalHandIndex::EActionHandIndex_Left)
+ {
+ CurrentTips[i] = SkeletalAction.SkeletalTransforms[FingerMap[i]].GetLocation().MirrorByVector(FVector::RightVector) - WristLoc;
+ }
+ else
+ {
+ CurrentTips[i] = SkeletalAction.SkeletalTransforms[FingerMap[i]].GetLocation() - WristLoc;
+ }
+ }
+
+ for (const FOpenXRGesture& Gesture : GesturesDB->Gestures)
+ {
+ // If not enough indexs to match curl values, or if this gesture requires finger splay and the controller can't do it
+ if (Gesture.FingerValues.Num() < 5 || SkeletalAction.SkeletalTransforms.Num() < EHandKeypointCount)
+ continue;
+
+ bool bDetectedPose = true;
+ for (int i = 0; i < 5; ++i)
+ {
+ FVector GestureV = Gesture.FingerValues[i].Value;
+ FVector CurrentV = CurrentTips[i];
+ FVector Difference = GestureV - CurrentV;
+
+ if (!Gesture.FingerValues[i].Value.Equals(CurrentTips[i], Gesture.FingerValues[i].Threshold))
+ {
+ bDetectedPose = false;
+ break;
+ }
+ }
+
+ if (bDetectedPose)
+ {
+ GestureOut = Gesture;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool UOpenXRHandPoseComponent::DetectCurrentPose(FBPOpenXRActionSkeletalData &SkeletalAction)
+{
+ if (!GesturesDB || GesturesDB->Gestures.Num() < 1 || SkeletalAction.SkeletalTransforms.Num() < EHandKeypointCount)
+ return false;
+
+ FTransform BoneTransform = FTransform::Identity;
+
+ int32 FingerMap[5] =
+ {
+ (int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_TIP_EXT,
+ (int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_TIP_EXT,
+ (int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_TIP_EXT,
+ (int32)EXRHandJointType::OXR_HAND_JOINT_RING_TIP_EXT,
+ (int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_TIP_EXT
+ };
+
+ FVector WristLoc = FVector::ZeroVector;
+
+ if (SkeletalAction.TargetHand == EVRSkeletalHandIndex::EActionHandIndex_Left)
+ {
+ WristLoc = SkeletalAction.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT].GetLocation().MirrorByVector(FVector::RightVector);
+ }
+ else
+ {
+ WristLoc = SkeletalAction.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT].GetLocation();
+ }
+
+ // Early fill in an array to keep from performing math for each gesture
+ TArray CurrentTips;
+ CurrentTips.AddUninitialized(5);
+ for (int i = 0; i < 5; ++i)
+ {
+ if (SkeletalAction.TargetHand == EVRSkeletalHandIndex::EActionHandIndex_Left)
+ {
+ CurrentTips[i] = SkeletalAction.SkeletalTransforms[FingerMap[i]].GetLocation().MirrorByVector(FVector::RightVector) - WristLoc;
+ }
+ else
+ {
+ CurrentTips[i] = SkeletalAction.SkeletalTransforms[FingerMap[i]].GetLocation() - WristLoc;
+ }
+ }
+
+ for (auto GestureIterator = GesturesDB->Gestures.CreateConstIterator(); GestureIterator; ++GestureIterator)
+ {
+ const FOpenXRGesture &Gesture = *GestureIterator;
+
+ // If not enough indexs to match curl values, or if this gesture requires finger splay and the controller can't do it
+ if (Gesture.FingerValues.Num() < 5 || SkeletalAction.SkeletalTransforms.Num() < EHandKeypointCount)
+ continue;
+
+ bool bDetectedPose = true;
+ for (int i = 0; i < 5; ++i)
+ {
+ if (Gesture.FingerValues[i].Threshold <= 0.0f)
+ continue;
+
+ if (!Gesture.FingerValues[i].Value.Equals(CurrentTips[i], Gesture.FingerValues[i].Threshold))
+ {
+ bDetectedPose = false;
+ break;
+ }
+ }
+
+ if (bDetectedPose)
+ {
+ if (SkeletalAction.LastHandGesture != Gesture.Name)
+ {
+ if (SkeletalAction.LastHandGesture != NAME_None)
+ OnGestureEnded.Broadcast(SkeletalAction.LastHandGesture, SkeletalAction.LastHandGestureIndex, SkeletalAction.TargetHand);
+
+ SkeletalAction.LastHandGesture = Gesture.Name;
+ SkeletalAction.LastHandGestureIndex = GestureIterator.GetIndex();
+ OnNewGestureDetected.Broadcast(SkeletalAction.LastHandGesture, SkeletalAction.LastHandGestureIndex, SkeletalAction.TargetHand);
+
+ return true;
+ }
+ else
+ return false; // Same gesture
+ }
+ }
+
+ if (SkeletalAction.LastHandGesture != NAME_None)
+ {
+ OnGestureEnded.Broadcast(SkeletalAction.LastHandGesture, SkeletalAction.LastHandGestureIndex, SkeletalAction.TargetHand);
+ SkeletalAction.LastHandGesture = NAME_None;
+ SkeletalAction.LastHandGestureIndex = INDEX_NONE;
+ }
+
+ return false;
+}
+
+UOpenXRHandPoseComponent::FTransformLerpManager::FTransformLerpManager()
+{
+ bReplicatedOnce = false;
+ bLerping = false;
+ UpdateCount = 0.0f;
+ UpdateRate = 0.0f;
+}
+
+void UOpenXRHandPoseComponent::FTransformLerpManager::PreCopyNewData(FBPOpenXRActionSkeletalData& ActionInfo, int NetUpdateRate, bool bExponentialSmoothing)
+{
+ if (!bExponentialSmoothing)
+ {
+ if (ActionInfo.SkeletalTransforms.Num())
+ {
+ OldTransforms = ActionInfo.SkeletalTransforms;
+ }
+ }
+}
+
+void UOpenXRHandPoseComponent::FTransformLerpManager::NotifyNewData(FBPOpenXRActionSkeletalData& ActionInfo, int NetUpdateRate, bool bExponentialSmoothing)
+{
+ UpdateRate = (1.0f / NetUpdateRate);
+
+ if (bReplicatedOnce)
+ {
+ bLerping = true;
+ UpdateCount = 0.0f;
+ NewTransforms = ActionInfo.SkeletalTransforms;
+
+ ActionInfo.SkeletalTransforms = OldTransforms;
+
+ }
+ else
+ {
+ if (bExponentialSmoothing)
+ {
+ OldTransforms = ActionInfo.SkeletalTransforms;
+ }
+
+ bReplicatedOnce = true;
+ }
+}
+
+void UOpenXRHandPoseComponent::FTransformLerpManager::UpdateManager(float DeltaTime, FBPOpenXRActionSkeletalData& ActionInfo, UOpenXRHandPoseComponent* ParentComp)
+{
+ if (!ActionInfo.bHasValidData || !OldTransforms.Num())
+ return;
+
+ if (bLerping)
+ {
+ bool bExponentialSmoothing = ParentComp->bUseExponentialSmoothing;
+ float LerpVal = 0.0f;
+
+ if (!bExponentialSmoothing || ParentComp->InterpolationSpeed <= 0.f)
+ {
+ UpdateCount += DeltaTime;
+
+ // Keep LerpVal
+ LerpVal = FMath::Clamp(UpdateCount / UpdateRate, 0.0f, 1.0f);
+ }
+ else
+ {
+ LerpVal = FMath::Clamp(DeltaTime * ParentComp->InterpolationSpeed, 0.f, 1.f);
+ }
+
+ if (!bExponentialSmoothing && LerpVal >= 1.0f)
+ {
+ bLerping = false;
+ UpdateCount = 0.0f;
+ ActionInfo.SkeletalTransforms = NewTransforms;
+ }
+ else
+ {
+ int32 BoneCountAdjustment = 6 + (ActionInfo.bEnableUE4HandRepSavings ? 4 : 0);
+ if ((NewTransforms.Num() < (EHandKeypointCount - BoneCountAdjustment)) || (NewTransforms.Num() != ActionInfo.SkeletalTransforms.Num()))
+ {
+ return;
+ }
+
+ ActionInfo.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_PALM_EXT] = FTransform::Identity;
+ BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
+
+ BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_METACARPAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
+ BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_PROXIMAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
+ BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_DISTAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
+ //BlendBone((uint8)EVROpenXRBones::eBone_Thumb3, ActionInfo, LerpVal); // Technically can be projected instead of blended
+
+ if (!ActionInfo.bEnableUE4HandRepSavings)
+ {
+ BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_METACARPAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
+ }
+ BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_PROXIMAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
+ BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_INTERMEDIATE_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
+ BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_DISTAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
+ //BlendBone((uint8)EVROpenXRBones::eBone_IndexFinger4, ActionInfo, LerpVal); // Technically can be projected instead of blended
+
+ if (!ActionInfo.bEnableUE4HandRepSavings)
+ {
+ BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_METACARPAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
+ }
+ BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_PROXIMAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
+ BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_INTERMEDIATE_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
+ BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_DISTAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
+ //BlendBone((uint8)EVROpenXRBones::eBone_IndexFinger4, ActionInfo, LerpVal); // Technically can be projected instead of blended
+
+ if (!ActionInfo.bEnableUE4HandRepSavings)
+ {
+ BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_RING_METACARPAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
+ }
+ BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_RING_PROXIMAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
+ BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_RING_INTERMEDIATE_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
+ BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_RING_DISTAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
+ //BlendBone((uint8)EVROpenXRBones::eBone_IndexFinger4, ActionInfo, LerpVal); // Technically can be projected instead of blended
+
+ if (!ActionInfo.bEnableUE4HandRepSavings)
+ {
+ BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_METACARPAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
+ }
+ BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_PROXIMAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
+ BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_INTERMEDIATE_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
+ BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_DISTAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
+ //BlendBone((uint8)EVROpenXRBones::eBone_IndexFinger4, ActionInfo, LerpVal); // Technically can be projected instead of blended
+
+ // These are copied from the 3rd joints as they use the same transform but a different root
+ // Don't want to waste cpu time blending these
+ //ActionInfo.SkeletalTransforms[(uint8)EVROpenXRBones::eBone_Aux_Thumb] = ActionInfo.SkeletalData.SkeletalTransforms[(uint8)EVROpenXRBones::eBone_Thumb2];
+ //ActionInfo.SkeletalTransforms[(uint8)EVROpenXRBones::eBone_Aux_IndexFinger] = ActionInfo.SkeletalData.SkeletalTransforms[(uint8)EVROpenXRBones::eBone_IndexFinger3];
+ //ActionInfo.SkeletalTransforms[(uint8)EVROpenXRBones::eBone_Aux_MiddleFinger] = ActionInfo.SkeletalData.SkeletalTransforms[(uint8)EVROpenXRBones::eBone_MiddleFinger3];
+ //ActionInfo.SkeletalTransforms[(uint8)EVROpenXRBones::eBone_Aux_RingFinger] = ActionInfo.SkeletalData.SkeletalTransforms[(uint8)EVROpenXRBones::eBone_RingFinger3];
+ //ActionInfo.SkeletalTransforms[(uint8)EVROpenXRBones::eBone_Aux_PinkyFinger] = ActionInfo.SkeletalData.SkeletalTransforms[(uint8)EVROpenXRBones::eBone_PinkyFinger3];
+ }
+ }
+}
+
+void FBPXRSkeletalRepContainer::CopyForReplication(FBPOpenXRActionSkeletalData& Other)
+{
+ TargetHand = Other.TargetHand;
+
+ if (!Other.bHasValidData)
+ return;
+
+ bAllowDeformingMesh = Other.bAllowDeformingMesh;
+ bEnableUE4HandRepSavings = Other.bEnableUE4HandRepSavings;
+
+ // Instead of doing this, we likely need to lerp but this is for testing
+ //SkeletalTransforms = Other.SkeletalData.SkeletalTransforms;
+
+ if (Other.SkeletalTransforms.Num() < EHandKeypointCount)
+ {
+ SkeletalTransforms.Empty();
+ return;
+ }
+
+ int32 BoneCountAdjustment = 6 + (bEnableUE4HandRepSavings ? 4 : 0);
+
+ if (SkeletalTransforms.Num() != EHandKeypointCount - BoneCountAdjustment)
+ {
+ SkeletalTransforms.Reset(EHandKeypointCount - BoneCountAdjustment); // Minus bones we don't need
+ SkeletalTransforms.AddUninitialized(EHandKeypointCount - BoneCountAdjustment);
+ }
+
+ int32 idx = 0;
+ // Root is always identity
+ //SkeletalTransforms[0] = Other.SkeletalData.SkeletalTransforms[(uint8)EVROpenInputBones::eBone_Root]; // This has no pos right? Need to skip pos on it
+ SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT];
+ SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_METACARPAL_EXT];
+ SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_PROXIMAL_EXT];
+ SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_DISTAL_EXT];
+
+ if (!bEnableUE4HandRepSavings)
+ {
+ SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_METACARPAL_EXT];
+ }
+ SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_PROXIMAL_EXT];
+ SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_INTERMEDIATE_EXT];
+ SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_DISTAL_EXT];
+
+ if (!bEnableUE4HandRepSavings)
+ {
+ SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_METACARPAL_EXT];
+ }
+ SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_PROXIMAL_EXT];
+ SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_INTERMEDIATE_EXT];
+ SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_DISTAL_EXT];
+
+ if (!bEnableUE4HandRepSavings)
+ {
+ SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_RING_METACARPAL_EXT];
+ }
+ SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_RING_PROXIMAL_EXT];
+ SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_RING_INTERMEDIATE_EXT];
+ SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_RING_DISTAL_EXT];
+
+ if (!bEnableUE4HandRepSavings)
+ {
+ SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_METACARPAL_EXT];
+ }
+ SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_PROXIMAL_EXT];
+ SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_INTERMEDIATE_EXT];
+ SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_DISTAL_EXT];
+}
+
+void FBPXRSkeletalRepContainer::CopyReplicatedTo(const FBPXRSkeletalRepContainer& Container, FBPOpenXRActionSkeletalData& Other)
+{
+ int32 BoneCountAdjustment = 6 + (Container.bEnableUE4HandRepSavings ? 4 : 0);
+ if (Container.SkeletalTransforms.Num() < (EHandKeypointCount - BoneCountAdjustment))
+ {
+ Other.SkeletalTransforms.Empty();
+ Other.bHasValidData = false;
+ return;
+ }
+
+ Other.bAllowDeformingMesh = Container.bAllowDeformingMesh;
+ Other.bEnableUE4HandRepSavings = Container.bEnableUE4HandRepSavings;
+
+ // Instead of doing this, we likely need to lerp but this is for testing
+ //Other.SkeletalData.SkeletalTransforms = Container.SkeletalTransforms;
+
+ if (Other.SkeletalTransforms.Num() != EHandKeypointCount)
+ {
+ Other.SkeletalTransforms.Reset(EHandKeypointCount);
+ Other.SkeletalTransforms.AddUninitialized(EHandKeypointCount);
+ }
+
+ int32 idx = 0;
+
+ // Only fill in the ones that we care about
+ Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_PALM_EXT] = FTransform::Identity; // Always identity
+ Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT] = Container.SkeletalTransforms[idx++];
+
+ Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_METACARPAL_EXT] = Container.SkeletalTransforms[idx++];
+ Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_PROXIMAL_EXT] = Container.SkeletalTransforms[idx++];
+ Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_DISTAL_EXT] = Container.SkeletalTransforms[idx++];
+ Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_TIP_EXT] = FTransform::Identity;
+
+ if (!Container.bEnableUE4HandRepSavings)
+ {
+ Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_METACARPAL_EXT] = Container.SkeletalTransforms[idx++];
+ }
+ else
+ {
+ Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_METACARPAL_EXT] = FTransform::Identity;
+ }
+ Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_PROXIMAL_EXT] = Container.SkeletalTransforms[idx++];
+ Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_INTERMEDIATE_EXT] = Container.SkeletalTransforms[idx++];
+ Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_DISTAL_EXT] = Container.SkeletalTransforms[idx++];
+ Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_TIP_EXT] = FTransform::Identity;
+
+ if (!Container.bEnableUE4HandRepSavings)
+ {
+ Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_METACARPAL_EXT] = Container.SkeletalTransforms[idx++];
+ }
+ else
+ {
+ Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_METACARPAL_EXT] = FTransform::Identity;
+ }
+ Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_PROXIMAL_EXT] = Container.SkeletalTransforms[idx++];
+ Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_INTERMEDIATE_EXT] = Container.SkeletalTransforms[idx++];
+ Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_DISTAL_EXT] = Container.SkeletalTransforms[idx++];
+ Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_TIP_EXT] = FTransform::Identity;
+
+ if (!Container.bEnableUE4HandRepSavings)
+ {
+ Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_RING_METACARPAL_EXT] = Container.SkeletalTransforms[idx++];
+ }
+ else
+ {
+ Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_RING_METACARPAL_EXT] = FTransform::Identity;
+ }
+ Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_RING_PROXIMAL_EXT] = Container.SkeletalTransforms[idx++];
+ Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_RING_INTERMEDIATE_EXT] = Container.SkeletalTransforms[idx++];
+ Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_RING_DISTAL_EXT] = Container.SkeletalTransforms[idx++];
+ Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_RING_TIP_EXT] = FTransform::Identity;
+
+ if (!Container.bEnableUE4HandRepSavings)
+ {
+ Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_METACARPAL_EXT] = Container.SkeletalTransforms[idx++];
+ }
+ else
+ {
+ Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_METACARPAL_EXT] = FTransform::Identity;
+ }
+ Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_PROXIMAL_EXT] = Container.SkeletalTransforms[idx++];
+ Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_INTERMEDIATE_EXT] = Container.SkeletalTransforms[idx++];
+ Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_DISTAL_EXT] = Container.SkeletalTransforms[idx++];
+ Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_TIP_EXT] = FTransform::Identity;
+
+ Other.bHasValidData = true;
+}
+
+bool FBPXRSkeletalRepContainer::NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess)
+{
+ bOutSuccess = true;
+
+ Ar.SerializeBits(&TargetHand, 1);
+ Ar.SerializeBits(&bAllowDeformingMesh, 1);
+ Ar.SerializeBits(&bEnableUE4HandRepSavings, 1);
+
+ int32 BoneCountAdjustment = 6 + (bEnableUE4HandRepSavings ? 4 : 0);
+ uint8 TransformCount = EHandKeypointCount - BoneCountAdjustment;
+
+ bool bHasValidData = SkeletalTransforms.Num() >= TransformCount;
+ Ar.SerializeBits(&bHasValidData, 1);
+
+ //Ar << TransformCount;
+
+ if (Ar.IsLoading())
+ {
+ SkeletalTransforms.Reset(TransformCount);
+ }
+
+ FVector Position = FVector::ZeroVector;
+ FRotator Rot = FRotator::ZeroRotator;
+
+ if (bHasValidData)
+ {
+ for (int i = 0; i < TransformCount; i++)
+ {
+ if (Ar.IsSaving())
+ {
+ if (bAllowDeformingMesh)
+ Position = SkeletalTransforms[i].GetLocation();
+
+ Rot = SkeletalTransforms[i].Rotator();
+ }
+
+ if (bAllowDeformingMesh)
+ bOutSuccess &= SerializePackedVector<10, 11>(Position, Ar);
+
+ Rot.SerializeCompressed(Ar); // Short? 10 bit?
+
+ if (Ar.IsLoading())
+ {
+ if (bAllowDeformingMesh)
+ SkeletalTransforms.Add(FTransform(Rot, Position));
+ else
+ SkeletalTransforms.Add(FTransform(Rot));
+ }
+ }
+ }
+
+ return bOutSuccess;
+}
+
+void UOpenXRAnimInstance::NativeBeginPlay()
+{
+ Super::NativeBeginPlay();
+
+ AActor* Owner = GetOwningComponent()->GetOwner();
+ UActorComponent* HandPoseComp = nullptr;
+
+ if (Owner)
+ {
+ HandPoseComp = Owner->GetComponentByClass(UOpenXRHandPoseComponent::StaticClass());
+
+ if (!HandPoseComp)
+ {
+ // We are also checking owner->owner in case hand mesh is in a sub actor
+ if (Owner->GetOwner())
+ {
+ HandPoseComp = Owner->GetOwner()->GetComponentByClass(UOpenXRHandPoseComponent::StaticClass());
+ }
+ }
+ }
+
+ if (!HandPoseComp)
+ {
+ return;
+ }
+
+ if (UOpenXRHandPoseComponent* HandComp = Cast(HandPoseComp))
+ {
+ OwningPoseComp = HandComp;
+ }
+}
+
+/*void UOpenXRAnimInstance::NativeInitializeAnimation()
+{
+ Super::NativeInitializeAnimation();
+
+ AActor* Owner = GetOwningComponent()->GetOwner();
+ UActorComponent* HandPoseComp = nullptr;
+
+ if (Owner)
+ {
+ HandPoseComp = Owner->GetComponentByClass(UOpenXRHandPoseComponent::StaticClass());
+
+ if (!HandPoseComp)
+ {
+ // We are also checking owner->owner in case hand mesh is in a sub actor
+ if (Owner->GetOwner())
+ {
+ HandPoseComp = Owner->GetOwner()->GetComponentByClass(UOpenXRHandPoseComponent::StaticClass());
+ }
+ }
+ }
+
+ if (!HandPoseComp)
+ {
+ return;
+ }
+
+ if (UOpenXRHandPoseComponent* HandComp = Cast(HandPoseComp))
+ {
+ OwningPoseComp = HandComp;
+ }
+}*/
+
+void UOpenXRAnimInstance::InitializeCustomBoneMapping(UPARAM(ref) FBPOpenXRSkeletalMappingData& SkeletalMappingData)
+{
+ USkeleton* AssetSkeleton = this->CurrentSkeleton;//RequiredBones.GetSkeletonAsset();
+
+ if (AssetSkeleton)
+ {
+ FBoneContainer& RequiredBones = this->GetRequiredBones();
+ for (FBPOpenXRSkeletalPair& BonePair : SkeletalMappingData.BonePairs)
+ {
+ // Fill in the bone name for the reference
+ BonePair.ReferenceToConstruct.BoneName = BonePair.BoneToTarget;
+
+ // Init the reference
+ BonePair.ReferenceToConstruct.Initialize(AssetSkeleton);
+ BonePair.ReferenceToConstruct.CachedCompactPoseIndex = BonePair.ReferenceToConstruct.GetCompactPoseIndex(RequiredBones);
+
+ if ((BonePair.ReferenceToConstruct.CachedCompactPoseIndex != INDEX_NONE))
+ {
+ // Get our parent bones index
+ BonePair.ParentReference = RequiredBones.GetParentBoneIndex(BonePair.ReferenceToConstruct.CachedCompactPoseIndex);
+ }
+ }
+
+ if (UObject* OwningAsset = RequiredBones.GetAsset())
+ {
+ SkeletalMappingData.LastInitializedName = OwningAsset->GetFName();
+ }
+
+ SkeletalMappingData.bInitialized = true;
+ return;
+ }
+
+ SkeletalMappingData.bInitialized = false;
+}
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionPlugin/Public/AnimNode_ApplyOpenXRHandPose.h b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionPlugin/Public/AnimNode_ApplyOpenXRHandPose.h
new file mode 100644
index 0000000..f3868d6
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionPlugin/Public/AnimNode_ApplyOpenXRHandPose.h
@@ -0,0 +1,120 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+#include "CoreMinimal.h"
+#include "Runtime/AnimGraphRuntime/Public/BoneControllers/AnimNode_SkeletalControlBase.h"
+#include "OpenXRExpansionTypes.h"
+//#include "Skeleton/BodyStateSkeleton.h"
+//#include "BodyStateAnimInstance.h"
+
+#include "AnimNode_ApplyOpenXRHandPose.generated.h"
+
+
+USTRUCT()
+struct OPENXREXPANSIONPLUGIN_API FAnimNode_ApplyOpenXRHandPose : public FAnimNode_SkeletalControlBase
+{
+ GENERATED_USTRUCT_BODY()
+
+public:
+
+ FVector WristForwardLS_UE;
+ FVector WristSideDirectionLS;
+
+ // Generally used when not passing in custom bone mappings, defines the auto mapping style
+ UPROPERTY(EditAnywhere, Category = Skeletal, meta = (PinShownByDefault))
+ EVROpenXRSkeletonType SkeletonType;
+
+ // If your hand is part of a full body or arm skeleton and you don't have a proxy bone to retain the position enable this
+ UPROPERTY(EditAnywhere, Category = Skeletal, meta = (PinShownByDefault))
+ bool bSkipRootBone;
+
+ // If you only want to use the wrist transform part of this
+ // This will also automatically add the deform to the wrist as it doesn't make much sense without it
+ UPROPERTY(EditAnywhere, Category = Skeletal, meta = (PinShownByDefault))
+ bool bOnlyApplyWristTransform;
+
+ // Generally used when not passing in custom bone mappings, defines the auto mapping style
+ UPROPERTY(EditAnywhere, Category = Skeletal, meta = (PinShownByDefault))
+ FBPOpenXRActionSkeletalData OptionalStoredActionInfo;
+
+ // MappedBonePairs, if you leave it blank then they will auto generate based off of the SkeletonType
+ // Otherwise, fill out yourself.
+ UPROPERTY(EditAnywhere, Category = Skeletal, meta = (PinHiddenByDefault))
+ FBPOpenXRSkeletalMappingData MappedBonePairs;
+
+ bool bIsOpenInputAnimationInstance;
+
+ void ConvertHandTransformsSpace(TArray& OutTransforms, const TArray& WorldTransforms, FTransform AddTrans, bool bMirrorLeftRight, bool bMergeMissingUE4Bones);
+
+ void CalculateSkeletalAdjustment(USkeleton* AssetSkeleton);
+ void CalculateOpenXRAdjustment();
+
+ FQuat CalcRotationAboutAxis(const FVector& FromDirection, const FVector& ToDirection, const FVector& Axis)
+ {
+ FVector FromDirectionCp = FVector::CrossProduct(Axis, FromDirection);
+ FVector ToDirectionCp = FVector::CrossProduct(Axis, ToDirection);
+
+ return FQuat::FindBetweenVectors(FromDirectionCp, ToDirectionCp);
+ }
+
+ FTransform GetRefBoneInCS(TArray& RefBones, TArray& RefBonesInfo, int32 BoneIndex)
+ {
+ FTransform BoneTransform;
+
+ if (BoneIndex >= 0)
+ {
+ BoneTransform = RefBones[BoneIndex];
+ if (RefBonesInfo[BoneIndex].ParentIndex >= 0)
+ {
+ BoneTransform *= GetRefBoneInCS(RefBones, RefBonesInfo, RefBonesInfo[BoneIndex].ParentIndex);
+ }
+ }
+
+ return BoneTransform;
+ }
+
+ void SetVectorToMaxElement(FVector& vec)
+ {
+ FVector absVal = vec.GetAbs();
+ if (absVal.X > absVal.Y && absVal.X > absVal.Z)
+ {
+ vec = vec.GetSignVector() * FVector(1.0f, 0.f, 0.f);
+ vec.Normalize();
+ }
+ else if (absVal.Y > absVal.X && absVal.Y > absVal.Z)
+ {
+ vec = vec.GetSignVector() * FVector(0.0f, 1.f, 0.f);
+ vec.Normalize();
+ }
+ else if (absVal.Z > absVal.X && absVal.Z > absVal.Y)
+ {
+ vec = vec.GetSignVector() * FVector(0.0f, 0.f, 1.f);
+ vec.Normalize();
+ }
+ else
+ {
+ vec.Normalize();
+ }
+ }
+
+ // FAnimNode_SkeletalControlBase interface
+ //virtual void UpdateInternal(const FAnimationUpdateContext& Context) override;
+ virtual void EvaluateSkeletalControl_AnyThread(FComponentSpacePoseContext& Output, TArray& OutBoneTransforms) override;
+ virtual bool IsValidToEvaluate(const USkeleton* Skeleton, const FBoneContainer& RequiredBones) override;
+ // End of FAnimNode_SkeletalControlBase interface
+ virtual void OnInitializeAnimInstance(const FAnimInstanceProxy* InProxy, const UAnimInstance* InAnimInstance) override;
+ virtual bool NeedsOnInitializeAnimInstance() const override { return true; }
+ virtual void InitializeBoneReferences(const FBoneContainer& RequiredBones) override;
+
+ virtual void Initialize_AnyThread(const FAnimationInitializeContext& Context) override;
+ virtual void CacheBones_AnyThread(const FAnimationCacheBonesContext& Context) override;
+
+ // Constructor
+ FAnimNode_ApplyOpenXRHandPose();
+
+protected:
+ bool WorldIsGame;
+ AActor* OwningActor;
+
+private:
+};
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionPlugin/Public/OpenXRExpansionFunctionLibrary.h b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionPlugin/Public/OpenXRExpansionFunctionLibrary.h
new file mode 100644
index 0000000..dbac085
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionPlugin/Public/OpenXRExpansionFunctionLibrary.h
@@ -0,0 +1,102 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+#include "CoreMinimal.h"
+#include "UObject/ObjectMacros.h"
+#include "Kismet/BlueprintFunctionLibrary.h"
+#include "UObject/Object.h"
+#include "Engine/EngineTypes.h"
+#include "HeadMountedDisplayTypes.h"
+#if defined(OPENXR_SUPPORTED)
+#include "OpenXRCore.h"
+#include "OpenXRHMD.h"
+#endif
+#include "OpenXRExpansionTypes.h"
+#include "HeadMountedDisplayFunctionLibrary.h"
+
+#include "Misc/FileHelper.h"
+#include "Misc/Paths.h"
+
+#include "OpenXRExpansionFunctionLibrary.generated.h"
+
+#if !defined(OPENXR_SUPPORTED)
+class FOpenXRHMD;
+#endif
+
+DECLARE_LOG_CATEGORY_EXTERN(OpenXRExpansionFunctionLibraryLog, Log, All);
+
+// This needs to be updated as the original gets changed, that or hope they make the original blueprint accessible.
+UENUM(Blueprintable)
+enum class EBPOpenXRControllerDeviceType : uint8
+{
+ DT_SimpleController,
+ DT_ValveIndexController,
+ DT_ViveController,
+ DT_ViveProController,
+ //DT_CosmosController,
+ DT_DaydreamController,
+ DT_OculusTouchController,
+ DT_OculusGoController,
+ DT_MicrosoftMotionController,
+ DT_MicrosoftXboxController,
+ DT_PicoNeo3Controller,
+ DT_WMRController,
+ DT_UnknownController
+};
+
+UCLASS(Blueprintable, meta = (BlueprintSpawnableComponent))
+class OPENXREXPANSIONPLUGIN_API UOpenXRExpansionFunctionLibrary : public UBlueprintFunctionLibrary
+{
+ //GENERATED_BODY()
+ GENERATED_BODY()
+
+public:
+ UOpenXRExpansionFunctionLibrary(const FObjectInitializer& ObjectInitializer);
+
+ ~UOpenXRExpansionFunctionLibrary();
+public:
+
+ static FOpenXRHMD* GetOpenXRHMD()
+ {
+#if defined(OPENXR_SUPPORTED)
+ static FName SystemName(TEXT("OpenXR"));
+ if (GEngine->XRSystem.IsValid() && (GEngine->XRSystem->GetSystemName() == SystemName))
+ {
+ return static_cast(GEngine->XRSystem.Get());
+ }
+#endif
+
+ return nullptr;
+ }
+
+ UFUNCTION(BlueprintCallable, Category = "VRExpansionFunctions|OpenXR", meta = (bIgnoreSelf = "true"))
+ static bool GetOpenXRHandPose(FBPOpenXRActionSkeletalData& HandPoseContainer, UOpenXRHandPoseComponent* HandPoseComponent, bool bGetMockUpPose = false);
+
+ //UFUNCTION(BlueprintCallable, Category = "VRExpansionFunctions|OpenXR", meta = (bIgnoreSelf = "true"))
+ static void GetFingerCurlValues(TArray& TransformArray, TArray& CurlArray);
+
+ // Get the estimated curl values from hand tracking
+ // Will return true if it was able to get the curls, false if it could not (hand tracking not enabled or no data for the tracked index)
+ UFUNCTION(BlueprintCallable, Category = "VRExpansionFunctions|OpenXR", meta = (WorldContext = "WorldContextObject"))
+ static bool GetOpenXRFingerCurlValuesForHand(
+ UObject* WorldContextObject,
+ EControllerHand TargetHand,
+ float& ThumbCurl,
+ float& IndexCurl,
+ float& MiddleCurl,
+ float& RingCurl,
+ float& PinkyCurl);
+
+ static float GetCurlValueForBoneRoot(TArray& TransformArray, EHandKeypoint RootBone);
+
+ //UFUNCTION(BlueprintCallable, Category = "VRExpansionFunctions|OpenXR", meta = (bIgnoreSelf = "true"))
+ static void ConvertHandTransformsSpaceAndBack(TArray& OutTransforms, const TArray& WorldTransforms);
+
+ UFUNCTION(BlueprintCallable, Category = "VRExpansionFunctions|OpenXR", meta = (bIgnoreSelf = "true"))
+ static void GetMockUpControllerData(FXRMotionControllerData& MotionControllerData, FBPOpenXRActionSkeletalData& SkeletalMappingData, bool bOpenHand = false);
+
+ // Get a list of all currently tracked devices and their types, index in the array is their device index
+ // Returns failed if the openXR query failed (no interaction profile yet or openXR is not running)
+ UFUNCTION(BlueprintCallable, Category = "VRExpansionFunctions|OpenXR", meta = (bIgnoreSelf = "true", ExpandEnumAsExecs = "Result"))
+ static void GetXRMotionControllerType(FString& TrackingSystemName, EBPOpenXRControllerDeviceType& DeviceType, EBPXRResultSwitch &Result);
+};
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionPlugin/Public/OpenXRExpansionPlugin.h b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionPlugin/Public/OpenXRExpansionPlugin.h
new file mode 100644
index 0000000..23d057c
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionPlugin/Public/OpenXRExpansionPlugin.h
@@ -0,0 +1,18 @@
+// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
+
+#pragma once
+
+#include "Modules/ModuleManager.h"
+
+class FOpenXRExpansionPluginModule : public IModuleInterface
+{
+public:
+
+ FOpenXRExpansionPluginModule()
+ {
+ }
+
+ /** IModuleInterface implementation */
+ virtual void StartupModule() override;
+ virtual void ShutdownModule() override;
+};
\ No newline at end of file
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionPlugin/Public/OpenXRExpansionTypes.h b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionPlugin/Public/OpenXRExpansionTypes.h
new file mode 100644
index 0000000..c2b7af6
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionPlugin/Public/OpenXRExpansionTypes.h
@@ -0,0 +1,479 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+#include "CoreMinimal.h"
+#include "UObject/ObjectMacros.h"
+#include "Kismet/BlueprintFunctionLibrary.h"
+#include "UObject/Object.h"
+#include "Engine/EngineTypes.h"
+
+#include "OpenXRExpansionTypes.generated.h"
+
+// This makes a lot of the blueprint functions cleaner
+UENUM()
+enum class EBPXRResultSwitch : uint8
+{
+ // On Success
+ OnSucceeded,
+ // On Failure
+ OnFailed
+};
+
+UENUM(BlueprintType)
+enum class EVRSkeletalHandIndex : uint8
+{
+ EActionHandIndex_Left = 0,
+ EActionHandIndex_Right
+};
+
+UENUM(BlueprintType)
+enum class EXRHandJointType : uint8
+{
+ OXR_HAND_JOINT_PALM_EXT = 0,
+ OXR_HAND_JOINT_WRIST_EXT = 1,
+ OXR_HAND_JOINT_THUMB_METACARPAL_EXT = 2,
+ OXR_HAND_JOINT_THUMB_PROXIMAL_EXT = 3,
+ OXR_HAND_JOINT_THUMB_DISTAL_EXT = 4,
+ OXR_HAND_JOINT_THUMB_TIP_EXT = 5,
+ OXR_HAND_JOINT_INDEX_METACARPAL_EXT = 6,
+ OXR_HAND_JOINT_INDEX_PROXIMAL_EXT = 7,
+ OXR_HAND_JOINT_INDEX_INTERMEDIATE_EXT = 8,
+ OXR_HAND_JOINT_INDEX_DISTAL_EXT = 9,
+ OXR_HAND_JOINT_INDEX_TIP_EXT = 10,
+ OXR_HAND_JOINT_MIDDLE_METACARPAL_EXT = 11,
+ OXR_HAND_JOINT_MIDDLE_PROXIMAL_EXT = 12,
+ OXR_HAND_JOINT_MIDDLE_INTERMEDIATE_EXT = 13,
+ OXR_HAND_JOINT_MIDDLE_DISTAL_EXT = 14,
+ OXR_HAND_JOINT_MIDDLE_TIP_EXT = 15,
+ OXR_HAND_JOINT_RING_METACARPAL_EXT = 16,
+ OXR_HAND_JOINT_RING_PROXIMAL_EXT = 17,
+ OXR_HAND_JOINT_RING_INTERMEDIATE_EXT = 18,
+ OXR_HAND_JOINT_RING_DISTAL_EXT = 19,
+ OXR_HAND_JOINT_RING_TIP_EXT = 20,
+ OXR_HAND_JOINT_LITTLE_METACARPAL_EXT = 21,
+ OXR_HAND_JOINT_LITTLE_PROXIMAL_EXT = 22,
+ OXR_HAND_JOINT_LITTLE_INTERMEDIATE_EXT = 23,
+ OXR_HAND_JOINT_LITTLE_DISTAL_EXT = 24,
+ OXR_HAND_JOINT_LITTLE_TIP_EXT = 25,
+ OXR_HAND_JOINT_MAX_ENUM_EXT = 0xFF
+};
+
+UENUM(BlueprintType)
+enum class EVROpenXRSkeletonType : uint8
+{
+ // UE4 Skeletal Right hand
+ OXR_SkeletonType_UE4Default_Right,
+ // UE4 Skeletal Left hand
+ OXR_SkeletonType_UE4Default_Left,
+
+ // OpenVR Skeletal Right hand
+ OXR_SkeletonType_OpenVRDefault_Right,
+
+ // OpenVR Skeletal Left hand
+ OXR_SkeletonType_OpenVRDefault_Left,
+
+ // UE5 Skeletal Right hand
+ OXR_SkeletonType_UE5Default_Right,
+ // UE5 Skeletal Left hand
+ OXR_SkeletonType_UE5Default_Left,
+
+ // OpenXR Skeletal Right hand
+ OXR_SkeletonType_OpenXRDefault_Right,
+ // OpenXR Skeletal Left hand
+ OXR_SkeletonType_OpenXRDefault_Left,
+
+ OXR_SkeletonType_Custom
+};
+
+
+
+USTRUCT(BlueprintType, Category = "VRExpansionFunctions|OpenXR|HandSkeleton")
+struct OPENXREXPANSIONPLUGIN_API FBPOpenXRActionSkeletalData
+{
+ GENERATED_BODY()
+public:
+
+ UPROPERTY(EditAnywhere, NotReplicated, BlueprintReadWrite, Category = Default)
+ EVRSkeletalHandIndex TargetHand;
+
+ // A world scale override that will replace the engines current value and force into the tracked data if non zero
+ UPROPERTY(EditAnywhere, NotReplicated, BlueprintReadWrite, Category = Default)
+ float WorldScaleOverride;
+
+ UPROPERTY(EditAnywhere, NotReplicated, BlueprintReadWrite, Category = Default)
+ bool bAllowDeformingMesh;
+
+ // If true then the bones will be mirrored from left/right, to allow you to swap a hand mesh or apply to a full body mesh
+ UPROPERTY(EditAnywhere, NotReplicated, BlueprintReadWrite, Category = Default)
+ bool bMirrorLeftRight;
+
+ // List of aproximated curls for each finger
+ UPROPERTY(BlueprintReadOnly, NotReplicated, Transient, Category = Default)
+ TArray FingerCurls;
+
+ UPROPERTY(BlueprintReadOnly, NotReplicated, Transient, Category = Default)
+ TArray SkeletalTransforms;
+
+ // If true we will assume that the target skeleton does not have the metacarpal bones and we will not replicate them
+ // Only really used for the old UE4 skeleton
+ UPROPERTY(EditAnywhere, NotReplicated, BlueprintReadWrite, Category = Default)
+ bool bEnableUE4HandRepSavings;
+
+ //UPROPERTY(BlueprintReadOnly, NotReplicated, Transient, Category = Default)
+ //TArray OldSkeletalTransforms;
+
+ // The rotation required to rotate the finger bones back to X+
+ // The animation node attempts to auto calculate it, if you have a non standard hand you may need to fill
+ // This in by yourself
+ UPROPERTY(EditAnywhere, NotReplicated, BlueprintReadWrite, Category = Default)
+ FTransform AdditionTransform;
+
+ UPROPERTY(NotReplicated, BlueprintReadOnly, Category = Default)
+ bool bHasValidData;
+
+ FName LastHandGesture;
+ int32 LastHandGestureIndex;
+
+ FBPOpenXRActionSkeletalData()
+ {
+ //bGetTransformsInParentSpace = false;
+ AdditionTransform = FTransform::Identity;// FTransform(FRotator(180.f, 0.f, -90.f), FVector::ZeroVector, FVector(1.f));//FTransform(FRotator(0.f, 90.f, 90.f), FVector::ZeroVector, FVector(1.f));
+ WorldScaleOverride = 0.0f;
+ bAllowDeformingMesh = true;
+ bMirrorLeftRight = false;
+ bEnableUE4HandRepSavings = false;
+ TargetHand = EVRSkeletalHandIndex::EActionHandIndex_Right;
+ bHasValidData = false;
+ LastHandGestureIndex = INDEX_NONE;
+ LastHandGesture = NAME_None;
+ }
+};
+
+USTRUCT(BlueprintType, Category = "VRExpansionFunctions|SteamVR|HandSkeleton")
+struct OPENXREXPANSIONPLUGIN_API FBPOpenXRSkeletalPair
+{
+ GENERATED_BODY()
+public:
+
+ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Default")
+ EXRHandJointType OpenXRBone;
+
+ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Default")
+ FName BoneToTarget;
+
+ FBoneReference ReferenceToConstruct;
+ FCompactPoseBoneIndex ParentReference;
+ FQuat RetargetRot;
+
+ FBPOpenXRSkeletalPair() :
+ ParentReference(INDEX_NONE)
+ {
+ OpenXRBone = EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT;
+ BoneToTarget = NAME_None;
+ RetargetRot = FQuat::Identity;
+ }
+
+ FBPOpenXRSkeletalPair(EXRHandJointType Bone, FString TargetBone) :
+ ParentReference(INDEX_NONE)
+ {
+ OpenXRBone = Bone;
+ BoneToTarget = FName(*TargetBone);
+ ReferenceToConstruct.BoneName = BoneToTarget;
+ RetargetRot = FQuat::Identity;
+ }
+
+ FORCEINLINE bool operator==(const int32& Other) const
+ {
+ return ReferenceToConstruct.CachedCompactPoseIndex.GetInt() == Other;
+ //return ReferenceToConstruct.BoneIndex == Other;
+ }
+};
+
+USTRUCT(BlueprintType, Category = "VRExpansionFunctions|SteamVR|HandSkeleton")
+struct OPENXREXPANSIONPLUGIN_API FBPOpenXRSkeletalMappingData
+{
+ GENERATED_BODY()
+public:
+
+ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Default")
+ TArray BonePairs;
+
+ TArray ReverseBonePairMap;
+
+ // Merge the transforms of bones that are missing from the OpenVR skeleton to the UE4 one.
+ // This should be always enabled for UE4 skeletons generally.
+ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Default")
+ bool bMergeMissingBonesUE4;
+
+ // The hand data to get, if not using a custom bone mapping then this value will be auto filled
+ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Default")
+ EVRSkeletalHandIndex TargetHand;
+
+ FQuat AdjustmentQuat;
+ bool bInitialized;
+
+ FName LastInitializedName;
+ EVROpenXRSkeletonType LastInitializedSkeleton;
+
+ void ClearMapping()
+ {
+ bInitialized = false;
+ LastInitializedName = NAME_None;
+ AdjustmentQuat = FQuat::Identity;
+ LastInitializedSkeleton = EVROpenXRSkeletonType::OXR_SkeletonType_Custom;
+
+ BonePairs.Empty();
+ ReverseBonePairMap.Empty();
+ }
+
+ void ConstructReverseMapping()
+ {
+ int32 MaxElements = ((uint8)EXRHandJointType::OXR_HAND_JOINT_LITTLE_TIP_EXT) + 1;
+ ReverseBonePairMap.Empty(MaxElements);
+ ReverseBonePairMap.AddUninitialized(MaxElements);
+ FMemory::Memset(ReverseBonePairMap.GetData(), 0, MaxElements * sizeof(int32));
+
+
+ for (int i = 0; i < BonePairs.Num(); ++i)
+ {
+ // Just in case someone messed up the mapping file
+ if (i < MaxElements)
+ {
+ ReverseBonePairMap[(uint8)BonePairs[i].OpenXRBone] = i;
+ }
+ }
+ }
+
+ void ConstructDefaultMappings(EVROpenXRSkeletonType SkeletonType, bool bSkipRootBone)
+ {
+ switch (SkeletonType)
+ {
+
+ case EVROpenXRSkeletonType::OXR_SkeletonType_OpenVRDefault_Left:
+ case EVROpenXRSkeletonType::OXR_SkeletonType_OpenVRDefault_Right:
+ {
+ bMergeMissingBonesUE4 = false;
+ SetDefaultOpenVRInputs(SkeletonType, bSkipRootBone);
+ }break;
+ case EVROpenXRSkeletonType::OXR_SkeletonType_UE4Default_Left:
+ case EVROpenXRSkeletonType::OXR_SkeletonType_UE4Default_Right:
+ {
+ bMergeMissingBonesUE4 = true;
+ SetDefaultUE4Inputs(SkeletonType, bSkipRootBone);
+ }break;
+ case EVROpenXRSkeletonType::OXR_SkeletonType_UE5Default_Left:
+ case EVROpenXRSkeletonType::OXR_SkeletonType_UE5Default_Right:
+ {
+ bMergeMissingBonesUE4 = false;
+ SetDefaultUE5Inputs(SkeletonType, bSkipRootBone);
+ }break;
+ case EVROpenXRSkeletonType::OXR_SkeletonType_OpenXRDefault_Left:
+ case EVROpenXRSkeletonType::OXR_SkeletonType_OpenXRDefault_Right:
+ {
+ bMergeMissingBonesUE4 = false;
+ SetDefaultOpenXRInputs(SkeletonType, bSkipRootBone);
+ }break;
+ }
+ }
+
+ void SetDefaultOpenVRInputs(EVROpenXRSkeletonType cSkeletonType, bool bSkipRootBone)
+ {
+ // Don't map anything if the end user already has
+ if (BonePairs.Num())
+ return;
+
+ bool bIsRightHand = cSkeletonType != EVROpenXRSkeletonType::OXR_SkeletonType_OpenVRDefault_Left;
+ FString HandDelimiterS = !bIsRightHand ? "l" : "r";
+ const TCHAR* HandDelimiter = *HandDelimiterS;
+
+ TargetHand = bIsRightHand ? EVRSkeletalHandIndex::EActionHandIndex_Right : EVRSkeletalHandIndex::EActionHandIndex_Left;
+
+ // Default OpenVR bones mapping
+ //if (!bSkipRootBone)
+ //{
+ //BonePairs.Add(FBPOpenVRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_PALM_EXT, FString::Printf(TEXT("Root"), HandDelimiter)));
+ //}
+
+ //if (!bSkipRootBone)
+ {
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT, FString::Printf(TEXT("wrist_%s"), HandDelimiter)));
+ }
+
+
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_METACARPAL_EXT, FString::Printf(TEXT("finger_thumb_0_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_PROXIMAL_EXT, FString::Printf(TEXT("finger_thumb_1_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_DISTAL_EXT, FString::Printf(TEXT("finger_thumb_2_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_TIP_EXT, FString::Printf(TEXT("finger_thumb_%s_end"), HandDelimiter)));
+
+
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_METACARPAL_EXT, FString::Printf(TEXT("finger_index_meta_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_PROXIMAL_EXT, FString::Printf(TEXT("finger_index_0_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_INTERMEDIATE_EXT, FString::Printf(TEXT("finger_index_1_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_DISTAL_EXT, FString::Printf(TEXT("finger_index_2_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_TIP_EXT, FString::Printf(TEXT("finger_index_%s_end"), HandDelimiter)));
+
+
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_METACARPAL_EXT, FString::Printf(TEXT("finger_middle_meta_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_PROXIMAL_EXT, FString::Printf(TEXT("finger_middle_0_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_INTERMEDIATE_EXT, FString::Printf(TEXT("finger_middle_1_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_DISTAL_EXT, FString::Printf(TEXT("finger_middle_2_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_TIP_EXT, FString::Printf(TEXT("finger_middle_%s_end"), HandDelimiter)));
+
+
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_METACARPAL_EXT, FString::Printf(TEXT("finger_ring_meta_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_PROXIMAL_EXT, FString::Printf(TEXT("finger_ring_0_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_INTERMEDIATE_EXT, FString::Printf(TEXT("finger_ring_1_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_DISTAL_EXT, FString::Printf(TEXT("finger_ring_2_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_TIP_EXT, FString::Printf(TEXT("finger_ring_%s_end"), HandDelimiter)));
+
+
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_METACARPAL_EXT, FString::Printf(TEXT("finger_pinky_meta_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_PROXIMAL_EXT, FString::Printf(TEXT("finger_pinky_0_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_INTERMEDIATE_EXT, FString::Printf(TEXT("finger_pinky_1_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_DISTAL_EXT, FString::Printf(TEXT("finger_pinky_2_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_TIP_EXT, FString::Printf(TEXT("finger_pinky_%s_end"), HandDelimiter)));
+
+ // Aux bones share the final knuckles location / rotation
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_DISTAL_EXT, FString::Printf(TEXT("finger_thumb_%s_aux"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_DISTAL_EXT, FString::Printf(TEXT("finger_index_%s_aux"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_DISTAL_EXT, FString::Printf(TEXT("finger_middle_%s_aux"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_DISTAL_EXT, FString::Printf(TEXT("finger_ring_%s_aux"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_DISTAL_EXT, FString::Printf(TEXT("finger_pinky_%s_aux"), HandDelimiter)));
+ }
+
+ void SetDefaultUE4Inputs(EVROpenXRSkeletonType cSkeletonType, bool bSkipRootBone)
+ {
+ // Don't map anything if the end user already has
+ if (BonePairs.Num())
+ return;
+
+ bool bIsRightHand = cSkeletonType != EVROpenXRSkeletonType::OXR_SkeletonType_UE4Default_Left;
+ FString HandDelimiterS = !bIsRightHand ? "l" : "r";
+ const TCHAR* HandDelimiter = *HandDelimiterS;
+
+ TargetHand = bIsRightHand ? EVRSkeletalHandIndex::EActionHandIndex_Right : EVRSkeletalHandIndex::EActionHandIndex_Left;
+
+ // Default ue4 skeleton hand to the OpenVR bones, skipping the extra joint and the aux joints
+ //if (!bSkipRootBone)
+ {
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT, FString::Printf(TEXT("hand_%s"), HandDelimiter)));
+ }
+
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_PROXIMAL_EXT, FString::Printf(TEXT("index_01_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_INTERMEDIATE_EXT, FString::Printf(TEXT("index_02_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_DISTAL_EXT, FString::Printf(TEXT("index_03_%s"), HandDelimiter)));
+
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_PROXIMAL_EXT, FString::Printf(TEXT("middle_01_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_INTERMEDIATE_EXT, FString::Printf(TEXT("middle_02_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_DISTAL_EXT, FString::Printf(TEXT("middle_03_%s"), HandDelimiter)));
+
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_PROXIMAL_EXT, FString::Printf(TEXT("pinky_01_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_INTERMEDIATE_EXT, FString::Printf(TEXT("pinky_02_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_DISTAL_EXT, FString::Printf(TEXT("pinky_03_%s"), HandDelimiter)));
+
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_PROXIMAL_EXT, FString::Printf(TEXT("ring_01_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_INTERMEDIATE_EXT, FString::Printf(TEXT("ring_02_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_DISTAL_EXT, FString::Printf(TEXT("ring_03_%s"), HandDelimiter)));
+
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_METACARPAL_EXT, FString::Printf(TEXT("thumb_01_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_PROXIMAL_EXT, FString::Printf(TEXT("thumb_02_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_DISTAL_EXT, FString::Printf(TEXT("thumb_03_%s"), HandDelimiter)));
+
+ }
+
+ void SetDefaultUE5Inputs(EVROpenXRSkeletonType cSkeletonType, bool bSkipRootBone)
+ {
+ // Don't map anything if the end user already has
+ if (BonePairs.Num())
+ return;
+
+ bool bIsRightHand = cSkeletonType != EVROpenXRSkeletonType::OXR_SkeletonType_UE5Default_Left;
+ FString HandDelimiterS = !bIsRightHand ? "l" : "r";
+ const TCHAR* HandDelimiter = *HandDelimiterS;
+
+ TargetHand = bIsRightHand ? EVRSkeletalHandIndex::EActionHandIndex_Right : EVRSkeletalHandIndex::EActionHandIndex_Left;
+
+ // Default ue5 skeleton hand to the OpenVR bones, skipping the extra joint and the aux joints
+ //if (!bSkipRootBone)
+ {
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT, FString::Printf(TEXT("hand_%s"), HandDelimiter)));
+ }
+
+ // There are inner and outer wrist elements to this, going to be anoying to map that to a single wrist index....
+ //OXR_HAND_JOINT_WRIST_EXT = 1,
+
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_PROXIMAL_EXT, FString::Printf(TEXT("index_01_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_INTERMEDIATE_EXT, FString::Printf(TEXT("index_02_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_DISTAL_EXT, FString::Printf(TEXT("index_03_%s"), HandDelimiter)));
+
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_PROXIMAL_EXT, FString::Printf(TEXT("middle_01_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_INTERMEDIATE_EXT, FString::Printf(TEXT("middle_02_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_DISTAL_EXT, FString::Printf(TEXT("middle_03_%s"), HandDelimiter)));
+
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_PROXIMAL_EXT, FString::Printf(TEXT("pinky_01_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_INTERMEDIATE_EXT, FString::Printf(TEXT("pinky_02_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_DISTAL_EXT, FString::Printf(TEXT("pinky_03_%s"), HandDelimiter)));
+
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_PROXIMAL_EXT, FString::Printf(TEXT("ring_01_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_INTERMEDIATE_EXT, FString::Printf(TEXT("ring_02_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_DISTAL_EXT, FString::Printf(TEXT("ring_03_%s"), HandDelimiter)));
+
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_METACARPAL_EXT, FString::Printf(TEXT("thumb_01_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_PROXIMAL_EXT, FString::Printf(TEXT("thumb_02_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_DISTAL_EXT, FString::Printf(TEXT("thumb_03_%s"), HandDelimiter)));
+
+ }
+
+ void SetDefaultOpenXRInputs(EVROpenXRSkeletonType cSkeletonType, bool bSkipRootBone)
+ {
+ // Don't map anything if the end user already has
+ if (BonePairs.Num())
+ return;
+
+ bool bIsRightHand = cSkeletonType != EVROpenXRSkeletonType::OXR_SkeletonType_OpenXRDefault_Left;
+ FString HandDelimiterS = !bIsRightHand ? "l" : "r";
+ const TCHAR* HandDelimiter = *HandDelimiterS;
+
+ TargetHand = bIsRightHand ? EVRSkeletalHandIndex::EActionHandIndex_Right : EVRSkeletalHandIndex::EActionHandIndex_Left;
+
+ // Default ue5 skeleton hand to the OpenVR bones, skipping the extra joint and the aux joints
+ //if (!bSkipRootBone)
+ {
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT, FString::Printf(TEXT("hand_%s"), HandDelimiter)));
+ }
+
+ // There are inner and outer wrist elements to this, going to be anoyying to map that to a single wrist index....
+ //OXR_HAND_JOINT_WRIST_EXT = 1,
+
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_PROXIMAL_EXT, FString::Printf(TEXT("index_01_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_INTERMEDIATE_EXT, FString::Printf(TEXT("index_02_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_DISTAL_EXT, FString::Printf(TEXT("index_03_%s"), HandDelimiter)));
+
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_PROXIMAL_EXT, FString::Printf(TEXT("middle_01_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_INTERMEDIATE_EXT, FString::Printf(TEXT("middle_02_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_DISTAL_EXT, FString::Printf(TEXT("middle_03_%s"), HandDelimiter)));
+
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_PROXIMAL_EXT, FString::Printf(TEXT("pinky_01_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_INTERMEDIATE_EXT, FString::Printf(TEXT("pinky_02_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_DISTAL_EXT, FString::Printf(TEXT("pinky_03_%s"), HandDelimiter)));
+
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_PROXIMAL_EXT, FString::Printf(TEXT("ring_01_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_INTERMEDIATE_EXT, FString::Printf(TEXT("ring_02_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_DISTAL_EXT, FString::Printf(TEXT("ring_03_%s"), HandDelimiter)));
+
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_METACARPAL_EXT, FString::Printf(TEXT("thumb_01_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_PROXIMAL_EXT, FString::Printf(TEXT("thumb_02_%s"), HandDelimiter)));
+ BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_DISTAL_EXT, FString::Printf(TEXT("thumb_03_%s"), HandDelimiter)));
+
+ }
+
+ FBPOpenXRSkeletalMappingData()
+ {
+ AdjustmentQuat = FQuat::Identity;
+ bInitialized = false;
+ bMergeMissingBonesUE4 = false;
+ TargetHand = EVRSkeletalHandIndex::EActionHandIndex_Right;
+ LastInitializedName = NAME_None;
+ LastInitializedSkeleton = EVROpenXRSkeletonType::OXR_SkeletonType_Custom;
+ }
+};
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionPlugin/Public/OpenXRHandPoseComponent.h b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionPlugin/Public/OpenXRHandPoseComponent.h
new file mode 100644
index 0000000..b27ff0f
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/OpenXRExpansionPlugin/Source/OpenXRExpansionPlugin/Public/OpenXRHandPoseComponent.h
@@ -0,0 +1,377 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+#include "CoreMinimal.h"
+#include "UObject/Object.h"
+#include "Engine/Texture.h"
+#include "Engine/EngineTypes.h"
+#include "HeadMountedDisplayTypes.h"
+//#include "Runtime/Launch/Resources/Version.h"
+
+#include "Animation/AnimInstanceProxy.h"
+#include "OpenXRExpansionTypes.h"
+#include "Engine/DataAsset.h"
+
+#include "OpenXRHandPoseComponent.generated.h"
+
+USTRUCT(BlueprintType, Category = "VRExpansionFunctions|OpenXR|HandSkeleton")
+struct OPENXREXPANSIONPLUGIN_API FBPXRSkeletalRepContainer
+{
+ GENERATED_BODY()
+public:
+
+ UPROPERTY(Transient, NotReplicated)
+ EVRSkeletalHandIndex TargetHand;
+
+ UPROPERTY(Transient, NotReplicated)
+ bool bAllowDeformingMesh;
+
+ // If true we will skip sending the 4 metacarpal bones that ue4 doesn't need, (STEAMVR skeletons need this disabled!)
+ // Only really used for the older UE4 skeleton
+ UPROPERTY(Transient, NotReplicated)
+ bool bEnableUE4HandRepSavings;
+
+ UPROPERTY(Transient, NotReplicated)
+ TArray SkeletalTransforms;
+
+ UPROPERTY(Transient, NotReplicated)
+ uint8 BoneCount;
+
+
+ FBPXRSkeletalRepContainer()
+ {
+ TargetHand = EVRSkeletalHandIndex::EActionHandIndex_Left;
+ bAllowDeformingMesh = false;
+ bEnableUE4HandRepSavings = false;
+ BoneCount = 0;
+ }
+
+ bool bHasValidData()
+ {
+ return SkeletalTransforms.Num() > 0;
+ }
+
+ void CopyForReplication(FBPOpenXRActionSkeletalData& Other);
+ static void CopyReplicatedTo(const FBPXRSkeletalRepContainer& Container, FBPOpenXRActionSkeletalData& Other);
+
+ bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess);
+};
+
+template<>
+struct TStructOpsTypeTraits< FBPXRSkeletalRepContainer > : public TStructOpsTypeTraitsBase2
+{
+ enum
+ {
+ WithNetSerializer = true
+ };
+};
+
+USTRUCT(BlueprintType, Category = "VRGestures")
+struct OPENXREXPANSIONPLUGIN_API FOpenXRGestureFingerPosition
+{
+ GENERATED_BODY()
+public:
+
+ // The Finger index, not editable
+ UPROPERTY(BlueprintReadOnly, VisibleAnywhere, Category = "VRGesture")
+ EXRHandJointType IndexType;
+
+ // The locational value of this element 0.f - 1.f
+ UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGesture")
+ FVector Value;
+
+ // The threshold within which this finger value will be detected as matching (1.0 would be always matching, IE: finger doesn't count)
+ UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGesture", meta = (ClampMin = "0.0", ClampMax = "100.0", UIMin = "0.0", UIMax = "100.0"))
+ float Threshold;
+
+ FOpenXRGestureFingerPosition(FVector TipLoc, EXRHandJointType Type)
+ {
+ IndexType = Type;
+ Value = TipLoc;
+ Threshold = 5.0f;
+ }
+ FOpenXRGestureFingerPosition()
+ {
+ IndexType = EXRHandJointType::OXR_HAND_JOINT_INDEX_TIP_EXT;
+ Value = FVector(0.f);
+ Threshold = 5.0f;
+ }
+};
+
+USTRUCT(BlueprintType, Category = "VRGestures")
+struct OPENXREXPANSIONPLUGIN_API FOpenXRGesture
+{
+ GENERATED_BODY()
+public:
+
+ // Name of the recorded gesture
+ UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGesture")
+ FName Name;
+
+ // Samples in the recorded gesture
+ UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGesture")
+ TArray FingerValues;
+
+ FOpenXRGesture()
+ {
+ InitPoseValues();
+ Name = NAME_None;
+ }
+
+ void InitPoseValues()
+ {
+ FingerValues.Add(FOpenXRGestureFingerPosition(FVector::ZeroVector, EXRHandJointType::OXR_HAND_JOINT_THUMB_TIP_EXT));
+ FingerValues.Add(FOpenXRGestureFingerPosition(FVector::ZeroVector, EXRHandJointType::OXR_HAND_JOINT_INDEX_TIP_EXT));
+ FingerValues.Add(FOpenXRGestureFingerPosition(FVector::ZeroVector, EXRHandJointType::OXR_HAND_JOINT_MIDDLE_TIP_EXT));
+ FingerValues.Add(FOpenXRGestureFingerPosition(FVector::ZeroVector, EXRHandJointType::OXR_HAND_JOINT_RING_TIP_EXT));
+ FingerValues.Add(FOpenXRGestureFingerPosition(FVector::ZeroVector, EXRHandJointType::OXR_HAND_JOINT_LITTLE_TIP_EXT));
+ }
+};
+
+/**
+* Items Database DataAsset, here we can save all of our game items
+*/
+UCLASS(BlueprintType, Category = "VRGestures")
+class OPENXREXPANSIONPLUGIN_API UOpenXRGestureDatabase : public UDataAsset
+{
+ GENERATED_BODY()
+public:
+
+ // Gestures in this database
+ UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGestures")
+ TArray Gestures;
+
+ UOpenXRGestureDatabase()
+ {
+ }
+};
+
+DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOpenXRGestureDetected, const FName &, GestureDetected, int32, GestureIndex, EVRSkeletalHandIndex, ActionHandType);
+DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOpenXRGestureEnded, const FName &, GestureEnded, int32, GestureIndex, EVRSkeletalHandIndex, ActionHandType);
+
+UCLASS(Blueprintable, meta = (BlueprintSpawnableComponent))
+class OPENXREXPANSIONPLUGIN_API UOpenXRHandPoseComponent : public UActorComponent
+{
+ GENERATED_BODY()
+
+public:
+ UOpenXRHandPoseComponent(const FObjectInitializer& ObjectInitializer);
+
+ // Says whether we should run gesture detection
+ UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGestures")
+ bool bDetectGestures;
+
+ UFUNCTION(BlueprintCallable, Category = "VRGestures")
+ void SetDetectGestures(bool bNewDetectGestures)
+ {
+ bDetectGestures = bNewDetectGestures;
+ }
+
+ UPROPERTY(BlueprintAssignable, Category = "VRGestures")
+ FOpenXRGestureDetected OnNewGestureDetected;
+
+ UPROPERTY(BlueprintAssignable, Category = "VRGestures")
+ FOpenXRGestureEnded OnGestureEnded;
+
+ // Known sequences
+ UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGestures")
+ UOpenXRGestureDatabase *GesturesDB;
+
+ UFUNCTION(BlueprintCallable, Category = "VRGestures")
+ bool SaveCurrentPose(FName RecordingName, EVRSkeletalHandIndex HandToSave = EVRSkeletalHandIndex::EActionHandIndex_Right);
+
+ UFUNCTION(BlueprintCallable, Category = "VRGestures", meta = (DisplayName = "DetectCurrentPose"))
+ bool K2_DetectCurrentPose(UPARAM(ref) FBPOpenXRActionSkeletalData& SkeletalAction, FOpenXRGesture & GestureOut);
+
+ // This version throws events
+ bool DetectCurrentPose(FBPOpenXRActionSkeletalData& SkeletalAction);
+
+ // Need this as I can't think of another way for an actor component to make sure it isn't on the server
+ inline bool IsLocallyControlled() const
+ {
+#if ENGINE_MAJOR_VERSION > 4 || (ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 22)
+ const AActor* MyOwner = GetOwner();
+ return MyOwner->HasLocalNetOwner();
+#else
+ // I like epics new authority check more than mine
+ const AActor* MyOwner = GetOwner();
+ const APawn* MyPawn = Cast(MyOwner);
+
+ return MyPawn ? MyPawn->IsLocallyControlled() : (MyOwner && MyOwner->GetLocalRole() == ENetRole::ROLE_Authority);
+#endif
+ }
+
+ // Using tick and not timers because skeletal components tick anyway, kind of a waste to make another tick by adding a timer over that
+ void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) override;
+
+
+ //virtual void OnUnregister() override;
+ virtual void BeginPlay() override;
+
+ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SkeletalData|Actions")
+ bool bGetMockUpPoseForDebugging;
+
+ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SkeletalData|Actions")
+ TArray HandSkeletalActions;
+
+ UPROPERTY(Replicated, Transient, ReplicatedUsing = OnRep_SkeletalTransformLeft)
+ FBPXRSkeletalRepContainer LeftHandRep;
+
+ UPROPERTY(Replicated, Transient, ReplicatedUsing = OnRep_SkeletalTransformRight)
+ FBPXRSkeletalRepContainer RightHandRep;
+
+ UFUNCTION(Unreliable, Server, WithValidation)
+ void Server_SendSkeletalTransforms(const FBPXRSkeletalRepContainer& SkeletalInfo);
+
+ bool bLerpingPositionLeft;
+ bool bLerpingPositionRight;
+
+ struct FTransformLerpManager
+ {
+ bool bReplicatedOnce;
+ bool bLerping;
+ float UpdateCount;
+ float UpdateRate;
+ TArray OldTransforms;
+ TArray NewTransforms;
+
+ FTransformLerpManager();
+ void PreCopyNewData(FBPOpenXRActionSkeletalData& ActionInfo, int NetUpdateRate, bool bExponentialSmoothing);
+ void NotifyNewData(FBPOpenXRActionSkeletalData& ActionInfo, int NetUpdateRate, bool bExponentialSmoothing);
+
+ FORCEINLINE void BlendBone(uint8 BoneToBlend, FBPOpenXRActionSkeletalData& ActionInfo, float& LerpVal, bool bExponentialSmoothing)
+ {
+ ActionInfo.SkeletalTransforms[BoneToBlend].Blend(OldTransforms[BoneToBlend], NewTransforms[BoneToBlend], LerpVal);
+
+ if (bExponentialSmoothing)
+ {
+ // Saving base back out for exponential
+ OldTransforms[BoneToBlend] = ActionInfo.SkeletalTransforms[BoneToBlend];
+ }
+ }
+
+ void UpdateManager(float DeltaTime, FBPOpenXRActionSkeletalData& ActionInfo, UOpenXRHandPoseComponent * ParentComp);
+
+ };
+
+ FTransformLerpManager LeftHandRepManager;
+ FTransformLerpManager RightHandRepManager;
+
+ UFUNCTION()
+ virtual void OnRep_SkeletalTransformLeft()
+ {
+ for (int i = 0; i < HandSkeletalActions.Num(); i++)
+ {
+ if (HandSkeletalActions[i].TargetHand == LeftHandRep.TargetHand)
+ {
+ if (bSmoothReplicatedSkeletalData)
+ {
+ LeftHandRepManager.PreCopyNewData(HandSkeletalActions[i], ReplicationRateForSkeletalAnimations, bUseExponentialSmoothing);
+ }
+
+ FBPXRSkeletalRepContainer::CopyReplicatedTo(LeftHandRep, HandSkeletalActions[i]);
+
+ if (bSmoothReplicatedSkeletalData)
+ {
+ LeftHandRepManager.NotifyNewData(HandSkeletalActions[i], ReplicationRateForSkeletalAnimations, bUseExponentialSmoothing);
+ }
+
+ break;
+ }
+ }
+ }
+
+ UFUNCTION()
+ virtual void OnRep_SkeletalTransformRight()
+ {
+ for (int i = 0; i < HandSkeletalActions.Num(); i++)
+ {
+ if (HandSkeletalActions[i].TargetHand == RightHandRep.TargetHand)
+ {
+ if (bSmoothReplicatedSkeletalData)
+ {
+ RightHandRepManager.PreCopyNewData(HandSkeletalActions[i], ReplicationRateForSkeletalAnimations, bUseExponentialSmoothing);
+ }
+
+ FBPXRSkeletalRepContainer::CopyReplicatedTo(RightHandRep, HandSkeletalActions[i]);
+
+ if (bSmoothReplicatedSkeletalData)
+ {
+ RightHandRepManager.NotifyNewData(HandSkeletalActions[i], ReplicationRateForSkeletalAnimations, bUseExponentialSmoothing);
+ }
+ break;
+ }
+ }
+ }
+
+ // If we should replicate the skeletal transform data
+ UPROPERTY(EditAnywhere, Category = SkeletalData)
+ bool bReplicateSkeletalData;
+
+ // If true we will lerp between updates of the skeletal mesh transforms and smooth the result
+ UPROPERTY(EditAnywhere, Category = SkeletalData)
+ bool bSmoothReplicatedSkeletalData;
+
+ // If true then we will use exponential smoothing with buffered correction
+ UPROPERTY(EditAnywhere, Category = "SkeletalData", meta = (editcondition = "bSmoothReplicatedSkeletalData"))
+ bool bUseExponentialSmoothing = false;
+
+ // Timestep of smoothing translation
+ UPROPERTY(EditAnywhere, Category = "SkeletalData", meta = (editcondition = "bUseExponentialSmoothing"))
+ float InterpolationSpeed = 25.0f;
+
+ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = SkeletalData)
+ float ReplicationRateForSkeletalAnimations;
+
+ // Used in Tick() to accumulate before sending updates, didn't want to use a timer in this case, also used for remotes to lerp position
+ float SkeletalNetUpdateCount;
+ // Used in Tick() to accumulate before sending updates, didn't want to use a timer in this case, also used for remotes to lerp position
+ float SkeletalUpdateCount;
+};
+
+USTRUCT()
+struct OPENXREXPANSIONPLUGIN_API FOpenXRAnimInstanceProxy : public FAnimInstanceProxy
+{
+public:
+ GENERATED_BODY()
+
+ FOpenXRAnimInstanceProxy() {}
+ FOpenXRAnimInstanceProxy(UAnimInstance* InAnimInstance);
+
+ /** Called before update so we can copy any data we need */
+ virtual void PreUpdate(UAnimInstance* InAnimInstance, float DeltaSeconds) override;
+
+public:
+
+ EVRSkeletalHandIndex TargetHand;
+ TArray HandSkeletalActionData;
+
+};
+
+UCLASS(transient, Blueprintable, hideCategories = AnimInstance, BlueprintType)
+class OPENXREXPANSIONPLUGIN_API UOpenXRAnimInstance : public UAnimInstance
+{
+ GENERATED_BODY()
+
+public:
+
+ UPROPERTY(transient)
+ UOpenXRHandPoseComponent * OwningPoseComp;
+
+ FOpenXRAnimInstanceProxy AnimInstanceProxy;
+
+ virtual FAnimInstanceProxy* CreateAnimInstanceProxy() override
+ {
+ return new FOpenXRAnimInstanceProxy(this);
+ //return &AnimInstanceProxy;
+ }
+
+ virtual void NativeBeginPlay() override;
+
+ //virtual void NativeInitializeAnimation() override;
+
+ UFUNCTION(BlueprintCallable, Category = "BoneMappings")
+ void InitializeCustomBoneMapping(UPARAM(ref) FBPOpenXRSkeletalMappingData & SkeletalMappingData);
+
+
+};
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/README.md b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/README.md
new file mode 100644
index 0000000..875fec0
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/README.md
@@ -0,0 +1,48 @@
+UE4 Forums Thread
+https://forums.unrealengine.com/development-discussion/vr-ar-development/89050-vr-openvr-expansion-plugin
+
+Example Template Project
+https://github.com/mordentral/VRExpPluginExample
+
+Website:
+www.vreue4.com
+
+***
+
+### Use Of This Plugin ###
+
+This Plugin is intended to add additional functionality to Open/SteamVR/(All VR now) in UE4.
+
+### Plugin Website ###
+[VREUE4.com](https://vreue4.com)
+
+### How do I install it? ###
+
+https://vreue4.com/documentation?section=installation
+
+**Guides for migrating between different engine versions of the plugin:**
+
+View the patch notes at www.vreue4.com for migration guides as well.
+
+**Option 1:**
+
+Go to www.vreue4.com and downloaded the pre-built binary version for the engine version you are using (not updated with every daily change, only weekly or with large patches).
+
+Install it into your ProjectName/Plugins Directory (Engine level hasn't worked since 4.25 or so when it stopped letting me reference other plugins when compiling for that).
+
+**Option 2 (More up to date - preferred if possible):**
+
+* Clone Or Download Zip and extract this repository to a folder named "VRExpansionPlugin" in your "ProjectName/Plugins" directory, create this directory if it is missing.
+
+* Add the VRExpansionPlugin to your projects PublicDependencyModuleNames in the projects build.cs if you have c++ code included.
+
+* IF you do not have c++ code, use the Add New button in the editor and add a blank c++ class to your project.
+
+* Open up the generated project .SLN file and build the project from the build menu.
+
+You need to have visual studio installed and follow the UE4 setup guide for it: https://docs.unrealengine.com/latest/INT/Programming/Development/VisualStudioSetup/
+
+### How do I use it? ###
+### How do I VR? ###
+
+The template project contains use examples of most of the features of the plugin as well as locomotion modes, interaction methods, and basic multiplayer.
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Config/FilterPlugin.ini b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Config/FilterPlugin.ini
new file mode 100644
index 0000000..ccebca2
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Config/FilterPlugin.ini
@@ -0,0 +1,8 @@
+[FilterPlugin]
+; This section lists additional files which will be packaged along with your plugin. Paths should be listed relative to the root plugin directory, and
+; may include "...", "*", and "?" wildcards to match directories, files, and individual characters respectively.
+;
+; Examples:
+; /README.txt
+; /Extras/...
+; /Binaries/ThirdParty/*.dll
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Resources/Icon128.png b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Resources/Icon128.png
new file mode 100644
index 0000000..23fec31
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Resources/Icon128.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bb8f329acc9d0c2815d28349d5a4144ebd337f7e2a93819cf1a7c3cc9b06ecea
+size 16085
diff --git a/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionEditor/Private/HandSocketComponentDetails.cpp b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionEditor/Private/HandSocketComponentDetails.cpp
new file mode 100644
index 0000000..cc31a36
--- /dev/null
+++ b/VIRTUOS_ExpansionPluginTests/Plugins/VRExpansionPlugin/VRExpansionPlugin/Source/VRExpansionEditor/Private/HandSocketComponentDetails.cpp
@@ -0,0 +1,849 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+
+#include "HandSocketComponentDetails.h"
+#include "HandSocketVisualizer.h"
+//#include "PropertyEditing.h"
+#include "Widgets/Text/STextBlock.h"
+#include "Widgets/Input/SButton.h"
+#include "PropertyHandle.h"
+#include "DetailLayoutBuilder.h"
+#include "DetailWidgetRow.h"
+#include "DetailCategoryBuilder.h"
+#include "IDetailsView.h"
+
+#include "Developer/AssetTools/Public/IAssetTools.h"
+#include "Developer/AssetTools/Public/AssetToolsModule.h"
+#include "Editor/ContentBrowser/Public/IContentBrowserSingleton.h"
+#include "Editor/ContentBrowser/Public/ContentBrowserModule.h"
+#include "AnimationUtils.h"
+#include "AssetRegistry/AssetRegistryModule.h"
+#include "UObject/SavePackage.h"
+#include "Misc/MessageDialog.h"
+#include "Widgets/Layout/SBorder.h"
+#include "Widgets/Layout/SSeparator.h"
+#include "Widgets/Layout/SUniformGridPanel.h"
+#include "Widgets/Input/SEditableTextBox.h"
+#include "Editor.h"
+#include "EditorStyleSet.h"
+#include "Styling/CoreStyle.h"
+
+#include "Animation/AnimData/AnimDataModel.h"
+
+#include "Editor/UnrealEdEngine.h"
+#include "UnrealEdGlobals.h"
+
+#define LOCTEXT_NAMESPACE "HandSocketComponentDetails"
+
+FText SCreateHandAnimationDlg::LastUsedAssetPath;
+
+static bool PromptUserForAssetPath(FString& AssetPath, FString& AssetName)
+{
+ TSharedRef NewAnimDlg = SNew(SCreateHandAnimationDlg);
+ if (NewAnimDlg->ShowModal() != EAppReturnType::Cancel)
+ {
+ AssetPath = NewAnimDlg->GetFullAssetPath();
+ AssetName = NewAnimDlg->GetAssetName();
+ return true;
+ }
+
+ return false;
+}
+
+TWeakObjectPtr FHandSocketComponentDetails::SaveAnimationAsset(const FString& InAssetPath, const FString& InAssetName)
+{
+
+ TWeakObjectPtr FinalAnimation;
+
+ // Replace when this moves to custom display
+ if (!HandSocketComponent.IsValid())
+ return FinalAnimation;
+
+ /*if (!HandSocketComponent->HandVisualizerComponent)// || !HandSocketComponent->HandVisualizerComponent->SkeletalMesh || !HandSocketComponent->HandVisualizerComponent->SkeletalMesh->Skeleton)
+ {
+ return false;
+ }*/
+
+ if (!HandSocketComponent->HandTargetAnimation && (!HandSocketComponent->VisualizationMesh || !HandSocketComponent->VisualizationMesh->GetSkeleton()))
+ {
+ return FinalAnimation;
+ }
+
+ // create the asset
+ FText InvalidPathReason;
+ bool const bValidPackageName = FPackageName::IsValidLongPackageName(InAssetPath, false, &InvalidPathReason);
+ if (bValidPackageName == false)
+ {
+ UE_LOG(LogAnimation, Log, TEXT("%s is an invalid asset path, prompting user for new asset path. Reason: %s"), *InAssetPath, *InvalidPathReason.ToString());
+ }
+
+ FString ValidatedAssetPath = InAssetPath;
+ FString ValidatedAssetName = InAssetName;
+
+ UObject* Parent = bValidPackageName ? CreatePackage(*ValidatedAssetPath) : nullptr;
+ if (Parent == nullptr)
+ {
+ // bad or no path passed in, do the popup
+ if (PromptUserForAssetPath(ValidatedAssetPath, ValidatedAssetName) == false)
+ {
+ return FinalAnimation;
+ }
+
+ Parent = CreatePackage(*ValidatedAssetPath);
+ }
+
+ UObject* const Object = LoadObject(Parent, *ValidatedAssetName, nullptr, LOAD_Quiet, nullptr);
+ // if object with same name exists, warn user
+ if (Object)
+ {
+ EAppReturnType::Type ReturnValue = FMessageDialog::Open(EAppMsgType::YesNo, NSLOCTEXT("UnrealEd", "Error_AssetExist", "Asset with same name exists. Do you wish to overwrite it?"));
+ if (ReturnValue == EAppReturnType::No)
+ {
+ return FinalAnimation; // failed
+ }
+ }
+
+ UAnimSequence* BaseAnimation = HandSocketComponent->HandTargetAnimation;
+ TArray LocalPoses;
+
+ if (!BaseAnimation)
+ {
+ LocalPoses = HandSocketComponent->VisualizationMesh->GetSkeleton()->GetRefLocalPoses();
+ }
+
+ // If not, create new one now.
+ UAnimSequence* const NewSeq = NewObject(Parent, *ValidatedAssetName, RF_Public | RF_Standalone);
+ if (NewSeq)
+ {
+ // set skeleton
+ if (BaseAnimation)
+ {
+ NewSeq->SetSkeleton(BaseAnimation->GetSkeleton());
+ }
+ else
+ {
+ NewSeq->SetSkeleton(HandSocketComponent->VisualizationMesh->GetSkeleton());
+ }
+
+ // Notify the asset registry
+ FAssetRegistryModule::AssetCreated(NewSeq);
+ //StartRecord(Component, NewSeq);
+
+ //return true;
+ UAnimSequence* AnimationObject = NewSeq;
+
+ IAnimationDataController& AnimController = AnimationObject->GetController();
+ {
+ IAnimationDataController::FScopedBracket ScopedBracket(AnimController, LOCTEXT("SaveAnimationAsset_VRE", "Creating Animation Sequence based on hand pose"));
+ AnimationObject->ResetAnimation();
+ if (BaseAnimation)
+ {
+ AnimationObject->BoneCompressionSettings = BaseAnimation->BoneCompressionSettings;
+ }
+ else
+ {
+ AnimationObject->BoneCompressionSettings = FAnimationUtils::GetDefaultAnimationBoneCompressionSettings();
+ }
+
+ AnimController.InitializeModel();
+ AnimController.RemoveAllBoneTracks(false);
+
+
+ //checkf(MovieScene, TEXT("No Movie Scene found for SequencerDataModel"));
+ //AnimController.SetPlayLength(4.f);
+ AnimController.SetNumberOfFrames(FFrameNumber(1), false);
+
+ // Set frame rate to 1 to 1 as we don't animate
+ AnimController.SetFrameRate(FFrameRate(1, 1));
+
+ TArray TrackNames;
+ const IAnimationDataModel* BaseDataModel = BaseAnimation ? BaseAnimation->GetController().GetModel() : nullptr;
+
+ if (BaseAnimation)
+ {
+ if (BaseDataModel)
+ {
+ BaseDataModel->GetBoneTrackNames(TrackNames);
+ for (FName TrackName : TrackNames)
+ {
+ AnimController.AddBoneCurve(TrackName);
+ }
+ }
+ else
+ {
+ return FinalAnimation;
+ }
+ }
+ else
+ {
+ int numBones = HandSocketComponent->VisualizationMesh->GetRefSkeleton().GetNum();
+ for (int i = 0; i < LocalPoses.Num() && i < numBones; ++i)
+ {
+ AnimController.AddBoneCurve(HandSocketComponent->VisualizationMesh->GetRefSkeleton().GetBoneName(i));
+ //AnimController.AddBoneTrack(HandSocketComponent->VisualizationMesh->GetRefSkeleton().GetBoneName(i));
+ }
+ }
+
+ if (BaseAnimation)
+ {
+ AnimationObject->RetargetSource = BaseAnimation->RetargetSource;
+ }
+ else
+ {
+ AnimationObject->RetargetSource = HandSocketComponent->VisualizationMesh ? HandSocketComponent->VisualizationMesh->GetSkeleton()->GetRetargetSourceForMesh(HandSocketComponent->VisualizationMesh) : NAME_None;
+ }
+
+ const IAnimationDataModel* DataModel = AnimController.GetModel();
+
+ /// SAVE POSE
+ if (BaseAnimation && DataModel && BaseDataModel)
+ {
+ for (int32 TrackIndex = 0; TrackIndex < /*DataModel->GetBoneAnimationTracks().Num()*/BaseDataModel->GetNumBoneTracks(); ++TrackIndex)
+ {
+ FName TrackName = TrackIndex < TrackNames.Num() ? TrackNames[TrackIndex] : NAME_None;
+ if (!BaseDataModel->IsValidBoneTrackName(TrackName))
+ {
+ continue;
+ }
+
+ FTransform FinalTrans = BaseDataModel->GetBoneTrackTransform(TrackName, 0);
+ //FTransform FinalTrans(Rot, Loc, Scale);
+
+ FQuat DeltaQuat = FQuat::Identity;
+ for (FBPVRHandPoseBonePair& HandPair : HandSocketComponent->CustomPoseDeltas)
+ {
+ if (HandPair.BoneName == TrackName)
+ {
+ DeltaQuat = HandPair.DeltaPose;
+ break;
+ }
+ }
+
+ FinalTrans.ConcatenateRotation(DeltaQuat);
+ FinalTrans.NormalizeRotation();
+
+ //FRawAnimSequenceTrack& RawNewTrack = DataModel->GetBoneTrackByIndex(TrackIndex).InternalTrackData;
+ AnimController.SetBoneTrackKeys(TrackName, { FinalTrans.GetTranslation() }, { FinalTrans.GetRotation() }, { FinalTrans.GetScale3D() });
+ }
+ }
+ else if(DataModel)
+ {
+ USkeletalMesh* SkeletalMesh = HandSocketComponent->VisualizationMesh;
+ FReferenceSkeleton RefSkeleton = SkeletalMesh->GetRefSkeleton();
+ USkeleton* AnimSkeleton = SkeletalMesh->GetSkeleton();
+
+ for (int32 TrackIndex = 0; TrackIndex < RefSkeleton.GetNum(); ++TrackIndex)
+ {
+
+ FName TrackName = RefSkeleton.GetBoneName(TrackIndex);
+ if (!DataModel->IsValidBoneTrackName(TrackName))
+ {
+ continue;
+ }
+
+ int32 BoneTreeIndex = RefSkeleton.FindBoneIndex(TrackName);
+
+ // verify if this bone exists in skeleton
+ //int32 BoneTreeIndex = DataModel->GetBoneTrackByIndex(TrackIndex).BoneTreeIndex;
+
+ if (BoneTreeIndex != INDEX_NONE)
+ {
+
+ int32 BoneIndex = BoneTreeIndex;//AnimSkeleton->GetMeshBoneIndexFromSkeletonBoneIndex(SkeletalMesh, BoneTreeIndex);
+ //int32 ParentIndex = SkeletalMesh->RefSkeleton.GetParentIndex(BoneIndex);
+ FTransform LocalTransform = LocalPoses[BoneIndex];
+
+
+ FName BoneName = AnimSkeleton->GetReferenceSkeleton().GetBoneName(BoneIndex);
+
+ FQuat DeltaQuat = FQuat::Identity;
+ for (FBPVRHandPoseBonePair& HandPair : HandSocketComponent->CustomPoseDeltas)
+ {
+ if (HandPair.BoneName == BoneName)
+ {
+ DeltaQuat = HandPair.DeltaPose;
+ }
+ }
+
+ LocalTransform.ConcatenateRotation(DeltaQuat);
+ LocalTransform.NormalizeRotation();
+
+ AnimController.SetBoneTrackKeys(BoneName, { LocalTransform.GetTranslation() }, { LocalTransform.GetRotation() }, { LocalTransform.GetScale3D() });
+ }
+ }
+ }
+
+ AnimController.NotifyPopulated();
+ }
+ /// END SAVE POSE
+ ///
+ ///
+ ///
+
+ // init notifies
+ AnimationObject->InitializeNotifyTrack();
+ //#TODO: 5.1, need to figure out what they replaced this with
+ //PRAGMA_DISABLE_DEPRECATION_WARNINGS
+ //AnimationObject->PostProcessSequence();
+ //PRAGMA_ENABLE_DEPRECATION_WARNINGS
+ AnimationObject->MarkPackageDirty();
+
+ //if (bAutoSaveAsset)
+ {
+ UPackage* const Package = AnimationObject->GetOutermost();
+ FString const PackageName = Package->GetName();
+ FString const PackageFileName = FPackageName::LongPackageNameToFilename(PackageName, FPackageName::GetAssetPackageExtension());
+
+ double StartTime = FPlatformTime::Seconds();
+
+ FSavePackageArgs PackageArguments;
+ PackageArguments.SaveFlags = RF_Standalone;
+ PackageArguments.SaveFlags = SAVE_NoError;
+ UPackage::SavePackage(Package, NULL, *PackageFileName, PackageArguments);
+ //UPackage::SavePackage(Package, NULL, RF_Standalone, *PackageFileName, GError, nullptr, false, true, SAVE_NoError);
+
+ double ElapsedTime = FPlatformTime::Seconds() - StartTime;
+ UE_LOG(LogAnimation, Log, TEXT("Animation Recorder saved %s in %0.2f seconds"), *PackageName, ElapsedTime);
+ }
+
+ FinalAnimation = AnimationObject;
+ return FinalAnimation;
+ }
+
+ return FinalAnimation;
+}
+TSharedRef< IDetailCustomization > FHandSocketComponentDetails::MakeInstance()
+{
+ return MakeShareable(new FHandSocketComponentDetails);
+}
+
+void FHandSocketComponentDetails::OnHandRelativeUpdated(IDetailLayoutBuilder* LayoutBuilder)
+{
+
+ if (!HandSocketComponent.IsValid())
+ {
+ return;
+ }
+
+ HandSocketComponent->Modify();
+ if (AActor* Owner = HandSocketComponent->GetOwner())
+ {
+ Owner->Modify();
+ }
+
+ TSharedPtr Visualizer = GUnrealEd->FindComponentVisualizer(HandSocketComponent->GetClass());
+ FHandSocketVisualizer* HandVisualizer = (FHandSocketVisualizer*)Visualizer.Get();
+
+ if (HandVisualizer)
+ {
+ if (UHandSocketComponent* RefHand = HandVisualizer->GetCurrentlyEditingComponent())
+ {
+ RefHand->HandRelativePlacement = HandSocketComponent->HandRelativePlacement;
+ }
+ }
+
+ FComponentVisualizer::NotifyPropertyModified(HandSocketComponent.Get(), FindFProperty(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandRelativePlacement)));
+}
+
+void FHandSocketComponentDetails::OnLeftDominantUpdated(IDetailLayoutBuilder* LayoutBuilder)
+{
+
+ if (!HandSocketComponent.IsValid())
+ {
+ return;
+ }
+
+ // Default to always flipping this
+ //if (HandSocketComponent->bFlipForLeftHand)
+ {
+ FTransform relTrans = HandSocketComponent->GetRelativeTransform();
+ FTransform HandPlacement = HandSocketComponent->GetHandRelativePlacement();
+
+ if (HandSocketComponent->bDecoupleMeshPlacement)
+ {
+ relTrans = FTransform::Identity;
+ }
+
+ FTransform ReturnTrans = (HandPlacement * relTrans);
+
+ HandSocketComponent->MirrorHandTransform(ReturnTrans, relTrans);
+
+ HandSocketComponent->Modify();
+ if (AActor* Owner = HandSocketComponent->GetOwner())
+ {
+ Owner->Modify();
+ }
+ ReturnTrans = ReturnTrans.GetRelativeTransform(relTrans);
+ HandSocketComponent->HandRelativePlacement = ReturnTrans;
+
+ TSharedPtr Visualizer = GUnrealEd->FindComponentVisualizer(HandSocketComponent->GetClass());
+ FHandSocketVisualizer* HandVisualizer = (FHandSocketVisualizer*)Visualizer.Get();
+
+ if (HandVisualizer)
+ {
+ if (UHandSocketComponent* RefHand = HandVisualizer->GetCurrentlyEditingComponent())
+ {
+ RefHand->HandRelativePlacement = HandSocketComponent->HandRelativePlacement;
+ //FComponentVisualizer::NotifyPropertyModified(RefHand, FindFProperty(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandRelativePlacement)));
+ }
+ }
+
+ FComponentVisualizer::NotifyPropertyModified(HandSocketComponent.Get(), FindFProperty(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandRelativePlacement)));
+ }
+}
+
+void FHandSocketComponentDetails::OnLockedStateUpdated(IDetailLayoutBuilder* LayoutBuilder)
+{
+
+ if (!HandSocketComponent.IsValid())
+ {
+ return;
+ }
+
+ if (HandSocketComponent->bDecoupleMeshPlacement)
+ {
+ //FTransform RelTrans = HandSocketComponent->GetRelativeTransform();
+ //FTransform WorldTrans = HandSocketComponent->GetComponentTransform();
+ //if (USceneComponent* ParentComp = HandSocketComponent->GetAttachParent())
+ {
+
+ HandSocketComponent->Modify();
+ if (AActor* Owner = HandSocketComponent->GetOwner())
+ {
+ Owner->Modify();
+ }
+
+ HandSocketComponent->HandRelativePlacement = HandSocketComponent->HandRelativePlacement * HandSocketComponent->GetRelativeTransform();// HandSocketComponent->GetComponentTransform();
+ HandSocketComponent->bDecoupled = true;
+
+ TSharedPtr Visualizer = GUnrealEd->FindComponentVisualizer(HandSocketComponent->GetClass());
+ FHandSocketVisualizer* HandVisualizer = (FHandSocketVisualizer*)Visualizer.Get();
+
+ if (HandVisualizer)
+ {
+ if (UHandSocketComponent* RefHand = HandVisualizer->GetCurrentlyEditingComponent())
+ {
+ RefHand->HandRelativePlacement = HandSocketComponent->HandRelativePlacement;
+ RefHand->bDecoupled = true;
+ //FComponentVisualizer::NotifyPropertyModified(RefHand, FindFProperty(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandRelativePlacement)));
+ }
+ }
+ }
+ }
+ else
+ {
+ //if (USceneComponent* ParentComp = HandSocketComponent->GetAttachParent())
+ {
+ HandSocketComponent->Modify();
+ if (AActor* Owner = HandSocketComponent->GetOwner())
+ {
+ Owner->Modify();
+ }
+ HandSocketComponent->HandRelativePlacement = HandSocketComponent->HandRelativePlacement.GetRelativeTransform(HandSocketComponent->GetRelativeTransform());
+ HandSocketComponent->bDecoupled = false;
+
+ TSharedPtr Visualizer = GUnrealEd->FindComponentVisualizer(HandSocketComponent->GetClass());
+ FHandSocketVisualizer* HandVisualizer = (FHandSocketVisualizer*)Visualizer.Get();
+
+ if (HandVisualizer)
+ {
+ if (UHandSocketComponent* RefHand = HandVisualizer->GetCurrentlyEditingComponent())
+ {
+ RefHand->HandRelativePlacement = HandSocketComponent->HandRelativePlacement;
+ RefHand->bDecoupled = false;
+ //FComponentVisualizer::NotifyPropertyModified(RefHand, FindFProperty(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandRelativePlacement)));
+ }
+ }
+ }
+ }
+
+ TArray PropertiesToModify;
+ PropertiesToModify.Add(FindFProperty(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandRelativePlacement)));
+ PropertiesToModify.Add(FindFProperty(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, bDecoupled)));
+ FComponentVisualizer::NotifyPropertiesModified(HandSocketComponent.Get(), PropertiesToModify);
+}
+
+void FHandSocketComponentDetails::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
+{
+ // Hide the SplineCurves property
+ //TSharedPtr HandPlacementProperty = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandRelativePlacement));
+ //HandPlacementProperty->MarkHiddenByCustomization();
+
+
+ TArray> ObjectsBeingCustomized;
+ DetailBuilder.GetObjectsBeingCustomized(ObjectsBeingCustomized);
+
+ if (ObjectsBeingCustomized.Num() == 1)
+ {
+ UHandSocketComponent* CurrentHandSocket = Cast(ObjectsBeingCustomized[0]);
+ if (CurrentHandSocket != NULL)
+ {
+ if (HandSocketComponent != CurrentHandSocket)
+ {
+ TSharedPtr Visualizer = GUnrealEd->FindComponentVisualizer(CurrentHandSocket->GetClass());
+ FHandSocketVisualizer* HandVisualizer = (FHandSocketVisualizer*)Visualizer.Get();
+
+ if (HandVisualizer)
+ {
+ HandVisualizer->CurrentlySelectedBoneIdx = INDEX_NONE;
+ HandVisualizer->CurrentlySelectedBone = NAME_None;
+ HandVisualizer->HandPropertyPath = FComponentPropertyPath();
+ //HandVisualizer->OldHandSocketComp = CurrentHandSocket;
+ }
+
+ HandSocketComponent = CurrentHandSocket;
+ }
+ }
+ }
+
+ /*const TArray< TWeakObjectPtr