// Copyright Epic Games, Inc. All Rights Reserved. #include "IntegrationGameModeBase.h" #include "lpx-drvutil.hpp" #include "DebugPrint.h" #include "Tangible.h" #include "TangibleManager.h" #include "TangibleInterface.h" #include "CommonTypes.h" #include "AnimQueue.h" #include #include using namespace DebugPrint; using namespace CommonTypes; 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() { FlxLockedWrapper 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. FlxLockedWrapper 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; NextRotateCube = 1.0; } void AIntegrationGameModeBase::UpdateConsoleOutput() { // Copy Luprex Stdout into the console. FlxLockedWrapper 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; FlxLockedWrapper lockedwrap(LockableWrapper); if (EngineSeconds >= NextThreadTrigger) { LuprexUpdateTask.Trigger(); NextThreadTrigger += 0.05; } } //#pragma optimize( "", off ) //void SetLocal(UObject* obj, const char *name, int value) { // FString sname((const UTF8CHAR *)name); // FName nname(sname); // UClass* uclass = obj->GetClass(); // FProperty* fprop = FindFProperty(uclass, nname); // FStructProperty* sprop = FindFProperty(uclass, nname); //} void AIntegrationGameModeBase::UpdateTangibles() { if (!Playing) return; FlxLockedWrapper 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); } // Update animation queues of live tangibles. IdArray tanids = TangibleManager.GetLive(); StringViewVec aqueues = w.GetAnimationQueues(tanids); for (int i = 0; i < tanids.Num(); i++) { uint64_t tanid = tanids[i]; std::string_view aqueue = aqueues[i]; UlxTangible* t = TangibleManager.GetTangible(tanid); check(t != nullptr); t->AnimTracker.Update(aqueue); TArray aborted = t->AnimTracker.GetAborted(); for (uint64 hash : aborted) { IlxTangibleInterface::Execute_AbortAnimation(t->Actor, hash); } FlxAnimStoredStep step; ElxAnimPlaybackMode mode = t->AnimTracker.GetNextStep(step); bool started = false; if (mode != ElxAnimPlaybackMode::INVALID) { FlxAnimStepDecoder::UnpackInto(step.Body, TEXT("aq"), true, t->Actor); } switch (mode) { case ElxAnimPlaybackMode::INVALID: started = false; // Nothing to do. break; case ElxAnimPlaybackMode::WARP_TO_FINAL: started = IlxTangibleInterface::Execute_WarpToFinal(t->Actor, step.Hash, step.Body.size()); break; case ElxAnimPlaybackMode::BLEND_TO_FINAL: started = IlxTangibleInterface::Execute_BlendToFinal(t->Actor, step.Hash, step.Body.size()); break; case ElxAnimPlaybackMode::START_ANIMATION: started = IlxTangibleInterface::Execute_StartAnimation(t->Actor, step.Hash, step.Body.size()); break; } if (started) { t->AnimTracker.StartedStep(step.Hash); } } } void AIntegrationGameModeBase::ConsoleSendInput(const FString& fs) { FlxLockedWrapper 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); if (Playing) { EngineSeconds += 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. FlxLockedWrapper 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(FlxSockets::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(); }