Files
Commonwealth-Online-Public/protocol/packets.md
T
andrew 89c3e82554 Implement proxy weapon animation sync for ranged guns
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.
2026-07-02 17:23:09 +12:00

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.