fix(world): destroy proxy on DESPAWN + handle stale weak ptr on SPAWN
ZMMOWorldSubsystem mantinha proxies no mapa RemoteEntities apos DESPAWN apenas marcando SetActorHiddenInGame(true) via SetEntityRelevant(false). Entry ficava la apontando pra actor escondido — SPAWN subsequente do mesmo entityId caia no fast-path "reactivar" e tudo bem... ate' o TWeakObjectPtr ficar stale (GC, World Partition stream-out, etc): a branch Contains(EntityId) saia com return incondicional sem spawnar nada. Resultado: SPAWN silenciosamente ignorado, pawn permanente invisivel ate' algo limpar a key. Fixes: 1. HandlePlayerDespawned: Actor->Destroy() + RemoteEntities.Remove (destroi de verdade em vez de so esconder). 2. HandlePlayerSpawned: quando Contains mas weak ptr stale, remove a entry orfa e cai pro caminho de spawn novo — em vez de return. Confirmado pelos logs de cliente durante teste de oscilacao do AOI (servidor enviando DESPAWN+SPAWN ao oscilar ZI/ZD). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -104,7 +104,10 @@ void UZMMOWorldSubsystem::HandlePlayerSpawned(const int32 EntityId, const bool b
|
||||
|
||||
if (RemoteEntities.Contains(EntityId))
|
||||
{
|
||||
// Ja existe um proxy: reactivar e re-snap.
|
||||
// Ja existe um proxy: tenta reactivar e re-snap. Se o weak ptr ficou
|
||||
// stale (actor coletado pelo GC, stream-out do World Partition, etc.),
|
||||
// remove a entry orfa e cai no caminho de spawn novo — evita SPAWN
|
||||
// silenciosamente ignorado depois de oscilacoes na AOI.
|
||||
if (AActor* Existing = RemoteEntities[EntityId].Get())
|
||||
{
|
||||
if (IZMMOEntityInterface* AsEntity = Cast<IZMMOEntityInterface>(Existing))
|
||||
@@ -112,8 +115,12 @@ void UZMMOWorldSubsystem::HandlePlayerSpawned(const int32 EntityId, const bool b
|
||||
AsEntity->SetEntityRelevant(true);
|
||||
}
|
||||
Existing->SetActorLocationAndRotation(PosCm, FRotator(0.0f, YawDeg, 0.0f));
|
||||
UE_LOG(LogZMMO, Verbose, TEXT("ZMMOWorldSubsystem: re-snap EntityId=%d"), EntityId);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
// weak ptr stale — limpa entry e cai pro spawn novo abaixo.
|
||||
RemoteEntities.Remove(EntityId);
|
||||
UE_LOG(LogZMMO, Log, TEXT("ZMMOWorldSubsystem: stale entry para EntityId=%d, respawnando"), EntityId);
|
||||
}
|
||||
|
||||
UWorld* World = GetWorld();
|
||||
@@ -166,15 +173,23 @@ void UZMMOWorldSubsystem::HandlePlayerDespawned(const int32 EntityId)
|
||||
return;
|
||||
}
|
||||
|
||||
// Destruir o actor pra valer + remover do mapa. Antes, despawn apenas
|
||||
// fazia SetActorHiddenInGame(true) via SetEntityRelevant(false), o que
|
||||
// deixava o entry no mapa apontando pra um actor escondido. SPAWN
|
||||
// subsequente do mesmo entityId era detectado como "duplicate" e nada
|
||||
// acontecia — pawn ficava invisivel ate' o weak ptr ser GC'd. Bug
|
||||
// confirmado nos logs do AOI ao oscilar entrada/saida da ZI/ZD.
|
||||
if (AActor* Actor = Entry->Get())
|
||||
{
|
||||
if (IZMMOEntityInterface* AsEntity = Cast<IZMMOEntityInterface>(Actor))
|
||||
{
|
||||
AsEntity->SetEntityRelevant(false);
|
||||
}
|
||||
Actor->Destroy();
|
||||
}
|
||||
RemoteEntities.Remove(EntityId);
|
||||
|
||||
UE_LOG(LogZMMO, Log, TEXT("ZMMOWorldSubsystem: despawn EntityId=%d"), EntityId);
|
||||
UE_LOG(LogZMMO, Log, TEXT("ZMMOWorldSubsystem: despawn EntityId=%d (destroyed)"), EntityId);
|
||||
}
|
||||
|
||||
void UZMMOWorldSubsystem::HandlePlayerStateUpdate(const int32 EntityId, const int32 InputSeq,
|
||||
|
||||
Reference in New Issue
Block a user