// 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() { Thread = nullptr; ThreadStopRequested = false; EngineSeconds = 0.0; NextThreadTrigger = 1.0; ThreadEvent = nullptr; //PrimaryActorTick.bCanEverTick = true; // Probably wrong //PrimaryActorTick.bTickEvenWhenPaused = true; // Probably wrong //PrimaryActorTick.TickGroup = TG_PrePhysics; // Probably wrong SetActorTickEnabled(true); SetActorTickInterval(0.0f); DebugPrintControl::EnableCollection(); } AIntegrationGameModeBase::~AIntegrationGameModeBase() { ResetToInitialState(); } // Run routine called by the worker thread. uint32 AIntegrationGameModeBase::Run() { while (true) { bool triggered = ThreadEvent->Wait(3000); if (ThreadStopRequested) { DPrint("Thread stopping as requested"); break; } if (!triggered) { DPrint("Thread waiting a long time..."); continue; } { FLockedWrapper lockedwrap(LockableWrapper); Sockets->Update(lockedwrap); lockedwrap->play_invoke_event_update(lockedwrap.Get(), EngineSeconds); Sockets->Update(lockedwrap); } } return 0; } void AIntegrationGameModeBase::ResetToInitialState() { // Shut down the thread and release the ThreadEvent if (Thread != nullptr) { ThreadStopRequested = true; ThreadEvent->Trigger(); delete Thread; // This waits for the thread to complete. Thread = nullptr; FPlatformProcess::ReturnSynchEventToPool(ThreadEvent); ThreadEvent = nullptr; } ThreadStopRequested = false; // 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::HandleLuprexConsoleOutput(FLockedWrapper &w) { uint32_t ndata; const char* data; w->get_outgoing(w.Get(), 0, &ndata, &data); if (ndata == 0) return; std::string_view src(data, ndata); int consumed; std::u16string cps = drvutil::utf8_to_ucs2(src, &consumed); w->play_sent_outgoing(w.Get(), 0, consumed); FString fs(cps.size(), (const UCS2CHAR*)(&cps[0])); ConsoleOutput.Append(fs); } void AIntegrationGameModeBase::Tick(float DeltaSeconds) { Super::Tick(DeltaSeconds); { FLockedWrapper lockedwrap(LockableWrapper); if (lockedwrap->engine != nullptr) { EngineSeconds += DeltaSeconds; HandleLuprexConsoleOutput(lockedwrap); } } TArray prints = DebugPrintControl::GetStored(); for (const FString& fs : prints) { ConsoleOutput.AppendLine(fs); } if (ConsoleOutput.IsDirty()) { ConsoleSetOutput(ConsoleOutput.Get()); ConsoleOutput.ClearDirty(); } if ((Thread != nullptr) && (EngineSeconds >= NextThreadTrigger)) { ThreadEvent->Trigger(); NextThreadTrigger += 0.05; } } 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::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(Thread == nullptr, 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); } else { DPrint("Luprex initialize success"); } } // If we successfully created a luprex engine, create a socket system and a worker thread. if (w->engine != nullptr) { Sockets.Reset(FLpxSockets::Create(w)); std::string error = Sockets->GetError(); check(error.empty()); ThreadEvent = FPlatformProcess::GetSynchEventFromPool(false); Thread = FRunnableThread::Create(this, TEXT("Worker Thread")); } // Create a tangible. TangibleManager.Init(GetWorld(), ClassTangibleActor); TangibleManager.MakeTangible(123); } void AIntegrationGameModeBase::EndPlay(const EEndPlayReason::Type EndPlayReason) { ResetToInitialState(); }