// Copyright Epic Games, Inc. All Rights Reserved. #include "IntegrationGameModeBase.h" #include "drvutil.hpp" #include "DebugPrint.h" #include "TangibleManager.h" #include #include using namespace DebugPrint; AIntegrationGameModeBase::AIntegrationGameModeBase() { EngineSeconds = 0.0; NextThreadTrigger = 1.0; //PrimaryActorTick.bCanEverTick = true; // Probably wrong //PrimaryActorTick.bTickEvenWhenPaused = true; // Probably wrong //PrimaryActorTick.TickGroup = TG_PrePhysics; // Probably wrong SetActorTickEnabled(true); SetActorTickInterval(0.0f); DebugPrintControl::EnableCollection(); ResetToInitialState(); } AIntegrationGameModeBase::~AIntegrationGameModeBase() { ResetToInitialState(); } // This method runs in the background thread, // at the moment we trigger it. // uint32 AIntegrationGameModeBase::Run() { FLockedWrapper lockedwrap(LockableWrapper); Sockets->Update(lockedwrap); lockedwrap->play_invoke_event_update(lockedwrap.Get(), EngineSeconds); Sockets->Update(lockedwrap); return 0; } void AIntegrationGameModeBase::ResetToInitialState() { Playing = false; // Shut down the thread LuprexUpdateTask.Shutdown(); // Now that the thread's gone, we should be able to // just claim and hold the lock on the wrapper. FLockedWrapper w(LockableWrapper); // Release and close all sockets. if (Sockets != nullptr) { Sockets->ForceCloseEverything(w); Sockets.Reset(); } // Delete the engine. if (w->release != nullptr) { w->release(w.Get()); } // Reset the clocks. EngineSeconds = 0; NextThreadTrigger = 1.0; } void AIntegrationGameModeBase::UpdateConsoleOutput() { // Copy Luprex Stdout into the console. FLockedWrapper lockedwrap(LockableWrapper); if (Playing) { ConsoleOutput.Append(lockedwrap.FetchStdout()); } // Copy Debugging Prints into the console. TArray prints = DebugPrintControl::GetStored(); for (const FString& fs : prints) { ConsoleOutput.AppendLine(fs); } // If the Console text has changed, update the widget. if (ConsoleOutput.IsDirty()) { ConsoleSetOutput(ConsoleOutput.Get()); ConsoleOutput.ClearDirty(); } } void AIntegrationGameModeBase::MaybeTriggerUpdateTask(float deltaseconds) { if (!Playing) return; FLockedWrapper lockedwrap(LockableWrapper); if (lockedwrap->engine != nullptr) { EngineSeconds += deltaseconds; if (EngineSeconds >= NextThreadTrigger) { LuprexUpdateTask.Trigger(); NextThreadTrigger += 0.05; } } } void AIntegrationGameModeBase::UpdateTangibles() { if (!Playing) return; FLockedWrapper w(LockableWrapper); int64 actor = w.GetActor(); TangibleManager.SetActor(actor); TangibleManager.SetNear(w.GetNear(actor, 100, 100, 100)); for (int64 id : TangibleManager.GetNear()) { TangibleManager.MakeTangible(id); } } void AIntegrationGameModeBase::ConsoleSendInput(const FString& fs) { FLockedWrapper w(LockableWrapper); if (w->engine != nullptr) { const TCHAR* fstchar = *fs; if (sizeof(TCHAR) == 2) { ConsoleOutput.AppendLine(FString("> ") + fs); std::u16string_view fsview((const char16_t*)fstchar, fs.Len()); std::string utf8 = drvutil::utf16_to_utf8(fsview); utf8 = utf8 + "\n"; w->play_recv_incoming(w.Get(), 0, utf8.size(), utf8.c_str()); } } } void AIntegrationGameModeBase::Tick(float deltaseconds) { Super::Tick(deltaseconds); UpdateConsoleOutput(); UpdateTangibles(); MaybeTriggerUpdateTask(deltaseconds); } void AIntegrationGameModeBase::BeginPlay() { Super::BeginPlay(); // Make sure we're starting from a clean slate. // Note: this claims the wrapper lock, so don't claim // the lock before calling this. ResetToInitialState(); // Now we're just going to claim the wrapper // lock for the remainder. When we create the thread, // the thread will hang until we release this lock. FLockedWrapper w(LockableWrapper); // 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) { DPrint("Luprex wrapper initialization failed"); } // If wrapper is initialized, try to initialize the luprex engine. if (w->play_initialize != nullptr) { drvutil::ostringstream srcpak; std::string srcpakerr = drvutil::package_lua_source("c:\\Luprex", &srcpak); if (!srcpakerr.empty()) { DPrint(srcpakerr.c_str()); } std::string_view srcpakv = srcpak.view(); char* argv[1]; argv[0] = const_cast("lpxserver"); w->play_initialize(w.Get(), 1, argv, srcpakv.size(), srcpakv.data(), ""); if (w->error[0]) { DPrint(w->error); } if (w->engine != nullptr) { DPrint("Luprex initialize success"); Playing = true; } } // If we successfully created a luprex engine, create a socket system and a worker thread. if (Playing) { Sockets.Reset(FLpxSockets::Create(w)); std::string error = Sockets->GetError(); check(error.empty()); LuprexUpdateTask.Startup(this); } // Initialize the tangible manager. TangibleManager.Init(GetWorld(), ClassTangibleActor); } void AIntegrationGameModeBase::EndPlay(const EEndPlayReason::Type EndPlayReason) { ResetToInitialState(); }