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.
221 lines
7.5 KiB
Markdown
221 lines
7.5 KiB
Markdown
# F4T Network Protocol - Phase 2 Extensions
|
|
|
|
## Overview
|
|
|
|
This document describes the Phase 2 protocol extensions to support actor state flags and action events for TiltedEvolution-style animation synchronization.
|
|
|
|
---
|
|
|
|
## Transform Packet Format (Extended)
|
|
|
|
### JSON Structure
|
|
|
|
```json
|
|
{
|
|
"type": "transform",
|
|
"playerId": 1,
|
|
"x": 1234.5,
|
|
"y": 5678.9,
|
|
"z": 100.0,
|
|
"angleZ": 45.0,
|
|
"cellId": "Sanctuary",
|
|
"worldspaceId": "Commonwealth",
|
|
"movementType": "normal",
|
|
"isMoving": true,
|
|
"isSprinting": false,
|
|
"isSneaking": false,
|
|
"isJumping": false,
|
|
"weaponDrawn": false,
|
|
"movementSpeed": 180.5,
|
|
"clientTime": 1717424000.123,
|
|
"serverTime": 1717424000.456,
|
|
|
|
"actorStateFlags1": 0x00000042,
|
|
"actorStateFlags2": 0x00000001,
|
|
|
|
"actionEvents": [
|
|
{
|
|
"type": 0,
|
|
"eventName": "ActorMovementStart",
|
|
"animationVariablesFloat": [85.0, 90.5, ...],
|
|
"animationVariablesBool": [true, false, ...],
|
|
"animationVariablesInt": [1, 0, ...],
|
|
"state1": 0x00000042,
|
|
"state2": 0x00000001,
|
|
"captureTime": 1717424000.456
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
### Field Definitions
|
|
|
|
#### Phase 1 Fields (Existing)
|
|
|
|
| Field | Type | Required | Description |
|
|
|-------|------|----------|-------------|
|
|
| `type` | String | YES | Packet type; must be `"transform"` |
|
|
| `playerId` | Integer (uint32) | YES | Player ID (from welcome packet) |
|
|
| `x`, `y`, `z` | Number (float) | YES | Position in game world |
|
|
| `angleZ` | Number (float) | YES | Rotation heading (degrees) |
|
|
| `cellId` | String | YES | Cell form ID or name |
|
|
| `worldspaceId` | String | NO | Worldspace ID (empty for cells) |
|
|
| `movementType` | String | NO | Movement type: `"normal"`, `"cell_change"`, `"worldspace_change"`, `"teleport"` (default: `"normal"`) |
|
|
| `isMoving` | Boolean | NO | Local player is moving (default: false) |
|
|
| `isSprinting` | Boolean | NO | Local player is sprinting (default: false) |
|
|
| `isSneaking` | Boolean | NO | Local player is sneaking (default: false) |
|
|
| `isJumping` | Boolean | NO | Local player is jumping (default: false) |
|
|
| `weaponDrawn` | Boolean | NO | Local player has weapon drawn (default: false) |
|
|
| `movementSpeed` | Number (float) | NO | Movement speed in game units/frame (default: 0.0) |
|
|
| `clientTime` | Number (double) | NO | Client-side timestamp (optional) |
|
|
| `serverTime` | Number (double) | NO | Server timestamp for diagnostics (optional) |
|
|
|
|
#### Phase 2 Fields (New - Conditionally Optional)
|
|
|
|
| Field | Type | Required | Description |
|
|
|-------|------|----------|-------------|
|
|
| `actorStateFlags1` | Integer (uint32) | NO | Actor state flags word 1 (combat, animation state, etc.) (default: 0) |
|
|
| `actorStateFlags2` | Integer (uint32) | NO | Actor state flags word 2 (additional flags) (default: 0) |
|
|
| `actionEvents` | Array of ActionEvent | NO | Queue of action events (default: empty) |
|
|
|
|
#### Phase 2 ActionEvent Structure
|
|
|
|
| Field | Type | Description |
|
|
|-------|------|-------------|
|
|
| `type` | Integer (uint32) | Action type (enum value from `ActorMediator`) |
|
|
| `eventName` | String | Human-readable action name (e.g., `"ActorMovementStart"`) |
|
|
| `animationVariablesFloat` | Array of Numbers | Float variable cache snapshot (indices correspond to descriptor table) |
|
|
| `animationVariablesBool` | Array of Booleans | Bool variable cache snapshot |
|
|
| `animationVariablesInt` | Array of Integers | Int variable cache snapshot |
|
|
| `state1` | Integer (uint32) | Actor state flags word 1 at action capture |
|
|
| `state2` | Integer (uint32) | Actor state flags word 2 at action capture |
|
|
| `captureTime` | Number (double) | Server timestamp when action was captured |
|
|
|
|
---
|
|
|
|
## World State Packet
|
|
|
|
Host-authoritative time and weather sync uses a separate `worldState` packet.
|
|
Full field definitions live in [`protocol/world-state.md`](../protocol/world-state.md).
|
|
|
|
```json
|
|
{
|
|
"type": "worldState",
|
|
"playerId": 1,
|
|
"gameHour": 14.25,
|
|
"gameDaysPassed": 12.5,
|
|
"weatherFormId": "00123456",
|
|
"timeSync": true,
|
|
"clientTime": 1717424000.123,
|
|
"serverTime": 1717424000.456
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Backward Compatibility
|
|
|
|
**Important:** The protocol is **backward compatible**. Existing clients that don't send Phase 2 fields will continue to work:
|
|
|
|
- Missing `actorStateFlags1/2` → Defaults to 0 (neutral state)
|
|
- Missing `actionEvents` → Defaults to empty array (no discrete actions)
|
|
|
|
**Action:** Old clients can upgrade incrementally; no immediate protocol version bump required.
|
|
|
|
---
|
|
|
|
## Implementation Timeline
|
|
|
|
### Phase 2.1 (DONE)
|
|
- ✅ Extend `RemotePlayerState` struct with actor state flags and action event queue
|
|
- ✅ Update `HandleTransformPacket()` to parse new optional fields
|
|
- ✅ Add logging for actor state flags
|
|
|
|
### Phase 2.2 (IN PROGRESS)
|
|
- Update server relay (`server/server.py`) to pass through new fields
|
|
- Update fake test client (`server/fake_client.py`) to populate new fields
|
|
- Create protocol documentation (this file)
|
|
|
|
### Phase 2.3 (PENDING)
|
|
- Add `PerformAction` hook in `main.cpp` to capture action events
|
|
- Serialize action events into JSON for transmission
|
|
- Test end-to-end action event capture and relay
|
|
|
|
---
|
|
|
|
## Actor State Flags Reference
|
|
|
|
### ActorState.flags1 (Common States)
|
|
|
|
Bitfield values for `actor→actorState.flags1`:
|
|
|
|
| Bit | Meaning | Animation Impact |
|
|
|-----|---------|-----------------|
|
|
| 0 | In combat | Triggers combat locomotion graph |
|
|
| 1 | Is sneaking | Triggers sneak locomotion (crouch) |
|
|
| 2 | Is attacking | Plays attack animations |
|
|
| 3 | Is fleeing | Triggers run/fleeing animations |
|
|
| 4 | Is sprinting | Redundant with isSprinting (graph driven) |
|
|
| 5 | Force walk | Locks to walk speed |
|
|
| 6 | Force run | Forces run speed |
|
|
| 7-31 | (various) | Context-dependent |
|
|
|
|
**Note:** Exact bit meanings should be verified in `RE/H/ActorState.h` in CommonLibF4.
|
|
|
|
### ActorState.flags2 (Additional States)
|
|
|
|
Bitfield values for `actor→actorState.flags2`:
|
|
|
|
| Bit | Meaning | Animation Impact |
|
|
|-----|---------|-----------------|
|
|
| 0-31 | (to be determined) | (to be determined) |
|
|
|
|
---
|
|
|
|
## Action Event Capture (Phase 2.3)
|
|
|
|
When a local player triggers an action, F4T will:
|
|
|
|
1. **Hook** `ActorMediator::PerformAction()` or equivalent
|
|
2. **Capture** the following:
|
|
- Action type (enum)
|
|
- Event name (string)
|
|
- Animation variable cache snapshot
|
|
- Actor state flags (flags1, flags2)
|
|
- Timestamp
|
|
3. **Queue** the action event in `RemotePlayerState.actionEvents`
|
|
4. **Serialize** to JSON `actionEvents` array in next transform packet
|
|
5. **Transmit** via relay server to other clients
|
|
6. **Replay** on proxy actors (Phase 5.2)
|
|
|
|
**Example Actions to Capture:**
|
|
- Idle animations (start/stop moving)
|
|
- Combat idles (stand ready, weapon draw)
|
|
- Equipping/unequipping items
|
|
- Using objects
|
|
- Emotes
|
|
- Crafting/trading
|
|
|
|
---
|
|
|
|
## Testing Checklist
|
|
|
|
- [ ] F4T plugin compiles with extended RemotePlayerState
|
|
- [ ] Fake client can send packets with actor state flags
|
|
- [ ] Relay server passes through new fields without error
|
|
- [ ] F4T parses actor state flags from incoming packets
|
|
- [ ] Logging shows actor state flags in remote update messages
|
|
- [ ] Phase 2.3: PerformAction hook implemented and tested
|
|
- [ ] Phase 2.3: Action events captured and serialized
|
|
- [ ] Phase 2.3: End-to-end action event relay verified
|
|
|
|
---
|
|
|
|
## Future Considerations
|
|
|
|
- **Action Queue Size:** Limit to prevent excessive bandwidth (recommend: 8-16 pending actions per player)
|
|
- **Compression:** Animation variable arrays could be compressed/delta-encoded if bandwidth becomes limiting
|
|
- **Animation Variable Descriptors:** Index-based approach (Phase 3) will replace string names
|
|
- **Thread Safety:** Ensure RemotePlayerState modifications are thread-safe (already using mutex in Phase 1)
|
|
|