Milestone 1: Proxy actors now equip remote players' right-hand weapons and play armed idle/locomotion poses when weaponDrawn=true. Adds rightHand weapon slot to equippedItems protocol, weapon graph variable support (iSyncGunDown: 0=drawn, 1=holstered), and weapon FSM events (weaponDraw, readyStateEnter, gunDownStateEnter). Fixes two critical animation state churn bugs: frustum-visibility flicker no longer resets animation state on every update tick, and idle proxies no longer register as changed on every frame. Includes WeaponBehavior graph loading via AIProcess::RequestLoadAnimationsForWeaponChange() and manual weapon attachment fallback. New docs/weapon-animation-sync.md reference guide maps animation events and variables from F4-Animation-Research. Experimental alert-state reassertion for armed proxies to test NPC combat state gating of weapon-ready poses.
251 lines
8.3 KiB
Markdown
251 lines
8.3 KiB
Markdown
# Packet Types
|
|
|
|
This document describes the currently implemented local prototype packet types
|
|
for Commonwealth Online.
|
|
|
|
The current protocol is newline-separated JSON over a local TCP connection to
|
|
`127.0.0.1:7777`.
|
|
|
|
Implemented packet types:
|
|
|
|
- `welcome`
|
|
- `transform`
|
|
- `disconnect`
|
|
|
|
The current system is still a local networking prototype. It is not playable
|
|
multiplayer yet.
|
|
|
|
## Implemented
|
|
|
|
### Welcome Packet
|
|
|
|
Sent from the server to a newly connected client after the server assigns a
|
|
`playerId`.
|
|
|
|
Example:
|
|
|
|
```json
|
|
{
|
|
"type": "welcome",
|
|
"playerId": 1,
|
|
"serverTime": 1780212098.457079
|
|
}
|
|
```
|
|
|
|
Fields:
|
|
|
|
```text
|
|
type Packet type. Always "welcome".
|
|
playerId Server-assigned player ID for this connection.
|
|
serverTime Server timestamp when the welcome packet was created.
|
|
```
|
|
|
|
The Fallout 4 plugin and `server/fake_client.py` receive this packet. The plugin
|
|
stores the assigned `playerId` for filtering its own future transform echoes.
|
|
|
|
### Transform Packet
|
|
|
|
Sent by the Fallout 4 plugin to the local server when the local player's
|
|
transform changes. The server adds `playerId` and `serverTime`, then broadcasts
|
|
the processed packet to other connected clients.
|
|
|
|
Example after server processing:
|
|
|
|
```json
|
|
{
|
|
"type": "transform",
|
|
"playerId": 2,
|
|
"x": -81781.26,
|
|
"y": 87962.61,
|
|
"z": 7696.05,
|
|
"angleZ": 3.20,
|
|
"movementType": "normal",
|
|
"cellId": "0000DD60",
|
|
"worldspaceId": "0000003C",
|
|
"clientTime": 1780212128.301,
|
|
"isMoving": true,
|
|
"isSprinting": false,
|
|
"isSneaking": false,
|
|
"isJumping": false,
|
|
"weaponDrawn": false,
|
|
"movementSpeed": 186.4,
|
|
"equippedItems": [
|
|
{ "slot": "body", "formId": "0001F66A" },
|
|
{ "slot": "headband", "formId": "" },
|
|
{ "slot": "rightHand", "formId": "0001F4A6" }
|
|
],
|
|
"appearance": {
|
|
"version": 3,
|
|
"raceFormId": "00013746",
|
|
"height": 1.0,
|
|
"morphWeight": { "thin": 0.0, "muscular": 0.0, "large": 0.0 },
|
|
"bodyTintColor": { "r": 255, "g": 220, "b": 190, "a": 255 },
|
|
"hairColorFormId": "",
|
|
"facialHairColorFormId": "",
|
|
"complexionFormId": "",
|
|
"headParts": [],
|
|
"morphs": [],
|
|
"morphRegions": [],
|
|
"facialBoneMorphs": [],
|
|
"tints": []
|
|
},
|
|
"serverTime": 1780212128.3011043
|
|
}
|
|
```
|
|
|
|
Fields:
|
|
|
|
```text
|
|
type Packet type. Always "transform".
|
|
playerId Server-assigned player ID for the sender.
|
|
x Local player X position.
|
|
y Local player Y position.
|
|
z Local player Z position.
|
|
angleZ Local player Z rotation angle.
|
|
movementType Movement classification for this transform update.
|
|
cellId Current cell form ID as a string.
|
|
worldspaceId Current worldspace form ID as a string.
|
|
clientTime Timestamp generated by the sending plugin.
|
|
isMoving Optional movement-state flag; defaults to false when missing.
|
|
isSprinting Optional movement-state flag; defaults to false when missing.
|
|
isSneaking Optional movement-state flag; defaults to false when missing.
|
|
isJumping Optional movement-state flag; defaults to false when missing.
|
|
weaponDrawn Optional weapon drawn state; defaults to false when missing.
|
|
movementSpeed Optional derived movement speed in game units per second; defaults to 0.0 when missing.
|
|
equippedItems Optional full snapshot of tracked visible apparel slots. Each entry has a string `slot` and hex-string `formId`; an empty `formId` means the slot is intentionally unequipped.
|
|
appearance Optional versioned player appearance snapshot for runtime proxy visuals.
|
|
serverTime Timestamp added by the server before broadcast.
|
|
```
|
|
|
|
Possible `movementType` values:
|
|
|
|
```text
|
|
normal
|
|
cell_change
|
|
worldspace_change
|
|
teleport
|
|
```
|
|
|
|
`cell_change` and `worldspace_change` are currently represented through
|
|
`movementType` inside transform packets. They are not separate packet types yet.
|
|
|
|
Movement state fields are data-only for now. Receivers must treat them as
|
|
optional and must not reject older transform packets when they are absent.
|
|
|
|
Equipment fields are optional and additive. When `equippedItems` is missing,
|
|
receivers keep their existing/default proxy appearance. When present, it is a
|
|
complete snapshot for the tracked visible apparel slots:
|
|
|
|
```text
|
|
hairTop
|
|
hairLong
|
|
body
|
|
underTorso
|
|
underLeftArm
|
|
underRightArm
|
|
underLeftLeg
|
|
underRightLeg
|
|
aboveTorso
|
|
aboveLeftArm
|
|
aboveRightArm
|
|
aboveLeftLeg
|
|
aboveRightLeg
|
|
headband
|
|
eyes
|
|
scalp
|
|
```
|
|
|
|
Tracked slots include 16 visible apparel BIPED slots (listed above) plus:
|
|
|
|
```text
|
|
rightHand Right-hand weapon slot (WEAP form ID); optional
|
|
```
|
|
|
|
The equipment-sync milestones send resolved `ARMO` and `WEAP` form IDs and
|
|
empty strings for unequipped tracked slots. Off-hand weapons, power armor,
|
|
weapon condition, legendary instance data, tint/material overrides, and forms
|
|
missing from the receiver load order are outside this packet extension.
|
|
|
|
When `rightHand` is missing, receivers keep their existing proxy weapon. When
|
|
present with an empty `formId`, the proxy is unequipped. When a `WEAP` form ID
|
|
is not found locally, receivers log a throttled warning and skip equipping
|
|
that weapon; the proxy continues with its current state and other sync intact.
|
|
|
|
Appearance fields are optional and additive. When `appearance` is missing,
|
|
receivers keep the proxy's existing/default body and face. When present, it is a
|
|
best-effort snapshot for male-proxy-compatible runtime visuals:
|
|
|
|
```text
|
|
version Appearance payload version. Current value is 3.
|
|
raceFormId Hex-string race form ID captured for diagnostics/future use; receivers do not switch proxy race yet.
|
|
height NPC height scalar.
|
|
morphWeight Body triangle weights: thin, muscular, and large.
|
|
bodyTintColor Body tint channels r, g, b, a as 0-255 integers.
|
|
hairColorFormId Hex-string `CLFM` form ID for hair color, or empty when unavailable.
|
|
facialHairColorFormId Hex-string `CLFM` form ID for facial hair color, or empty when unavailable.
|
|
complexionFormId Hex-string `TXST` form ID for skin complexion (hands, arms, body tone), or empty when unavailable.
|
|
headParts Hex-string `HDPT` form IDs for the sender's current head parts.
|
|
morphs Morph slider values as `{ "id": hex-string, "value": number }` entries.
|
|
morphRegions Region morph slider values as a flat array of numbers (added in version 2).
|
|
facialBoneMorphs Bone-based facial morphs as `{ "id": hex-string, "position": [x,y,z], "rotation": [x,y,z], "scale": [x,y,z] }` entries (added in version 2).
|
|
tints Character tint layers (skin tone, complexion, makeup, beard shade) as `{ "id": number, "type": number, "value": number, "color": hex-string, "swatch": number }` entries (added in version 3). `id` is the race tint-template uniqueID, `type` is the tint entry type (0 mask, 1 palette, 2 texture), and `value` is the 0-255 intensity. `color` (ARGB hex) and `swatch` are present only for palette entries.
|
|
```
|
|
|
|
`morphRegions` and `facialBoneMorphs` are optional and additive. Version 1
|
|
senders omit them; receivers must treat their absence as "no change" and keep
|
|
existing proxy morph data.
|
|
|
|
`tints` is optional and additive (added in version 3). Earlier senders omit it;
|
|
receivers must treat its absence as "no change". Receivers apply a tint by
|
|
matching `id` against an existing proxy tint entry (updating intensity/color in
|
|
place) and only reconstruct a missing entry when the proxy race exposes a
|
|
matching tint template, skipping unresolved tints rather than failing.
|
|
|
|
Receivers must treat missing or malformed appearance subfields as non-fatal and
|
|
apply only the parts they can resolve locally. Gender is intentionally not part
|
|
of this payload yet; female proxy support can add it later.
|
|
|
|
### Disconnect Packet
|
|
|
|
Sent by the server to remaining connected clients when a client disconnects.
|
|
|
|
Example:
|
|
|
|
```json
|
|
{
|
|
"type": "disconnect",
|
|
"playerId": 2,
|
|
"serverTime": 1780212134.0
|
|
}
|
|
```
|
|
|
|
Fields:
|
|
|
|
```text
|
|
type Packet type. Always "disconnect".
|
|
playerId Server-assigned player ID that disconnected.
|
|
serverTime Server timestamp when the disconnect packet was created.
|
|
```
|
|
|
|
The Fallout 4 plugin and `server/fake_client.py` use this packet to remove
|
|
remote players from their in-memory state tables.
|
|
|
|
## Receiver Behavior
|
|
|
|
The Fallout 4 plugin and fake client both receive:
|
|
|
|
```text
|
|
welcome
|
|
transform
|
|
disconnect
|
|
```
|
|
|
|
The plugin receive loop runs on a background networking thread. It stores plain
|
|
remote-player state only and does not spawn actors or touch Fallout 4 game
|
|
objects.
|
|
|
|
## Planned Later
|
|
|
|
- Remote actor spawning.
|
|
- Gameplay synchronization.
|