2 Commits

Author SHA1 Message Date
c0f6d32d95 feat(zmmo-client): logs AOI categorias + K4 cfg recv
Rodando sem Jitter visivel.

- DECLARE/DEFINE LogZeusHandoff/AOI/Proxy/HaloTransition no modulo
  ZMMO (nao no plugin) por causa do adaptive non-unity build do UBT
  que usa git status do repo do cliente -- plugin em outro repo nao
  dispara rebuild quando edit so o header dele.
- ZeusAOIComponent::HandleAoiConfig loga raios recebidos do server
  pra cruzar com Net.AOI server-side ("AOI cfg recv interestCm=6000
  (60m) despawnCm=8000 (80m)").
- Parte da milestone 2026-06-11: proxy/PIE estaveis apos fix de
  double-emit AOI no server (AOIRegistry::ForceTickNow).
2026-06-11 18:01:32 -03:00
f21d059b67 fix(zmmo-client): ZeusPlayerProxy clock offset EMA + asset cleanup L_TestWorld
ZeusPlayerProxy.cpp -- H3 fix (2026-06-06): EMA com clamp para
ServerClockOffsetMs
================================================================
Antes: offset congelado na 1a amostra de ApplyEntitySnapshot. Em
cross-server handoff, a fonte do ServerTimeMs muda (publisher antigo
emitia 12340000, novo emite 12351000) e o offset fica permanentemente
skewed. Causa tremor pos-handoff + drift cumulativo de clock cliente
vs server.

Depois: cada amostra recalcula candidato (LocalNowMs - Snapshot.ServerTimeMs)
e suaviza via EMA com alpha=0.02 (~50 amostras para convergir) + clamp
max 2ms por amostra (a 30Hz isso converge a 60ms/s, suficiente para
absorver drift normal sem causar tremor visual). 1a amostra continua
bootstrap direto.

Trecho:
  const double Alpha = 0.02;
  const int64 RawTarget = ServerClockOffsetMs*(1-Alpha) + OffsetCandidate*Alpha;
  const int64 Delta = RawTarget - ServerClockOffsetMs;
  const int64 ClampedDelta = clamp(Delta, -2, 2);
  ServerClockOffsetMs += ClampedDelta;

Asset cleanup L_TestWorld
==========================
DT_Maps.uasset modificado + 3 ExternalActors deletados (provavelmente
limpeza no UE editor ao reorganizar mapa). Empacotado junto pra branch
ficar limpa.

* Content/ZMMO/Data/World/DT_Maps.uasset (M)
* Content/__ExternalActors__/ZMMO/Maps/World/L_TestWorld/2/BR/38W34OMKW0W3L1PGS3Y98G.uasset (D)
* Content/__ExternalActors__/ZMMO/Maps/World/L_TestWorld/4/8L/X3PMRJRGAACIU1WG1P1KJ0.uasset (D)
* Content/__ExternalActors__/ZMMO/Maps/World/L_TestWorld/8/L1/C6M65Q91PG5E7ML91VQZX3.uasset (D)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-11 14:52:39 -03:00
8 changed files with 65 additions and 6 deletions

View File

@@ -222,13 +222,33 @@ void AZeusPlayerProxy::SetZeusIdentity(const int64 InEntityId, const EZeusEntity
void AZeusPlayerProxy::ApplyEntitySnapshot(const FZeusEntitySnapshot& Snapshot) void AZeusPlayerProxy::ApplyEntitySnapshot(const FZeusEntitySnapshot& Snapshot)
{ {
// Estabiliza o offset de relogio na primeira amostra. Subsequente nao // 2026-06-06 H3 fix — disciplina o ServerClockOffsetMs via EMA com clamp.
// re-sincroniza para evitar saltos visuais; um esquema mais sofisticado // Antes: offset congelado na 1a amostra. Em cross-server handoff a fonte
// (ema do offset) cabe quando tivermos ping/RTT estabilizado por sessao. // do ServerTimeMs muda (publisher antigo -> novo) e o offset fica
// permanentemente skewed, alimentando tremor pos-handoff e drift cumulativo
// de clock cliente vs server. Agora cada amostra recalcula candidato e
// suaviza (alpha=0.02, ~50 amostras pra convergir) com clamp max 2ms/snapshot
// pra evitar salto visual durante transicao.
const int64 LocalNowMs = static_cast<int64>(FPlatformTime::Seconds() * 1000.0);
const int64 OffsetCandidate = LocalNowMs - Snapshot.ServerTimeMs;
if (SnapshotBuffer.Num() == 0) if (SnapshotBuffer.Num() == 0)
{ {
const int64 LocalNowMs = static_cast<int64>(FPlatformTime::Seconds() * 1000.0); // Primeira amostra: bootstrap direto (nao tem baseline pra suavizar).
ServerClockOffsetMs = LocalNowMs - Snapshot.ServerTimeMs; ServerClockOffsetMs = OffsetCandidate;
}
else
{
const double Alpha = 0.02;
const int64 RawTarget = static_cast<int64>(
static_cast<double>(ServerClockOffsetMs) * (1.0 - Alpha)
+ static_cast<double>(OffsetCandidate) * Alpha);
// Clamp max 2ms por amostra — em 30Hz isso converge a 60ms/s, mais que
// o suficiente pra absorver drift normal sem causar tremor visual.
const int64 Delta = RawTarget - ServerClockOffsetMs;
const int64 ClampedDelta = (Delta > 2) ? 2
: (Delta < -2) ? -2
: Delta;
ServerClockOffsetMs += ClampedDelta;
} }
FZeusProxySnapshot S; FZeusProxySnapshot S;

View File

@@ -3,6 +3,7 @@
#include "ZeusAOIComponent.h" #include "ZeusAOIComponent.h"
#include "ZeusNetworkSubsystem.h" // plugin ZeusNetwork: OnDebugAoiInfo + SendDebugAoiRequest #include "ZeusNetworkSubsystem.h" // plugin ZeusNetwork: OnDebugAoiInfo + SendDebugAoiRequest
#include "ZMMONetLog.h" // Batch 2.5: LogZeusAOI (categoria do modulo ZMMO)
#include "DrawDebugHelpers.h" #include "DrawDebugHelpers.h"
#include "Engine/GameInstance.h" #include "Engine/GameInstance.h"
@@ -107,6 +108,12 @@ void UZeusAOIComponent::HandleAoiConfig(float InterestCm, float DespawnCm)
{ {
InterestRadiusCm = InterestCm; InterestRadiusCm = InterestCm;
DespawnRadiusCm = DespawnCm; DespawnRadiusCm = DespawnCm;
// K4: log AOI cfg recebido do server (paridade com Net.AOI server-side
// "config_applied ZI=... ZD=..."). Cold path (1 por sessao).
UE_LOG(LogZeusAOI, Display,
TEXT("AOI cfg recv interestCm=%.0f (%.0fm) despawnCm=%.0f (%.0fm)"),
InterestCm, InterestCm / 100.0f,
DespawnCm, DespawnCm / 100.0f);
} }
bool UZeusAOIComponent::ResolveOverlay(EZeusAOIOverlay Overlay) const bool UZeusAOIComponent::ResolveOverlay(EZeusAOIOverlay Overlay) const

View File

@@ -1,8 +1,16 @@
// Copyright Epic Games, Inc. All Rights Reserved. // Copyright Epic Games, Inc. All Rights Reserved.
#include "ZMMO.h" #include "ZMMO.h"
#include "ZMMONetLog.h"
#include "Modules/ModuleManager.h" #include "Modules/ModuleManager.h"
IMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl, ZMMO, "ZMMO" ); IMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl, ZMMO, "ZMMO" );
DEFINE_LOG_CATEGORY(LogZMMO) DEFINE_LOG_CATEGORY(LogZMMO)
// Batch 2.5 — categorias vivem no modulo ZMMO (nao no plugin) por causa
// do adaptive non-unity build do UBT (ver ZMMONetLog.h).
DEFINE_LOG_CATEGORY(LogZeusHandoff)
DEFINE_LOG_CATEGORY(LogZeusAOI)
DEFINE_LOG_CATEGORY(LogZeusProxy)
DEFINE_LOG_CATEGORY(LogZeusHaloTransition)

24
Source/ZMMO/ZMMONetLog.h Normal file
View File

@@ -0,0 +1,24 @@
// =============================================================================
// ZMMONetLog.h — categorias UE_LOG do CLIENTE ZMMO (modulo Game), nao do plugin.
//
// Por que aqui e nao no plugin ZeusNetwork?
// UBT usa `git status` em Clients/ZMMO/ pra adaptive non-unity build. O plugin
// ZeusNetwork mora em Server/Plugins/Unreal/ZeusNetwork/ (OUTRO repo git),
// entao UBT nao enxerga mudancas la e pula o rebuild do plugin. Resultado:
// adicionar DECLARE_LOG_CATEGORY_EXTERN no header do plugin nao funciona
// sem rebuild manual do plugin (Build.bat). Solucao: categorias que vivem
// no proprio CLIENTE moram aqui — UBT enxerga e linka direto.
//
// Setup -logcmds="LogZeusHandoff Verbose, LogZeusAOI Verbose, LogZeusProxy Verbose"
// pra filtrar Output Log seletivamente.
//
// DEFINE_LOG_CATEGORY correspondente em ZMMO.cpp.
// =============================================================================
#pragma once
#include "CoreMinimal.h"
DECLARE_LOG_CATEGORY_EXTERN(LogZeusHandoff, Log, All);
DECLARE_LOG_CATEGORY_EXTERN(LogZeusAOI, Log, All);
DECLARE_LOG_CATEGORY_EXTERN(LogZeusProxy, Verbose, All);
DECLARE_LOG_CATEGORY_EXTERN(LogZeusHaloTransition, Log, All);