# 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)