Investigation summary: - Tested velocity-based animation triggering: graph variable writes, animation events, character controller velocity manipulation, and Move() API calls - All approaches failed: animations do not play on SetPosition-backed proxy actors - Root cause: Fallout 4's animation system requires active AIProcess-driven locomotion packages, which are incompatible with networked puppet actors - Fallout 4's animation system fundamentally ties animation evaluation to the character controller's actual velocity AND active AI-driven locomotion state - PlaceAtMe proxies updated via SetPosition cannot provide either requirement Proxy actors currently work correctly for: - Smooth position synchronization - Heading/rotation updates - Jump animations (via Z-position) - Network sync and lifecycle What remains impossible without deep engine access: - Walk/run/sneak animation playback - Animation graph variable manipulation affecting behavior - Character controller velocity synthesis for puppets Recommendation: Accept this architectural limitation and provide alternative visual feedback (particles, glows, state indicators) instead of animations. Files changed: - plugin/src/F4TProxyActorController.cpp: Reverted to SetPosition-only approach - docs/dev-log.md: Added comprehensive investigation summary and conclusions Co-authored-by: Cursor <cursoragent@cursor.com>
139 lines
5.4 KiB
Markdown
139 lines
5.4 KiB
Markdown
# Player Sync
|
|
|
|
Player synchronization is the first major technical goal for Fallout 4 Together.
|
|
The current implementation is still a local networking prototype.
|
|
|
|
## Current Implemented Flow
|
|
|
|
```text
|
|
Fallout 4 Plugin ↔ Local Python Server ↔ Other Clients
|
|
↓
|
|
Plugin Remote Player State
|
|
```
|
|
|
|
The Fallout 4 plugin currently reads the local player's transform and sends it
|
|
to the local Python server. Normal movement sends are throttled separately from
|
|
local movement logs: while the player is moving, the plugin targets roughly
|
|
10 Hz and skips sends until position or rotation changes meaningfully. The
|
|
server assigns a `playerId`, adds `serverTime`, and broadcasts transform packets
|
|
to other connected clients.
|
|
|
|
The Fallout 4 plugin also receives server packets on a background networking
|
|
thread. It stores its assigned `playerId`, stores remote transform state by
|
|
remote `playerId`, and removes remote state when disconnect packets arrive.
|
|
`server/fake_client.py` remains a lightweight test receiver for the same packet
|
|
lifecycle.
|
|
|
|
The plugin's game-thread proxy actor controller can now read a copied snapshot
|
|
of this remote-player state and move the single placed test proxy actor
|
|
`F4TProxyRemotePlayer01REF` in `F4TTestCell01`. The networking thread still does
|
|
not touch Fallout 4 actors or references. If no valid same-cell remote player is
|
|
available, the controller moves the placed proxy to a hidden holding position
|
|
inside the same test cell instead of leaving it frozen at the last represented
|
|
position.
|
|
|
|
## Current Remote Player State Model
|
|
|
|
The plugin and fake client store remote players by `playerId`.
|
|
|
|
Each remote player state entry tracks:
|
|
|
|
```text
|
|
playerId
|
|
x
|
|
y
|
|
z
|
|
angleZ
|
|
movementType
|
|
cellId
|
|
worldspaceId
|
|
clientTime
|
|
serverTime
|
|
isMoving
|
|
isSprinting
|
|
isSneaking
|
|
isJumping
|
|
weaponDrawn
|
|
movementSpeed
|
|
lastReceivedLocalTime
|
|
```
|
|
|
|
Required transform packet fields are `type`, `playerId`, `x`, `y`, `z`,
|
|
`angleZ`, and `cellId`. `worldspaceId`, `movementType`, `clientTime`, and
|
|
`serverTime` are optional; interior test cells may omit `worldspaceId`.
|
|
Movement state fields are also optional and default to not moving, not
|
|
sprinting, not sneaking, not jumping, weapon holstered, and speed `0.0` when
|
|
missing.
|
|
|
|
This model proves the data shape and lifecycle before spawning remote actors.
|
|
For the current visual milestone, the controller represents only one remote
|
|
player and chooses the lowest available `playerId` if more than one remote
|
|
player exists.
|
|
|
|
## Implemented
|
|
|
|
- The plugin sends local player transform packets.
|
|
- Normal transform sends are independent from movement logging and are
|
|
thresholded to avoid packet-per-frame traffic.
|
|
- The plugin receives `welcome` packets and stores its assigned `playerId`.
|
|
- The plugin receives broadcast transform packets.
|
|
- The plugin ignores transform packets for its own assigned `playerId`.
|
|
- The plugin stores remote player state by `playerId`.
|
|
- The plugin receives `disconnect` packets and removes remote player state.
|
|
- The server assigns incrementing `playerId` values.
|
|
- The server sends `welcome` packets.
|
|
- The server adds `playerId` and `serverTime` to transform packets.
|
|
- The server broadcasts transform packets to other connected clients.
|
|
- The fake client stores remote player state by `playerId`.
|
|
- The server broadcasts `disconnect` packets.
|
|
- The fake client removes disconnected remote players from its state table.
|
|
- The plugin moves the single placed test proxy actor from a thread-safe
|
|
remote-player snapshot on the game-thread update path.
|
|
- Normal proxy movement is smoothed toward the latest received target, while
|
|
`cell_change`, `worldspace_change`, and `teleport` transforms snap directly to
|
|
avoid slow movement across large discontinuities.
|
|
- The game-thread proxy controller holds the single proxy at a hidden in-cell
|
|
position when the represented player disconnects, disappears, or leaves the
|
|
local test cell, then snaps back to a valid same-cell remote player before
|
|
resuming smoothing.
|
|
- Transform packets include movement state data (`isMoving`, `movementSpeed`,
|
|
`isSprinting`, `isSneaking`, `isJumping`, `weaponDrawn`).
|
|
- The game-thread proxy controller applies confirmed Havok animation graph
|
|
variables to runtime proxy actors from that remote state (locomotion, sneak,
|
|
jump, weapon drawn). Transform position/heading sync is unchanged.
|
|
|
|
## Transform Cadence
|
|
|
|
Normal local movement currently uses three separate rates:
|
|
|
|
- Network sends target roughly 10 Hz while the player is moving.
|
|
- Sends are skipped until position changes by about 3 game units or rotation
|
|
changes by about 0.02 radians.
|
|
- Local movement logs stay much slower, around once per second, so
|
|
`Fallout4Together.log` remains readable.
|
|
|
|
`cell_change`, `worldspace_change`, and `teleport` movement types bypass the
|
|
normal send interval and are sent immediately. The receiving plugin still stores
|
|
remote transform state on the networking thread and moves actors only from the
|
|
game-thread proxy controller.
|
|
|
|
Runtime proxy actors are spawned per remote `playerId` in the test cell (see
|
|
architecture). Full animation graph replication is not implemented; only curated
|
|
graph variable writes drive proxy locomotion visuals.
|
|
|
|
## Planned Later
|
|
|
|
- Add one proxy actor per remote player after the single-proxy test is stable.
|
|
- Dynamically spawn remote actors only after placed-proxy movement is stable.
|
|
|
|
## Not Required Yet
|
|
|
|
- Full animation graph sync
|
|
- Combat sync
|
|
- Inventory sync
|
|
- Quest sync
|
|
- Dialogue sync
|
|
- Settlement sync
|
|
- Accurate physics sync
|
|
- Public networking or matchmaking
|