diff --git a/Config/BroadcastDeviceProviderRegistry.json b/Config/BroadcastDeviceProviderRegistry.json
new file mode 100644
index 0000000..a5f7f98
Binary files /dev/null and b/Config/BroadcastDeviceProviderRegistry.json differ
diff --git a/Config/DefaultEngine.ini b/Config/DefaultEngine.ini
new file mode 100644
index 0000000..d6d54b7
--- /dev/null
+++ b/Config/DefaultEngine.ini
@@ -0,0 +1,154 @@
+[/Script/HardwareTargeting.HardwareTargetingSettings]
+; quality settings
+TargetedHardwareClass=Desktop
+AppliedTargetedHardwareClass=Desktop
+DefaultGraphicsPerformance=Maximum
+AppliedDefaultGraphicsPerformance=Maximum
+
+
+[/Script/Engine.RendererSettings]
+; exposure control and tone mapping settings
+r.Tonemapper.Sharpen=2
+r.DefaultFeature.AutoExposure.ExtendDefaultLuminanceRange=True
+r.DefaultFeature.AutoExposure.ExtendDefaultLuminanceRange=True
+r.DefaultFeature.AutoExposure.ExtendDefaultLuminanceRange=True
+r.DefaultFeature.Bloom=False
+r.DefaultFeature.AutoExposure=False
+r.DefaultFeature.LocalExposure.HighlightContrastScale=0.8
+r.DefaultFeature.LocalExposure.HighlightContrastScale=0.8
+r.DefaultFeature.LocalExposure.HighlightContrastScale=1.0
+r.DefaultFeature.LocalExposure.ShadowContrastScale=0.8
+r.DefaultFeature.LocalExposure.ShadowContrastScale=0.8
+r.DefaultFeature.LocalExposure.ShadowContrastScale=1.0
+r.DefaultFeature.MotionBlur=False
+r.SceneRenderTargetResizeMethod=2
+
+; dynamic gi settings
+r.LightPropagationVolume=0
+
+; quality settings
+r.ReflectionCaptureResolution=2048
+r.AllowStaticLighting=True
+r.HighResScreenshotDelay=8
+r.DefaultBackBufferPixelFormat=4
+r.AllowGlobalClipPlane=False
+r.GBufferFormat=3
+
+; shader settings
+r.PostProcessing.PropagateAlpha=1
+r.SupportSkyAtmosphereAffectsHeightFog=True
+r.DefaultFeature.LensFlare=True
+r.ClearCoatNormal=False
+r.NormalMapsForStaticLighting=False
+
+
+
+r.GenerateMeshDistanceFields=True
+
+r.DynamicGlobalIlluminationMethod=1
+
+r.ReflectionMethod=1
+
+r.SkinCache.CompileShaders=True
+r.SkinCache.CompileShaders=True
+
+r.RayTracing=True
+
+r.Shadow.Virtual.Enable=1
+r.Deferred.SupportPrimitiveAlphaHoldout=True
+
+r.RayTracing.RayTracingProxies.ProjectEnabled=True
+
+[/Script/EngineSettings.GameMapsSettings]
+;defines what level to be loaded by default in the editor and at playback time
+EditorStartupMap=/Game/MotionDesign/Maps/3D_MotionDesign.3D_MotionDesign
+EditorStartupMap=/Game/MotionDesign/Maps/3D_MotionDesign.3D_MotionDesign
+
+[/Script/UnrealEd.UnrealEdEngine]
+; Remove Engine Template maps
+-TemplateMapInfos=(ThumbnailTexture=Texture2D'/Engine/Maps/Templates/Thumbnails/VR-Basic.VR-Basic',Map="/Engine/Maps/Templates/VR-Basic")
+
+
+; Allows for Hardware Accelerated Video Decoding
+
+[/Script/WmfMediaFactory.WmfMediaSettings]
+AllowNonStandardCodecs=True
+HardwareAcceleratedVideoDecoding=True
+LowLatency=False
+NativeAudioOut=False
+
+; Adds Virtual Scouting Widget for VR Scouting
+
+[/Script/VPUtilitiesEditor.VPUtilitiesEditorSettings]
+VirtualScoutingUI=/VirtualProductionUtilities/Editor/VirtualScoutingWidget.VirtualScoutingWidget_C
+FlightSpeed=0.500000
+GripNavSpeed=0.250000
+bUseMetric=False
+bUseTransformGizmo=False
+bUseGripInertiaDamping=True
+InertiaDamping=0.950000
+bIsHelperSystemEnabled=True
+ScoutingSubsystemEdititorUtilityActorClassPath=/VirtualProductionUtilities/VirtualProductionHelpers.VirtualProductionHelpers_C
+
+
+; Enables WebControl API
+WebControl.EnableServerOnStartup=1
+
+;Adds Remote Session for Vcam
+
+[RemoteSession]
++Channels=(Name=FRemoteSessionFrameBufferChannel,Mode=Write)
++Channels=(Name=FRemoteSessionInputChannel,Mode=Read)
++Channels=(Name=FRemoteSessionXRTrackingChannel,Mode=Read)
+
+; Setup for Multiuser
+
+[/Script/Concert.ConcertClientConfig]
+bIsHeadless=False
+bInstallEditorToolbarButton=True
+bAutoConnect=False
+DefaultServerURL=
+DefaultSessionName=
+DefaultSessionToRestore=
+DefaultSaveSessionAs=
+ClientSettings=(DisplayName="",AvatarColor=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),DesktopAvatarActorClass=/ConcertSyncClient/DesktopPresence.DesktopPresence_C,VRAvatarActorClass=/ConcertSyncClient/VRPresence.VRPresence_C,DiscoveryTimeoutSeconds=5,SessionTickFrequencySeconds=1,LatencyCompensationMs=0.000000,Tags=)
+EndpointSettings=(bEnableLogging=False,PurgeProcessedMessageDelaySeconds=30,RemoteEndpointTimeoutSeconds=60)
+
+; Setup for Ndisplay
+
+[/Script/DisplayClusterEditor.DisplayClusterEditorSettings]
+bEnabled=True
+
+[/Script/WindowsTargetPlatform.WindowsTargetSettings]
+DefaultGraphicsRHI=DefaultGraphicsRHI_DX12
+DefaultGraphicsRHI=DefaultGraphicsRHI_DX12
+DefaultGraphicsRHI=DefaultGraphicsRHI_DX12
+-D3D12TargetedShaderFormats=PCD3D_SM5
++D3D12TargetedShaderFormats=PCD3D_SM6
+-D3D11TargetedShaderFormats=PCD3D_SM5
++D3D11TargetedShaderFormats=PCD3D_SM5
+
+[/Script/LinuxTargetPlatform.LinuxTargetSettings]
+-TargetedRHIs=SF_VULKAN_SM5
++TargetedRHIs=SF_VULKAN_SM6
+
+[/Script/Engine.Engine]
++ActiveGameNameRedirects=(OldGameName="TP_MotionDesignBP",NewGameName="/Script/Yotta2025")
++ActiveGameNameRedirects=(OldGameName="/Script/TP_MotionDesignBP",NewGameName="/Script/Yotta2025")
++ActiveGameNameRedirects=(OldGameName="TP_ME_BlankBP",NewGameName="/Script/TP_MotionDesign")
++ActiveGameNameRedirects=(OldGameName="/Script/TP_ME_BlankBP",NewGameName="/Script/TP_MotionDesign")
+
+[/Script/AndroidFileServerEditor.AndroidFileServerRuntimeSettings]
+bEnablePlugin=True
+bAllowNetworkConnection=True
+SecurityToken=CC7D8F16428D1B3404A9ECB1FC43C9D3
+bIncludeInShipping=False
+bAllowExternalStartInShipping=False
+bCompileAFSProject=False
+bUseCompression=False
+bLogFiles=False
+bReportStats=False
+ConnectionType=USBOnly
+bUseManualIPAddress=False
+ManualIPAddress=
+
diff --git a/Config/DefaultInput.ini b/Config/DefaultInput.ini
new file mode 100644
index 0000000..f6cd2c9
--- /dev/null
+++ b/Config/DefaultInput.ini
@@ -0,0 +1,89 @@
+
+
+[/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="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="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="Mouse2D",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="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="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="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_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))
++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))
+bAltEnterTogglesFullscreen=True
+bF11TogglesFullscreen=True
+bUseMouseForTouch=False
+bEnableMouseSmoothing=True
+bEnableFOVScaling=True
+bCaptureMouseOnLaunch=True
+bEnableLegacyInputScales=True
+bEnableMotionControls=True
+bFilterInputByPlatformUser=False
+bEnableInputDeviceSubsystem=True
+bShouldFlushPressedKeysOnViewportFocusLost=True
+bEnableDynamicComponentInputBinding=True
+bAlwaysShowTouchInterface=False
+bShowConsoleOnFourFingerTap=True
+bEnableGestureRecognizer=False
+bUseAutocorrect=False
+DefaultViewportMouseCaptureMode=CapturePermanently_IncludingInitialMouseDown
+DefaultViewportMouseLockMode=LockOnCapture
+FOVScale=0.011110
+DoubleClickTime=0.200000
+DefaultPlayerInputClass=/Script/EnhancedInput.EnhancedPlayerInput
+DefaultInputComponentClass=/Script/EnhancedInput.EnhancedInputComponent
+DefaultTouchInterface=/Engine/MobileResources/HUD/DefaultVirtualJoysticks.DefaultVirtualJoysticks
+-ConsoleKeys=Tilde
++ConsoleKeys=Tilde
++ConsoleKeys=#
+
diff --git a/Config/EaseCurves/Ease In Out.json b/Config/EaseCurves/Ease In Out.json
new file mode 100644
index 0000000..74a90f6
--- /dev/null
+++ b/Config/EaseCurves/Ease In Out.json
@@ -0,0 +1,10 @@
+{
+ "Sine": "0.37, 0.0, 0.63, 1.0",
+ "Cubic": "0.65, 0.0, 0.35, 1.0",
+ "Quintic": "0.83, 0.0, 0.17, 1.0",
+ "Circular": "0.85, 0.0, 0.15, 1.0",
+ "Quadratic": "0.45, 0.0, 0.55, 1.0",
+ "Quartic": "0.76, 0.0, 0.24, 1.0",
+ "Exponential": "0.87, 0.0, 0.13, 1.0",
+ "Back": "0.68, -0.6, 0.32, 1.6"
+}
\ No newline at end of file
diff --git a/Config/EaseCurves/Ease In.json b/Config/EaseCurves/Ease In.json
new file mode 100644
index 0000000..047d7c5
--- /dev/null
+++ b/Config/EaseCurves/Ease In.json
@@ -0,0 +1,10 @@
+{
+ "Sine": "0.12, 0.0, 0.39, 0.0",
+ "Cubic": "0.32, 0.0, 0.67, 0.0",
+ "Quintic": "0.64, 0.0, 0.78, 0.0",
+ "Circular": "0.55, 0.0, 1.0, 0.45",
+ "Quadratic": "0.11, 0.0, 0.5, 0.0",
+ "Quartic": "0.5, 0.0, 0.75, 0.0",
+ "Exponential": "0.7, 0.0, 0.84, 0.0",
+ "Back": "0.36, 0.0, 0.66, -0.56"
+}
\ No newline at end of file
diff --git a/Config/EaseCurves/Ease Out.json b/Config/EaseCurves/Ease Out.json
new file mode 100644
index 0000000..af8bb7a
--- /dev/null
+++ b/Config/EaseCurves/Ease Out.json
@@ -0,0 +1,10 @@
+{
+ "Sine": "0.61, 1.0, 0.88, 1.0",
+ "Cubic": "0.33, 1.0, 0.68, 1.0",
+ "Quintic": "0.22, 1.0, 0.36, 1.0",
+ "Circular": "0.0, 0.55, 0.45, 1.0",
+ "Quadratic": "0.5, 1.0, 0.89, 1.0",
+ "Quartic": "0.25, 1.0, 0.5, 1.0",
+ "Exponential": "0.16, 1.0, 0.3, 1.0",
+ "Back": "0.34, 1.56, 0.64, 1.0"
+}
\ No newline at end of file
diff --git a/Content/SystemFonts/FontFaces/IntegralCF_Bold.uasset b/Content/SystemFonts/FontFaces/IntegralCF_Bold.uasset
new file mode 100644
index 0000000..5da3425
Binary files /dev/null and b/Content/SystemFonts/FontFaces/IntegralCF_Bold.uasset differ
diff --git a/Content/SystemFonts/FontFaces/IntegralCF_BoldOblique.uasset b/Content/SystemFonts/FontFaces/IntegralCF_BoldOblique.uasset
new file mode 100644
index 0000000..c03306d
Binary files /dev/null and b/Content/SystemFonts/FontFaces/IntegralCF_BoldOblique.uasset differ
diff --git a/Content/SystemFonts/FontFaces/IntegralCF_DemiBold.uasset b/Content/SystemFonts/FontFaces/IntegralCF_DemiBold.uasset
new file mode 100644
index 0000000..5b4770a
Binary files /dev/null and b/Content/SystemFonts/FontFaces/IntegralCF_DemiBold.uasset differ
diff --git a/Content/SystemFonts/FontFaces/IntegralCF_DemiBoldOblique.uasset b/Content/SystemFonts/FontFaces/IntegralCF_DemiBoldOblique.uasset
new file mode 100644
index 0000000..f8c2784
Binary files /dev/null and b/Content/SystemFonts/FontFaces/IntegralCF_DemiBoldOblique.uasset differ
diff --git a/Content/SystemFonts/FontFaces/IntegralCF_ExtraBold.uasset b/Content/SystemFonts/FontFaces/IntegralCF_ExtraBold.uasset
new file mode 100644
index 0000000..43152b2
Binary files /dev/null and b/Content/SystemFonts/FontFaces/IntegralCF_ExtraBold.uasset differ
diff --git a/Content/SystemFonts/FontFaces/IntegralCF_ExtraBoldOblique.uasset b/Content/SystemFonts/FontFaces/IntegralCF_ExtraBoldOblique.uasset
new file mode 100644
index 0000000..af31745
Binary files /dev/null and b/Content/SystemFonts/FontFaces/IntegralCF_ExtraBoldOblique.uasset differ
diff --git a/Content/SystemFonts/FontFaces/IntegralCF_Heavy.uasset b/Content/SystemFonts/FontFaces/IntegralCF_Heavy.uasset
new file mode 100644
index 0000000..6ccfc9d
Binary files /dev/null and b/Content/SystemFonts/FontFaces/IntegralCF_Heavy.uasset differ
diff --git a/Content/SystemFonts/FontFaces/IntegralCF_HeavyOblique.uasset b/Content/SystemFonts/FontFaces/IntegralCF_HeavyOblique.uasset
new file mode 100644
index 0000000..3a655f5
Binary files /dev/null and b/Content/SystemFonts/FontFaces/IntegralCF_HeavyOblique.uasset differ
diff --git a/Content/SystemFonts/FontFaces/IntegralCF_Medium.uasset b/Content/SystemFonts/FontFaces/IntegralCF_Medium.uasset
new file mode 100644
index 0000000..227b732
Binary files /dev/null and b/Content/SystemFonts/FontFaces/IntegralCF_Medium.uasset differ
diff --git a/Content/SystemFonts/FontFaces/IntegralCF_MediumOblique.uasset b/Content/SystemFonts/FontFaces/IntegralCF_MediumOblique.uasset
new file mode 100644
index 0000000..56b07b2
Binary files /dev/null and b/Content/SystemFonts/FontFaces/IntegralCF_MediumOblique.uasset differ
diff --git a/Content/SystemFonts/FontFaces/IntegralCF_Regular.uasset b/Content/SystemFonts/FontFaces/IntegralCF_Regular.uasset
new file mode 100644
index 0000000..3029cca
Binary files /dev/null and b/Content/SystemFonts/FontFaces/IntegralCF_Regular.uasset differ
diff --git a/Content/SystemFonts/FontFaces/IntegralCF_RegularOblique.uasset b/Content/SystemFonts/FontFaces/IntegralCF_RegularOblique.uasset
new file mode 100644
index 0000000..96ae8fa
Binary files /dev/null and b/Content/SystemFonts/FontFaces/IntegralCF_RegularOblique.uasset differ
diff --git a/Content/SystemFonts/Fonts/IntegralCF.uasset b/Content/SystemFonts/Fonts/IntegralCF.uasset
new file mode 100644
index 0000000..642ac25
Binary files /dev/null and b/Content/SystemFonts/Fonts/IntegralCF.uasset differ
diff --git a/Content/Yotta2025/Blueprints/BP_ConfigDepart.uasset b/Content/Yotta2025/Blueprints/BP_ConfigDepart.uasset
new file mode 100644
index 0000000..e108be9
Binary files /dev/null and b/Content/Yotta2025/Blueprints/BP_ConfigDepart.uasset differ
diff --git a/Content/Yotta2025/Blueprints/BP_LignePoursuite.uasset b/Content/Yotta2025/Blueprints/BP_LignePoursuite.uasset
new file mode 100644
index 0000000..eedc8b3
Binary files /dev/null and b/Content/Yotta2025/Blueprints/BP_LignePoursuite.uasset differ
diff --git a/Content/Yotta2025/Blueprints/BP_LignePoursuiteProchain.uasset b/Content/Yotta2025/Blueprints/BP_LignePoursuiteProchain.uasset
new file mode 100644
index 0000000..ef79614
Binary files /dev/null and b/Content/Yotta2025/Blueprints/BP_LignePoursuiteProchain.uasset differ
diff --git a/Content/Yotta2025/Blueprints/DA_TestPoursuite.uasset b/Content/Yotta2025/Blueprints/DA_TestPoursuite.uasset
new file mode 100644
index 0000000..0b3cf33
Binary files /dev/null and b/Content/Yotta2025/Blueprints/DA_TestPoursuite.uasset differ
diff --git a/Content/Yotta2025/Blueprints/PDA_TestPoursuite.uasset b/Content/Yotta2025/Blueprints/PDA_TestPoursuite.uasset
new file mode 100644
index 0000000..bba8765
Binary files /dev/null and b/Content/Yotta2025/Blueprints/PDA_TestPoursuite.uasset differ
diff --git a/Content/Yotta2025/Blueprints/S_Poursuite.uasset b/Content/Yotta2025/Blueprints/S_Poursuite.uasset
new file mode 100644
index 0000000..616ef7a
Binary files /dev/null and b/Content/Yotta2025/Blueprints/S_Poursuite.uasset differ
diff --git a/Content/Yotta2025/Displays/Depart/Level/L_poursuite.umap b/Content/Yotta2025/Displays/Depart/Level/L_poursuite.umap
new file mode 100644
index 0000000..dfe9475
Binary files /dev/null and b/Content/Yotta2025/Displays/Depart/Level/L_poursuite.umap differ
diff --git a/Content/Yotta2025/Displays/Real/Levels/L_Tableau_Contest.umap b/Content/Yotta2025/Displays/Real/Levels/L_Tableau_Contest.umap
new file mode 100644
index 0000000..aed7b75
Binary files /dev/null and b/Content/Yotta2025/Displays/Real/Levels/L_Tableau_Contest.umap differ
diff --git a/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-JoueursMysteresRelais.png b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-JoueursMysteresRelais.png
new file mode 100644
index 0000000..05b28ff
Binary files /dev/null and b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-JoueursMysteresRelais.png differ
diff --git a/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-JoueursMysteresRelais.uasset b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-JoueursMysteresRelais.uasset
new file mode 100644
index 0000000..e8e4809
Binary files /dev/null and b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-JoueursMysteresRelais.uasset differ
diff --git a/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-JoueursMysteresSolo.png b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-JoueursMysteresSolo.png
new file mode 100644
index 0000000..c0cf5bc
Binary files /dev/null and b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-JoueursMysteresSolo.png differ
diff --git a/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-JoueursMysteresSolo.uasset b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-JoueursMysteresSolo.uasset
new file mode 100644
index 0000000..46351b7
Binary files /dev/null and b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-JoueursMysteresSolo.uasset differ
diff --git a/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chevron.png b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chevron.png
new file mode 100644
index 0000000..04a8663
Binary files /dev/null and b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chevron.png differ
diff --git a/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chevron.uasset b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chevron.uasset
new file mode 100644
index 0000000..a1be3bf
Binary files /dev/null and b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chevron.uasset differ
diff --git a/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chrono-10sec-lettre.png b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chrono-10sec-lettre.png
new file mode 100644
index 0000000..d6271c9
Binary files /dev/null and b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chrono-10sec-lettre.png differ
diff --git a/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chrono-10sec-lettre.uasset b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chrono-10sec-lettre.uasset
new file mode 100644
index 0000000..89360c4
Binary files /dev/null and b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chrono-10sec-lettre.uasset differ
diff --git a/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chrono-10sec.png b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chrono-10sec.png
new file mode 100644
index 0000000..0b6472f
Binary files /dev/null and b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chrono-10sec.png differ
diff --git a/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chrono-10sec.uasset b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chrono-10sec.uasset
new file mode 100644
index 0000000..37b1d5a
Binary files /dev/null and b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chrono-10sec.uasset differ
diff --git a/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chrono-F.png b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chrono-F.png
new file mode 100644
index 0000000..51ce358
Binary files /dev/null and b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chrono-F.png differ
diff --git a/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chrono-F.uasset b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chrono-F.uasset
new file mode 100644
index 0000000..d364eb1
Binary files /dev/null and b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chrono-F.uasset differ
diff --git a/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chrono-H.png b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chrono-H.png
new file mode 100644
index 0000000..6576c47
Binary files /dev/null and b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chrono-H.png differ
diff --git a/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chrono-H.uasset b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chrono-H.uasset
new file mode 100644
index 0000000..e5f4ad8
Binary files /dev/null and b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chrono-H.uasset differ
diff --git a/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chrono-lettre.png b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chrono-lettre.png
new file mode 100644
index 0000000..e695993
Binary files /dev/null and b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chrono-lettre.png differ
diff --git a/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chrono-lettre.uasset b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chrono-lettre.uasset
new file mode 100644
index 0000000..b57a84d
Binary files /dev/null and b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chrono-lettre.uasset differ
diff --git a/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chrono-timeover-lettre.png b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chrono-timeover-lettre.png
new file mode 100644
index 0000000..5a557ec
Binary files /dev/null and b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chrono-timeover-lettre.png differ
diff --git a/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chrono-timeover-lettre.uasset b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chrono-timeover-lettre.uasset
new file mode 100644
index 0000000..2924fec
Binary files /dev/null and b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chrono-timeover-lettre.uasset differ
diff --git a/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chrono-timeover.png b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chrono-timeover.png
new file mode 100644
index 0000000..f4f73ea
Binary files /dev/null and b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chrono-timeover.png differ
diff --git a/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chrono-timeover.uasset b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chrono-timeover.uasset
new file mode 100644
index 0000000..5be86ae
Binary files /dev/null and b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chrono-timeover.uasset differ
diff --git a/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chrono.png b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chrono.png
new file mode 100644
index 0000000..8e75918
Binary files /dev/null and b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chrono.png differ
diff --git a/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chrono.uasset b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chrono.uasset
new file mode 100644
index 0000000..be3bd8c
Binary files /dev/null and b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-chrono.uasset differ
diff --git a/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-diagonale-photo.png b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-diagonale-photo.png
new file mode 100644
index 0000000..f0b997a
Binary files /dev/null and b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-diagonale-photo.png differ
diff --git a/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-diagonale-photo.uasset b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-diagonale-photo.uasset
new file mode 100644
index 0000000..ba36f55
Binary files /dev/null and b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-diagonale-photo.uasset differ
diff --git a/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-fond-animev2-H.264.mp4 b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-fond-animev2-H.264.mp4
new file mode 100644
index 0000000..2ec88db
Binary files /dev/null and b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-fond-animev2-H.264.mp4 differ
diff --git a/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-fond-attente.mp4 b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-fond-attente.mp4
new file mode 100644
index 0000000..c0488cb
Binary files /dev/null and b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-fond-attente.mp4 differ
diff --git a/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-fond-winner.mp4 b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-fond-winner.mp4
new file mode 100644
index 0000000..af7df79
Binary files /dev/null and b/Content/Yotta2025/Medias/24-ARCHE-MEDIAS/24-ARCHE-fond-winner.mp4 differ
diff --git a/Content/Yotta2025/Medias/24-DEPART-MEDIAS/24-DEPART-fond-anime-H.264.mp4 b/Content/Yotta2025/Medias/24-DEPART-MEDIAS/24-DEPART-fond-anime-H.264.mp4
new file mode 100644
index 0000000..80c5b9f
Binary files /dev/null and b/Content/Yotta2025/Medias/24-DEPART-MEDIAS/24-DEPART-fond-anime-H.264.mp4 differ
diff --git a/Content/Yotta2025/Medias/24-DEPART-MEDIAS/24-DEPART-fond-attente.mp4 b/Content/Yotta2025/Medias/24-DEPART-MEDIAS/24-DEPART-fond-attente.mp4
new file mode 100644
index 0000000..6790501
Binary files /dev/null and b/Content/Yotta2025/Medias/24-DEPART-MEDIAS/24-DEPART-fond-attente.mp4 differ
diff --git a/Content/Yotta2025/Medias/24-DEPART-MEDIAS/DEPART-GO-annonce-diff.png b/Content/Yotta2025/Medias/24-DEPART-MEDIAS/DEPART-GO-annonce-diff.png
new file mode 100644
index 0000000..3cb34ea
Binary files /dev/null and b/Content/Yotta2025/Medias/24-DEPART-MEDIAS/DEPART-GO-annonce-diff.png differ
diff --git a/Content/Yotta2025/Medias/24-DEPART-MEDIAS/DEPART-GO-annonce-diff.uasset b/Content/Yotta2025/Medias/24-DEPART-MEDIAS/DEPART-GO-annonce-diff.uasset
new file mode 100644
index 0000000..f61c5d9
Binary files /dev/null and b/Content/Yotta2025/Medias/24-DEPART-MEDIAS/DEPART-GO-annonce-diff.uasset differ
diff --git a/Content/Yotta2025/Medias/24-DEPART-MEDIAS/DEPART-attente.png b/Content/Yotta2025/Medias/24-DEPART-MEDIAS/DEPART-attente.png
new file mode 100644
index 0000000..52eacba
Binary files /dev/null and b/Content/Yotta2025/Medias/24-DEPART-MEDIAS/DEPART-attente.png differ
diff --git a/Content/Yotta2025/Medias/24-DEPART-MEDIAS/DEPART-attente.uasset b/Content/Yotta2025/Medias/24-DEPART-MEDIAS/DEPART-attente.uasset
new file mode 100644
index 0000000..fc7e49e
Binary files /dev/null and b/Content/Yotta2025/Medias/24-DEPART-MEDIAS/DEPART-attente.uasset differ
diff --git a/Content/Yotta2025/Medias/24-DEPART-MEDIAS/DEPART-chrono-annonce-simple.png b/Content/Yotta2025/Medias/24-DEPART-MEDIAS/DEPART-chrono-annonce-simple.png
new file mode 100644
index 0000000..1c62957
Binary files /dev/null and b/Content/Yotta2025/Medias/24-DEPART-MEDIAS/DEPART-chrono-annonce-simple.png differ
diff --git a/Content/Yotta2025/Medias/24-DEPART-MEDIAS/DEPART-chrono-annonce-simple.uasset b/Content/Yotta2025/Medias/24-DEPART-MEDIAS/DEPART-chrono-annonce-simple.uasset
new file mode 100644
index 0000000..e882eec
Binary files /dev/null and b/Content/Yotta2025/Medias/24-DEPART-MEDIAS/DEPART-chrono-annonce-simple.uasset differ
diff --git a/Content/Yotta2025/Medias/24-DEPART-MEDIAS/DEPART-rectangle-noir-annonce-simple.png b/Content/Yotta2025/Medias/24-DEPART-MEDIAS/DEPART-rectangle-noir-annonce-simple.png
new file mode 100644
index 0000000..6173631
Binary files /dev/null and b/Content/Yotta2025/Medias/24-DEPART-MEDIAS/DEPART-rectangle-noir-annonce-simple.png differ
diff --git a/Content/Yotta2025/Medias/24-DEPART-MEDIAS/DEPART-rectangle-noir-annonce-simple.uasset b/Content/Yotta2025/Medias/24-DEPART-MEDIAS/DEPART-rectangle-noir-annonce-simple.uasset
new file mode 100644
index 0000000..7af621b
Binary files /dev/null and b/Content/Yotta2025/Medias/24-DEPART-MEDIAS/DEPART-rectangle-noir-annonce-simple.uasset differ
diff --git a/Content/Yotta2025/Medias/24-DEPART-MEDIAS/DEPART-rectangle-noir-nom-annonce-diff.png b/Content/Yotta2025/Medias/24-DEPART-MEDIAS/DEPART-rectangle-noir-nom-annonce-diff.png
new file mode 100644
index 0000000..dfe6e96
Binary files /dev/null and b/Content/Yotta2025/Medias/24-DEPART-MEDIAS/DEPART-rectangle-noir-nom-annonce-diff.png differ
diff --git a/Content/Yotta2025/Medias/24-DEPART-MEDIAS/DEPART-rectangle-noir-nom-annonce-diff.uasset b/Content/Yotta2025/Medias/24-DEPART-MEDIAS/DEPART-rectangle-noir-nom-annonce-diff.uasset
new file mode 100644
index 0000000..8c10fd4
Binary files /dev/null and b/Content/Yotta2025/Medias/24-DEPART-MEDIAS/DEPART-rectangle-noir-nom-annonce-diff.uasset differ
diff --git a/Content/Yotta2025/Medias/24-DEPART-MEDIAS/DEPART-rectangle-noir-nom.uasset b/Content/Yotta2025/Medias/24-DEPART-MEDIAS/DEPART-rectangle-noir-nom.uasset
new file mode 100644
index 0000000..b545606
Binary files /dev/null and b/Content/Yotta2025/Medias/24-DEPART-MEDIAS/DEPART-rectangle-noir-nom.uasset differ
diff --git a/Content/Yotta2025/Medias/24-DEPART-MEDIAS/DEPART-rectangle-rouge-nom-annonce-diff.png b/Content/Yotta2025/Medias/24-DEPART-MEDIAS/DEPART-rectangle-rouge-nom-annonce-diff.png
new file mode 100644
index 0000000..2149e1c
Binary files /dev/null and b/Content/Yotta2025/Medias/24-DEPART-MEDIAS/DEPART-rectangle-rouge-nom-annonce-diff.png differ
diff --git a/Content/Yotta2025/Medias/24-DEPART-MEDIAS/DEPART-rectangle-rouge-nom-annonce-diff.uasset b/Content/Yotta2025/Medias/24-DEPART-MEDIAS/DEPART-rectangle-rouge-nom-annonce-diff.uasset
new file mode 100644
index 0000000..a217f4b
Binary files /dev/null and b/Content/Yotta2025/Medias/24-DEPART-MEDIAS/DEPART-rectangle-rouge-nom-annonce-diff.uasset differ
diff --git a/Content/Yotta2025/Medias/24-PICTO/PICTO-2km-blanc.png b/Content/Yotta2025/Medias/24-PICTO/PICTO-2km-blanc.png
new file mode 100644
index 0000000..9de7980
Binary files /dev/null and b/Content/Yotta2025/Medias/24-PICTO/PICTO-2km-blanc.png differ
diff --git a/Content/Yotta2025/Medias/24-PICTO/PICTO-2km-blanc.uasset b/Content/Yotta2025/Medias/24-PICTO/PICTO-2km-blanc.uasset
new file mode 100644
index 0000000..0c8d027
Binary files /dev/null and b/Content/Yotta2025/Medias/24-PICTO/PICTO-2km-blanc.uasset differ
diff --git a/Content/Yotta2025/Medias/24-PICTO/PICTO-2km-noir.png b/Content/Yotta2025/Medias/24-PICTO/PICTO-2km-noir.png
new file mode 100644
index 0000000..544c251
Binary files /dev/null and b/Content/Yotta2025/Medias/24-PICTO/PICTO-2km-noir.png differ
diff --git a/Content/Yotta2025/Medias/24-PICTO/PICTO-2km-noir.uasset b/Content/Yotta2025/Medias/24-PICTO/PICTO-2km-noir.uasset
new file mode 100644
index 0000000..fabbfb2
Binary files /dev/null and b/Content/Yotta2025/Medias/24-PICTO/PICTO-2km-noir.uasset differ
diff --git a/Content/Yotta2025/Medias/24-PICTO/PICTO-4km-blanc.png b/Content/Yotta2025/Medias/24-PICTO/PICTO-4km-blanc.png
new file mode 100644
index 0000000..3748049
Binary files /dev/null and b/Content/Yotta2025/Medias/24-PICTO/PICTO-4km-blanc.png differ
diff --git a/Content/Yotta2025/Medias/24-PICTO/PICTO-4km-blanc.uasset b/Content/Yotta2025/Medias/24-PICTO/PICTO-4km-blanc.uasset
new file mode 100644
index 0000000..c56f886
Binary files /dev/null and b/Content/Yotta2025/Medias/24-PICTO/PICTO-4km-blanc.uasset differ
diff --git a/Content/Yotta2025/Medias/24-PICTO/PICTO-4km-noir.png b/Content/Yotta2025/Medias/24-PICTO/PICTO-4km-noir.png
new file mode 100644
index 0000000..b9102aa
Binary files /dev/null and b/Content/Yotta2025/Medias/24-PICTO/PICTO-4km-noir.png differ
diff --git a/Content/Yotta2025/Medias/24-PICTO/PICTO-4km-noir.uasset b/Content/Yotta2025/Medias/24-PICTO/PICTO-4km-noir.uasset
new file mode 100644
index 0000000..898050b
Binary files /dev/null and b/Content/Yotta2025/Medias/24-PICTO/PICTO-4km-noir.uasset differ
diff --git a/Content/Yotta2025/Medias/24-PICTO/PICTO-6km-blanc.png b/Content/Yotta2025/Medias/24-PICTO/PICTO-6km-blanc.png
new file mode 100644
index 0000000..7e6866d
Binary files /dev/null and b/Content/Yotta2025/Medias/24-PICTO/PICTO-6km-blanc.png differ
diff --git a/Content/Yotta2025/Medias/24-PICTO/PICTO-6km-blanc.uasset b/Content/Yotta2025/Medias/24-PICTO/PICTO-6km-blanc.uasset
new file mode 100644
index 0000000..7c41df6
Binary files /dev/null and b/Content/Yotta2025/Medias/24-PICTO/PICTO-6km-blanc.uasset differ
diff --git a/Content/Yotta2025/Medias/24-PICTO/PICTO-6km-noir.png b/Content/Yotta2025/Medias/24-PICTO/PICTO-6km-noir.png
new file mode 100644
index 0000000..30231d4
Binary files /dev/null and b/Content/Yotta2025/Medias/24-PICTO/PICTO-6km-noir.png differ
diff --git a/Content/Yotta2025/Medias/24-PICTO/PICTO-6km-noir.uasset b/Content/Yotta2025/Medias/24-PICTO/PICTO-6km-noir.uasset
new file mode 100644
index 0000000..382e4c9
Binary files /dev/null and b/Content/Yotta2025/Medias/24-PICTO/PICTO-6km-noir.uasset differ
diff --git a/Content/Yotta2025/Medias/24-PICTO/PICTO-Course-blanc.png b/Content/Yotta2025/Medias/24-PICTO/PICTO-Course-blanc.png
new file mode 100644
index 0000000..50daba8
Binary files /dev/null and b/Content/Yotta2025/Medias/24-PICTO/PICTO-Course-blanc.png differ
diff --git a/Content/Yotta2025/Medias/24-PICTO/PICTO-Course-blanc.uasset b/Content/Yotta2025/Medias/24-PICTO/PICTO-Course-blanc.uasset
new file mode 100644
index 0000000..cbb4ab2
Binary files /dev/null and b/Content/Yotta2025/Medias/24-PICTO/PICTO-Course-blanc.uasset differ
diff --git a/Content/Yotta2025/Medias/24-PICTO/PICTO-Course-noir.png b/Content/Yotta2025/Medias/24-PICTO/PICTO-Course-noir.png
new file mode 100644
index 0000000..2ac680d
Binary files /dev/null and b/Content/Yotta2025/Medias/24-PICTO/PICTO-Course-noir.png differ
diff --git a/Content/Yotta2025/Medias/24-PICTO/PICTO-Course-noir.uasset b/Content/Yotta2025/Medias/24-PICTO/PICTO-Course-noir.uasset
new file mode 100644
index 0000000..18ae2af
Binary files /dev/null and b/Content/Yotta2025/Medias/24-PICTO/PICTO-Course-noir.uasset differ
diff --git a/Content/Yotta2025/Medias/24-PICTO/PICTO-Drapeau-blanc.png b/Content/Yotta2025/Medias/24-PICTO/PICTO-Drapeau-blanc.png
new file mode 100644
index 0000000..d67110a
Binary files /dev/null and b/Content/Yotta2025/Medias/24-PICTO/PICTO-Drapeau-blanc.png differ
diff --git a/Content/Yotta2025/Medias/24-PICTO/PICTO-Drapeau-blanc.uasset b/Content/Yotta2025/Medias/24-PICTO/PICTO-Drapeau-blanc.uasset
new file mode 100644
index 0000000..113a60a
Binary files /dev/null and b/Content/Yotta2025/Medias/24-PICTO/PICTO-Drapeau-blanc.uasset differ
diff --git a/Content/Yotta2025/Medias/24-PICTO/PICTO-Drapeau-noir.png b/Content/Yotta2025/Medias/24-PICTO/PICTO-Drapeau-noir.png
new file mode 100644
index 0000000..1278559
Binary files /dev/null and b/Content/Yotta2025/Medias/24-PICTO/PICTO-Drapeau-noir.png differ
diff --git a/Content/Yotta2025/Medias/24-PICTO/PICTO-Drapeau-noir.uasset b/Content/Yotta2025/Medias/24-PICTO/PICTO-Drapeau-noir.uasset
new file mode 100644
index 0000000..5ccfc77
Binary files /dev/null and b/Content/Yotta2025/Medias/24-PICTO/PICTO-Drapeau-noir.uasset differ
diff --git a/Content/Yotta2025/Medias/24-PICTO/PICTO-Gap-blanc.png b/Content/Yotta2025/Medias/24-PICTO/PICTO-Gap-blanc.png
new file mode 100644
index 0000000..cf641bc
Binary files /dev/null and b/Content/Yotta2025/Medias/24-PICTO/PICTO-Gap-blanc.png differ
diff --git a/Content/Yotta2025/Medias/24-PICTO/PICTO-Gap-blanc.uasset b/Content/Yotta2025/Medias/24-PICTO/PICTO-Gap-blanc.uasset
new file mode 100644
index 0000000..938b21e
Binary files /dev/null and b/Content/Yotta2025/Medias/24-PICTO/PICTO-Gap-blanc.uasset differ
diff --git a/Content/Yotta2025/Medias/24-PICTO/PICTO-Gap-noir.png b/Content/Yotta2025/Medias/24-PICTO/PICTO-Gap-noir.png
new file mode 100644
index 0000000..324be29
Binary files /dev/null and b/Content/Yotta2025/Medias/24-PICTO/PICTO-Gap-noir.png differ
diff --git a/Content/Yotta2025/Medias/24-PICTO/PICTO-Gap-noir.uasset b/Content/Yotta2025/Medias/24-PICTO/PICTO-Gap-noir.uasset
new file mode 100644
index 0000000..4d67002
Binary files /dev/null and b/Content/Yotta2025/Medias/24-PICTO/PICTO-Gap-noir.uasset differ
diff --git a/Content/Yotta2025/Medias/24-PICTO/PICTO-Nage-blanc.png b/Content/Yotta2025/Medias/24-PICTO/PICTO-Nage-blanc.png
new file mode 100644
index 0000000..4cdab1b
Binary files /dev/null and b/Content/Yotta2025/Medias/24-PICTO/PICTO-Nage-blanc.png differ
diff --git a/Content/Yotta2025/Medias/24-PICTO/PICTO-Nage-blanc.uasset b/Content/Yotta2025/Medias/24-PICTO/PICTO-Nage-blanc.uasset
new file mode 100644
index 0000000..b3c618d
Binary files /dev/null and b/Content/Yotta2025/Medias/24-PICTO/PICTO-Nage-blanc.uasset differ
diff --git a/Content/Yotta2025/Medias/24-PICTO/PICTO-Nage-noir.png b/Content/Yotta2025/Medias/24-PICTO/PICTO-Nage-noir.png
new file mode 100644
index 0000000..c93cb67
Binary files /dev/null and b/Content/Yotta2025/Medias/24-PICTO/PICTO-Nage-noir.png differ
diff --git a/Content/Yotta2025/Medias/24-PICTO/PICTO-Nage-noir.uasset b/Content/Yotta2025/Medias/24-PICTO/PICTO-Nage-noir.uasset
new file mode 100644
index 0000000..609b63f
Binary files /dev/null and b/Content/Yotta2025/Medias/24-PICTO/PICTO-Nage-noir.uasset differ
diff --git a/Content/Yotta2025/Medias/24-PICTO/PICTO-Repos-blanc.png b/Content/Yotta2025/Medias/24-PICTO/PICTO-Repos-blanc.png
new file mode 100644
index 0000000..6b561ad
Binary files /dev/null and b/Content/Yotta2025/Medias/24-PICTO/PICTO-Repos-blanc.png differ
diff --git a/Content/Yotta2025/Medias/24-PICTO/PICTO-Repos-blanc.uasset b/Content/Yotta2025/Medias/24-PICTO/PICTO-Repos-blanc.uasset
new file mode 100644
index 0000000..8a8cf08
Binary files /dev/null and b/Content/Yotta2025/Medias/24-PICTO/PICTO-Repos-blanc.uasset differ
diff --git a/Content/Yotta2025/Medias/24-PICTO/PICTO-Repos-noir.png b/Content/Yotta2025/Medias/24-PICTO/PICTO-Repos-noir.png
new file mode 100644
index 0000000..9501e4c
Binary files /dev/null and b/Content/Yotta2025/Medias/24-PICTO/PICTO-Repos-noir.png differ
diff --git a/Content/Yotta2025/Medias/24-PICTO/PICTO-Repos-noir.uasset b/Content/Yotta2025/Medias/24-PICTO/PICTO-Repos-noir.uasset
new file mode 100644
index 0000000..8430a75
Binary files /dev/null and b/Content/Yotta2025/Medias/24-PICTO/PICTO-Repos-noir.uasset differ
diff --git a/Content/Yotta2025/Medias/24-PICTO/PICTO-Time-blanc.png b/Content/Yotta2025/Medias/24-PICTO/PICTO-Time-blanc.png
new file mode 100644
index 0000000..ab2edbf
Binary files /dev/null and b/Content/Yotta2025/Medias/24-PICTO/PICTO-Time-blanc.png differ
diff --git a/Content/Yotta2025/Medias/24-PICTO/PICTO-Time-blanc.uasset b/Content/Yotta2025/Medias/24-PICTO/PICTO-Time-blanc.uasset
new file mode 100644
index 0000000..3f4fdfe
Binary files /dev/null and b/Content/Yotta2025/Medias/24-PICTO/PICTO-Time-blanc.uasset differ
diff --git a/Content/Yotta2025/Medias/24-PICTO/PICTO-Time-noir.png b/Content/Yotta2025/Medias/24-PICTO/PICTO-Time-noir.png
new file mode 100644
index 0000000..b31434e
Binary files /dev/null and b/Content/Yotta2025/Medias/24-PICTO/PICTO-Time-noir.png differ
diff --git a/Content/Yotta2025/Medias/24-PICTO/PICTO-Time-noir.uasset b/Content/Yotta2025/Medias/24-PICTO/PICTO-Time-noir.uasset
new file mode 100644
index 0000000..291c243
Binary files /dev/null and b/Content/Yotta2025/Medias/24-PICTO/PICTO-Time-noir.uasset differ
diff --git a/Content/Yotta2025/Medias/24-PICTO/PICTO-Trans-blanc.png b/Content/Yotta2025/Medias/24-PICTO/PICTO-Trans-blanc.png
new file mode 100644
index 0000000..d4fd984
Binary files /dev/null and b/Content/Yotta2025/Medias/24-PICTO/PICTO-Trans-blanc.png differ
diff --git a/Content/Yotta2025/Medias/24-PICTO/PICTO-Trans-blanc.uasset b/Content/Yotta2025/Medias/24-PICTO/PICTO-Trans-blanc.uasset
new file mode 100644
index 0000000..b90d32b
Binary files /dev/null and b/Content/Yotta2025/Medias/24-PICTO/PICTO-Trans-blanc.uasset differ
diff --git a/Content/Yotta2025/Medias/24-PICTO/PICTO-Trans-noir.png b/Content/Yotta2025/Medias/24-PICTO/PICTO-Trans-noir.png
new file mode 100644
index 0000000..d7f9a40
Binary files /dev/null and b/Content/Yotta2025/Medias/24-PICTO/PICTO-Trans-noir.png differ
diff --git a/Content/Yotta2025/Medias/24-PICTO/PICTO-Trans-noir.uasset b/Content/Yotta2025/Medias/24-PICTO/PICTO-Trans-noir.uasset
new file mode 100644
index 0000000..d737f89
Binary files /dev/null and b/Content/Yotta2025/Medias/24-PICTO/PICTO-Trans-noir.uasset differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-chrono-10sec-lettre.png b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-chrono-10sec-lettre.png
new file mode 100644
index 0000000..d6271c9
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-chrono-10sec-lettre.png differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-chrono-10sec-lettre.uasset b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-chrono-10sec-lettre.uasset
new file mode 100644
index 0000000..cc7457f
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-chrono-10sec-lettre.uasset differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-chrono-10sec.png b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-chrono-10sec.png
new file mode 100644
index 0000000..0b6472f
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-chrono-10sec.png differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-chrono-10sec.uasset b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-chrono-10sec.uasset
new file mode 100644
index 0000000..c0ab782
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-chrono-10sec.uasset differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-chrono-lettre.png b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-chrono-lettre.png
new file mode 100644
index 0000000..e695993
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-chrono-lettre.png differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-chrono-lettre.uasset b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-chrono-lettre.uasset
new file mode 100644
index 0000000..2654fcb
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-chrono-lettre.uasset differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-chrono-timeover-lettre.png b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-chrono-timeover-lettre.png
new file mode 100644
index 0000000..5a557ec
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-chrono-timeover-lettre.png differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-chrono-timeover-lettre.uasset b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-chrono-timeover-lettre.uasset
new file mode 100644
index 0000000..aab3c5e
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-chrono-timeover-lettre.uasset differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-chrono-timeover.png b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-chrono-timeover.png
new file mode 100644
index 0000000..f4f73ea
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-chrono-timeover.png differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-chrono-timeover.uasset b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-chrono-timeover.uasset
new file mode 100644
index 0000000..64616b1
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-chrono-timeover.uasset differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-chrono.png b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-chrono.png
new file mode 100644
index 0000000..8e75918
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-chrono.png differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-chrono.uasset b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-chrono.uasset
new file mode 100644
index 0000000..f792c4c
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-chrono.uasset differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-colonne-fond.png b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-colonne-fond.png
new file mode 100644
index 0000000..2bc422e
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-colonne-fond.png differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-colonne-fond.uasset b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-colonne-fond.uasset
new file mode 100644
index 0000000..b16e32e
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-colonne-fond.uasset differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-slip fond.png b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-slip fond.png
new file mode 100644
index 0000000..f704a3a
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-slip fond.png differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-slip_fond.uasset b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-slip_fond.uasset
new file mode 100644
index 0000000..8c0e4d7
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-slip_fond.uasset differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-triangle Y fond.png b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-triangle Y fond.png
new file mode 100644
index 0000000..c37069d
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-triangle Y fond.png differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-triangle_Y_fond.uasset b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-triangle_Y_fond.uasset
new file mode 100644
index 0000000..29a06f0
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/24-STREAM-triangle_Y_fond.uasset differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STRAM-fond-attente.mp4 b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STRAM-fond-attente.mp4
new file mode 100644
index 0000000..d415f2d
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STRAM-fond-attente.mp4 differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM - rank 1.png b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM - rank 1.png
new file mode 100644
index 0000000..7c51ed2
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM - rank 1.png differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM - rank numéros.png b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM - rank numéros.png
new file mode 100644
index 0000000..f7ccbd6
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM - rank numéros.png differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM - split-BN.png b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM - split-BN.png
new file mode 100644
index 0000000..7d34791
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM - split-BN.png differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM - split-NB.png b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM - split-NB.png
new file mode 100644
index 0000000..fe727ff
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM - split-NB.png differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM - split.png b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM - split.png
new file mode 100644
index 0000000..eb10d22
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM - split.png differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM - yottaxp.png b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM - yottaxp.png
new file mode 100644
index 0000000..9e9746f
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM - yottaxp.png differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM - yottaxpelite.png b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM - yottaxpelite.png
new file mode 100644
index 0000000..0f8b414
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM - yottaxpelite.png differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM - yottaxps.png b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM - yottaxps.png
new file mode 100644
index 0000000..f73182f
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM - yottaxps.png differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM - yottaxpsrelais.png b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM - yottaxpsrelais.png
new file mode 100644
index 0000000..5707808
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM - yottaxpsrelais.png differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM-Fond.png b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM-Fond.png
new file mode 100644
index 0000000..3ec4fa2
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM-Fond.png differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM-Fond.uasset b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM-Fond.uasset
new file mode 100644
index 0000000..c5b60da
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM-Fond.uasset differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM-FondY.png b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM-FondY.png
new file mode 100644
index 0000000..948987f
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM-FondY.png differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM-FondY.uasset b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM-FondY.uasset
new file mode 100644
index 0000000..26582d3
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM-FondY.uasset differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM-Nom.png b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM-Nom.png
new file mode 100644
index 0000000..5b0ba5e
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM-Nom.png differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM-Nom.uasset b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM-Nom.uasset
new file mode 100644
index 0000000..a3865b2
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM-Nom.uasset differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM-rect-bleu-timeline.png b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM-rect-bleu-timeline.png
new file mode 100644
index 0000000..9ee9d15
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM-rect-bleu-timeline.png differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM-rect-bleu-timeline.uasset b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM-rect-bleu-timeline.uasset
new file mode 100644
index 0000000..427d4a1
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM-rect-bleu-timeline.uasset differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM-rect-rouge-timeline.png b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM-rect-rouge-timeline.png
new file mode 100644
index 0000000..cb766b1
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM-rect-rouge-timeline.png differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM-rect-rouge-timeline.uasset b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM-rect-rouge-timeline.uasset
new file mode 100644
index 0000000..ad33f9c
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM-rect-rouge-timeline.uasset differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM-rect-vert-timeline.png b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM-rect-vert-timeline.png
new file mode 100644
index 0000000..619349e
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM-rect-vert-timeline.png differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM-rect-vert-timeline.uasset b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM-rect-vert-timeline.uasset
new file mode 100644
index 0000000..5687372
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM-rect-vert-timeline.uasset differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM-timeline.png b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM-timeline.png
new file mode 100644
index 0000000..0b6aaca
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM-timeline.png differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM-timeline.uasset b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM-timeline.uasset
new file mode 100644
index 0000000..24f07e1
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM-timeline.uasset differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM_-_rank_1.uasset b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM_-_rank_1.uasset
new file mode 100644
index 0000000..0d12fcd
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM_-_rank_1.uasset differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM_-_rank_numéros.uasset b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM_-_rank_numéros.uasset
new file mode 100644
index 0000000..c57283a
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM_-_rank_numéros.uasset differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM_-_split-BN.uasset b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM_-_split-BN.uasset
new file mode 100644
index 0000000..0e6c01f
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM_-_split-BN.uasset differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM_-_split-NB.uasset b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM_-_split-NB.uasset
new file mode 100644
index 0000000..dbf1912
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM_-_split-NB.uasset differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM_-_split.uasset b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM_-_split.uasset
new file mode 100644
index 0000000..3bfd0bc
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM_-_split.uasset differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM_-_yottaxp.uasset b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM_-_yottaxp.uasset
new file mode 100644
index 0000000..91bd112
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM_-_yottaxp.uasset differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM_-_yottaxpelite.uasset b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM_-_yottaxpelite.uasset
new file mode 100644
index 0000000..3bdeab2
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM_-_yottaxpelite.uasset differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM_-_yottaxps.uasset b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM_-_yottaxps.uasset
new file mode 100644
index 0000000..4c1c1c2
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM_-_yottaxps.uasset differ
diff --git a/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM_-_yottaxpsrelais.uasset b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM_-_yottaxpsrelais.uasset
new file mode 100644
index 0000000..1ce5d14
Binary files /dev/null and b/Content/Yotta2025/Medias/24-STREAM-MEDIAS/STREAM_-_yottaxpsrelais.uasset differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/2km.png b/Content/Yotta2025/Medias/Exports-PNG/2km.png
new file mode 100644
index 0000000..9499be9
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/2km.png differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/2km.uasset b/Content/Yotta2025/Medias/Exports-PNG/2km.uasset
new file mode 100644
index 0000000..5ceac79
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/2km.uasset differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/4km.png b/Content/Yotta2025/Medias/Exports-PNG/4km.png
new file mode 100644
index 0000000..c78ed1e
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/4km.png differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/4km.uasset b/Content/Yotta2025/Medias/Exports-PNG/4km.uasset
new file mode 100644
index 0000000..5bc14c8
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/4km.uasset differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/6km.png b/Content/Yotta2025/Medias/Exports-PNG/6km.png
new file mode 100644
index 0000000..7fa85b1
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/6km.png differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/6km.uasset b/Content/Yotta2025/Medias/Exports-PNG/6km.uasset
new file mode 100644
index 0000000..2cd9cef
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/6km.uasset differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/Arche - joueur mystere.png b/Content/Yotta2025/Medias/Exports-PNG/Arche - joueur mystere.png
new file mode 100644
index 0000000..194a02a
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/Arche - joueur mystere.png differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/Arche_-_joueur_mystere.uasset b/Content/Yotta2025/Medias/Exports-PNG/Arche_-_joueur_mystere.uasset
new file mode 100644
index 0000000..3f45b52
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/Arche_-_joueur_mystere.uasset differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/Exports-photos coureurs.png b/Content/Yotta2025/Medias/Exports-PNG/Exports-photos coureurs.png
new file mode 100644
index 0000000..41c2b38
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/Exports-photos coureurs.png differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/Exports-photos_coureurs.uasset b/Content/Yotta2025/Medias/Exports-PNG/Exports-photos_coureurs.uasset
new file mode 100644
index 0000000..30c2d4d
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/Exports-photos_coureurs.uasset differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/JoueursMysteresRelais.png b/Content/Yotta2025/Medias/Exports-PNG/JoueursMysteresRelais.png
new file mode 100644
index 0000000..05b28ff
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/JoueursMysteresRelais.png differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/JoueursMysteresRelais.uasset b/Content/Yotta2025/Medias/Exports-PNG/JoueursMysteresRelais.uasset
new file mode 100644
index 0000000..a35ac13
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/JoueursMysteresRelais.uasset differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/JoueursMysteresSolo.png b/Content/Yotta2025/Medias/Exports-PNG/JoueursMysteresSolo.png
new file mode 100644
index 0000000..c0cf5bc
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/JoueursMysteresSolo.png differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/JoueursMysteresSolo.uasset b/Content/Yotta2025/Medias/Exports-PNG/JoueursMysteresSolo.uasset
new file mode 100644
index 0000000..c33a2e4
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/JoueursMysteresSolo.uasset differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/Stream - Collone fond.png b/Content/Yotta2025/Medias/Exports-PNG/Stream - Collone fond.png
new file mode 100644
index 0000000..2bc422e
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/Stream - Collone fond.png differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/Stream - chronos fond.png b/Content/Yotta2025/Medias/Exports-PNG/Stream - chronos fond.png
new file mode 100644
index 0000000..7218349
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/Stream - chronos fond.png differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/Stream - rank 1.png b/Content/Yotta2025/Medias/Exports-PNG/Stream - rank 1.png
new file mode 100644
index 0000000..7c51ed2
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/Stream - rank 1.png differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/Stream - rank numéros.png b/Content/Yotta2025/Medias/Exports-PNG/Stream - rank numéros.png
new file mode 100644
index 0000000..f7ccbd6
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/Stream - rank numéros.png differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/Stream - yottaxp.png b/Content/Yotta2025/Medias/Exports-PNG/Stream - yottaxp.png
new file mode 100644
index 0000000..2bfea69
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/Stream - yottaxp.png differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/Stream - yottaxpelite.png b/Content/Yotta2025/Medias/Exports-PNG/Stream - yottaxpelite.png
new file mode 100644
index 0000000..a3b7f58
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/Stream - yottaxpelite.png differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/Stream - yottaxps.png b/Content/Yotta2025/Medias/Exports-PNG/Stream - yottaxps.png
new file mode 100644
index 0000000..5bdcc28
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/Stream - yottaxps.png differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/Stream - yottaxpsrelais.png b/Content/Yotta2025/Medias/Exports-PNG/Stream - yottaxpsrelais.png
new file mode 100644
index 0000000..dd5cc7f
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/Stream - yottaxpsrelais.png differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/Stream -slip fond.png b/Content/Yotta2025/Medias/Exports-PNG/Stream -slip fond.png
new file mode 100644
index 0000000..f704a3a
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/Stream -slip fond.png differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/Stream -triangle Y fond.png b/Content/Yotta2025/Medias/Exports-PNG/Stream -triangle Y fond.png
new file mode 100644
index 0000000..c37069d
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/Stream -triangle Y fond.png differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/Stream_-_Collone_fond.uasset b/Content/Yotta2025/Medias/Exports-PNG/Stream_-_Collone_fond.uasset
new file mode 100644
index 0000000..453830a
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/Stream_-_Collone_fond.uasset differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/Stream_-_chronos_fond.uasset b/Content/Yotta2025/Medias/Exports-PNG/Stream_-_chronos_fond.uasset
new file mode 100644
index 0000000..eda9291
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/Stream_-_chronos_fond.uasset differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/Stream_-_rank_1.uasset b/Content/Yotta2025/Medias/Exports-PNG/Stream_-_rank_1.uasset
new file mode 100644
index 0000000..e4c3917
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/Stream_-_rank_1.uasset differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/Stream_-_rank_numéros.uasset b/Content/Yotta2025/Medias/Exports-PNG/Stream_-_rank_numéros.uasset
new file mode 100644
index 0000000..0a45beb
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/Stream_-_rank_numéros.uasset differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/Stream_-_yottaxp.uasset b/Content/Yotta2025/Medias/Exports-PNG/Stream_-_yottaxp.uasset
new file mode 100644
index 0000000..b32b39f
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/Stream_-_yottaxp.uasset differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/Stream_-_yottaxpelite.uasset b/Content/Yotta2025/Medias/Exports-PNG/Stream_-_yottaxpelite.uasset
new file mode 100644
index 0000000..d9337c1
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/Stream_-_yottaxpelite.uasset differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/Stream_-_yottaxps.uasset b/Content/Yotta2025/Medias/Exports-PNG/Stream_-_yottaxps.uasset
new file mode 100644
index 0000000..5d000f3
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/Stream_-_yottaxps.uasset differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/Stream_-_yottaxpsrelais.uasset b/Content/Yotta2025/Medias/Exports-PNG/Stream_-_yottaxpsrelais.uasset
new file mode 100644
index 0000000..8eefc21
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/Stream_-_yottaxpsrelais.uasset differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/Stream_-slip_fond.uasset b/Content/Yotta2025/Medias/Exports-PNG/Stream_-slip_fond.uasset
new file mode 100644
index 0000000..57bbdb3
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/Stream_-slip_fond.uasset differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/Stream_-triangle_Y_fond.uasset b/Content/Yotta2025/Medias/Exports-PNG/Stream_-triangle_Y_fond.uasset
new file mode 100644
index 0000000..453c6da
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/Stream_-triangle_Y_fond.uasset differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/Thumbs.db b/Content/Yotta2025/Medias/Exports-PNG/Thumbs.db
new file mode 100644
index 0000000..b7a9922
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/Thumbs.db differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/WINNER.png b/Content/Yotta2025/Medias/Exports-PNG/WINNER.png
new file mode 100644
index 0000000..a48b2ef
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/WINNER.png differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/WINNER.uasset b/Content/Yotta2025/Medias/Exports-PNG/WINNER.uasset
new file mode 100644
index 0000000..5fb8cf2
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/WINNER.uasset differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/ex photo relais.png b/Content/Yotta2025/Medias/Exports-PNG/ex photo relais.png
new file mode 100644
index 0000000..31f41fb
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/ex photo relais.png differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/ex photo simple.png b/Content/Yotta2025/Medias/Exports-PNG/ex photo simple.png
new file mode 100644
index 0000000..614d17a
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/ex photo simple.png differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/ex_photo_relais.uasset b/Content/Yotta2025/Medias/Exports-PNG/ex_photo_relais.uasset
new file mode 100644
index 0000000..1ca39a8
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/ex_photo_relais.uasset differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/ex_photo_simple.uasset b/Content/Yotta2025/Medias/Exports-PNG/ex_photo_simple.uasset
new file mode 100644
index 0000000..df5acd4
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/ex_photo_simple.uasset differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/fond depart.png b/Content/Yotta2025/Medias/Exports-PNG/fond depart.png
new file mode 100644
index 0000000..06a241c
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/fond depart.png differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/fond_depart.uasset b/Content/Yotta2025/Medias/Exports-PNG/fond_depart.uasset
new file mode 100644
index 0000000..80edc71
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/fond_depart.uasset differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/nage-2.png b/Content/Yotta2025/Medias/Exports-PNG/nage-2.png
new file mode 100644
index 0000000..3696e18
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/nage-2.png differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/nage-2.uasset b/Content/Yotta2025/Medias/Exports-PNG/nage-2.uasset
new file mode 100644
index 0000000..1d30025
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/nage-2.uasset differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/trans.png b/Content/Yotta2025/Medias/Exports-PNG/trans.png
new file mode 100644
index 0000000..de77d91
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/trans.png differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/trans.uasset b/Content/Yotta2025/Medias/Exports-PNG/trans.uasset
new file mode 100644
index 0000000..0be8d66
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/trans.uasset differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/yotteelitetest.png b/Content/Yotta2025/Medias/Exports-PNG/yotteelitetest.png
new file mode 100644
index 0000000..5afd1d4
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/yotteelitetest.png differ
diff --git a/Content/Yotta2025/Medias/Exports-PNG/yotteelitetest.uasset b/Content/Yotta2025/Medias/Exports-PNG/yotteelitetest.uasset
new file mode 100644
index 0000000..99c9f71
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-PNG/yotteelitetest.uasset differ
diff --git a/Content/Yotta2025/Medias/Exports-SVG/2km-2.svg b/Content/Yotta2025/Medias/Exports-SVG/2km-2.svg
new file mode 100644
index 0000000..709d12c
--- /dev/null
+++ b/Content/Yotta2025/Medias/Exports-SVG/2km-2.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Content/Yotta2025/Medias/Exports-SVG/2km-2.uasset b/Content/Yotta2025/Medias/Exports-SVG/2km-2.uasset
new file mode 100644
index 0000000..b6de91d
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-SVG/2km-2.uasset differ
diff --git a/Content/Yotta2025/Medias/Exports-SVG/2km.svg b/Content/Yotta2025/Medias/Exports-SVG/2km.svg
new file mode 100644
index 0000000..26463f4
--- /dev/null
+++ b/Content/Yotta2025/Medias/Exports-SVG/2km.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Content/Yotta2025/Medias/Exports-SVG/2km.uasset b/Content/Yotta2025/Medias/Exports-SVG/2km.uasset
new file mode 100644
index 0000000..e05c489
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-SVG/2km.uasset differ
diff --git a/Content/Yotta2025/Medias/Exports-SVG/4km.svg b/Content/Yotta2025/Medias/Exports-SVG/4km.svg
new file mode 100644
index 0000000..7806eec
--- /dev/null
+++ b/Content/Yotta2025/Medias/Exports-SVG/4km.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Content/Yotta2025/Medias/Exports-SVG/4km.uasset b/Content/Yotta2025/Medias/Exports-SVG/4km.uasset
new file mode 100644
index 0000000..73bc295
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-SVG/4km.uasset differ
diff --git a/Content/Yotta2025/Medias/Exports-SVG/6km.svg b/Content/Yotta2025/Medias/Exports-SVG/6km.svg
new file mode 100644
index 0000000..18296ce
--- /dev/null
+++ b/Content/Yotta2025/Medias/Exports-SVG/6km.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Content/Yotta2025/Medias/Exports-SVG/6km.uasset b/Content/Yotta2025/Medias/Exports-SVG/6km.uasset
new file mode 100644
index 0000000..7333699
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-SVG/6km.uasset differ
diff --git a/Content/Yotta2025/Medias/Exports-SVG/Chronos-fond.svg b/Content/Yotta2025/Medias/Exports-SVG/Chronos-fond.svg
new file mode 100644
index 0000000..7dbe1b8
--- /dev/null
+++ b/Content/Yotta2025/Medias/Exports-SVG/Chronos-fond.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Content/Yotta2025/Medias/Exports-SVG/Chronos-fond.uasset b/Content/Yotta2025/Medias/Exports-SVG/Chronos-fond.uasset
new file mode 100644
index 0000000..fefabaa
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-SVG/Chronos-fond.uasset differ
diff --git a/Content/Yotta2025/Medias/Exports-SVG/Fond-Colonne-Noir.svg b/Content/Yotta2025/Medias/Exports-SVG/Fond-Colonne-Noir.svg
new file mode 100644
index 0000000..9b8f952
--- /dev/null
+++ b/Content/Yotta2025/Medias/Exports-SVG/Fond-Colonne-Noir.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Content/Yotta2025/Medias/Exports-SVG/Fond-Colonne-Noir.uasset b/Content/Yotta2025/Medias/Exports-SVG/Fond-Colonne-Noir.uasset
new file mode 100644
index 0000000..3c378a3
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-SVG/Fond-Colonne-Noir.uasset differ
diff --git a/Content/Yotta2025/Medias/Exports-SVG/Infos - images - John.odt b/Content/Yotta2025/Medias/Exports-SVG/Infos - images - John.odt
new file mode 100644
index 0000000..beac00e
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-SVG/Infos - images - John.odt differ
diff --git a/Content/Yotta2025/Medias/Exports-SVG/Infos - images - John2.odt b/Content/Yotta2025/Medias/Exports-SVG/Infos - images - John2.odt
new file mode 100644
index 0000000..a8d565f
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-SVG/Infos - images - John2.odt differ
diff --git a/Content/Yotta2025/Medias/Exports-SVG/Rank -1.svg b/Content/Yotta2025/Medias/Exports-SVG/Rank -1.svg
new file mode 100644
index 0000000..4072b92
--- /dev/null
+++ b/Content/Yotta2025/Medias/Exports-SVG/Rank -1.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Content/Yotta2025/Medias/Exports-SVG/Rank -numeros.svg b/Content/Yotta2025/Medias/Exports-SVG/Rank -numeros.svg
new file mode 100644
index 0000000..7682a7a
--- /dev/null
+++ b/Content/Yotta2025/Medias/Exports-SVG/Rank -numeros.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Content/Yotta2025/Medias/Exports-SVG/Rank_-1.uasset b/Content/Yotta2025/Medias/Exports-SVG/Rank_-1.uasset
new file mode 100644
index 0000000..cdc22bf
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-SVG/Rank_-1.uasset differ
diff --git a/Content/Yotta2025/Medias/Exports-SVG/Rank_-numeros.uasset b/Content/Yotta2025/Medias/Exports-SVG/Rank_-numeros.uasset
new file mode 100644
index 0000000..5abe204
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-SVG/Rank_-numeros.uasset differ
diff --git a/Content/Yotta2025/Medias/Exports-SVG/Slip-Fond.svg b/Content/Yotta2025/Medias/Exports-SVG/Slip-Fond.svg
new file mode 100644
index 0000000..1d89b12
--- /dev/null
+++ b/Content/Yotta2025/Medias/Exports-SVG/Slip-Fond.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Content/Yotta2025/Medias/Exports-SVG/Slip-Fond.uasset b/Content/Yotta2025/Medias/Exports-SVG/Slip-Fond.uasset
new file mode 100644
index 0000000..4f39ffe
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-SVG/Slip-Fond.uasset differ
diff --git a/Content/Yotta2025/Medias/Exports-SVG/Y-trianglerouge.svg b/Content/Yotta2025/Medias/Exports-SVG/Y-trianglerouge.svg
new file mode 100644
index 0000000..70366f9
--- /dev/null
+++ b/Content/Yotta2025/Medias/Exports-SVG/Y-trianglerouge.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Content/Yotta2025/Medias/Exports-SVG/Y-trianglerouge.uasset b/Content/Yotta2025/Medias/Exports-SVG/Y-trianglerouge.uasset
new file mode 100644
index 0000000..dbc3031
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-SVG/Y-trianglerouge.uasset differ
diff --git a/Content/Yotta2025/Medias/Exports-SVG/YOTTAXP.svg b/Content/Yotta2025/Medias/Exports-SVG/YOTTAXP.svg
new file mode 100644
index 0000000..aa5db2d
--- /dev/null
+++ b/Content/Yotta2025/Medias/Exports-SVG/YOTTAXP.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Content/Yotta2025/Medias/Exports-SVG/YOTTAXP.uasset b/Content/Yotta2025/Medias/Exports-SVG/YOTTAXP.uasset
new file mode 100644
index 0000000..10dc41f
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-SVG/YOTTAXP.uasset differ
diff --git a/Content/Yotta2025/Medias/Exports-SVG/YOTTAXPS.svg b/Content/Yotta2025/Medias/Exports-SVG/YOTTAXPS.svg
new file mode 100644
index 0000000..2c4f543
--- /dev/null
+++ b/Content/Yotta2025/Medias/Exports-SVG/YOTTAXPS.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Content/Yotta2025/Medias/Exports-SVG/YOTTAXPS.uasset b/Content/Yotta2025/Medias/Exports-SVG/YOTTAXPS.uasset
new file mode 100644
index 0000000..8d3393c
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-SVG/YOTTAXPS.uasset differ
diff --git a/Content/Yotta2025/Medias/Exports-SVG/YOTTAXPSrelais.svg b/Content/Yotta2025/Medias/Exports-SVG/YOTTAXPSrelais.svg
new file mode 100644
index 0000000..facbfdf
--- /dev/null
+++ b/Content/Yotta2025/Medias/Exports-SVG/YOTTAXPSrelais.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Content/Yotta2025/Medias/Exports-SVG/YOTTAXPSrelais.uasset b/Content/Yotta2025/Medias/Exports-SVG/YOTTAXPSrelais.uasset
new file mode 100644
index 0000000..0ea4988
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-SVG/YOTTAXPSrelais.uasset differ
diff --git a/Content/Yotta2025/Medias/Exports-SVG/nage-2.svg b/Content/Yotta2025/Medias/Exports-SVG/nage-2.svg
new file mode 100644
index 0000000..cc35d17
--- /dev/null
+++ b/Content/Yotta2025/Medias/Exports-SVG/nage-2.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Content/Yotta2025/Medias/Exports-SVG/nage-2.uasset b/Content/Yotta2025/Medias/Exports-SVG/nage-2.uasset
new file mode 100644
index 0000000..a4b8a85
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-SVG/nage-2.uasset differ
diff --git a/Content/Yotta2025/Medias/Exports-SVG/nage.svg b/Content/Yotta2025/Medias/Exports-SVG/nage.svg
new file mode 100644
index 0000000..34ddffe
--- /dev/null
+++ b/Content/Yotta2025/Medias/Exports-SVG/nage.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Content/Yotta2025/Medias/Exports-SVG/nage.uasset b/Content/Yotta2025/Medias/Exports-SVG/nage.uasset
new file mode 100644
index 0000000..86c3d0b
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-SVG/nage.uasset differ
diff --git a/Content/Yotta2025/Medias/Exports-SVG/transition.svg b/Content/Yotta2025/Medias/Exports-SVG/transition.svg
new file mode 100644
index 0000000..e2fc3c2
--- /dev/null
+++ b/Content/Yotta2025/Medias/Exports-SVG/transition.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Content/Yotta2025/Medias/Exports-SVG/transition.uasset b/Content/Yotta2025/Medias/Exports-SVG/transition.uasset
new file mode 100644
index 0000000..97413ed
Binary files /dev/null and b/Content/Yotta2025/Medias/Exports-SVG/transition.uasset differ
diff --git a/Content/Yotta2025/Medias/graph V1/ARCHE-attente.png b/Content/Yotta2025/Medias/graph V1/ARCHE-attente.png
new file mode 100644
index 0000000..768320e
Binary files /dev/null and b/Content/Yotta2025/Medias/graph V1/ARCHE-attente.png differ
diff --git a/Content/Yotta2025/Medias/graph V1/ARCHE-coureur.png b/Content/Yotta2025/Medias/graph V1/ARCHE-coureur.png
new file mode 100644
index 0000000..a67998a
Binary files /dev/null and b/Content/Yotta2025/Medias/graph V1/ARCHE-coureur.png differ
diff --git a/Content/Yotta2025/Medias/graph V1/ARCHE-doublechronos-time over.png b/Content/Yotta2025/Medias/graph V1/ARCHE-doublechronos-time over.png
new file mode 100644
index 0000000..3f94e78
Binary files /dev/null and b/Content/Yotta2025/Medias/graph V1/ARCHE-doublechronos-time over.png differ
diff --git a/Content/Yotta2025/Medias/graph V1/ARCHE-doublechronos.png b/Content/Yotta2025/Medias/graph V1/ARCHE-doublechronos.png
new file mode 100644
index 0000000..c2c7acf
Binary files /dev/null and b/Content/Yotta2025/Medias/graph V1/ARCHE-doublechronos.png differ
diff --git a/Content/Yotta2025/Medias/graph V1/ARCHE-time over.png b/Content/Yotta2025/Medias/graph V1/ARCHE-time over.png
new file mode 100644
index 0000000..7d1d8da
Binary files /dev/null and b/Content/Yotta2025/Medias/graph V1/ARCHE-time over.png differ
diff --git a/Content/Yotta2025/Medias/graph V1/ARCHE-winner.png b/Content/Yotta2025/Medias/graph V1/ARCHE-winner.png
new file mode 100644
index 0000000..5e24300
Binary files /dev/null and b/Content/Yotta2025/Medias/graph V1/ARCHE-winner.png differ
diff --git a/Content/Yotta2025/Medias/graph V1/DEPART-annonce simple.png b/Content/Yotta2025/Medias/graph V1/DEPART-annonce simple.png
new file mode 100644
index 0000000..d7ef9be
Binary files /dev/null and b/Content/Yotta2025/Medias/graph V1/DEPART-annonce simple.png differ
diff --git a/Content/Yotta2025/Medias/graph V1/DEPART-annoncedecale.png b/Content/Yotta2025/Medias/graph V1/DEPART-annoncedecale.png
new file mode 100644
index 0000000..be31f62
Binary files /dev/null and b/Content/Yotta2025/Medias/graph V1/DEPART-annoncedecale.png differ
diff --git a/Content/Yotta2025/Medias/graph V1/DEPART-decaleGO.png b/Content/Yotta2025/Medias/graph V1/DEPART-decaleGO.png
new file mode 100644
index 0000000..ec1cbef
Binary files /dev/null and b/Content/Yotta2025/Medias/graph V1/DEPART-decaleGO.png differ
diff --git a/Content/Yotta2025/Medias/graph V1/STREAM-classement.png b/Content/Yotta2025/Medias/graph V1/STREAM-classement.png
new file mode 100644
index 0000000..1f69593
Binary files /dev/null and b/Content/Yotta2025/Medias/graph V1/STREAM-classement.png differ
diff --git a/Content/Yotta2025/Medias/graph V1/STREAM-colonne-base.png b/Content/Yotta2025/Medias/graph V1/STREAM-colonne-base.png
new file mode 100644
index 0000000..de414a1
Binary files /dev/null and b/Content/Yotta2025/Medias/graph V1/STREAM-colonne-base.png differ
diff --git a/Content/Yotta2025/Medias/graph V1/STREAM-colonne-class - John.png b/Content/Yotta2025/Medias/graph V1/STREAM-colonne-class - John.png
new file mode 100644
index 0000000..fb8c950
Binary files /dev/null and b/Content/Yotta2025/Medias/graph V1/STREAM-colonne-class - John.png differ
diff --git a/Content/Yotta2025/Medias/graph V1/STREAM-colonne-class.png b/Content/Yotta2025/Medias/graph V1/STREAM-colonne-class.png
new file mode 100644
index 0000000..e62cfbe
Binary files /dev/null and b/Content/Yotta2025/Medias/graph V1/STREAM-colonne-class.png differ
diff --git a/Content/Yotta2025/Medias/graph V1/STREAM-colonne-coureur.png b/Content/Yotta2025/Medias/graph V1/STREAM-colonne-coureur.png
new file mode 100644
index 0000000..703f336
Binary files /dev/null and b/Content/Yotta2025/Medias/graph V1/STREAM-colonne-coureur.png differ
diff --git a/Content/Yotta2025/Medias/graph V1/STREAM-commentateurs.png b/Content/Yotta2025/Medias/graph V1/STREAM-commentateurs.png
new file mode 100644
index 0000000..0a2c951
Binary files /dev/null and b/Content/Yotta2025/Medias/graph V1/STREAM-commentateurs.png differ
diff --git a/Content/Yotta2025/Medias/graph V1/STREAM-split.png b/Content/Yotta2025/Medias/graph V1/STREAM-split.png
new file mode 100644
index 0000000..6e40e10
Binary files /dev/null and b/Content/Yotta2025/Medias/graph V1/STREAM-split.png differ
diff --git a/Content/Yotta2025/Medias/graph_V1/ARCHE-attente.uasset b/Content/Yotta2025/Medias/graph_V1/ARCHE-attente.uasset
new file mode 100644
index 0000000..ca25544
Binary files /dev/null and b/Content/Yotta2025/Medias/graph_V1/ARCHE-attente.uasset differ
diff --git a/Content/Yotta2025/Medias/graph_V1/ARCHE-coureur.uasset b/Content/Yotta2025/Medias/graph_V1/ARCHE-coureur.uasset
new file mode 100644
index 0000000..af5db1e
Binary files /dev/null and b/Content/Yotta2025/Medias/graph_V1/ARCHE-coureur.uasset differ
diff --git a/Content/Yotta2025/Medias/graph_V1/ARCHE-doublechronos-time_over.uasset b/Content/Yotta2025/Medias/graph_V1/ARCHE-doublechronos-time_over.uasset
new file mode 100644
index 0000000..5a5ff08
Binary files /dev/null and b/Content/Yotta2025/Medias/graph_V1/ARCHE-doublechronos-time_over.uasset differ
diff --git a/Content/Yotta2025/Medias/graph_V1/ARCHE-doublechronos.uasset b/Content/Yotta2025/Medias/graph_V1/ARCHE-doublechronos.uasset
new file mode 100644
index 0000000..99129d8
Binary files /dev/null and b/Content/Yotta2025/Medias/graph_V1/ARCHE-doublechronos.uasset differ
diff --git a/Content/Yotta2025/Medias/graph_V1/ARCHE-time_over.uasset b/Content/Yotta2025/Medias/graph_V1/ARCHE-time_over.uasset
new file mode 100644
index 0000000..fd67a12
Binary files /dev/null and b/Content/Yotta2025/Medias/graph_V1/ARCHE-time_over.uasset differ
diff --git a/Content/Yotta2025/Medias/graph_V1/ARCHE-winner.uasset b/Content/Yotta2025/Medias/graph_V1/ARCHE-winner.uasset
new file mode 100644
index 0000000..9d3aac0
Binary files /dev/null and b/Content/Yotta2025/Medias/graph_V1/ARCHE-winner.uasset differ
diff --git a/Content/Yotta2025/Medias/graph_V1/DEPART-annonce_simple.uasset b/Content/Yotta2025/Medias/graph_V1/DEPART-annonce_simple.uasset
new file mode 100644
index 0000000..809a940
Binary files /dev/null and b/Content/Yotta2025/Medias/graph_V1/DEPART-annonce_simple.uasset differ
diff --git a/Content/Yotta2025/Medias/graph_V1/DEPART-annoncedecale.uasset b/Content/Yotta2025/Medias/graph_V1/DEPART-annoncedecale.uasset
new file mode 100644
index 0000000..e3a4a67
Binary files /dev/null and b/Content/Yotta2025/Medias/graph_V1/DEPART-annoncedecale.uasset differ
diff --git a/Content/Yotta2025/Medias/graph_V1/DEPART-decaleGO.uasset b/Content/Yotta2025/Medias/graph_V1/DEPART-decaleGO.uasset
new file mode 100644
index 0000000..a524bdc
Binary files /dev/null and b/Content/Yotta2025/Medias/graph_V1/DEPART-decaleGO.uasset differ
diff --git a/Content/Yotta2025/Medias/graph_V1/STREAM-classement.uasset b/Content/Yotta2025/Medias/graph_V1/STREAM-classement.uasset
new file mode 100644
index 0000000..32638e3
Binary files /dev/null and b/Content/Yotta2025/Medias/graph_V1/STREAM-classement.uasset differ
diff --git a/Content/Yotta2025/Medias/graph_V1/STREAM-colonne-base.uasset b/Content/Yotta2025/Medias/graph_V1/STREAM-colonne-base.uasset
new file mode 100644
index 0000000..e8bda31
Binary files /dev/null and b/Content/Yotta2025/Medias/graph_V1/STREAM-colonne-base.uasset differ
diff --git a/Content/Yotta2025/Medias/graph_V1/STREAM-colonne-class.uasset b/Content/Yotta2025/Medias/graph_V1/STREAM-colonne-class.uasset
new file mode 100644
index 0000000..6b40569
Binary files /dev/null and b/Content/Yotta2025/Medias/graph_V1/STREAM-colonne-class.uasset differ
diff --git a/Content/Yotta2025/Medias/graph_V1/STREAM-colonne-class_-_John.uasset b/Content/Yotta2025/Medias/graph_V1/STREAM-colonne-class_-_John.uasset
new file mode 100644
index 0000000..26cc4ff
Binary files /dev/null and b/Content/Yotta2025/Medias/graph_V1/STREAM-colonne-class_-_John.uasset differ
diff --git a/Content/Yotta2025/Medias/graph_V1/STREAM-colonne-coureur.uasset b/Content/Yotta2025/Medias/graph_V1/STREAM-colonne-coureur.uasset
new file mode 100644
index 0000000..d2dbe6e
Binary files /dev/null and b/Content/Yotta2025/Medias/graph_V1/STREAM-colonne-coureur.uasset differ
diff --git a/Content/Yotta2025/Medias/graph_V1/STREAM-commentateurs.uasset b/Content/Yotta2025/Medias/graph_V1/STREAM-commentateurs.uasset
new file mode 100644
index 0000000..22548da
Binary files /dev/null and b/Content/Yotta2025/Medias/graph_V1/STREAM-commentateurs.uasset differ
diff --git a/Content/Yotta2025/Medias/graph_V1/STREAM-split.uasset b/Content/Yotta2025/Medias/graph_V1/STREAM-split.uasset
new file mode 100644
index 0000000..26e9245
Binary files /dev/null and b/Content/Yotta2025/Medias/graph_V1/STREAM-split.uasset differ
diff --git a/Plugins/CommonTime/CommonTime.uplugin b/Plugins/CommonTime/CommonTime.uplugin
new file mode 100644
index 0000000..e8043af
--- /dev/null
+++ b/Plugins/CommonTime/CommonTime.uplugin
@@ -0,0 +1,22 @@
+{
+ "FileVersion": 3,
+ "Version": 1,
+ "VersionName": "0.1.0",
+ "FriendlyName": "CommonTime",
+ "Description": "Provides an editor view for editing DateTime and Timespan values.",
+ "Category": "Editor",
+ "CreatedBy": "MrRobinOfficial",
+ "CreatedByURL": "https://github.com/MrRobinOfficial",
+ "DocsURL": "https://github.com/MrRobinOfficial/Unreal-CommonTime",
+ "MarketplaceURL": "",
+ "SupportURL": "https://github.com/MrRobinOfficial/Unreal-CommonTime/issues",
+ "CanContainContent": true,
+ "Installed": true,
+ "Modules": [
+ {
+ "Name": "CommonTime",
+ "Type": "Editor",
+ "LoadingPhase": "PostEngineInit"
+ }
+ ]
+}
diff --git a/Plugins/CommonTime/Config/FilterPlugin.ini b/Plugins/CommonTime/Config/FilterPlugin.ini
new file mode 100644
index 0000000..ccebca2
--- /dev/null
+++ b/Plugins/CommonTime/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/Plugins/CommonTime/LICENSE.txt b/Plugins/CommonTime/LICENSE.txt
new file mode 100644
index 0000000..90eed77
--- /dev/null
+++ b/Plugins/CommonTime/LICENSE.txt
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2023 MrRobin
+
+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.
diff --git a/Plugins/CommonTime/README.md b/Plugins/CommonTime/README.md
new file mode 100644
index 0000000..f7ee81b
--- /dev/null
+++ b/Plugins/CommonTime/README.md
@@ -0,0 +1,50 @@
+
+
+
+
+
+
CommonTime [Unreal Engine]
+
+
+
+*Provides an editor view for editing DateTime and Timespan values.*
+
+[](https://github.com/mrrobinofficial/unreal-commontime/blob/HEAD/LICENSE.txt)
+
+
+
+
+
+#
+
+## 📋 TODO
+
+* Fix padding issue (not prioritize)
+
+## ⚙️ Supported Platforms
+This plug-in was last built against Unreal Engine 5.3. But should be able to support any version of the engine.
+
+## ⚒️ Installation
+
+You can install from the release section.
+
+Alternatively, you can install this plugin via terminal with [*git*](https://git-scm.com/). **Here is the command for installing it**.
+
+```console
+git clone git@github.com:MrRobinOfficial/Unreal-CommonTime.git CommonTime
+```
+
+#
+
+
+
🖼️ Screenshots
+
From this:
+
+
To this:
+
+
+
+#
+
+## 🆘 Support
+If you have any questions or issue, just write either to my [YouTube channel](https://www.youtube.com/@mrrobinofficial), [Email](mailto:mrrobin123mail@gmail.com) or [Twitter DM](https://twitter.com/MrRobinOfficial).
diff --git a/Plugins/CommonTime/Resources/Icon128.png b/Plugins/CommonTime/Resources/Icon128.png
new file mode 100644
index 0000000..4862a02
Binary files /dev/null and b/Plugins/CommonTime/Resources/Icon128.png differ
diff --git a/Plugins/CommonTime/Resources/Screenshot_01.png b/Plugins/CommonTime/Resources/Screenshot_01.png
new file mode 100644
index 0000000..a7d59a9
Binary files /dev/null and b/Plugins/CommonTime/Resources/Screenshot_01.png differ
diff --git a/Plugins/CommonTime/Resources/Screenshot_02.png b/Plugins/CommonTime/Resources/Screenshot_02.png
new file mode 100644
index 0000000..e871bb9
Binary files /dev/null and b/Plugins/CommonTime/Resources/Screenshot_02.png differ
diff --git a/Plugins/CommonTime/Source/CommonTime/CommonTime.Build.cs b/Plugins/CommonTime/Source/CommonTime/CommonTime.Build.cs
new file mode 100644
index 0000000..440f934
--- /dev/null
+++ b/Plugins/CommonTime/Source/CommonTime/CommonTime.Build.cs
@@ -0,0 +1,30 @@
+// Copyright 2023 MrRobin. All Rights Reserved.
+
+using UnrealBuildTool;
+
+public class CommonTime : ModuleRules
+{
+ public CommonTime(ReadOnlyTargetRules Target) : base(Target)
+ {
+ PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
+
+ PublicDependencyModuleNames.AddRange(new string[]
+ {
+ "Core",
+ });
+
+ PublicDependencyModuleNames.AddRange(new string[]
+ {
+ "CoreUObject",
+ "Engine",
+ "Slate",
+ "SlateCore",
+ "DeveloperSettings",
+ "UnrealEd",
+ "PropertyEditor",
+ "ClassViewer",
+ "InputCore",
+ "BlueprintGraph",
+ });
+ }
+}
diff --git a/Plugins/CommonTime/Source/CommonTime/Private/CommonTimeModule.cpp b/Plugins/CommonTime/Source/CommonTime/Private/CommonTimeModule.cpp
new file mode 100644
index 0000000..f461531
--- /dev/null
+++ b/Plugins/CommonTime/Source/CommonTime/Private/CommonTimeModule.cpp
@@ -0,0 +1,46 @@
+// Copyright 2023 MrRobin. All Rights Reserved.
+
+#include "CommonTimeModule.h"
+
+#include "Modules/ModuleManager.h"
+
+#include "DetailCustomizations/MyDateTimeDetailCustomization.h"
+#include "DetailCustomizations/MyTimespanDetailCustomization.h"
+
+DEFINE_LOG_CATEGORY(LogCommonTime);
+
+class FCommonTimeModule : public IModuleInterface
+{
+public:
+ void StartupModule() override
+ {
+ FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked(TEXT("PropertyEditor"));
+
+ PropertyModule.RegisterCustomPropertyTypeLayout(
+ TEXT("Timespan"),
+ FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FMyTimespanDetailCustomization::MakeInstance)
+ );
+
+ PropertyModule.RegisterCustomPropertyTypeLayout(
+ TEXT("DateTime"),
+ FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FMyDateTimeDetailCustomization::MakeInstance)
+ );
+
+ PropertyModule.NotifyCustomizationModuleChanged();
+ }
+
+ void ShutdownModule() override
+ {
+ if (FModuleManager::Get().IsModuleLoaded(TEXT("PropertyEditor")))
+ {
+ FPropertyEditorModule& PropertyModule = FModuleManager::GetModuleChecked("PropertyEditor");
+
+ PropertyModule.UnregisterCustomPropertyTypeLayout(TEXT("Timespan"));
+ PropertyModule.UnregisterCustomPropertyTypeLayout(TEXT("DateTime"));
+
+ PropertyModule.NotifyCustomizationModuleChanged();
+ }
+ }
+};
+
+IMPLEMENT_MODULE(FCommonTimeModule, CommonTime);
\ No newline at end of file
diff --git a/Plugins/CommonTime/Source/CommonTime/Private/DetailCustomizations/MyDateTimeDetailCustomization.cpp b/Plugins/CommonTime/Source/CommonTime/Private/DetailCustomizations/MyDateTimeDetailCustomization.cpp
new file mode 100644
index 0000000..527b90c
--- /dev/null
+++ b/Plugins/CommonTime/Source/CommonTime/Private/DetailCustomizations/MyDateTimeDetailCustomization.cpp
@@ -0,0 +1,395 @@
+// Copyright 2023 MrRobin. All Rights Reserved.
+
+#include "DetailCustomizations/MyDateTimeDetailCustomization.h"
+
+#include "Containers/Array.h"
+#include "Containers/UnrealString.h"
+#include "DetailWidgetRow.h"
+#include "Fonts/SlateFontInfo.h"
+#include "HAL/PlatformCrt.h"
+#include "Internationalization/Internationalization.h"
+#include "Misc/Attribute.h"
+#include "Misc/DateTime.h"
+#include "PropertyHandle.h"
+#include "Styling/AppStyle.h"
+#include "Styling/ISlateStyle.h"
+#include "UObject/NameTypes.h"
+#include "UObject/UnrealType.h"
+#include "Widgets/DeclarativeSyntaxSupport.h"
+
+#include "DetailLayoutBuilder.h"
+#include "Widgets/Input/SNumericEntryBox.h"
+#include "Widgets/Input/SVectorInputBox.h"
+#include "Widgets/Input/NumericTypeInterface.h"
+#include "Widgets/Input/NumericUnitTypeInterface.inl"
+#include "Math/UnitConversion.h"
+
+// SExpanderArrow
+
+#define LOCTEXT_NAMESPACE "MyDateTimeDetailCustomization"
+
+/* IDetailCustomization interface
+ *****************************************************************************/
+
+void FMyDateTimeDetailCustomization::CustomizeChildren(TSharedRef StructPropertyHandle, class IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils)
+{
+ /* do nothing */
+}
+
+void FMyDateTimeDetailCustomization::CustomizeHeader(TSharedRef StructPropertyHandle, class FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils)
+{
+ PropertyHandle = StructPropertyHandle;
+
+ SAssignNew(YearEntryBox, SNumericEntryBox)
+ .AllowSpin(true)
+ .MinValue(1)
+ .MaxValue(9999)
+ .MinSliderValue(1)
+ .MaxSliderValue(9999)
+ .MinFractionalDigits(0)
+ .MaxFractionalDigits(0)
+ .Font(IDetailLayoutBuilder::GetDetailFont())
+ .Value(this, &FMyDateTimeDetailCustomization::OnGetValue, 3)
+ .OnValueChanged(this, &FMyDateTimeDetailCustomization::OnValueChanged, 3)
+ .OnValueCommitted(this, &FMyDateTimeDetailCustomization::OnValueCommitted, 3)
+ .OnBeginSliderMovement(this, &FMyDateTimeDetailCustomization::OnBeginSliderMovement)
+ .OnEndSliderMovement(this, &FMyDateTimeDetailCustomization::OnEndSliderMovement)
+ .TypeInterface(MakeShareable(new TNumericUnitTypeInterface(EUnit::Years)))
+ .LinearDeltaSensitivity(1);
+
+ SAssignNew(MonthEntryBox, SNumericEntryBox)
+ .AllowSpin(true)
+ .MinValue(1)
+ .MaxValue(12)
+ .MinSliderValue(1)
+ .MaxSliderValue(12)
+ .MinFractionalDigits(0)
+ .MaxFractionalDigits(0)
+ .Font(IDetailLayoutBuilder::GetDetailFont())
+ .Value(this, &FMyDateTimeDetailCustomization::OnGetValue, 4)
+ .OnValueChanged(this, &FMyDateTimeDetailCustomization::OnValueChanged, 4)
+ .OnValueCommitted(this, &FMyDateTimeDetailCustomization::OnValueCommitted, 4)
+ .OnBeginSliderMovement(this, &FMyDateTimeDetailCustomization::OnBeginSliderMovement)
+ .OnEndSliderMovement(this, &FMyDateTimeDetailCustomization::OnEndSliderMovement)
+ .TypeInterface(MakeShareable(new TNumericUnitTypeInterface(EUnit::Months)))
+ .LinearDeltaSensitivity(1);
+
+ SAssignNew(DayEntryBox, SNumericEntryBox)
+ .AllowSpin(true)
+ .MinValue(1)
+ .MaxValue(31)
+ .MinSliderValue(1)
+ .MaxSliderValue(31)
+ .MinFractionalDigits(0)
+ .MaxFractionalDigits(0)
+ .Font(IDetailLayoutBuilder::GetDetailFont())
+ .Value(this, &FMyDateTimeDetailCustomization::OnGetValue, 5)
+ .OnValueChanged(this, &FMyDateTimeDetailCustomization::OnValueChanged, 5)
+ .OnValueCommitted(this, &FMyDateTimeDetailCustomization::OnValueCommitted, 5)
+ .OnBeginSliderMovement(this, &FMyDateTimeDetailCustomization::OnBeginSliderMovement)
+ .OnEndSliderMovement(this, &FMyDateTimeDetailCustomization::OnEndSliderMovement)
+ .TypeInterface(MakeShareable(new TNumericUnitTypeInterface(EUnit::Days)))
+ .LinearDeltaSensitivity(1);
+
+ SAssignNew(HourEntryBox, SNumericEntryBox)
+ .AllowSpin(true)
+ .MinValue(0)
+ .MaxValue(23)
+ .MinSliderValue(0)
+ .MaxSliderValue(23)
+ .MinFractionalDigits(0)
+ .MaxFractionalDigits(0)
+ .Font(IDetailLayoutBuilder::GetDetailFont())
+ .Value(this, &FMyDateTimeDetailCustomization::OnGetValue, 0)
+ .OnValueChanged(this, &FMyDateTimeDetailCustomization::OnValueChanged, 0)
+ .OnValueCommitted(this, &FMyDateTimeDetailCustomization::OnValueCommitted, 0)
+ .OnBeginSliderMovement(this, &FMyDateTimeDetailCustomization::OnBeginSliderMovement)
+ .OnEndSliderMovement(this, &FMyDateTimeDetailCustomization::OnEndSliderMovement)
+ .TypeInterface(MakeShareable(new TNumericUnitTypeInterface(EUnit::Hours)))
+ .LinearDeltaSensitivity(1);
+
+ SAssignNew(MinuteEntryBox, SNumericEntryBox)
+ .AllowSpin(true)
+ .MinValue(0)
+ .MaxValue(59)
+ .MinSliderValue(0)
+ .MaxSliderValue(59)
+ .MinFractionalDigits(0)
+ .MaxFractionalDigits(0)
+ .Font(IDetailLayoutBuilder::GetDetailFont())
+ .Value(this, &FMyDateTimeDetailCustomization::OnGetValue, 1)
+ .OnValueChanged(this, &FMyDateTimeDetailCustomization::OnValueChanged, 1)
+ .OnValueCommitted(this, &FMyDateTimeDetailCustomization::OnValueCommitted, 1)
+ .OnBeginSliderMovement(this, &FMyDateTimeDetailCustomization::OnBeginSliderMovement)
+ .OnEndSliderMovement(this, &FMyDateTimeDetailCustomization::OnEndSliderMovement)
+ .TypeInterface(MakeShareable(new TNumericUnitTypeInterface(EUnit::Minutes)))
+ .LinearDeltaSensitivity(1);
+
+ SAssignNew(SecondEntryBox, SNumericEntryBox)
+ .AllowSpin(true)
+ .MinValue(0)
+ .MaxValue(59)
+ .MinSliderValue(0)
+ .MaxSliderValue(59)
+ .MinFractionalDigits(0)
+ .MaxFractionalDigits(0)
+ .Font(IDetailLayoutBuilder::GetDetailFont())
+ .Value(this, &FMyDateTimeDetailCustomization::OnGetValue, 2)
+ .OnValueChanged(this, &FMyDateTimeDetailCustomization::OnValueChanged, 2)
+ .OnValueCommitted(this, &FMyDateTimeDetailCustomization::OnValueCommitted, 2)
+ .OnBeginSliderMovement(this, &FMyDateTimeDetailCustomization::OnBeginSliderMovement)
+ .OnEndSliderMovement(this, &FMyDateTimeDetailCustomization::OnEndSliderMovement)
+ .TypeInterface(MakeShareable(new TNumericUnitTypeInterface(EUnit::Seconds)))
+ .LinearDeltaSensitivity(1);
+
+ HeaderRow
+ .NameContent()
+ [
+ StructPropertyHandle->CreatePropertyNameWidget()
+ ]
+ .ValueContent()
+ .MinDesiredWidth(125.0f * 3.0f)
+ .MaxDesiredWidth(125.0f * 3.0f)
+ [
+ SNew(SVerticalBox)
+ + SVerticalBox::Slot()
+ .AutoHeight()
+ [
+ SNew(SHorizontalBox)
+ + SHorizontalBox::Slot()
+ .Padding(0.0f, 0.0f, 2.0f, 0.0f)
+ [
+ YearEntryBox.ToSharedRef()
+ ]
+ + SHorizontalBox::Slot()
+ .Padding(2.0f, 0.0f)
+ [
+ MonthEntryBox.ToSharedRef()
+ ]
+ + SHorizontalBox::Slot()
+ .Padding(2.0f, 0.0f, 0.0f, 0.0f)
+ [
+ DayEntryBox.ToSharedRef()
+ ]
+ ]
+ + SVerticalBox::Slot()
+ .AutoHeight()
+ [
+ SNew(SSpacer)
+ .Size(FVector2D(8.0f))
+ ]
+ + SVerticalBox::Slot()
+ .AutoHeight()
+ [
+ SNew(SHorizontalBox)
+ + SHorizontalBox::Slot()
+ .Padding(0.0f, 0.0f, 2.0f, 0.0f)
+ [
+ HourEntryBox.ToSharedRef()
+ ]
+ + SHorizontalBox::Slot()
+ .Padding(2.0f, 0.0f)
+ [
+ MinuteEntryBox.ToSharedRef()
+ ]
+ + SHorizontalBox::Slot()
+ .Padding(2.0f, 0.0f, 0.0f, 0.0f)
+ [
+ SecondEntryBox.ToSharedRef()
+ ]
+ ]
+ ];
+}
+
+
+/* FMyDateTimeDetailCustomization callbacks
+ *****************************************************************************/
+
+TOptional FMyDateTimeDetailCustomization::OnGetValue(int32 Index) const
+{
+ TArray RawData;
+ PropertyHandle->AccessRawData(RawData);
+
+ if (RawData.Num() != 1)
+ return TOptional();
+
+ if (RawData[0] == nullptr)
+ return TOptional();
+
+ auto* DateTime = ((FDateTime*)RawData[0]);
+
+ switch (Index)
+ {
+ case 0:
+ return TOptional(DateTime->GetHour());
+
+ case 1:
+ return TOptional(DateTime->GetMinute());
+
+ case 2:
+ return TOptional(DateTime->GetSecond());
+
+ case 3:
+ return TOptional(DateTime->GetYear());
+
+ case 4:
+ return TOptional(DateTime->GetMonth());
+
+ case 5:
+ return TOptional(DateTime->GetDay());
+
+ default:
+ return TOptional();
+ }
+}
+
+void FMyDateTimeDetailCustomization::OnBeginSliderMovement()
+{
+ bIsUsingSlider = true;
+
+ GEditor->BeginTransaction(
+ FText::Format(
+ NSLOCTEXT("MyDateTimeDetailCustomization", "SetDateTimeProperty", "Edit {0}"),
+ PropertyHandle->GetPropertyDisplayName())
+ );
+}
+
+void FMyDateTimeDetailCustomization::OnEndSliderMovement(double NewValue)
+{
+ bIsUsingSlider = false;
+
+ GEditor->EndTransaction();
+}
+
+void FMyDateTimeDetailCustomization::OnValueCommitted(double NewValue, ETextCommit::Type CommitType, int32 Index)
+{
+ NewValue = FMath::CeilToDouble(NewValue);
+
+ TArray RawData;
+
+ PropertyHandle->AccessRawData(RawData);
+ PropertyHandle->NotifyPreChange();
+
+ for (auto RawDataInstance : RawData)
+ {
+ auto* DateTime = (FDateTime*)RawDataInstance;
+
+ int32 Year = DateTime->GetYear();
+ int32 Month = DateTime->GetMonth();
+ int32 Day = DateTime->GetDay();
+
+ int32 Hour = DateTime->GetHour();
+ int32 Minute = DateTime->GetMinute();
+ int32 Second = DateTime->GetSecond();
+
+ int32 MaxDays = FDateTime::DaysInMonth(Year, Month);
+
+ switch (Index)
+ {
+ case 0:
+ *DateTime = FDateTime(Year, Month, Day > MaxDays ? MaxDays : Day, NewValue, Minute, Second);
+ break;
+
+ case 1:
+ *DateTime = FDateTime(Year, Month, Day > MaxDays ? MaxDays : Day, Hour, NewValue, Second);
+ break;
+
+ case 2:
+ *DateTime = FDateTime(Year, Month, Day > MaxDays ? MaxDays : Day, Hour, Minute, NewValue);
+ break;
+
+ case 3:
+ {
+ MaxDays = FDateTime::DaysInMonth(NewValue, Month);
+ *DateTime = FDateTime(NewValue, Month, Day > MaxDays ? MaxDays : Day, Hour, Minute, Second);
+ }
+ break;
+
+ case 4:
+ {
+ MaxDays = FDateTime::DaysInMonth(Year, NewValue);
+ *DateTime = FDateTime(Year, NewValue, Day > MaxDays ? MaxDays : Day, Hour, Minute, Second);
+ }
+ break;
+
+ case 5:
+ *DateTime = FDateTime(Year, Month, NewValue > MaxDays ? MaxDays : NewValue, Hour, Minute, Second);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ PropertyHandle->NotifyPostChange(EPropertyChangeType::ValueSet);
+ PropertyHandle->NotifyFinishedChangingProperties();
+}
+
+void FMyDateTimeDetailCustomization::OnValueChanged(double NewValue, int32 Index)
+{
+ if (!bIsUsingSlider)
+ return;
+
+ NewValue = FMath::CeilToDouble(NewValue);
+
+ TArray RawData;
+
+ PropertyHandle->AccessRawData(RawData);
+ PropertyHandle->NotifyPreChange();
+
+ for (auto RawDataInstance : RawData)
+ {
+ auto* DateTime = (FDateTime*)RawDataInstance;
+
+ int32 Year = DateTime->GetYear();
+ int32 Month = DateTime->GetMonth();
+ int32 Day = DateTime->GetDay();
+
+ int32 Hour = DateTime->GetHour();
+ int32 Minute = DateTime->GetMinute();
+ int32 Second = DateTime->GetSecond();
+
+ int32 MaxDays = FDateTime::DaysInMonth(Year, Month);
+
+ switch (Index)
+ {
+ case 0:
+ *DateTime = FDateTime(Year, Month, Day > MaxDays ? MaxDays : Day, NewValue, Minute, Second);
+ break;
+
+ case 1:
+ *DateTime = FDateTime(Year, Month, Day > MaxDays ? MaxDays : Day, Hour, NewValue, Second);
+ break;
+
+ case 2:
+ *DateTime = FDateTime(Year, Month, Day > MaxDays ? MaxDays : Day, Hour, Minute, NewValue);
+ break;
+
+ case 3:
+ {
+ MaxDays = FDateTime::DaysInMonth(NewValue, Month);
+ *DateTime = FDateTime(NewValue, Month, Day > MaxDays ? MaxDays : Day, Hour, Minute, Second);
+ }
+ break;
+
+ case 4:
+ {
+ MaxDays = FDateTime::DaysInMonth(Year, NewValue);
+ *DateTime = FDateTime(Year, NewValue, Day > MaxDays ? MaxDays : Day, Hour, Minute, Second);
+ }
+ break;
+
+ case 5:
+ *DateTime = FDateTime(Year, Month, NewValue > MaxDays ? MaxDays : NewValue, Hour, Minute, Second);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ PropertyHandle->NotifyPostChange(EPropertyChangeType::ValueSet);
+ PropertyHandle->NotifyFinishedChangingProperties();
+}
+
+#undef LOCTEXT_NAMESPACE
diff --git a/Plugins/CommonTime/Source/CommonTime/Private/DetailCustomizations/MyTimespanDetailCustomization.cpp b/Plugins/CommonTime/Source/CommonTime/Private/DetailCustomizations/MyTimespanDetailCustomization.cpp
new file mode 100644
index 0000000..b4e9f15
--- /dev/null
+++ b/Plugins/CommonTime/Source/CommonTime/Private/DetailCustomizations/MyTimespanDetailCustomization.cpp
@@ -0,0 +1,256 @@
+// Copyright 2023 MrRobin. All Rights Reserved.
+
+#include "DetailCustomizations/MyTimespanDetailCustomization.h"
+
+#include "Containers/Array.h"
+#include "Containers/UnrealString.h"
+#include "DetailWidgetRow.h"
+#include "Fonts/SlateFontInfo.h"
+#include "HAL/PlatformCrt.h"
+#include "Internationalization/Internationalization.h"
+#include "Misc/Attribute.h"
+#include "Misc/Timespan.h"
+#include "PropertyHandle.h"
+#include "Styling/AppStyle.h"
+#include "Styling/ISlateStyle.h"
+#include "UObject/NameTypes.h"
+#include "UObject/UnrealType.h"
+#include "Widgets/DeclarativeSyntaxSupport.h"
+
+#include "DetailLayoutBuilder.h"
+#include "Widgets/Input/SNumericEntryBox.h"
+#include "Widgets/Input/SVectorInputBox.h"
+#include "Widgets/Input/NumericTypeInterface.h"
+#include "Widgets/Input/NumericUnitTypeInterface.inl"
+#include "Math/UnitConversion.h"
+
+#define LOCTEXT_NAMESPACE "MyTimespanDetailCustomization"
+
+/* IDetailCustomization interface
+ *****************************************************************************/
+
+void FMyTimespanDetailCustomization::CustomizeChildren(
+ TSharedRef StructPropertyHandle,
+ class IDetailChildrenBuilder& StructBuilder,
+ IPropertyTypeCustomizationUtils& StructCustomizationUtils)
+{
+ /* do nothing */
+}
+
+void FMyTimespanDetailCustomization::CustomizeHeader(
+ TSharedRef StructPropertyHandle,
+ class FDetailWidgetRow& HeaderRow,
+ IPropertyTypeCustomizationUtils& StructCustomizationUtils)
+{
+ PropertyHandle = StructPropertyHandle;
+
+ SAssignNew(HourEntryBox, SNumericEntryBox)
+ .AllowSpin(true)
+ .MinValue(0)
+ .MaxValue(23)
+ .MinSliderValue(0)
+ .MaxSliderValue(23)
+ .MinFractionalDigits(0)
+ .MaxFractionalDigits(0)
+ .Font(IDetailLayoutBuilder::GetDetailFont())
+ .Value(this, &FMyTimespanDetailCustomization::OnGetValue, 0)
+ .OnValueChanged(this, &FMyTimespanDetailCustomization::OnValueChanged, 0)
+ .OnValueCommitted(this, &FMyTimespanDetailCustomization::OnValueCommitted, 0)
+ .OnBeginSliderMovement(this, &FMyTimespanDetailCustomization::OnBeginSliderMovement)
+ .OnEndSliderMovement(this, &FMyTimespanDetailCustomization::OnEndSliderMovement)
+ .TypeInterface(MakeShareable(new TNumericUnitTypeInterface(EUnit::Hours)))
+ .LinearDeltaSensitivity(1);
+
+ SAssignNew(MinuteEntryBox, SNumericEntryBox)
+ .AllowSpin(true)
+ .MinValue(0)
+ .MaxValue(59)
+ .MinSliderValue(0)
+ .MaxSliderValue(59)
+ .MinFractionalDigits(0)
+ .MaxFractionalDigits(0)
+ .Font(IDetailLayoutBuilder::GetDetailFont())
+ .Value(this, &FMyTimespanDetailCustomization::OnGetValue, 1)
+ .OnValueChanged(this, &FMyTimespanDetailCustomization::OnValueChanged, 1)
+ .OnValueCommitted(this, &FMyTimespanDetailCustomization::OnValueCommitted, 1)
+ .OnBeginSliderMovement(this, &FMyTimespanDetailCustomization::OnBeginSliderMovement)
+ .OnEndSliderMovement(this, &FMyTimespanDetailCustomization::OnEndSliderMovement)
+ .TypeInterface(MakeShareable(new TNumericUnitTypeInterface(EUnit::Minutes)))
+ .LinearDeltaSensitivity(1);
+
+ SAssignNew(SecondEntryBox, SNumericEntryBox)
+ .AllowSpin(true)
+ .MinValue(0)
+ .MaxValue(59)
+ .MinSliderValue(0)
+ .MaxSliderValue(59)
+ .MinFractionalDigits(0)
+ .MaxFractionalDigits(0)
+ .Font(IDetailLayoutBuilder::GetDetailFont())
+ .Value(this, &FMyTimespanDetailCustomization::OnGetValue, 2)
+ .OnValueChanged(this, &FMyTimespanDetailCustomization::OnValueChanged, 2)
+ .OnValueCommitted(this, &FMyTimespanDetailCustomization::OnValueCommitted, 2)
+ .OnBeginSliderMovement(this, &FMyTimespanDetailCustomization::OnBeginSliderMovement)
+ .OnEndSliderMovement(this, &FMyTimespanDetailCustomization::OnEndSliderMovement)
+ .TypeInterface(MakeShareable(new TNumericUnitTypeInterface(EUnit::Seconds)))
+ .LinearDeltaSensitivity(1);
+
+ HeaderRow
+ .NameContent()
+ [
+ StructPropertyHandle->CreatePropertyNameWidget()
+ ]
+ .ValueContent()
+ .MinDesiredWidth(125.0f * 3.0f)
+ .MaxDesiredWidth(125.0f * 3.0f)
+ [
+ SNew(SHorizontalBox)
+ + SHorizontalBox::Slot()
+ .Padding(0.0f)
+ [
+ HourEntryBox.ToSharedRef()
+ ]
+ + SHorizontalBox::Slot()
+ .Padding(2.5f, 0.0f)
+ [
+ MinuteEntryBox.ToSharedRef()
+ ]
+ + SHorizontalBox::Slot()
+ .Padding(0.0f)
+ [
+ SecondEntryBox.ToSharedRef()
+ ]
+ ];
+}
+
+
+/* FMyTimespanDetailCustomization callbacks
+ *****************************************************************************/
+
+TOptional FMyTimespanDetailCustomization::OnGetValue(int32 Index) const
+{
+ TArray RawData;
+ PropertyHandle->AccessRawData(RawData);
+
+ if (RawData.Num() != 1)
+ return TOptional();
+
+ if (RawData[0] == nullptr)
+ return TOptional();
+
+ auto* Timespan = ((FTimespan*)RawData[0]);
+
+ switch (Index)
+ {
+ case 0:
+ return TOptional(Timespan->GetHours());
+
+ case 1:
+ return TOptional(Timespan->GetMinutes());
+
+ case 2:
+ return TOptional(Timespan->GetSeconds());
+
+ default:
+ return TOptional();
+ }
+}
+
+void FMyTimespanDetailCustomization::OnBeginSliderMovement()
+{
+ bIsUsingSlider = true;
+
+ GEditor->BeginTransaction(FText::Format(NSLOCTEXT("MyTimespanDetailCustomization", "SetTimespanProperty", "Edit {0}"), PropertyHandle->GetPropertyDisplayName()));
+}
+
+void FMyTimespanDetailCustomization::OnEndSliderMovement(double NewValue)
+{
+ bIsUsingSlider = false;
+
+ GEditor->EndTransaction();
+}
+
+void FMyTimespanDetailCustomization::OnValueCommitted(double NewValue, ETextCommit::Type CommitType, int32 Index)
+{
+ NewValue = FMath::CeilToDouble(NewValue);
+
+ TArray RawData;
+
+ PropertyHandle->AccessRawData(RawData);
+ PropertyHandle->NotifyPreChange();
+
+ for (auto RawDataInstance : RawData)
+ {
+ auto* Timespan = (FTimespan*)RawDataInstance;
+
+ int32 Hours = Timespan->GetHours();
+ int32 Minutes = Timespan->GetMinutes();
+ int32 Seconds = Timespan->GetSeconds();
+
+ switch (Index)
+ {
+ case 0:
+ *Timespan = FTimespan(NewValue, Minutes, Seconds);
+ break;
+
+ case 1:
+ *Timespan = FTimespan(Hours, NewValue, Seconds);
+ break;
+
+ case 2:
+ *Timespan = FTimespan(Hours, Minutes, NewValue);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ PropertyHandle->NotifyPostChange(EPropertyChangeType::ValueSet);
+ PropertyHandle->NotifyFinishedChangingProperties();
+}
+
+void FMyTimespanDetailCustomization::OnValueChanged(double NewValue, int32 Index)
+{
+ if (!bIsUsingSlider)
+ return;
+
+ NewValue = FMath::CeilToDouble(NewValue);
+
+ TArray RawData;
+
+ PropertyHandle->AccessRawData(RawData);
+ PropertyHandle->NotifyPreChange();
+
+ for (auto RawDataInstance : RawData)
+ {
+ auto* Timespan = (FTimespan*)RawDataInstance;
+
+ int32 Hours = Timespan->GetHours();
+ int32 Minutes = Timespan->GetMinutes();
+ int32 Seconds = Timespan->GetSeconds();
+
+ switch (Index)
+ {
+ case 0:
+ *Timespan = FTimespan(NewValue, Minutes, Seconds);
+ break;
+
+ case 1:
+ *Timespan = FTimespan(Hours, NewValue, Seconds);
+ break;
+
+ case 2:
+ *Timespan = FTimespan(Hours, Minutes, NewValue);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ PropertyHandle->NotifyPostChange(EPropertyChangeType::ValueSet);
+ PropertyHandle->NotifyFinishedChangingProperties();
+}
+
+#undef LOCTEXT_NAMESPACE
diff --git a/Plugins/CommonTime/Source/CommonTime/Public/CommonTimeModule.h b/Plugins/CommonTime/Source/CommonTime/Public/CommonTimeModule.h
new file mode 100644
index 0000000..efc4b93
--- /dev/null
+++ b/Plugins/CommonTime/Source/CommonTime/Public/CommonTimeModule.h
@@ -0,0 +1,7 @@
+// Copyright 2023 MrRobin. All Rights Reserved.
+
+#pragma once
+
+#include "Logging/LogMacros.h"
+
+DECLARE_LOG_CATEGORY_EXTERN(LogCommonTime, Log, All);
\ No newline at end of file
diff --git a/Plugins/CommonTime/Source/CommonTime/Public/DetailCustomizations/MyDateTimeDetailCustomization.h b/Plugins/CommonTime/Source/CommonTime/Public/DetailCustomizations/MyDateTimeDetailCustomization.h
new file mode 100644
index 0000000..26ea689
--- /dev/null
+++ b/Plugins/CommonTime/Source/CommonTime/Public/DetailCustomizations/MyDateTimeDetailCustomization.h
@@ -0,0 +1,58 @@
+// Copyright 2023 MrRobin. All Rights Reserved.
+
+#pragma once
+
+#include "IPropertyTypeCustomization.h"
+#include "Internationalization/Text.h"
+#include "Styling/SlateColor.h"
+#include "Templates/SharedPointer.h"
+#include "Types/SlateEnums.h"
+
+class IPropertyHandle;
+class SEditableTextBox;
+
+/**
+ * Implements a details view customization for the FDateTime structure.
+ */
+class FMyDateTimeDetailCustomization
+ : public IPropertyTypeCustomization
+{
+public:
+public:
+ /**
+ * Creates an instance of this class.
+ *
+ * @return The new instance.
+ */
+ static TSharedRef MakeInstance()
+ {
+ return MakeShareable(new FMyDateTimeDetailCustomization());
+ }
+
+public:
+ // IPropertyTypeCustomization interface
+ void CustomizeChildren(TSharedRef StructPropertyHandle, class IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override;
+
+ void CustomizeHeader(TSharedRef StructPropertyHandle, class FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override;
+
+private:
+ TOptional OnGetValue(int32 Index) const;
+ void OnValueCommitted(double NewValue, ETextCommit::Type CommitType, int32 Index);
+ void OnValueChanged(double NewValue, int32 Index);
+ void OnBeginSliderMovement();
+ void OnEndSliderMovement(double NewValue);
+
+private:
+ /** Holds a handle to the property being edited. */
+ TSharedPtr PropertyHandle;
+
+ /** True if a value is being changed by dragging a slider */
+ bool bIsUsingSlider;
+
+ TSharedPtr YearEntryBox;
+ TSharedPtr MonthEntryBox;
+ TSharedPtr DayEntryBox;
+ TSharedPtr HourEntryBox;
+ TSharedPtr MinuteEntryBox;
+ TSharedPtr SecondEntryBox;
+};
diff --git a/Plugins/CommonTime/Source/CommonTime/Public/DetailCustomizations/MyTimespanDetailCustomization.h b/Plugins/CommonTime/Source/CommonTime/Public/DetailCustomizations/MyTimespanDetailCustomization.h
new file mode 100644
index 0000000..4cc1c4c
--- /dev/null
+++ b/Plugins/CommonTime/Source/CommonTime/Public/DetailCustomizations/MyTimespanDetailCustomization.h
@@ -0,0 +1,54 @@
+// Copyright 2023 MrRobin. All Rights Reserved.
+
+#pragma once
+
+#include "IPropertyTypeCustomization.h"
+#include "Internationalization/Text.h"
+#include "Styling/SlateColor.h"
+#include "Templates/SharedPointer.h"
+#include "Types/SlateEnums.h"
+
+class IPropertyHandle;
+class SEditableTextBox;
+
+/**
+ * Implements a details view customization for the FTimespan structure.
+ */
+class FMyTimespanDetailCustomization
+ : public IPropertyTypeCustomization
+{
+public:
+ /**
+ * Creates an instance of this class.
+ *
+ * @return The new instance.
+ */
+ static TSharedRef MakeInstance()
+ {
+ return MakeShareable(new FMyTimespanDetailCustomization());
+ }
+
+public:
+ // IPropertyTypeCustomization interface
+ void CustomizeChildren(TSharedRef StructPropertyHandle, class IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override;
+
+ void CustomizeHeader(TSharedRef StructPropertyHandle, class FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override;
+
+private:
+ TOptional OnGetValue(int32 Index) const;
+ void OnValueCommitted(double NewValue, ETextCommit::Type CommitType, int32 Index);
+ void OnValueChanged(double NewValue, int32 Index);
+ void OnBeginSliderMovement();
+ void OnEndSliderMovement(double NewValue);
+
+private:
+ /** Holds a handle to the property being edited. */
+ TSharedPtr PropertyHandle;
+
+ /** True if a value is being changed by dragging a slider */
+ bool bIsUsingSlider;
+
+ TSharedPtr HourEntryBox;
+ TSharedPtr MinuteEntryBox;
+ TSharedPtr SecondEntryBox;
+};
diff --git a/Plugins/DTFluxAPI/Config/DefaultDTFluxAPI.ini b/Plugins/DTFluxAPI/Config/DefaultDTFluxAPI.ini
new file mode 100644
index 0000000..a6e6533
--- /dev/null
+++ b/Plugins/DTFluxAPI/Config/DefaultDTFluxAPI.ini
@@ -0,0 +1,4 @@
+[CoreRedirects]
++ClassRedirects=(OldName="/Script/DTFluxAPI.DTHttpServerObject",NewName="/Script/DTFluxAPI.DTFluxHttpServerObject")
++StructRedirects=(OldName="/Script/DTFluxAPI.DTHttpServerParams",NewName="/Script/DTFluxAPI.DTFluxHttpServerParams")
++ClassRedirects=(OldName="/Script/DTFluxAPI.MyClass",NewName="/Script/DTFluxAPI.DTFluxDataStorage")
\ No newline at end of file
diff --git a/Plugins/DTFluxAPI/DOCS.md b/Plugins/DTFluxAPI/DOCS.md
new file mode 100644
index 0000000..5d776e4
--- /dev/null
+++ b/Plugins/DTFluxAPI/DOCS.md
@@ -0,0 +1,75 @@
+# DTFluxAPI plugin doc
+
+## DESCRIPTION
+
+This document describe the usage of the plugin.
+
+## USAGE
+### Plugin Project Settings
+
+#### Race Result API
+
+- Race Result Port (int) : Port of the Race Result API (Local Server ONLY)
+- Race Result Url (FString) : Race Result API URL (Local Server ONLY)
+- Access Is Local (bool) : Race Result API only in local Mode (prevent polling limits)
+- RaceResultSessionID (FString) : Race result session ID
+- Start List Access Token (FString) : Token to access Start List endpoint
+- General Classification Access Token (FString) : Token to access General Classification endpoint
+- Live Stage Results Access Token (FString) : Token to access Live Stage Results endpoint
+
+#### Chrono Proxy
+
+- ProxyAddress
+- ProxyRootPath
+- ProxyPort
+
+#### Server Config
+
+- InPort (int) : Listening port of the embedded server
+- Endpoints (FString[] ) : Array of endpoints to be served (Wil be modified in the future)
+
+#### Objects provided
+
+- EDTFluxProxyRoute : Routes type for the Proxy
+- EDTFluxAPIRoute : Routes type for the API Race Result
+- FSearchFilters : struct that contain a ContestId, a StageId and a gender to filter api searches
+
+#### Functions provided
+
+All the function provided by the Project Settings are available both in blueprint and in c++
+
+- FString GetAPIPath(APIRouteType, FString Filters )
+- GetAPIPathFiltered(APIRouteType, FSearchFilters Filters )
+- GetProxyPath( ProxyRouteType, int ContestId, int StageId)
+
+
+### Model
+
+### DataStorage
+
+
+#### Public Data
+
+##### Collections
+
+###### Chrono
+
+- Chrono of each stage (count down)
+- Current Stage and current contest
+
+###### Participant
+
+- Current progression (pourcentage done in stage -> number of checkpoints done)
+-
+
+
+
+
+### Subsystem
+
+
+##### Delegates :
+
+-
+
+-
diff --git a/Plugins/DTFluxAPI/DTFluxAPI.uplugin b/Plugins/DTFluxAPI/DTFluxAPI.uplugin
new file mode 100644
index 0000000..a278bb5
--- /dev/null
+++ b/Plugins/DTFluxAPI/DTFluxAPI.uplugin
@@ -0,0 +1,30 @@
+{
+ "FileVersion": 3,
+ "Version": 1,
+ "VersionName": "1.0",
+ "FriendlyName": "DTFluxAPI",
+ "Description": "DTFlux API plugin",
+ "Category": "Other",
+ "CreatedBy": "Ange-Marie MAURIN",
+ "CreatedByURL": "",
+ "DocsURL": "",
+ "MarketplaceURL": "",
+ "SupportURL": "",
+ "CanContainContent": true,
+ "IsBetaVersion": false,
+ "IsExperimentalVersion": false,
+ "Installed": false,
+ "Modules": [
+ {
+ "Name": "DTFluxAPI",
+ "Type": "Runtime",
+ "LoadingPhase": "Default"
+ }
+ ],
+ "Plugins": [
+ {
+ "Name": "Avalanche",
+ "Enabled": true
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Plugins/DTFluxAPI/LICENSE b/Plugins/DTFluxAPI/LICENSE
new file mode 100644
index 0000000..f288702
--- /dev/null
+++ b/Plugins/DTFluxAPI/LICENSE
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
diff --git a/Plugins/DTFluxAPI/README.md b/Plugins/DTFluxAPI/README.md
new file mode 100644
index 0000000..d18269a
--- /dev/null
+++ b/Plugins/DTFluxAPI/README.md
@@ -0,0 +1 @@
+# DTFluxAPI Plugin for UNREAL ENGINE (version 5.4.2)
diff --git a/Plugins/DTFluxAPI/Resources/Icon128.png b/Plugins/DTFluxAPI/Resources/Icon128.png
new file mode 100644
index 0000000..1231d4a
Binary files /dev/null and b/Plugins/DTFluxAPI/Resources/Icon128.png differ
diff --git a/Plugins/DTFluxAPI/Source/DTFluxAPI/DTFluxAPI.Build.cs b/Plugins/DTFluxAPI/Source/DTFluxAPI/DTFluxAPI.Build.cs
new file mode 100644
index 0000000..3c944de
--- /dev/null
+++ b/Plugins/DTFluxAPI/Source/DTFluxAPI/DTFluxAPI.Build.cs
@@ -0,0 +1,44 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+
+using UnrealBuildTool;
+
+public class DTFluxAPI : ModuleRules
+{
+ public DTFluxAPI(ReadOnlyTargetRules Target) : base(Target)
+ {
+ PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
+
+ PublicDependencyModuleNames.AddRange(
+ new string[]
+ {
+ "Core", "AvalancheText",
+ }
+ );
+
+ PrivateDependencyModuleNames.AddRange(
+ new string[]
+ {
+ "CoreUObject",
+ "Engine",
+ "EditorSubsystem",
+ "EditorFramework",
+ "Slate",
+ "SlateCore",
+ "HTTPServer",
+ "HTTP",
+ "Networking",
+ "WebSockets",
+ "WebSocketNetworking",
+ "DeveloperToolSettings",
+ "DeveloperSettings",
+ "Json",
+ "JsonUtilities",
+ "SlateCore",
+ "Text3D",
+ "AvalancheCore",
+ "AvalancheMedia",
+ "AvalancheText",
+ }
+ );
+ }
+}
diff --git a/Plugins/DTFluxAPI/Source/DTFluxAPI/Private/DTFluxAPI.cpp b/Plugins/DTFluxAPI/Source/DTFluxAPI/Private/DTFluxAPI.cpp
new file mode 100644
index 0000000..dacdf82
--- /dev/null
+++ b/Plugins/DTFluxAPI/Source/DTFluxAPI/Private/DTFluxAPI.cpp
@@ -0,0 +1,22 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+
+#include "DTFluxAPI.h"
+#include "DTFluxAPILog.h"
+
+#define LOCTEXT_NAMESPACE "FDTFluxAPIModule"
+DEFINE_LOG_CATEGORY(LogDTFluxAPI);
+
+void FDTFluxAPIModule::StartupModule()
+{
+ // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
+}
+
+void FDTFluxAPIModule::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.
+}
+
+#undef LOCTEXT_NAMESPACE
+
+IMPLEMENT_MODULE(FDTFluxAPIModule, DTFluxAPI)
\ No newline at end of file
diff --git a/Plugins/DTFluxAPI/Source/DTFluxAPI/Private/DTFluxCountDown/DTFluxCountDownComponent.cpp b/Plugins/DTFluxAPI/Source/DTFluxAPI/Private/DTFluxCountDown/DTFluxCountDownComponent.cpp
new file mode 100644
index 0000000..1d0ec3b
--- /dev/null
+++ b/Plugins/DTFluxAPI/Source/DTFluxAPI/Private/DTFluxCountDown/DTFluxCountDownComponent.cpp
@@ -0,0 +1,92 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+
+#include "DTFluxCountDown/DTFluxCountDownComponent.h"
+
+#include "DTFluxAPILog.h"
+#include "DTFluxSubsystem/DTFluxSubsystem.h"
+
+
+// Sets default values for this component's properties
+UDTFluxCountDownComponent::UDTFluxCountDownComponent()
+{
+ // Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features
+ // off to improve performance if you don't need them.
+ PrimaryComponentTick.bCanEverTick = true;
+ if(FModuleManager::Get().IsModuleLoaded("DTFluxApi"))
+ {
+ DataStorage = GEngine->GetEngineSubsystem()->GetDataStorage();
+ UE_LOG(LogDTFluxAPI, Log, TEXT("DTFluxApi loaded"))
+ }else
+ {
+ UE_LOG(LogDTFluxAPI, Error, TEXT("DTFluxApi Not Loaded"))
+ }
+ // ...
+}
+
+
+// Called when the game starts
+void UDTFluxCountDownComponent::BeginPlay()
+{
+ Super::BeginPlay();
+
+
+ UWorld* World = GetWorld();
+ if(World)
+ {
+ World->GetTimerManager().
+ SetTimer(WaitingTimer, this,
+ &UDTFluxCountDownComponent::WaitingTimerFn, WaitingRate, true);
+ }
+ // ...
+
+}
+
+
+
+
+// Called every frame
+void UDTFluxCountDownComponent::TickComponent(float DeltaTime, ELevelTick TickType,
+ FActorComponentTickFunction* ThisTickFunction)
+{
+ Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
+
+ // ...
+}
+
+void UDTFluxCountDownComponent::SetGoTime(FDateTime NewGoTime)
+{
+ GoTime = NewGoTime;
+}
+
+void UDTFluxCountDownComponent::SetDuration(int32 NewDuration)
+{
+ // Need to be protected while counting
+ Duration = NewDuration;
+}
+
+void UDTFluxCountDownComponent::SetTarget(UAvaText3DComponent* TextComponent)
+{
+ // Need to be protected while counting
+ TextRef = TextComponent;
+ if(IsValid(TextRef))
+ {
+ FText WaitingText = FText::FromString("WAITING !!!");
+
+ TextRef->SetText(WaitingText);
+
+ UE_LOG(LogDTFluxAPI, Log, TEXT("Setting TextRef to %s"), *TextRef->GetText().ToString());
+ }
+}
+
+void UDTFluxCountDownComponent::CountUpTimerFn()
+{
+
+}
+
+void UDTFluxCountDownComponent::WaitingTimerFn()
+{
+
+ // UE_LOG(LogDTFluxAPI, Log, TEXT("WAITING"));
+}
+
diff --git a/Plugins/DTFluxAPI/Source/DTFluxAPI/Private/DTFluxDataStorage/DTFluxDataStorage.cpp b/Plugins/DTFluxAPI/Source/DTFluxAPI/Private/DTFluxDataStorage/DTFluxDataStorage.cpp
new file mode 100644
index 0000000..e1eebd0
--- /dev/null
+++ b/Plugins/DTFluxAPI/Source/DTFluxAPI/Private/DTFluxDataStorage/DTFluxDataStorage.cpp
@@ -0,0 +1,666 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+
+#include "DTFluxDataStorage/DTFluxDataStorage.h"
+
+#include "DTFluxAPILog.h"
+#include "DTFluxModel/DTFluxModel.h"
+
+
+TArray UDTFluxDataStorage::GetStages(const int ContestId)
+{
+ TArray Stages;
+ for(const auto& Contest : Contests)
+ {
+ Stages.Append(Contest.Stages);
+ }
+ return Stages;
+}
+
+
+
+void UDTFluxDataStorage::UpdateSplitRanking(const FDTFluxStageRankingResponse& SplitRankingResponse)
+{
+ for(auto& Contest : Contests)
+ {
+ if(Contest.Id == SplitRankingResponse.ContestID)
+ {
+ for(auto& Stage : Contest.Stages)
+ {
+ if(Stage.Id == SplitRankingResponse.StageID)
+ {
+ for(auto& Split : Stage.Splits)
+ {
+ if(Split.Id == SplitRankingResponse.SplitID)
+ {
+ for(auto& SplitRankingItemResp : SplitRankingResponse.Datas)
+ {
+ Split.InsertOrReplace(SplitRankingItemResp);
+ }
+ }
+
+ }
+ }
+ }
+ }
+ }
+}
+
+// // Bad implementation
+// void UDTFluxDataStorage::UpdateParticipant(const FDTFluxTeamUpdateResponse& TeamUpdateResponse)
+// {
+// FDTFluxParticipant Participant;
+//
+// for(auto& TeamUpdateRespItem : TeamUpdateResponse.Datas)
+// {
+// Participant.Bib = TeamUpdateRespItem.Bib;
+// Participant.Category = TeamUpdateRespItem.Category;
+// Participant.Club = TeamUpdateRespItem.Club;
+// Participant.Elite = TeamUpdateRespItem.Elite;
+// Participant.Person1.Gender = TeamUpdateRespItem.Gender;
+// Participant.Person1.FirstName = TeamUpdateRespItem.FirstName;
+// Participant.Person1.LastName = TeamUpdateRespItem.LastName;
+// // TODO ???
+// // Participant.Person2.Gender = TeamListItemResponse.Gender2;
+// Participant.Person2.FirstName = TeamUpdateRespItem.FirstName2;
+// Participant.Person2.LastName = TeamUpdateRespItem.LastName2;
+// Participant.Status = static_cast>(TeamUpdateRespItem.Status);
+// bool Update = false;
+// for(auto& Contest : Contests)
+// {
+// int Idx = 0;
+// for(auto& OldParticipant : Contest.Participants)
+// {
+// if(OldParticipant.Bib == Participant.Bib)
+// {
+// Update = true;
+// UE_LOG(LogDTFluxAPI, Log, TEXT("Idx %d OLD : %s %s New : %s %s In Contest%02d"),
+// Idx, *OldParticipant.Person1.FirstName, *OldParticipant.Person1.LastName,
+// *Participant.Person1.FirstName, *Participant.Person1.LastName, Contest.Id);
+// break;
+// }
+// Idx++;
+// }
+// if(Update)
+// {
+// if(Contest.Participants.IsValidIndex(Idx))
+// {
+// UE_LOG(LogDTFluxAPI, Log, TEXT("Idx %d, to be removed : %s %s in Contest%02d"),
+// Idx, *Contest.Participants[Idx].Person1.FirstName, *Contest.Participants[Idx].Person1.LastName,
+// Contest.Id);
+// Contest.Participants.RemoveAt(Idx);
+// }
+// }
+// Contest.Participants.Add(Participant);
+//
+// }
+// }
+// }
+
+// TODO NOT IMPLEMENTED
+void UDTFluxDataStorage::UpdateParticipantStatus(const FDTFluxStatusUpdateResponse& StatusUpdateResponse)
+{
+ EDTFluxParticipantStatusType NewStatus = static_cast>(StatusUpdateResponse.Datas.Status);
+ UE_LOG(LogDTFluxAPI, Log, TEXT("Status to be UPDATED for Bib %d to %s"), StatusUpdateResponse.Datas.Bib,
+ *UEnum::GetValueAsString(NewStatus));
+ bool Found = false;
+ for(auto& Contest : Contests)
+ {
+ UE_LOG(LogDTFluxAPI, Log, TEXT("Checking Participant witg Bib %d in Contest %02d"),
+ StatusUpdateResponse.Datas.Bib, Contest.Id);
+ for(auto& Participant: Contest.Participants)
+ {
+ if(Participant.Bib == StatusUpdateResponse.Datas.Bib)
+ {
+ Found = true;
+ EDTFluxParticipantStatusType OldStatus = Participant.Status;
+ Participant.Status = static_cast>(StatusUpdateResponse.Datas.Status);
+ UE_LOG(LogDTFluxAPI, Log, TEXT("Status UPDATED from %s to %s"),
+ *UEnum::GetValueAsString(OldStatus), *UEnum::GetValueAsString(Participant.Status));
+ return;
+ }
+ }
+ }
+}
+
+// TODO NOT IMPLEMENTED
+bool UDTFluxDataStorage::IsFinisherSplit(const FDTFluxSplitSensorResponse& SplitSensorResponse)
+{
+ return true;
+}
+
+// TODO NOT IMPLEMENTED
+FDTFluxFinisher UDTFluxDataStorage::GetFinisherStatus(const FDTFluxSplitSensorResponse& SplitSensorResponse)
+{
+ FDTFluxFinisher Finisher;
+ Finisher.Participant.Bib = 15222;
+ return Finisher;
+}
+
+bool UDTFluxDataStorage::GetContest(const int ContestId, FDTFluxContest& OutContest )
+{
+ // UE_LOG(LogDTFluxAPI, Warning, TEXT("RequestedContest %d"),
+ // ContestId);
+ for(auto& Contest : Contests)
+ {
+ // UE_LOG(LogDTFluxAPI, Warning, TEXT("Checking Contest %d"),
+ // Contest.Id);
+ if(Contest.Id == ContestId)
+ {
+ // UE_LOG(LogDTFluxAPI, Warning, TEXT("Found Contest %d"),
+ // Contest.Id);
+ OutContest = Contest;
+ return true;
+ }
+ }
+ // UE_LOG(LogDTFluxAPI, Error, TEXT("Contest %d not Found "),
+ // ContestId);
+ return false;
+}
+
+bool UDTFluxDataStorage::GetStage(const int ContestId, const int StageId, FDTFluxStage& OutStage)
+{
+ FDTFluxContest Contest;
+ // UE_LOG(LogDTFluxAPI, Warning, TEXT("RequestedStage %d in Contest%02d ****"),
+ // ContestId, StageId);
+ if(GetContest(ContestId, Contest))
+ {
+ for(auto & Stage: Contest.Stages)
+ {
+// UE_LOG(LogDTFluxAPI, Warning, TEXT("Checking Stage %d "),
+// Stage.Id);
+ if(Stage.Id == StageId)
+ {
+
+ // UE_LOG(LogDTFluxAPI, Warning, TEXT("Found %s in Stage ***"),
+ // *Stage.Name);
+ OutStage = Stage;
+ return true;
+ }
+ }
+ }
+ // UE_LOG(LogDTFluxAPI, Error, TEXT("Stage %d Not Found in Contest %d ****"),
+ // StageId, ContestId);
+ return false;
+}
+
+bool UDTFluxDataStorage::GetSplit(const int ContestId, const int StageId, const int SplitId, FDTFluxSplit& OutSplit)
+{
+ // DumpContest();
+ FDTFluxStage Stage;
+ // UE_LOG(LogDTFluxAPI, Warning, TEXT("RequestedSplit %d in Stage%02d of Contest%02d"),
+ // ContestId, StageId, SplitId);
+ if(GetStage(ContestId, StageId, Stage))
+ {
+ for(auto& Split: Stage.Splits)
+ {
+ if(Split.Id == SplitId)
+ {
+ // UE_LOG(LogDTFluxAPI, Warning, TEXT("Get Split %s in Stage%02d of Contest%02d"),
+ // *Split.Name, StageId, SplitId);
+ OutSplit = Split;
+ return true;
+ }
+ }
+ }
+ return false;
+
+}
+
+FDTFluxSplitRanking UDTFluxDataStorage::AddSplitRanking(const FDTFluxSplitSensorItemResponse& SplitSensorItem)
+{
+ FDTFluxSplitRanking NewSplitRanking;
+ NewSplitRanking.Bib = SplitSensorItem.Bib;
+ NewSplitRanking.Gap = SplitSensorItem.Gap;
+ NewSplitRanking.Rank = SplitSensorItem.Rank;
+ NewSplitRanking.Time = SplitSensorItem.Time;
+ NewSplitRanking.ContestId = SplitSensorItem.ContestID;
+ NewSplitRanking.StageId = SplitSensorItem.StageID;
+ NewSplitRanking.SplitId = SplitSensorItem.SplitID;
+ FDTFluxSplit Split;
+ if(GetSplit(SplitSensorItem.ContestID, SplitSensorItem.StageID,
+ SplitSensorItem.SplitID, Split))
+ {
+ Split.SplitRankings.Add(NewSplitRanking);
+ return NewSplitRanking;
+ }
+ UE_LOG(LogDTFluxAPI, Error,
+ TEXT("Error, Cannot process split sensor."))
+ UE_LOG(LogDTFluxAPI, Error, TEXT("Split %d from stage %d of Contest %d does not exist"),
+ SplitSensorItem.SplitID, SplitSensorItem.StageID, SplitSensorItem.ContestID);
+ return NewSplitRanking;
+}
+
+EDTFluxSplitType UDTFluxDataStorage::GetSplitStatus(int ContestID, int StageID, int SplitID)
+{
+ FDTFluxStage Stage;
+ if(GetStage(ContestID, StageID, Stage))
+ {
+ int SplitCount = Stage.Splits.Num();
+ FDTFluxSplit S;
+ return Stage.GetSplitType(SplitID);
+ }
+ return EDTFluxSplitType::UnknownSplitType;
+}
+
+bool UDTFluxDataStorage::GetStageRankingForBib(int ContestID, int StageID, int Bib,
+ FDTFluxStageRanking& OutStageRanking)
+{
+ FDTFluxStage Stage;
+ GetStage(ContestID, StageID, Stage);
+ for(auto& StageRanking : Stage.StageRanking)
+ {
+ if(StageRanking.Bib == Bib )
+ {
+ OutStageRanking = StageRanking;
+ return true;
+ }
+ }
+ return false;
+}
+
+
+TArray UDTFluxDataStorage::GetParticipants(const int ContestId)
+{
+ TArray Participants;
+ for(const auto& Contest : Contests)
+ {
+ if (ContestId <= -1)
+ {
+ Participants.Append(Contest.Participants);
+ }
+ else if(ContestId == Contest.Id)
+ {
+ Participants.Append(Contest.Participants);
+ }
+
+ }
+ return Participants;
+}
+
+void UDTFluxDataStorage::GetParticipant(const int ContestID, const int ParticipantBib, FDTFluxParticipant& OutParticipant)
+{
+ TArray Particpants = GetParticipants(ContestID);
+ for(auto& Participant : Particpants)
+ {
+ if(Participant.Bib == ParticipantBib)
+ {
+ OutParticipant = Participant;
+ }
+ }
+}
+
+TArray UDTFluxDataStorage::GetStageRanking(const int ContestId, const int StageId)
+{
+ TArray StageRanking;
+ FDTFluxStage Stage;
+ if(GetStage(ContestId, StageId, Stage))
+ {
+ Stage.StageRanking.Sort([](FDTFluxStageRanking A, FDTFluxStageRanking B)
+ {
+ return A.Rank < B.Rank;
+ });
+ return Stage.StageRanking;
+ }
+ return StageRanking;
+}
+
+void UDTFluxDataStorage::AddOrUpdateContest(const FDTFluxContestResponse& ContestResponse)
+{
+ FDTFluxContest Contest;
+ bool NewContest = false;
+ int ContestIdx = 0;
+ // UE_LOG(LogDTFluxAPI, Warning, TEXT("DateTime Json Contest \"%s\""), *ContestResponse.Date.ToString() );
+ if(!Contests.IsEmpty() )
+ {
+ for(auto& OldContest: Contests)
+ {
+ NewContest = true;
+ if(OldContest.Id == ContestResponse.Id)
+ {
+ // We have the contest that need to be updated
+ Contest = OldContest;
+ NewContest = false;
+ break;
+ }
+ // updating ContestIndex
+ ContestIdx++;
+ }
+ }else
+ {
+ // this is a new contest because we don't have one
+ NewContest = true;
+ }
+ // Updating values
+ Contest.Id = ContestResponse.Id;
+ Contest.Name = ContestResponse.Name;
+ Contest.Date = ContestResponse.Date;
+ TArray Splits;
+ for(auto Split: ContestResponse.Splits)
+ {
+ FDTFluxSplit S;
+ S.Id = Split.Id;
+ S.Name = Split.Name;
+ Splits.Add(S);
+ }
+ for(auto StageResp : ContestResponse.Stages )
+ {
+ FDTFluxStage Stage;
+ Stage.Id = StageResp.Id;
+ Stage.Name = StageResp.Name;
+ // UE_LOG(LogDTFluxAPI, Warning, TEXT("ContestResponse.Stage StartTime = %s"), *StageResp.StartTime);
+ // UE_LOG(LogDTFluxAPI, Warning, TEXT("ContestResponse.Stage EndTime = %s"), *StageResp.EndTime);
+ // UE_LOG(LogDTFluxAPI, Warning, TEXT("ContestResponse.Stage CutOff = %s"), *StageResp.CutOff);
+ FTimespan StartTimeSpan;
+ FTimespan::Parse(StageResp.StartTime, StartTimeSpan);
+ FTimespan EndTimeSpan;
+ FTimespan::Parse(StageResp.EndTime, EndTimeSpan);
+ FTimespan CutOffTimeSpan;
+ FTimespan::Parse(StageResp.CutOff, CutOffTimeSpan);
+ Stage.StartTime = Contest.Date + StartTimeSpan;
+ Stage.EndTime = Contest.Date + EndTimeSpan;
+ Stage.CutOff = Stage.StartTime + CutOffTimeSpan;
+ // UE_LOG(LogDTFluxAPI, Warning, TEXT("STAGE StartTime = %s"), *Stage.StartTime.ToString());
+ // UE_LOG(LogDTFluxAPI, Warning, TEXT("STAGE EndTime = %s"), *Stage.EndTime.ToString());
+ // UE_LOG(LogDTFluxAPI, Warning, TEXT("STAGE CutOff = %s"), *Stage.CutOff.ToString());
+
+ Stage.Splits = Splits;
+ Contest.Stages.Add(Stage);
+ }
+ if(NewContest)
+ {
+ // Contest.Dump();
+ Contests.Add(Contest);
+ return;
+ }
+ // Contest.Dump();
+ Contests.RemoveAt(ContestIdx);
+ Contests.Insert(Contest, ContestIdx);
+ // handle updating contest
+}
+
+void UDTFluxDataStorage::AddOrUpdateParticipant(const FDTFluxTeamListItemResponse& TeamListItemResponse)
+{
+ // UE_LOG(LogDTFluxAPI, Log, TEXT("In DataStorage::AddOrUpdateParticipant"));
+ // UE_LOG(LogDTFluxAPI, Log, TEXT("AboutToUpdateOrAdd Participant %d %s %s in Contest%02d "),
+ // TeamListItemResponse.Bib, *TeamListItemResponse.FirstName, *TeamListItemResponse.LastName,
+ // TeamListItemResponse.ContestId);
+ FDTFluxParticipant Participant;
+ Participant.Bib = TeamListItemResponse.Bib;
+ Participant.Category = TeamListItemResponse.Category;
+ Participant.Club = TeamListItemResponse.Club;
+ Participant.Elite = TeamListItemResponse.Elite;
+ Participant.Person1.Gender = TeamListItemResponse.Gender;
+ Participant.Person1.FirstName = TeamListItemResponse.FirstName;
+ Participant.Person1.LastName = TeamListItemResponse.LastName;
+ // TODO ???
+ // Participant.Person2.Gender = TeamListItemResponse.Gender2;
+ Participant.Person2.FirstName = TeamListItemResponse.FirstName2;
+ Participant.Person2.LastName = TeamListItemResponse.LastName2;
+ Participant.Status = static_cast>(TeamListItemResponse.Status);
+ for(auto& Contest: Contests)
+ {
+ if(Contest.Id == TeamListItemResponse.ContestId)
+ {
+ // UE_LOG(LogDTFluxAPI, Log, TEXT("AboutToUpdateOrAdd Participant %d %s %s in Contest%02d "),
+ // Participant.Bib, *Participant.Person1.FirstName, *Participant.Person1.LastName,
+ // Contest.Id);
+
+ Contest.AddParticipant(Participant);
+ return;
+ }
+ // UE_LOG(LogDTFluxAPI, Log, TEXT("Contest%02d has now %04d Participants"), Contest.Id,
+ // Contest.Participants.Num());
+
+ }
+}
+
+void UDTFluxDataStorage::UpdateContestRanking(const FDTFluxContestRankingResponse& InContestRanking)
+{
+
+ for(auto& Contest : Contests)
+ {
+ if(Contest.Id == InContestRanking.ContestID)
+ {
+ // Clean ContestRanking
+ Contest.ContestRanking.Empty();
+ for(const auto& TeamContestRankingResponse : InContestRanking.Datas)
+ {
+ FDTFluxContestRanking NewRankingEl;
+ NewRankingEl.Bib = TeamContestRankingResponse.Bib;
+ NewRankingEl.Rank = TeamContestRankingResponse.Rank;
+ NewRankingEl.Gap = TeamContestRankingResponse.Gap;
+ NewRankingEl.Time = TeamContestRankingResponse.Time;
+ Contest.ContestRanking.Add(NewRankingEl);
+ Contest.SortContestRanking();
+ }
+ }
+ }
+}
+
+void UDTFluxDataStorage::UpdateStageRanking(const FDTFluxStageRankingResponse& StageRankingResponse)
+{
+ // UE_LOG(LogDTFluxAPI, Log, TEXT("In DataStorage::UdpateStageRanking"));
+ // UE_LOG(LogDTFluxAPI, Log, TEXT("AboutToUpdate Contest%02d and Stage%02d"),
+ // StageRankingResponse.ContestID, StageRankingResponse.StageID);
+ for(auto& Contest: Contests)
+ {
+ if(Contest.Id == StageRankingResponse.ContestID)
+ {
+ // UE_LOG(LogDTFluxAPI, Log, TEXT("Found Contest::%02d "),Contest.Id);
+ if(Contest.Stages.IsEmpty())
+ {
+ // UE_LOG(LogDTFluxAPI, Log, TEXT("WTF Contest::%02d HAS NO STAGES ???"),Contest.Id);
+ }
+ for(auto& Stage: Contest.Stages)
+ {
+
+ // UE_LOG(LogDTFluxAPI, Log, TEXT("Current Stage is Stage::%02d "),Stage.Id);
+ if(Stage.Id == StageRankingResponse.StageID)
+ {
+ // UE_LOG(LogDTFluxAPI, Log, TEXT("Found Stage::%02d "),Stage.Id);
+ // Cleaning StageRanking
+ Stage.StageRanking.Empty();
+ for(auto& StageRankingResp: StageRankingResponse.Datas )
+ {
+ FDTFluxStageRanking NewStageRanking;
+ NewStageRanking.TimeRun = StageRankingResp.TimeRun;
+ FTimespan StartTimeSpan;
+ FTimespan::Parse(StageRankingResp.TimeStart, StartTimeSpan);
+ NewStageRanking.Time = StageRankingResp.Time;
+ NewStageRanking.TimeStart = Contest.Date + StartTimeSpan;
+ NewStageRanking.TimeTransition = StageRankingResp.TimeTransition;
+ NewStageRanking.TimeSwim = StageRankingResp.TimeSwim;
+ NewStageRanking.Bib = StageRankingResp.Bib;
+ NewStageRanking.Gap = StageRankingResp.Gap;
+ NewStageRanking.Rank = StageRankingResp.Rank;
+ NewStageRanking.SpeedRunning = StageRankingResp.SpeedRunning;
+ NewStageRanking.SpeedSwim = StageRankingResp.SpeedSwim;
+ NewStageRanking.SpeedTotal = StageRankingResp.SpeedTotal;
+ Stage.StageRanking.Add(NewStageRanking);
+ Stage.SortStageRanking();
+ // UE_LOG(LogDTFluxAPI, Log,
+ // TEXT("Testing -> Stage %02d::%s in Contest %02d::%s, has now %02d Rankings\n"),
+ // Stage.Id, *Stage.Name, Contest.Id, *Contest.Name, Stage.StageRanking.Num());
+ }
+ }
+ }
+ }
+ }
+}
+
+void UDTFluxDataStorage::AddSplitSensorResult(FDTFluxSplitSensorItemResponse Response)
+{
+ // Send SplitSensor Result to BP
+
+
+
+}
+
+void UDTFluxDataStorage::GoToNextStage()
+{
+ // If Number of stages is less or equal to the current stageID
+ if(IsInitialized())
+ {
+ if(Contests[CurrentContestId].Stages.Num() -1 <= CurrentStageId)
+ {
+ CurrentStageId += 1;
+ }else
+ {
+ ResetStageId();
+ ChangeCurrentContest();
+ }
+ }
+
+}
+
+void UDTFluxDataStorage::ChangeCurrentContest()
+{
+ // Contest Are initialized
+ if(IsInitialized())
+ {
+ if(CurrentContestId < Contests.Num() -1)
+ {
+ // last Contest
+ CurrentContestId = 0;
+ }
+
+ }
+}
+
+const FString UDTFluxDataStorage::GetConcurrentFormatedName(int Bib, bool Truncate, int MaxSize)
+{
+ {
+ for(const auto& Participant : GetParticipants())
+ {
+ if(Participant.Bib == Bib)
+ {
+ return Participant.GetParticipantFormatedName(Truncate, MaxSize);
+ }
+ }
+ return "";
+ };
+}
+
+// ReSharper disable once IdentifierTypo
+TArray UDTFluxDataStorage::GetPoursuitWithStageTime(const TArray ContestIds, const int StageId, float DelaTimeSeconds)
+{
+ TArray RemoveIdx;
+ int Idx = 0;
+ TArray StagesRankingsTemp;
+ TArray ReturnStageRankings;
+ FDateTime PresumedStartStage;
+ for(const int ContestId : ContestIds)
+ {
+ FDTFluxStage StageTemp;
+ if(GetStage(ContestId, StageId, StageTemp))
+ {
+ PresumedStartStage = StageTemp.StartTime;
+ StagesRankingsTemp .Append(StageTemp.StageRanking);
+ // ContestsRankings
+ }
+ }
+ StagesRankingsTemp.Sort([](const FDTFluxStageRanking& A, const FDTFluxStageRanking& B)
+ {
+ return A.TimeStart < B.TimeStart;
+ });
+ FDateTime MassStartDate = PresumedStartStage + FTimespan::FromSeconds(DelaTimeSeconds) ;
+ for( auto & StageRanking : StagesRankingsTemp)
+ {
+ if ( StageRanking.TimeStart < MassStartDate )
+ {
+ ReturnStageRankings.Add(StageRanking);
+ }
+ }
+ return ReturnStageRankings;
+
+}
+// ReSharper disable once IdentifierTypo
+TArray UDTFluxDataStorage::GetPoursuitWithTimeStart(const TArray ContestIds, const int StageId, float DelaTimeSeconds)
+{
+ TArray RemoveIdx;
+ int Idx = 0;
+ TArray StagesRankingsTemp;
+ TArray ReturnStageRankings;
+ for(const int ContestId : ContestIds)
+ {
+ FDTFluxStage StageTemp;
+ if(GetStage(ContestId, StageId, StageTemp))
+ {
+ StagesRankingsTemp .Append(StageTemp.StageRanking);
+ // ContestsRankings
+ }
+ }
+ StagesRankingsTemp.Sort([](const FDTFluxStageRanking& A, const FDTFluxStageRanking& B)
+ {
+ return A.TimeStart < B.TimeStart;
+ });
+ FDateTime MassStartDate;
+ if (!StagesRankingsTemp.IsEmpty())
+ {
+ MassStartDate = StagesRankingsTemp[0].TimeStart + FTimespan::FromSeconds(DelaTimeSeconds) ;
+
+ }
+ for( auto & StageRanking : StagesRankingsTemp)
+ {
+ if ( StageRanking.TimeStart < MassStartDate )
+ {
+ ReturnStageRankings.Add(StageRanking);
+ }
+ }
+ return ReturnStageRankings;
+
+}
+
+bool UDTFluxDataStorage::GetFirstStageOfContest(const int ContestId, FDTFluxStage& Stage)
+{
+ if(Contests.IsEmpty())
+ {
+ return false;
+ }
+ for (auto& Contest : Contests)
+ {
+ if(Contest.Id == ContestId)
+ {
+ Contest.Stages.Sort([](const FDTFluxStage& A, const FDTFluxStage& B)
+ {
+ return A.Id < B.Id;
+ });
+ if(Contest.Stages.IsValidIndex(0))
+ {
+ Stage = Contest.Stages[0];
+ return true;
+ }
+ return false;
+
+ }
+ }
+ return false;
+}
+
+void UDTFluxDataStorage::DumpContest()
+{
+ for(const auto& Contest : Contests)
+ {
+ UE_LOG(LogDTFluxAPI, Warning, TEXT("Contest%02d with name %s : Date %s\n"),
+ Contest.Id, *Contest.Name, *Contest.Date.ToString());
+ // UE_LOG(LogDTFluxAPI, Warning, TEXT("Participants :\n"));
+ // for(const auto& Participant : Contest.Participants)
+ // {
+ // Participant.Dump();
+ // }
+ UE_LOG(LogDTFluxAPI, Warning, TEXT("Stages :\n"));
+ for(const auto& Stage : Contest.Stages)
+ {
+ Stage.Dump();
+ }
+ UE_LOG(LogDTFluxAPI, Warning, TEXT("ContestRanking :\n"));
+ for(const auto& ContestRankingItem : Contest.ContestRanking)
+ {
+ ContestRankingItem.Dump();
+ }
+
+ }
+}
+
+
diff --git a/Plugins/DTFluxAPI/Source/DTFluxAPI/Private/DTFluxModel/DTFluxModel.cpp b/Plugins/DTFluxAPI/Source/DTFluxAPI/Private/DTFluxModel/DTFluxModel.cpp
new file mode 100644
index 0000000..db6f67c
--- /dev/null
+++ b/Plugins/DTFluxAPI/Source/DTFluxAPI/Private/DTFluxModel/DTFluxModel.cpp
@@ -0,0 +1,68 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+
+#include "DTFluxModel/DTFluxModel.h"
+
+bool FDTFluxParticipant::IsTeam() const
+{
+ return (Person2.FirstName != "");
+}
+
+void FDTFluxParticipant::Dump() const{
+ FString EliteStr = "NO";
+ if(Elite)
+ {
+ EliteStr = "YES";
+ }
+ UE_LOG(LogDTFluxAPI, Log, TEXT("PARTICIPANT with bib: %03d"), Bib);
+ UE_LOG(LogDTFluxAPI, Log, TEXT("Fullname : %s %s"), *Person1.FirstName, *Person1.LastName);
+ if(IsTeam())
+ {
+ UE_LOG(LogDTFluxAPI, Log, TEXT("Teamate : %s %s"), *Person2.FirstName, *Person2.LastName);
+ UE_LOG(LogDTFluxAPI, Log, TEXT("Team name : %s"), *Team);
+ }
+ UE_LOG(LogDTFluxAPI, Log, TEXT("Club : %s, Category : %s, IsElite : %s, Status : %s"),
+ *Club, *Category, *EliteStr, *UEnum::GetValueAsString(Status));
+
+}
+
+bool FDTFluxStage::SetStartTime(const FDateTime& ContestDate, const FString& TimeString)
+{
+ TArray TimeTokensStart;
+ TimeString.ParseIntoArray(TimeTokensStart, TEXT(":"));
+ const int32 HoursStart = FCString::Atoi(*TimeTokensStart[0]);
+ const int32 MinutesStart = FCString::Atoi(*TimeTokensStart[1]);
+ const int32 SecondsStart = FCString::Atoi(*TimeTokensStart[2]);
+ StartTime = FDateTime(ContestDate.GetYear(), ContestDate.GetMonth(), ContestDate.GetDay(),
+ HoursStart, MinutesStart, SecondsStart);
+ UE_LOG(LogDTFluxAPI, Log, TEXT("Setting StartTime For %s to %s"), *Name, *StartTime.ToString());
+ return true;
+}
+
+bool FDTFluxStage::SetEndTime(const FDateTime& ContestDate, const FString& TimeString)
+{
+ TArray TimeTokens;
+ TimeString.ParseIntoArray(TimeTokens, TEXT(":"));
+ const int32 Hours = FCString::Atoi(*TimeTokens[0]);
+ const int32 Minutes = FCString::Atoi(*TimeTokens[1]);
+ const int32 Seconds = FCString::Atoi(*TimeTokens[2]);
+ EndTime = FDateTime(ContestDate.GetYear(), ContestDate.GetMonth(), ContestDate.GetDay(),
+ Hours, Minutes, Seconds);
+ UE_LOG(LogDTFluxAPI, Log, TEXT("Setting EndTime For %s to %s"), *Name, *StartTime.ToString());
+ return true;
+}
+
+bool FDTFluxContest::SetDate(const FString& StringDate)
+{
+ TArray Tokens;
+ StringDate.ParseIntoArray(Tokens, TEXT("-"));
+ if(Tokens.Num() != 3)
+ {
+ return false;
+ }
+ const int32 Year = FCString::Atoi(*Tokens[0]);
+ const int32 Month = FCString::Atoi(*Tokens[1]);
+ const int32 Day = FCString::Atoi(*Tokens[2]);
+ Date = FDateTime(Year, Month, Day);
+ return true;
+}
diff --git a/Plugins/DTFluxAPI/Source/DTFluxAPI/Private/DTFluxModel/DTFluxModelResponse.cpp b/Plugins/DTFluxAPI/Source/DTFluxAPI/Private/DTFluxModel/DTFluxModelResponse.cpp
new file mode 100644
index 0000000..e17fe44
--- /dev/null
+++ b/Plugins/DTFluxAPI/Source/DTFluxAPI/Private/DTFluxModel/DTFluxModelResponse.cpp
@@ -0,0 +1,4 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+
+#include "DTFluxModel/DTFluxModelResponse.h"
diff --git a/Plugins/DTFluxAPI/Source/DTFluxAPI/Private/DTFluxProjectSettings/DTFluxProjectSettings.cpp b/Plugins/DTFluxAPI/Source/DTFluxAPI/Private/DTFluxProjectSettings/DTFluxProjectSettings.cpp
new file mode 100644
index 0000000..8bd688c
--- /dev/null
+++ b/Plugins/DTFluxAPI/Source/DTFluxAPI/Private/DTFluxProjectSettings/DTFluxProjectSettings.cpp
@@ -0,0 +1,22 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+
+#include "DTFluxProjectSettings/DTFluxProjectSettings.h"
+
+const UDTFluxProjectSettings* UDTFluxProjectSettings::GetDTFluxAPIProjectSettings()
+{
+ return GetDefault();
+}
+
+void UDTFluxProjectSettings::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
+{
+ Super::PostEditChangeProperty(PropertyChangedEvent);
+ FString ChangeKey = PropertyChangedEvent.Property->GetName();
+ UE_LOG(LogDTFluxAPI, Log, TEXT("Settings %s has changed"), *ChangeKey);
+ OnProjectSettingsModified.Broadcast(ChangeKey, GetDTFluxAPIProjectSettings());
+}
+
+UDTFluxProjectSettings::UDTFluxProjectSettings()
+{
+ CategoryName = "DTFlux Settings";
+}
diff --git a/Plugins/DTFluxAPI/Source/DTFluxAPI/Private/DTFluxSubsystem/DTFluxSubsystem.cpp b/Plugins/DTFluxAPI/Source/DTFluxAPI/Private/DTFluxSubsystem/DTFluxSubsystem.cpp
new file mode 100644
index 0000000..75404bb
--- /dev/null
+++ b/Plugins/DTFluxAPI/Source/DTFluxAPI/Private/DTFluxSubsystem/DTFluxSubsystem.cpp
@@ -0,0 +1,553 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#include "DTFluxSubsystem/DTFluxSubsystem.h"
+#include "DTFluxProjectSettings/DTFluxProjectSettings.h"
+#include "DTFluxModel/DTFluxModel.h"
+#include "DTFluxAPILog.h"
+#include "DTFluxDataStorage/DTFluxDataStorage.h"
+#include "JsonObjectConverter.h"
+#include "DTFluxSubsystemAPISettings/DTFluxSubsystemAPISettings.h"
+
+
+/****
+ * DTFlux subsystem
+ ****/
+
+void UDTFluxSubsystem::Initialize(FSubsystemCollectionBase& Collection)
+{
+ Super::Initialize(Collection);
+ const UDTFluxProjectSettings* Settings = GetSettings();
+ LoadConfig(Settings);
+ WsClient = NewObject(this, UDTFluxWebSocketClient::StaticClass());
+ WsClient->OnConnectionConnected.AddDynamic(this, &UDTFluxSubsystem::WsConnected );
+ WsClient->OnConnectionClosed.AddDynamic(this, &UDTFluxSubsystem::WsConnectionClosed );
+ WsClient->OnConnectionError.AddDynamic(this, &UDTFluxSubsystem::WsConnectionError );
+ WsClient->OnReceivedMessage.AddDynamic(this, &UDTFluxSubsystem::WsReceivedMessage );
+ UE_LOG(LogDTFluxAPI, Log, TEXT("Trying to connect to %s:%i"), *SubSettings.WebsocketAddress, SubSettings.WebsocketPort);
+ WsClient->Connect(SubSettings.WebsocketAddress, SubSettings.WebsocketPort);
+
+ DataStorage = NewObject();
+ // DataStorage->InitDatastorage();
+
+
+
+ FDateTime Now = FDateTime::Now();
+ FDateTime Send1Min = Now + FTimespan::FromMinutes(1);
+ UE_LOG(LogDTFluxAPI, Log, TEXT("TEST timer timeSpan Duration : %s"), *Send1Min.ToString());
+
+}
+
+void UDTFluxSubsystem::Deinitialize()
+{
+ if(WsClient)
+ {
+ if (WsClient->Close())
+ {
+ UE_LOG(LogDTFluxAPI, Log, TEXT("WsClient is closed"));
+ }
+ UE_LOG(LogDTFluxAPI, Error, TEXT("WsClient can not be closed"));
+ }
+ UE_LOG(LogDTFluxAPI, Log, TEXT("WsClient has been GC'ed"));
+
+ Super::Deinitialize();
+}
+
+void UDTFluxSubsystem::InitDataStorage()
+{
+
+}
+
+bool UDTFluxSubsystem::ReloadSubsystem()
+{
+ return Reconnect();
+}
+
+bool UDTFluxSubsystem::Reconnect()
+{
+ bool Result = WsClient->Close();
+ DataStorageRaceDataInit = false;
+ DataStorageTeamListInit = false;
+ if(!WsClient->IsConnected())
+ return WsClient->Connect( SubSettings.WebsocketAddress, SubSettings.WebsocketPort);
+ return false;
+}
+
+void UDTFluxSubsystem::LoadConfig(const UDTFluxProjectSettings* Settings)
+{
+ SubSettings.WebsocketPort = Settings->WebsocketServerPort;
+ SubSettings.WebsocketAddress = Settings->WebsocketServerAddress;
+ SubSettings.ProxyAddress = Settings->ProxyAddress;
+ // SubSettings.ProxyPort = Settings->ProxyPort;
+ // TMap SettingsEndpoints;
+ // SettingsEndpoints.Add(FString("race-data"), Settings->ProxyRaceDataEndpoint);
+ // SettingsEndpoints.Add(FString("contest-ranking"), Settings->ProxyRankingEndpoint);
+ // SettingsEndpoints.Add(FString("stage-ranking"), Settings->ProxyRankingEndpoint);
+ // SettingsEndpoints.Add(FString("team-list"), Settings->ProxyTeamsEndpoint);
+ // SubSettings.ProxyEndpoints = SettingsEndpoints;
+}
+
+// Get project Settings
+const UDTFluxProjectSettings* UDTFluxSubsystem::GetSettings()
+{
+ if(const UDTFluxProjectSettings* Settings = UDTFluxProjectSettings::GetDTFluxAPIProjectSettings())
+ return Settings;
+ else
+ {
+ UE_LOG(LogDTFluxAPI, Error, TEXT("Unable to get DTFlux API settings"));
+ return nullptr;
+ }
+}
+
+// tick function
+void UDTFluxSubsystem::Tick(float DeltaTime)
+{
+ if(Timer.Num() > 0)
+ {
+ TArray Done;
+ for(auto const& El : Timer)
+ {
+ FDateTime Dt = FDateTime::Now();
+ if(Dt >= El.Key)
+ {
+ El.Value.Execute(TEXT("Tick"));
+ OnTimerTriggered.Broadcast();
+ UE_LOG(LogDTFluxAPI, Log, TEXT("Execution"));
+ UE_LOG(LogDTFluxAPI, Log, TEXT("TICK : exec time: %lld == %lld"), El.Key.GetTicks(), Dt.GetTicks());
+ Done.Add(El.Key);
+ }
+
+ }
+ if(Done.Num() > 0)
+ {
+ UE_LOG(LogDTFluxAPI, Log, TEXT("TICK : Cleaning %i"), Done.Num());
+ for(auto const& ToDelete: Done)
+ {
+ Timer.Remove(ToDelete);
+ }
+ }
+ // UE_LOG(LogDTFluxAPI, Log, TEXT("TICK : Timer Length=%i"), Timer.Num());
+ }
+}
+
+void UDTFluxSubsystem::RequestRaceDatas()
+{
+ WsClient->SendMessage(TEXT("{\"path\": \"race-datas\"}"));
+}
+
+void UDTFluxSubsystem::RequestTeamList()
+{
+ WsClient->SendMessage(TEXT("{\"path\": \"team-list\"}"));
+}
+
+void UDTFluxSubsystem::RequestContestRanking(const int ContestId)
+{
+ const FString Request = FString::Printf(TEXT("{\"path\": \"contest-ranking\", \"contestID\" : %i}"), ContestId);
+ UE_LOG(LogDTFluxAPI, Log, TEXT("Sending %s to server"), *Request);
+ WsClient->SendMessage(Request);
+}
+
+void UDTFluxSubsystem::RequestStageRanking(const int ContestId, const int StageId)
+{
+ const FString Request = FString::Printf(TEXT("{\"path\": \"stage-ranking\", \"contestID\" : %i, \"stageID\" : %i}"), ContestId, StageId);
+ WsClient->SendMessage(Request);
+}
+
+void UDTFluxSubsystem::RequestSplitGaps(const int ContestId, const int StageId, const int SplitId)
+{
+ const FString Request =
+ FString::Printf(TEXT("{\"path\": \"stage-ranking\", \"contestID\" : %i, \"stageID\" : %i, \"splitID\" : %i}"),
+ ContestId, StageId, SplitId);
+
+ WsClient->SendMessage(Request);
+}
+
+void UDTFluxSubsystem::UpdateRaceData()
+{
+ RequestRaceDatas();
+}
+
+void UDTFluxSubsystem::UpdateTeamList()
+{
+ RequestTeamList();
+}
+
+void UDTFluxSubsystem::UpdateTeam()
+{
+}
+
+void UDTFluxSubsystem::UpdateContestRanking(const int ContestID)
+{
+ RequestContestRanking(ContestID);
+}
+
+void UDTFluxSubsystem::UpdateStageRanking(const int ContestID, const int StageID, const int SplitID)
+{
+ if(SplitID == -1)
+ {
+ RequestStageRanking(ContestID, StageID);
+ }
+ else
+ {
+ RequestSplitGaps(ContestID, StageID, SplitID);
+ }
+}
+
+/***
+ * Timer handling
+ ***/
+void UDTFluxSubsystem::BroadcastTimerEvent()
+{
+ OnTimerTriggered.Broadcast();
+ UE_LOG(LogDTFluxAPI, Log, TEXT("TEST timer trigerred at : %s"), *FDateTime::Now().ToString());
+
+}
+
+void UDTFluxSubsystem::SetTimerEvent(const FDateTime& When)
+{
+ FTimespan TimeSpan = FDateTime::Now() - When;
+ UE_LOG(LogDTFluxAPI, Log, TEXT("TEST timer timeSpan Duration : %s"), *TimeSpan.GetDuration().ToString());
+ FOnTimer NewTimer;
+ // NewTimer.BindUFunction()
+ // AddTimer(When, )
+}
+
+bool UDTFluxSubsystem::AddTimer(FDateTime Time, FOnTimer NewTimer)
+{
+ Timer.Add(Time, NewTimer);
+ return true;
+}
+
+/**
+ * END TIMER HANDLING
+ ***/
+
+void UDTFluxSubsystem::WsConnected()
+{
+ FDTFluxWsResponseEvent Event;
+ Event.WsResponseType = EDTFluxResponseType::WsConnected;
+ Event.RawData = "Connected";
+ if(WsClient->IsConnected())
+ {
+ UE_LOG(LogDTFluxAPI, Log, TEXT("Initializing DataStorage"));
+ UpdateRaceData();
+ }
+ OnWsEvent.Broadcast(Event);
+ UE_LOG(LogDTFluxAPI, Log, TEXT("Ws Connected"));
+
+}
+
+void UDTFluxSubsystem::WsReceivedMessage( const FString& MessageReceived)
+{
+ FDTFluxWsResponseEvent Event;
+ Event.WsResponseType = UnknownResponse;
+ Event.RawData = MessageReceived;
+
+ TSharedPtr JsonValue;
+ TSharedRef> Reader = TJsonReaderFactory<>::Create(MessageReceived);
+ if (FJsonSerializer::Deserialize(Reader, JsonValue))
+ {
+ TSharedPtr Json = JsonValue->AsObject();
+ FString Type = Json->GetStringField(TEXT("type"));
+ if(Type.Contains("race-datas"))
+ {
+ FDTFluxRaceDataResponse RaceDataResponse;
+ if(!FJsonObjectConverter::JsonObjectToUStruct
+ (Json.ToSharedRef(), &RaceDataResponse))
+ {
+ UE_LOG(LogDTFluxAPI, Error, TEXT("Message %s is not a valid \"race-data\" object"), *MessageReceived);
+ }
+ UE_LOG(LogDTFluxAPI, Log, TEXT("Message %s is valid race-data object"), *MessageReceived);
+ ProcessRaceDataResponse(RaceDataResponse);
+ if(!DataStorageRaceDataInit)
+ {
+ DataStorageRaceDataInit = true;
+ RequestTeamList();
+ }
+ Event.WsResponseType = RaceData;
+ }
+ if(Type.Contains("contest-ranking"))
+ {
+ FDTFluxContestRankingResponse ContestRankingResponse;
+ if(!FJsonObjectConverter::JsonObjectToUStruct
+ (Json.ToSharedRef(), &ContestRankingResponse))
+ {
+ UE_LOG(LogDTFluxAPI, Error, TEXT("Message %s is not a valid \"contest-ranking\" object"), *MessageReceived);
+ }
+ ProcessContestRankingResponse(ContestRankingResponse);
+ Event.WsResponseType = ContestRanking;
+
+ }
+ if(Type.Contains("stage-ranking"))
+ {
+ FDTFluxStageRankingResponse StageRankingResponse;
+ if(!FJsonObjectConverter::JsonObjectToUStruct
+ (Json.ToSharedRef(), &StageRankingResponse))
+ {
+ UE_LOG(LogDTFluxAPI, Error, TEXT("Message %s is not a valid \"stage-ranking\" object"), *MessageReceived);
+ }
+ UE_LOG(LogDTFluxAPI, Log, TEXT("\"stage-ranking\" object received"));
+ if(StageRankingResponse.SplitID == -1)
+ {
+ ProcessStageRankingResponse(StageRankingResponse);
+ Event.WsResponseType = StageRanking;
+ }
+ ProcessSplitRankingResponse(StageRankingResponse);
+ Event.WsResponseType = SplitRanking;
+
+ }
+ if(Type.Contains("team-list"))
+ {
+ FDTFluxTeamListResponse TeamListResponse;
+ if( !FJsonObjectConverter::JsonObjectToUStruct
+ (Json.ToSharedRef(), &TeamListResponse))
+ {
+ UE_LOG(LogDTFluxAPI, Error, TEXT("Message %s is not a valid team-list object"), *MessageReceived)
+ }
+ UE_LOG(LogDTFluxAPI, Log, TEXT("Received team-list data"));
+ ProcessTeamListResponse(TeamListResponse);
+ if(!DataStorageTeamListInit)
+ {
+ DataStorageTeamListInit = true;
+ // Initialize contest-rankings
+ for(const auto& Contest: DataStorage->Contests)
+ {
+ RequestContestRanking(Contest.Id);
+ // Initialize stage-rankings
+ for(const auto Stage : Contest.Stages)
+ {
+ RequestStageRanking(Contest.Id, Stage.Id);
+ }
+ }
+ }
+ Event.WsResponseType = TeamList;
+ }
+ if(Type.Contains("team-update"))
+ {
+ FDTFluxTeamUpdateResponse TeamUpdateResponse;
+ if( !FJsonObjectConverter::JsonObjectToUStruct
+ (Json.ToSharedRef(), &TeamUpdateResponse))
+ {
+ UE_LOG(LogDTFluxAPI, Error, TEXT("Message %s is not a valid team-update object"), *MessageReceived)
+ }
+ UE_LOG(LogDTFluxAPI, Log, TEXT("Received team-update data"));
+ ProcessTeamUpdateResponse(TeamUpdateResponse);
+ Event.WsResponseType = TeamUpdate;
+ }
+ if(Type.Contains("split-sensor"))
+ {
+ FDTFluxSplitSensorResponse SplitSensorResponse;
+ if( !FJsonObjectConverter::JsonObjectToUStruct
+ (Json.ToSharedRef(), &SplitSensorResponse))
+ {
+ UE_LOG(LogDTFluxAPI, Error, TEXT("Message %s is not a valid split-sensor data"), *MessageReceived)
+ }
+ UE_LOG(LogDTFluxAPI, Log, TEXT("Received split-sensor data"));
+ ProcessSplitSensor(SplitSensorResponse);
+ Event.WsResponseType = SplitSensor;
+
+ }
+ if(Type.Contains("status-update"))
+ {
+ FDTFluxStatusUpdateResponse StatusUpdateResponse;
+ if( !FJsonObjectConverter::JsonObjectToUStruct
+ (Json.ToSharedRef(), &StatusUpdateResponse))
+ {
+ UE_LOG(LogDTFluxAPI, Error, TEXT("Message %s is not a valid status-update data"), *MessageReceived)
+ }
+ UE_LOG(LogDTFluxAPI, Log, TEXT("Received status-update data %s"), *MessageReceived);
+ ProcessStatusUpdateResponse(StatusUpdateResponse);
+ Event.WsResponseType = StatusUpdate;
+
+ }
+ if(Type.Contains("broadcast-message"))
+ {
+ FDTFluxArchSelectResponse ArchSelectResponse;
+ if( !FJsonObjectConverter::JsonObjectToUStruct
+ (Json.ToSharedRef(), &ArchSelectResponse))
+ {
+ UE_LOG(LogDTFluxAPI, Error, TEXT("Message %s is not a valid broadcast-message data"), *MessageReceived)
+ }
+ for(const auto& ArchSelect : ArchSelectResponse.Datas)
+ {
+ ProcessArchSelect(ArchSelect);
+ }
+ Event.RawData = "ArchSelect";
+ Event.WsResponseType = ArchSelect;
+ UE_LOG(LogDTFluxAPI, Log, TEXT("Received broadcast-message data"));
+ }
+ }
+ OnWsEvent.Broadcast(Event);
+}
+
+void UDTFluxSubsystem::WsConnectionClosed(const FString& Reason)
+{
+ UE_LOG(LogDTFluxAPI, Log, TEXT("Ws ConnectionClosed with reason %s trying to reconnect"), *Reason);
+
+ if(!WsClient->IsConnected()){}
+ WsClient->Connect( SubSettings.WebsocketAddress, SubSettings.WebsocketPort);
+ FDTFluxWsResponseEvent Event;
+ Event.WsResponseType = WsClosed;
+ Event.RawData = Reason;
+ OnWsEvent.Broadcast(Event);
+}
+
+void UDTFluxSubsystem::WsConnectionError(const FString& Error)
+{
+ UE_LOG(LogDTFluxAPI, Log, TEXT("Ws Error %s trying to reconnect"), *Error);
+ FDTFluxWsResponseEvent Event;
+ Event.WsResponseType = WsError;
+ Event.RawData = Error;
+ OnWsEvent.Broadcast(Event);
+ bool Result = WsClient->Close();
+ DataStorageRaceDataInit = false;
+ DataStorageTeamListInit = false;
+ if(!WsClient->IsConnected()){}
+ WsClient->Connect( SubSettings.WebsocketAddress, SubSettings.WebsocketPort);
+}
+
+bool UDTFluxSubsystem::IsConnected() const
+{
+ return WsClient->IsConnected();
+}
+
+void UDTFluxSubsystem::ProcessTeamListResponse(const FDTFluxTeamListResponse& TeamListResponse)
+{
+ for( const auto& TeamListItemResponse : TeamListResponse.Datas)
+ {
+ DataStorage->AddOrUpdateParticipant(TeamListItemResponse);
+ }
+ // for(auto& Contest : DataStorage->Contests)
+ // {
+ // Contest.DumpParticipant();
+ // }
+ // UE_LOG(LogDTFluxAPI, Log, TEXT("New Particpant list Size %d"), DataStorage->GetParticipants().Num())
+
+
+}
+
+void UDTFluxSubsystem::ProcessRaceDataResponse(const FDTFluxRaceDataResponse& DataResponse)
+{
+ for(const auto ContestResponse : DataResponse.Datas)
+ {
+ DataStorage->AddOrUpdateContest(ContestResponse);
+ }
+ FDTFluxWsResponseEvent Event;
+ Event.WsResponseType = RaceData;
+ Event.RawData = "race-data";
+ OnWsEvent.Broadcast(Event);
+ OnRaceDataReceived.Broadcast();
+ // UE_LOG(LogDTFluxAPI, Log, TEXT("New Contest Size %d"), DataStorage->Contests.Num())
+
+}
+
+void UDTFluxSubsystem::ProcessContestRankingResponse(const FDTFluxContestRankingResponse& ContestRankingResponse)
+{
+ DataStorage->UpdateContestRanking(ContestRankingResponse);
+ FDTFluxWsResponseEvent Event;
+ Event.WsResponseType = ContestRanking;
+ Event.RawData = "contest-ranking";
+ OnWsEvent.Broadcast(Event);
+}
+
+void UDTFluxSubsystem::ProcessStageRankingResponse(const FDTFluxStageRankingResponse& StageRankingResponse)
+{
+ DataStorage->UpdateStageRanking(StageRankingResponse);
+ FDTFluxWsResponseEvent Event;
+ Event.WsResponseType = StageRanking;
+ Event.RawData = "stage-ranking";
+ OnWsEvent.Broadcast(Event);
+}
+
+void UDTFluxSubsystem::ProcessSplitRankingResponse(const FDTFluxStageRankingResponse& SplitRankingResponse)
+{
+ DataStorage->UpdateSplitRanking(SplitRankingResponse);
+ FDTFluxWsResponseEvent Event;
+ Event.WsResponseType = SplitRanking;
+ Event.RawData = "split-ranking";
+ OnWsEvent.Broadcast(Event);
+}
+
+void UDTFluxSubsystem::ProcessTeamUpdateResponse(const FDTFluxTeamUpdateResponse& TeamUpdateResponse)
+{
+ UE_LOG(LogDTFluxAPI, Log, TEXT("team-update received in c++"));
+ for(auto& TeamListRespItem: TeamUpdateResponse.Datas)
+ {
+ DataStorage->AddOrUpdateParticipant(TeamListRespItem);
+ }
+ FDTFluxWsResponseEvent Event;
+ Event.WsResponseType = TeamUpdate;
+ Event.RawData = "team-update";
+ OnWsEvent.Broadcast(Event);
+}
+
+void UDTFluxSubsystem::ProcessStatusUpdateResponse(const FDTFluxStatusUpdateResponse& StatusUpdateResponse)
+{
+ UE_LOG(LogDTFluxAPI, Log, TEXT("Processing status-update data"));
+ DataStorage->UpdateParticipantStatus(StatusUpdateResponse);
+ FDTFluxWsResponseEvent Event;
+ Event.WsResponseType = StatusUpdate;
+ Event.RawData = "status-update";
+ OnWsEvent.Broadcast(Event);
+}
+
+void UDTFluxSubsystem::ProcessSplitSensor(const FDTFluxSplitSensorResponse& SplitSensorResponse)
+{
+ //
+
+ for(auto& SplitSensorItem : SplitSensorResponse.Datas)
+ {
+ FDTFluxSplitRanking NewRanking = DataStorage->AddSplitRanking(SplitSensorItem);
+ UE_LOG(LogDTFluxAPI, Log, TEXT("Checking SplitStatus ..."))
+ EDTFluxSplitType SplitType = DataStorage->GetSplitStatus(SplitSensorItem.ContestID,
+ SplitSensorItem.StageID, SplitSensorItem.SplitID);
+ FDTFluxFinisherData Data;
+ Data.Bib = SplitSensorItem.Bib;
+ Data.ContestId = SplitSensorItem.ContestID;
+ Data.StageId = SplitSensorItem.StageID;
+ Data.SplitRanking = NewRanking;
+ switch(SplitType)
+ {
+ case PreFinnishSplit:
+
+ UE_LOG(LogDTFluxAPI, Warning, TEXT("SplitSensor %d for Stage%02d in Contest%02d is a Prefinish Sensor"),
+ SplitSensorItem.SplitID, SplitSensorItem.StageID, SplitSensorItem.ContestID);
+ OnSpotter.Broadcast(Data);
+ break;
+ case FinishSplit:
+ UE_LOG(LogDTFluxAPI, Warning, TEXT("SplitSensor %d for Stage%02d in Contest%02d is a Finish Sensor"),
+ SplitSensorItem.SplitID, SplitSensorItem.StageID, SplitSensorItem.ContestID);
+ DataStorage->GetStageRankingForBib(SplitSensorItem.ContestID, SplitSensorItem.StageID, SplitSensorItem.Bib, Data.StageRanking);
+ OnFinisher.Broadcast(Data);
+ break;
+ case NormalSplit:
+ UE_LOG(LogDTFluxAPI, Warning, TEXT("SplitSensor %d for Stage%02d in Contest%02d is a Normal Split"),
+ SplitSensorItem.SplitID, SplitSensorItem.StageID, SplitSensorItem.ContestID);
+ OnSplitSensor.Broadcast(NewRanking);
+ break;
+ default:
+ UE_LOG(LogDTFluxAPI, Error, TEXT("SplitSensor %d for Stage%02d in Contest%02d %s"),
+ SplitSensorItem.SplitID, SplitSensorItem.StageID, SplitSensorItem.ContestID,
+ *UEnum::GetValueAsString(SplitType));
+ break;
+ }
+ }
+ FDTFluxWsResponseEvent Event;
+ Event.WsResponseType = SplitSensor;
+ Event.RawData = "split-sensor";
+ OnWsEvent.Broadcast(Event);
+
+}
+
+void UDTFluxSubsystem::ProcessArchSelect(FDTFluxArchSelectResponseItem ArchSelectResponse)
+{
+ OnArchSelect.Broadcast(ArchSelectResponse.ContestId, ArchSelectResponse.StageId);
+}
+
+TArray UDTFluxSubsystem::SortByRank(TArray SplitRankingArray)
+{
+ SplitRankingArray.Sort([](const FDTFluxSplitRanking& A, const FDTFluxSplitRanking& B )
+{
+ return A.Rank < B.Rank;
+});
+ return SplitRankingArray;
+}
diff --git a/Plugins/DTFluxAPI/Source/DTFluxAPI/Private/DTFluxSubsystem/DTFluxSubsystemTimer.cpp b/Plugins/DTFluxAPI/Source/DTFluxAPI/Private/DTFluxSubsystem/DTFluxSubsystemTimer.cpp
new file mode 100644
index 0000000..9d0a158
--- /dev/null
+++ b/Plugins/DTFluxAPI/Source/DTFluxAPI/Private/DTFluxSubsystem/DTFluxSubsystemTimer.cpp
@@ -0,0 +1,207 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+
+#include "DTFluxSubsystem/DTFluxSubsystemTimer.h"
+
+#include "DTFluxAPILog.h"
+#include "DTFluxSubsystem/DTFluxSubsystem.h"
+
+void UDTFluxSubsystemTimer::Initialize(FSubsystemCollectionBase& Collection)
+{
+ Super::Initialize(Collection);
+ // Not Necessary Anymore
+ // UDTFluxSubsystem* Subsystem = GetDTFluxSubSystem();
+ // Subsystem->OnRaceDataReceived.AddDynamic(this, &UDTFluxSubsystemTimer::OnDataStorageInit);
+
+}
+
+void UDTFluxSubsystemTimer::Deinitialize()
+{
+ Super::Deinitialize();
+}
+
+void UDTFluxSubsystemTimer::Tick(float DeltaTime)
+{
+ Super::Tick(DeltaTime);
+}
+
+
+
+void UDTFluxSubsystemTimer::OnDataStorageInit()
+{
+ UE_LOG(LogDTFluxAPI, Log, TEXT("DataStorage Has been Set Or Updated"));
+ UDTFluxDataStorage* DataStorage = GetDTFluxDataStorage();
+ for(const auto&Contest : DataStorage->Contests)
+ {
+ for (const auto& Stage: Contest.Stages)
+ {
+
+ UWorld* World = GetWorld();
+ if(World)
+ {
+ FDTFluxContestTimerHandle StartContestTimerHandle;
+ StartContestTimerHandle.Type = EDTFluxTimerEventType::StageStart;
+ StartContestTimerHandle.ContestId = Contest.Id;
+ StartContestTimerHandle.StageId = Stage.Id;
+
+ FDTFluxContestTimerHandle CutOffContestTimerHandle;
+ CutOffContestTimerHandle.Type = EDTFluxTimerEventType::StageCutOff;
+ CutOffContestTimerHandle.ContestId = Contest.Id;
+ CutOffContestTimerHandle.StageId = Stage.Id;
+
+ float StartTimeTriggerSeconds = GetSecondsFrom(Stage.StartTime);
+ float CutOffTimeTriggerSeconds = GetSecondsFrom(Stage.CutOff);
+ if( StartTimeTriggerSeconds > 0)
+ {
+ UE_LOG(LogDTFluxAPI, Log, TEXT("Can Set Time to %04f Seconds"), StartTimeTriggerSeconds );
+ World->GetTimerManager().SetTimer(StartContestTimerHandle.Handle, this, &UDTFluxSubsystemTimer::OnStartTimer, StartTimeTriggerSeconds);
+ World->GetTimerManager().SetTimer(CutOffContestTimerHandle.Handle, this, &UDTFluxSubsystemTimer::OnCutOffTimer, CutOffTimeTriggerSeconds);
+ Timers.Add(StartContestTimerHandle);
+ Timers.Add(CutOffContestTimerHandle);
+
+ }
+ else
+ {
+ UE_LOG(LogDTFluxAPI, Warning, TEXT("Unable to Set Time to %04f Seconds"), StartTimeTriggerSeconds);
+ }
+ }
+
+ }
+ }
+}
+
+void UDTFluxSubsystemTimer::AddCutoffTimer(const int ContestID, const int StageID)
+{
+ UWorld* World = GetWorld();
+ if(World)
+ {
+ FTimerHandle Timer;
+ World->GetTimerManager().SetTimer(Timer, this, &UDTFluxSubsystemTimer::OnStartTimer, 1.0, true);
+ UE_LOG(LogDTFluxAPI, Warning, TEXT("AddCutoffTimer Added"));
+ }
+ UE_LOG(LogDTFluxAPI, Error,
+ TEXT("UDTFluxSubsystemTimer::AddCutoffTimer Cannot have the World"));
+}
+
+
+void UDTFluxSubsystemTimer::AddStageStartedTimer(const int ContestID, const int StageID)
+{
+ UWorld* World = GetWorld();
+ if(World)
+ {
+ FTimerHandle Timer;
+ World->GetTimerManager().SetTimer(Timer, this, &UDTFluxSubsystemTimer::OnStartTimer, 1.0, true);
+ UE_LOG(LogDTFluxAPI, Warning, TEXT("AddStageStartedTimer Added"));
+ }
+ UE_LOG(LogDTFluxAPI, Error,
+ TEXT("UDTFluxSubsystemTimer::AddStageStartedTimer Cannot have the World"));
+
+}
+
+void UDTFluxSubsystemTimer::OnStartTimer()
+{
+ UWorld* World = GetWorld();
+ if(World)
+ {
+ int Idx = 0 ;
+ for(auto& Timer : Timers)
+ {
+ if(Timer.Type == EDTFluxTimerEventType::StageStart)
+ {
+ if(World->GetTimerManager().GetTimerRemaining(Timer.Handle) == 0)
+ {
+ TArray ContestIds;
+ ContestIds.Add(Timer.ContestId);
+ OnStageStarted.Broadcast(ContestIds, Timer.StageId);
+ break;
+ }
+ }
+ Idx++;
+ }
+ if(Timers.IsValidIndex(Idx))
+ {
+ Timers.RemoveAt(Idx);
+ }
+ }
+}
+void UDTFluxSubsystemTimer::OnCutOffTimer()
+{
+ UWorld* World = GetWorld();
+ if(World)
+ {
+ int Idx = 0 ;
+ for(auto& Timer : Timers)
+ {
+ if(Timer.Type == EDTFluxTimerEventType::StageCutOff)
+ {
+ if(World->GetTimerManager().GetTimerRemaining(Timer.Handle) == 0)
+ {
+ TArray ContestIds;
+ ContestIds.Add(Timer.ContestId);
+ OnCutoff.Broadcast(ContestIds, Timer.StageId);
+ break;
+ }
+ }
+ Idx++;
+ }
+ if(Timers.IsValidIndex(Idx))
+ {
+ Timers.RemoveAt(Idx);
+ }
+ }
+}
+
+void UDTFluxSubsystemTimer::ClearTimer(FDTFluxContestTimerHandle TimerHandle)
+{
+ UWorld* World = GetWorld();
+ if(World)
+ {
+ World->GetTimerManager().ClearTimer(TimerHandle.Handle);
+ }
+ UE_LOG(LogDTFluxAPI, Error, TEXT("Cannot Clear Timer %s of type %s for Stage%02d of Contest%02d"),
+ *TimerHandle.Handle.ToString(), *UEnum::GetValueAsString(TimerHandle.Type),
+ TimerHandle.StageId, TimerHandle.ContestId)
+
+}
+
+void UDTFluxSubsystemTimer::ClearTimer(const int HandleIndex)
+{
+}
+
+
+void UDTFluxSubsystemTimer::TriggerOnCutOff(const TArray& ContestIds, const int StageId)
+{
+ OnCutoff.Broadcast(ContestIds, StageId);
+}
+
+void UDTFluxSubsystemTimer::TriggerStartTime(const TArray& ContestIds, const int StageId)
+{
+ OnStageStarted.Broadcast(ContestIds, StageId);
+}
+
+void UDTFluxSubsystemTimer::TriggerStageLoading(const TArray& ContestIds, const int StageId, int DelayBeforeStageStart)
+{
+ OnStageLoading.Broadcast(ContestIds, StageId, DelayBeforeStageStart);
+}
+
+void UDTFluxSubsystemTimer::TriggerOnDeleteRequested(const TArray& LineIndex)
+{
+ OnRemoveLineRequested.Broadcast(LineIndex);
+}
+
+UDTFluxSubsystem* UDTFluxSubsystemTimer::GetDTFluxSubSystem()
+{
+ return GEngine->GetEngineSubsystem();
+}
+
+UDTFluxDataStorage* UDTFluxSubsystemTimer::GetDTFluxDataStorage()
+{
+ return GetDTFluxSubSystem()->GetDataStorage();
+}
+
+float UDTFluxSubsystemTimer::GetSecondsFrom(const FDateTime When)
+{
+ FTimespan Delta = When - FDateTime::Now();
+ return static_cast(Delta.GetTotalSeconds()) ;
+}
+
diff --git a/Plugins/DTFluxAPI/Source/DTFluxAPI/Private/DTFluxSubsystemAPISettings/DTFluxSubsystemAPISettings.cpp b/Plugins/DTFluxAPI/Source/DTFluxAPI/Private/DTFluxSubsystemAPISettings/DTFluxSubsystemAPISettings.cpp
new file mode 100644
index 0000000..6ddf833
--- /dev/null
+++ b/Plugins/DTFluxAPI/Source/DTFluxAPI/Private/DTFluxSubsystemAPISettings/DTFluxSubsystemAPISettings.cpp
@@ -0,0 +1,70 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#include "DTFluxSubsystemAPISettings/DTFluxSubsystemAPISettings.h"
+#include "DTFluxAPILog.h"
+
+// DEPRECATED : Now in WS
+FString FDTFluxSubsystemAPISettings::GetRaceDataEndpoint(const FDTFluxSubsystemAPISettings* Settings)
+{
+ if(Settings)
+ {
+ FString RaceDataEndpoint =
+ FString::Printf(TEXT("%s/%p"), *Settings->GetProxyBaseEndpoint(), Settings->ProxyEndpoints.FindKey("race-datas"));
+ UE_LOG(LogDTFluxAPI, Log, TEXT("Proxy Race Data -> %s"), *RaceDataEndpoint);
+ return RaceDataEndpoint;
+ }
+ return FString("");
+}
+// DEPRECATED : Now in WS
+FString FDTFluxSubsystemAPISettings::GetContestRankingEndpoint(const FDTFluxSubsystemAPISettings* Settings, const int ContestId)
+{
+ if(Settings)
+ {
+ FString Ranking = *Settings->ProxyEndpoints.FindKey("ranking");
+ const TCHAR* ContestIDTmpl = *FString("{:ContestID}");
+ const TCHAR* ContestIDValue = *FString(TEXT("%i"),ContestId);
+ FString ContestRanking = Ranking.Replace(ContestIDTmpl, ContestIDValue );
+ FString ContestRankingEndpoint = Settings->GetProxyBaseEndpoint() + ContestRanking;
+ UE_LOG(LogDTFluxAPI, Log, TEXT("Proxy Contest Ranking -> %s"), *ContestRankingEndpoint);
+ return ContestRankingEndpoint;
+ }
+ return FString("");
+}
+// DEPRECATED : Now in WS
+FString FDTFluxSubsystemAPISettings::GetStageRankingEndpoint(const FDTFluxSubsystemAPISettings* Settings, const int ContestId,
+ const int StageId)
+{
+ if(Settings)
+ {
+ FString StageRanking = GetContestRankingEndpoint(Settings, ContestId);
+ StageRanking = FString::Printf(TEXT("%s/stage/%i/"), *StageRanking, StageId);
+ UE_LOG(LogDTFluxAPI, Log, TEXT("Proxy Stage Ranking -> %s"), *StageRanking);
+ return StageRanking;
+ }
+ return FString("");
+}
+// DEPRECATED : Now in WS
+FString FDTFluxSubsystemAPISettings::GetStageRankingFilteredEndpoint(const FDTFluxSubsystemAPISettings* Settings,
+ const int ContestId, const int StageId, const FString SplitName)
+{
+ if (Settings){
+ FString StageRanking = GetStageRankingEndpoint(Settings, ContestId, StageId);
+ StageRanking = FString::Printf(TEXT("%s?splitname=%s"), *StageRanking, *SplitName);
+ UE_LOG(LogDTFluxAPI, Log, TEXT("Proxy Stage Ranking with Splitname -> %s"), *StageRanking);
+ return StageRanking;
+ }
+ return FString("");
+}
+
+// DEPRECATED : Now in WS
+FString FDTFluxSubsystemAPISettings::GetTeamsEndpoint(const FDTFluxSubsystemAPISettings* Settings)
+{
+ if(Settings)
+ {
+ FString TeamsEndpoint =
+ FString::Printf(TEXT("%s/%p"), *Settings->GetProxyBaseEndpoint(), Settings->ProxyEndpoints.FindKey("teams"));
+ UE_LOG(LogDTFluxAPI, Log, TEXT("Proxy Teams -> %s"), *TeamsEndpoint );
+ return TeamsEndpoint;
+ }
+ return FString("");
+}
diff --git a/Plugins/DTFluxAPI/Source/DTFluxAPI/Private/DTFluxUtils/DTFluxEnums.cpp b/Plugins/DTFluxAPI/Source/DTFluxAPI/Private/DTFluxUtils/DTFluxEnums.cpp
new file mode 100644
index 0000000..14c68b7
--- /dev/null
+++ b/Plugins/DTFluxAPI/Source/DTFluxAPI/Private/DTFluxUtils/DTFluxEnums.cpp
@@ -0,0 +1,4 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+
+#include "DTFluxUtils/DTFluxEnums.h"
diff --git a/Plugins/DTFluxAPI/Source/DTFluxAPI/Private/DTFluxUtils/DTFluxUtils.cpp b/Plugins/DTFluxAPI/Source/DTFluxAPI/Private/DTFluxUtils/DTFluxUtils.cpp
new file mode 100644
index 0000000..4a73986
--- /dev/null
+++ b/Plugins/DTFluxAPI/Source/DTFluxAPI/Private/DTFluxUtils/DTFluxUtils.cpp
@@ -0,0 +1,88 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+
+#include "DTFluxUtils/DTFluxUtils.h"
+
+#include "DTFluxModel/DTFluxModel.h"
+
+EDTFluxStageStatusType UDTFluxModelHelper::GetStatusType(const int ContestID, const int StageID,
+ UDTFluxDataStorage* DataStorage)
+{
+ EDTFluxStageStatusType StageStatus = UnknownStatus;
+
+ FDTFluxStage SelectedStage;
+ if( DataStorage->GetStage(ContestID, StageID, SelectedStage))
+ {
+ StageStatus = StageWaiting;
+ FDateTime Now = FDateTime::Now();
+ if(SelectedStage.StartTime <= Now)
+ {
+ StageStatus = StageStarted;
+ }
+ if(SelectedStage.CutOff <= Now)
+ {
+ StageStatus = StageEnded;
+ }
+ }
+ return StageStatus;
+}
+
+int UDTFluxModelHelper::GetCurrentContest(UDTFluxDataStorage* DataStorage)
+{
+ int ContestId = -1;
+ FDateTime Now = FDateTime::Now();
+ for(const auto& Contest : DataStorage->Contests)
+ {
+ for(const auto& Stage : Contest.Stages)
+ {
+ // Stage has begun
+ if(Stage.StartTime <= Now)
+ {
+ return Contest.Id;
+ }
+ }
+ }
+ return ContestId;
+}
+
+TArray UDTFluxModelHelper::GetCurrentStage(UDTFluxDataStorage* DataStorage)
+{
+ TArray ContestAndStageId;
+ FDateTime Now = FDateTime::Now();
+ for(const auto& Contest : DataStorage->Contests)
+ {
+ for(const auto& Stage : Contest.Stages)
+ {
+ // Stage has begun
+ if(Stage.StartTime <= Now)
+ {
+ ContestAndStageId.Add(Contest.Id);
+ ContestAndStageId.Add(Stage.Id);
+ return ContestAndStageId;
+ }
+ }
+ }
+ return ContestAndStageId;
+}
+
+FString UDTFluxModelHelper::GetCurrentStageName(UDTFluxDataStorage* DataStorage)
+{
+ FString Name;
+ FDateTime Now = FDateTime::Now();
+ for(const auto& Contest : DataStorage->Contests)
+ {
+ for(const auto& Stage : Contest.Stages)
+ {
+ // Stage has begun
+ if(Stage.StartTime <= Now)
+ {
+ Name = FString::Printf(TEXT("Contest %s Stage %s"), *Contest.Name, *Stage.Name);
+ return Name;
+ }
+ }
+ }
+ return Name;
+}
+
+
+
diff --git a/Plugins/DTFluxAPI/Source/DTFluxAPI/Private/DTFluxWebSocket/DTFluxWebsocketClient.cpp b/Plugins/DTFluxAPI/Source/DTFluxAPI/Private/DTFluxWebSocket/DTFluxWebsocketClient.cpp
new file mode 100644
index 0000000..cb5f96b
--- /dev/null
+++ b/Plugins/DTFluxAPI/Source/DTFluxAPI/Private/DTFluxWebSocket/DTFluxWebsocketClient.cpp
@@ -0,0 +1,120 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+
+#include "DTFluxWebSocket/DTFluxWebsocketClient.h"
+// Fill out your copyright notice in the Description page of Project Settings.
+
+
+#include "DTFluxWebSocket/DTFluxWebsocketClient.h"
+#include "IWebSocket.h"
+#include "WebSocketsModule.h"
+#include "DTFluxAPILog.h"
+
+
+
+// UDTFluxWebSocketClient::~UDTFluxWebSocketClient()
+// {
+//
+// }
+
+void UDTFluxWebSocketClient::Initialize()
+{
+}
+
+// void UDTFluxWebSocketClient::BeginDestroy()
+// {
+// // if(Ws->IsConnected())
+// // Ws->Close();
+// UObject::BeginDestroy();
+// }
+
+bool UDTFluxWebSocketClient::Connect(const FString URL, const int Port)
+{
+ FString ServerUrl = FString::Printf(TEXT("%s:%i/"), *URL, Port);
+
+ UE_LOG(LogDTFluxAPI, Log, TEXT("[DTFluxWebsocketClient %s"), *ServerUrl);
+ Ws = FWebSocketsModule::Get().CreateWebSocket(ServerUrl);
+ Ws->OnConnected().AddLambda([this]()
+ {
+ if(IsValid(this) == false) return;
+ OnConnectionConnectedInternal();
+ });
+
+ Ws->OnClosed().AddLambda([this](int32 StatusCode, const FString& Reason, bool bWasClean)
+ {
+ if(IsValid(this) == false) return;
+ OnConnectionClosedInternal(Reason);
+ });
+
+ Ws->OnConnectionError().AddLambda([this](const FString& Error)
+ {
+ if(IsValid(this) == false) return;
+ OnConnectionErrorInternal(Error);
+ });
+
+ Ws->OnMessage().AddLambda([this](const FString& MessageString)
+ {
+ if(IsValid(this) == false) return;
+ OnReceivedMessageInternal(MessageString);
+ });
+ Ws->Connect();
+ if(Ws->IsConnected())
+ {
+ UE_LOG(LogDTFluxAPI, Log, TEXT("Listening"));
+ return true;
+ }
+
+ return false;
+}
+
+void UDTFluxWebSocketClient::Disconnect()
+{
+ Ws->Close();
+}
+
+bool UDTFluxWebSocketClient::SendMessage(const FString Message, const bool Broadcast)
+{
+ if(Ws->IsConnected() == false) return false;
+ Ws->Send(Message);
+ return true;
+}
+
+void UDTFluxWebSocketClient::OnConnectionConnectedInternal() const
+{
+ OnConnectionConnected.Broadcast();
+ UE_LOG(LogDTFluxAPI, Log, TEXT("WebSocketsSubsystem, [Connected]"));
+}
+
+void UDTFluxWebSocketClient::OnConnectionErrorInternal(const FString& Error) const
+{
+ OnConnectionError.Broadcast( Error);
+ UE_LOG(LogDTFluxAPI, Error, TEXT("WebSocketsSubsystem, [Error] : %s"), *Error);
+}
+
+void UDTFluxWebSocketClient::OnConnectionClosedInternal(const FString& Reason) const
+{
+ OnConnectionClosed.Broadcast(Reason);
+ UE_LOG(LogDTFluxAPI, Log, TEXT("WebSocketsSubsystem, [Closed], Reason : %s"), *Reason);
+
+}
+
+void UDTFluxWebSocketClient::OnReceivedMessageInternal(const FString& Message) const
+{
+ OnReceivedMessage.Broadcast(Message);
+ // UE_LOG(LogDTFluxAPI, Log, TEXT("WebSocketsSubsystem, [Message Reveived], Message : %s"), *Message);
+
+}
+
+bool UDTFluxWebSocketClient::Close() const
+{
+ if (Ws->IsConnected())
+ Ws->Close();
+ return Ws->IsConnected();
+}
+
+bool UDTFluxWebSocketClient::IsConnected()
+{
+ return Ws->IsConnected();
+}
+
+
diff --git a/Plugins/DTFluxAPI/Source/DTFluxAPI/Private/DTFluxWebSocket/DTFluxWebsocketServer.cpp b/Plugins/DTFluxAPI/Source/DTFluxAPI/Private/DTFluxWebSocket/DTFluxWebsocketServer.cpp
new file mode 100644
index 0000000..20ee74d
--- /dev/null
+++ b/Plugins/DTFluxAPI/Source/DTFluxAPI/Private/DTFluxWebSocket/DTFluxWebsocketServer.cpp
@@ -0,0 +1,246 @@
+// // Fill out your copyright notice in the Description page of Project Settings.
+//
+//
+// #include "DTFluxWebSocket/DTFluxWebsocketServer.h"
+//
+// #include
+//
+// #include "IWebSocket.h"
+// #include "WebSocketsModule.h"
+// #include "INetworkingWebSocket.h"
+// #include "IWebSocketNetworkingModule.h"
+// #include "WebSocketNetworkingDelegates.h"
+// #include "DTFluxAPILog.h"
+// #include "IWebSocketServer.h"
+//
+//
+// UDTFluxWebsocketServer::UDTFluxWebsocketServer()
+// {
+// Ws = UDTFluxWebsocketServer::GetServer();
+// }
+//
+// UDTFluxWebsocketServer::~UDTFluxWebsocketServer()
+// {
+//
+// }
+//
+// bool UDTFluxWebsocketServer::ConnectToChannel(const FString URL, const int Port)
+// {
+// //try to get a WsServer
+// if(!Ws)
+// Ws = UDTFluxWebsocketServer::GetServer();
+// if(Ws)
+// {
+// FWebSocketClientConnectedCallBack ClientConnectedCb;
+// ClientConnectedCb.BindUObject(this, &UDTFluxWebsocketServer::OnConnectionConnectedInternal);
+// if(Ws->Init((uint32) Port, ClientConnectedCb, FString("127.0.0.1")) )
+// {
+// UE_LOG(LogDTFluxAPI, Log, TEXT("Server listening on %s:%i"),*URL, Port);
+// if (Ws.IsValid())
+// UE_LOG(LogDTFluxAPI, Log, TEXT("Server pointer Is Valid %s"), *Ws->Info());
+//
+// return true;
+// }
+// else
+// {
+// UE_LOG(LogDTFluxAPI, Error, TEXT("Server Init() failed"));
+// Ws.Reset();
+// ClientConnectedCb.Unbind();
+// return false;
+// }
+// }
+// UE_LOG(LogDTFluxAPI, Error, TEXT("Unable to get a WsServer Object"));
+//
+// return false;
+// }
+//
+// void UDTFluxWebsocketServer::LeaveChannel()
+// {
+// bIsConnected = false;
+// Ws = nullptr;
+// }
+//
+// bool UDTFluxWebsocketServer::SendMessageToAll(const FString Message)
+// {
+// if(!Ws)
+// {
+// UE_LOG(LogDTFluxAPI, Error, TEXT("WebSocketServer Not Up"));
+// return false;
+// }
+// if(Clients.Num() <= 0)
+// {
+// UE_LOG(LogDTFluxAPI, Log, TEXT("No Clients Yet !!!"));
+// return false;
+// }
+// int i = 0;
+// for(const auto& Client : Clients)
+// {
+// const TCHAR* SerializedChar = Message.GetCharArray().GetData();
+// int32 Size = FCString::Strlen(SerializedChar);
+// int32 Sent = 0;
+// uint8* Data = (uint8*)TCHAR_TO_UTF8(SerializedChar);
+// if(Client->Send(Data, Size))
+// {
+// UE_LOG(LogDTFluxAPI, Log, TEXT("Message %s sent to client n°%i"), *Message, i);
+// i++;
+// }
+// }
+// if(i != 0)
+// {
+// UE_LOG(LogDTFluxAPI, Log, TEXT("%i Messages sent"), i);
+// return true;
+// }
+// UE_LOG(LogDTFluxAPI, Error, TEXT("Message has not been sent"));
+// return false;
+// }
+//
+// void UDTFluxWebsocketServer::OnConnectionConnectedInternal(INetworkingWebSocket* ClientWebSocket)
+// {
+// OnConnectionConnected.Broadcast(ChannelName);
+// FWebSocketPacketReceivedCallBack PacketReceivedCallBack;
+// PacketReceivedCallBack.BindUObject(this, &UDTFluxWebsocketServer::OnReceivedMessageInternal);
+// ClientWebSocket->SetReceiveCallBack(PacketReceivedCallBack);
+// FWebSocketInfoCallBack InfoCallBack;
+// InfoCallBack.BindLambda([this, ClientWebSocket]()
+// {
+// int i = 0;
+//
+// for(const auto& Client : Clients)
+// {
+// int Removed = Clients.Remove(Client);
+// if(Removed)
+// UE_LOG(LogDTFluxAPI, Log, TEXT("Client Disconnected %i"), i);
+// i++;
+// }
+// });
+// ClientWebSocket->SetSocketClosedCallBack(InfoCallBack);
+// FWebSocketInfoCallBack InfoCallBackError;
+// InfoCallBackError.BindUObject(this, &UDTFluxWebsocketServer::OnConnectionErrorInternal);
+// ClientWebSocket->SetErrorCallBack(InfoCallBackError);
+// UE_LOG(LogDTFluxAPI, Log, TEXT("WebSocketServer', [Connected], Channel: %s"), *ChannelName);
+// }
+//
+// void UDTFluxWebsocketServer::OnConnectionErrorInternal()
+// {
+// UE_LOG(LogDTFluxAPI, Error, TEXT("WebSocketServer'"));
+// OnConnectionError.Broadcast(TEXT("Channel"), TEXT("UNKNOWN ERROR"));
+//
+// }
+//
+// void UDTFluxWebsocketServer::OnReceivedMessageInternal(void* Data, int32 Count)
+// {
+// FString Message;
+// if (Count == 0) // nothing to process
+// {
+// return;
+// }
+// const uint8* DataRef = reinterpret_cast(Data);
+// const TArray MessageData(DataRef, Count);
+// const FString JSonData = UTF8_TO_TCHAR(MessageData.GetData());
+// OnReceivedMessage.Broadcast(ChannelName, Message);
+// UE_LOG(LogDTFluxAPI, Log, TEXT("WebSocketServer, [Message Reveived]\nMessage : %s"), *Message);
+//
+// }
+//
+// TUniquePtr UDTFluxWebsocketServer::GetServer()
+// {
+// return FModuleManager::Get().LoadModuleChecked(TEXT("WebSocketNetworking")).
+// CreateServer();
+// }
+//
+// void UDTFluxWebsocketServer::Close()
+// {
+// UE_LOG(LogDTFluxAPI, Log, TEXT("WebSocketServer, [Closed()]"));
+//
+// }
+
+
+#include "DTFluxWebSocket/DTFluxWebsocketServer.h"
+//Fill out your copyright notice in the Description page of Project Settings.
+
+#include "INetworkingWebSocket.h"
+#include "IWebSocketNetworkingModule.h"
+#include "WebSocketNetworkingDelegates.h"
+#include "DTFluxAPILog.h"
+
+
+UDTFluxServerWebSocket::UDTFluxServerWebSocket()
+{
+}
+
+UDTFluxServerWebSocket::~UDTFluxServerWebSocket()
+{
+}
+
+void UDTFluxServerWebSocket::Init(const int& Port, const FString& Url)
+{
+ ServerWebSocket = FModuleManager::Get().LoadModuleChecked(TEXT("WebSocketNetworking")).CreateServer();
+
+ FWebSocketClientConnectedCallBack CallBack;
+ CallBack.BindLambda([this](INetworkingWebSocket* Client)
+ {
+
+ FGuid uuid = FGuid::NewGuid();
+ ConnectedClients.Add(uuid, Client);
+ });
+
+ if (!ServerWebSocket->Init(Port, CallBack, Url))
+ {
+ UE_LOG(LogDTFluxAPI, Error, TEXT("ServerWebSocket Init FAIL"));
+ ServerWebSocket.Reset();
+ CallBack.Unbind();
+ return;
+ }
+ UE_LOG(LogDTFluxAPI, Log, TEXT("ServerWebSocket Connected to port %d"), Port);
+
+
+ TickHandle = FTSTicker::GetCoreTicker().AddTicker(FTickerDelegate::CreateLambda([this](float Time)
+ {
+ if (ServerWebSocket)
+ {
+ ServerWebSocket->Tick();
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }));
+}
+
+void UDTFluxServerWebSocket::BeginDestroy()
+{
+ Super::BeginDestroy();
+ Close();
+
+ if (TickHandle.IsValid())
+ {
+ FTSTicker::GetCoreTicker().RemoveTicker(TickHandle);
+ TickHandle.Reset();
+ }
+}
+
+void UDTFluxServerWebSocket::OnWebSocketClientConnected(INetworkingWebSocket* ClientWebSocket)
+{
+ FWebSocketPacketReceivedCallBack CallBack;
+ CallBack.BindUObject(this, &UDTFluxServerWebSocket::ReceivedRawPacket);
+ UE_LOG(LogDTFluxAPI, Log, TEXT("ServerWebSocket Connected"));
+
+ ClientWebSocket->SetReceiveCallBack(CallBack);
+}
+
+void UDTFluxServerWebSocket::ReceivedRawPacket(void* Data, int32 Count)
+{
+ if (Count == 0) // nothing to process
+ {
+ return;
+ }
+
+ const uint8* DataRef = reinterpret_cast(Data);
+
+ const TArray MessageData(DataRef, Count);
+
+ const FString JSonData = UTF8_TO_TCHAR(MessageData.GetData());
+ UE_LOG(LogDTFluxAPI, Log, TEXT("ServerWebSocket received %s"), *JSonData);
+ OnJsonRecieved.Broadcast(JSonData);
+}
\ No newline at end of file
diff --git a/Plugins/DTFluxAPI/Source/DTFluxAPI/Public/DTFluxAPI.h b/Plugins/DTFluxAPI/Source/DTFluxAPI/Public/DTFluxAPI.h
new file mode 100644
index 0000000..a805861
--- /dev/null
+++ b/Plugins/DTFluxAPI/Source/DTFluxAPI/Public/DTFluxAPI.h
@@ -0,0 +1,15 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "Modules/ModuleManager.h"
+
+class FDTFluxAPIModule : public IModuleInterface
+{
+public:
+
+ /** IModuleInterface implementation */
+ virtual void StartupModule() override;
+ virtual void ShutdownModule() override;
+};
diff --git a/Plugins/DTFluxAPI/Source/DTFluxAPI/Public/DTFluxAPILog.h b/Plugins/DTFluxAPI/Source/DTFluxAPI/Public/DTFluxAPILog.h
new file mode 100644
index 0000000..30d3703
--- /dev/null
+++ b/Plugins/DTFluxAPI/Source/DTFluxAPI/Public/DTFluxAPILog.h
@@ -0,0 +1,6 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+#pragma once
+
+#include "CoreMinimal.h"
+
+DECLARE_LOG_CATEGORY_EXTERN(LogDTFluxAPI, Log, All);
\ No newline at end of file
diff --git a/Plugins/DTFluxAPI/Source/DTFluxAPI/Public/DTFluxCountDown/DTFluxCountDownComponent.h b/Plugins/DTFluxAPI/Source/DTFluxAPI/Public/DTFluxCountDown/DTFluxCountDownComponent.h
new file mode 100644
index 0000000..a8d50bf
--- /dev/null
+++ b/Plugins/DTFluxAPI/Source/DTFluxAPI/Public/DTFluxCountDown/DTFluxCountDownComponent.h
@@ -0,0 +1,80 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "AvaText3DComponent.h"
+#include "Components/ActorComponent.h"
+#include "DTFluxDataStorage/DTFluxDataStorage.h"
+#include "DTFluxCountDownComponent.generated.h"
+
+
+
+
+
+UCLASS(ClassGroup=(DTFlux), meta=(BlueprintSpawnableComponent))
+class DTFLUXAPI_API UDTFluxCountDownComponent : public UActorComponent
+{
+ GENERATED_BODY()
+
+public:
+ // Sets default values for this component's properties
+ UDTFluxCountDownComponent();
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|Counter")
+ FDateTime GoTime;
+ UPROPERTY(BlueprintSetter=SetDuration, Category="DTFlux|Counter")
+ int32 Duration;
+
+
+protected:
+ // Called when the game starts
+ virtual void BeginPlay() override;
+ UPROPERTY()
+ UAvaText3DComponent* TextRef;
+ FTimerHandle WaitingTimer;
+ FTimerHandle ContDownTimer;
+ int64 InternalDuration;
+ bool IsWaiting;
+ bool IsCounting;
+ UDTFluxDataStorage* DataStorage;
+
+
+
+public:
+
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|Counter")
+ FString EndString;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|Counter")
+ float WaitingRate = 1.0f;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|Counter")
+ float CountDownRate = 1.0f;
+
+ // Called every frame
+ virtual void TickComponent(float DeltaTime, ELevelTick TickType,
+ FActorComponentTickFunction* ThisTickFunction) override;
+
+
+
+ UFUNCTION(BlueprintCallable, Category="DTFlux|Counter")
+ void SetGoTime(FDateTime NewGoTime);
+ UFUNCTION(BlueprintCallable, Category="DTFlux|Counter")
+ void SetDuration(int32 NewDuration);
+
+ // set the current stage
+ UFUNCTION(BlueprintCallable, Category="DTFlux|Counter")
+ void SetStage(const int ContestId, const int StageId){};
+
+ // set the current contest
+ UFUNCTION(BlueprintCallable, Category="DTFlux|Counter")
+ void SetContest(const int ContestId){};
+
+ UFUNCTION(BlueprintCallable, Category="DTFlux|Counter")
+ void SetTarget(UAvaText3DComponent* TextComponent);
+
+ UFUNCTION()
+ void CountUpTimerFn();
+
+ UFUNCTION()
+ void WaitingTimerFn();
+
+};
diff --git a/Plugins/DTFluxAPI/Source/DTFluxAPI/Public/DTFluxDataStorage/DTFluxDataStorage.h b/Plugins/DTFluxAPI/Source/DTFluxAPI/Public/DTFluxDataStorage/DTFluxDataStorage.h
new file mode 100644
index 0000000..ace41ba
--- /dev/null
+++ b/Plugins/DTFluxAPI/Source/DTFluxAPI/Public/DTFluxDataStorage/DTFluxDataStorage.h
@@ -0,0 +1,133 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "DTFluxModel/DTFluxModel.h"
+#include "UObject/Object.h"
+#include "DTFluxDataStorage.generated.h"
+
+/**
+ *
+ */
+struct FDTFluxStageRanking;
+struct FDTFluxTeam;
+struct FDTFluxParticipant;
+struct FDTFluxStage;
+struct FDTFluxContest;
+
+
+DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnDataStorageUpdated, FString, What);
+DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnDataStorageInit);
+
+UCLASS(BlueprintType, Category="DTFlux|Datastorage")
+class DTFLUXAPI_API UDTFluxDataStorage : public UObject
+{
+ GENERATED_BODY()
+ friend FDTFluxContest;
+ friend FDTFluxStage;
+
+public:
+
+ UPROPERTY(BlueprintAssignable, Category="DTFlux|DataStorage|Event")
+ FOnDataStorageInit OnDataStorageInit;
+ UPROPERTY(BlueprintAssignable, Category="DTFlux|DataStorage|Event")
+ FOnDataStorageUpdated OnDataStorageUpdated;
+
+ UPROPERTY(BlueprintReadOnly, Category="DTFlux|DataStorage")
+ TArray Contests;
+ UPROPERTY(BlueprintReadOnly, Category="DTFlux|DataStorage")
+ int CurrentStageId = 0;
+ UPROPERTY(BlueprintReadOnly, Category="DTFlux|DataStorage")
+ int CurrentContestId = 0;
+ UFUNCTION(BlueprintCallable, Category="DTFlux|DataStorage")
+ int CurrentContest()
+ {
+ if(IsInitialized())
+ {
+ return CurrentContestId;
+ }
+ return -1;
+ }
+
+ void UpdateSplitRanking(const FDTFluxStageRankingResponse& SplitRankingResponse);
+ // void UpdateParticipant(const FDTFluxTeamUpdateResponse& TeamUpdateResponse);
+ void UpdateParticipantStatus(const FDTFluxStatusUpdateResponse& StatusUpdateResponse);
+ bool IsFinisherSplit(const FDTFluxSplitSensorResponse& SplitSensorResponse);
+ FDTFluxFinisher GetFinisherStatus(const FDTFluxSplitSensorResponse& SplitSensorResponse);
+
+
+ UFUNCTION(BlueprintCallable, Category="DTFlux|DataStorage")
+ bool GetContest(const int ContestId, FDTFluxContest& OutContest);
+ UFUNCTION(BlueprintCallable, Category="DTFlux|DataStorage")
+ TArray GetStages(const int ContestId);
+ UFUNCTION(BlueprintCallable, Category="DTFlux|DataStorage")
+ bool GetStage( const int ContestId, const int StageId, FDTFluxStage& OutStage);
+
+ UFUNCTION(BlueprintCallable, Category="DTFlux|DataStorage")
+ TArray GetParticipants(const int ContestId = -1);
+ UFUNCTION()
+ void GetParticipant(const int ContestID, const int ParticipantBib, FDTFluxParticipant& OutParticipant);
+ UFUNCTION(BlueprintCallable, Category="DTFlux|DataStorage")
+ TArray GetStageRanking(const int ContestId, const int StageId);
+
+ UFUNCTION(BlueprintType, Category="DTFlux|Datastorage")
+ void AddOrUpdateContest(const FDTFluxContestResponse& ContestResponse);
+ UFUNCTION(BlueprintType, Category="DTFlux|Datastorage")
+ void AddOrUpdateParticipant(const FDTFluxTeamListItemResponse& TeamListItemResponse);
+ UFUNCTION(BlueprintType, Category="DTFlux|Datastorage")
+ void UpdateContestRanking(const FDTFluxContestRankingResponse& InContestRanking);
+ UFUNCTION(BlueprintType, Category="DTFlux|Datastorage")
+ void UpdateStageRanking(const FDTFluxStageRankingResponse& StageRankingResponse);
+
+ UFUNCTION(BlueprintCallable, Category="DTFlux")
+ bool IsInitialized()
+ {
+ return Contests.Num() < 0;
+ }
+
+ UFUNCTION(BlueprintCallable, Category="DTFlux|Datastorage")
+ bool GetParticipantByBib(const int Bib, FDTFluxParticipant& OutParticipant)
+ {
+ for(auto& Contest : Contests)
+ {
+ if(Contest.GetParticipant(Bib, OutParticipant))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ UFUNCTION(BlueprintCallable, Category="DTFlux")
+ void AddSplitSensorResult(FDTFluxSplitSensorItemResponse Response);
+ UFUNCTION(BlueprintCallable, Category="DTFlux")
+ void ResetStageId(){ CurrentStageId = 0; }
+ UFUNCTION(BlueprintCallable, Category="DTFlux")
+ void SetCurrentStage(int NewId){ CurrentStageId = NewId; }
+ UFUNCTION(BlueprintCallable, Category="DTFlux")
+ void GoToNextStage();
+ UFUNCTION(BlueprintCallable, Category="DTFlux")
+ void ChangeCurrentContest();
+ UFUNCTION(BlueprintCallable, Category="DTFlux|DataStorage")
+ const FString GetConcurrentFormatedName( int Bib, bool Truncate = true, int MaxSize = 20);
+ UFUNCTION(BlueprintCallable, Category="DTFlux|DataStorage")
+ // ReSharper disable once IdentifierTypo
+ TArray GetPoursuitWithStageTime(const TArray ContestIds, const int StageId, float DelaTimeSeconds = 300.0f);
+ UFUNCTION(BlueprintCallable, Category="DTFlux|DataStorage")
+ // ReSharper disable once IdentifierTypo
+ TArray GetPoursuitWithTimeStart(const TArray ContestIds, const int StageId, float DelaTimeSeconds);
+
+ UFUNCTION()
+ bool GetFirstStageOfContest(const int ContestId, FDTFluxStage& Stage);
+ void DumpContest();
+ UFUNCTION()
+ bool GetSplit(const int ContestID, const int StageID, const int SplitID, FDTFluxSplit& OutSplit);
+ UFUNCTION()
+ FDTFluxSplitRanking AddSplitRanking(const FDTFluxSplitSensorItemResponse& SplitSensorItem);
+ UFUNCTION()
+ EDTFluxSplitType GetSplitStatus(int ContestID, int StageID, int SplitID);
+
+ bool GetStageRankingForBib(int ContestID, int StageID, int Bib, FDTFluxStageRanking& OutStageRanking);
+};
diff --git a/Plugins/DTFluxAPI/Source/DTFluxAPI/Public/DTFluxModel/DTFluxModel.h b/Plugins/DTFluxAPI/Source/DTFluxAPI/Public/DTFluxModel/DTFluxModel.h
new file mode 100644
index 0000000..a874f40
--- /dev/null
+++ b/Plugins/DTFluxAPI/Source/DTFluxAPI/Public/DTFluxModel/DTFluxModel.h
@@ -0,0 +1,588 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "DTFluxAPILog.h"
+#include "DTFluxModelResponse.h"
+#include "DTFluxUtils/DTFluxEnums.h"
+#include "UObject/Object.h"
+#include "DTFluxModel.generated.h"
+
+
+USTRUCT(BlueprintType, Category="DTFlux|Model")
+struct DTFLUXAPI_API FDTFluxPerson
+{
+ GENERATED_BODY()
+public:
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
+ FString FirstName;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
+ FString LastName;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
+ FString Gender;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
+ FString FunctionLine1 = TEXT("");
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
+ FString FunctionLine2 = TEXT("");
+};
+
+USTRUCT(BlueprintType, Category="DTFlux|Model")
+struct DTFLUXAPI_API FDTFluxParticipant
+{
+ GENERATED_BODY()
+public:
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
+ int Bib = -1;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
+ FDTFluxPerson Person1;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
+ FDTFluxPerson Person2;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
+ FString Category;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
+ FString Club;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
+ bool Elite;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
+ TEnumAsByte Status;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
+ FString Team;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
+ int LastSplitId = 0;
+ bool IsTeam() const;
+ void Dump() const;
+ FString GetParticipantFormatedName(bool Truncate = false, int MaxSize = 20) const
+ {
+ FString ParticipantName;
+ if(Truncate)
+ {
+ if(IsTeam())
+ {
+ //Concatenate the team name;
+ if(Team.Len() > MaxSize - 3)
+ {
+ return Team.Left(MaxSize - 3).Append(TEXT("..."));
+ }
+ return Team;
+ }
+ if(Person1.FirstName.Contains("-") )
+ {
+ FString Formated = "";
+ //Compound Firstname
+ TArray Out;
+ Person1.FirstName.ParseIntoArray(Out,TEXT("-"),true);
+ for(const auto& Str : Out)
+ {
+ Formated.Append(Str.Left(1).ToUpper()).Append(".");
+ }
+ // TODO : Camel Case handling for LastName
+ Formated.Append(" ").Append(*Person1.LastName);
+ UE_LOG(LogDTFluxAPI, Log, TEXT("Firstname is with space compound. Formated Name %s length %02d MAX Size : %02d"),
+ *Formated, Formated.Len(), MaxSize);
+ if(Formated.Len() >= MaxSize)
+ {
+ UE_LOG(LogDTFluxAPI, Log, TEXT("Reducing %s Formated"), *Formated);
+
+ return Formated.Left(MaxSize - 3).Append("...");
+ }
+ return Formated;
+ }
+ if(Person1.FirstName.Contains(" "))
+ {
+ FString Formated = "";
+ //Compound Firstname
+ TArray Out;
+ Person1.FirstName.ParseIntoArray(Out,TEXT(" "),true);
+ for(const auto& Str : Out)
+ {
+ Formated.Append(Str.Left(1).ToUpper()).Append(".");
+ }
+ // TODO : Camel Case handling for LastName
+ Formated.Append(" ").Append(*Person1.LastName);
+ UE_LOG(LogDTFluxAPI, Log, TEXT("Firstname is with space compound. Formated Name %s length %02d MAX Size : %02d"),
+ *Formated, Formated.Len(), MaxSize);
+ if(Formated.Len() >= MaxSize)
+ {
+ UE_LOG(LogDTFluxAPI, Log, TEXT("Reducing %s Formated"), *Formated);
+ return Formated.Left(MaxSize - 3).Append("...");
+ }
+ return Formated;
+ }
+ FString Formated = Person1.FirstName.Left(1).Append(". ");
+ Formated.Append(Person1.LastName);
+ UE_LOG(LogDTFluxAPI, Log, TEXT("Firstname is not compound. Formated Name %s length %02d MAX Size : %02d"),
+ *Formated, Formated.Len(), MaxSize);
+ if(Formated.Len() >= MaxSize)
+ {
+ UE_LOG(LogDTFluxAPI, Log, TEXT("Reducing %s Formated"), *Formated);
+ return Formated.Left(MaxSize - 3).Append("...");
+ }
+ return Formated;
+ }
+ else
+ {
+ if(!IsTeam())
+ {
+ return FString::Printf(TEXT("%s %s"), *Person1.FirstName, *Person2.LastName);
+ }
+ return Team;
+ }
+ }
+};
+
+USTRUCT(BlueprintType, Category="DTFlux|Model")
+struct DTFLUXAPI_API FDTFluxContestRanking
+{
+ GENERATED_BODY()
+public:
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
+ int Bib;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
+ int Rank;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
+ FString Gap;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
+ FString Time;
+ void Dump () const
+ {
+ UE_LOG(LogDTFluxAPI, Log,
+ TEXT("FDTFluxContestRanking ->> \n \"rank\" : %d, Participant with Bib %d \"Gap\" : %s, \"Time\" : %s "),
+ Rank, Bib, *Gap, *Time );
+ };
+};
+
+USTRUCT(BlueprintType, Category="DTFlux|Model")
+struct DTFLUXAPI_API FDTFluxStageRanking
+{
+ GENERATED_BODY()
+public:
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
+ int Bib;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
+ int Rank;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
+ FString Gap;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
+ FString Time;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
+ FString TimeSwim;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
+ FString TimeTransition;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
+ FString TimeRun;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
+ FDateTime TimeStart;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
+ float SpeedRunning;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
+ float SpeedTotal;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
+ float SpeedSwim;
+
+
+
+ void Dump() const
+ {
+ UE_LOG(LogDTFluxAPI, Log, TEXT("RANKING : %02d. Participant bib %d %s %s %s %s %s"),
+ Rank, Bib, *Gap, *TimeSwim,
+ *TimeTransition, *TimeRun, *TimeStart.ToString());
+ }
+};
+
+USTRUCT(BlueprintType, Category="DTFlux|Model")
+struct DTFLUXAPI_API FDTFluxSplitRanking
+{
+ GENERATED_BODY()
+public:
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
+ int Bib;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
+ int ContestId = 0;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
+ int StageId = 0;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
+ int SplitId = 0;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
+ FString Gap;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
+ FString Time;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
+ int Rank = 0;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
+ bool Display = false;
+ void Dump() const
+ {
+ UE_LOG(LogDTFluxAPI, Log, TEXT("SplitGapItem"))
+ // Participant.Dump();
+ UE_LOG(LogDTFluxAPI, Log, TEXT("Bib %02d Rank %02d Gap %s Time %s"), Bib, Rank, *Gap, *Time);
+ }
+};
+
+USTRUCT(BlueprintType, Category="DTFlux|Model")
+// ReSharper disable once IdentifierTypo
+struct DTFLUXAPI_API FDTFluxFinisherData
+{
+ GENERATED_BODY()
+public:
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
+ int ContestId;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
+ int StageId;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
+ int Bib = -1;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
+ FDTFluxSplitRanking SplitRanking;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
+ FDTFluxStageRanking StageRanking;
+};
+
+USTRUCT(BlueprintType, Category="DTFlux|Model")
+struct DTFLUXAPI_API FDTFluxSplit
+{
+ GENERATED_BODY()
+public:
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
+ int Id = -1;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|Model")
+ FString Name;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
+ TArray SplitRankings;
+
+ void Dump() const
+ {
+ UE_LOG(LogDTFluxAPI, Log, TEXT("Split %02d::%s *****\n"), Id, *Name);
+ for(const auto& SplitGapItem : SplitRankings)
+ {
+ SplitGapItem.Dump();
+ }
+ }
+
+ void InsertOrReplace(const FDTFluxStageRankingResponseItem& SplitRankingItemResp)
+ {
+ FDTFluxSplitRanking NewSplitGapItem;
+ NewSplitGapItem.Bib = SplitRankingItemResp.Bib;
+ NewSplitGapItem.Gap = SplitRankingItemResp.Gap;
+ NewSplitGapItem.Rank = SplitRankingItemResp.Rank;
+ NewSplitGapItem.Time = SplitRankingItemResp.Time;
+ if(SplitRankings.IsEmpty())
+ {
+ SplitRankings.Add(NewSplitGapItem);
+ return;
+ }
+ bool Update = true;
+ int Idx = 0;
+ for(auto& SplitGapItem : SplitRankings)
+ {
+ if(SplitGapItem.Bib == SplitRankingItemResp.Bib)
+ {
+ Update = false;
+ }
+ Idx++;
+ }
+ if(Update)
+ {
+ if(SplitRankings.IsValidIndex(Idx))
+ {
+ SplitRankings.RemoveAt(Idx);
+ }
+ }
+ SplitRankings.Add(NewSplitGapItem);
+ };
+
+ void SortByRank()
+ {
+ SplitRankings.Sort([](const FDTFluxSplitRanking& A, const FDTFluxSplitRanking& B)
+ {
+ if(A.Rank == 0 && B.Rank == 0)
+ return true;
+ return A.Rank < B.Rank;
+ });
+ }
+
+ TArray GetSplitRanking(const int From = 0, const int DisplayNumber = 0)
+ {
+ TArray NewSplitRankings;
+ SortByRank();
+ NewSplitRankings.Append(SplitRankings);
+
+ if(From == 0 && DisplayNumber == 0)
+ return NewSplitRankings;
+ for(auto& SRank : SplitRankings)
+ {
+ if(SRank.Rank >= From)
+ {
+ NewSplitRankings.Add(SRank);
+ if(NewSplitRankings.Num() >= DisplayNumber)
+ {
+ return NewSplitRankings;
+ }
+ }
+ }
+ return NewSplitRankings;
+
+ }
+};
+
+USTRUCT(BlueprintType, Category="DTFlux|Model")
+struct DTFLUXAPI_API FDTFluxStage
+{
+ GENERATED_BODY()
+public:
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
+ int Id;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
+ FString Name;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
+ FDateTime StartTime;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
+ FDateTime EndTime;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
+ FDateTime CutOff;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
+ TArray Splits;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
+ TArray StageRanking;
+ bool SetStartTime(const FDateTime& ContestDate, const FString& TimeString);
+ bool SetEndTime(const FDateTime& ContestDate, const FString& TimeString);
+ void Dump() const
+ {
+ UE_LOG(LogDTFluxAPI, Log, TEXT("Stage %02d::%s"), Id, *Name);
+ UE_LOG(LogDTFluxAPI, Log, TEXT("Starts at %s and is supposed to finnish at %s"),
+ *StartTime.ToString(), *EndTime.ToString());
+ UE_LOG(LogDTFluxAPI, Log, TEXT("Splits : \n"));
+ for(const auto& StageRankingEl : StageRanking)
+ {
+ StageRankingEl.Dump();
+ }
+ UE_LOG(LogDTFluxAPI, Log, TEXT("Splits : \n"));
+ for(const auto& Split : Splits)
+ {
+ Split.Dump();
+ }
+
+ }
+
+ EDTFluxSplitType GetSplitType(int SplitID)
+ {
+ int SplitCount = Splits.Num();
+ //sort by ID
+ Splits.Sort([](const FDTFluxSplit& A, const FDTFluxSplit& B)
+ {
+ return A.Id < B.Id;
+ });
+ int SplitIndex = Splits.IndexOfByPredicate([SplitID](const FDTFluxSplit& Split)
+ {
+ return Split.Id == SplitID;
+ });
+
+ if(SplitCount -2 == SplitIndex )
+ {
+ return EDTFluxSplitType::PreFinnishSplit;
+ }
+ if(SplitCount -1 == SplitIndex)
+ {
+ return EDTFluxSplitType::FinishSplit;
+ }
+ return EDTFluxSplitType::NormalSplit;
+
+ };
+ void SortStageRanking()
+ {
+ StageRanking.Sort([](const FDTFluxStageRanking& A, const FDTFluxStageRanking& B)
+ {
+ if(A.Rank == 0 || B.Rank == 0)
+ return true;
+ return A.Rank > B.Rank;
+ });
+ };
+protected:
+};
+
+USTRUCT(BlueprintType, Category="DTFlux|Model")
+struct DTFLUXAPI_API FDTFluxContest
+{
+ GENERATED_BODY()
+public:
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
+ int Id = -1;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
+ FString Name;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
+ TArray Participants;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
+ TArray Stages;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
+ FDateTime Date;
+ UPROPERTY(BlueprintReadWrite, Category="DTFlux|model")
+ TArray ContestRanking;
+
+ bool SetDate(const FString& StringDate);
+ void AddParticipant(const FDTFluxParticipant& Participant)
+ {
+ if(Participants.IsEmpty())
+ {
+ Participants.Add(Participant);
+ return;
+ }
+ int Index = 0;
+ FDTFluxParticipant ToUpdate;
+ bool Update = false;
+ for(auto P : Participants)
+ {
+ if(P.Bib == Participant.Bib)
+ {
+ UE_LOG(LogDTFluxAPI, Log, TEXT("Idx %d, OLD : %s %s new %s %s in Contest%02d"),
+ Index, *P.Person1.FirstName, *P.Person1.LastName,
+ *Participant.Person1.FirstName, *Participant.Person1.LastName, Id);
+ ToUpdate = P;
+ Update = true;
+ break;
+ }
+ else
+ {
+ Index++;
+ }
+ }
+ if(Update)
+ {
+ if(Participants.IsValidIndex(Index))
+ {
+ UE_LOG(LogDTFluxAPI, Log, TEXT("Idx %d, REMOVED : %s %s in Contest%02d"),
+ Index,
+ *Participants[Index].Person1.FirstName, *Participants[Index].Person1.LastName, Id);
+ Participants.RemoveAt(Index);
+ }
+
+ }
+ Participants.Add(Participant);
+ };
+ bool GetParticipant(const int Bib, FDTFluxParticipant& OutParticipant)
+ {
+ for (auto& Participant : Participants)
+ {
+ if(Participant.Bib == Bib)
+ {
+ OutParticipant = Participant;
+ return true;
+ }
+ }
+ return false;
+ }
+ void DumpParticipant()
+ {
+ int Num = 0;
+ for(const auto& Participant: Participants )
+ {
+ UE_LOG(LogDTFluxAPI, Log, TEXT("DUMP Participant : Name -> %s Bib %d"), *Participant.Person1.FirstName, Participant.Bib);
+ Num ++;
+ }
+ UE_LOG(LogDTFluxAPI, Log, TEXT("DUMP Participant : In Contest with ID %d there are %d Participant(s)"), Id, Num);
+
+ };
+ bool AddContestRanking(const FDTFluxContestRanking& NewRanking)
+ {
+ bool Update = false;
+ if(ContestRanking.IsEmpty())
+ {
+ ContestRanking.Add(NewRanking);
+ return true;
+ }
+ int Idx = 0;
+ for( auto& Ranking : ContestRanking)
+ {
+ if(NewRanking.Bib == Ranking.Bib)
+ {
+ // we need to update a ranking
+ Update = true;
+ break;
+ }
+ Idx++;
+ }
+ if(Update)
+ {
+ ContestRanking.RemoveAt(Idx);
+ ContestRanking.Insert(NewRanking, Idx);
+ UE_LOG(LogDTFluxAPI, Log,
+ TEXT("Inserting %d with rank %d in Contest with ID %d"),
+ NewRanking.Bib, NewRanking.Rank, Id );
+ return true;
+ }
+
+ ContestRanking.Add(NewRanking);
+ return true;
+ }
+ void Dump()
+ {
+ UE_LOG(LogDTFluxAPI, Log, TEXT("CONTEST DUMP BEGIN *****%s::%02d *****\n"), *Name, Id);
+ UE_LOG(LogDTFluxAPI, Log, TEXT("Date : %s"), *Date.ToString());
+ UE_LOG(LogDTFluxAPI, Log, TEXT("PARTICIPANTS : \n"));
+ DumpParticipant();
+ for(auto& Stage: Stages)
+ {
+ Stage.Dump();
+ }
+ for(auto& ContestRankingEl: ContestRanking)
+ {
+ ContestRankingEl.Dump();
+ }
+ UE_LOG(LogDTFluxAPI, Log, TEXT("CONTEST DUMP END *****%s::%02d *****\n"), *Name, Id);
+ }
+ void SortContestRanking()
+ {
+ ContestRanking.Sort([](const FDTFluxContestRanking& A, const FDTFluxContestRanking& B)
+ {
+ if(A.Rank == 0 || B.Rank == 0)
+ return true;
+ return A.Rank > B.Rank;
+ });
+ };
+};
+
+USTRUCT(BlueprintType, Category="FDTFlux|Model")
+struct DTFLUXAPI_API FDTFluxFinisher
+{
+ GENERATED_BODY()
+
+ UPROPERTY(BlueprintReadOnly, Category="FDTFlux|Model")
+ TEnumAsByte Type;
+ UPROPERTY(BlueprintReadOnly, Category="FDTFlux|Model")
+ FDTFluxParticipant Participant;
+ UPROPERTY(BlueprintReadOnly, Category="FDTFlux|Model")
+ FDTFluxStageRanking CurrentRanking;
+};
+
+USTRUCT(BlueprintType, Category="DTFlux|Subsystem|Events")
+struct DTFLUXAPI_API FDTFluxStageFinished
+{
+ GENERATED_BODY()
+
+ UPROPERTY(BlueprintReadOnly, Category="DTFlux|Subsystem|Events")
+ int ContestId = 0;
+ UPROPERTY(BlueprintReadOnly, Category="DTFlux|Subsystem|Events")
+ int StageId = 0;
+ UPROPERTY(BlueprintReadOnly, Category="DTFlux|Subsystem|Events")
+ TArray Rankings;
+};
+
+USTRUCT(BlueprintType, Category="DTFlux|Subsystem|Events")
+struct DTFLUXAPI_API FDTFluxContestFinished
+{
+ GENERATED_BODY()
+
+ UPROPERTY(BlueprintReadOnly, Category="DTFlux|Subsystem|Events")
+ int ContestId = 0;
+ UPROPERTY(BlueprintReadOnly, Category="DTFlux|Subsystem|Events")
+ TArray Rankings;
+};
+
+USTRUCT(BlueprintType, Category="DTFlux|Subsystem")
+struct FDTFluxPoursuit
+{
+ GENERATED_BODY()
+
+ UPROPERTY(BlueprintReadOnly, Category="DTFlux|Subsystem")
+ int Bib;
+ UPROPERTY(BlueprintReadOnly, Category="DTFlux|Subsystem")
+ FDateTime TimeStart;
+};
\ No newline at end of file
diff --git a/Plugins/DTFluxAPI/Source/DTFluxAPI/Public/DTFluxModel/DTFluxModelResponse.h b/Plugins/DTFluxAPI/Source/DTFluxAPI/Public/DTFluxModel/DTFluxModelResponse.h
new file mode 100644
index 0000000..8168161
--- /dev/null
+++ b/Plugins/DTFluxAPI/Source/DTFluxAPI/Public/DTFluxModel/DTFluxModelResponse.h
@@ -0,0 +1,321 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "UObject/Object.h"
+#include "DTFluxUtils/DTFluxEnums.h"
+#include "DTFluxModelResponse.generated.h"
+
+/**
+ *
+ */
+
+USTRUCT(BlueprintType, Category="DTFlux|Model")
+struct DTFLUXAPI_API FSplitResponse
+{
+ GENERATED_BODY()
+public:
+ UPROPERTY()
+ FString Type = "split-response-data";
+ UPROPERTY()
+ int Id;
+ UPROPERTY()
+ FString Name;
+};
+
+USTRUCT(BlueprintType, Category="DTFlux|Model")
+struct DTFLUXAPI_API FStageResponse
+{
+ GENERATED_BODY()
+public:
+ UPROPERTY()
+ FString Type = "stage-response-data";
+ UPROPERTY()
+ int Id;
+ UPROPERTY()
+ FString Name;
+ UPROPERTY()
+ FString StartTime;
+ UPROPERTY()
+ FString EndTime;
+ UPROPERTY()
+ FString CutOff;
+
+};
+
+USTRUCT(BlueprintType, Category="DTFlux|Model")
+struct DTFLUXAPI_API FDTFluxContestResponse
+{
+ GENERATED_BODY()
+public:
+ UPROPERTY()
+ FString Type = "contest";
+ UPROPERTY()
+ int Id;
+ UPROPERTY()
+ FString Name;
+ UPROPERTY()
+ FDateTime Date;
+ UPROPERTY()
+ TArray Stages;
+ UPROPERTY()
+ TArray Splits;
+};
+
+USTRUCT()
+struct DTFLUXAPI_API FDTFluxContestRankingResponseItem
+{
+ GENERATED_BODY()
+
+public:
+ UPROPERTY()
+ FString Type = "team-contest-ranking";
+ UPROPERTY()
+ int Bib;
+ UPROPERTY()
+ int Rank;
+ UPROPERTY()
+ FString Time;
+ UPROPERTY();
+ FString Gap;
+ UPROPERTY();
+ FString SpeedSwimAverage;
+ UPROPERTY();
+ FString SpeedRunningAverage;
+ UPROPERTY();
+ FString SpeedTotalAverage;
+};
+
+USTRUCT()
+struct DTFLUXAPI_API FDTFluxContestRankingResponse
+{
+ GENERATED_BODY()
+
+public:
+ UPROPERTY()
+ FString Type = "contest-ranking";
+ UPROPERTY()
+ int ContestID;
+ UPROPERTY()
+ TArray Datas;
+};
+
+USTRUCT()
+struct DTFLUXAPI_API FDTFluxStageRankingResponseItem
+{
+ GENERATED_BODY()
+
+public:
+ UPROPERTY()
+ FString Type = "team-stage-ranking";
+ UPROPERTY()
+ int Bib;
+ UPROPERTY()
+ int Rank;
+ UPROPERTY()
+ FString Time;
+ UPROPERTY();
+ FString Gap;
+ UPROPERTY()
+ FString TimeSwim;
+ UPROPERTY();
+ FString TimeTransition;
+ UPROPERTY()
+ FString TimeRun;
+ UPROPERTY();
+ FString TimeStart;
+ UPROPERTY()
+ float SpeedSwim;
+ UPROPERTY()
+ float SpeedRunning;
+ UPROPERTY()
+ float SpeedTotal;
+};
+
+USTRUCT()
+struct DTFLUXAPI_API FDTFluxStageRankingResponse
+{
+ GENERATED_BODY()
+
+public:
+ UPROPERTY()
+ FString Type = "stage-ranking";
+ UPROPERTY()
+ int ContestID;
+ UPROPERTY()
+ int StageID;
+ UPROPERTY()
+ int SplitID = -1;
+ UPROPERTY()
+ // ReSharper disable once IdentifierTypo
+ TArray Datas;
+};
+
+USTRUCT(BlueprintType, Category="DTFlux|Model")
+struct DTFLUXAPI_API FDTFluxRaceDataResponse
+{
+ GENERATED_BODY()
+public:
+
+ UPROPERTY()
+ // ReSharper disable once StringLiteralTypo
+ FString Type = "race-datas";
+ UPROPERTY()
+ // ReSharper disable once IdentifierTypo
+ TArray Datas;
+};
+
+USTRUCT()
+struct DTFLUXAPI_API FDTFluxStatusTeamUpdateResponse
+{
+ GENERATED_BODY()
+ UPROPERTY()
+ FString Type = "status-team-update";
+ UPROPERTY()
+ int Bib;
+ UPROPERTY()
+ int Status;
+};
+
+USTRUCT()
+struct DTFLUXAPI_API FDTFluxStatusUpdateResponse
+{
+ GENERATED_BODY()
+ UPROPERTY()
+ FString Type = "status-update";
+ UPROPERTY()
+ // ReSharper disable once IdentifierTypo
+ FDTFluxStatusTeamUpdateResponse Datas;
+
+};
+
+
+USTRUCT(BlueprintType, Category="DTFlux|Model")
+struct DTFLUXAPI_API FDTFluxSplitSensorItemResponse
+{
+ GENERATED_BODY()
+ UPROPERTY()
+ int Bib;
+ UPROPERTY()
+ FString Type = "split-sensor-item";
+ UPROPERTY()
+ int ContestID;
+ UPROPERTY()
+ int StageID;
+ UPROPERTY()
+ int SplitID;
+ UPROPERTY()
+ FString Time = "-";
+ UPROPERTY()
+ FString Gap = "-";
+ UPROPERTY()
+ int Rank;
+};
+
+USTRUCT(BlueprintType, Category="DTFlux|Model")
+struct DTFLUXAPI_API FDTFluxSplitSensorResponse
+{
+ GENERATED_BODY()
+ UPROPERTY()
+ FString Type = "split-sensor";
+ UPROPERTY()
+ // ReSharper disable once IdentifierTypo
+ TArray Datas;
+};
+
+USTRUCT(BlueprintType, Category="DTFlux|Model")
+struct DTFLUXAPI_API FDTFluxTeamListItemResponse
+{
+ GENERATED_BODY()
+public:
+ UPROPERTY()
+ FString Type = "team-list-item";
+ UPROPERTY()
+ int ContestId;
+ UPROPERTY()
+ int Bib;
+ UPROPERTY()
+ FString FirstName;
+ UPROPERTY()
+ FString LastName;
+ UPROPERTY()
+ FString FirstName2 = "";
+ UPROPERTY()
+ FString LastName2 = "";
+ UPROPERTY()
+ FString Team = "";
+ UPROPERTY()
+ FString Gender;
+ UPROPERTY()
+ FString Gender2;
+ UPROPERTY()
+ bool Elite;
+ UPROPERTY()
+ FString Category;
+ UPROPERTY()
+ int Status;
+ UPROPERTY()
+ FString Club;
+};
+
+USTRUCT(BlueprintType, Category="DTFlux|Model")
+struct DTFLUXAPI_API FDTFluxTeamListResponse
+{
+ GENERATED_BODY()
+public:
+ UPROPERTY()
+ FString Type = "team-list";
+ UPROPERTY()
+ // ReSharper disable once IdentifierTypo
+ TArray Datas;
+
+};
+
+USTRUCT()
+struct DTFLUXAPI_API FDTFluxTeamUpdateResponse
+{
+ GENERATED_BODY()
+ UPROPERTY()
+ FString Type = "team-update";
+ UPROPERTY()
+ // ReSharper disable once IdentifierTypo
+ TArray Datas;
+
+};
+
+USTRUCT()
+struct DTFLUXAPI_API FDTFluxArchSelectResponseItem
+{
+ GENERATED_BODY()
+ UPROPERTY()
+ FString Type = "arch-select";
+ UPROPERTY()
+ int ContestId;
+ UPROPERTY()
+ int StageId;
+};
+
+USTRUCT()
+struct DTFLUXAPI_API FDTFluxArchSelectResponse
+{
+ GENERATED_BODY()
+
+ UPROPERTY()
+ FString Type = "arch-select";
+ UPROPERTY()
+ TArray Datas;
+
+
+};
+
+USTRUCT(BlueprintType, Category="DTFlux|Subsystem|Events")
+struct DTFLUXAPI_API FDTFluxWsResponseEvent
+{
+ GENERATED_BODY()
+
+ UPROPERTY(BlueprintReadOnly, Category="DTFlux|Subsystem|Events")
+ TEnumAsByte WsResponseType;
+ UPROPERTY(BlueprintReadOnly, Category="DTFlux|Subsystem|Events")
+ FString RawData;
+};
diff --git a/Plugins/DTFluxAPI/Source/DTFluxAPI/Public/DTFluxProjectSettings/DTFluxProjectSettings.h b/Plugins/DTFluxAPI/Source/DTFluxAPI/Public/DTFluxProjectSettings/DTFluxProjectSettings.h
new file mode 100644
index 0000000..18648c6
--- /dev/null
+++ b/Plugins/DTFluxAPI/Source/DTFluxAPI/Public/DTFluxProjectSettings/DTFluxProjectSettings.h
@@ -0,0 +1,92 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "Engine/DeveloperSettings.h"
+#include "DTFluxAPILog.h"
+#include "DTFluxProjectSettings.generated.h"
+
+DECLARE_MULTICAST_DELEGATE_TwoParams(OnDTFluxProjectSettingsModified, FString, const UDTFluxProjectSettings* );
+
+
+/**
+ * DTFlux project settings
+ */
+UCLASS(Blueprintable, Config=Engine, DefaultConfig, meta=(DisplayName="DTFlux Project Settings"))
+class DTFLUXAPI_API UDTFluxProjectSettings : public UDeveloperSettings
+{
+ GENERATED_BODY()
+
+public:
+
+ OnDTFluxProjectSettingsModified OnProjectSettingsModified;
+
+ // UPROPERTY(Category="DTFlux|Config|Race Result API", Config, EditAnywhere, BlueprintReadOnly)
+ // int RaceResultPort = 80;
+ //
+ // UPROPERTY(Category="DTFlux|Config|Race Result API", Config, EditAnywhere, BlueprintReadOnly)
+ // FString RaceResultUrl = "http://localhost";
+ //
+ // UPROPERTY(Category="DTFlux|Config|Race Result API", Config, EditAnywhere, BlueprintReadOnly)
+ // FString RaceResultSessionID;
+ //
+ // UPROPERTY(Category="DTFlux|Config|Race Result API", Config, EditAnywhere, BlueprintReadOnly)
+ // bool bAccessIsLocal = true;
+ //
+ // UPROPERTY(Category="DTFlux|Config|Race Result API", Config, EditAnywhere, BlueprintReadOnly)
+ // FString StartListAccessToken;
+ //
+ // UPROPERTY(Category="DTFlux|Config|Race Result API", Config, EditAnywhere, BlueprintReadOnly)
+ // FString GeneralClassificationAccessToken;
+ //
+ // UPROPERTY(Category="DTFlux|Config|Race Result API", Config, EditAnywhere, BlueprintReadOnly)
+ // FString LiveStageResultsAccessToken ;
+// Proxy
+
+ UPROPERTY(Category="DTFlux|Config|Chrono Proxy", Config, EditAnywhere, BlueprintReadOnly)
+ int WebsocketServerPort = 3000;
+
+ UPROPERTY(Category="DTFlux|Config|Chrono Proxy", Config, EditAnywhere, BlueprintReadOnly)
+ FString WebsocketServerAddress = "ws://127.0.0.1/ws";
+
+ UPROPERTY(Category="DTFlux|Config|Chrono Proxy", Config, EditAnywhere, BlueprintReadOnly)
+ FString ProxyAddress = "http://localhost";
+
+ // UPROPERTY(Category="DTFlux|Chrono Proxy", Config, EditAnywhere, BlueprintReadOnly)
+ // int ProxyPort = 8000;
+ //
+ // UPROPERTY(Category="DTFlux|Config|Chrono Proxy", Config, EditAnywhere, BlueprintReadOnly)
+ // FString ProxyRootPath = "/endpoints";
+ // UPROPERTY(Category="DTFlux|Config|Chrono Proxy", Config, EditAnywhere, BlueprintReadOnly)
+ // FString ProxyRaceDataEndpoint;
+ // UPROPERTY(Category="DTFlux|Config|Chrono Proxy", Config, EditAnywhere, BlueprintReadOnly)
+ // FString ProxyRankingEndpoint;
+ // UPROPERTY(Category="DTFlux|Config|Chrono Proxy", Config, EditAnywhere, BlueprintReadOnly)
+ // FString ProxyTeamsEndpoint;
+
+
+ //Server Config ****NOT USED****
+
+
+ UPROPERTY(Category="DTFlux|Server Config", Config, EditAnywhere, BlueprintReadOnly)
+ int InPort = 8080;
+
+
+ UFUNCTION(BlueprintCallable, Category="DTFlux|Config")
+ static const UDTFluxProjectSettings* GetDTFluxAPIProjectSettings();
+
+
+
+#if WITH_EDITOR
+ virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
+#endif
+
+
+protected:
+
+
+private:
+ UDTFluxProjectSettings();
+};
+
\ No newline at end of file
diff --git a/Plugins/DTFluxAPI/Source/DTFluxAPI/Public/DTFluxSubsystem/DTFluxSubsystem.h b/Plugins/DTFluxAPI/Source/DTFluxAPI/Public/DTFluxSubsystem/DTFluxSubsystem.h
new file mode 100644
index 0000000..1e8227d
--- /dev/null
+++ b/Plugins/DTFluxAPI/Source/DTFluxAPI/Public/DTFluxSubsystem/DTFluxSubsystem.h
@@ -0,0 +1,193 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "Runtime/Engine/Public/Subsystems/EngineSubsystem.h"
+// #include "DTFluxWebSocket/DTFluxWebsocketServer.h"
+//
+// #include "HttpServerRequest.h"
+// #include "HttpResultCallback.h"
+// #include "HttpRouteHandle.h"
+// #include
+
+#include "DTFluxAPILog.h"
+#include "DTFluxDataStorage/DTFluxDataStorage.h"
+#include "DTFluxModel/DTFluxModel.h"
+#include "DTFluxSubsystemAPISettings/DTFluxSubsystemAPISettings.h"
+#include "DTFluxWebSocket/DTFluxWebsocketClient.h"
+#include "DTFluxSubsystem.generated.h"
+
+class UDTFluxDataStorage;
+class UDTFluxProjectSettings;
+
+
+
+
+
+DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnTimerTriggered);
+DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnRaceDataReceived);
+DECLARE_DYNAMIC_DELEGATE_OneParam(FOnTimer, FString, TimerName);
+DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnWsEvent, FDTFluxWsResponseEvent, WsResponseEvent);
+DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnFinisher, FDTFluxFinisherData, FinisherData);
+DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnSpotter, FDTFluxFinisherData, SpotterData);
+DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnSplitSensor, FDTFluxSplitRanking, ParticipantSplitData);
+DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnContestBegin, int, ContestId);
+DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnStageBegin, int, ContestId, int, StageId);
+DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnTimesUp, int, ContestId, int, StageId);
+DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnRestTimeBegin, int, ContestId, int, StageId);
+DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnArchSelect, int, ContestId, int, StageId);
+
+/**
+ * DTFlux API Subsystem
+ *
+ * This Subsystem set up a Websocket server and Listen to incoming events and poll some http request to Proxy when needed.
+ * it handles a datastore where data are being saved and present to blueprint or actors.
+ */
+UCLASS(BlueprintType, Category="DTFlux|API Subsystem")
+class DTFLUXAPI_API UDTFluxSubsystem : public UEngineSubsystem, public FTickableGameObject
+{
+ GENERATED_BODY()
+
+private:
+
+ static const UDTFluxProjectSettings* GetSettings();
+ int WebSocketPort = 0;
+ FDTFluxSubsystemAPISettings SubSettings;
+
+ UPROPERTY()
+ UDTFluxWebSocketClient* WsClient;
+ UPROPERTY()
+ UDTFluxDataStorage* DataStorage;
+ virtual void Tick(float DeltaTime) override;
+
+ virtual bool IsTickableInEditor() const override
+ {
+ return true;
+ }
+ virtual TStatId GetStatId() const override
+ {
+ RETURN_QUICK_DECLARE_CYCLE_STAT(UDTFluxSubsystem, STATGROUP_Tickables);
+ }
+
+
+
+protected:
+ UFUNCTION()
+ void RequestRaceDatas();
+ UFUNCTION()
+ void RequestTeamList();
+ UFUNCTION()
+ void RequestContestRanking(const int ContestId);
+ UFUNCTION()
+ void RequestStageRanking(const int ContestId, const int StageId);
+ UFUNCTION()
+ void RequestSplitGaps(const int ContestId, const int StageId, const int SplitId);
+ UPROPERTY()
+ FDateTime TestTimer;
+ UFUNCTION()
+ void BroadcastTimerEvent();
+ UPROPERTY()
+ TMap Timer;
+ bool DataStorageRaceDataInit = false;
+ bool DataStorageTeamListInit = false;
+
+public:
+ /** Implement this for initialization of instances of the system */
+ virtual void Initialize(FSubsystemCollectionBase& Collection) override;
+
+ /** Implement this for deinitialization of instances of the system */
+ virtual void Deinitialize() override;
+ void InitDataStorage();
+ void LoadConfig(const UDTFluxProjectSettings* Settings);
+
+ UFUNCTION(BlueprintCallable, Category="DTFluxAPI | Subsytem")
+ bool ReloadSubsystem();
+
+ UPROPERTY(BlueprintAssignable, Category="DTFlux|Events")
+ FOnTimerTriggered OnTimerTriggered;
+ UPROPERTY(BlueprintAssignable, Category="DTFlux|Events")
+ FOnWsEvent OnWsEvent;
+ UPROPERTY(BlueprintAssignable, Category="DTFlux|Events")
+ FOnSplitSensor OnSplitSensor;
+ UPROPERTY(BlueprintAssignable, Category="DTFlux|Events")
+ FOnSpotter OnSpotter;
+ UPROPERTY(BlueprintAssignable, Category="DTFlux|Events")
+ FOnFinisher OnFinisher;
+ UPROPERTY(BlueprintAssignable, Category="DTFlux|Events")
+ FOnContestBegin OnContestBegin;
+ UPROPERTY(BlueprintAssignable, Category="DTFlux|Events")
+ FOnStageBegin OnStageBegin;
+ UPROPERTY(BlueprintAssignable, Category="DTFlux|Events")
+ FOnTimesUp OnTimesUp;
+ UPROPERTY(BlueprintAssignable, Category="DTFlux|Events")
+ FOnRestTimeBegin FOnRestTimeBegin;
+ UPROPERTY(BlueprintAssignable, Category="DTFlux|Events")
+ FOnRaceDataReceived OnRaceDataReceived;
+ UPROPERTY(BlueprintAssignable, Category="DTFlux|Events")
+ FOnArchSelect OnArchSelect;
+
+ // UPROPERTY(BlueprintReadWrite, Category="DTFlux|Subsystem|Websocket")
+ // int ReconnectTimeout = 60; //seconds
+ UFUNCTION(BlueprintCallable, Category="DTFlux|Subsystem|WebSocket")
+ bool Reconnect();
+
+ UFUNCTION(BlueprintCallable, Category="DTFlux|Subsystem|WebSocket")
+ bool AddTimer(FDateTime Time, FOnTimer NewTimer);
+ UFUNCTION(BlueprintCallable, Category="DTFlux|Subsystem")
+ void SetTimerEvent(const FDateTime& When);
+
+ UFUNCTION(BlueprintCallable, Category="DTFlux|Subsystem")
+ void UpdateRaceData();
+ UFUNCTION(BlueprintCallable, Category="DTFlux|Subsystem")
+ void UpdateTeamList();
+ UFUNCTION(BlueprintCallable, Category="DTFlux|Subsystem")
+ void UpdateTeam();
+ UFUNCTION(BlueprintCallable, Category="DTFlux|Subsystem")
+ void UpdateContestRanking(const int ContestID);
+ UFUNCTION(BlueprintCallable, Category="DTFlux|Subsystem")
+ void UpdateStageRanking(const int ContestID, const int StageID, const int SplitID = -1);
+
+ UFUNCTION(BlueprintCallable, Category="DTFlux|Subsystem")
+ UDTFluxDataStorage* GetDataStorage()
+ {
+ return DataStorage;
+ };
+
+
+ UFUNCTION()
+ void ProcessTeamListResponse(const FDTFluxTeamListResponse& TeamListResponse);
+ UFUNCTION()
+ void ProcessRaceDataResponse(const FDTFluxRaceDataResponse& DataResponse);
+ UFUNCTION()
+ void ProcessContestRankingResponse(const FDTFluxContestRankingResponse& ContestRankingResponse);
+ UFUNCTION()
+ void ProcessStageRankingResponse(const FDTFluxStageRankingResponse& StageRankingResponse);
+ UFUNCTION()
+ void ProcessSplitRankingResponse(const FDTFluxStageRankingResponse& StageRankingResponse);
+ UFUNCTION()
+ void ProcessTeamUpdateResponse(const FDTFluxTeamUpdateResponse& TeamUpdateResponse);
+ UFUNCTION()
+ void ProcessStatusUpdateResponse(const FDTFluxStatusUpdateResponse& StatusUpdateResponse);
+ UFUNCTION()
+ void ProcessSplitSensor(const FDTFluxSplitSensorResponse& SplitSensorResponse);
+
+ UFUNCTION()
+ void ProcessArchSelect(FDTFluxArchSelectResponseItem ArchSelectResponse);
+
+ UFUNCTION()
+ void WsConnected();
+ UFUNCTION()
+ void WsReceivedMessage(const FString& MessageReceived);
+ UFUNCTION()
+ void WsConnectionClosed(const FString& Reason);
+ UFUNCTION()
+ void WsConnectionError(const FString& Error);
+
+ UFUNCTION(BlueprintCallable, Category="DTFlux|subsystem")
+ bool IsConnected() const;
+
+ UFUNCTION(BlueprintCallable, Category="DTFlux|Subsystem")
+ TArray SortByRank(TArray SplitRankingArray);
+};
+
diff --git a/Plugins/DTFluxAPI/Source/DTFluxAPI/Public/DTFluxSubsystem/DTFluxSubsystemTimer.h b/Plugins/DTFluxAPI/Source/DTFluxAPI/Public/DTFluxSubsystem/DTFluxSubsystemTimer.h
new file mode 100644
index 0000000..83f9630
--- /dev/null
+++ b/Plugins/DTFluxAPI/Source/DTFluxAPI/Public/DTFluxSubsystem/DTFluxSubsystemTimer.h
@@ -0,0 +1,102 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "DTFluxUtils/DTFluxEnums.h"
+#include "Subsystems/WorldSubsystem.h"
+#include "DTFluxSubsystemTimer.generated.h"
+
+/**
+ *
+ */
+
+class UDTFluxDataStorage;
+class UDTFluxSubsystem;
+
+USTRUCT()
+struct FDTFluxContestTimerHandle
+{
+ GENERATED_BODY()
+
+public:
+
+ UPROPERTY()
+ int ContestId;
+ UPROPERTY()
+ int StageId;
+ UPROPERTY();
+ TEnumAsByte Type;
+ UPROPERTY();
+ FTimerHandle Handle;
+};
+
+DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnCutoff, TArray, ContestIds, int, StageId);
+DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnStageStarted, const TArray&, ContestIds, int, StageId);
+DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOnStageLoading, const TArray&, ContestIds, int, StageId, int, DelayBeforeStageStart);
+DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnRemoveLineRequested, const TArray&, LineIndex);
+
+
+UCLASS(BlueprintType, Category="DTFlux|Timer")
+class DTFLUXAPI_API UDTFluxSubsystemTimer : public UTickableWorldSubsystem
+{
+ GENERATED_BODY()
+public:
+ /** Implement this for initialization of instances of the system */
+ virtual void Initialize(FSubsystemCollectionBase& Collection) override;
+
+ /** Implement this for deinitialization of instances of the system */
+ virtual void Deinitialize() override;
+
+ virtual void Tick(float DeltaTime) override;
+
+ virtual TStatId GetStatId() const override
+ {
+ RETURN_QUICK_DECLARE_CYCLE_STAT(UDTFluxSubsystemTimer, STATGROUP_Tickables);
+ }
+
+ UPROPERTY()
+ TArray Timers;
+
+
+ UPROPERTY(BlueprintAssignable, Category="DTFlux|Timer")
+ FOnCutoff OnCutoff;
+
+ UPROPERTY(BlueprintAssignable, Category="DTFlux|Timer")
+ FOnStageStarted OnStageStarted;
+
+ UPROPERTY(BlueprintAssignable, Category="DTFlux|Timer")
+ FOnStageLoading OnStageLoading;
+
+ UPROPERTY(BlueprintAssignable, Category="DTFlux|Timer")
+ FOnRemoveLineRequested OnRemoveLineRequested;
+
+ UFUNCTION()
+ void OnDataStorageInit();
+
+ void AddCutoffTimer(const int ContestID, const int StageID);
+ void AddStageStartedTimer(const int ContestID, const int StageID);
+ void OnStartTimer();
+ void OnCutOffTimer();
+
+ void ClearTimer(FDTFluxContestTimerHandle TimerHandle);
+ void ClearTimer(const int HandleIndex);
+
+ UFUNCTION(BlueprintCallable, Category="DTFlux|Timer")
+ void TriggerOnCutOff(const TArray& ContestIds, const int StageId);
+
+ UFUNCTION(BlueprintCallable, Category="DTFlux|Timer")
+ void TriggerStartTime(const TArray& ContestIds, const int StageId);
+
+ UFUNCTION(BlueprintCallable, Category="DTFlux|Timer")
+ void TriggerStageLoading(const TArray& ContestIds, int StageId, int DelayBeforeStageStart);
+ UFUNCTION(BlueprintCallable, Category="DTFlux|Timer")
+ void TriggerOnDeleteRequested( const TArray& LineIndex);
+
+ static UDTFluxSubsystem* GetDTFluxSubSystem();
+ static UDTFluxDataStorage* GetDTFluxDataStorage();
+
+ static float GetSecondsFrom(const FDateTime When);
+};
+
+
diff --git a/Plugins/DTFluxAPI/Source/DTFluxAPI/Public/DTFluxSubsystemAPISettings/DTFluxSubsystemAPISettings.h b/Plugins/DTFluxAPI/Source/DTFluxAPI/Public/DTFluxSubsystemAPISettings/DTFluxSubsystemAPISettings.h
new file mode 100644
index 0000000..407a98c
--- /dev/null
+++ b/Plugins/DTFluxAPI/Source/DTFluxAPI/Public/DTFluxSubsystemAPISettings/DTFluxSubsystemAPISettings.h
@@ -0,0 +1,35 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "UObject/Object.h"
+#include "DTFluxSubsystemAPISettings.generated.h"
+
+/**
+ *
+ */
+USTRUCT()
+struct FDTFluxSubsystemAPISettings
+{
+ GENERATED_BODY()
+public:
+ FString WebsocketAddress = "ws://localhost";
+ int WebsocketPort = 3000;
+ FString ProxyAddress = "http://localhost";
+ int ProxyPort = 80;
+ //TODO : Maybe we must make a dedicated struct with enum to make endpoints more clean.
+ TMap ProxyEndpoints;
+
+ static FString GetRaceDataEndpoint(const FDTFluxSubsystemAPISettings* Settings);
+ static FString GetContestRankingEndpoint(const FDTFluxSubsystemAPISettings* Settings, const int ContestId);
+ static FString GetStageRankingEndpoint(const FDTFluxSubsystemAPISettings* Settings, const int ContestId, const int StageId);
+ static FString GetStageRankingFilteredEndpoint(const FDTFluxSubsystemAPISettings* Settings, const int ContestId, const int StageId, const FString SplitName);
+ static FString GetTeamsEndpoint(const FDTFluxSubsystemAPISettings* Settings);
+
+private:
+ FString GetProxyBaseEndpoint() const
+ {
+ return FString::Printf(TEXT("%s:%i"), *ProxyAddress, ProxyPort);
+ };
+};
diff --git a/Plugins/DTFluxAPI/Source/DTFluxAPI/Public/DTFluxUtils/DTFluxEnums.h b/Plugins/DTFluxAPI/Source/DTFluxAPI/Public/DTFluxUtils/DTFluxEnums.h
new file mode 100644
index 0000000..e508800
--- /dev/null
+++ b/Plugins/DTFluxAPI/Source/DTFluxAPI/Public/DTFluxUtils/DTFluxEnums.h
@@ -0,0 +1,103 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "UObject/Object.h"
+#include "DTFluxEnums.generated.h"
+
+/**
+ *
+ */
+UENUM()
+enum EDTFluxParticipantStatusType : uint8
+{
+ Normal = 0 UMETA(DisplayName="Normal"),
+ OutOfRace = 1 UMETA(DisplayName="HorsCourse"),
+ DSQ = 2 UMETA(DisplayName="Disqualifié"),
+ DNF = 3 UMETA(DisplayName="Abandon"),
+ DNS = 4 UMETA(DisplayName="NonPartant"),
+ NotLinedUp = 5 UMETA(DisplayName="NonPresentAuDépart"),
+};
+
+UENUM()
+enum EDTFluxFinisherType : uint8
+{
+ Winner = 0 UMETA(DisplayName="Winner"),
+ Spotter = 1 UMETA(DisplayName="Spotter"),
+ Finish = 2 UMETA(DisplayName="Finish"),
+};
+
+UENUM(BlueprintType, Category="DTFlux|Server")
+enum EDTFluxResponseErrorCode
+{
+ Unknown_Error UMETA(DisplayName="Unknown Error"),
+ InvalidBody_Error UMETA(DisplayName="Invalid Body"),
+ InvalidRequest_Error UMETA(DisplayName="Invalid Request"),
+ Internal_Error UMETA(DisplayName="Internal Server Error")
+};
+
+UENUM(BlueprintType, Category="DTFlux|Subsystem")
+enum EDTFluxResponseType: uint8
+{
+ UnknownResponse = 0 UMETA(DisplayName="UnknownResponse"),
+ RaceData = 1 UMETA(DisplayName="RaceData"),
+ ContestRanking = 2 UMETA(DisplayName="ContestRanking"),
+ StageRanking = 3 UMETA(DisplayName="StageRanking"),
+ SplitRanking = 4 UMETA(DisplayName="SplitRanking"),
+ TeamList = 5 UMETA(DisplayName="TeamList"),
+ TeamUpdate = 6 UMETA(DisplayName="TeamUpdate"),
+ SplitSensor = 7 UMETA(DisplayName="SplitSensor"),
+ StatusUpdate = 8 UMETA(DisplayName="StatusUpdate"),
+ WsConnected = 9 UMETA(DisplayName="WsConnected"),
+ WsClosed = 10 UMETA(DisplayName="WsClosed"),
+ WsError = 11 UMETA(DisplayName="WsError"),
+ ArchSelect = 12 UMETA(DisplayName="ArchSelect"),
+};
+
+UENUM(BlueprintType, Category="DTFlux|Subsystem")
+enum EDTFluxSplitType : uint8
+{
+ UnknownSplitType = 0 UMETA(DisplayName="UnknownSplitType"),
+ NormalSplit = 1 UMETA(DisplayName="NormalSplit"),
+ PreFinnishSplit = 2 UMETA(DisplayName="PreFinnishSplit"),
+ FinishSplit = 3 UMETA(DisplayName="FinishSplit"),
+};
+
+
+UENUM(BlueprintType, Category="DTFlux|DataStorage")
+// ReSharper disable once IdentifierTypo
+enum EDTFluxDataStorageEventType : uint8
+{
+ UnknownEvent = 0 UMETA(DisplayName="ParticipantUpdateEvent"),
+ ParticipantCreateEvent = 1 UMETA(DisplayName="ParticipantUpdateEvent"),
+ ParticipantUpdateEvent = 2 UMETA(DisplayName="ParticipantUpdateEvent"),
+ ParticipantDeleteEvent = 3 UMETA(DisplayName="ParticipantDeleteEvent"),
+ ParticipantStatusUpdateEvent = 4 UMETA(DisplayName="ParticipantUpdateEvent"),
+ RaceDataCreateEvent = 5 UMETA(DisplayName="RaceDataCreateEvent"),
+ RaceDataUpdateEvent = 6 UMETA(DisplayName="RaceDataUpdateEvent"),
+ RaceDataDeleteEvent = 7 UMETA(DisplayName="RaceDataDeleteEvent"),
+ ContestRankingUpdate = 8 UMETA(DisplayName="ContestRankingUpdate"),
+ StageRankingUpdate = 9 UMETA(DisplayName="StageRankingUpdate"),
+ SplitRankingUpdate = 10 UMETA(DisplayName="SplitRankingUpdate"),
+};
+
+
+UENUM()
+enum EDTFluxTimerEventType : uint8
+{
+ StageStart = 0 UMETA(DisplayName="StageStart"),
+ StageCutOff = 1 UMETA(DisplayName="StageCutOff"),
+
+};
+
+UENUM()
+enum EDTFluxStageStatusType : uint8
+{
+ UnknownStatus = 0 UMETA(DisplayName="UnknownStatus"),
+ StageWaiting = 1 UMETA(DisplayName="StageWaiting"),
+ StageStarted = 2 UMETA(DisplayName="StageStarted"),
+ StageEnded = 3 UMETA(DisplayName="StageCutOff")
+};
+
+
diff --git a/Plugins/DTFluxAPI/Source/DTFluxAPI/Public/DTFluxUtils/DTFluxUtils.h b/Plugins/DTFluxAPI/Source/DTFluxAPI/Public/DTFluxUtils/DTFluxUtils.h
new file mode 100644
index 0000000..8f58407
--- /dev/null
+++ b/Plugins/DTFluxAPI/Source/DTFluxAPI/Public/DTFluxUtils/DTFluxUtils.h
@@ -0,0 +1,135 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "CrossCompilerCommon.h"
+#include "DTFluxModel/DTFluxModel.h"
+#include "DTFluxSubsystem/DTFluxSubsystem.h"
+#include "UObject/Object.h"
+#include "DTFluxUtils.generated.h"
+
+/**
+ *
+ */
+UCLASS(BlueprintType, Category="DTFlux|Model|Helpers")
+class DTFLUXAPI_API UDTFluxModelHelper : public UBlueprintFunctionLibrary
+{
+ GENERATED_BODY()
+
+public:
+ UFUNCTION(BlueprintCallable, Category="DTFlux|Model")
+ static bool IsParticipantATeam(const FDTFluxParticipant& Participant)
+ {
+ return Participant.Person2.FirstName != "";
+ }
+
+ UFUNCTION(BlueprintCallable, Category="DTFlux|Model|Helpers")
+ static TArray GetSplitRanking(const int ContestId, const int StageId,
+ const int SplitId, const int From = 0, const int DisplayNumber = 0)
+ {
+ TArray SplitRankings;
+ UDTFluxSubsystem* Subsystem = GEngine->GetEngineSubsystem();
+ TArray Contests = Subsystem->GetDataStorage()->Contests;
+ for( auto& Contest : Contests)
+ {
+ if(Contest.Id == ContestId)
+ {
+ for( auto& Stage : Contest.Stages)
+ {
+ if(Stage.Id == StageId)
+ {
+ for( auto& Split : Stage.Splits)
+ {
+ if(Split.Id == SplitId)
+ {
+ Split.SortByRank();
+ return Split.GetSplitRanking(From, DisplayNumber);
+ }
+ }
+ }
+ }
+ }
+ }
+ return SplitRankings;
+
+ }
+
+ UFUNCTION(BlueprintCallable, Category="DTFlux|Model|Helpers")
+ static TArray GetStageRanking(const int ContestId, const int StageId, const int From = 0, const int DisplayNumber = 0)
+ {
+ TArray StageRankings;
+ UDTFluxSubsystem* Subsystem = GEngine->GetEngineSubsystem();
+ TArray Contests = Subsystem->GetDataStorage()->Contests;
+ for( auto& Contest : Contests)
+ {
+ if(Contest.Id == ContestId)
+ {
+ for( auto& Stage : Contest.Stages)
+ {
+ if(Stage.Id == StageId)
+ {
+ StageRankings = Stage.StageRanking;
+ }
+ }
+ }
+ }
+ //CAREFUL Can Be Empty
+ return StageRankings;
+ }
+
+ UFUNCTION(BlueprintCallable, Category="DTFlux|Model|Helpers")
+ static TArray GetContestRanking(const int ContestId, const int StageId, const int From = 0, const int DisplayNumber = 0)
+ {
+ TArray ContestRankings;
+ UDTFluxSubsystem* Subsystem = GEngine->GetEngineSubsystem();
+ TArray Contests = Subsystem->GetDataStorage()->Contests;
+ for( auto& Contest : Contests)
+ {
+ if(Contest.Id == ContestId)
+ {
+ ContestRankings = Contest.ContestRanking;
+ }
+ }
+ //CAREFUL Can Be Empty
+ return ContestRankings;
+ }
+
+ UFUNCTION(BlueprintCallable, Category="DTFlux|Model|Helpers")
+ static bool GetParticipant(const int Bib, FDTFluxParticipant& Participant)
+ {
+ UDTFluxSubsystem* Subsystem= GEngine->GetEngineSubsystem();
+ UDTFluxDataStorage* DataStorage = Subsystem->GetDataStorage();
+
+ return DataStorage->GetParticipantByBib(Bib, Participant);
+
+ }
+
+ UFUNCTION(BlueprintCallable, Category="DTFlux|Model|Helpers")
+ static FString GetParticipantString(const int Bib, bool Truncate = true, int MaxSize = 20)
+ {
+ FString ParticipantStr = "";
+ FDTFluxParticipant Participant;
+ if(UDTFluxModelHelper::GetParticipant(Bib, Participant))
+ {
+ ParticipantStr = Participant.GetParticipantFormatedName(Truncate, MaxSize);
+ }
+ return ParticipantStr;
+ }
+
+ UFUNCTION(BlueprintCallable, Category="DTFlux|Model|Helpers")
+ static EDTFluxStageStatusType GetStatusType(const int ContestID, const int StageID, UDTFluxDataStorage* DataStorage);
+
+
+ UFUNCTION(BlueprintCallable, Category="DTFlux|Model|Helpers")
+ static int GetCurrentContest(UDTFluxDataStorage* DataStorage);
+
+ UFUNCTION(BlueprintCallable, Category="DTFlux|Model|Helpers")
+ static TArray GetCurrentStage(UDTFluxDataStorage* DataStorage);
+
+ UFUNCTION(BlueprintCallable, Category="DTFlux|Model|Helpers")
+ static FString GetCurrentStageName(UDTFluxDataStorage* DataStorage);
+
+
+
+};
diff --git a/Plugins/DTFluxAPI/Source/DTFluxAPI/Public/DTFluxWebSocket/DTFluxWebsocketClient.h b/Plugins/DTFluxAPI/Source/DTFluxAPI/Public/DTFluxWebSocket/DTFluxWebsocketClient.h
new file mode 100644
index 0000000..a65b544
--- /dev/null
+++ b/Plugins/DTFluxAPI/Source/DTFluxAPI/Public/DTFluxWebSocket/DTFluxWebsocketClient.h
@@ -0,0 +1,62 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "IWebSocket.h"
+#include "UObject/Object.h"
+#include "DTFluxWebsocketClient.generated.h"
+
+DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnConnectionConnected);
+DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnConnectionClosed, const FString&, Reason);
+DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnConnectionError, const FString&, Error);
+DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnReceivedMessage, const FString&, Message);
+
+
+UCLASS(Blueprintable, Category="DTFlux|Websocket")
+class DTFLUXAPI_API UDTFluxWebSocketClient : public UObject
+{
+ GENERATED_BODY()
+
+public:
+
+ // UDTFluxWebSocketClient() = default;
+ void Initialize();
+ // virtual void BeginDestroy() override;
+ // virtual ~UDTFluxWebSocketClient() override;
+
+ UFUNCTION(BlueprintCallable, Category="DTFlux|Websocket")
+ bool Connect(const FString URL, const int Port );
+
+ UFUNCTION(BlueprintCallable, Category="DTFlux|Websocket")
+ void Disconnect();
+
+ UFUNCTION(BlueprintCallable, Category="DTFlux|Websocket")
+ bool SendMessage(const FString Message, const bool Broadcast = false);
+
+ UPROPERTY(BlueprintAssignable, Category="DTFlux|Websocket")
+ FOnConnectionConnected OnConnectionConnected;
+
+ UPROPERTY(BlueprintAssignable, Category="DTFlux|Websocket")
+ FOnConnectionClosed OnConnectionClosed;
+
+ UPROPERTY(BlueprintAssignable, Category="DTFlux|Websocket")
+ FOnConnectionError OnConnectionError;
+
+ UPROPERTY(BlueprintAssignable, Category="DTFlux|Websocket")
+ FOnReceivedMessage OnReceivedMessage;
+
+ bool Close() const;
+ bool IsConnected();
+
+protected:
+
+
+private:
+ void OnConnectionConnectedInternal() const;
+ void OnConnectionErrorInternal(const FString& Error) const;
+ void OnConnectionClosedInternal(const FString& Reason) const;
+ void OnReceivedMessageInternal(const FString& Message) const;
+
+ TSharedPtr Ws;
+};
diff --git a/Plugins/DTFluxAPI/Source/DTFluxAPI/Public/DTFluxWebSocket/DTFluxWebsocketServer.h b/Plugins/DTFluxAPI/Source/DTFluxAPI/Public/DTFluxWebSocket/DTFluxWebsocketServer.h
new file mode 100644
index 0000000..5dafc3b
--- /dev/null
+++ b/Plugins/DTFluxAPI/Source/DTFluxAPI/Public/DTFluxWebSocket/DTFluxWebsocketServer.h
@@ -0,0 +1,131 @@
+// // Fill out your copyright notice in the Description page of Project Settings.
+//
+// #pragma once
+//
+// #include "CoreMinimal.h"
+// #include "UObject/Object.h"
+// #include "IWebSocketServer.h"
+// #include "DTFluxWebsocketServer.generated.h"
+//
+//
+//
+// DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnConnectionConnected, const FString&, Channel);
+// DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnConnectionClosed, const FString&, Channel, const FString&, Reason);
+// DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnConnectionError, const FString&, Channel, const FString&, Error);
+// DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnReceivedMessage, const FString&, Channel, const FString&, Message);
+// class INetworkingWebSocket;
+//
+// UCLASS(Blueprintable, Category="DTFlux|Websocket")
+// class DTFLUXAPI_API UDTFluxWebsocketServer : public UObject
+// {
+// GENERATED_BODY()
+//
+// public:
+//
+// UDTFluxWebsocketServer();
+//
+//
+//
+// void Initialize(const FString& Channel)
+// {
+// ChannelName = Channel;
+// };
+//
+// virtual ~UDTFluxWebsocketServer() override;
+//
+// UFUNCTION(BlueprintCallable, Category="DTFlux|Websocket")
+// bool ConnectToChannel(const FString URL, const int Port );
+//
+// UFUNCTION(BlueprintCallable, Category="DTFlux|Websocket")
+// void LeaveChannel();
+//
+// UFUNCTION(BlueprintCallable, Category="DTFlux|Websocket")
+// bool SendMessageToAll(const FString Message);
+//
+// UPROPERTY(BlueprintAssignable, Category="DTFlux|Websocket")
+// FOnConnectionConnected OnConnectionConnected;
+//
+// UPROPERTY(BlueprintAssignable, Category="DTFlux|Websocket")
+// FOnConnectionClosed OnConnectionClosed;
+//
+// UPROPERTY(BlueprintAssignable, Category="DTFlux|Websocket")
+// FOnConnectionError OnConnectionError;
+//
+// UPROPERTY(BlueprintAssignable, Category="DTFlux|Websocket")
+// FOnReceivedMessage OnReceivedMessage;
+//
+// UPROPERTY(BlueprintReadOnly, Category="DTFlux|Websocket")
+// FString ChannelName;
+//
+// void Close();
+//
+//
+// protected:
+//
+//
+//
+// private:
+//
+// bool bIsConnected = false;
+// void OnConnectionConnectedInternal(INetworkingWebSocket* ClientWebSocket);
+// void OnConnectionErrorInternal();
+// // void OnConnectionClosedInternal(const FString& Reason);
+// void OnReceivedMessageInternal(void* Data, int32 Count);
+//
+// static TUniquePtr GetServer();
+//
+// TArray Clients;
+// // TSharedPtr Ws;
+// TUniquePtr Ws;
+// };
+
+#pragma once
+
+#include "IWebSocketServer.h"
+#include "Subsystems/GameInstanceSubsystem.h"
+#include "DTFluxWebsocketServer.generated.h"
+
+
+
+UCLASS(Blueprintable, Category="DTFlux|Websocket")
+class DTFLUXAPI_API UDTFluxServerWebSocket : public UObject
+{
+ GENERATED_BODY()
+
+public:
+ DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnConnectionConnected);
+ DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnConnectionClosed, const FString&, Reason);
+ DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnConnectionError, const FString&, Error);
+ DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnReceivedMessage, const FString&, Message);
+
+
+ UDTFluxServerWebSocket();
+ virtual ~UDTFluxServerWebSocket() override;
+
+ void Init(const int& Port, const FString& Url = TEXT("ws://localhost"));
+ virtual void BeginDestroy() override;
+
+ DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnJsonRecieved, const FString&, Payload);
+
+ UPROPERTY(BlueprintAssignable)
+ FOnJsonRecieved OnJsonRecieved;
+ void Close()
+ {
+ ServerWebSocket = nullptr;
+
+ };
+
+protected:
+
+ void OnWebSocketClientConnected(INetworkingWebSocket* ClientWebSocket); // to the server.
+
+ virtual void ReceivedRawPacket(void* Data, int32 Count);
+
+
+private:
+
+ TUniquePtr ServerWebSocket;
+ TMap ConnectedClients;
+ /** Delegate for callbacks to GameThreadTick */
+ FTSTicker::FDelegateHandle TickHandle;
+};
diff --git a/Yotta2025.uproject b/Yotta2025.uproject
new file mode 100644
index 0000000..3572acd
--- /dev/null
+++ b/Yotta2025.uproject
@@ -0,0 +1,193 @@
+{
+ "FileVersion": 3,
+ "EngineAssociation": "5.6",
+ "Category": "",
+ "Description": "",
+ "Plugins": [
+ {
+ "Name": "PythonScriptPlugin",
+ "Enabled": true
+ },
+ {
+ "Name": "HDRIBackdrop",
+ "Enabled": true
+ },
+ {
+ "Name": "SunPosition",
+ "Enabled": true
+ },
+ {
+ "Name": "SequencerScripting",
+ "Enabled": true
+ },
+ {
+ "Name": "Takes",
+ "Enabled": true
+ },
+ {
+ "Name": "VirtualProductionUtilities",
+ "Enabled": true
+ },
+ {
+ "Name": "LiveLink",
+ "Enabled": true
+ },
+ {
+ "Name": "Composure",
+ "Enabled": true
+ },
+ {
+ "Name": "MediaFrameworkUtilities",
+ "Enabled": true
+ },
+ {
+ "Name": "MediaIOFramework",
+ "Enabled": true
+ },
+ {
+ "Name": "RemoteControl",
+ "Enabled": true
+ },
+ {
+ "Name": "nDisplay",
+ "Enabled": true,
+ "SupportedTargetPlatforms": [
+ "Win64",
+ "Linux"
+ ]
+ },
+ {
+ "Name": "AjaMedia",
+ "Enabled": true,
+ "SupportedTargetPlatforms": [
+ "Win64"
+ ]
+ },
+ {
+ "Name": "BlackmagicMedia",
+ "Enabled": true,
+ "SupportedTargetPlatforms": [
+ "Win64",
+ "Linux"
+ ]
+ },
+ {
+ "Name": "AppleProResMedia",
+ "Enabled": true,
+ "SupportedTargetPlatforms": [
+ "Win64"
+ ]
+ },
+ {
+ "Name": "HAPMedia",
+ "Enabled": true,
+ "SupportedTargetPlatforms": [
+ "Win64"
+ ]
+ },
+ {
+ "Name": "PixelStreaming",
+ "Enabled": true
+ },
+ {
+ "Name": "VariantManager",
+ "Enabled": true
+ },
+ {
+ "Name": "DMXProtocol",
+ "Enabled": true
+ },
+ {
+ "Name": "MovieRenderPipeline",
+ "Enabled": true
+ },
+ {
+ "Name": "Avalanche",
+ "Enabled": true,
+ "SupportedTargetPlatforms": [
+ "Win64",
+ "Linux",
+ "Mac"
+ ]
+ },
+ {
+ "Name": "ElectraCodecs",
+ "Enabled": true
+ },
+ {
+ "Name": "ElectraPlayer",
+ "Enabled": true
+ },
+ {
+ "Name": "ElectraUtil",
+ "Enabled": true
+ },
+ {
+ "Name": "HAPDecoderElectra",
+ "Enabled": true,
+ "SupportedTargetPlatforms": [
+ "Win64",
+ "Mac"
+ ]
+ },
+ {
+ "Name": "VPxDecoderElectra",
+ "Enabled": true
+ },
+ {
+ "Name": "NDIMedia",
+ "Enabled": true,
+ "SupportedTargetPlatforms": [
+ "Win64"
+ ]
+ },
+ {
+ "Name": "AvalancheDisplayCluster",
+ "Enabled": true,
+ "SupportedTargetPlatforms": [
+ "Win64",
+ "Linux"
+ ]
+ },
+ {
+ "Name": "StormSyncAvalancheBridge",
+ "Enabled": true
+ },
+ {
+ "Name": "RemoteControlProtocolDMX",
+ "Enabled": true
+ },
+ {
+ "Name": "RemoteControlProtocolMIDI",
+ "Enabled": true
+ },
+ {
+ "Name": "RemoteControlProtocolOSC",
+ "Enabled": true
+ },
+ {
+ "Name": "RemoteControlWebInterface",
+ "Enabled": true
+ },
+ {
+ "Name": "DMXControlConsole",
+ "Enabled": true
+ },
+ {
+ "Name": "DMXEngine",
+ "Enabled": true
+ },
+ {
+ "Name": "DMXFixtures",
+ "Enabled": true
+ },
+ {
+ "Name": "DMXDisplayCluster",
+ "Enabled": true
+ },
+ {
+ "Name": "FloatingProperties",
+ "Enabled": true
+ }
+ ]
+}
\ No newline at end of file