// Copyright Epic Games, Inc. All Rights Reserved. #include "LuprexGameModeBase.h" #include "PlayerControllerBase.h" #include "LockedWrapper.h" #include "lpx-drvutil.hpp" #include "Misc/Paths.h" #include "Tangible.h" #include "TangibleManager.h" #include "LuaCall.h" #include "Blueprint/UserWidget.h" #include "Blueprint/WidgetBlueprintLibrary.h" #include "Kismet/GameplayStatics.h" #include "Engine/GameInstance.h" #include "Common.h" #include "AnimQueue.h" #include #include using namespace LpxCommonTypes; ALuprexGameModeBase::ALuprexGameModeBase() {} ALuprexGameModeBase::~ALuprexGameModeBase() {} void ALuprexGameModeBase::ResetToInitialState() { Playing = false; TickEnabled = true; // Stop the tick function. FWorldDelegates::OnWorldPostActorTick.Remove(OnWorldPostActorTickHandle); OnWorldPostActorTickHandle.Reset(); FlxLockedWrapper w; // Release and close all sockets. if (Sockets != nullptr) { Sockets->ForceCloseEverything(w); Sockets.Reset(); } // Delete the engine. if (w->release != nullptr) { w->release(w.Get()); } // Clear the lua call assembly buffer. UlxEngineWrapper::GetLuaCallBuffer().clear(); // Stop trapping log errors to the debugger. BreakToDebuggerLogVerbosityDevice.Reset(); // Clear the PlayerID PlayerId = 0; // Reset the clocks. EngineSeconds = 0.0; } void ALuprexGameModeBase::UpdateConsoleOutput() { // Copy Luprex Stdout into the console. FlxLockedWrapper lockedwrap; if (Playing) { FString Text = lockedwrap.ChannelPrints(); if (!Text.IsEmpty()) { ConsoleAddOutput(Text); } } } void ALuprexGameModeBase::UpdateTangibles() { double radius = 1000.0; // Hardwired for now. using TanArray = UlxTangibleManager::TanArray; if (!Playing) return; UlxTangibleManager *TM = GetGameInstance()->GetSubsystem(); TanArray alltans; { FlxLockedWrapper w; PlayerId = w.GetActor(); IdView nearids = w.GetNear(PlayerId, radius, radius, radius); TM->UpdateNearAccordingToLuprex(nearids); alltans = TM->GetAllTangibles(); IdArray allids = TM->GetIds(alltans); StringViewVec allqueues = w.GetAnimationQueues(allids); for (int i = 0; i < alltans.Num(); i++) { alltans[i]->UpdateAnimationQueue(allqueues[i]); } // Debugging hook. volatile bool printaqs = false; if (printaqs) { for (int i = 0; i < alltans.Num(); i++) { UE_LOG(LogLuprex, Display, TEXT("\n--- AQ of %ld ---\n%s\n"), allids[i], *(alltans[i]->AnimTracker.DebugString())); } } } // This is where we run the blueprint code that updates animation // states. Be aware that the blueprint code could call back // into the gamemode, which could in turn access the FlxLockedWrapper. // This is why we've released the FlxLockedWrapper before doing this. for (int i = 0; i < alltans.Num(); i++) { alltans[i]->MaybeExecuteAnimStateChanged(); } TM->RecalcNearAccordingToUnreal(PlayerId, radius); TM->DeleteFarawayTangibles(); } void ALuprexGameModeBase::UpdatePossessedTangible() { UlxTangibleManager *TM = GetGameInstance()->GetSubsystem(); UlxTangible *ptan = TM->GetPossessedTangible(); UlxTangible *tan = TM->GetTangible(PlayerId); APlayerController *ctrl = GetWorld()->GetFirstPlayerController(); APawn *pawn = nullptr; if (tan != nullptr) pawn = Cast(tan->GetActor()); if (pawn == nullptr) { if (ptan != nullptr) { TM->SetPossessedTangible(nullptr); ctrl->UnPossess(); } } else { if (ptan != tan) { TM->SetPossessedTangible(tan); ctrl->Possess(pawn); } } } void ALuprexGameModeBase::UpdateLuaSourceCode() { FlxLockedWrapper lockedwrap; if (lockedwrap->get_rescan_lua_source(lockedwrap.Get()) || ReloadSource) { ReloadSource = false; drvutil::ostringstream srcpak; FString LuprexRoot = FPaths::Combine(FPaths::ProjectDir(), TEXT("luprex")); std::string srcpakerr = drvutil::package_lua_source(TCHAR_TO_UTF8(*LuprexRoot), &srcpak); if (!srcpakerr.empty()) { FString FMessage((const UTF8CHAR *)(srcpakerr.c_str())); UE_LOG(LogLuprexIntegration, Error, TEXT("Trying to read Lua source: %s"), *FMessage); } else { std::string_view srcpakv = srcpak.view(); lockedwrap->play_access(lockedwrap.Get(), AccessKind::INVOKE_LUA_SOURCE, 0, srcpakv.size(), srcpakv.data(), nullptr, nullptr); } } } void ALuprexGameModeBase::UpdateSocketsAndLua() { FlxLockedWrapper lockedwrap; Sockets->Update(lockedwrap); lockedwrap->play_update(lockedwrap.Get(), EngineSeconds); Sockets->Update(lockedwrap); } void ALuprexGameModeBase::OnWorldPostActorTick(UWorld* InWorld, ELevelTick InLevelTick, float deltaseconds) { if(Playing && TickEnabled && (GetWorld() == InWorld) && (InLevelTick == LEVELTICK_All)) { EngineSeconds += deltaseconds; UpdateLuaSourceCode(); UpdateSocketsAndLua(); UpdateConsoleOutput(); UpdateTangibles(); UpdatePossessedTangible(); AlxPlayerControllerBase *PC = Cast(GetWorld()->GetFirstPlayerController()); if (PC != nullptr) { PC->UpdateLookAt(); PC->UpdateInputMode(); } } } void ALuprexGameModeBase::BeginPlay() { ResetToInitialState(); InitializeGlobalState(); Super::BeginPlay(); } void ALuprexGameModeBase::InitializeGlobalState() { FlxLockedWrapper w; // Sanity checks. Make sure everything is clean. checkf(w->engine == nullptr, TEXT("There should be no engine here.")); // If we failed to initialize the wrapper, print an error message. if (w->play_initialize == nullptr) { UE_LOG(LogLuprexIntegration, Error, TEXT("Luprex wrapper initialization failed")); } // If wrapper is initialized, try to initialize the luprex engine. if (w->play_initialize != nullptr) { w->play_initialize(w.Get(), "lpxclient", ""); if (w->error[0]) { FString FMessage((const UTF8CHAR *)w->error); UE_LOG(LogLuprexIntegration, Error, TEXT("Calling Luprex play_initialize: %s"), *FMessage); } if (w->engine != nullptr) { UE_LOG(LogLuprexIntegration, Verbose, TEXT("Luprex initialization success.")); Playing = true; } } // Possibly tell the engine to connect to a server. if (Playing) { FString LuprexServer; FParse::Value(FCommandLine::Get(), TEXT("-LuprexServer="), LuprexServer); LuprexServer = LuprexServer.ToLower(); UE_LOG(LogTemp, Display, TEXT("LuprexServer = %s"), *LuprexServer) if (LuprexServer != TEXT("standalone")) { FTCHARToUTF8 utf8server(LuprexServer); w->play_access(w.Get(), AccessKind::CONNECT_TO_SERVER, 0, utf8server.Length(), utf8server.Get(), nullptr, nullptr); } } // If we successfully created a luprex engine, create a socket system. if (Playing) { Sockets.Reset(FlxSockets::Create(w)); std::string error = Sockets->GetError(); check(error.empty()); } if (Playing) { OnWorldPostActorTickHandle = FWorldDelegates::OnWorldPostActorTick.AddUObject(this, &ALuprexGameModeBase::OnWorldPostActorTick); } // If somebody generates a log message that's severe enough, break to debugger. BreakToDebuggerLogVerbosityDevice.Reset( new FlxBreakToDebuggerOutputDevice(BreakToDebuggerLogVerbosity)); } void ALuprexGameModeBase::EndPlay(const EEndPlayReason::Type EndPlayReason) { ResetToInitialState(); Super::EndPlay(EndPlayReason); } int64 ALuprexGameModeBase::GetPlayerId() { return PlayerId; } ALuprexGameModeBase *ALuprexGameModeBase::FromContext(const UObject *context) { ALuprexGameModeBase *result = context->GetWorld()->GetAuthGameMode(); if (result == nullptr) { UE_LOG(LogBlueprint, Fatal, TEXT("Not currently using a Luprex Game Mode.")); } return result; } void ALuprexGameModeBase::TriggerReloadSource(const UObject *WorldContextObject) { ALuprexGameModeBase *GameMode = FromContext(WorldContextObject); GameMode->ReloadSource = true; }