7.5 KiB
7.5 KiB
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
{
"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.
{
"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
RemotePlayerStatestruct 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
PerformActionhook inmain.cppto 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:
- Hook
ActorMediator::PerformAction()or equivalent - Capture the following:
- Action type (enum)
- Event name (string)
- Animation variable cache snapshot
- Actor state flags (flags1, flags2)
- Timestamp
- Queue the action event in
RemotePlayerState.actionEvents - Serialize to JSON
actionEventsarray in next transform packet - Transmit via relay server to other clients
- 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)