fix(frontend): HandleServerTravelRequested idempotente quando ja no mapa certo
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.
This commit is contained in:
@@ -19,7 +19,9 @@
|
|||||||
#include "UObject/UObjectGlobals.h"
|
#include "UObject/UObjectGlobals.h"
|
||||||
#include "Engine/AssetManager.h"
|
#include "Engine/AssetManager.h"
|
||||||
#include "Engine/StreamableManager.h"
|
#include "Engine/StreamableManager.h"
|
||||||
|
#include "Engine/World.h"
|
||||||
#include "Kismet/GameplayStatics.h"
|
#include "Kismet/GameplayStatics.h"
|
||||||
|
#include "Misc/PackageName.h"
|
||||||
|
|
||||||
void UUIFrontEndFlowSubsystem::Initialize(FSubsystemCollectionBase& Collection)
|
void UUIFrontEndFlowSubsystem::Initialize(FSubsystemCollectionBase& Collection)
|
||||||
{
|
{
|
||||||
@@ -406,6 +408,46 @@ void UUIFrontEndFlowSubsystem::ClearSelectedRealm()
|
|||||||
void UUIFrontEndFlowSubsystem::HandleServerTravelRequested(const FString& MapName, const FString& MapPath)
|
void UUIFrontEndFlowSubsystem::HandleServerTravelRequested(const FString& MapName, const FString& MapPath)
|
||||||
{
|
{
|
||||||
UE_LOG(LogZMMO, Log, TEXT("FrontEndFlow: travel pedido pelo servidor → %s (%s)."), *MapName, *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_<N>_<MapName>" — 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;
|
bTravelingToWorld = true;
|
||||||
SetState(EZMMOFrontEndState::EnteringWorld);
|
SetState(EZMMOFrontEndState::EnteringWorld);
|
||||||
|
|
||||||
@@ -415,9 +457,6 @@ void UUIFrontEndFlowSubsystem::HandleServerTravelRequested(const FString& MapNam
|
|||||||
// World Settings do .umap podem ter override herdado de L_FrontEnd
|
// World Settings do .umap podem ter override herdado de L_FrontEnd
|
||||||
// (GameModeOverride=ZMMOFrontEndGameMode) — sobrescrevemos via URL
|
// (GameModeOverride=ZMMOFrontEndGameMode) — sobrescrevemos via URL
|
||||||
// option pra nao depender de cada artista configurar.
|
// 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");
|
const FString Options = TEXT("game=/Script/ZMMO.ZMMOGameMode");
|
||||||
UGameplayStatics::OpenLevel(GetGameInstance(), FName(*MapPath), /*bAbsolute=*/true, Options);
|
UGameplayStatics::OpenLevel(GetGameInstance(), FName(*MapPath), /*bAbsolute=*/true, Options);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user