From 7bb4854844e397617a0180ff39cbdf62eee9e511 Mon Sep 17 00:00:00 2001 From: jyelon Date: Fri, 27 Feb 2026 17:00:46 -0500 Subject: [PATCH] Refactoring to move things from GameMode into UlxEngineWrapper subsystem. --- Source/Integration/LockedWrapper.cpp | 52 ++++++++++++--------- Source/Integration/LockedWrapper.h | 55 +++++++++++++---------- Source/Integration/LuaCall.cpp | 43 +++++++----------- Source/Integration/LuprexGameModeBase.cpp | 28 ++++-------- Source/Integration/LuprexGameModeBase.h | 33 +++----------- Source/Integration/UtilityLibrary.cpp | 7 +-- 6 files changed, 99 insertions(+), 119 deletions(-) diff --git a/Source/Integration/LockedWrapper.cpp b/Source/Integration/LockedWrapper.cpp index a3e73021..2c3b701d 100644 --- a/Source/Integration/LockedWrapper.cpp +++ b/Source/Integration/LockedWrapper.cpp @@ -5,18 +5,13 @@ using namespace LpxCommonTypes; +UlxEngineWrapper* UlxEngineWrapper::Instance = nullptr; -void FlxLockedWrapper::DPrintHook(const char *Msg, size_t Size) -{ - FString FMessage(Size, (const UTF8CHAR *)Msg); - UE_LOG(LogLuprex, Error, TEXT("%s"), *FMessage); -} +void UlxEngineWrapper::Initialize(FSubsystemCollectionBase& Collection) { + Super::Initialize(Collection); + Instance = this; -void FlxLockedWrapper::InitWrapper() { - if (Lockable.Wrapper.play_initialize != nullptr) { - // Already initialized. - return; - } + // Open the DLL and hook up all the function pointers. #if PLATFORM_LINUX FString dll = FPaths::Combine(FPaths::ProjectDir(), TEXT("luprex/build/Linux/luprexlib.so")); #elif PLATFORM_WINDOWS @@ -30,12 +25,24 @@ void FlxLockedWrapper::InitWrapper() { using InitFn = void (*)(EngineWrapper*); InitFn init = (InitFn)FPlatformProcess::GetDllExport(DLL, TEXT("init_engine_wrapper")); if (init != nullptr) { - init(&Lockable.Wrapper); - Lockable.Wrapper.hook_dprint(DPrintHook); + init(&Wrapper); + Wrapper.hook_dprint(DPrintHook); } } } +void UlxEngineWrapper::Deinitialize() { + Instance = nullptr; + Super::Deinitialize(); +} + + +void UlxEngineWrapper::DPrintHook(const char *Msg, size_t Size) +{ + FString FMessage(Size, (const UTF8CHAR *)Msg); + UE_LOG(LogLuprex, Error, TEXT("%s"), *FMessage); +} + FString FlxLockedWrapper::ChannelPrints() { if (Lockable.Wrapper.get_have_prints(Get())) { @@ -62,22 +69,27 @@ IdView FlxLockedWrapper::GetNear(int64 id, double rx, double ry, double rz) { } StringViewVec FlxLockedWrapper::GetAnimationQueues(IdView ids) { + // This is only called from the game thread, so + // these static buffers are safe without locking. + static TArray AQLenBuffer; + static TArray AQStrBuffer; + // How many animation queues are we fetching? int num = ids.Num(); - // Enlarge the static buffers if necessary. + // Enlarge the buffers if necessary. // Add a little extra space so we don't have to enlarge // the buffers every time we get a new tangible. - if (num > Lockable.AQLenBuffer.Num()) { - Lockable.AQLenBuffer.SetNum(num + 1000); - Lockable.AQStrBuffer.SetNum(num + 1000); + if (num > AQLenBuffer.Num()) { + AQLenBuffer.SetNum(num + 1000); + AQStrBuffer.SetNum(num + 1000); } - // Get pointers to the static buffers. - uint32* LenBuf = Lockable.AQLenBuffer.GetData(); - const char** StrBuf = Lockable.AQStrBuffer.GetData(); + // Get pointers to the buffers. + uint32* LenBuf = AQLenBuffer.GetData(); + const char** StrBuf = AQStrBuffer.GetData(); - // Get the animation queues into the static buffers. + // Get the animation queues into the buffers. Lockable.Wrapper.get_animation_queues(Get(), num, (const int64_t *)ids.GetData(), LenBuf, StrBuf); // Transfer data from static buffers into an array of string_view diff --git a/Source/Integration/LockedWrapper.h b/Source/Integration/LockedWrapper.h index ffcc7c76..69e24970 100644 --- a/Source/Integration/LockedWrapper.h +++ b/Source/Integration/LockedWrapper.h @@ -11,29 +11,50 @@ #include "CoreMinimal.h" #include "lpx-enginewrapper.hpp" #include "Common.h" +#include "StreamBuffer.h" +#include "Subsystems/GameInstanceSubsystem.h" +#include "LockedWrapper.generated.h" //////////////////////////////////////////////////////////// // -// FlxLockableWrapper +// UlxEngineWrapper // -// Contains the EngineWrapper and a Mutex to lock it. -// This class has no methods. To access the -// EngineWrapper, construct a FlxLockedWrapper. +// GameInstanceSubsystem that owns the EngineWrapper and +// a Mutex to lock it. To access the EngineWrapper, +// construct a FlxLockedWrapper. // //////////////////////////////////////////////////////////// -class FlxLockableWrapper { +UCLASS() +class INTEGRATION_API UlxEngineWrapper : public UGameInstanceSubsystem { + GENERATED_BODY() + private: + static UlxEngineWrapper* Instance; + + // Called by luprex to output debugging messages. + // A thin wrapper around UE_LOG. + // + static void DPrintHook(const char *Msg, size_t Size); + FCriticalSection Mutex; EngineWrapper Wrapper; - // Temporary buffers used only inside wrapper - // methods. Nothing persistent in these. + // The Lua Call Assembly Buffer. Used to build up + // a call across multiple UFUNCTION invocations. + // Only accessed from the game thread. // - TArray AQLenBuffer; - TArray AQStrBuffer; + FlxStreamBuffer LuaCallBuffer; public: + virtual void Initialize(FSubsystemCollectionBase& Collection) override; + virtual void Deinitialize() override; + + // Get the Lua Call Assembly Buffer. + // Only called from the game thread. + // + static FlxStreamBuffer& GetLuaCallBuffer() { return Instance->LuaCallBuffer; } + friend class FlxLockedWrapper; }; @@ -49,12 +70,7 @@ public: class FlxLockedWrapper { private: - FlxLockableWrapper& Lockable; - - // Called by luprex to output debugging messages. - // A thin wrapper around UE_LOG. - // - static void DPrintHook(const char *Msg, size_t Size); + UlxEngineWrapper& Lockable; public: // Import these types into our namespace. @@ -66,7 +82,7 @@ public: public: // The constructor claims the mutex. // - FlxLockedWrapper(FlxLockableWrapper& w) : Lockable(w) { + FlxLockedWrapper() : Lockable(*UlxEngineWrapper::Instance) { Lockable.Mutex.Lock(); } @@ -90,13 +106,6 @@ public: return &Lockable.Wrapper; } - // Initialize the engine wrapper if it's not - // already. All this does is open the DLL and - // hook up all the function pointers in the - // wrapper to point into the DLL. - // - void InitWrapper(); - // Fetch Stdout as a string. // FString ChannelPrints(); diff --git a/Source/Integration/LuaCall.cpp b/Source/Integration/LuaCall.cpp index 9b96c005..b0daed00 100644 --- a/Source/Integration/LuaCall.cpp +++ b/Source/Integration/LuaCall.cpp @@ -1,6 +1,6 @@ #include "LuaCall.h" -#include "LuprexGameModeBase.h" +#include "LockedWrapper.h" #include "Tangible.h" #include "StreamBuffer.h" @@ -199,8 +199,7 @@ FString UlxLuaCallLibrary::AllFunctionsWithPrefix(const TCHAR *Prefix) void UlxLuaCallLibrary::InvokeLuaExpr(UObject *context, const FString &Code) { - ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); - FlxLockedWrapper w(mode->GetLockableWrapper()); + FlxLockedWrapper w; w.InvokeLuaExpr(Code); } @@ -218,8 +217,7 @@ static int64 ResolvePlaceId(AActor *place) void UlxLuaCallLibrary::LuaCallBegin(UObject *context, const FString &cname, const FString &fname) { - ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); - FlxStreamBuffer &sb = mode->GetLuaCallBuffer(); + FlxStreamBuffer &sb = UlxEngineWrapper::GetLuaCallBuffer(); sb.clear(); sb.write_string(cname); sb.write_string(fname); @@ -227,29 +225,27 @@ void UlxLuaCallLibrary::LuaCallBegin(UObject *context, const FString &cname, con void UlxLuaCallLibrary::LuaCallInvoke(UObject *context, AActor *place) { - ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); - FlxStreamBuffer &sb = mode->GetLuaCallBuffer(); + FlxStreamBuffer &sb = UlxEngineWrapper::GetLuaCallBuffer(); if (NotInitialized(sb)) return; int64 place_id = ResolvePlaceId(place); if (place_id < 0) { sb.clear(); return; } - FlxLockedWrapper w(mode->GetLockableWrapper()); + FlxLockedWrapper w; w.InvokeLuaFunction(sb.view(), place_id); sb.clear(); } bool UlxLuaCallLibrary::LuaCallProbe(UObject *context, AActor *place, UlxLuaValues *&ReturnArray) { - ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); - FlxStreamBuffer &sb = mode->GetLuaCallBuffer(); + FlxStreamBuffer &sb = UlxEngineWrapper::GetLuaCallBuffer(); if (NotInitialized(sb)) return false; int64 place_id = ResolvePlaceId(place); if (place_id < 0) { sb.clear(); - ReturnArray = NewObject(mode); + ReturnArray = NewObject(); return false; } - ReturnArray = NewObject(mode); - FlxLockedWrapper w(mode->GetLockableWrapper()); + ReturnArray = NewObject(); + FlxLockedWrapper w; w.ProbeLuaFunction(sb.view(), place_id, [&](std::string_view retpk) { ReturnArray->Initialize(retpk); }); @@ -285,8 +281,7 @@ bool UlxLuaCallLibrary::LuaCallProbe(UObject *context, AActor *place, UlxLuaValu ///////////////////////////////////////////////////////////////// void UlxLuaCallLibrary::LuaCallArgument_string(UObject *context, const FString &pstring) { - ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); - FlxStreamBuffer &sb = mode->GetLuaCallBuffer(); + FlxStreamBuffer &sb = UlxEngineWrapper::GetLuaCallBuffer(); if (NotInitialized(sb)) return; sb.write_lua_value_type(LuaValueType::STRING); sb.write_string(pstring); @@ -300,40 +295,35 @@ void UlxLuaCallLibrary::LuaCallArgument_name(UObject *context, const FName &pnam { UE_LOG(LogBlueprint, Error, TEXT("Names passed to lua must be short, and must contain only lowercase and digits")); } - ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); - FlxStreamBuffer &sb = mode->GetLuaCallBuffer(); + FlxStreamBuffer &sb = UlxEngineWrapper::GetLuaCallBuffer(); if (NotInitialized(sb)) return; sb.write_lua_value_type(LuaValueType::TOKEN); sb.write_string(namestr); } void UlxLuaCallLibrary::LuaCallArgument_float(UObject *context, double pfloat) { - ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); - FlxStreamBuffer &sb = mode->GetLuaCallBuffer(); + FlxStreamBuffer &sb = UlxEngineWrapper::GetLuaCallBuffer(); if (NotInitialized(sb)) return; sb.write_lua_value_type(LuaValueType::NUMBER); sb.write_double(pfloat); } void UlxLuaCallLibrary::LuaCallArgument_int(UObject *context, int value) { - ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); - FlxStreamBuffer &sb = mode->GetLuaCallBuffer(); + FlxStreamBuffer &sb = UlxEngineWrapper::GetLuaCallBuffer(); if (NotInitialized(sb)) return; sb.write_lua_value_type(LuaValueType::NUMBER); sb.write_double(value); } void UlxLuaCallLibrary::LuaCallArgument_vector(UObject *context, const FVector &pvector) { - ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); - FlxStreamBuffer &sb = mode->GetLuaCallBuffer(); + FlxStreamBuffer &sb = UlxEngineWrapper::GetLuaCallBuffer(); if (NotInitialized(sb)) return; sb.write_lua_value_type(LuaValueType::VECTOR); sb.write_fvector(pvector); } void UlxLuaCallLibrary::LuaCallArgument_vector2d(UObject *context, const FVector2D &pvector) { - ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); - FlxStreamBuffer &sb = mode->GetLuaCallBuffer(); + FlxStreamBuffer &sb = UlxEngineWrapper::GetLuaCallBuffer(); if (NotInitialized(sb)) return; sb.write_lua_value_type(LuaValueType::VECTOR); sb.write_double(pvector.X); @@ -342,8 +332,7 @@ void UlxLuaCallLibrary::LuaCallArgument_vector2d(UObject *context, const FVector } void UlxLuaCallLibrary::LuaCallArgument_boolean(UObject *context, bool pbool) { - ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); - FlxStreamBuffer &sb = mode->GetLuaCallBuffer(); + FlxStreamBuffer &sb = UlxEngineWrapper::GetLuaCallBuffer(); if (NotInitialized(sb)) return; sb.write_lua_value_type(LuaValueType::BOOLEAN); sb.write_bool(pbool); diff --git a/Source/Integration/LuprexGameModeBase.cpp b/Source/Integration/LuprexGameModeBase.cpp index c3e91363..e00026e0 100644 --- a/Source/Integration/LuprexGameModeBase.cpp +++ b/Source/Integration/LuprexGameModeBase.cpp @@ -1,6 +1,7 @@ // Copyright Epic Games, Inc. All Rights Reserved. #include "LuprexGameModeBase.h" +#include "LockedWrapper.h" #include "lpx-drvutil.hpp" #include "Misc/Paths.h" #include "Tangible.h" @@ -20,25 +21,17 @@ using namespace LpxCommonTypes; ALuprexGameModeBase::ALuprexGameModeBase() { - PlayerId = 0; - EngineSeconds = 0.0; - MustCallLookAtChanged = false; - //PrimaryActorTick.bCanEverTick = true; // Probably wrong - //PrimaryActorTick.bTickEvenWhenPaused = true; // Probably wrong - //PrimaryActorTick.TickGroup = TG_PrePhysics; // Probably wrong - ResetToInitialState(); } ALuprexGameModeBase::~ALuprexGameModeBase() { - ResetToInitialState(); } // This method runs in the background thread, // at the moment we trigger it. // uint32 ALuprexGameModeBase::Run() { - FlxLockedWrapper lockedwrap(LockableWrapper); + FlxLockedWrapper lockedwrap; if (lockedwrap->get_rescan_lua_source(lockedwrap.Get())) { drvutil::ostringstream srcpak; @@ -77,7 +70,7 @@ void ALuprexGameModeBase::ResetToInitialState() // Now that the thread's gone, we should be able to // just claim and hold the lock on the wrapper. - FlxLockedWrapper w(LockableWrapper); + FlxLockedWrapper w; // Release and close all sockets. if (Sockets != nullptr) @@ -92,12 +85,12 @@ void ALuprexGameModeBase::ResetToInitialState() w->release(w.Get()); } + // Clear the lua call assembly buffer. + UlxEngineWrapper::GetLuaCallBuffer().clear(); + // Stop trapping log errors to the debugger. BreakToDebuggerLogVerbosityDevice.Reset(); - // Clear the lua call assembly buffer. - LuaCallBuffer.clear(); - // Clear the PlayerID PlayerId = 0; @@ -112,7 +105,7 @@ void ALuprexGameModeBase::ResetToInitialState() void ALuprexGameModeBase::UpdateConsoleOutput() { // Copy Luprex Stdout into the console. - FlxLockedWrapper lockedwrap(LockableWrapper); + FlxLockedWrapper lockedwrap; if (Playing) { FString Text = lockedwrap.ChannelPrints(); if (!Text.IsEmpty()) @@ -130,7 +123,7 @@ void ALuprexGameModeBase::UpdateTangibles() { UlxTangibleManager *TM = GetGameInstance()->GetSubsystem(); TanArray alltans; { - FlxLockedWrapper w(LockableWrapper); + FlxLockedWrapper w; PlayerId = w.GetActor(); IdView nearids = w.GetNear(PlayerId, radius, radius, radius); TM->UpdateNearAccordingToLuprex(nearids); @@ -210,15 +203,12 @@ void ALuprexGameModeBase::BeginPlay() void ALuprexGameModeBase::InitializeGlobalState() { - FlxLockedWrapper w(LockableWrapper); + FlxLockedWrapper w; // Sanity checks. Make sure everything is clean. checkf(!LuprexUpdateTask.IsRunning(), TEXT("There should be no thread here.")); checkf(w->engine == nullptr, TEXT("There should be no engine here.")); - // Try to initialize the wrapper. - w.InitWrapper(); - // If we failed to initialize the wrapper, print an error message. if (w->play_initialize == nullptr) { diff --git a/Source/Integration/LuprexGameModeBase.h b/Source/Integration/LuprexGameModeBase.h index 152005e9..65775848 100644 --- a/Source/Integration/LuprexGameModeBase.h +++ b/Source/Integration/LuprexGameModeBase.h @@ -5,8 +5,6 @@ #include "CoreMinimal.h" #include "Engine/HitResult.h" #include "GameFramework/GameModeBase.h" -#include "lpx-enginewrapper.hpp" -#include "StreamBuffer.h" #include "LuprexSockets.h" #include "TriggeredTask.h" #include "BreakToDebugger.h" @@ -66,13 +64,6 @@ public: void LookAtChanged(); - // The Lua Call Assembly Buffer. Used by - // UlxLuaCallLibrary to build up a call across - // multiple UFUNCTION invocations. - // - FlxStreamBuffer &GetLuaCallBuffer() { return LuaCallBuffer; } - - // Transfer console output from the Luprex engine to unreal. void UpdateConsoleOutput(); @@ -104,24 +95,12 @@ public: UPROPERTY() FHitResult CurrentLookAt; - bool MustCallLookAtChanged; + bool MustCallLookAtChanged = false; // The sensitivity level at which a log message triggers a debugger breakpoint. UPROPERTY(EditAnywhere, Category="Debugging Tools") ElxBreakToDebuggerThreshold BreakToDebuggerLogVerbosity; - // The Luprex EngineWrapper, with a Mutex to protect it. - // To access it, construct a FlxLockedWrapper. - // - FlxLockableWrapper LockableWrapper; - - // Get the LockableWrapper. - // - FlxLockableWrapper& GetLockableWrapper() { return LockableWrapper; } - - // The Lua Call Assembly Buffer. - FlxStreamBuffer LuaCallBuffer; - // This utility runs the luprex update and socket update in a thread. FTriggeredTask LuprexUpdateTask; @@ -129,19 +108,19 @@ public: TUniquePtr Sockets; // True if 'BeginPlay' has been successfully completed. - bool Playing; + bool Playing = false; // This is always true unless you use the debugger to set it to false. - bool TickEnabled; + bool TickEnabled = true; // Current Player ID - int64 PlayerId; + int64 PlayerId = 0; // Amount of elapsed time since BeginPlay. - float EngineSeconds; + float EngineSeconds = 0.0f; // When do we next rotate the cube. - float NextRotateCube; + float NextRotateCube = 1.0f; // These allow us to pre-tick and post-tick. FDelegateHandle OnWorldPreActorTickHandle; diff --git a/Source/Integration/UtilityLibrary.cpp b/Source/Integration/UtilityLibrary.cpp index d2ae3e27..f9fcf8a2 100644 --- a/Source/Integration/UtilityLibrary.cpp +++ b/Source/Integration/UtilityLibrary.cpp @@ -2,7 +2,8 @@ #include "UtilityLibrary.h" -#include "LuprexGameModeBase.h" +#include "LockedWrapper.h" +#include "Engine/GameViewportClient.h" #include "GameFramework/PlayerController.h" #include "EnhancedInputSubsystems.h" #include "Kismet/KismetSystemLibrary.h" @@ -12,6 +13,7 @@ #include "InputMappingContext.h" #include "EnhancedInputComponent.h" #include "Animation/AnimSequenceBase.h" +#include "GameFramework/Pawn.h" #define LOCTEXT_NAMESPACE "Luprex Utility" @@ -256,7 +258,6 @@ FVector UlxUtilityLibrary::GetActorForwardVelocity(const AActor *Actor, double S void UlxUtilityLibrary::ValidateLuaExpr( ElxLuaSyntaxCheck &Status, FString &ErrorMessage, UObject *context, const FString &Code) { - ALuprexGameModeBase *mode = ALuprexGameModeBase::FromContext(context); - FlxLockedWrapper w(mode->GetLockableWrapper()); + FlxLockedWrapper w; Status = w.ValidateLuaExpr(Code, ErrorMessage); }