From 5f4c88637fbbfedcc0b04a248ce6f5f5755bc894 Mon Sep 17 00:00:00 2001 From: Mateus Rodrigues Date: Fri, 12 Jun 2026 21:04:43 -0300 Subject: [PATCH] ZN V1: overlay AOI canonico + ENT_SPAWN keyframe [Change-Set: AOI-VIS-2026-06-12] Lado CLIENTE ZMMO (par com o commit de mesmo Change-Set no repo ZeusServerEngine -- devem ir juntos, o wire do ENT_SPAWN mudou). - UZeusAOIComponent: migrado do transporte legacy (SendDebugAoiRequest/OnDebugAoiInfo, desativado no V1) pro canonico (EmitDebugAoiRequest 6160 / OnDebugAoiInfo 6161). Esfera de debug 32 -> 48 segments + clamp visual de 500m (resolve "zona sem limite / poucas linhas"). - ZeusWorldSubsystem::OnNetEntitySpawned: recebe vel + grounded + serverTimeMs (delegate FZeusV1OnEntitySpawned 4 -> 7 params) e semeia o proxy recem-criado via HandlePlayerStateUpdate -> ancora o relogio de interpolacao + a vel inicial -> proxy nasce ja' animando no re-spawn (sai/volta do raio AOI segurando W), sem Idle deslizando. Co-Authored-By: Claude Opus 4.8 (1M context) --- Source/ZMMO/Game/Network/ZeusAOIComponent.cpp | 33 +++++++++++++------ .../ZMMO/Game/Network/ZeusWorldSubsystem.cpp | 20 +++++++++-- Source/ZMMO/Game/Network/ZeusWorldSubsystem.h | 3 +- 3 files changed, 42 insertions(+), 14 deletions(-) diff --git a/Source/ZMMO/Game/Network/ZeusAOIComponent.cpp b/Source/ZMMO/Game/Network/ZeusAOIComponent.cpp index abcb5b6..3886ea0 100644 --- a/Source/ZMMO/Game/Network/ZeusAOIComponent.cpp +++ b/Source/ZMMO/Game/Network/ZeusAOIComponent.cpp @@ -2,7 +2,7 @@ #include "ZeusAOIComponent.h" -#include "ZeusNetworkSubsystem.h" // plugin ZeusNetwork: OnDebugAoiInfo + SendDebugAoiRequest +#include "ZeusNetworkingClientSubsystem.h" // V1 canonico: OnDebugAoiInfo (6161) + EmitDebugAoiRequest (6160) #include "ZMMONetLog.h" // Batch 2.5: LogZeusAOI (categoria do modulo ZMMO) #include "DrawDebugHelpers.h" @@ -30,6 +30,14 @@ namespace const FColor kColorDespawn(255, 140, 90); // laranja — Zona de Despawn (externa) const FColor kColorCell(232, 192, 96); const FColor kColorHandoff(255, 155, 190); + + // Resolucao das esferas de overlay. 32 segments deixavam a "bolha" facetada + // (poucas linhas) num raio de 60-80m; 48 deixa o circulo bem mais denso/legivel. + constexpr int32 kSphereSegments = 48; + // Clamp visual defensivo: se o raio vier absurdo (config corrompida), nao desenha + // uma esfera gigante que engole o mapa. 500m cobre qualquer AOI real (max=800m, + // mas overlay > 500m ja' nao ajuda a leitura). So' afeta o desenho, nao o gameplay. + constexpr float kMaxOverlayRadiusCm = 50000.0f; } UZeusAOIComponent::UZeusAOIComponent() @@ -43,14 +51,17 @@ void UZeusAOIComponent::BeginPlay() { Super::BeginPlay(); - // Bind do feed de config de AOI (servidor -> cliente). Multicast plain C++. + // Bind do feed de config de AOI (servidor -> cliente) pelo transporte V1 + // canonico. O legacy (UZeusNetworkSubsystem) esta desativado no V1 -> usava + // SendDebugAoiRequest que logava "ignorado: nao conectado" e o overlay nunca + // recebia a config. Agora via UZeusNetworkingClientSubsystem (OnDebugAoiInfo 6161). if (UWorld* World = GetWorld()) { if (UGameInstance* GI = World->GetGameInstance()) { - if (UZeusNetworkSubsystem* Net = GI->GetSubsystem()) + if (UZeusNetworkingClientSubsystem* NetC = GI->GetSubsystem()) { - AoiConfigHandle_ = Net->OnDebugAoiInfo.AddUObject(this, &UZeusAOIComponent::HandleAoiConfig); + AoiConfigHandle_ = NetC->OnDebugAoiInfo.AddUObject(this, &UZeusAOIComponent::HandleAoiConfig); } } } @@ -68,9 +79,9 @@ void UZeusAOIComponent::EndPlay(const EEndPlayReason::Type EndPlayReason) { if (UGameInstance* GI = World->GetGameInstance()) { - if (UZeusNetworkSubsystem* Net = GI->GetSubsystem()) + if (UZeusNetworkingClientSubsystem* NetC = GI->GetSubsystem()) { - Net->OnDebugAoiInfo.Remove(AoiConfigHandle_); + NetC->OnDebugAoiInfo.Remove(AoiConfigHandle_); } } } @@ -96,9 +107,9 @@ void UZeusAOIComponent::RequestAoiConfig() { if (UGameInstance* GI = World->GetGameInstance()) { - if (UZeusNetworkSubsystem* Net = GI->GetSubsystem()) + if (UZeusNetworkingClientSubsystem* NetC = GI->GetSubsystem()) { - Net->SendDebugAoiRequest(); // no-op se nao conectado + NetC->EmitDebugAoiRequest(); // V1: no-op se ainda nao Accepted } } } @@ -152,12 +163,14 @@ void UZeusAOIComponent::DrawOverlays() // ainda nao chegou a config: nao desenha (loading). if (ResolveOverlay(EZeusAOIOverlay::AOIRadius) && InterestRadiusCm > 0.0f) { - DrawDebugSphere(World, Loc, InterestRadiusCm, 32, kColorInterest, false, -1.0f, 0, LineThickness); + const float R = FMath::Min(InterestRadiusCm, kMaxOverlayRadiusCm); + DrawDebugSphere(World, Loc, R, kSphereSegments, kColorInterest, false, -1.0f, 0, LineThickness); } // Zona de Despawn (externa, > interesse = histerese) — raio real do servidor. if (ResolveOverlay(EZeusAOIOverlay::DespawnZone) && DespawnRadiusCm > 0.0f) { - DrawDebugSphere(World, Loc, DespawnRadiusCm, 32, kColorDespawn, false, -1.0f, 0, LineThickness); + const float R = FMath::Min(DespawnRadiusCm, kMaxOverlayRadiusCm); + DrawDebugSphere(World, Loc, R, kSphereSegments, kColorDespawn, false, -1.0f, 0, LineThickness); } if (ResolveOverlay(EZeusAOIOverlay::CellBounds)) { diff --git a/Source/ZMMO/Game/Network/ZeusWorldSubsystem.cpp b/Source/ZMMO/Game/Network/ZeusWorldSubsystem.cpp index 2ca28c1..ecc584e 100644 --- a/Source/ZMMO/Game/Network/ZeusWorldSubsystem.cpp +++ b/Source/ZMMO/Game/Network/ZeusWorldSubsystem.cpp @@ -56,7 +56,10 @@ void UZeusWorldSubsystem::OnWorldBeginPlay(UWorld& InWorld) Net->ForEachRemoteEntity( [this, &ReplayCount](int64 EntityId, FVector PosCm, float YawDeg) { - OnNetEntitySpawned(EntityId, /*Kind=*/1 /*Player*/, PosCm, YawDeg); + // Catch-up local: ForEachRemoteEntity nao expoe vel/serverTimeMs -> + // ZeroVector + grounded + ts=0 (seed nao roda; o 1o delta ancora o relogio). + OnNetEntitySpawned(EntityId, /*Kind=*/1 /*Player*/, PosCm, YawDeg, + FVector::ZeroVector, /*bGrounded=*/true, /*ServerTimeMs=*/0); ++ReplayCount; }); if (ReplayCount > 0) @@ -450,13 +453,24 @@ void UZeusWorldSubsystem::HandlePlayerStateUpdate(const int64 EntityId, const in AsEntity->ApplyEntitySnapshot(Snapshot); } -void UZeusWorldSubsystem::OnNetEntitySpawned(int64 EntityId, int32 /*Kind*/, FVector PosCm, float YawDeg) +void UZeusWorldSubsystem::OnNetEntitySpawned(int64 EntityId, int32 /*Kind*/, FVector PosCm, float YawDeg, + FVector VelCmS, bool bGrounded, int64 ServerTimeMs) { // O sistema novo nao manda bIsLocal: derivamos do LocalEntityId (setado por // ENT_SELF). Se ainda for 0 e este ENT_SPAWN for do proprio char, o // OnNetSelfEntityAssigned destruira o fantasma quando o ENT_SELF chegar. const bool bIsLocal = (LocalEntityId != 0 && EntityId == LocalEntityId); - HandlePlayerSpawned(EntityId, bIsLocal, PosCm, YawDeg, /*ServerTimeMs=*/0); + HandlePlayerSpawned(EntityId, bIsLocal, PosCm, YawDeg, ServerTimeMs); + + // V1-SPAWN-KEYFRAME(-TS): o ENT_SPAWN carrega vel + grounded + serverTimeMs. Aplica + // esse keyframe INICIAL no proxy recem-criado pelo mesmo caminho do delta (semeia o + // SnapshotBuffer + ANCORA o ServerClockOffsetMs com o tempo CERTO). Antes o seed usava + // ServerTimeMs=0 -> bootstrap do relogio de interpolacao errado -> proxy preso/flutuando + // ate' o EMA convergir. Agora com o serverTimeMs real o proxy interpola desde o frame 1. + if (!bIsLocal && ServerTimeMs > 0) + { + HandlePlayerStateUpdate(EntityId, /*InputSeq=*/0, PosCm, VelCmS, bGrounded, ServerTimeMs); + } } void UZeusWorldSubsystem::OnNetEntityDespawned(int64 EntityId, int32 /*Reason*/) diff --git a/Source/ZMMO/Game/Network/ZeusWorldSubsystem.h b/Source/ZMMO/Game/Network/ZeusWorldSubsystem.h index 3bf7811..94d184d 100644 --- a/Source/ZMMO/Game/Network/ZeusWorldSubsystem.h +++ b/Source/ZMMO/Game/Network/ZeusWorldSubsystem.h @@ -84,7 +84,8 @@ private: // Sistema de rede novo (canonico) -- recebe spawn/despawn/delta + self-entity. // Adaptam a assinatura nova para os handlers de spawn/despawn/delta acima. UFUNCTION() - void OnNetEntitySpawned(int64 EntityId, int32 Kind, FVector PosCm, float YawDeg); + void OnNetEntitySpawned(int64 EntityId, int32 Kind, FVector PosCm, float YawDeg, + FVector VelCmS, bool bGrounded, int64 ServerTimeMs); UFUNCTION() void OnNetEntityDespawned(int64 EntityId, int32 Reason);