ZMMO: CHAR_INFO via V1 (Fase A) + fix loading travado

- ZeusWorldSubsystem consome ENT_CHAR_INFO (delegate OnCharInfo): aplica
  nome/guild no PlayerState do entityId certo (self ou proxy), com cache porque
  o CHAR_INFO chega antes do ator existir (server emite antes do ENT_SPAWN).
- FIX loading travado: a etapa "Spawn" do loading dependia do legacy
  OnPlayerSpawned, que não dispara mais com V1 -> loading eterno. Agora a etapa
  é marcada pelo sinal V1 OnSelfEntityAssigned (ENT_SELF); bind legacy removido.
  Travel ainda passa por OnServerTravelRequested (ponte legacy) até a Fase D.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-12 13:06:42 -03:00
parent 9f5ccd3a05
commit 77d52a703b
4 changed files with 121 additions and 3 deletions

View File

@@ -42,6 +42,7 @@ void UZeusWorldSubsystem::OnWorldBeginPlay(UWorld& InWorld)
Net->OnEntityDespawned.AddDynamic(this, &UZeusWorldSubsystem::OnNetEntityDespawned);
Net->OnEntityDeltaApplied.AddDynamic(this, &UZeusWorldSubsystem::OnNetEntityDelta);
Net->OnSelfEntityAssigned.AddDynamic(this, &UZeusWorldSubsystem::OnNetSelfEntityAssigned);
Net->OnCharInfo.AddDynamic(this, &UZeusWorldSubsystem::OnNetCharInfo);
UE_LOG(LogZMMO, Log, TEXT("ZeusWorldSubsystem bound to network client subsystem delegates."));
// O subsystem de rede e' per-GameInstance e sobrevive ao OpenLevel, entao
@@ -77,9 +78,11 @@ void UZeusWorldSubsystem::Deinitialize()
Net->OnEntityDespawned.RemoveDynamic(this, &UZeusWorldSubsystem::OnNetEntityDespawned);
Net->OnEntityDeltaApplied.RemoveDynamic(this, &UZeusWorldSubsystem::OnNetEntityDelta);
Net->OnSelfEntityAssigned.RemoveDynamic(this, &UZeusWorldSubsystem::OnNetSelfEntityAssigned);
Net->OnCharInfo.RemoveDynamic(this, &UZeusWorldSubsystem::OnNetCharInfo);
}
RemoteEntities.Reset();
PendingCharInfo.Reset();
LocalEntityId = 0;
Super::Deinitialize();
}
@@ -112,6 +115,9 @@ void UZeusWorldSubsystem::RegisterLocalEntity(const int64 EntityId, AActor* Loca
}
RemoteEntities.Add(EntityId, TWeakObjectPtr<AActor>(LocalActor));
// V1-CHARINFO: o nome do proprio char pode ter chegado antes do pawn local
// existir (CHAR_INFO vem logo apos ENT_SELF); aplica agora.
FlushPendingCharInfo(EntityId);
UE_LOG(LogZMMO, Log, TEXT("ZeusWorldSubsystem: local entity registered EntityId=%lld actor=%s"),
EntityId, *GetNameSafe(LocalActor));
}
@@ -255,10 +261,63 @@ void UZeusWorldSubsystem::HandlePlayerSpawned(const int64 EntityId, const bool b
}
RemoteEntities.Add(EntityId, TWeakObjectPtr<AActor>(SpawnedActor));
// V1-CHARINFO: o nome chega ANTES do ator (server emite ENT_CHAR_INFO antes
// do ENT_SPAWN); aplica agora que o proxy + PlayerState ja existem.
FlushPendingCharInfo(EntityId);
UE_LOG(LogZMMO, Log, TEXT("ZeusWorldSubsystem: spawned remote EntityId=%d at (%s) yaw=%.1f t=%lld"),
EntityId, *PosCm.ToString(), YawDeg, ServerTimeMs);
}
void UZeusWorldSubsystem::OnNetCharInfo(const int64 EntityId, const FString& CharName, const FString& GuildName)
{
if (EntityId == 0)
{
return;
}
// Aplica direto se o ator/PlayerState ja existe; senao guarda pra aplicar no spawn.
if (!ApplyCharInfoToEntity(EntityId, CharName, GuildName))
{
PendingCharInfo.Add(EntityId, FZeusPendingCharInfo{ CharName, GuildName });
}
}
bool UZeusWorldSubsystem::ApplyCharInfoToEntity(const int64 EntityId, const FString& CharName, const FString& GuildName)
{
const TWeakObjectPtr<AActor>* Found = RemoteEntities.Find(EntityId);
if (!Found || !Found->IsValid())
{
return false;
}
APawn* Pawn = Cast<APawn>(Found->Get());
if (!Pawn)
{
return false;
}
AZeusPlayerState* PS = Pawn->GetPlayerState<AZeusPlayerState>();
if (!PS)
{
return false;
}
PS->SetCharInfo(CharName, GuildName);
UE_LOG(LogZMMO, Log,
TEXT("ZeusWorldSubsystem: CHAR_INFO aplicado EntityId=%lld name='%s' guild='%s'"),
EntityId, *CharName, *GuildName);
return true;
}
void UZeusWorldSubsystem::FlushPendingCharInfo(const int64 EntityId)
{
const FZeusPendingCharInfo* Pending = PendingCharInfo.Find(EntityId);
if (!Pending)
{
return;
}
if (ApplyCharInfoToEntity(EntityId, Pending->CharName, Pending->GuildName))
{
PendingCharInfo.Remove(EntityId);
}
}
void UZeusWorldSubsystem::HandlePlayerDespawned(const int64 EntityId)
{
const TWeakObjectPtr<AActor>* Entry = RemoteEntities.Find(EntityId);