// Copyright Epic Games, Inc. All Rights Reserved. #include "IntegrationGameModeBase.h" #include "drvutil.hpp" #include "engineutil.hpp" #include #include 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); } AIntegrationGameModeBase::~AIntegrationGameModeBase() { ResetToInitialState(); } // Run routine called by the worker thread. uint32 AIntegrationGameModeBase::Run() { while (true) { bool triggered = ThreadEvent->Wait(3000); if (ThreadStopRequested) { engineutil::DPrint("Thread stopping as requested"); break; } if (!triggered) { engineutil::DPrint("Thread waiting a long time..."); continue; } { FScopeLock lk(&LuprexMutex); Sockets->Update(); Luprex.play_invoke_event_update(&Luprex, EngineSeconds); Sockets->Update(); } } 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; // Release and close all sockets. Sockets.Reset(); // Delete the engine. if (Luprex.release != nullptr) { Luprex.release(&Luprex); } // Reset the clocks. EngineSeconds = 0; NextThreadTrigger = 1.0; } void AIntegrationGameModeBase::HandleLuprexConsoleOutput() { uint32_t ndata; const char* data; Luprex.get_outgoing(&Luprex, 0, &ndata, &data); if (ndata == 0) return; std::string_view src(data, ndata); int consumed; std::u16string cps = drvutil::utf8_to_ucs2(src, &consumed); Luprex.play_sent_outgoing(&Luprex, 0, consumed); FString fs(cps.size(), (const UCS2CHAR*)(&cps[0])); ConsoleOutput.Append(fs); } void AIntegrationGameModeBase::Tick(float DeltaSeconds) { Super::Tick(DeltaSeconds); { FScopeLock lk(&LuprexMutex); if (Luprex.engine != nullptr) { EngineSeconds += DeltaSeconds; HandleLuprexConsoleOutput(); } } TArray prints = engineutil::DPrintGetStored(); 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) { if (Luprex.engine != nullptr) { FScopeLock lk(&LuprexMutex); 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"; Luprex.play_recv_incoming(&Luprex, 0, utf8.size(), utf8.c_str()); } } } void AIntegrationGameModeBase::BeginPlay() { Super::BeginPlay(); // Sanity checks. checkf(Thread == nullptr, TEXT("There should be no thread here.")); checkf(Luprex.engine == nullptr, TEXT("There should be no engine here.")); // Make sure we're starting from a clean slate. ResetToInitialState(); // Try to initialize the wrapper. if (Luprex.play_initialize == nullptr) { engineutil::init_wrapper(&Luprex); } // If we failed to initialize the wrapper, print an error message. if (Luprex.play_initialize == nullptr) { engineutil::DPrint("Luprex wrapper initialization failed"); } // If wrapper is initialized, try to initialize the luprex engine. if (Luprex.play_initialize != nullptr) { drvutil::ostringstream srcpak; std::string srcpakerr = drvutil::package_lua_source("c:\\Luprex", &srcpak); if (!srcpakerr.empty()) { engineutil::DPrint(srcpakerr.c_str()); } std::string_view srcpakv = srcpak.view(); char* argv[1]; argv[0] = const_cast("lpxserver"); Luprex.play_initialize(&Luprex, 1, argv, srcpakv.size(), srcpakv.data(), ""); if (Luprex.error[0]) { engineutil::DPrint(Luprex.error); } else { engineutil::DPrint("Luprex initialize success"); } } // If we successfully created a luprex engine, create a socket system and a worker thread. if (Luprex.engine != nullptr) { Sockets.Reset(FLpxSockets::Create(&Luprex)); std::string error = Sockets->GetError(); check(error.empty()); ThreadEvent = FPlatformProcess::GetSynchEventFromPool(false); Thread = FRunnableThread::Create(this, TEXT("Worker Thread")); } } void AIntegrationGameModeBase::EndPlay(const EEndPlayReason::Type EndPlayReason) { ResetToInitialState(); }