From b2c115322991277a56907c855ea14af8051190b5 Mon Sep 17 00:00:00 2001 From: Mateus Rodrigues Date: Sun, 31 May 2026 23:43:13 -0300 Subject: [PATCH] fix(frontend): HandleServerTravelRequested idempotente quando ja no mapa certo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cliente fazia OpenLevel 2x: 1. CharSelect resolve mapId via DT_Maps -> TravelToMapById -> OpenLevel 2. Server confirma com S_TRAVEL_TO_MAP -> HandleServerTravelRequested -> OpenLevel **de novo** O segundo OpenLevel destruia o World, PlayerState, AttributeComponent e HUD que ja tinham sido criados/bindados. AttributeComponent novo iniciava com snapshot vazio (entity=0, level=1, hp=0/0) — HUD ficava nesse default. S_ATTRIBUTE_SNAPSHOT_FULL com dados reais chegava DEPOIS do reload, mas HUD nao tinha sido recriado/reativado no novo mundo, entao HandleAttributesChanged nunca rodava. A deduplicacao em ZeusNetworkSubsystem (`S_TRAVEL_TO_MAP duplicate ignored`) so pegava do 3o TRAVEL em diante — o 2o ja fazia o estrago. Fix: compara nome curto do World atual (strip prefixo UEDPIE_N_ do PIE) com o target. Se ja' estao no mesmo mapa, skipa OpenLevel e so seta state pra InWorld — preservando HUD/PlayerState/AttributeComponent. --- .../UI/FrontEnd/UIFrontEndFlowSubsystem.cpp | 45 +++++++++++++++++-- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/Source/ZMMO/Game/UI/FrontEnd/UIFrontEndFlowSubsystem.cpp b/Source/ZMMO/Game/UI/FrontEnd/UIFrontEndFlowSubsystem.cpp index 2165ad4..a4c18b2 100644 --- a/Source/ZMMO/Game/UI/FrontEnd/UIFrontEndFlowSubsystem.cpp +++ b/Source/ZMMO/Game/UI/FrontEnd/UIFrontEndFlowSubsystem.cpp @@ -19,7 +19,9 @@ #include "UObject/UObjectGlobals.h" #include "Engine/AssetManager.h" #include "Engine/StreamableManager.h" +#include "Engine/World.h" #include "Kismet/GameplayStatics.h" +#include "Misc/PackageName.h" void UUIFrontEndFlowSubsystem::Initialize(FSubsystemCollectionBase& Collection) { @@ -406,6 +408,46 @@ void UUIFrontEndFlowSubsystem::ClearSelectedRealm() void UUIFrontEndFlowSubsystem::HandleServerTravelRequested(const FString& MapName, const FString& MapPath) { UE_LOG(LogZMMO, Log, TEXT("FrontEndFlow: travel pedido pelo servidor → %s (%s)."), *MapName, *MapPath); + + // Idempotencia: se o cliente JA carregou esse mapa via TravelToMapById + // (ex: Lobby após CharSelect resolveu DT_Maps), e o S_TRAVEL_TO_MAP do + // servidor chega DEPOIS confirmando, NAO recarrega. Recarregar destruiria + // HUD, PlayerState e AttributeComponent — perdendo o snapshot ja aplicado. + if (!MapPath.IsEmpty()) + { + if (const UWorld* CurrentWorld = GetWorld()) + { + // CurrentWorld->GetOutermost()->GetName() devolve algo como + // "/Game/ZMMO/Maps/World/UEDPIE_0_L_TestWorld" no PIE. Comparamos + // pelo nome curto pra ignorar prefixo UEDPIE_N_. + const FString CurrentShort = FPackageName::GetShortName(CurrentWorld->GetOutermost()->GetName()); + const FString TargetShort = FPackageName::GetShortName(MapPath); + // PIE prefixa "UEDPIE__" — strip se presente. + FString CurrentClean = CurrentShort; + int32 UndIdx = INDEX_NONE; + if (CurrentClean.StartsWith(TEXT("UEDPIE_")) && CurrentClean.FindChar('_', UndIdx)) + { + const int32 SecondUnd = CurrentClean.Find(TEXT("_"), ESearchCase::CaseSensitive, ESearchDir::FromStart, UndIdx + 1); + if (SecondUnd != INDEX_NONE) + { + CurrentClean = CurrentClean.Mid(SecondUnd + 1); + } + } + if (CurrentClean.Equals(TargetShort, ESearchCase::IgnoreCase)) + { + UE_LOG(LogZMMO, Log, + TEXT("FrontEndFlow: ja estamos em %s — ignorando OpenLevel do S_TRAVEL_TO_MAP (mantem HUD/PlayerState)"), + *TargetShort); + bTravelingToWorld = false; + if (CurrentState != EZMMOFrontEndState::InWorld) + { + SetState(EZMMOFrontEndState::InWorld); + } + return; + } + } + } + bTravelingToWorld = true; SetState(EZMMOFrontEndState::EnteringWorld); @@ -415,9 +457,6 @@ void UUIFrontEndFlowSubsystem::HandleServerTravelRequested(const FString& MapNam // World Settings do .umap podem ter override herdado de L_FrontEnd // (GameModeOverride=ZMMOFrontEndGameMode) — sobrescrevemos via URL // option pra nao depender de cada artista configurar. - // Evolucao natural: quando o WorldServer enviar GameMode no - // S_TRAVEL_TO_MAP (wire estendido), trocar este literal por - // `?game=` + valor recebido do server. const FString Options = TEXT("game=/Script/ZMMO.ZMMOGameMode"); UGameplayStatics::OpenLevel(GetGameInstance(), FName(*MapPath), /*bAbsolute=*/true, Options); }