Skip to content

Commit 9846f84

Browse files
authored
feat: unreal specific ecsact plugin + starting on ecsact runner (#11)
depends on ecsact-dev/ecsact_cli#120 depends on ecsact-dev/ecsact_codegen#53
1 parent b730f14 commit 9846f84

28 files changed

+1025
-58
lines changed

.clang-tidy

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
---
2+
Checks: 'clang-diagnostic-*,clang-analyzer-*,cppcoreguidelines-*,modernize-*,-modernize-use-trailing-return-type'

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
*.png filter=lfs diff=lfs merge=lfs -text
2+
*.dll filter=lfs diff=lfs merge=lfs -text

.github/workflows/main.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,5 @@ jobs:
1717
runs-on: ubuntu-latest
1818
steps:
1919
- uses: actions/checkout@v4
20-
- uses: greut/eclint-action@v0
2120
- uses: jidicula/clang-format-action@v4.11.0
2221
with: { clang-format-version: "18" }

Config/FilterPlugin.ini

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[FilterPlugin]
2+
; This section lists additional files which will be packaged along with your plugin. Paths should be listed relative to the root plugin directory, and
3+
; may include "...", "*", and "?" wildcards to match directories, files, and individual characters respectively.
4+
;
5+
; Examples:
6+
; /README.txt
7+
; /Extras/...
8+
; /Binaries/ThirdParty/*.dll

Ecsact.uplugin

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@
2525
"Name": "EcsactEditor",
2626
"Type": "Editor",
2727
"LoadingPhase": "Default"
28+
},
29+
{
30+
"Name": "EcsactUnrealCodegenPlugin",
31+
"Type": "Editor",
32+
"LoadingPhase": "None"
2833
}
2934
]
3035
}

Source/Ecsact/Ecsact.Build.cs

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@
22
using System.IO;
33
using System.Diagnostics;
44
using System;
5-
using System.Linq;
65
using System.Collections.Generic;
7-
using UnrealBuildBase;
86
using EpicGames.Core;
97

108
[Serializable]
@@ -33,13 +31,29 @@ public Ecsact(ReadOnlyTargetRules Target) : base(Target) {
3331
"SlateCore",
3432
});
3533

34+
DynamicallyLoadedModuleNames.AddRange(new string[] {
35+
"EcsactUnrealCodegenPlugin",
36+
});
37+
38+
var EcsactUnrealCodegenPluginPath = Path.Combine(
39+
ModuleDirectory,
40+
"..",
41+
"..",
42+
"Binaries",
43+
Target.Platform.ToString(),
44+
"UnrealEditor-EcsactUnrealCodegenPlugin.dll"
45+
);
46+
3647
if(Target.bBuildEditor) {
3748
PrivateDependencyModuleNames.Add("UnrealEd");
3849
}
3950

4051
var EcsactSdkVersion = GetEcsactSdkVersion();
41-
Environment.SetEnvironmentVariable("EcsactPlugin_SdkVersion", EcsactSdkVersion);
42-
52+
Environment.SetEnvironmentVariable(
53+
"EcsactPlugin_SdkVersion",
54+
EcsactSdkVersion
55+
);
56+
4357
var EcsactSdkIncludeDir = GetEcsactSdkIncludeDir();
4458
PublicIncludePaths.Add(EcsactSdkIncludeDir);
4559

@@ -64,6 +78,16 @@ public Ecsact(ReadOnlyTargetRules Target) : base(Target) {
6478
"--plugin=cpp_systems_header",
6579
"--plugin=cpp_systems_source"
6680
};
81+
82+
// if(!File.Exists(EcsactUnrealCodegenPluginPath)) {
83+
// Console.WriteLine(
84+
// "warning: EcsactUnrealCodegenPlugin was not built. It should have "
85+
// + "been shipped with the Ecsact Unreal integration plugin."
86+
// );
87+
// } else {
88+
// CodegenArgs.Add($"--plugin={EcsactUnrealCodegenPluginPath}");
89+
// }
90+
6791
CodegenArgs.AddRange(EcsactSources);
6892
ExecEcsactCli(CodegenArgs);
6993
}
@@ -158,14 +182,16 @@ private void ExecEcsactCli(IList<string> Args) {
158182
}
159183

160184
private DirectoryReference GetProjectRoot(ReadOnlyTargetRules Target) {
161-
if (Target.ProjectFile != null) {
185+
if(Target.ProjectFile != null) {
162186
return Target.ProjectFile.Directory;
163187
}
164188

165-
// Without a Target.ProjectFile we're probably installed as an Engine plugin.
166-
// Only information we have about the project is the current directory.
189+
// Without a Target.ProjectFile we're probably installed as an Engine
190+
// plugin. Only information we have about the project is the current
191+
// directory.
167192
var Root = new DirectoryReference(Directory.GetCurrentDirectory());
168-
while (Root != null && !File.Exists(Path.Combine(Root.FullName, "*.uproject"))) {
193+
while(Root != null &&
194+
!File.Exists(Path.Combine(Root.FullName, "*.uproject"))) {
169195
Root = Root.ParentDirectory;
170196
}
171197
return Root;

Source/Ecsact/Private/Ecsact.cpp

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
#include "Ecsact.h"
22
#include "CoreGlobals.h"
3+
#include "EcsactSettings.h"
34
#include "HAL/PlatformProcess.h"
5+
#include "Logging/LogVerbosity.h"
46
#include "Misc/Paths.h"
7+
#include "EcsactAsyncRunner.h"
58
#include "ecsact/runtime.h"
69

710
#define LOCTEXT_NAMESPACE "FEcsactModule"
@@ -12,6 +15,13 @@ DEFINE_LOG_CATEGORY(Ecsact);
1215
FOR_EACH_ECSACT_API_FN(INIT_ECSACT_API_FN, UNUSED_PARAM);
1316
#undef INIT_ECSACT_API_FN
1417

18+
FEcsactModule* FEcsactModule::Self = nullptr;
19+
20+
auto FEcsactModule::Get() -> FEcsactModule& {
21+
check(Self != nullptr);
22+
return *Self;
23+
}
24+
1525
auto FEcsactModule::Abort() -> void {
1626
#ifdef WITH_EDITOR
1727
if(GEditor) {
@@ -26,6 +36,7 @@ auto FEcsactModule::LoadEcsactRuntime() -> void {
2636
FPaths::ProjectDir(),
2737
TEXT("Binaries/Win64/EcsactRuntime.dll")
2838
);
39+
UE_LOG(Ecsact, Log, TEXT("Loading ecsact runtime %s"), *ecsact_runtime_path);
2940

3041
EcsactRuntimeHandle = FPlatformProcess::GetDllHandle(*ecsact_runtime_path);
3142

@@ -43,12 +54,22 @@ auto FEcsactModule::LoadEcsactRuntime() -> void {
4354
#define LOAD_ECSACT_FN(fn, UNUSED_PARAM) \
4455
fn = reinterpret_cast<decltype(fn)>( \
4556
FPlatformProcess::GetDllExport(EcsactRuntimeHandle, TEXT(#fn)) \
46-
)
57+
); \
58+
if(fn != nullptr) { \
59+
UE_LOG(Ecsact, Log, TEXT("loaded %s"), TEXT(#fn)); \
60+
} else { \
61+
UE_LOG(Ecsact, Error, TEXT("failed to load %s"), TEXT(#fn)); \
62+
} \
63+
static_assert(true, "require ;")
4764
FOR_EACH_ECSACT_API_FN(LOAD_ECSACT_FN);
4865
#undef LOAD_ECSACT_FN
66+
StartRunner();
4967
}
5068

5169
auto FEcsactModule::UnloadEcsactRuntime() -> void {
70+
UE_LOG(Ecsact, Log, TEXT("Unloading ecsact runtime"));
71+
72+
StopRunner();
5273
if(EcsactRuntimeHandle) {
5374
FPlatformProcess::FreeDllHandle(EcsactRuntimeHandle);
5475
EcsactRuntimeHandle = nullptr;
@@ -60,6 +81,7 @@ auto FEcsactModule::UnloadEcsactRuntime() -> void {
6081
}
6182

6283
auto FEcsactModule::StartupModule() -> void {
84+
Self = this;
6385
if(!GIsEditor) {
6486
LoadEcsactRuntime();
6587
}
@@ -78,6 +100,7 @@ auto FEcsactModule::ShutdownModule() -> void {
78100
FEditorDelegates::PreBeginPIE.RemoveAll(this);
79101
FEditorDelegates::EndPIE.RemoveAll(this);
80102
#endif
103+
Self = nullptr;
81104
}
82105

83106
auto FEcsactModule::OnPreBeginPIE(bool _) -> void {
@@ -88,6 +111,56 @@ auto FEcsactModule::OnEndPIE(bool _) -> void {
88111
UnloadEcsactRuntime();
89112
}
90113

114+
auto FEcsactModule::StartRunner() -> void {
115+
const auto* settings = GetDefault<UEcsactSettings>();
116+
117+
if(Runner != nullptr) {
118+
UE_LOG(
119+
Ecsact,
120+
Warning,
121+
TEXT("StartRunner() was called while runner was already running. "
122+
"Stopping previous one before starting new.")
123+
);
124+
StopRunner();
125+
}
126+
127+
switch(settings->Runner) {
128+
case EEcsactRuntimeRunnerType::Automatic:
129+
if(ecsact_async_flush_events == nullptr) {
130+
Runner = NewObject<UEcsactSyncRunner>();
131+
} else {
132+
Runner = NewObject<UEcsactAsyncRunner>();
133+
}
134+
break;
135+
case EEcsactRuntimeRunnerType::Asynchronous:
136+
Runner = NewObject<UEcsactAsyncRunner>();
137+
break;
138+
case EEcsactRuntimeRunnerType::Custom:
139+
if(settings->CustomRunnerClass != nullptr) {
140+
Runner = NewObject<UEcsactRunner>(nullptr, settings->CustomRunnerClass);
141+
}
142+
break;
143+
}
144+
145+
if(Runner != nullptr) {
146+
UE_LOG(
147+
Ecsact,
148+
Verbose,
149+
TEXT("Using ecsact runner: %s"),
150+
*Runner->StaticClass()->GetName()
151+
);
152+
Runner->AddToRoot();
153+
}
154+
}
155+
156+
auto FEcsactModule::StopRunner() -> void {
157+
if(Runner != nullptr) {
158+
Runner->RemoveFromRoot();
159+
Runner->ConditionalBeginDestroy();
160+
Runner = nullptr;
161+
}
162+
}
163+
91164
#undef LOCTEXT_NAMESPACE
92165

93166
IMPLEMENT_MODULE(FEcsactModule, Ecsact)

Source/Ecsact/Public/Ecsact.h

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,24 @@
66
DECLARE_LOG_CATEGORY_EXTERN(Ecsact, Log, All);
77

88
class FEcsactModule : public IModuleInterface {
9-
void* EcsactRuntimeHandle;
10-
auto LoadEcsactRuntime() -> void;
11-
auto UnloadEcsactRuntime() -> void;
12-
auto Abort() -> void;
13-
auto OnPreBeginPIE(bool bIsSimulating) -> void;
14-
auto OnEndPIE(const bool bIsSimulating) -> void;
9+
friend class EcsactUnrealExecution;
10+
11+
static FEcsactModule* Self;
12+
void* EcsactRuntimeHandle;
13+
class UEcsactRunner* Runner;
14+
15+
auto LoadEcsactRuntime() -> void;
16+
auto UnloadEcsactRuntime() -> void;
17+
auto Abort() -> void;
18+
auto OnPreBeginPIE(bool bIsSimulating) -> void;
19+
auto OnEndPIE(const bool bIsSimulating) -> void;
20+
21+
auto StartRunner() -> void;
22+
auto StopRunner() -> void;
1523

1624
public:
25+
static auto Get() -> FEcsactModule&;
26+
1727
auto StartupModule() -> void override;
1828
auto ShutdownModule() -> void override;
1929
};
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#include "EcsactAsyncRunner.h"
2+
#include "Ecsact.h"
3+
#include "EcsactUnrealEventsCollector.h"
4+
#include "ecsact/runtime/async.h"
5+
#include "ecsact/runtime/common.h"
6+
7+
auto UEcsactAsyncRunner::Tick(float DeltaTime) -> void {
8+
if(ecsact_async_flush_events == nullptr) {
9+
UE_LOG(Ecsact, Error, TEXT("ecsact_async_flush_events is unavailable"));
10+
} else {
11+
ecsact_execution_events_collector* evc_c = nullptr;
12+
if(EventsCollector != nullptr) {
13+
evc_c = EventsCollector->GetCEVC();
14+
}
15+
ecsact_async_flush_events(evc_c, nullptr);
16+
}
17+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
2+
#pragma once
3+
4+
#include "CoreMinimal.h"
5+
#include "Tickable.h"
6+
#include "UObject/NoExportTypes.h"
7+
#include "EcsactRunner.h"
8+
#include "EcsactAsyncRunner.generated.h"
9+
10+
UCLASS(NotBlueprintable)
11+
12+
class UEcsactAsyncRunner : public UEcsactRunner {
13+
GENERATED_BODY()
14+
public:
15+
UPROPERTY()
16+
class UEcsactUnrealEventsCollector* EventsCollector;
17+
18+
auto Tick(float DeltaTime) -> void override;
19+
};

0 commit comments

Comments
 (0)