Add new Papyrus scripts (CoSync.psc, CoSyncPlayer.psc, CoSyncQuest.psc) providing native bindings and quest/player proxy logic for networking: connection/session APIs, entity management, world/weather/workshop/companion sync, consumable/pipboy/door/power-armor events, and proxy animation/initialization handlers. Refactor the proxy actor controller (F4TProxyActorController.cpp) to support dynamic/runtime proxy spawning and improved lifecycle/visibility handling: change proxy base/form IDs to COPlayerProxy/CommonwealthOnline, add process-list actor lookup, spawn helpers, play-space/cell checks, 3D visibility refresh, ActorHandle usage, and spawn mutexes and globals for safer spawning. Update docs and setup (architecture.md, limitations.md, plugin/setup.md) to describe global dynamic spawn behavior, max runtime proxies (kMaxRuntimeProxyActors = 4), and validation checklist; update launch-two-fallout4.bat to show instance log locations. Misc: small tweaks to logging and debug flags to gate diagnostics.
140 lines
5.5 KiB
Markdown
140 lines
5.5 KiB
Markdown
# Player Sync
|
|
|
|
Player synchronization is the first major technical goal for Commonwealth Online.
|
|
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 reads a copied snapshot of this
|
|
remote-player state and dynamically spawns/moves runtime proxy actors in the
|
|
local player's current loaded cell. The networking thread still does not touch
|
|
Fallout 4 actors or references. If a remote player is not in the same
|
|
cell/worldspace, the controller holds that player's proxy at a hidden in-cell
|
|
position instead of leaving it at the last visible location.
|
|
|
|
## 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
|
|
`CommonwealthOnline.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 dynamically spawned per remote `playerId` in the local
|
|
player's current loaded cell (see architecture). Remote players in a different
|
|
cell are held at a hidden in-cell position until the local player enters their
|
|
cell. Full animation graph replication is not implemented; only curated graph
|
|
variable writes drive proxy locomotion visuals.
|
|
|
|
## Planned Later
|
|
|
|
- Increase concurrent proxy cap beyond four players per client
|
|
- Cross-cell actor transfer without respawn (if respawn proves insufficient)
|
|
|
|
## Not Required Yet
|
|
|
|
- Full animation graph sync
|
|
- Combat sync
|
|
- Inventory sync
|
|
- Quest sync
|
|
- Dialogue sync
|
|
- Settlement sync
|
|
- Accurate physics sync
|
|
- Public networking or matchmaking
|