commit 9591e7f503ff5b6e9eff30a6a617e99dee8a1d21 Author: Norman Lansing Date: Tue Feb 24 22:39:26 2026 -0500 Initial Commit - Lesson 31 (Commit #1) diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..e461f6ee --- /dev/null +++ b/.gitignore @@ -0,0 +1,72 @@ +# Visual Studio user-specific files +vs/ + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app +*.ipa + +# These project files can be generated by the engine +*.xcodeproj +*.xcworkspace +*.sln +*.suo +*.opensdf +*.sdf +*.VC.db +*.VC.opendb + +# Precompiled Assets +SourceArt/**/*.png +SourceArt/**/*.tga + +# Binary Files +Binaries/* +Plugins/**/Binaries/* + +# Builds +Build/* + +# Whitelist PakBlacklist-.txt files +!Build/*/PakBlacklist*.txt + +# Don't ignore icon files in Build +!Build/**/*.ico + +# Built data for maps +*_BuiltData.uasset + +# Configuration files generated by the Editor +Saved/* + +# Compiled source files for the engine to use +Intermediate/* +Plugins/**/Intermediate/* + +# Cache files for the editor to use +DerivedDataCache/* \ No newline at end of file diff --git a/.idea/.idea.FPSTemplate.dir/.idea/.gitignore b/.idea/.idea.FPSTemplate.dir/.idea/.gitignore new file mode 100644 index 00000000..83b75a2d --- /dev/null +++ b/.idea/.idea.FPSTemplate.dir/.idea/.gitignore @@ -0,0 +1,15 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Rider ignored files +/.idea.FPSTemplate.iml +/modules.xml +/contentModel.xml +/projectSettingsUpdater.xml +# Ignored default folder with query files +/queries/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/.idea.FPSTemplate.dir/.idea/encodings.xml b/.idea/.idea.FPSTemplate.dir/.idea/encodings.xml new file mode 100644 index 00000000..df87cf95 --- /dev/null +++ b/.idea/.idea.FPSTemplate.dir/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/.idea.FPSTemplate.dir/.idea/indexLayout.xml b/.idea/.idea.FPSTemplate.dir/.idea/indexLayout.xml new file mode 100644 index 00000000..7b08163c --- /dev/null +++ b/.idea/.idea.FPSTemplate.dir/.idea/indexLayout.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.FPSTemplate.dir/.idea/vcs.xml b/.idea/.idea.FPSTemplate.dir/.idea/vcs.xml new file mode 100644 index 00000000..2c2e183a --- /dev/null +++ b/.idea/.idea.FPSTemplate.dir/.idea/vcs.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.vsconfig b/.vsconfig new file mode 100644 index 00000000..b981b2e5 --- /dev/null +++ b/.vsconfig @@ -0,0 +1,19 @@ +{ + "version": "1.0", + "components": [ + "Component.Unreal.Debugger", + "Component.Unreal.Ide", + "Microsoft.Net.Component.4.6.2.TargetingPack", + "Microsoft.VisualStudio.Component.VC.14.38.17.8.ATL", + "Microsoft.VisualStudio.Component.VC.14.38.17.8.x86.x64", + "Microsoft.VisualStudio.Component.VC.14.44.17.14.ATL", + "Microsoft.VisualStudio.Component.VC.14.44.17.14.x86.x64", + "Microsoft.VisualStudio.Component.VC.Llvm.Clang", + "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", + "Microsoft.VisualStudio.Component.Windows11SDK.22621", + "Microsoft.VisualStudio.Workload.CoreEditor", + "Microsoft.VisualStudio.Workload.ManagedDesktop", + "Microsoft.VisualStudio.Workload.NativeDesktop", + "Microsoft.VisualStudio.Workload.NativeGame" + ] +} diff --git a/Config/DefaultEditor.ini b/Config/DefaultEditor.ini new file mode 100644 index 00000000..f38756e3 --- /dev/null +++ b/Config/DefaultEditor.ini @@ -0,0 +1,2 @@ +[/Script/AdvancedPreviewScene.SharedProfiles] + diff --git a/Config/DefaultEngine.ini b/Config/DefaultEngine.ini new file mode 100644 index 00000000..d58bdfda --- /dev/null +++ b/Config/DefaultEngine.ini @@ -0,0 +1,181 @@ +[SystemSettings] +CommonUI.Debug.CheckGameViewportClientValid=0 + +[/Script/EngineSettings.GameMapsSettings] +GameDefaultMap=/Game/ThirdPerson/Maps/ThirdPersonMap.ThirdPersonMap +EditorStartupMap=/Game/ThirdPerson/Maps/ThirdPersonMap.ThirdPersonMap +GlobalDefaultGameMode=/Game/Blueprints/Game/BP_ShooterGameMode.BP_ShooterGameMode_C +ServerDefaultMap=/Game/ThirdPerson/Maps/ThirdPersonMap.ThirdPersonMap + +[/Script/WindowsTargetPlatform.WindowsTargetSettings] +DefaultGraphicsRHI=DefaultGraphicsRHI_DX12 +-D3D12TargetedShaderFormats=PCD3D_SM5 ++D3D12TargetedShaderFormats=PCD3D_SM6 +-D3D11TargetedShaderFormats=PCD3D_SM5 ++D3D11TargetedShaderFormats=PCD3D_SM5 +Compiler=Default +AudioSampleRate=48000 +AudioCallbackBufferFrameSize=1024 +AudioNumBuffersToEnqueue=1 +AudioMaxChannels=0 +AudioNumSourceWorkers=4 +SpatializationPlugin= +SourceDataOverridePlugin= +ReverbPlugin= +OcclusionPlugin= +CompressionOverrides=(bOverrideCompressionTimes=False,DurationThreshold=5.000000,MaxNumRandomBranches=0,SoundCueQualityIndex=0) +CacheSizeKB=65536 +MaxChunkSizeOverrideKB=0 +bResampleForDevice=False +MaxSampleRate=48000.000000 +HighSampleRate=32000.000000 +MedSampleRate=24000.000000 +LowSampleRate=12000.000000 +MinSampleRate=8000.000000 +CompressionQualityModifier=1.000000 +AutoStreamingThreshold=0.000000 +SoundCueCookQualityIndex=-1 + +[/Script/Engine.RendererSettings] +r.Mobile.EnableNoPrecomputedLightingCSMShader=True + +r.GenerateMeshDistanceFields=True + +r.DynamicGlobalIlluminationMethod=1 + +r.ReflectionMethod=1 + +r.Shadow.Virtual.Enable=1 + +r.DefaultFeature.AutoExposure.ExtendDefaultLuminanceRange=True + +r.DefaultFeature.LocalExposure.HighlightContrastScale=0.8 + +r.DefaultFeature.LocalExposure.ShadowContrastScale=0.8 +r.DefaultFeature.MotionBlur=False + +[/Script/LinuxTargetPlatform.LinuxTargetSettings] +-TargetedRHIs=SF_VULKAN_SM5 ++TargetedRHIs=SF_VULKAN_SM6 + +[/Script/HardwareTargeting.HardwareTargetingSettings] +TargetedHardwareClass=Desktop +AppliedTargetedHardwareClass=Desktop +DefaultGraphicsPerformance=Maximum +AppliedDefaultGraphicsPerformance=Maximum + +[/Script/WorldPartitionEditor.WorldPartitionEditorSettings] +CommandletClass=Class'/Script/UnrealEd.WorldPartitionConvertCommandlet' + +[/Script/Engine.UserInterfaceSettings] +bAuthorizeAutomaticWidgetVariableCreation=False +FontDPIPreset=Standard +FontDPI=72 + +[/Script/Engine.Engine] ++ActiveGameNameRedirects=(OldGameName="TP_Blank",NewGameName="/Script/FPSTemplate") ++ActiveGameNameRedirects=(OldGameName="/Script/TP_Blank",NewGameName="/Script/FPSTemplate") + +[/Script/AndroidFileServerEditor.AndroidFileServerRuntimeSettings] +bEnablePlugin=True +bAllowNetworkConnection=True +SecurityToken=62442D434388E2324BF7AFB7F208A9F3 +bIncludeInShipping=False +bAllowExternalStartInShipping=False +bCompileAFSProject=False +bUseCompression=False +bLogFiles=False +bReportStats=False +ConnectionType=USBOnly +bUseManualIPAddress=False +ManualIPAddress= + +[/Script/Engine.CollisionProfile] +-Profiles=(Name="NoCollision",CollisionEnabled=NoCollision,ObjectTypeName="WorldStatic",CustomResponses=((Channel="Visibility",Response=ECR_Ignore),(Channel="Camera",Response=ECR_Ignore)),HelpMessage="No collision",bCanModify=False) +-Profiles=(Name="BlockAll",CollisionEnabled=QueryAndPhysics,ObjectTypeName="WorldStatic",CustomResponses=,HelpMessage="WorldStatic object that blocks all actors by default. All new custom channels will use its own default response. ",bCanModify=False) +-Profiles=(Name="OverlapAll",CollisionEnabled=QueryOnly,ObjectTypeName="WorldStatic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Overlap),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldStatic object that overlaps all actors by default. All new custom channels will use its own default response. ",bCanModify=False) +-Profiles=(Name="BlockAllDynamic",CollisionEnabled=QueryAndPhysics,ObjectTypeName="WorldDynamic",CustomResponses=,HelpMessage="WorldDynamic object that blocks all actors by default. All new custom channels will use its own default response. ",bCanModify=False) +-Profiles=(Name="OverlapAllDynamic",CollisionEnabled=QueryOnly,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Overlap),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldDynamic object that overlaps all actors by default. All new custom channels will use its own default response. ",bCanModify=False) +-Profiles=(Name="IgnoreOnlyPawn",CollisionEnabled=QueryOnly,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="Pawn",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore)),HelpMessage="WorldDynamic object that ignores Pawn and Vehicle. All other channels will be set to default.",bCanModify=False) +-Profiles=(Name="OverlapOnlyPawn",CollisionEnabled=QueryOnly,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="Pawn",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Ignore)),HelpMessage="WorldDynamic object that overlaps Pawn, Camera, and Vehicle. All other channels will be set to default. ",bCanModify=False) +-Profiles=(Name="Pawn",CollisionEnabled=QueryAndPhysics,ObjectTypeName="Pawn",CustomResponses=((Channel="Visibility",Response=ECR_Ignore)),HelpMessage="Pawn object. Can be used for capsule of any playerable character or AI. ",bCanModify=False) +-Profiles=(Name="Spectator",CollisionEnabled=QueryOnly,ObjectTypeName="Pawn",CustomResponses=((Channel="WorldStatic",Response=ECR_Block),(Channel="Pawn",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore),(Channel="WorldDynamic",Response=ECR_Ignore),(Channel="Camera",Response=ECR_Ignore),(Channel="PhysicsBody",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore),(Channel="Destructible",Response=ECR_Ignore)),HelpMessage="Pawn object that ignores all other actors except WorldStatic.",bCanModify=False) +-Profiles=(Name="CharacterMesh",CollisionEnabled=QueryOnly,ObjectTypeName="Pawn",CustomResponses=((Channel="Pawn",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore)),HelpMessage="Pawn object that is used for Character Mesh. All other channels will be set to default.",bCanModify=False) +-Profiles=(Name="PhysicsActor",CollisionEnabled=QueryAndPhysics,ObjectTypeName="PhysicsBody",CustomResponses=,HelpMessage="Simulating actors",bCanModify=False) +-Profiles=(Name="Destructible",CollisionEnabled=QueryAndPhysics,ObjectTypeName="Destructible",CustomResponses=,HelpMessage="Destructible actors",bCanModify=False) +-Profiles=(Name="InvisibleWall",CollisionEnabled=QueryAndPhysics,ObjectTypeName="WorldStatic",CustomResponses=((Channel="Visibility",Response=ECR_Ignore)),HelpMessage="WorldStatic object that is invisible.",bCanModify=False) +-Profiles=(Name="InvisibleWallDynamic",CollisionEnabled=QueryAndPhysics,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="Visibility",Response=ECR_Ignore)),HelpMessage="WorldDynamic object that is invisible.",bCanModify=False) +-Profiles=(Name="Trigger",CollisionEnabled=QueryOnly,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Ignore),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldDynamic object that is used for trigger. All other channels will be set to default.",bCanModify=False) +-Profiles=(Name="Ragdoll",CollisionEnabled=QueryAndPhysics,ObjectTypeName="PhysicsBody",CustomResponses=((Channel="Pawn",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore)),HelpMessage="Simulating Skeletal Mesh Component. All other channels will be set to default.",bCanModify=False) +-Profiles=(Name="Vehicle",CollisionEnabled=QueryAndPhysics,ObjectTypeName="Vehicle",CustomResponses=,HelpMessage="Vehicle object that blocks Vehicle, WorldStatic, and WorldDynamic. All other channels will be set to default.",bCanModify=False) +-Profiles=(Name="UI",CollisionEnabled=QueryOnly,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Block),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldStatic object that overlaps all actors by default. All new custom channels will use its own default response. ",bCanModify=False) ++Profiles=(Name="NoCollision",CollisionEnabled=NoCollision,bCanModify=False,ObjectTypeName="WorldStatic",CustomResponses=((Channel="Visibility",Response=ECR_Ignore),(Channel="Camera",Response=ECR_Ignore)),HelpMessage="No collision") ++Profiles=(Name="BlockAll",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="WorldStatic",CustomResponses=,HelpMessage="WorldStatic object that blocks all actors by default. All new custom channels will use its own default response. ") ++Profiles=(Name="OverlapAll",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="WorldStatic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Overlap),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldStatic object that overlaps all actors by default. All new custom channels will use its own default response. ") ++Profiles=(Name="BlockAllDynamic",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="WorldDynamic",CustomResponses=,HelpMessage="WorldDynamic object that blocks all actors by default. All new custom channels will use its own default response. ") ++Profiles=(Name="OverlapAllDynamic",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Overlap),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldDynamic object that overlaps all actors by default. All new custom channels will use its own default response. ") ++Profiles=(Name="IgnoreOnlyPawn",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="Pawn",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore)),HelpMessage="WorldDynamic object that ignores Pawn and Vehicle. All other channels will be set to default.") ++Profiles=(Name="OverlapOnlyPawn",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="Pawn",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Ignore)),HelpMessage="WorldDynamic object that overlaps Pawn, Camera, and Vehicle. All other channels will be set to default. ") ++Profiles=(Name="Pawn",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="Pawn",CustomResponses=((Channel="Visibility",Response=ECR_Ignore)),HelpMessage="Pawn object. Can be used for capsule of any playerable character or AI. ") ++Profiles=(Name="Spectator",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="Pawn",CustomResponses=((Channel="WorldStatic"),(Channel="Pawn",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore),(Channel="WorldDynamic",Response=ECR_Ignore),(Channel="Camera",Response=ECR_Ignore),(Channel="PhysicsBody",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore),(Channel="Destructible",Response=ECR_Ignore)),HelpMessage="Pawn object that ignores all other actors except WorldStatic.") ++Profiles=(Name="CharacterMesh",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="Pawn",CustomResponses=((Channel="Pawn",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore)),HelpMessage="Pawn object that is used for Character Mesh. All other channels will be set to default.") ++Profiles=(Name="PhysicsActor",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="PhysicsBody",CustomResponses=,HelpMessage="Simulating actors") ++Profiles=(Name="Destructible",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="Destructible",CustomResponses=,HelpMessage="Destructible actors") ++Profiles=(Name="InvisibleWall",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="WorldStatic",CustomResponses=((Channel="Visibility",Response=ECR_Ignore)),HelpMessage="WorldStatic object that is invisible.") ++Profiles=(Name="InvisibleWallDynamic",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="Visibility",Response=ECR_Ignore)),HelpMessage="WorldDynamic object that is invisible.") ++Profiles=(Name="Trigger",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Ignore),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldDynamic object that is used for trigger. All other channels will be set to default.") ++Profiles=(Name="Ragdoll",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="PhysicsBody",CustomResponses=((Channel="Pawn",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore)),HelpMessage="Simulating Skeletal Mesh Component. All other channels will be set to default.") ++Profiles=(Name="Vehicle",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="Vehicle",CustomResponses=,HelpMessage="Vehicle object that blocks Vehicle, WorldStatic, and WorldDynamic. All other channels will be set to default.") ++Profiles=(Name="UI",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility"),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldStatic object that overlaps all actors by default. All new custom channels will use its own default response. ") ++Profiles=(Name="Pickup",CollisionEnabled=QueryOnly,bCanModify=True,ObjectTypeName="WorldStatic",CustomResponses=((Channel="WorldStatic",Response=ECR_Ignore),(Channel="WorldDynamic",Response=ECR_Ignore),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Ignore),(Channel="Camera",Response=ECR_Ignore),(Channel="PhysicsBody",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore),(Channel="Destructible",Response=ECR_Ignore),(Channel="Weapon",Response=ECR_Ignore)),HelpMessage="Only overlaps with Pawn") ++DefaultChannelResponses=(Channel=ECC_GameTraceChannel1,DefaultResponse=ECR_Block,bTraceType=True,bStaticObject=False,Name="Weapon") ++EditProfiles=(Name="Pawn",CustomResponses=((Channel="Weapon",Response=ECR_Ignore))) +-ProfileRedirects=(OldName="BlockingVolume",NewName="InvisibleWall") +-ProfileRedirects=(OldName="InterpActor",NewName="IgnoreOnlyPawn") +-ProfileRedirects=(OldName="StaticMeshComponent",NewName="BlockAllDynamic") +-ProfileRedirects=(OldName="SkeletalMeshActor",NewName="PhysicsActor") +-ProfileRedirects=(OldName="InvisibleActor",NewName="InvisibleWallDynamic") ++ProfileRedirects=(OldName="BlockingVolume",NewName="InvisibleWall") ++ProfileRedirects=(OldName="InterpActor",NewName="IgnoreOnlyPawn") ++ProfileRedirects=(OldName="StaticMeshComponent",NewName="BlockAllDynamic") ++ProfileRedirects=(OldName="SkeletalMeshActor",NewName="PhysicsActor") ++ProfileRedirects=(OldName="InvisibleActor",NewName="InvisibleWallDynamic") +-CollisionChannelRedirects=(OldName="Static",NewName="WorldStatic") +-CollisionChannelRedirects=(OldName="Dynamic",NewName="WorldDynamic") +-CollisionChannelRedirects=(OldName="VehicleMovement",NewName="Vehicle") +-CollisionChannelRedirects=(OldName="PawnMovement",NewName="Pawn") ++CollisionChannelRedirects=(OldName="Static",NewName="WorldStatic") ++CollisionChannelRedirects=(OldName="Dynamic",NewName="WorldDynamic") ++CollisionChannelRedirects=(OldName="VehicleMovement",NewName="Vehicle") ++CollisionChannelRedirects=(OldName="PawnMovement",NewName="Pawn") + +[/Script/Engine.PhysicsSettings] +PhysicsPrediction=(bEnablePhysicsPrediction=False,bEnablePhysicsResimulation=False,ResimulationErrorThreshold=10.000000,MaxSupportedLatencyPrediction=1000.000000) +PhysicErrorCorrection=(PingExtrapolation=0.100000,PingLimit=100.000000,ErrorPerLinearDifference=1.000000,ErrorPerAngularDifference=1.000000,MaxRestoredStateError=1.000000,MaxLinearHardSnapDistance=400.000000,PositionLerp=0.000000,AngleLerp=0.400000,LinearVelocityCoefficient=100.000000,AngularVelocityCoefficient=10.000000,ErrorAccumulationSeconds=0.500000,ErrorAccumulationDistanceSq=15.000000,ErrorAccumulationSimilarity=100.000000) +DefaultDegreesOfFreedom=Full3D +bSuppressFaceRemapTable=False +bSupportUVFromHitResults=False +bDisableActiveActors=False +bDisableKinematicStaticPairs=False +bDisableKinematicKinematicPairs=False +bDisableCCD=False +AnimPhysicsMinDeltaTime=0.000000 +bSimulateAnimPhysicsAfterReset=False +MinPhysicsDeltaTime=0.000000 +MaxPhysicsDeltaTime=0.033333 +bSubstepping=False +bSubsteppingAsync=False +bTickPhysicsAsync=False +AsyncFixedTimeStepSize=0.033333 +MaxSubstepDeltaTime=0.016667 +MaxSubsteps=6 +SyncSceneSmoothingFactor=0.000000 +InitialAverageFrameRate=0.016667 +PhysXTreeRebuildRate=10 ++PhysicalSurfaces=(Type=SurfaceType1,Name="Character") ++PhysicalSurfaces=(Type=SurfaceType2,Name="Concrete") ++PhysicalSurfaces=(Type=SurfaceType3,Name="Glass") +DefaultBroadphaseSettings=(bUseMBPOnClient=False,bUseMBPOnServer=False,bUseMBPOuterBounds=False,MBPBounds=(Min=(X=0.000000,Y=0.000000,Z=0.000000),Max=(X=0.000000,Y=0.000000,Z=0.000000),IsValid=False),MBPOuterBounds=(Min=(X=0.000000,Y=0.000000,Z=0.000000),Max=(X=0.000000,Y=0.000000,Z=0.000000),IsValid=False),MBPNumSubdivs=2) +MinDeltaVelocityForHitEvents=0.000000 +ChaosSettings=(DefaultThreadingModel=TaskGraph,DedicatedThreadTickMode=VariableCappedWithTarget,DedicatedThreadBufferMode=Double) + diff --git a/Config/DefaultGame.ini b/Config/DefaultGame.ini new file mode 100644 index 00000000..27445d90 --- /dev/null +++ b/Config/DefaultGame.ini @@ -0,0 +1,101 @@ + + +[/Script/EngineSettings.GeneralProjectSettings] +ProjectID=98BF0A5A45D54BDA3BDC1C9EE63E8017 + +[/Script/Engine.GameSession] +MaxPlayers=100 + +[/Script/UnrealEd.ProjectPackagingSettings] +Build=IfProjectHasCode +BuildConfiguration=PPBC_Development +BuildTarget= +FullRebuild=False +ForDistribution=False +IncludeDebugFiles=False +BlueprintNativizationMethod=Disabled +bIncludeNativizedAssetsInProjectGeneration=False +bExcludeMonolithicEngineHeadersInNativizedCode=False +UsePakFile=True +bUseIoStore=True +bUseZenStore=False +bMakeBinaryConfig=False +bGenerateChunks=False +bGenerateNoChunks=False +bChunkHardReferencesOnly=False +bForceOneChunkPerFile=False +MaxChunkSize=0 +bBuildHttpChunkInstallData=False +HttpChunkInstallDataDirectory=(Path="") +WriteBackMetadataToAssetRegistry=Disabled +bWritePluginSizeSummaryJsons=False +bCompressed=True +PackageCompressionFormat=Oodle +bForceUseProjectCompressionFormatIgnoreHardwareOverride=False +PackageAdditionalCompressionOptions= +PackageCompressionMethod=Kraken +PackageCompressionLevel_DebugDevelopment=4 +PackageCompressionLevel_TestShipping=4 +PackageCompressionLevel_Distribution=7 +PackageCompressionMinBytesSaved=1024 +PackageCompressionMinPercentSaved=5 +bPackageCompressionEnableDDC=False +PackageCompressionMinSizeToConsiderDDC=0 +HttpChunkInstallDataVersion= +IncludePrerequisites=True +IncludeAppLocalPrerequisites=False +bShareMaterialShaderCode=True +bDeterministicShaderCodeOrder=False +bSharedMaterialNativeLibraries=True +ApplocalPrerequisitesDirectory=(Path="") +IncludeCrashReporter=False +InternationalizationPreset=English +-CulturesToStage=en ++CulturesToStage=en +LocalizationTargetCatchAllChunkId=0 +bCookAll=False +bCookMapsOnly=False +bSkipEditorContent=False +bSkipMovies=False +-IniKeyDenylist=KeyStorePassword +-IniKeyDenylist=KeyPassword +-IniKeyDenylist=rsa.privateexp +-IniKeyDenylist=rsa.modulus +-IniKeyDenylist=rsa.publicexp +-IniKeyDenylist=aes.key +-IniKeyDenylist=SigningPublicExponent +-IniKeyDenylist=SigningModulus +-IniKeyDenylist=SigningPrivateExponent +-IniKeyDenylist=EncryptionKey +-IniKeyDenylist=DevCenterUsername +-IniKeyDenylist=DevCenterPassword +-IniKeyDenylist=IOSTeamID +-IniKeyDenylist=SigningCertificate +-IniKeyDenylist=MobileProvision +-IniKeyDenylist=IniKeyDenylist +-IniKeyDenylist=IniSectionDenylist ++IniKeyDenylist=KeyStorePassword ++IniKeyDenylist=KeyPassword ++IniKeyDenylist=rsa.privateexp ++IniKeyDenylist=rsa.modulus ++IniKeyDenylist=rsa.publicexp ++IniKeyDenylist=aes.key ++IniKeyDenylist=SigningPublicExponent ++IniKeyDenylist=SigningModulus ++IniKeyDenylist=SigningPrivateExponent ++IniKeyDenylist=EncryptionKey ++IniKeyDenylist=DevCenterUsername ++IniKeyDenylist=DevCenterPassword ++IniKeyDenylist=IOSTeamID ++IniKeyDenylist=SigningCertificate ++IniKeyDenylist=MobileProvision ++IniKeyDenylist=IniKeyDenylist ++IniKeyDenylist=IniSectionDenylist +-IniSectionDenylist=HordeStorageServers +-IniSectionDenylist=StorageServers ++IniSectionDenylist=HordeStorageServers ++IniSectionDenylist=StorageServers ++MapsToCook=(FilePath="/Game/ThirdPerson/Maps/ThirdPersonMap") +bRetainStagedDirectory=False +CustomStageCopyHandler= + diff --git a/Config/DefaultInput.ini b/Config/DefaultInput.ini new file mode 100644 index 00000000..91fbc4d3 --- /dev/null +++ b/Config/DefaultInput.ini @@ -0,0 +1,99 @@ +[/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 ++ActionMappings=(ActionName="Jump",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=Gamepad_FaceButton_Bottom) ++ActionMappings=(ActionName="Jump",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=SpaceBar) ++AxisMappings=(AxisName="Look Up / Down Gamepad",Scale=1.000000,Key=Gamepad_RightY) ++AxisMappings=(AxisName="Look Up / Down Mouse",Scale=-1.000000,Key=MouseY) ++AxisMappings=(AxisName="Move Forward / Backward",Scale=1.000000,Key=Gamepad_LeftY) ++AxisMappings=(AxisName="Move Forward / Backward",Scale=-1.000000,Key=S) ++AxisMappings=(AxisName="Move Forward / Backward",Scale=1.000000,Key=W) ++AxisMappings=(AxisName="Move Right / Left",Scale=-1.000000,Key=A) ++AxisMappings=(AxisName="Move Right / Left",Scale=1.000000,Key=D) ++AxisMappings=(AxisName="Move Right / Left",Scale=1.000000,Key=Gamepad_LeftX) ++AxisMappings=(AxisName="Turn Right / Left Gamepad",Scale=1.000000,Key=Gamepad_RightX) ++AxisMappings=(AxisName="Turn Right / Left Mouse",Scale=1.000000,Key=MouseX) +DefaultPlayerInputClass=/Script/EnhancedInput.EnhancedPlayerInput +DefaultInputComponentClass=/Script/EnhancedInput.EnhancedInputComponent +DefaultTouchInterface=/Engine/MobileResources/HUD/DefaultVirtualJoysticks.DefaultVirtualJoysticks +-ConsoleKeys=Tilde ++ConsoleKeys=Tilde ++ConsoleKeys=Caret + diff --git a/FPSTemplate.uproject b/FPSTemplate.uproject new file mode 100644 index 00000000..b86d3e8f --- /dev/null +++ b/FPSTemplate.uproject @@ -0,0 +1,58 @@ +{ + "FileVersion": 3, + "EngineAssociation": "{B9F6BD5B-49A9-C0EB-5AFC-04B92D107838}", + "Category": "", + "Description": "", + "Modules": [ + { + "Name": "FPSTemplate", + "Type": "Runtime", + "LoadingPhase": "Default", + "AdditionalDependencies": [ + "Engine", + "UMG" + ] + } + ], + "Plugins": [ + { + "Name": "ModelingToolsEditorMode", + "Enabled": true, + "TargetAllowList": [ + "Editor" + ] + }, + { + "Name": "OnlineSubsystemSteam", + "Enabled": true + }, + { + "Name": "CommonUI", + "Enabled": true + }, + { + "Name": "AnimationWarping", + "Enabled": true + }, + { + "Name": "AudioModulation", + "Enabled": true + }, + { + "Name": "Spatialization", + "Enabled": true + }, + { + "Name": "SubversionSourceControl", + "Enabled": false + }, + { + "Name": "PlasticSourceControl", + "Enabled": false + }, + { + "Name": "PerforceSourceControl", + "Enabled": false + } + ] +} \ No newline at end of file diff --git a/Plugins/Developer/RiderLink/Resources/Icon128.png b/Plugins/Developer/RiderLink/Resources/Icon128.png new file mode 100644 index 00000000..41609544 Binary files /dev/null and b/Plugins/Developer/RiderLink/Resources/Icon128.png differ diff --git a/Plugins/Developer/RiderLink/Resources/checksum b/Plugins/Developer/RiderLink/Resources/checksum new file mode 100644 index 00000000..dc1c9539 --- /dev/null +++ b/Plugins/Developer/RiderLink/Resources/checksum @@ -0,0 +1 @@ +5L9OHnq \ No newline at end of file diff --git a/Plugins/Developer/RiderLink/RiderLink.uplugin b/Plugins/Developer/RiderLink/RiderLink.uplugin new file mode 100644 index 00000000..55b6e7da --- /dev/null +++ b/Plugins/Developer/RiderLink/RiderLink.uplugin @@ -0,0 +1,61 @@ +{ + "FileVersion": 3, + "Version": 1, + "VersionName": "2025.3.0.1708", + "FriendlyName": "RiderLink", + "Description": "Plugin for establishing IPC connection with JetBrains Rider IDE", + "Category": "Programming", + "CreatedBy": "JetBrains", + "CreatedByURL": "https://www.jetbrains.com/", + "DocsURL": "", + "MarketplaceURL": "", + "SupportURL": "", + "CanContainContent": false, + "Installed": false, + "Modules": [ + { + "Name": "RD", + "Type": "EditorNoCommandlet", + "LoadingPhase": "PreDefault" + }, + { + "Name": "RiderLink", + "Type": "EditorNoCommandlet", + "LoadingPhase": "PreDefault" + }, + { + "Name": "RiderLogging", + "Type": "EditorNoCommandlet", + "LoadingPhase": "PreDefault" + }, + { + "Name": "RiderBlueprint", + "Type": "EditorNoCommandlet", + "LoadingPhase": "Default" + }, + { + "Name": "RiderGameControl", + "Type": "EditorNoCommandlet", + "LoadingPhase": "Default" + }, + { + "Name": "RiderShaderInfo", + "Type": "EditorNoCommandlet", + "LoadingPhase": "PostEngineInit" + }, + { + "Name": "RiderLC", + "Type": "EditorNoCommandlet", + "LoadingPhase": "PostEngineInit" + }, + { + "Name": "RiderDebuggerSupport", + "Type": "EditorNoCommandlet", + "LoadingPhase": "PreDefault", + "PlatformAllowList": [ + "Win64" + ] + } + ], + "EnabledByDefault": true +} \ No newline at end of file diff --git a/Plugins/Developer/RiderLink/Source/RD/RD.Build.cs b/Plugins/Developer/RiderLink/Source/RD/RD.Build.cs new file mode 100644 index 00000000..1376c855 --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/RD.Build.cs @@ -0,0 +1,93 @@ +using System.IO; +using UnrealBuildTool; + +public class RD : ModuleRules +{ + public RD(ReadOnlyTargetRules Target) : base(Target) + { + PublicDependencyModuleNames.Add("Core"); + bUseRTTI = true; + +#if UE_5_2_OR_LATER + bDisableStaticAnalysis = true; +#endif + +#if UE_5_2_OR_LATER + IWYUSupport = IWYUSupport.KeepAsIs; +#else + bEnforceIWYU = false; +#endif + +#if UE_5_6_OR_LATER + CppStandard = CppStandardVersion.Cpp20; +#elif UE_4_22_OR_LATER + CppStandard = CppStandardVersion.Cpp17; +#endif + +#if UE_4_22_OR_LATER + PCHUsage = PCHUsageMode.NoPCHs; +#else + PCHUsage = PCHUsageMode.NoSharedPCHs; +#endif + +#if UE_5_6_OR_LATER + CppCompileWarningSettings.ShadowVariableWarningLevel = WarningLevel.Off; +#elif UE_4_24_OR_LATER + ShadowVariableWarningLevel = WarningLevel.Off; +#else + bEnableShadowVariableWarnings = false; +#endif + +#if UE_4_24_OR_LATER + bUseUnity = false; +#else + bFasterWithoutUnity = true; +#endif + + PublicDefinitions.Add("_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS"); + + if (Target.Platform == UnrealTargetPlatform.Win64) + { + PublicDefinitions.Add("_WINSOCK_DEPRECATED_NO_WARNINGS"); + PublicDefinitions.Add("_CRT_SECURE_NO_WARNINGS"); + PublicDefinitions.Add("_CRT_NONSTDC_NO_DEPRECATE"); + PublicDefinitions.Add("SPDLOG_WCHAR_FILENAMES"); + PublicDefinitions.Add("SPDLOG_WCHAR_TO_UTF8_SUPPORT"); + PrivateDefinitions.Add("WIN32_LEAN_AND_MEAN"); + } + + if (Target.Platform == UnrealTargetPlatform.Mac) + { + PublicDefinitions.Add("_DARWIN"); + } + + // Common dependencies + PrivateDefinitions.Add("rd_framework_cpp_EXPORTS"); + PrivateDefinitions.Add("rd_core_cpp_EXPORTS"); + PrivateDefinitions.Add("spdlog_EXPORTS"); + PrivateDefinitions.Add("FMT_EXPORT"); + + PublicDefinitions.Add("SPDLOG_NO_EXCEPTIONS"); + PublicDefinitions.Add("SPDLOG_COMPILED_LIB"); + PublicDefinitions.Add("SPDLOG_SHARED_LIB"); + PublicDefinitions.Add( + "nssv_CONFIG_SELECT_STRING_VIEW=nssv_STRING_VIEW_NONSTD"); + PublicDefinitions.Add("FMT_SHARED"); + + string[] Paths = + { + "src", "src/rd_core_cpp", "src/rd_core_cpp/src/main" + , "src/rd_framework_cpp", "src/rd_framework_cpp/src/main" + , "src/rd_framework_cpp/src/main/util", "src/rd_gen_cpp/src" + , "thirdparty", "thirdparty/ordered-map/include" + , "thirdparty/optional/tl", "thirdparty/variant/include" + , "thirdparty/string-view-lite/include", "thirdparty/spdlog/include" + , "thirdparty/clsocket/src", "thirdparty/CTPL/include", "thirdparty/utf-cpp/include" + }; + + foreach (var Item in Paths) + { + PublicIncludePaths.Add(Path.Combine(ModuleDirectory, Item)); + } + } +} diff --git a/Plugins/Developer/RiderLink/Source/RD/RD.cpp b/Plugins/Developer/RiderLink/Source/RD/RD.cpp new file mode 100644 index 00000000..38404287 --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/RD.cpp @@ -0,0 +1,11 @@ +#include "RD.h" + +#include + +#define LOCTEXT_NAMESPACE "RD" + +DEFINE_LOG_CATEGORY(FLogRDModule); + +IMPLEMENT_MODULE(FRDModule, RD); + +#undef LOCTEXT_NAMESPACE \ No newline at end of file diff --git a/Plugins/Developer/RiderLink/Source/RD/RD.h b/Plugins/Developer/RiderLink/Source/RD/RD.h new file mode 100644 index 00000000..4a477e10 --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/RD.h @@ -0,0 +1,16 @@ +// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "Logging/LogMacros.h" +#include "Logging/LogVerbosity.h" +#include "Modules/ModuleInterface.h" + +DECLARE_LOG_CATEGORY_EXTERN(FLogRDModule, Log, All); + +class FRDModule : public IModuleInterface +{ +public: + FRDModule() = default; + ~FRDModule() = default; +}; diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/rd_core_export.h b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/rd_core_export.h new file mode 100644 index 00000000..50b8f729 --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/rd_core_export.h @@ -0,0 +1,83 @@ + +#ifndef RD_CORE_API_H +#define RD_CORE_API_H + +#if defined(_WIN32) +#ifdef RD_CORE_STATIC_DEFINE +# define RD_CORE_API +# define RD_CORE_NO_EXPORT +#else +# ifndef RD_CORE_API +# ifdef rd_core_cpp_EXPORTS + /* We are building this library */ +# define RD_CORE_API __declspec(dllexport) +# else + /* We are using this library */ +# define RD_CORE_API __declspec(dllimport) +# endif +# endif + +# ifndef RD_CORE_NO_EXPORT +# define RD_CORE_NO_EXPORT +# endif +#endif + +#ifndef RD_CORE_DEPRECATED +# define RD_CORE_DEPRECATED __declspec(deprecated) +#endif + +#ifndef RD_CORE_DEPRECATED_EXPORT +# define RD_CORE_DEPRECATED_EXPORT RD_CORE_API RD_CORE_DEPRECATED +#endif + +#ifndef RD_CORE_DEPRECATED_NO_EXPORT +# define RD_CORE_DEPRECATED_NO_EXPORT RD_CORE_NO_EXPORT RD_CORE_DEPRECATED +#endif + +#if 0 /* DEFINE_NO_DEPRECATED */ +# ifndef RD_CORE_NO_DEPRECATED +# define RD_CORE_NO_DEPRECATED +# endif +#endif +#endif + +#if defined(unix) || defined(__unix__) || defined(__unix) || defined(__APPLE__) +#ifdef RD_CORE_STATIC_DEFINE +# define RD_CORE_API +# define RD_CORE_NO_EXPORT +#else +# ifndef RD_CORE_API +# ifdef rd_core_cpp_EXPORTS + /* We are building this library */ +# define RD_CORE_API __attribute__((visibility("default"))) +# else + /* We are using this library */ +# define RD_CORE_API __attribute__((visibility("default"))) +# endif +# endif + +# ifndef RD_CORE_NO_EXPORT +# define RD_CORE_NO_EXPORT __attribute__((visibility("hidden"))) +# endif +#endif + +#ifndef RD_CORE_DEPRECATED +# define RD_CORE_DEPRECATED __attribute__ ((__deprecated__)) +#endif + +#ifndef RD_CORE_DEPRECATED_EXPORT +# define RD_CORE_DEPRECATED_EXPORT RD_CORE_API RD_CORE_DEPRECATED +#endif + +#ifndef RD_CORE_DEPRECATED_NO_EXPORT +# define RD_CORE_DEPRECATED_NO_EXPORT RD_CORE_NO_EXPORT RD_CORE_DEPRECATED +#endif + +#if 0 /* DEFINE_NO_DEPRECATED */ +# ifndef RD_CORE_NO_DEPRECATED +# define RD_CORE_NO_DEPRECATED +# endif +#endif +#endif + +#endif /* RD_CORE_API_H */ diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/Lifetime.cpp b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/Lifetime.cpp new file mode 100644 index 00000000..d63a9bfb --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/Lifetime.cpp @@ -0,0 +1,49 @@ +#include "Lifetime.h" + +#include + +#include +#include +#include + +namespace rd +{ +/*thread_local */ Lifetime::Allocator Lifetime::allocator; + +LifetimeImpl* Lifetime::operator->() const +{ + return ptr.operator->(); +} + +std::once_flag onceFlag; + +Lifetime::Lifetime(bool is_eternal) : ptr(std::allocate_shared(allocator, is_eternal)) +{ + std::call_once(onceFlag, [] { + spdlog::set_default_logger(spdlog::stderr_color_mt("default", spdlog::color_mode::automatic)); + }); +} + +Lifetime Lifetime::create_nested() const +{ + Lifetime lw(false); + ptr->attach_nested(lw.ptr); + return lw; +} + +Lifetime const& Lifetime::Eternal() +{ + static Lifetime ETERNAL(true); + return ETERNAL; +} + +bool operator==(Lifetime const& lw1, Lifetime const& lw2) +{ + return lw1.ptr == lw2.ptr; +} + +bool operator!=(Lifetime const& lw1, Lifetime const& lw2) +{ + return !(lw1 == lw2); +} +} // namespace rd diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/Lifetime.h b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/Lifetime.h new file mode 100644 index 00000000..72c48cf7 --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/Lifetime.h @@ -0,0 +1,76 @@ +#ifndef RD_CPP_CORE_LIFETIMEWRAPPER_H +#define RD_CPP_CORE_LIFETIMEWRAPPER_H + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4251) +#endif + +#include "LifetimeImpl.h" + +#include + +#include + +#include + +namespace rd +{ +class Lifetime; + +template <> +struct RD_CORE_API hash +{ + size_t operator()(const Lifetime& value) const noexcept; +}; + +class RD_CORE_API Lifetime final +{ +private: + using Allocator = std::allocator; + + static /*thread_local */ Allocator allocator; + + friend class LifetimeDefinition; + + friend struct hash; + + std::shared_ptr ptr; + +public: + static Lifetime const& Eternal(); + + // region ctor/dtor + + Lifetime(Lifetime const& other) = default; + + Lifetime& operator=(Lifetime const& other) = default; + + Lifetime(Lifetime&& other) noexcept = default; + + Lifetime& operator=(Lifetime&& other) noexcept = default; + + ~Lifetime() = default; + // endregion + + friend bool RD_CORE_API operator==(Lifetime const& lw1, Lifetime const& lw2); + friend bool RD_CORE_API operator!=(Lifetime const& lw1, Lifetime const& lw2); + + explicit Lifetime(bool is_eternal = false); + + LifetimeImpl* operator->() const; + + Lifetime create_nested() const; +}; + +inline size_t hash::operator()(const Lifetime& value) const noexcept +{ + return hash >()(value.ptr); +} +} // namespace rd +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + + +#endif // RD_CPP_CORE_LIFETIMEWRAPPER_H diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/LifetimeDefinition.cpp b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/LifetimeDefinition.cpp new file mode 100644 index 00000000..afadd735 --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/LifetimeDefinition.cpp @@ -0,0 +1,54 @@ +#include "LifetimeDefinition.h" + +#include + +namespace rd +{ +LifetimeDefinition::LifetimeDefinition(bool eternaled) : eternaled(eternaled), lifetime(eternaled) +{ +} + +LifetimeDefinition::LifetimeDefinition(const Lifetime& parent) : LifetimeDefinition(false) +{ + parent->attach_nested(lifetime.ptr); +} + +bool LifetimeDefinition::is_terminated() const +{ + return lifetime->is_terminated(); +} + +void LifetimeDefinition::terminate() +{ + lifetime->terminate(); +} + +bool LifetimeDefinition::is_eternal() const +{ + return lifetime->is_eternal(); +} + +namespace +{ +LifetimeDefinition ETERNAL(true); +} + +std::shared_ptr LifetimeDefinition::get_shared_eternal() +{ + return std::shared_ptr(&ETERNAL, [](LifetimeDefinition* /*ld*/) {}); +} + +LifetimeDefinition::~LifetimeDefinition() +{ + if (lifetime.ptr != nullptr) + { // wasn't moved + if (!is_eternal()) + { + if (!lifetime->is_terminated()) + { + lifetime->terminate(); + } + } + } +} +} // namespace rd diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/LifetimeDefinition.h b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/LifetimeDefinition.h new file mode 100644 index 00000000..f28584d6 --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/LifetimeDefinition.h @@ -0,0 +1,59 @@ +#ifndef RD_CPP_CORE_LIFETIME_DEFINITION_H +#define RD_CPP_CORE_LIFETIME_DEFINITION_H + +#include "util/core_traits.h" + +#include "LifetimeImpl.h" +#include "Lifetime.h" + +#include +#include + +#include + +namespace rd +{ +class RD_CORE_API LifetimeDefinition +{ +private: + friend class SequentialLifetimes; + + bool eternaled = false; + +public: + Lifetime lifetime; + + explicit LifetimeDefinition(bool is_eternal = false); + + explicit LifetimeDefinition(const Lifetime& parent); + + LifetimeDefinition(LifetimeDefinition const& other) = delete; + + LifetimeDefinition& operator=(LifetimeDefinition const& other) = delete; + + LifetimeDefinition(LifetimeDefinition&& other) = default; + + LifetimeDefinition& operator=(LifetimeDefinition&& other) = default; + + virtual ~LifetimeDefinition(); + + // static std::shared_ptr eternal; + static std::shared_ptr get_shared_eternal(); + + bool is_terminated() const; + + bool is_eternal() const; + + void terminate(); + + template + static auto use(F&& block) -> typename util::result_of_t + { + LifetimeDefinition definition(false); + Lifetime lw = definition.lifetime.create_nested(); + return block(lw); + } +}; +} // namespace rd + +#endif // RD_CPP_CORE_LIFETIME_DEFINITION_H diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/LifetimeImpl.cpp b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/LifetimeImpl.cpp new file mode 100644 index 00000000..fa6953bc --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/LifetimeImpl.cpp @@ -0,0 +1,66 @@ +#include "LifetimeImpl.h" + +#include + +namespace rd +{ +#if __cplusplus < 201703L +LifetimeImpl::counter_t LifetimeImpl::get_id = 0; +#endif + +LifetimeImpl::LifetimeImpl(bool is_eternal) : eternaled(is_eternal), id(LifetimeImpl::get_id++) +{ +} + +void LifetimeImpl::terminate() +{ + if (is_eternal()) + return; + + terminated = true; + + // region thread-safety section + + actions_t actions_copy; + { + std::lock_guard guard(actions_lock); + actions_copy = std::move(actions); + + actions.clear(); + } + // endregion + + for (auto it = actions_copy.rbegin(); it != actions_copy.rend(); ++it) + { + it->second(); + } +} + +bool LifetimeImpl::is_terminated() const +{ + return terminated; +} + +bool LifetimeImpl::is_eternal() const +{ + return eternaled; +} + +void LifetimeImpl::attach_nested(std::shared_ptr nested) +{ + if (nested->is_terminated() || is_eternal()) + return; + + std::function action = [nested] { nested->terminate(); }; + counter_t action_id = add_action(action); + nested->add_action([this, id = action_id] { actions.erase(id); }); +} + +LifetimeImpl::~LifetimeImpl() +{ + /*if (!is_eternal() && !is_terminated()) { + spdlog::error("forget to terminate lifetime with id: {}", to_string(id)); + terminate(); + }*/ +} +} // namespace rd diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/LifetimeImpl.h b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/LifetimeImpl.h new file mode 100644 index 00000000..455f28c8 --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/LifetimeImpl.h @@ -0,0 +1,108 @@ +#ifndef RD_CPP_CORE_LIFETIME_H +#define RD_CPP_CORE_LIFETIME_H + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4251) +#endif + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include + +namespace rd +{ +class RD_CORE_API LifetimeImpl final +{ +public: + friend class LifetimeDefinition; + + friend class Lifetime; + + using counter_t = int32_t; + +private: + bool eternaled = false; + std::atomic terminated{false}; + + counter_t id = 0; + + counter_t action_id_in_map = 0; + using actions_t = ordered_map, rd::hash>; + actions_t actions; + + void terminate(); + + std::mutex actions_lock; + +public: + // region ctor/dtor + explicit LifetimeImpl(bool is_eternal = false); + + LifetimeImpl(LifetimeImpl const& other) = delete; + + ~LifetimeImpl(); + // endregion + + template + counter_t add_action(F&& action) + { + std::lock_guard guard(actions_lock); + + if (is_eternal()) + { + return -1; + } + if (is_terminated()) + { + throw std::invalid_argument("Already Terminated"); + } + + actions[action_id_in_map] = std::forward(action); + return action_id_in_map++; + } + + void remove_action(counter_t i) + { + std::lock_guard guard(actions_lock); + + actions.erase(i); + } + +#if __cplusplus >= 201703L + static inline counter_t get_id = 0; +#else + static counter_t get_id; +#endif + + template + void bracket(F&& opening, G&& closing) + { + if (is_terminated()) + return; + opening(); + add_action(std::forward(closing)); + } + + bool is_terminated() const; + + bool is_eternal() const; + + void attach_nested(std::shared_ptr nested); +}; +} // namespace rd +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + + +#endif // RD_CPP_CORE_LIFETIME_H diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/SequentialLifetimes.cpp b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/SequentialLifetimes.cpp new file mode 100644 index 00000000..f57c5c69 --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/SequentialLifetimes.cpp @@ -0,0 +1,33 @@ +#include "SequentialLifetimes.h" + +namespace rd +{ +SequentialLifetimes::SequentialLifetimes(Lifetime parent_lifetime) : parent_lifetime(std::move(parent_lifetime)) +{ + this->parent_lifetime->add_action([this] { set_current_lifetime(LifetimeDefinition::get_shared_eternal()); }); +} + +Lifetime SequentialLifetimes::next() +{ + std::shared_ptr new_def = std::make_shared(parent_lifetime); + set_current_lifetime(new_def); + return current_def->lifetime; +} + +void SequentialLifetimes::terminate_current() +{ + set_current_lifetime(LifetimeDefinition::get_shared_eternal()); +} + +bool SequentialLifetimes::is_terminated() const +{ + return current_def->is_eternal() || current_def->is_terminated(); +} + +void SequentialLifetimes::set_current_lifetime(std::shared_ptr new_def) +{ + std::shared_ptr prev = current_def; + current_def = new_def; + prev->terminate(); +} +} // namespace rd diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/SequentialLifetimes.h b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/SequentialLifetimes.h new file mode 100644 index 00000000..2a42275e --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/lifetime/SequentialLifetimes.h @@ -0,0 +1,51 @@ +#ifndef RD_CPP_CORE_SEQUENTIAL_LIFETIMES_H +#define RD_CPP_CORE_SEQUENTIAL_LIFETIMES_H + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4251) +#endif + +#include "LifetimeDefinition.h" +#include "Lifetime.h" + +#include + +namespace rd +{ +class RD_CORE_API SequentialLifetimes +{ +private: + std::shared_ptr current_def = LifetimeDefinition::get_shared_eternal(); + Lifetime parent_lifetime; + +public: + // region ctor/dtor + SequentialLifetimes() = delete; + + SequentialLifetimes(SequentialLifetimes const&) = delete; + + SequentialLifetimes& operator=(SequentialLifetimes const&) = delete; + + SequentialLifetimes(SequentialLifetimes&&) = delete; + + SequentialLifetimes& operator=(SequentialLifetimes&&) = delete; + + explicit SequentialLifetimes(Lifetime parent_lifetime); + // endregion + + Lifetime next(); + + void terminate_current(); + + bool is_terminated() const; + + void set_current_lifetime(std::shared_ptr new_def); +}; +} // namespace rd +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + + +#endif // RD_CPP_CORE_SEQUENTIAL_LIFETIMES_H diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/Property.h b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/Property.h new file mode 100644 index 00000000..24fc8b1a --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/Property.h @@ -0,0 +1,75 @@ +#ifndef RD_CPP_CORE_PROPERTY_H +#define RD_CPP_CORE_PROPERTY_H + +#include "base/IProperty.h" +#include "reactive/base/SignalX.h" + +#include + +namespace rd +{ +/** + * \brief complete class which has \a Property 's properties. + * \tparam T type of stored value (may be abstract) + */ +template +class Property : public IProperty +{ + using WT = typename IProperty::WT; + +public: + // region ctor/dtor + + Property() = default; + + Property(Property&& other) = default; + + Property& operator=(Property&& other) = default; + + virtual ~Property() = default; + + template + explicit Property(F&& value) : IProperty(std::forward(value)) + { + } + // endregion + + T const& get() const override + { + RD_ASSERT_THROW_MSG(this->has_value(), "get of uninitialized value from property"); + return *(this->value); + } + + void set(WT new_value) const override + { + if (!this->has_value() || (this->get() != wrapper::get(new_value))) + { + if (this->has_value()) + { + this->before_change.fire(*(this->value)); + } + this->value = std::move(new_value); + this->change.fire(*(this->value)); + } + } + + friend bool operator==(const Property& lhs, const Property& rhs) + { + return &lhs == &rhs; + } + + friend bool operator!=(const Property& lhs, const Property& rhs) + { + return !(rhs == lhs); + } + + friend std::string to_string(Property const& value) + { + return value.has_value() ? to_string(value.get()) : "empty property"s; + } +}; +} // namespace rd + +static_assert(std::is_move_constructible>::value, "Is not move constructible from Property"); + +#endif // RD_CPP_CORE_PROPERTY_H diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/ViewableList.h b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/ViewableList.h new file mode 100644 index 00000000..e214e704 --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/ViewableList.h @@ -0,0 +1,331 @@ +#ifndef RD_CPP_CORE_VIEWABLELIST_H +#define RD_CPP_CORE_VIEWABLELIST_H + +#include "base/IViewableList.h" +#include "reactive/base/SignalX.h" +#include "util/core_util.h" + +#include +#include +#include + +namespace rd +{ +/** + * \brief complete class which has @code IViewableList's properties + */ +template > +class ViewableList : public IViewableList +{ +public: + using Event = typename IViewableList::Event; + +private: + using WA = typename std::allocator_traits::template rebind_alloc>; + + using data_t = std::vector, WA>; + mutable data_t list; + Signal change; + +protected: + using WT = typename IViewableList::WT; + + const std::vector>& getList() const override + { + return list; + } + +public: + // region ctor/dtor + + ViewableList() = default; + + ViewableList(ViewableList&&) = default; + + ViewableList& operator=(ViewableList&&) = default; + + virtual ~ViewableList() = default; + + // endregion + + // region iterators +public: + class iterator + { + friend class ViewableList; + + typename data_t::iterator it_; + + explicit iterator(const typename data_t::iterator& it) : it_(it) + { + } + + public: + using iterator_category = std::random_access_iterator_tag; + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = T const*; + using reference = T const&; + + iterator(const iterator& other) = default; + + iterator(iterator&& other) noexcept = default; + + iterator& operator=(const iterator& other) = default; + + iterator& operator=(iterator&& other) noexcept = default; + + iterator& operator++() + { + ++it_; + return *this; + } + + iterator operator++(int) + { + auto it = *this; + ++*this; + return it; + } + + iterator& operator--() + { + --it_; + return *this; + } + + iterator operator--(int) + { + auto it = *this; + --*this; + return it; + } + + iterator& operator+=(difference_type delta) + { + it_ += delta; + return *this; + } + + iterator& operator-=(difference_type delta) + { + it_ -= delta; + return *this; + } + + iterator operator+(difference_type delta) const + { + auto it = *this; + return it += delta; + } + + iterator operator-(difference_type delta) const + { + auto it = *this; + return it -= delta; + } + + difference_type operator-(iterator const& other) const + { + return it_ - other.it_; + } + + bool operator<(iterator const& other) const noexcept + { + return this->it_ < other.it_; + } + + bool operator>(iterator const& other) const noexcept + { + return this->it_ > other.it_; + } + + bool operator==(iterator const& other) const noexcept + { + return this->it_ == other.it_; + } + + bool operator!=(iterator const& other) const noexcept + { + return !(*this == other); + } + + bool operator<=(iterator const& other) const noexcept + { + return (this->it_ < other.it_) || (*this == other); + } + + bool operator>=(iterator const& other) const noexcept + { + return (this->it_ > other.it_) || (*this == other); + } + + reference operator*() noexcept + { + return **it_; + } + + reference operator*() const noexcept + { + return **it_; + } + + pointer operator->() noexcept + { + return (*it_).get(); + } + + pointer operator->() const noexcept + { + return (*it_).get(); + } + }; + + using reverse_iterator = std::reverse_iterator; + + iterator begin() const + { + return iterator(list.begin()); + } + + iterator end() const + { + return iterator(list.end()); + } + + reverse_iterator rbegin() const + { + return reverse_iterator(end()); + } + + reverse_iterator rend() const + { + return reverse_iterator(begin()); + } + // endregion + + void advise(Lifetime lifetime, std::function handler) const override + { + if (lifetime->is_terminated()) + return; + change.advise(lifetime, handler); + for (int32_t i = 0; i < static_cast(size()); ++i) + { + handler(typename Event::Add(i, &(*list[i]))); + } + } + + bool add(WT element) const override + { + list.emplace_back(std::move(element)); + change.fire(typename Event::Add(static_cast(size()) - 1, &(*list.back()))); + return true; + } + + bool add(size_t index, WT element) const override + { + list.emplace(list.begin() + index, std::move(element)); + change.fire(typename Event::Add(static_cast(index), &(*list[index]))); + return true; + } + + WT removeAt(size_t index) const override + { + auto res = std::move(list[index]); + list.erase(list.begin() + index); + + change.fire(typename Event::Remove(static_cast(index), &(*res))); + return wrapper::unwrap(std::move(res)); + } + + bool remove(T const& element) const override + { + auto it = std::find_if(list.begin(), list.end(), [&element](auto const& p) { return *p == element; }); + if (it == list.end()) + { + return false; + } + ViewableList::removeAt(std::distance(list.begin(), it)); + return true; + } + + T const& get(size_t index) const override + { + return *list[index]; + } + + WT set(size_t index, WT element) const override + { + auto old_value = std::move(list[index]); + list[index] = Wrapper(std::move(element)); + change.fire(typename Event::Update(static_cast(index), &(*old_value), &(*list[index]))); //??? + return wrapper::unwrap(std::move(old_value)); + } + + bool addAll(size_t index, std::vector elements) const override + { + for (auto& element : elements) + { + ViewableList::add(index, std::move(element)); + ++index; + } + return true; + } + + bool addAll(std::vector elements) const override + { + for (auto&& element : elements) + { + ViewableList::add(std::move(element)); + } + return true; + } + + void clear() const override + { + std::vector changes; + for (size_t i = size(); i > 0; --i) + { + changes.push_back(typename Event::Remove(static_cast(i - 1), &(*list[i - 1]))); + } + for (auto const& e : changes) + { + change.fire(e); + } + list.clear(); + } + + bool removeAll(std::vector elements) const override + { + // TO-DO faster + // std::unordered_set set(elements.begin(), elements.end()); + + bool res = false; + for (size_t i = list.size(); i > 0; --i) + { + auto const& x = list[i - 1]; + if (std::count_if(elements.begin(), elements.end(), + [&x](auto const& elem) { return wrapper::TransparentKeyEqual()(elem, x); }) > 0) + { + removeAt(i - 1); + res = true; + } + } + return res; + } + + size_t size() const override + { + return list.size(); + } + + bool empty() const override + { + return list.empty(); + } +}; +} // namespace rd + +static_assert(std::is_move_constructible>::value, "Is move constructible from ViewableList"); + +#endif // RD_CPP_CORE_VIEWABLELIST_H diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/ViewableMap.h b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/ViewableMap.h new file mode 100644 index 00000000..66269493 --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/ViewableMap.h @@ -0,0 +1,335 @@ +#ifndef RD_CPP_CORE_VIEWABLE_MAP_H +#define RD_CPP_CORE_VIEWABLE_MAP_H + +#include "base/IViewableMap.h" +#include "reactive/base/SignalX.h" + +#include +#include + +#include + +#include +#include + +namespace rd +{ +/** + * \brief complete class which has @code IViewableMap's properties + */ +template , typename VA = std::allocator> +class ViewableMap : public IViewableMap +{ +public: + using Event = typename IViewableMap::Event; + +private: + using WK = typename IViewableMap::WK; + using WV = typename IViewableMap::WV; + using OV = typename IViewableMap::OV; + using PA = typename std::allocator_traits::template rebind_alloc, Wrapper>>; + + Signal change; + + using data_t = ordered_map, Wrapper, wrapper::TransparentHash, wrapper::TransparentKeyEqual, PA>; + mutable data_t map; + +public: + // region ctor/dtor + + ViewableMap() = default; + + ViewableMap(ViewableMap&&) = default; + + ViewableMap& operator=(ViewableMap&&) = default; + + virtual ~ViewableMap() = default; + // endregion + + // region iterators + +public: + class iterator + { + friend class ViewableMap; + + mutable typename data_t::iterator it_; + + explicit iterator(const typename data_t::iterator& it) : it_(it) + { + } + + public: + using iterator_category = typename data_t::iterator::iterator_category; + using key_type = K; + using value_type = V; + using difference_type = std::ptrdiff_t; + using reference = V const&; + using pointer = V const*; + + iterator(const iterator& other) = default; + + iterator(iterator&& other) noexcept = default; + + iterator& operator=(const iterator& other) = default; + + iterator& operator=(iterator&& other) noexcept = default; + + iterator& operator++() + { + ++it_; + return *this; + } + + iterator operator++(int) + { + auto it = *this; + ++*this; + return it; + } + + iterator& operator--() + { + --it_; + return *this; + } + + iterator operator--(int) + { + auto it = *this; + --*this; + return it; + } + + iterator& operator+=(difference_type delta) + { + it_ += delta; + return *this; + } + + iterator& operator-=(difference_type delta) + { + it_ -= delta; + return *this; + } + + iterator operator+(difference_type delta) const + { + auto it = *this; + return it += delta; + } + + iterator operator-(difference_type delta) const + { + auto it = *this; + return it -= delta; + } + + difference_type operator-(iterator const& other) const + { + return it_ - other.it_; + } + + bool operator<(iterator const& other) const noexcept + { + return this->it_ < other.it_; + } + + bool operator>(iterator const& other) const noexcept + { + return this->it_ > other.it_; + } + + bool operator==(iterator const& other) const noexcept + { + return this->it_ == other.it_; + } + + bool operator!=(iterator const& other) const noexcept + { + return !(*this == other); + } + + bool operator<=(iterator const& other) const noexcept + { + return (this->it_ < other.it_) || (*this == other); + } + + bool operator>=(iterator const& other) const noexcept + { + return (this->it_ > other.it_) || (*this == other); + } + + reference operator*() const noexcept + { + return *it_.value(); + } + + pointer operator->() const noexcept + { + return it_.value().get(); + } + + key_type const& key() const + { + return *it_.key(); + } + + value_type const& value() const + { + return *it_.value(); + } + }; + + class reverse_iterator : public std::reverse_iterator + { + using base_t = std::reverse_iterator; + + public: + using iterator_category = typename iterator::iterator_category; + using key_type = typename iterator::key_type; + using value_type = typename iterator::value_type; + using difference_type = typename iterator::difference_type; + using reference = typename iterator::reference; + using pointer = typename iterator::pointer; + + reverse_iterator(const reverse_iterator& other) = default; + + reverse_iterator& operator=(const reverse_iterator& other) = default; + + explicit reverse_iterator(const iterator& other) : base_t(other){}; + + reverse_iterator& operator=(const iterator& other) + { + static_cast(*this) = other; + }; + + key_type const& key() const + { + auto it = base_t::current; + return (--(it)).key(); + } + + value_type const& value() const + { + auto it = base_t::current; + return (--it).value(); + } + }; + + iterator begin() const + { + return iterator(map.begin()); + } + + iterator end() const + { + return iterator(map.end()); + } + + reverse_iterator rbegin() const + { + return reverse_iterator(end()); + } + + reverse_iterator rend() const + { + return reverse_iterator(begin()); + } + + // endregion + + void advise(Lifetime lifetime, std::function handler) const override + { + change.advise(lifetime, handler); + /*for (auto const &[key, value] : map) {*/ + for (auto const& it : map) + { + auto& key = it.first; + auto& value = it.second; + handler(Event(typename Event::Add(&(*key), &(*value)))); + ; + } + } + + const V* get(K const& key) const override + { + auto it = map.find(key); + if (it == map.end()) + { + return nullptr; + } + return &(*it->second); + } + + const V* set(WK key, WV value) const override + { + if (map.count(key) == 0) + { + /*auto[it, success] = map.emplace(std::make_unique(std::move(key)), std::make_unique(std::move(value)));*/ + auto node = map.emplace(std::move(key), std::move(value)); + auto& it = node.first; + auto const& key_ptr = it->first; + auto const& value_ptr = it->second; + change.fire(typename Event::Add(&(*key_ptr), &(*value_ptr))); + return nullptr; + } + else + { + auto it = map.find(key); + auto const& key_ptr = it->first; + auto const& value_ptr = it->second; + + if (*value_ptr != wrapper::get(value)) + { // TO-DO more effective + Wrapper old_value = std::move(map.at(key)); + + map.at(key_ptr) = Wrapper(std::move(value)); + change.fire(typename Event::Update(&(*key_ptr), &(*old_value), &(*value_ptr))); + } + return &*(value_ptr); + } + } + + OV remove(K const& key) const override + { + if (map.count(key) > 0) + { + Wrapper old_value = std::move(map.at(key)); + change.fire(typename Event::Remove(&key, &(*old_value))); + map.erase(key); + return wrapper::unwrap(std::move(old_value)); + } + return nullopt; + } + + void clear() const override + { + std::vector changes; + /*for (auto const &[key, value] : map) {*/ + for (auto const& it : map) + { + changes.push_back(typename Event::Remove(&(*it.first), &(*it.second))); + } + for (auto const& it : changes) + { + change.fire(it); + } + map.clear(); + } + + size_t size() const override + { + return map.size(); + } + + bool empty() const override + { + return map.empty(); + } +}; +} // namespace rd + +static_assert(std::is_move_constructible>::value, "Is move constructible from ViewableMap"); + +#endif // RD_CPP_CORE_VIEWABLE_MAP_H diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/ViewableSet.h b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/ViewableSet.h new file mode 100644 index 00000000..ca3ba7bb --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/ViewableSet.h @@ -0,0 +1,272 @@ +#ifndef RD_CPP_CORE_VIEWABLESET_H +#define RD_CPP_CORE_VIEWABLESET_H + +#include "base/IViewableSet.h" +#include "reactive/base/SignalX.h" + +#include +#include + +namespace rd +{ +/** + * \brief complete class which has @code IViewableSet's properties + * \tparam T + */ +template > +class ViewableSet : public IViewableSet +{ +public: + using Event = typename IViewableSet::Event; + + using IViewableSet::advise; + +private: + using WT = typename IViewableSet::WT; + using WA = typename std::allocator_traits::template rebind_alloc>; + + Signal change; + using data_t = ordered_set, wrapper::TransparentHash, wrapper::TransparentKeyEqual, WA>; + mutable data_t set; + +public: + // region ctor/dtor + + ViewableSet() = default; + + ViewableSet(ViewableSet&&) = default; + + ViewableSet& operator=(ViewableSet&&) = default; + + virtual ~ViewableSet() = default; + // endregion + + // region iterators +public: + class iterator + { + friend class ViewableSet; + + typename data_t::iterator it_; + + explicit iterator(const typename data_t::iterator& it) : it_(it) + { + } + + public: + using iterator_category = std::random_access_iterator_tag; + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = T const*; + using reference = T const&; + + iterator(const iterator& other) = default; + + iterator(iterator&& other) noexcept = default; + + iterator& operator=(const iterator& other) = default; + + iterator& operator=(iterator&& other) noexcept = default; + + iterator& operator++() + { + ++it_; + return *this; + } + + iterator operator++(int) + { + auto it = *this; + ++*this; + return it; + } + + iterator& operator--() + { + --it_; + return *this; + } + + iterator operator--(int) + { + auto it = *this; + --*this; + return it; + } + + iterator& operator+=(difference_type delta) + { + it_ += delta; + return *this; + } + + iterator& operator-=(difference_type delta) + { + it_ -= delta; + return *this; + } + + iterator operator+(difference_type delta) const + { + auto it = *this; + return it += delta; + } + + iterator operator-(difference_type delta) const + { + auto it = *this; + return it -= delta; + } + + difference_type operator-(iterator const& other) const + { + return it_ - other.it_; + } + + bool operator<(iterator const& other) const noexcept + { + return this->it_ < other.it_; + } + + bool operator>(iterator const& other) const noexcept + { + return this->it_ > other.it_; + } + + bool operator==(iterator const& other) const noexcept + { + return this->it_ == other.it_; + } + + bool operator!=(iterator const& other) const noexcept + { + return !(*this == other); + } + + bool operator<=(iterator const& other) const noexcept + { + return (this->it_ < other.it_) || (*this == other); + } + + bool operator>=(iterator const& other) const noexcept + { + return (this->it_ > other.it_) || (*this == other); + } + + reference operator*() const noexcept + { + return **it_; + } + + pointer operator->() const noexcept + { + return (*it_).get(); + } + }; + + using reverse_iterator = std::reverse_iterator; + + iterator begin() const + { + return iterator(set.begin()); + } + + iterator end() const + { + return iterator(set.end()); + } + + reverse_iterator rbegin() const + { + return reverse_iterator(end()); + } + + reverse_iterator rend() const + { + return reverse_iterator(begin()); + } + + // endregion + + bool add(WT element) const override + { + /*auto const &[it, success] = set.emplace(std::make_unique(std::move(element)));*/ + auto const& it = set.emplace(std::move(element)); + if (!it.second) + { + return false; + } + change.fire(Event(AddRemove::ADD, &(wrapper::get(*it.first)))); + return true; + } + + bool addAll(std::vector elements) const override + { + for (auto&& element : elements) + { + ViewableSet::add(std::move(element)); + } + return true; + } + + void clear() const override + { + std::vector changes; + for (auto const& element : set) + { + changes.push_back(Event(AddRemove::REMOVE, &(*element))); + } + for (auto const& e : changes) + { + change.fire(e); + } + set.clear(); + } + + bool remove(T const& element) const override + { + if (!ViewableSet::contains(element)) + { + return false; + } + auto it = set.find(element); + change.fire(Event(AddRemove::REMOVE, &(wrapper::get(*it)))); + set.erase(it); + return true; + } + + void advise(Lifetime lifetime, std::function handler) const override + { + for (auto const& x : set) + { + handler(Event(AddRemove::ADD, &(*x))); + } + change.advise(lifetime, handler); + } + + size_t size() const override + { + return set.size(); + } + + bool contains(T const& element) const override + { + return set.count(element) > 0; + } + + bool empty() const override + { + return set.empty(); + } + + template + bool emplace_add(Args&&... args) const + { + return add(WT{std::forward(args)...}); + } +}; +} // namespace rd + +static_assert(std::is_move_constructible>::value, "Is move constructible from ViewableSet"); + +#endif // RD_CPP_CORE_VIEWABLESET_H diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/IProperty.h b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/IProperty.h new file mode 100644 index 00000000..f8766fc7 --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/IProperty.h @@ -0,0 +1,96 @@ +#ifndef RD_CPP_IPROPERTY_H +#define RD_CPP_IPROPERTY_H + +#include "SignalX.h" +#include "IPropertyBase.h" + +#include +#include + +#include + +namespace rd +{ +/** + * \brief A mutable property. + * \tparam T type of stored value (may be abstract) + */ +template +class IProperty : public IPropertyBase +{ +protected: + using WT = typename IPropertyBase::WT; + +public: + // region ctor/dtor + + IProperty() = default; + + IProperty(IProperty&& other) = default; + + IProperty& operator=(IProperty&& other) = default; + + explicit IProperty(T const& value) : IPropertyBase(value) + { + } + + template + explicit IProperty(F&& value) : IPropertyBase(std::forward(value)) + { + } + + virtual ~IProperty() = default; + // endregion + + virtual T const& get() const = 0; + +private: + void advise0(Lifetime lifetime, std::function handler, Signal const& signal) const + { + if (lifetime->is_terminated()) + { + return; + } + signal.advise(lifetime, handler); + if (this->has_value()) + { + handler(this->get()); + } + } + + void advise_before(Lifetime lifetime, std::function handler) const override + { + advise0(lifetime, handler, this->before_change); + } + +public: + void advise(Lifetime lifetime, std::function handler) const override + { + advise0(lifetime, std::move(handler), this->change); + } + + /** + * \brief set value of type T or derived type to it. + */ + virtual void set(value_or_wrapper) const = 0; + + /** + * \brief construct value of type T and delegate call to set + */ + template + void emplace(Args&&... args) const + { + set(value_or_wrapper{std::forward(args)...}); + } + + void set_if_empty(WT new_value) const + { + if (!this->has_value()) + { + set(std::move(new_value)); + } + } +}; +} // namespace rd + +#endif // RD_CPP_IPROPERTY_H diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/IPropertyBase.h b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/IPropertyBase.h new file mode 100644 index 00000000..209e235e --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/IPropertyBase.h @@ -0,0 +1,73 @@ +#ifndef RD_CPP_IPROPERTYBASE_H +#define RD_CPP_IPROPERTYBASE_H + +#include "interfaces.h" +#include "SignalX.h" + +#include +#include + +#include "thirdparty.hpp" + +namespace rd +{ +template +class IPropertyBase : public ISource, public IViewable +{ +protected: + mutable property_storage value; + + Signal change, before_change; + + using WT = value_or_wrapper; + +public: + bool has_value() const + { + return (bool) (value); + } + + // region ctor/dtor + + IPropertyBase() = default; + + IPropertyBase(IPropertyBase&& other) = default; + + IPropertyBase& operator=(IPropertyBase&& other) = default; + + template + explicit IPropertyBase(F&& value) : value(std::forward(value)) + { + } + + virtual ~IPropertyBase() = default; + // endregion + + virtual void advise_before(Lifetime lifetime, std::function handler) const = 0; + + void view(Lifetime lifetime, std::function handler) const override + { + if (lifetime->is_terminated()) + return; + + Lifetime lf = lifetime.create_nested(); + std::shared_ptr seq = std::make_shared(lf); + + this->advise_before(lf, [lf, seq](T const& /*v*/) { + if (!lf->is_terminated()) + { + seq->terminate_current(); + } + }); + + this->advise(lf, [lf, seq, handler](T const& v) { + if (!lf->is_terminated()) + { + handler(seq->next(), v); + } + }); + } +}; +} // namespace rd + +#endif // RD_CPP_IPROPERTYBASE_H diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/IViewableList.h b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/IViewableList.h new file mode 100644 index 00000000..cf40c2db --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/IViewableList.h @@ -0,0 +1,257 @@ +#ifndef RD_CPP_IVIEWABLELIST_H +#define RD_CPP_IVIEWABLELIST_H + +#include "interfaces.h" +#include "viewable_collections.h" + +#include +#include +#include + +#include + +#include +#include +#include + +#include "thirdparty.hpp" + +namespace rd +{ +namespace detail +{ +template +class ListEvent +{ +public: + class Add + { + public: + int32_t index; + T const* new_value; + + Add(int32_t index, T const* new_value) : index(index), new_value(new_value) + { + } + }; + + class Update + { + public: + int32_t index; + T const* old_value; + T const* new_value; + + Update(int32_t index, T const* old_value, T const* new_value) : index(index), old_value(old_value), new_value(new_value) + { + } + }; + + class Remove + { + public: + int32_t index; + T const* old_value; + + Remove(int32_t index, T const* old_value) : index(index), old_value(old_value) + { + } + }; + + variant v; + + ListEvent(Add x) : v(x) + { + } + + ListEvent(Update x) : v(x) + { + } + + ListEvent(Remove x) : v(x) + { + } + + int32_t get_index() const + { + return visit(util::make_visitor([](Add const& e) { return e.index; }, [](Update const& e) { return e.index; }, + [](Remove const& e) { return e.index; }), + v); + } + + T const* get_new_value() const + { + return visit(util::make_visitor([](Add const& e) { return e.new_value; }, [](Update const& e) { return e.new_value; }, + [](Remove const& /*e*/) { return static_cast(nullptr); }), + v); + } + + friend std::string to_string(ListEvent const& e) + { + std::string res = visit( + util::make_visitor( + [](typename ListEvent::Add const& e) { return "Add " + std::to_string(e.index) + ":" + to_string(*e.new_value); }, + [](typename ListEvent::Update const& e) { + return "Update " + std::to_string(e.index) + ":" + + // to_string(e.old_value) + ":" + + to_string(*e.new_value); + }, + [](typename ListEvent::Remove const& e) { return "Remove " + std::to_string(e.index); }), + e.v); + return res; + } +}; +} // namespace detail +/** + * \brief A list allowing its contents to be observed. + * \tparam T type of stored values (may be abstract) + */ +template +class IViewableList : public IViewable>, public ISource> +{ +protected: + using WT = value_or_wrapper; + +public: + /** + * \brief Represents an addition, update or removal of an element in the list. + */ + using Event = typename detail::ListEvent; + +protected: + mutable rd::unordered_map> lifetimes; + +public: + // region ctor/dtor + + IViewableList() = default; + + IViewableList(IViewableList&&) = default; + + IViewableList& operator=(IViewableList&&) = default; + + virtual ~IViewableList() = default; + // endregion + + /** + * \brief Adds a subscription to additions and removals of list elements. When a list element is updated, + * the [handler] is called twice: to report the removal of the old element and the addition of the new one. + * \param lifetime lifetime of subscription. + * \param handler to be called. + */ + void advise_add_remove(Lifetime lifetime, std::function handler) const + { + advise(lifetime, [handler](Event e) { + visit(util::make_visitor([handler](typename Event::Add const& e) { handler(AddRemove::ADD, e.index, *e.new_value); }, + [handler](typename Event::Update const& e) { + handler(AddRemove::REMOVE, e.index, *e.old_value); + handler(AddRemove::ADD, e.index, *e.new_value); + }, + [handler](typename Event::Remove const& e) { handler(AddRemove::REMOVE, e.index, *e.old_value); }), + e.v); + }); + } + + /** + * \brief Adds a subscription to changes of the contents of the list. + * \param lifetime lifetime of subscription. + * \param handler to be called. + */ + void view(Lifetime lifetime, std::function const&)> handler) const override + { + view(lifetime, [handler](Lifetime lt, size_t idx, T const& v) { handler(lt, std::make_pair(idx, &v)); }); + } + + /** + * \brief @see view above + */ + void view(Lifetime lifetime, std::function handler) const + { + advise_add_remove(lifetime, [this, lifetime, handler](AddRemove kind, size_t idx, T const& value) { + switch (kind) + { + case AddRemove::ADD: + { + LifetimeDefinition def(lifetime); + std::vector& v = lifetimes[lifetime]; + auto it = v.emplace(v.begin() + idx, std::move(def)); + handler(it->lifetime, idx, value); + break; + } + case AddRemove::REMOVE: + { + LifetimeDefinition def = std::move(lifetimes.at(lifetime)[idx]); + std::vector& v = lifetimes.at(lifetime); + v.erase(v.begin() + idx); + def.terminate(); + break; + } + } + }); + } + + void advise(Lifetime lifetime, std::function handler) const override = 0; + + virtual bool add(WT element) const = 0; + + virtual bool add(size_t index, WT element) const = 0; + + virtual WT removeAt(size_t index) const = 0; + + virtual bool remove(T const& element) const = 0; + + virtual T const& get(size_t index) const = 0; + + virtual WT set(size_t index, WT element) const = 0; + + virtual bool addAll(size_t index, std::vector elements) const = 0; + + virtual bool addAll(std::vector elements) const = 0; + + virtual void clear() const = 0; + + virtual bool removeAll(std::vector elements) const = 0; + + virtual size_t size() const = 0; + + virtual bool empty() const = 0; + + template + bool emplace_add(Args&&... args) const + { + return add(WT{std::forward(args)...}); + } + + template + bool emplace_add(size_t index, Args&&... args) const + { + return add(index, WT{std::forward(args)...}); + } + + template + WT emplace_set(size_t index, Args&&... args) const + { + return set(index, WT{std::forward(args)...}); + } + + template + friend typename std::enable_if<(!std::is_abstract::value), std::vector>::type convert_to_list( + IViewableList const& list); + +protected: + virtual const std::vector>& getList() const = 0; +}; + +template +typename std::enable_if<(!std::is_abstract::value), std::vector>::type convert_to_list(IViewableList const& list) +{ + std::vector res(list.size()); + std::transform(list.getList().begin(), list.getList().end(), res.begin(), [](Wrapper const& ptr) { return *ptr; }); + return res; +} +} // namespace rd + +static_assert( + std::is_move_constructible::Event>::value, "Is move constructible from IViewableList::Event"); + +#endif // RD_CPP_IVIEWABLELIST_H diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/IViewableMap.h b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/IViewableMap.h new file mode 100644 index 00000000..d749c659 --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/IViewableMap.h @@ -0,0 +1,247 @@ +#ifndef RD_CPP_IVIEWABLEMAP_H +#define RD_CPP_IVIEWABLEMAP_H + +#include "lifetime/LifetimeDefinition.h" +#include "util/overloaded.h" +#include "interfaces.h" +#include "viewable_collections.h" +#include "util/core_util.h" + +#include "std/unordered_map.h" + +#include "thirdparty.hpp" + +namespace rd +{ +namespace detail +{ +template +class MapEvent +{ +public: + class Add + { + public: + K const* key; + V const* new_value; + + Add(K const* key, V const* new_value) : key(key), new_value(new_value) + { + } + }; + + class Update + { + public: + K const* key; + V const* old_value; + V const* new_value; + + Update(K const* key, V const* old_value, V const* new_value) : key(key), old_value(old_value), new_value(new_value) + { + } + }; + + class Remove + { + public: + K const* key; + V const* old_value; + + Remove(K const* key, V const* old_value) : key(key), old_value(old_value) + { + } + }; + + variant v; + + MapEvent(Add x) : v(x) + { + } + + MapEvent(Update x) : v(x) + { + } + + MapEvent(Remove x) : v(x) + { + } + + K const* get_key() const + { + return visit( + util::make_visitor([](typename MapEvent::Add const& e) { return e.key; }, + [](typename MapEvent::Update const& e) { return e.key; }, [](typename MapEvent::Remove const& e) { return e.key; }), + v); + } + + V const* get_old_value() const + { + return visit(util::make_visitor([](typename MapEvent::Add const&) { return static_cast(nullptr); }, + [](typename MapEvent::Update const& e) { return e.old_value; }, + [](typename MapEvent::Remove const& e) { e.old_value; }), + v); + } + + V const* get_new_value() const + { + return visit(util::make_visitor([](typename MapEvent::Add const& e) { return e.new_value; }, + [](typename MapEvent::Update const& e) { return e.new_value; }, + [](typename MapEvent::Remove const& /*e*/) { return static_cast(nullptr); }), + v); + } + + friend std::string to_string(MapEvent const& e) + { + std::string res = + visit(util::make_visitor([](typename MapEvent::Add const& e) + -> std::string { return "Add " + to_string(*e.key) + ":" + to_string(*e.new_value); }, + [](typename MapEvent::Update const& e) -> std::string { + return "Update " + to_string(*e.key) + ":" + + // to_string(e.old_value) + ":" + + to_string(*e.new_value); + }, + [](typename MapEvent::Remove const& e) -> std::string { return "Remove " + to_string(*e.key); }), + e.v); + return res; + } +}; +} // namespace detail + +/** + * \brief A set allowing its contents to be observed. + * \tparam K type of stored keys (may be abstract) + * \tparam V type of stored values (may be abstract) + */ +template +class IViewableMap : public IViewable>, public ISource> +{ +protected: + using WK = value_or_wrapper; + using WV = value_or_wrapper; + using OV = opt_or_wrapper; + + mutable rd::unordered_map, wrapper::TransparentKeyEqual>> + lifetimes; + +public: + /** + * \brief Represents an addition, update or removal of an element in the map. + */ + using Event = typename detail::MapEvent; + + // region ctor/dtor + + IViewableMap() = default; + + IViewableMap(IViewableMap&&) = default; + + IViewableMap& operator=(IViewableMap&&) = default; + + virtual ~IViewableMap() = default; + // endregion + + void view(Lifetime lifetime, std::function const&) + + > + handler) const override + { + advise_add_remove(lifetime, [this, lifetime, handler](AddRemove kind, K const& key, V const& value) { + const std::pair entry = std::make_pair(&key, &value); + switch (kind) + { + case AddRemove::ADD: + { + if (lifetimes[lifetime].count(key) == 0) + { + /*auto const &[it, inserted] = lifetimes[lifetime].emplace(key, LifetimeDefinition(lifetime));*/ + auto const& pair = lifetimes[lifetime].emplace(&key, LifetimeDefinition(lifetime)); + auto& it = pair.first; + auto& inserted = pair.second; + RD_ASSERT_MSG(inserted, "lifetime definition already exists in viewable map by key:" + to_string(key)); + handler(it->second.lifetime, entry); + } + break; + } + case AddRemove::REMOVE: + { + RD_ASSERT_MSG(lifetimes.at(lifetime).count(key) > 0, + "attempting to remove non-existing lifetime in viewable map by key:" + to_string(key)); + LifetimeDefinition def = std::move(lifetimes.at(lifetime).at(key)); + lifetimes.at(lifetime).erase(key); + def.terminate(); + break; + } + } + }); + } + + /** + * \brief Adds a subscription to additions and removals of map elements. When a map element is updated, the [handler] + * is called twice: to report the removal of the old element and the addition of the new one. + * \param lifetime lifetime of subscription. + * \param handler to be called. + */ + void advise_add_remove(Lifetime lifetime, std::function + handler) const + { + advise(lifetime, [handler](Event e) { + visit(util::make_visitor([handler](typename Event::Add const& e) { handler(AddRemove::ADD, *e.key, *e.new_value); }, + [handler](typename Event::Update const& e) { + handler(AddRemove::REMOVE, *e.key, *e.old_value); + handler(AddRemove::ADD, *e.key, *e.new_value); + }, + [handler](typename Event::Remove const& e) { handler(AddRemove::REMOVE, *e.key, *e.old_value); }), + e.v); + }); + } + + /** + * \brief Adds a subscription to changes of the contents of the map, with the handler receiving keys and values + * as separate parameters. + * + * \details When [handler] is initially added, it is called receiving all keys and values currently in the map. + * Every time a key/value pair is added to the map, the [handler] is called receiving the new key and value. + * The [Lifetime] instance passed to the handler expires when the key/value pair is removed from the map. + * + * \param lifetime lifetime of subscription. + * \param handler to be called. + */ + void view(Lifetime lifetime, std::function + handler) const + { + view(lifetime, + [handler](Lifetime lf, const std::pair entry) { handler(lf, *entry.first, *entry.second); }); + } + + void advise(Lifetime lifetime, std::function handler) const override = 0; + + virtual const V* get(K const&) const = 0; + + virtual const V* set(WK, WV) const = 0; + + virtual OV remove(K const&) const = 0; + + virtual void clear() const = 0; + + virtual size_t size() const = 0; + + virtual bool empty() const = 0; + + template + const V* emplace_set(WK wk, Args&&... args) const + { + return set(std::move(wk), WV{std::forward(args)...}); + } +}; +} // namespace rd + +static_assert(std::is_move_constructible::Event>::value, + "Is move constructible from IViewableMap::Event"); + +#endif // RD_CPP_IVIEWABLEMAP_H diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/IViewableSet.h b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/IViewableSet.h new file mode 100644 index 00000000..d7ffb2dd --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/IViewableSet.h @@ -0,0 +1,148 @@ +#ifndef RD_CPP_IVIEWABLESET_H +#define RD_CPP_IVIEWABLESET_H + +#include "interfaces.h" +#include "viewable_collections.h" + +#include +#include + +#include + +#include + +namespace rd +{ +namespace detail +{ +template +class SetEvent +{ +public: + SetEvent(AddRemove kind, T const* value) : kind(kind), value(value) + { + } + + AddRemove kind; + T const* value; + + friend std::string to_string(SetEvent const& e) + { + return to_string(e.kind) + ":" + to_string(*e.value); + } +}; +} // namespace detail + +/** + * \brief A set allowing its contents to be observed. + * \tparam T type of stored values (may be abstract) + */ +template > +class IViewableSet : public IViewable, public ISource> +{ +protected: + using WT = value_or_wrapper; + mutable rd::unordered_map, wrapper::TransparentKeyEqual>> + lifetimes; + +public: + // region ctor/dtor + + IViewableSet() = default; + + IViewableSet(IViewableSet&&) = default; + + IViewableSet& operator=(IViewableSet&&) = default; + + virtual ~IViewableSet() = default; + // endregion + + /** + * \brief Represents an addition or removal of an element in the set. + */ + using Event = typename detail::SetEvent; + + /** + * \brief Adds a subscription for additions and removals of set elements. When the subscription is initially + * added, [handler] is called with [AddRemove::Add] events for all elements currently in the set. + * + * \param lifetime lifetime of subscription. + * \param handler to be called. + */ + void advise(Lifetime lifetime, std::function handler) const + { + this->advise(lifetime, [handler](Event e) { handler(e.kind, *e.value); }); + } + + /** + * \brief Adds a subscription to changes of the contents of the set. + * + * \details When [handler] is initially added, it is called receiving all elements currently in the set. + * Every time an object is added to the set, the [handler] is called receiving the new element. + * The [Lifetime] instance passed to the handler expires when the element is removed from the set. + * + * \param lifetime + * \param handler + */ + void view(Lifetime lifetime, std::function handler) const override + { + advise(lifetime, [this, lifetime, handler](AddRemove kind, T const& key) { + switch (kind) + { + case AddRemove::ADD: + { + /*auto const &[it, inserted] = lifetimes[lifetime].emplace(key, LifetimeDefinition(lifetime));*/ + auto const& it = lifetimes[lifetime].emplace(&key, lifetime); + RD_ASSERT_MSG(it.second, "lifetime definition already exists in viewable set by key:" + to_string(key)); + handler(it.first->second.lifetime, key); + break; + } + case AddRemove::REMOVE: + { + RD_ASSERT_MSG(lifetimes.at(lifetime).count(key) > 0, + "attempting to remove non-existing lifetime in viewable set by key:" + to_string(key)); + LifetimeDefinition def = std::move(lifetimes.at(lifetime).at(key)); + lifetimes.at(lifetime).erase(key); + def.terminate(); + break; + } + } + }); + } + + /** + * \brief Adds a subscription for additions and removals of set elements. When the subscription is initially + * added, [handler] is called with [AddRemove.Add] events for all elements currently in the set. + * + * \param lifetime lifetime of subscription. + * \param handler to be called. + */ + void advise(Lifetime lifetime, std::function handler) const override = 0; + + virtual bool add(WT) const = 0; + + virtual bool addAll(std::vector elements) const = 0; + + virtual void clear() const = 0; + + virtual bool remove(T const&) const = 0; + + virtual size_t size() const = 0; + + virtual bool contains(T const&) const = 0; + + virtual bool empty() const = 0; + + template + bool emplace_add(Args&&... args) const + { + return add(WT{std::forward(args)...}); + } +}; +} // namespace rd + +static_assert( + std::is_move_constructible::Event>::value, "Is move constructible from IViewableSet::Event"); + +#endif // RD_CPP_IVIEWABLESET_H diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/SignalCookie.cpp b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/SignalCookie.cpp new file mode 100644 index 00000000..5aa1d78d --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/SignalCookie.cpp @@ -0,0 +1,23 @@ +#include "SignalCookie.h" + +#include + +namespace +{ +std::atomic cookie; +} + +void rd_signal_cookie_inc() +{ + ++cookie; +} + +void rd_signal_cookie_dec() +{ + --cookie; +} + +int32_t rd_signal_cookie_get() +{ + return cookie; +} diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/SignalCookie.h b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/SignalCookie.h new file mode 100644 index 00000000..b2659b77 --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/SignalCookie.h @@ -0,0 +1,11 @@ +#ifndef RD_CPP_SIGNALCOOKIE_H +#define RD_CPP_SIGNALCOOKIE_H + +#include +#include + +extern "C" void RD_CORE_API rd_signal_cookie_inc(); +extern "C" void RD_CORE_API rd_signal_cookie_dec(); +extern "C" int32_t RD_CORE_API rd_signal_cookie_get(); + +#endif // RD_CPP_SIGNALCOOKIE_H diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/SignalX.h b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/SignalX.h new file mode 100644 index 00000000..598cc609 --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/SignalX.h @@ -0,0 +1,138 @@ +#ifndef RD_CPP_CORE_SIGNAL_H +#define RD_CPP_CORE_SIGNAL_H + +#include "interfaces.h" +#include "SignalCookie.h" + +#include +#include + +#include +#include +#include + +namespace rd +{ +/** + * \brief complete class which has \a Signal 's properties + */ +template +class Signal final : public ISignal +{ +private: + using WT = typename ISignal::WT; + + class Event + { + private: + std::function action; + Lifetime lifetime; + + public: + // region ctor/dtor + Event() = delete; + + template + Event(F&& action, Lifetime lifetime) : action(std::forward(action)), lifetime(lifetime) + { + } + + Event(Event&&) = default; + // endregion + + bool is_alive() const + { + return !lifetime->is_terminated(); + } + + void execute_if_alive(T const& value) const + { + if (is_alive()) + { + action(value); + } + } + }; + + using counter_t = int32_t; + using listeners_t = std::map; + + mutable counter_t advise_id = 0; + mutable listeners_t listeners, priority_listeners; + + static void cleanup(listeners_t& queue) + { + util::erase_if(queue, [](Event const& e) -> bool { return !e.is_alive(); }); + } + + void fire_impl(T const& value, listeners_t& queue) const + { + for (auto const& p : queue) + { + auto const& event = p.second; + event.execute_if_alive(value); + } + cleanup(queue); + } + + template + void advise0(const Lifetime& lifetime, F&& handler, listeners_t& queue) const + { + if (lifetime->is_terminated()) + return; + counter_t id = advise_id /*.load()*/; + queue.emplace(id, Event(std::forward(handler), lifetime)); + ++advise_id; + } + +public: + // region ctor/dtor + + Signal() = default; + + Signal(Signal const& other) = delete; + + Signal& operator=(Signal const& other) = delete; + + Signal(Signal&&) = default; + + Signal& operator=(Signal&&) = default; + + virtual ~Signal() = default; + + // endregion + + using ISignal::fire; + + void fire(T const& value) const override + { + fire_impl(value, priority_listeners); + fire_impl(value, listeners); + } + + using ISignal::advise; + + void advise(Lifetime lifetime, std::function handler) const override + { + advise0(lifetime, std::move(handler), isPriorityAdvise() ? priority_listeners : listeners); + } + + static bool isPriorityAdvise() + { + return rd_signal_cookie_get() > 0; + } +}; + +template +void priorityAdviseSection(F&& block) +{ + rd_signal_cookie_inc(); + block(); + rd_signal_cookie_dec(); +} +} // namespace rd + +static_assert(std::is_move_constructible>::value, "Is not move constructible from Signal"); +static_assert(std::is_move_constructible>::value, "Is not move constructible from Signal"); + +#endif // RD_CPP_CORE_SIGNAL_H diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/interfaces.h b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/interfaces.h new file mode 100644 index 00000000..6588b340 --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/interfaces.h @@ -0,0 +1,90 @@ +#ifndef RD_CPP_CORE_INTERFACES_H +#define RD_CPP_CORE_INTERFACES_H + +#include +#include +#include + +#include +#include + +namespace rd +{ +/** + * \brief An object that allows to subscribe to events. + * \tparam T type of events + */ +template +class ISource +{ +public: + virtual ~ISource() = default; + + /** + * \brief Adds an event subscription. + * \param lifetime lifetime of subscription. + * \param handler to be called, every time an event occurs. + */ + virtual void advise(Lifetime lifetime, std::function handler) const = 0; + + /** + * \brief @code advise with Eternal lifetime + */ + template + void advise_eternal(F&& handler) const + { + advise(Lifetime::Eternal(), std::forward(handler)); + } + + /** + * \brief @code Void specialisation of @code advise method, at @tparam T=Void + */ + void advise(Lifetime lifetime, std::function handler) const + { + advise(lifetime, [handler = std::move(handler)](Void) { handler(); }); + } +}; + +/** + * \brief An object that allows to subscribe to changes of its contents. + * \tparam T type of content + */ +template +class IViewable +{ +public: + virtual ~IViewable() = default; + + virtual void view(Lifetime lifetime, std::function + handler) const = 0; +}; + +/** + * \brief An object which has a collection of event listeners and can broadcast an event to the listeners. + * \tparam T type of events + */ +template +class ISignal : public ISource +{ +protected: + using WT = value_or_wrapper; + +public: + virtual ~ISignal() = default; + + virtual void fire(T const& value) const = 0; + + /** + * \brief @code fire specialisation at T=Void + */ + template + typename std::enable_if_t> fire() const + { + fire(Void{}); + } +}; +} // namespace rd + +#endif // RD_CPP_CORE_INTERFACES_H diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/viewable_collections.h b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/viewable_collections.h new file mode 100644 index 00000000..bd900087 --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/reactive/base/viewable_collections.h @@ -0,0 +1,53 @@ +#ifndef RD_CPP_VIEWABLE_COLLECTIONS_H +#define RD_CPP_VIEWABLE_COLLECTIONS_H + +#include + +namespace rd +{ +enum class AddRemove +{ + ADD, + REMOVE +}; + +inline std::string to_string(AddRemove kind) +{ + switch (kind) + { + case AddRemove::ADD: + return "Add"; + case AddRemove::REMOVE: + return "Remove"; + default: + return ""; + } +} + +enum class Op +{ + ADD, + UPDATE, + REMOVE, + ACK +}; + +inline std::string to_string(Op op) +{ + switch (op) + { + case Op::ADD: + return "Add"; + case Op::UPDATE: + return "Update"; + case Op::REMOVE: + return "Remove"; + case Op::ACK: + return "Ack"; + default: + return ""; + } +} +} // namespace rd + +#endif // RD_CPP_VIEWABLE_COLLECTIONS_H diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/std/allocator.h b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/std/allocator.h new file mode 100644 index 00000000..b8a7227a --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/std/allocator.h @@ -0,0 +1,12 @@ +#ifndef RD_CPP_ALLOCATOR_H +#define RD_CPP_ALLOCATOR_H + +#include + +namespace rd +{ +template +using allocator = std::allocator; +} + +#endif // RD_CPP_ALLOCATOR_H diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/std/hash.h b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/std/hash.h new file mode 100644 index 00000000..fce90dff --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/std/hash.h @@ -0,0 +1,19 @@ +#ifndef RD_CPP_HASH_H +#define RD_CPP_HASH_H + +#include +#include + +namespace rd +{ +template +struct hash +{ + size_t operator()(const T& value) const noexcept + { + return std::hash()(value); + } +}; +} // namespace rd + +#endif // RD_CPP_HASH_H diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/std/list.h b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/std/list.h new file mode 100644 index 00000000..dd6ecc81 --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/std/list.h @@ -0,0 +1,35 @@ +#ifndef RD_CPP_LIST_H +#define RD_CPP_LIST_H + +#include +#include + +namespace rd +{ +template +int32_t size(T const& value) = delete; + +// c++17 has std::size for std::vector +#if __cplusplus < 201703L + +template +int32_t size(std::vector const& value) +{ + return static_cast(value.size()); +} +#else +template +int32_t size(std::vector const& value) +{ + return std::size(value); +} +#endif + +template +void resize(std::vector& value, int32_t size) +{ + value.resize(size); +} +} // namespace rd + +#endif // RD_CPP_LIST_H diff --git a/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/std/to_string.h b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/std/to_string.h new file mode 100644 index 00000000..91fbab39 --- /dev/null +++ b/Plugins/Developer/RiderLink/Source/RD/src/rd_core_cpp/src/main/std/to_string.h @@ -0,0 +1,147 @@ +// ReSharper disable CppUE4CodingStandardNamingViolationWarning +#ifndef RD_CPP_TO_STRING_H +#define RD_CPP_TO_STRING_H + +#include +#include +#include +#include +#include +#include + +#include "ww898/utf_converters.hpp" + +#include + +namespace rd +{ +namespace detail +{ +using std::to_string; + +inline std::string to_string(std::string const& val) +{ + return val; +} + +inline std::string to_string(const char* val) +{ + return val; +} + +inline std::string to_string(std::wstring const& val) +{ + return ww898::utf::conv(val); +} + +inline std::string to_string(std::thread::id const& id) +{ + std::ostringstream ss; + ss << id; + return ss.str(); +} + +inline std::string to_string(std::exception const& e) +{ + return std::string(e.what()); +} + +inline std::string to_string(std::future_status const& status) +{ + switch (status) + { + case std::future_status::ready: + return "ready"; + case std::future_status::timeout: + return "timeout"; + case std::future_status::deferred: + return "deferred"; + default: + return "unknown"; + } +} + +template +inline std::string to_string(std::chrono::duration const& time) +{ + return std::to_string(std::chrono::duration_cast(time).count()) + "ms"; +} + +template +inline std::string to_string(T const* val) +{ + return val ? to_string(*val) : "nullptr"; +} + +template +inline std::string to_string(std::atomic const& value) +{ + return to_string(value.load()); +} + +template +inline std::string to_string(optional const& val) +{ + if (val.has_value()) + { + return to_string(*val); + } + else + { + return "nullopt"; + } +} + +template +inline std::string to_string(const std::pair p) +{ + return "(" + to_string(p.first) + ", " + to_string(p.second) + ")"; +} + +template