Add appearance tint syncing (appearance v3)

Introduce tint syncing for skin/complection/makeup/beard by adding RemoteAppearanceTint and a tints vector, and bump appearance payload version to 3. Parse/emit tints in networking and update fake server/client test payloads. Capture live Player/NPC tintingData during appearance capture. Apply tints to proxies by updating existing entries or reconstructing missing ones via template resolution and MakeTintEntry (game allocator + vtable fixup); unresolved templates are skipped with throttled warnings. Update docs/changelog and add logging for applied tints. Backwards compatible with v1/v2 packets.
This commit is contained in:
2026-06-30 22:07:49 +12:00
parent 5b54db64d4
commit 76fd8772bd
10 changed files with 317 additions and 9 deletions
+38
View File
@@ -9,6 +9,44 @@ failed experiments, successful tests, and next steps.
---
## 2026-06-30 - Skin Colour / Tint Appearance Sync (Appearance v3, Phase 2)
### Summary
Phase 2 of the appearance fix: sync character tints so proxies mirror skin tone, complexion, makeup, dirt, scars, and beard shade. These live in `tintingData` (a `BGSCharacterTint::Entries` list of polymorphic entries), which was never captured or applied, so skin colour stayed default. Bumped the appearance payload to version 3.
### Files Changed
- `plugin/include/F4TRemotePlayerState.h`
- `plugin/src/main.cpp`
- `plugin/src/F4TNetworking.cpp`
- `plugin/src/F4TProxyActorController.cpp`
- `protocol/packets.md`
- `server/fake_client.py`
- `server/fake_player.py`
- `changelog.md`
- `docs/dev-log.md`
### Details
- Added `RemoteAppearanceTint { id, type, value, color, swatch, hasColor }` and a `tints` vector to `RemoteAppearance`; default `version` is now 3.
- Capture reads the live chargen tint list from `PlayerCharacter::tintingData` (falling back to the baked `TESNPC::tintingData`), recording each entry's `idLink`, `tingingValue`, and `EntryType`, plus `tintingColor`/`swatchID` for palette entries.
- Apply prefers updating an existing proxy tint entry in place when `idLink` matches (no allocation, vtable/template already valid). When no entry matches, it resolves the proxy race's tint template by `uniqueID` via `TESRace::FaceRelatedData::tintingTemplate` and reconstructs the entry.
- Reconstruction uses the game allocator (`RE::malloc`) and pokes the correct vtable from the `BGSCharacterTint::*Entry::VTABLE` relocation IDs, because the entry classes are `__declspec(novtable)` and a plain `new` would crash facegen. Unresolved templates are skipped with a throttled warning rather than creating a dangling entry.
- The apply success log now also reports `tints=applied/sent`.
### Testing
- Static review and linter only; `python -m py_compile` passes for the harness files. No in-game run yet.
- Recommended manual check: run the Python server, connect two Fallout 4 instances with visibly different skin tones, and confirm each proxy's skin colour/complexion now matches the remote player, with a log line like `tints=NN/NN` and no `Skipped tint` template warnings.
- Backwards compatibility: confirm version 1/2 packets (without `tints`) still apply without error.
### Known Issues
- Tint rendering depends on facegen re-reading `tintingData` during the existing head/face 3D refresh; if skin tone does not visibly change despite `tints=applied`, a dedicated facegen/complexion regen step may be needed.
- Reconstructed entries are appended and not removed if the sender later drops a tint; tint idLinks are stable in practice, so this is cosmetic.
- Appearance data is still written to the proxy's shared base `TESNPC`, so 3+ proxies per client could clobber (pre-existing limitation).
### Next Steps
- Verify skin colour in-game; if facegen does not pick up tints from the standard refresh, add a targeted tint/complexion regeneration call.
---
## 2026-06-30 - Head-Part Count Mismatch Fix (Hair Sync)
### Summary