Refactoring to move things from GameMode into UlxEngineWrapper subsystem.

This commit is contained in:
2026-02-27 17:00:46 -05:00
parent 0aea1e0798
commit 7bb4854844
6 changed files with 99 additions and 119 deletions

View File

@@ -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<uint32> AQLenBuffer;
static TArray<const char*> 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

View File

@@ -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<uint32> AQLenBuffer;
TArray<const char*> 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();

View File

@@ -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<UlxLuaValues>(mode);
ReturnArray = NewObject<UlxLuaValues>();
return false;
}
ReturnArray = NewObject<UlxLuaValues>(mode);
FlxLockedWrapper w(mode->GetLockableWrapper());
ReturnArray = NewObject<UlxLuaValues>();
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);

View File

@@ -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<UlxTangibleManager>();
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)
{

View File

@@ -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<FlxSockets> 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;

View File

@@ -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);
}