Files
Commonwealth-Online-Public/docs/protocol-phase2-extensions.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

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