Files
Commonwealth-Online-Public/protocol/world-state.md
T
andrew 70933db6e5 Event-driven world time sync (timeSync flag)
Introduce an optional timeSync field and event-driven time pushes so host time is only re-applied on connect, resume-from-freeze, or large jumps while weather continues on ~1 Hz heartbeats. Added IsGameTimeFrozen detection and client buffering of latest host time; clients apply host time only when timeSync is true or when gameplay just resumed. Updated networking to read/write timeSync, adjusted F4TWorldStateSync logic (separate time vs weather send/apply paths, changed function signatures and behavior), and updated protocol/docs/changelog and fake_client logging to reflect the new field and behavior.
2026-06-23 15:55:05 +12:00

3.3 KiB
Raw Blame History

World State Packet

Host-authoritative time and weather sync for the Commonwealth Online prototype.

Authority

  • The server tracks a worldStateHostPlayerId for the session.
  • The first connected client becomes host. When the host disconnects, the server reassigns host to the lowest remaining playerId and broadcasts worldStateHost.
  • Only the current host sends worldState packets.
  • The Python relay validates the sender and broadcasts to all other clients.

Promoted hosts keep their already-synced local time and weather; reassignment should not change world state for remaining clients.

worldStateHost Packet

Sent in welcome (initial host) and broadcast when the host disconnects:

{
  "type": "worldStateHost",
  "worldStateHostPlayerId": 2,
  "serverTime": 1780212128.301
}
Field Type Required Description
type string yes Must be "worldStateHost"
worldStateHostPlayerId integer yes Connected player ID that is now world-state host
serverTime number (double) no Relay timestamp

The welcome packet also includes optional worldStateHostPlayerId so new joiners know the current host immediately.

Packet Format

{
  "type": "worldState",
  "playerId": 1,
  "gameHour": 14.25,
  "gameDaysPassed": 12.5,
  "weatherFormId": "00123456",
  "timeSync": true,
  "clientTime": 1780212128.301,
  "serverTime": 1780212128.301
}

Field Definitions

Field Type Required Description
type string yes Must be "worldState"
playerId integer yes (server) Injected by server; identifies the sending host
gameHour number (float) yes In-game hour of day, 0.024.0
gameDaysPassed number (float) yes In-game days passed global
weatherFormId string no 8-character hex TESWeather form ID; omitted or "" when host is indoors
timeSync boolean no When true, non-host clients should apply gameHour and gameDaysPassed. Omitted or false on weather-only heartbeats.
clientTime number (double) no Sender Unix epoch seconds
serverTime number (double) no Relay timestamp added by server

Send Cadence (Host)

  • About 1 Hz heartbeat for weather while connected (timeSync omitted or false)
  • Immediate send with timeSync: true when:
    • First connected or promoted to host
    • Game time resumes after a freeze (menus, pip-boy, dialogue, loading screens, save/load, wait/sleep)
    • gameHour or gameDaysPassed jumps beyond a small threshold (wait, sleep, console time changes)
  • Immediate send when weatherFormId changes (includes current time fields; sets timeSync only when time also changed or on resume)

Client Apply Rules

  • Time: non-host clients track the latest host time from every packet but only snap local calendar globals when timeSync is true and gameplay time is not frozen, or immediately when gameplay time resumes after a freeze (using the latest stored host values).
  • Weather: non-host clients apply forced weather only when the local player is in an exterior cell.
  • Missing optional fields use safe defaults on receive.

Backward Compatibility

Older clients that do not send or parse worldState continue to work. Transform sync is unchanged.