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

83 lines
3.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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:
```json
{
"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
```json
{
"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.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 |
| `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.