Skip to content

feat(sdk): built-in clock synchronization with the authoritative server - #1628

Draft
LautaroPetaccio wants to merge 1 commit into
auth-serverfrom
feat/network-clock-sync
Draft

LautaroPetaccio wants to merge 1 commit into
auth-serverfrom
feat/network-clock-sync

Conversation

@LautaroPetaccio

Copy link
Copy Markdown
Contributor

Summary

Adds a built-in ping exchange between clients and the authoritative server and exposes the result from @dcl/sdk/network:

  • getServerTime(): estimated server clock in ms, undefined until the first reply. On the server it returns the server's own clock.
  • getClockSyncStats(): offset, latest round trip, best round trip, sample count and whether the estimate is frozen.
  • freezeServerClock(frozen): hold the estimate so a time-critical sequence cannot jump mid-way; samples keep flowing so the estimate is fresh when released.
  • getPlayerLatency(userId): server only, a player's last measured round trip, for bounding how early a client may claim an input happened.

Why

Multiplayer scenes that schedule shared events or validate client timestamps on the server have no clock they can agree on today: the room exposes no server time, round trip or per-player latency. Every such scene ends up rebuilding the same exchange, and getting it wrong only shows under load. This came out of a rhythm game built on the auth server, where the same mechanism was needed for the song clock, the note timestamps and the server's input-plausibility window.

How it works

Clients ping every 1.5 s with their own stamp; the server answers only that client, echoing the stamp and adding its own; the client acknowledges so the server can measure its round trip as well. Each reply yields a round trip and an offset (server minus local at the trip midpoint). The offset from the fastest of the last ten replies wins, since a fast trip is the least distorted by queueing. Stale replies (not matching the latest ping), negative and oversized trips are dropped; the server rate-limits pings to one per 800 ms per player and clamps its own measurement.

The exchange rides on the existing room with three reserved message names (__sdkClockPing, __sdkClockPong, __sdkClockAck), so it inherits queueing until the room is ready and the server-only-sender rule on clients. Scene registries cannot collide with the names. Players are forgotten on onLeaveScene.

Base branch

auth-server: the room, the message registry and the server-side event path exist only there; main has no Room.

Testing

test/sdk/network/clock-sync.spec.ts: 34 cases across the estimator (midpoint offset, best-of-window, outlier rejection, freeze/unfreeze), the server registry (rate limit, ack matching, clamping, forget) and the wiring against a room double (ping cadence, room readiness, stale replies, server answers only the sender, no pings on the server). The existing network transport, messages and two-transport connectivity specs pass alongside it.

Follow-ups

  • An ADR describing the exchange and the API, in the style of the audio playback position one (ADR-318: Audio playback position reports adr#324).
  • Once merged, the rhythm game can drop its hand-rolled ping and read getServerTime() / getPlayerLatency().

Multiplayer scenes that schedule anything shared (countdowns, song positions,
race starts) or validate client timestamps on the server had no clock they
could agree on: the room exposes no server time, no round trip and no per-player
latency, so every scene rebuilt the same ping exchange by hand.

The sync transport now runs a reserved exchange over the room. Clients ping
every 1.5 s with their own stamp; the server answers only that client with the
stamp echoed plus its own; the client acknowledges so the server measures its
round trip too. Each reply yields a round trip and an offset (server minus local
at the trip midpoint); the offset from the fastest of the last ten replies wins,
because a fast trip is the least distorted by queueing. Stale replies, negative
and oversized trips are dropped, and the server rate-limits pings per player.

Scene-facing API in @dcl/sdk/network:
- getServerTime(): estimated server clock, undefined until the first reply
  (the server returns its own clock)
- getClockSyncStats(): offset, rtt, best rtt, sample count, frozen flag
- freezeServerClock(frozen): hold the estimate so a running performance
  cannot jump; samples keep flowing for when it is released
- getPlayerLatency(userId): server only, the player's last measured round trip

The reserved message names start with a double underscore so scene registries
cannot collide with them. Players are forgotten when they leave the scene.

Tests cover the estimator, the server registry and the wiring against a room
double (34 cases); the existing network transport and connectivity specs pass.
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying js-sdk-toolchain with  Cloudflare Pages  Cloudflare Pages

Latest commit: ceaffef
Status: ✅  Deploy successful!
Preview URL: https://ec14d7d6.js-sdk-toolchain.pages.dev
Branch Preview URL: https://feat-network-clock-sync.js-sdk-toolchain.pages.dev

View logs

@github-actions

Copy link
Copy Markdown
Contributor

Test this pull request

  • The @dcl/sdk package can be tested in scenes by running

    npm install "https://sdk-team-cdn.decentraland.org/@dcl/js-sdk-toolchain/branch/feat/network-clock-sync/dcl-sdk-7.29.1-35116602724.commit-daaba71.tgz"
  • The @dcl/js-runtime package can be tested in scenes by running

    npm install "https://sdk-team-cdn.decentraland.org/@dcl/js-sdk-toolchain/branch/feat/network-clock-sync/@dcl/js-runtime/dcl-js-runtime-7.29.1-35116602724.commit-daaba71.tgz"
  • To test with npx init

    export SDK_COMMANDS="https://sdk-team-cdn.decentraland.org/@dcl/js-sdk-toolchain/branch/feat/network-clock-sync/dcl-sdk-commands-7.29.1-35116602724.commit-daaba71.tgz"
    npx $SDK_COMMANDS init
  • The /changerealm command to test test in-world

    /changerealm https://sdk-team-cdn.decentraland.org/ipfs/feat/network-clock-sync-e2e
    
  • You can preview this build entering:
    https://playground.decentraland.org/?sdk-branch=feat/network-clock-sync

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant