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>
This commit is contained in:
2026-06-08 20:36:28 -03:00
parent e87b2cec6c
commit f21d059b67
5 changed files with 25 additions and 5 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;