Implement host-authoritative time/weather sync across server and clients. Adds a new worldState packet (and worldStateHost for host assignment) and server-side host tracking/reassignment to the lowest remaining playerId when the host disconnects. Plugin-side changes introduce F4T::WorldStateSync (new header + implementation), polling/apply hooks in the game-thread main loop, and Networking support to send/receive/parse worldState/worldStateHost packets. The server validates host-only sends, relays worldState to peers, broadcasts worldStateHost on host handoff, and augments logging/stats. Protocol and documentation files updated (protocol/world-state.md, docs/*) and server/fake_client.py extended to track/print host world-state snapshots.
2.7 KiB
2.7 KiB
World State Packet
Host-authoritative time and weather sync for the Commonwealth Online prototype.
Authority
- The server tracks a
worldStateHostPlayerIdfor the session. - The first connected client becomes host. When the host disconnects, the server
reassigns host to the lowest remaining
playerIdand broadcastsworldStateHost. - Only the current host sends
worldStatepackets. - 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",
"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.0–24.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 |
clientTime |
number (double) | no | Sender Unix epoch seconds |
serverTime |
number (double) | no | Relay timestamp added by server |
Send Cadence (Host)
- About 1 Hz heartbeat while connected
- Immediate send when
weatherFormIdchanges - Immediate send when
gameHourorgameDaysPassedjumps beyond a small threshold (wait, sleep, console time changes)
Client Apply Rules
- Time: non-host clients snap local calendar globals when host values differ beyond threshold.
- 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.