BREAKTHROUGH INSIGHT: The user reported: AI goes aggressive then immediately returns to neutral. This matches the symptom of SetPosition() breaking the character controller. ROOT CAUSE: - We were calling SetPosition() every 500ms - SetPosition() bypasses character controller → kills all movement - Between SetPosition calls, Move() had to re-establish control - This constant switching BREAKS the AI's ability to function THE FIX: - ONLY use SetPosition() for emergencies (drift > 300 units OR > 2 seconds) - Always use Move() with small, bounded deltas (max 50 units/frame) - This lets the character controller and AI run continuously - AI can now maintain state and play animations properly CRITICAL CHANGES: 1. Emergency thresholds: 300 unit drift OR 2 second timeout (not 200/500ms) 2. Normal movement: Use Move() with small incremental deltas 3. Never SetPosition during normal gameplay 4. SetPosition only as emergency correction EXPECTED RESULT: - ✅ Smooth continuous movement (no more jerky jumps) - ✅ AI stays active (can get aggro and respond naturally) - ✅ Proper animations (velocity continuous, not reset every 500ms) - ✅ Network sync maintained (emergency corrections prevent drift) Co-authored-by: Cursor <cursoragent@cursor.com>
3514 lines
133 KiB
Markdown
3514 lines
133 KiB
Markdown
# Development Log
|
||
|
||
This file tracks development progress for Fallout 4 Together.
|
||
|
||
Use this log to record setup decisions, tool versions, technical discoveries,
|
||
failed experiments, successful tests, and next steps.
|
||
|
||
---
|
||
|
||
## 2026-05-30 - Repository Setup
|
||
|
||
### What Changed
|
||
|
||
- Created the Fallout 4 Together repository.
|
||
- Added the initial repository folder structure.
|
||
- Added starter Markdown documentation files.
|
||
- Added project roadmap, disclaimer, setup notes, protocol notes, and plugin planning documents.
|
||
|
||
### Current Focus
|
||
|
||
The current focus is setting up the early development environment before writing multiplayer logic.
|
||
|
||
The immediate priority is to make sure the project has:
|
||
|
||
- A documented target Fallout 4 version
|
||
- A documented F4SE version
|
||
- A chosen CommonLibF4 plugin template
|
||
- A clean Fallout 4 test profile
|
||
- A basic native plugin that can load through F4SE
|
||
|
||
### Decisions Made
|
||
|
||
- Project name: Fallout 4 Together
|
||
- Internal prefix: F4T
|
||
- Primary target platform: Steam Fallout 4
|
||
- First prototype goal: two clients in a controlled test cell, with each player visible as a synced remote actor
|
||
- Early test cell name: F4TTestCell01
|
||
- Early test ESP name: Fallout4Together_Test.esp
|
||
- Early plugin DLL name: Fallout4Together.dll
|
||
|
||
### Current Status
|
||
|
||
```text
|
||
Repository created
|
||
Documentation structure added
|
||
Version tracking started
|
||
No plugin code yet
|
||
No server code yet
|
||
No Creation Kit test cell yet
|
||
```
|
||
|
||
### Next Steps
|
||
|
||
- Add `docs/version-targets.md` to the repository.
|
||
- Fill in the exact installed Fallout 4 version.
|
||
- Fill in the exact installed F4SE version.
|
||
- Choose a CommonLibF4 plugin template.
|
||
- Create the first GitHub milestones.
|
||
- Create the first GitHub issues.
|
||
- Set up a clean Fallout 4 test profile.
|
||
- Build an empty F4SE plugin.
|
||
- Confirm the empty plugin loads in Fallout 4.
|
||
|
||
---
|
||
|
||
## 2026-05-31 - Initial DLL Load
|
||
|
||
### What Changed
|
||
|
||
- Built the initial Fallout 4 Together DLL.
|
||
- Installed the DLL into `Data/F4SE/Plugins/`.
|
||
- Launched Fallout 4 through F4SE.
|
||
|
||
### What Worked
|
||
|
||
- The plugin loaded successfully.
|
||
- The plugin wrote a log file.
|
||
- The template test message `Hello World!` appeared in the log.
|
||
|
||
### What Broke
|
||
|
||
- The plugin is still using the template log name and identity.
|
||
|
||
### Notes
|
||
|
||
- The current log file is named `commonlibf4-template.log`.
|
||
- The next step is to rename the plugin identity and log output to `Fallout4Together`.
|
||
|
||
### Next Steps
|
||
|
||
- Replace template name references with `Fallout4Together`.
|
||
- Replace `Hello World!` with a Fallout 4 Together startup message.
|
||
- Rebuild the DLL.
|
||
- Launch through F4SE again.
|
||
- Confirm `Fallout4Together.log` is created.
|
||
|
||
---
|
||
|
||
## 2026-05-31 - Plugin Identity Renamed
|
||
|
||
### What Changed
|
||
|
||
- Renamed the plugin/log identity from the CommonLibF4 template to Fallout4Together.
|
||
- Rebuilt and installed `Fallout4Together.dll`.
|
||
- Launched Fallout 4 through F4SE.
|
||
|
||
### What Worked
|
||
|
||
- `Fallout4Together.log` was created.
|
||
- The plugin loaded successfully.
|
||
- The plugin startup code executed.
|
||
|
||
### What Broke
|
||
|
||
- Nothing currently recorded.
|
||
|
||
### Notes
|
||
|
||
- The log still contains the template message `Hello World!`.
|
||
- Next step is to replace this message with a Fallout 4 Together startup message.
|
||
|
||
### Next Steps
|
||
|
||
- Replace `Hello World!` with a proper plugin startup log message.
|
||
- Rebuild and reinstall the DLL.
|
||
- Confirm the updated message appears in `Fallout4Together.log`.
|
||
- Begin testing local player position readout.
|
||
|
||
---
|
||
|
||
## 2026-05-31 - Local Player Position Readout
|
||
|
||
### What Changed
|
||
|
||
- Updated the Fallout 4 Together plugin startup message.
|
||
- Added local player position readout.
|
||
- Logged the player's X, Y, Z position and Z rotation angle.
|
||
|
||
### What Worked
|
||
|
||
- `Fallout4Together.dll` built successfully.
|
||
- Fallout 4 launched through F4SE.
|
||
- `Fallout4Together.log` was created.
|
||
- The plugin loaded successfully.
|
||
- The plugin safely read and logged the local player position.
|
||
|
||
### What Broke
|
||
|
||
- Nothing recorded.
|
||
|
||
### Notes
|
||
|
||
- The first successful player position log was:
|
||
|
||
```text
|
||
Player position: X=2048.00, Y=2048.00, Z=0.00, AngleZ=0.00
|
||
```
|
||
|
||
---
|
||
|
||
## 2026-05-31 - Movement Logging Throttled
|
||
|
||
### What Changed
|
||
|
||
- Updated player position logging so it only writes when the player position changes.
|
||
- Added throttling so movement updates do not flood the log.
|
||
- Rebuilt and tested `Fallout4Together.dll`.
|
||
|
||
### What Worked
|
||
|
||
- Fallout 4 launched through F4SE.
|
||
- `Fallout4Together.log` was created.
|
||
- The plugin logged the player position while moving.
|
||
- Position logging now updates at a readable pace instead of many times per second.
|
||
|
||
### What Broke
|
||
|
||
- Nothing recorded.
|
||
|
||
### Notes
|
||
|
||
- The player transform readout milestone is now working.
|
||
- The plugin can access the local player and track movement changes.
|
||
|
||
### Next Steps
|
||
|
||
- Create a local test server.
|
||
- Send player transform data from the plugin to the server.
|
||
- Keep the first network test local-only.
|
||
|
||
---
|
||
|
||
## 2026-05-31 - Local Server Connection
|
||
|
||
### What Changed
|
||
|
||
- Added a local Python test server.
|
||
- Added basic networking from the Fallout 4 Together plugin to the local server.
|
||
- Sent player transform data from Fallout 4 to the server.
|
||
- Server now prints received transform packets.
|
||
|
||
### What Worked
|
||
|
||
- `server/server.py` runs successfully.
|
||
- Server listens on `127.0.0.1:7777`.
|
||
- Fallout 4 launches through F4SE.
|
||
- The plugin connects to the local server.
|
||
- Moving the player sends transform packets to the server.
|
||
- Server prints X, Y, Z, and AngleZ values.
|
||
- Server handles client disconnects safely.
|
||
|
||
### What Broke
|
||
|
||
- Nothing recorded.
|
||
|
||
### Notes
|
||
|
||
- First successful server connection used local client port `55912`.
|
||
- The server received live player transform data from Fallout 4.
|
||
- This confirms the basic data path from game plugin to external server.
|
||
|
||
### Next Steps
|
||
|
||
- Add a player ID or client ID to transform packets.
|
||
- Add timestamps to transform packets.
|
||
- Have the server echo/broadcast transform packets to connected clients.
|
||
- Add a fake test client before attempting a second Fallout 4 client.
|
||
|
||
---
|
||
|
||
## 2026-05-31 - Transform Metadata Added
|
||
|
||
### What Changed
|
||
|
||
- Added transform metadata for movement type, cell ID, and worldspace ID.
|
||
- Updated transform packets to include `movementType`.
|
||
- Added detection for cell changes and worldspace changes.
|
||
- Updated the server to print the additional transform fields.
|
||
|
||
### What Worked
|
||
|
||
- Normal movement packets still send correctly.
|
||
- Cell changes are detected and sent as `movementType=cell_change`.
|
||
- Worldspace changes are detected and sent as `movementType=worldspace_change`.
|
||
- Transform packets now include `cellId` and `worldspaceId`.
|
||
- The server remains compatible with the expanded packet format.
|
||
- The server handled client disconnects safely.
|
||
|
||
### What Broke
|
||
|
||
- Nothing recorded.
|
||
|
||
### Notes
|
||
|
||
- Example normal movement packet included `cellId=0000DD5F` and `worldspaceId=0000003C`.
|
||
- Example cell change packet included `movementType=cell_change`.
|
||
- Interior cell behavior should be verified later, especially whether `worldspaceId` should be null or inherited.
|
||
|
||
### Next Steps
|
||
|
||
- Add `playerId` to transform packets.
|
||
- Add packet timestamps.
|
||
- Add server-side client IDs.
|
||
- Make the server broadcast transform packets to other connected clients.
|
||
- Create a fake client to receive broadcast packets.
|
||
|
||
---
|
||
|
||
## 2026-05-31 - Server Broadcast And Fake Client
|
||
|
||
### What Changed
|
||
|
||
- Updated the local server to assign incrementing player IDs.
|
||
- Added welcome packets for newly connected clients.
|
||
- Added server-side timestamps to transform packets.
|
||
- Added server broadcast support for transform packets.
|
||
- Added a receiver-only fake client.
|
||
- Added client-side timestamps to plugin transform packets.
|
||
- Updated server documentation with the fake-client test flow.
|
||
|
||
### What Worked
|
||
|
||
- `server/server.py` compiles with `python -m py_compile`.
|
||
- `server/fake_client.py` compiles with `python -m py_compile`.
|
||
- The server accepts multiple clients.
|
||
- The server assigns player IDs.
|
||
- The server broadcasts transform packets to clients other than the sender.
|
||
- The fake client receives broadcast transform packets.
|
||
- The plugin still builds successfully with `xmake build`.
|
||
|
||
### What Broke
|
||
|
||
- Nothing recorded.
|
||
|
||
### Notes
|
||
|
||
- The plugin does not yet read welcome packets from the server.
|
||
- Server-to-plugin receive handling is intentionally left as a TODO.
|
||
- The fake client exists so transform broadcast can be tested before running two Fallout 4 clients.
|
||
|
||
### Next Steps
|
||
|
||
- Run the full test with Fallout 4, the server, and the fake client.
|
||
- Confirm the fake client receives live transform packets from the Fallout 4 plugin.
|
||
- Add protocol documentation for `playerId`, `clientTime`, and `serverTime`.
|
||
- Begin planning remote player state storage on the receiving client.
|
||
|
||
---
|
||
|
||
## 2026-05-31 - Full Broadcast Flow Tested
|
||
|
||
### What Changed
|
||
|
||
- Tested the full server broadcast flow.
|
||
- Connected a fake Python client to the local server.
|
||
- Connected the Fallout 4 Together plugin as a second client.
|
||
- Confirmed that transform packets from the Fallout 4 plugin are broadcast to the fake client.
|
||
|
||
### What Worked
|
||
|
||
- The server assigned player IDs to connected clients.
|
||
- The fake client received a welcome packet.
|
||
- The Fallout 4 plugin connected to the server.
|
||
- The server received transform packets from the Fallout 4 plugin.
|
||
- The server added `playerId` and `serverTime` fields.
|
||
- The server broadcast transform packets to other clients.
|
||
- The fake client received live Fallout 4 transform packets.
|
||
- Cell change and worldspace movement types were preserved through broadcast.
|
||
|
||
### What Broke
|
||
|
||
- Nothing recorded.
|
||
|
||
### Notes
|
||
|
||
- In the successful test, the fake client connected as player 1.
|
||
- The Fallout 4 plugin connected as player 2.
|
||
- The server broadcast player 2 movement to player 1.
|
||
- This confirms the first working relay path from Fallout 4 to another connected client.
|
||
|
||
### Next Steps
|
||
|
||
- Make `fake_client.py` store remote player state instead of only printing packets.
|
||
- Track remote players by `playerId`.
|
||
- Store position, rotation, cellId, worldspaceId, movementType, and last update time.
|
||
- Print a readable remote player state table.
|
||
|
||
---
|
||
|
||
## 2026-05-31 - Fake Client Remote State
|
||
|
||
### What Changed
|
||
|
||
- Updated `fake_client.py` so it stores remote player state by `playerId`.
|
||
- Remote player state now tracks position, angle, movement type, cell ID, worldspace ID, server time, and local receive time.
|
||
- Tested the full relay flow from Fallout 4 plugin to server to fake client.
|
||
|
||
### What Worked
|
||
|
||
- The server received transform packets from the Fallout 4 plugin.
|
||
- The server assigned the Fallout 4 plugin a player ID.
|
||
- The server broadcast transform packets to the fake client.
|
||
- The fake client received transform packets.
|
||
- The fake client updated remote player state for player 2.
|
||
- Cell changes and worldspace metadata were preserved.
|
||
|
||
### What Broke
|
||
|
||
- Nothing recorded.
|
||
|
||
### Notes
|
||
|
||
- `clientTime` currently appears as `None` in fake client output.
|
||
- This should be investigated before building the Fallout-side receive loop.
|
||
- The fake client now models the kind of remote player state table that the eventual Fallout 4 receiving client will need.
|
||
|
||
### Next Steps
|
||
|
||
- Fix or verify `clientTime` in outgoing plugin transform packets.
|
||
- Add disconnect packet broadcasting so fake clients can remove disconnected remote players.
|
||
- Then begin planning a plugin receive loop for server messages.
|
||
|
||
---
|
||
|
||
## 2026-05-31 - Client Time Fixed
|
||
|
||
### What Changed
|
||
|
||
- Fixed `clientTime` in transform packets.
|
||
- Confirmed the Fallout 4 plugin sends `clientTime`.
|
||
- Confirmed the server preserves and broadcasts `clientTime`.
|
||
- Confirmed the fake client receives and stores `clientTime`.
|
||
|
||
### What Worked
|
||
|
||
- `clientTime` now appears in server output.
|
||
- `clientTime` now appears in fake client remote player state.
|
||
- `serverTime` is still added by the server.
|
||
- Remote player state now has both client-side and server-side timing data.
|
||
|
||
### What Broke
|
||
|
||
- Nothing recorded.
|
||
|
||
### Notes
|
||
|
||
- The packet timing fields are now suitable for later interpolation experiments.
|
||
- The next lifecycle feature should be disconnect broadcasting.
|
||
|
||
### Next Steps
|
||
|
||
- Add disconnect packets when a client leaves.
|
||
- Have fake clients remove remote players when disconnect packets are received.
|
||
|
||
---
|
||
|
||
## 2026-05-31 - Disconnect Lifecycle
|
||
|
||
### What Changed
|
||
|
||
- Added server-side disconnect packet broadcasting.
|
||
- Updated the fake client to handle disconnect packets.
|
||
- Fake client now removes disconnected remote players from its remote player state table.
|
||
- Tested disconnect lifecycle using the Fallout 4 plugin and fake client.
|
||
|
||
### What Worked
|
||
|
||
- Server detected the Fallout 4 plugin disconnecting.
|
||
- Server removed the disconnected client.
|
||
- Server broadcast a disconnect packet to remaining clients.
|
||
- Fake client received the disconnect packet.
|
||
- Fake client removed remote player 2 from its state table.
|
||
- Fake client correctly reported that no remote players were currently tracked.
|
||
|
||
### What Broke
|
||
|
||
- Nothing recorded.
|
||
|
||
### Notes
|
||
|
||
- The fake client reported `WinError 10054` after the server was stopped manually. This is expected during shutdown and is not a protocol issue.
|
||
- The basic networking lifecycle now works: connect, welcome, transform, disconnect, cleanup.
|
||
|
||
### Next Steps
|
||
|
||
- Add protocol documentation for welcome, transform, and disconnect packets.
|
||
- Begin planning a Fallout 4 plugin receive loop.
|
||
- Store remote player state inside the plugin, without spawning actors yet.
|
||
|
||
---
|
||
|
||
## 2026-05-31 - Protocol Documentation Updated
|
||
|
||
### What Changed
|
||
|
||
- Documented the current networking protocol.
|
||
- Documented welcome, transform, and disconnect packets.
|
||
- Documented the fake client remote player state model.
|
||
- Documented the current server and fake client test flow.
|
||
|
||
### What Worked
|
||
|
||
- The networking lifecycle now includes connect, welcome, transform broadcast, disconnect broadcast, and remote-player cleanup.
|
||
- `clientTime` and `serverTime` are both present in transform packets.
|
||
- The fake client can store and remove remote player state.
|
||
|
||
### What Broke
|
||
|
||
- Nothing recorded.
|
||
|
||
### Notes
|
||
|
||
- The Fallout 4 plugin does not yet receive server packets.
|
||
- The current receiver is `server/fake_client.py`.
|
||
- The next technical milestone is a Fallout 4 plugin receive loop that stores remote player state internally.
|
||
|
||
### Next Steps
|
||
|
||
- Add a plugin receive loop.
|
||
- Parse welcome, transform, and disconnect packets in the plugin.
|
||
- Store remote player state inside the plugin.
|
||
- Do not spawn remote actors yet.
|
||
|
||
---
|
||
|
||
## 2026-05-31 - Plugin Receive State Implemented
|
||
|
||
### What Changed
|
||
|
||
- Added plugin-side receiving for server `welcome`, `transform`, and `disconnect` packets.
|
||
- Added internal remote player state storage keyed by server-assigned `playerId`.
|
||
- Added defensive packet parsing so malformed or unknown packets are ignored without crashing the plugin.
|
||
- Kept remote actor spawning and movement out of scope.
|
||
|
||
### What Worked
|
||
|
||
- The plugin now stores its assigned server `playerId`.
|
||
- Remote transforms can be stored internally without touching Fallout 4 actors or game objects.
|
||
- Disconnect packets remove remote player state.
|
||
- `xmake build` succeeds.
|
||
|
||
### What Broke
|
||
|
||
- Nothing recorded.
|
||
|
||
### Notes
|
||
|
||
- The receive loop runs on a background networking thread.
|
||
- Remote transform logging is throttled so `Fallout4Together.log` remains readable.
|
||
- Actor spawning, actor movement, animation sync, combat sync, inventory sync, quest sync, and settlement sync remain planned later.
|
||
|
||
### Next Steps
|
||
|
||
- Test with Fallout 4 launched through F4SE against `server/server.py`.
|
||
- Add a controlled game-thread reader for remote player snapshots before any actor spawning work.
|
||
|
||
---
|
||
|
||
## 2026-05-31 - Per-Instance Client Log Prefixes
|
||
|
||
### What Changed
|
||
|
||
- Added a shared local-player log prefix helper for plugin networking logs.
|
||
- Prefixed important startup, connection, welcome, transform send, remote update, disconnect, and networking warning logs with `[LocalPlayerId=unassigned]` or `[LocalPlayerId=N]`.
|
||
- Kept the existing `Fallout4Together.log` file name and avoided per-instance log files for this milestone.
|
||
|
||
### What Worked
|
||
|
||
- The prefix reads the existing assigned player ID state through the thread-safe remote-player state accessor.
|
||
- Remote player state storage and packet relay behavior remain unchanged.
|
||
|
||
### What Broke
|
||
|
||
- Nothing recorded.
|
||
|
||
### Notes
|
||
|
||
- Actor spawning, actor movement, gameplay sync, and protocol changes remain out of scope.
|
||
- A future launcher/profile milestone can revisit separate per-instance log files.
|
||
|
||
### Next Steps
|
||
|
||
- Test two Fallout 4 instances through F4SE and confirm prefixed remote update/disconnect logs are readable in the shared log file.
|
||
|
||
---
|
||
|
||
## 2026-05-31 - Placed Proxy Actor Control
|
||
|
||
### What Changed
|
||
|
||
- Fixed proxy actor lookup for `F4TProxyRemotePlayer01REF`.
|
||
- Added more robust lookup logic for the placed proxy actor.
|
||
- Confirmed the plugin can control a placed actor reference in `F4TTestCell01`.
|
||
- The proxy actor now moves to the local player position plus an offset.
|
||
|
||
### What Worked
|
||
|
||
- Fallout 4 launched through F4SE.
|
||
- `coc F4TTestCell01` worked.
|
||
- The proxy actor was found successfully.
|
||
- The proxy actor teleported to the player position plus offset.
|
||
- Actor movement occurred from the safe game-thread update path.
|
||
- The networking receive thread still does not touch Fallout 4 actors.
|
||
|
||
### What Broke
|
||
|
||
- Nothing recorded.
|
||
|
||
### Notes
|
||
|
||
- This is the first successful visible actor-control test for Fallout 4 Together.
|
||
- The proxy is still driven by local player position plus offset, not remote player state.
|
||
- Dynamic spawning is still not implemented.
|
||
- Remote actor syncing has not started yet.
|
||
|
||
### Next Steps
|
||
|
||
- Replace local-player-offset movement with remote player state movement.
|
||
- Only move the proxy when the remote player is in the same cell/worldspace.
|
||
- Snap movement first.
|
||
- Add smoothing/interpolation later.
|
||
|
||
---
|
||
|
||
## 2026-05-31 - Remote-State Proxy Movement
|
||
|
||
### What Changed
|
||
|
||
- Changed the proxy actor controller plan from local-player-offset movement to
|
||
remote-state-driven movement.
|
||
- The placed proxy actor `F4TProxyRemotePlayer01REF` now represents the lowest
|
||
available remote `playerId` from a copied remote-player snapshot.
|
||
- Proxy movement remains game-thread-only and throttled to roughly 5 Hz.
|
||
- The networking receive thread still only parses packets and updates plain
|
||
remote-player state.
|
||
- Relaxed receive-side transform validation so interior/test-cell packets only
|
||
require `cellId`; missing `worldspaceId` is stored as an empty string.
|
||
- Added a same-test-cell proxy movement log for `F4TTestCell01`.
|
||
|
||
### What Worked
|
||
|
||
- Existing proxy actor lookup and safe handle caching were preserved.
|
||
- The local-player-offset safety movement remains available as a debug fallback
|
||
mode in code.
|
||
|
||
### What Broke
|
||
|
||
- Not tested in-game yet.
|
||
|
||
### Notes
|
||
|
||
- The proxy is left wherever it last moved after a remote disconnect for now.
|
||
- Dynamic spawning, multiple proxy actors, interpolation, and gameplay sync are
|
||
still intentionally out of scope.
|
||
|
||
### Next Steps
|
||
|
||
- Build the plugin with `xmake build`.
|
||
- Run the single-client, two-client, and disconnect tests in `F4TTestCell01`.
|
||
|
||
---
|
||
|
||
## 2026-06-02 - Remote Proxy Smoothing
|
||
|
||
### What Changed
|
||
|
||
- Added simple position smoothing for normal remote proxy movement.
|
||
- Kept proxy actor movement on the `F4TProxyActorController` game-thread update
|
||
path using copied remote-player snapshots.
|
||
- Kept the single-proxy, lowest-`playerId` selection behavior.
|
||
- Added snap movement for `cell_change`, `worldspace_change`, and `teleport`
|
||
remote transforms.
|
||
- Left rotation snapping in place with a TODO for wrapped angle smoothing.
|
||
- Left the proxy idle at its last position when the represented remote player
|
||
disconnects, disappears, or leaves the same cell.
|
||
- Added readable one-shot and throttled logs for smoothing, first smoothed
|
||
movement, special movement snaps, disappearance, and same-cell mismatch idle
|
||
behavior.
|
||
|
||
### What Worked
|
||
|
||
- Dynamic spawning and multiple proxy actors remain out of scope.
|
||
- Networking still only updates plain remote-player state and does not touch
|
||
Fallout 4 actors or references.
|
||
|
||
### What Broke
|
||
|
||
- Not tested in-game yet.
|
||
|
||
### Notes
|
||
|
||
- Hiding or disabling the proxy actor is still deferred. The current safe
|
||
behavior is to leave the placed proxy idle at its last valid position.
|
||
|
||
### Next Steps
|
||
|
||
- Build the plugin with `xmake build`.
|
||
- Run the single-client, two-client, special-movement, disconnect, and cell-leave
|
||
tests in `F4TTestCell01`.
|
||
|
||
---
|
||
|
||
## 2026-06-02 - Smooth Remote Proxy Movement Tested
|
||
|
||
### What Changed
|
||
|
||
- Separated normal local transform send cadence from local movement log cadence.
|
||
- Lowered normal transform send thresholds to support smoother remote motion:
|
||
about 3 game units of position change or 0.02 radians of rotation change.
|
||
- Targeted normal transform sends at roughly 10 Hz while moving, without sending
|
||
every frame.
|
||
- Kept local movement logs readable at the slower debug interval.
|
||
- Kept `cell_change`, `worldspace_change`, and `teleport` sends immediate and
|
||
baseline-updating.
|
||
- Removed the 200 ms throttle from remote-player proxy visual movement on the
|
||
game-thread update path.
|
||
- Tuned normal proxy position smoothing to a per-update lerp alpha near 0.15.
|
||
- Preserved direct snapping for special remote movement types and kept rotation
|
||
snapping through the existing safe heading path.
|
||
|
||
### What Worked
|
||
|
||
- The networking receive thread remains actor-free and still only updates plain
|
||
remote-player state.
|
||
- The single placed proxy actor remains the only remote visual representation;
|
||
dynamic spawning and multiple proxy actors are still out of scope.
|
||
- Two Fallout 4 clients can still connect to the local server.
|
||
- Server transform relay still works.
|
||
- Remote player state still updates correctly.
|
||
- The proxy actor now moves smoothly compared to the previous snapping/jittery
|
||
version.
|
||
- The current visual result is close to feeling correct, with animation sync now
|
||
being the obvious missing piece.
|
||
|
||
### What Broke
|
||
|
||
- Nothing recorded.
|
||
|
||
### Notes
|
||
|
||
- `server/server.py` and `server/fake_client.py` were intentionally left
|
||
unchanged because the transform packet protocol did not need to change.
|
||
- This is the first smooth visible multiplayer prototype for Fallout 4
|
||
Together.
|
||
- The proxy actor still lacks synced animations.
|
||
- Only one proxy actor is supported.
|
||
- Dynamic spawning is not implemented.
|
||
- Combat, inventory, quest, settlement, interaction, and animation sync remain
|
||
out of scope for this milestone.
|
||
|
||
### Next Steps
|
||
|
||
- Add basic movement state sync.
|
||
- Start with simple animation-related states like idle, walking, running,
|
||
sprinting, crouching, jumping, and weapon drawn.
|
||
- Keep animation sync separate from combat and inventory sync.
|
||
|
||
---
|
||
|
||
## 2026-06-02 - Basic Movement State Sync
|
||
|
||
### What Changed
|
||
|
||
- Added data-only movement state fields to transform packets:
|
||
`isMoving`, `isSprinting`, `isSneaking`, `isJumping`, `weaponDrawn`, and
|
||
`movementSpeed`.
|
||
- Derived local movement speed from sampled position delta over elapsed time on
|
||
the game-thread polling path.
|
||
- Stored optional movement state fields in remote-player state with safe
|
||
defaults for older packets.
|
||
- Added throttled remote movement-state logs so packet data can be inspected
|
||
without logging every transform.
|
||
- Updated `server/fake_client.py` to display movement state while keeping
|
||
missing fields backwards compatible.
|
||
|
||
### What Worked
|
||
|
||
- The server relay remains unchanged because it preserves unknown transform
|
||
fields when it adds `playerId` and `serverTime`.
|
||
- The networking receive thread remains actor-free and only updates plain
|
||
remote-player state.
|
||
- Proxy actor movement remains unchanged; movement state is not applied to
|
||
animations yet.
|
||
|
||
### What Broke
|
||
|
||
- Nothing recorded.
|
||
|
||
### Notes
|
||
|
||
- This milestone prepares the remote-player data model for later proxy
|
||
animation or behavior work.
|
||
- Dynamic spawning, multiple proxy actors, animation graph sync, combat,
|
||
inventory, quest, settlement, and interaction sync remain out of scope.
|
||
|
||
### Next Steps
|
||
|
||
- Test with two Fallout 4 clients in `F4TTestCell01`.
|
||
- Use the synced state in a later animation milestone without expanding
|
||
gameplay sync scope.
|
||
|
||
---
|
||
|
||
## 2026-06-02 - Proxy Sneak State Observation
|
||
|
||
### What Changed
|
||
|
||
- Added a log-only movement-state behaviour hook in `F4TProxyActorController`.
|
||
- The single represented proxy now observes the remote player's `isSneaking`
|
||
value after the normal same-cell validation.
|
||
- Added controller-local tracking for the represented remote `playerId` and its
|
||
last observed sneak state so logs happen only on transitions.
|
||
- Reset sneak-state tracking when the represented remote player changes or
|
||
disappears.
|
||
|
||
### What Worked
|
||
|
||
- Existing smooth proxy transform movement remains unchanged.
|
||
- Special movement types still snap through the existing movement path.
|
||
- The networking receive thread remains actor-free and continues to update only
|
||
plain remote-player state.
|
||
|
||
### What Broke
|
||
|
||
- Nothing recorded.
|
||
|
||
### Notes
|
||
|
||
- This milestone intentionally does not force the proxy to crouch visually.
|
||
- Animation graph variables/events, direct `ActorState` writes, and
|
||
`PerformAction(kActionSneak)` remain out of scope until a safe actor crouch API
|
||
is confirmed.
|
||
- Dynamic spawning, multiple proxy actors, combat, inventory, quest, settlement,
|
||
weapon, and interaction sync remain out of scope.
|
||
|
||
### Next Steps
|
||
|
||
- Test with two Fallout 4 clients in `F4TTestCell01` and confirm sneak toggles
|
||
produce one log line per transition.
|
||
- Research a safe visual crouch/sneak application path for the placed proxy
|
||
actor.
|
||
|
||
---
|
||
|
||
## 2026-06-02 - Local Sneak Detection Diagnostics
|
||
|
||
### What Changed
|
||
|
||
- Improved local `isSneaking` detection to combine safe read-only signals from
|
||
`PlayerCharacter::IsSneaking()`, actor-state stance, and actor-state
|
||
`forceSneak`.
|
||
- Added throttled local sneak diagnostics showing each read-only signal and the
|
||
final `isSneaking` value sent in transform packets.
|
||
- Kept movement-state change detection unchanged, so `isSneaking` transitions
|
||
can trigger transform sends even without a meaningful position delta.
|
||
|
||
### What Worked
|
||
|
||
- The networking protocol remains unchanged and backwards compatible.
|
||
- The receive thread remains actor-free.
|
||
- Proxy sneak behaviour remains log-only; no visual crouch is applied.
|
||
|
||
### What Broke
|
||
|
||
- Nothing recorded.
|
||
|
||
### Notes
|
||
|
||
- No animation graph variables/events, direct actor-state writes, or
|
||
`PerformAction(kActionSneak)` were added.
|
||
- Server files and fake-client compatibility were left unchanged.
|
||
|
||
### Next Steps
|
||
|
||
- Test crouch/sneak in-game and compare `apiSneaking`, `actorStateSneaking`,
|
||
`actorStateForceSneak`, and `finalIsSneaking` in `Fallout4Together.log`.
|
||
|
||
---
|
||
|
||
## 2026-06-02 - Proxy Lifecycle Holding
|
||
|
||
### What Changed
|
||
|
||
- Added controller-local lifecycle state for the single placed proxy actor.
|
||
- Added a temporary hidden holding position inside `F4TTestCell01` for cases
|
||
where no valid same-cell remote player should be represented.
|
||
- Moved the proxy to the holding position when the represented remote player
|
||
disconnects, disappears from remote-player state, or leaves the local test
|
||
cell.
|
||
- Restored representation by snapping the proxy to a valid same-cell remote
|
||
player once before resuming existing smooth movement.
|
||
- Kept disconnect, no-remote-player, and cell-mismatch logs transition-based or
|
||
throttled so the log does not spam every update.
|
||
|
||
### What Worked
|
||
|
||
- Actor/reference access remains inside `F4TProxyActorController` on the
|
||
game-thread update path.
|
||
- The networking protocol, Python server, and fake client remain unchanged.
|
||
- Existing smooth movement, special movement snapping, and sneak-state
|
||
observation remain in place for valid same-cell remote players.
|
||
|
||
### What Broke
|
||
|
||
- Nothing recorded during implementation.
|
||
|
||
### Notes
|
||
|
||
- This milestone deliberately does not call `Disable()`, `Enable()`,
|
||
`SetAlpha()`, invisibility APIs, or animation graph APIs.
|
||
- Dynamic spawning, multiple proxy actors, combat, inventory, quest, settlement,
|
||
weapon/projectile, interaction, and animation sync remain out of scope.
|
||
- The holding position is a prototype-safe fallback until a hide/disable path is
|
||
validated for the persistent placed proxy reference.
|
||
|
||
### Next Steps
|
||
|
||
- Test single-client, two-client, disconnect, cell-leave, and return/reconnect
|
||
flows in `F4TTestCell01`.
|
||
- If the holding fallback proves stable, separately validate whether a true
|
||
hide/disable path is safe for the placed proxy reference.
|
||
|
||
---
|
||
|
||
## 2026-06-02 - Reusable Server Core Refactor
|
||
|
||
### What Changed
|
||
|
||
- Split the Python relay server into a reusable server core and a thin terminal
|
||
launcher.
|
||
- Added `server/client_session.py` to track connected client state, packet
|
||
counters, last packet time, and last transform snapshots.
|
||
- Added `server/server_core.py` with `FalloutTogetherServer` lifecycle methods,
|
||
log listeners, client snapshots, server stats, and thread-safe shared state.
|
||
- Updated `server/README.md` with the new server structure and validation
|
||
commands.
|
||
|
||
### What Worked
|
||
|
||
- The terminal command remains `cd server` followed by `python server.py`.
|
||
- The server still defaults to `127.0.0.1:7777`.
|
||
- Newline-separated JSON handling, welcome packets, transform mutation,
|
||
transform relay, disconnect relay, and unknown transform field preservation
|
||
remain protocol-compatible.
|
||
- The refactor prepares the server for a future desktop dev-server UI without
|
||
adding PySide6, fake-player management, movement scripts, or gameplay
|
||
features.
|
||
|
||
### What Broke
|
||
|
||
- Nothing recorded during implementation.
|
||
|
||
### Notes
|
||
|
||
- The future UI can use `FalloutTogetherServer.start()`, `stop()`,
|
||
`is_running()`, `get_clients()`, `get_stats()`, and log listener callbacks
|
||
without scraping terminal output.
|
||
- Fallout 4 plugin code and network protocol fields were left unchanged.
|
||
|
||
### Next Steps
|
||
|
||
- Run compile checks for the server files.
|
||
- Validate server startup, fake-client welcome handling, transform relay, and
|
||
disconnect relay with fake clients and Fallout 4 plugin clients.
|
||
|
||
---
|
||
|
||
## 2026-06-02 - Dev Server GUI
|
||
|
||
### What Changed
|
||
|
||
- Added a PySide6 desktop developer GUI for the reusable Python server core.
|
||
- Added a Server Console tab with start/stop controls, live server logs, live
|
||
stats, and a connected-client table.
|
||
- Added a Fake Clients tab placeholder for future fake-client management and
|
||
movement-script controls.
|
||
- Added a server-local requirements file for the GUI dependency.
|
||
- Updated server documentation with GUI install and run instructions while
|
||
preserving the terminal server flow.
|
||
|
||
### What Worked
|
||
|
||
- The GUI uses `FalloutTogetherServer` instead of duplicating networking logic.
|
||
- Server logs flow through `add_log_listener(...)` and a Qt signal bridge rather
|
||
than stdout scraping.
|
||
- Stats and connected-client snapshots are refreshed through `get_stats()` and
|
||
`get_clients()` on a `QTimer`.
|
||
- The terminal launcher, fake client, network protocol, and plugin code were
|
||
left unchanged.
|
||
|
||
### What Broke
|
||
|
||
- Nothing recorded during implementation.
|
||
|
||
### Notes
|
||
|
||
- Fake client spawning and movement scripts remain intentionally unimplemented.
|
||
- Full GUI interaction and Fallout 4/F4SE validation still need to be run
|
||
manually on the desktop.
|
||
|
||
### Testing
|
||
|
||
- Ran `python -m py_compile server.py server_core.py client_session.py fake_client.py dev_server_app.py`
|
||
from `server`; it succeeded.
|
||
- Checked IDE lints for the new GUI and edited docs; no diagnostics were
|
||
reported.
|
||
- Checked the current Python environment for `PySide6`; it is not installed, so
|
||
`pip install -r requirements.txt` is required before launching the GUI.
|
||
- Did not start a duplicate terminal server because an existing `python
|
||
server.py` session and `python fake_client.py` session were already present.
|
||
|
||
### Next Steps
|
||
|
||
- Run `python dev_server_app.py`, click `Start Server`, and connect
|
||
`fake_client.py`.
|
||
- Launch Fallout 4 through F4SE and confirm the plugin appears in the GUI client
|
||
table with transform stats updating.
|
||
|
||
---
|
||
|
||
## 2026-06-02 - GUI Fake Client Manager
|
||
|
||
### Summary
|
||
|
||
Added GUI-managed synthetic TCP clients for local relay testing. Fake clients
|
||
connect through the normal server socket, receive a welcome `playerId`, send one
|
||
idle transform per second for `F4TTestCell01`, and disconnect cleanly from the
|
||
Fake Clients tab or when the GUI server stops.
|
||
|
||
### Files Changed
|
||
|
||
- `server/fake_player.py`
|
||
- `server/dev_server_app.py`
|
||
- `server/README.md`
|
||
- `docs/dev-log.md`
|
||
|
||
### Details
|
||
|
||
- Added `FakePlayerClient` and `FakePlayerManager` as GUI test tooling only.
|
||
- Fake clients use TCP `127.0.0.1:7777` and newline-separated JSON packets, so
|
||
the server sees them as normal external clients.
|
||
- The Fake Clients tab now supports `+ Add Fake Client` and
|
||
`- Remove Selected`.
|
||
- Fake-client lifecycle logs are routed to the GUI console with a
|
||
`[FakeClient]` prefix through the Qt signal bridge.
|
||
- Stopping or closing the GUI disconnects all fake clients before stopping the
|
||
server.
|
||
- The Fallout 4 plugin, network protocol, terminal server launcher, and
|
||
standalone `fake_client.py` were left unchanged.
|
||
|
||
### Testing
|
||
|
||
- Ran `python -m py_compile server.py server_core.py client_session.py
|
||
fake_client.py dev_server_app.py fake_player.py` from `server`; it succeeded.
|
||
- Ran `python server.py` from `server` and confirmed the terminal launcher
|
||
reached the expected listening state.
|
||
- Ran a headless fake-client smoke test on an alternate local port after
|
||
`127.0.0.1:7777` was temporarily held by another process. The fake client
|
||
received `playerId` 1, reached `Connected`, and the server recorded the idle
|
||
transform for `0B000F99`.
|
||
- Imported `dev_server_app.py` successfully to confirm the GUI module and
|
||
PySide6 dependency load in the current Python environment.
|
||
- Not yet run: interactive `python dev_server_app.py` GUI clicks.
|
||
- Not yet run: Fallout 4/F4SE validation that a real client receives the fake
|
||
client's idle transform in `F4TTestCell01`.
|
||
|
||
### Known Issues
|
||
|
||
- No movement scripts beyond `Idle` are implemented yet.
|
||
- Full Fallout 4/F4SE validation still needs to be run manually.
|
||
|
||
### Next Steps
|
||
|
||
- Add selectable fake-client movement scripts after the idle-client milestone is
|
||
validated.
|
||
- Consider fake-client controls for script start/stop once multiple scripts
|
||
exist.
|
||
|
||
---
|
||
|
||
## 2026-06-02 - Fake Client Walk To Player
|
||
|
||
### Summary
|
||
|
||
Added Fake Clients tab movement controls and a first useful fake-client movement
|
||
script. GUI-managed fake clients can now be set to Idle or Walk To Player while
|
||
remaining ordinary external TCP clients that send normal transform packets
|
||
through the Python server.
|
||
|
||
### Files Changed
|
||
|
||
- `server/fake_player.py`
|
||
- `server/dev_server_app.py`
|
||
- `server/README.md`
|
||
- `docs/dev-log.md`
|
||
|
||
### Details
|
||
|
||
- Added `Idle` and `Walk To Player` script modes to `FakePlayerClient`.
|
||
- Added thread-safe script switching, pose tracking, and target state for
|
||
GUI-managed fake clients.
|
||
- Added `Set Idle` and `Walk To Player` buttons to the Fake Clients tab.
|
||
- Added safe selected-client handling with readable logs when no fake client is
|
||
selected or the server is stopped.
|
||
- Walk To Player reads `FalloutTogetherServer.get_clients()` snapshots through a
|
||
manager callback, filters out GUI-managed fake clients, and targets the lowest
|
||
real `playerId` with a valid `lastTransform`.
|
||
- Walk To Player moves in a straight line toward the real player's latest
|
||
transform plus a +150 X offset, sends moving transforms at about 10 Hz, and
|
||
returns to low-rate idle keep-alive transforms after reaching the stop
|
||
distance.
|
||
- No plugin code, protocol fields, `server/server.py`, or `server/fake_client.py`
|
||
were changed.
|
||
|
||
### Testing
|
||
|
||
- Ran `python -m py_compile server.py server_core.py client_session.py
|
||
fake_client.py dev_server_app.py fake_player.py` from `server`; it succeeded.
|
||
- Still needs manual GUI validation with `python dev_server_app.py`.
|
||
- Still needs in-game Fallout 4/F4SE validation in `F4TTestCell01`.
|
||
|
||
### Known Issues
|
||
|
||
- Walk To Player uses simple straight-line movement only; it does not perform
|
||
pathfinding, navmesh checks, or obstacle avoidance.
|
||
- Additional fake-client scripts are still future work.
|
||
|
||
### Next Steps
|
||
|
||
- Manually validate the GUI buttons with no real player connected and with a
|
||
Fallout 4 client in `F4TTestCell01`.
|
||
- Add future dev/test scripts such as Walk Circle, Walk Square, Follow Player,
|
||
Jump Loop, Sneak Toggle, Teleport Test, Leave Cell / Return, and Disconnect
|
||
After Delay.
|
||
|
||
---
|
||
|
||
## 2026-06-02 - Fake Client Test Scripts
|
||
|
||
### Summary
|
||
|
||
Added developer-only fake-client test scripts and actions to the PySide6 Fake
|
||
Clients tab. GUI-managed fake clients can now test jump state, sneak state,
|
||
circle movement, cell mismatch holding, return-to-cell behavior, and teleport
|
||
snapping without launching multiple Fallout 4 instances.
|
||
|
||
### Files Changed
|
||
|
||
- `server/fake_player.py`
|
||
- `server/dev_server_app.py`
|
||
- `server/README.md`
|
||
- `docs/dev-log.md`
|
||
|
||
### Details
|
||
|
||
- Added `Walk Circle` and `Left Cell` continuous fake-client modes.
|
||
- Added one-shot `Jump Once`, `Toggle Sneak`, `Return To Cell`, and
|
||
`Teleport Test` actions.
|
||
- Kept fake clients as external TCP clients that send normal newline-separated
|
||
JSON transform packets through the existing server socket.
|
||
- Preserved the existing `Idle` and `Walk To Player` controls.
|
||
- Added Fake Clients tab buttons for `Jump Once`, `Toggle Sneak`, `Walk Circle`,
|
||
`Leave Cell`, `Return To Cell`, and `Teleport Test`.
|
||
- Kept Fake Clients table columns unchanged while continuing to refresh script,
|
||
cell, and position snapshots from the manager.
|
||
- Added a socket send lock and protected script, pose, movement flags, and
|
||
one-shot action state with the existing client lock.
|
||
- Left the Fallout 4 plugin, protocol, `server/server.py`, `server/fake_client.py`,
|
||
and server networking behavior unchanged.
|
||
|
||
### Testing
|
||
|
||
- Ran `python -m py_compile server.py server_core.py client_session.py
|
||
fake_client.py dev_server_app.py fake_player.py` from `server`; it succeeded.
|
||
- Checked editor diagnostics for `server/fake_player.py` and
|
||
`server/dev_server_app.py`; no linter errors were reported.
|
||
- Ran a headless smoke test on alternate local port `7788`; a GUI-managed fake
|
||
client connected through the real server socket and exercised Walk Circle,
|
||
Toggle Sneak, Jump Once, Leave Cell, Return To Cell, and Teleport Test without
|
||
disconnecting.
|
||
- Not yet run: `python server.py` terminal launcher smoke test.
|
||
- Not yet run: `python dev_server_app.py`, start the server, add/remove fake
|
||
clients, and click each Fake Clients script/action button.
|
||
- Not yet run: Fallout 4/F4SE validation inside `F4TTestCell01` that Walk
|
||
Circle, Jump Once, Toggle Sneak, Leave Cell, Return To Cell, and Teleport Test
|
||
produce the expected proxy behavior.
|
||
|
||
### Known Issues
|
||
|
||
- Full Fallout 4/F4SE validation still needs to be run manually.
|
||
- Walk Circle is a simple mathematical circle only; it does not use pathfinding,
|
||
navmesh, or obstacle avoidance.
|
||
|
||
### Next Steps
|
||
|
||
- Manually validate the GUI scripts with a real Fallout 4 client in
|
||
`F4TTestCell01`.
|
||
- Consider future fake-client scripts such as Walk Square, Follow Player, Sprint
|
||
Toggle, Weapon Drawn Toggle, Disconnect After Delay, and multi-fake-player
|
||
choreography.
|
||
|
||
---
|
||
|
||
## 2026-06-02 - Runtime Proxy Actor Manager Stage 1
|
||
|
||
### Summary
|
||
|
||
Added lookup-only diagnostics for the future runtime proxy actor path. The
|
||
plugin now confirms whether `Fallout4Together_Test.esp` is loaded and whether
|
||
the actor base `F4T_RemotePlayerProxy` resolves by editor ID or by its confirmed
|
||
plugin-local form ID on the existing game-thread proxy controller path.
|
||
|
||
### Files Changed
|
||
|
||
- `plugin/src/F4TProxyActorController.cpp`
|
||
- `docs/dev-log.md`
|
||
- `docs/architecture.md`
|
||
|
||
### Details
|
||
|
||
- Added `ResolveProxyActorBase()` as a Stage 1 diagnostic function.
|
||
- Added a test ESP loaded check before actor-base lookup diagnostics.
|
||
- Tightened the diagnostic call site so it runs in `F4TTestCell01` before placed
|
||
proxy fallback lookup and before remote-player selection.
|
||
- Made runtime proxy actor-base log messages explicit for diagnostic searches:
|
||
plugin loaded, lookup running, lookup resolved, or lookup failed.
|
||
- Uses `LookupModByName` as a fallback for the ESP loaded check, matching the
|
||
mod lookup path already used by the placed-reference form ID fallback.
|
||
- Added editor-ID lookup for `F4T_RemotePlayerProxy` as a `RE::TESNPC` actor
|
||
base and validation that the resolved form still has the expected editor ID.
|
||
- Added confirmed plugin-local FormID fallback lookup:
|
||
`LookupForm<RE::TESNPC>(0x0020A1, "Fallout4Together_Test.esp")`.
|
||
- Kept the actor-base local ID `0x0020A1` separate from the placed reference
|
||
local ID `0x0020A2`.
|
||
- Validates the resolved actor base is usable as the future
|
||
`NEW_REFR_DATA::object` source before reporting success.
|
||
- Accepts the confirmed `LookupForm<RE::TESNPC>` result even if runtime editor
|
||
ID extraction is unavailable or mismatched; editor ID mismatch is logged as a
|
||
warning instead of rejecting the actor base.
|
||
- Logs fallback diagnostics including runtime form ID, local form ID, form type,
|
||
`TESNPC` status, editor ID if available, display name if available, and
|
||
whether the form was accepted.
|
||
- Logs whether actor-base lookup succeeded through editor ID lookup or
|
||
plugin-local FormID fallback.
|
||
- Added clear one-shot or throttled logs for test ESP load status, actor-base
|
||
lookup success, missing editor ID, or wrong form type.
|
||
- Did not call `CreateReferenceAtLocation` and did not spawn runtime actors.
|
||
- Left placed proxy lookup, smoothing, special movement snapping, holding
|
||
lifecycle, and sneak-state observation unchanged.
|
||
- Left networking protocol, Python server code, and fake-client tooling
|
||
unchanged.
|
||
|
||
### Testing
|
||
|
||
- Ran `xmake build` from `plugin`; it succeeded.
|
||
- Pending: launch Fallout 4 through F4SE, load `Fallout4Together_Test.esp`, and
|
||
`coc F4TTestCell01`.
|
||
- Pending: confirm `Fallout4Together.log` reports the test ESP load status and
|
||
`F4T_RemotePlayerProxy` lookup result.
|
||
- Pending: confirm the existing placed proxy fallback still represents one fake
|
||
client and still holds/restores correctly.
|
||
|
||
### Known Issues
|
||
|
||
- Runtime actor spawning is intentionally not implemented in this stage.
|
||
|
||
### Next Steps
|
||
|
||
- Run the build and in-game lookup diagnostics.
|
||
- After lookup is proven stable, proceed to Stage 2: one game-thread-only runtime
|
||
spawn diagnostic in `F4TTestCell01` while keeping the placed reference fallback.
|
||
|
||
---
|
||
|
||
## 2026-06-02 - Runtime Proxy Spawning Stage 2
|
||
|
||
### Summary
|
||
|
||
Added a one-shot diagnostic runtime spawn attempt for `F4T_RemotePlayerProxy` in
|
||
`F4TTestCell01`. The spawn runs only from the game-thread proxy controller path
|
||
and remains separate from the placed proxy fallback, which is still the active
|
||
remote player representation.
|
||
|
||
### Files Changed
|
||
|
||
- `plugin/src/F4TProxyActorController.cpp`
|
||
- `plugin/include/F4TProxyActorController.h`
|
||
- `docs/dev-log.md`
|
||
- `docs/architecture.md`
|
||
|
||
### Details
|
||
|
||
- Added `kEnableRuntimeProxySpawnDiagnostic` as the explicit internal gate for
|
||
the Stage 2 diagnostic.
|
||
- Added one-shot diagnostic state flags and a separate `RE::ObjectRefHandle` for
|
||
the runtime-spawned actor.
|
||
- Reused the Stage 1 `F4T_RemotePlayerProxy` actor-base resolution path.
|
||
- Calls `RE::TESDataHandler::CreateReferenceAtLocation(RE::NEW_REFR_DATA&)`
|
||
only after the local player exists, the parent cell exists, the player is in
|
||
`F4TTestCell01`, and the actor base has resolved.
|
||
- Fills `NEW_REFR_DATA` for the interior test cell with the proxy actor base as
|
||
the bound object, the local player's parent cell as `interior`, no worldspace,
|
||
scripts initialized, and an X offset of `250.0`.
|
||
- Revalidates the returned handle immediately as a non-player `RE::Actor` in the
|
||
expected test cell.
|
||
- Logs a single readable attempt and a single success or failure outcome with
|
||
actor base, cell, position, handle, spawned reference, actor cast, and parent
|
||
cell details where available.
|
||
- Explicitly skips the diagnostic spawned reference during placed fallback
|
||
base-actor scanning so it is not selected as the active remote proxy.
|
||
- Leaves the spawned actor in place for the session; cleanup/despawn/hold
|
||
behavior is a later runtime proxy manager milestone.
|
||
- Left placed proxy movement, smoothing, special movement snapping, holding
|
||
lifecycle, sneak-state observation, networking, protocol, Python server code,
|
||
and fake-client tooling unchanged.
|
||
|
||
### Testing
|
||
|
||
- Ran `xmake build` from `plugin`; it succeeded.
|
||
- Pending: launch Fallout 4 through F4SE, load `Fallout4Together_Test.esp`, and
|
||
`coc F4TTestCell01`.
|
||
- Pending: confirm `Fallout4Together.log` shows actor-base resolution, one
|
||
runtime spawn attempt, and either spawn success details or a clear failure
|
||
reason.
|
||
- Pending: confirm the new runtime proxy actor appears near the player if spawn
|
||
succeeds.
|
||
- Pending: confirm the original placed proxy fallback still represents fake
|
||
clients and remains the active remote representation.
|
||
|
||
### Known Issues
|
||
|
||
- In-game runtime spawn behavior is not yet validated.
|
||
- Runtime-spawned actors are not cleaned up, held, or driven from remote-player
|
||
state in this stage.
|
||
|
||
### Next Steps
|
||
|
||
- Run the build and in-game diagnostic validation.
|
||
- If the diagnostic spawn is stable, plan a later stage for safe runtime proxy
|
||
lifecycle cleanup/holding before driving spawned proxies from remote state.
|
||
|
||
---
|
||
|
||
## 2026-06-02 - Runtime Proxy Spawning Stage 3
|
||
|
||
### Summary
|
||
|
||
Made the single runtime-spawned `F4T_RemotePlayerProxy` actor the preferred
|
||
active remote player representation in `F4TTestCell01`, while preserving
|
||
`F4TProxyRemotePlayer01REF` as the placed fallback if runtime spawn or runtime
|
||
handle validation is unavailable.
|
||
|
||
### Files Changed
|
||
|
||
- `plugin/src/F4TProxyActorController.cpp`
|
||
- `plugin/include/F4TProxyActorController.h`
|
||
- `docs/architecture.md`
|
||
- `docs/dev-log.md`
|
||
|
||
### Details
|
||
|
||
- Reframed the Stage 2 diagnostic runtime handle as the single Stage 3 runtime
|
||
proxy handle stored in `RE::ObjectRefHandle`.
|
||
- Added active-proxy selection that revalidates the runtime handle each
|
||
controller update and prefers it when it resolves as a non-player actor in
|
||
`F4TTestCell01`.
|
||
- Falls back to the placed reference path when runtime spawn has not completed,
|
||
spawn failed, the handle does not resolve, the handle does not resolve as an
|
||
actor, or runtime actor validation fails.
|
||
- Kept placed fallback lookup available through `F4TProxyRemotePlayer01REF` and
|
||
the ESP-local fallback FormID, and prevents placed fallback cell scanning from
|
||
selecting the runtime-spawned actor by base actor.
|
||
- Reused the existing single remote-player selection behavior: lowest
|
||
`playerId` only. Added TODO notes for Stage 4 one-proxy-per-player mapping.
|
||
- Routed existing smoothing, special movement snapping, same-cell gating,
|
||
holding-position lifecycle, restore behavior, and sneak-state observation
|
||
through the selected active proxy.
|
||
- Did not add visual sneak/crouch application, gameplay sync, animation sync,
|
||
multi-proxy mapping, protocol changes, networking changes, Python server
|
||
changes, or fake-client tooling changes.
|
||
|
||
### Testing
|
||
|
||
- Ran `xmake build` from `plugin`; it succeeded.
|
||
- Pending: launch Fallout 4 through F4SE, load `Fallout4Together_Test.esp`, and
|
||
`coc F4TTestCell01`.
|
||
- Pending: confirm runtime actor-base resolution, one runtime spawn, runtime
|
||
active selection, smooth fake-client movement, special movement snaps,
|
||
leave-cell holding, return-cell restore, disconnect holding, and sneak-state
|
||
observation.
|
||
- Pending: force or observe runtime validation failure and confirm the placed
|
||
fallback still represents the selected remote player.
|
||
|
||
### Known Issues
|
||
|
||
- Runtime support is still intentionally single-proxy only.
|
||
- Runtime proxy actors are held for reuse and are not disabled, deleted, made
|
||
invisible, or despawned in this milestone.
|
||
|
||
### Next Steps
|
||
|
||
- Run the build and in-game Stage 3 validation checklist.
|
||
- If Stage 3 is stable, proceed later to Stage 4 multi-proxy mapping with a
|
||
`remotePlayerId -> proxy actor handle` design.
|
||
|
||
---
|
||
|
||
## 2026-06-02 - Runtime Proxy Spawning Stage 3.1
|
||
|
||
### Summary
|
||
|
||
Added a temporary runtime proxy visibility diagnostic layer so the next in-game
|
||
test can prove whether the single runtime-spawned proxy actor is visibly
|
||
renderable, loaded, and being moved as expected.
|
||
|
||
### Files Changed
|
||
|
||
- `plugin/src/F4TProxyActorController.cpp`
|
||
- `docs/architecture.md`
|
||
- `docs/dev-log.md`
|
||
|
||
### Details
|
||
|
||
- Added post-spawn runtime proxy diagnostics for created ref FormID, actor base
|
||
FormID, handle value, resolved actor state, parent cell, proxy position, local
|
||
player position, and local distance.
|
||
- Added safe diagnostic checks for runtime form created/deleted/disabled flags,
|
||
available-in-game state, actor visibility, and `Get3D`/`GetFullyLoaded3D`/
|
||
`GetCurrent3D` pointer availability. Reference enable state remains logged as
|
||
unavailable rather than guessed.
|
||
- Added a temporary debug visibility mode,
|
||
`kDebugForceRuntimeProxyNearLocalPlayer`, which forces the runtime proxy near
|
||
the local player at X + 350 and Z + 20 after spawn and scales it to 1.5 for
|
||
easier visual confirmation.
|
||
- Added the runtime console helper log:
|
||
`prid <runtimeRef>` followed by `moveto player` or `enable`.
|
||
- Added explicit active source logs for runtime proxy selection and placed
|
||
fallback selection.
|
||
- Added throttled post-restore and post-move diagnostics with proxy source,
|
||
proxy FormID, assigned remote `playerId`, target remote position, actual proxy
|
||
position, local player position, local distance, same-cell result, and movement
|
||
mode (`restore`, `smooth`, or `snap`).
|
||
- Preserved Stage 3 single-proxy behavior: lowest remote `playerId`, same-cell
|
||
gating, holding lifecycle, smoothing, snap movement, movement-state
|
||
observation, placed fallback support, and skipping runtime-created actors
|
||
during fallback scans.
|
||
- Did not implement Stage 4 multi-proxy mapping or any
|
||
`remotePlayerId -> proxy actor handle` table.
|
||
|
||
### Testing
|
||
|
||
- Ran `xmake build` from `plugin`; it succeeded.
|
||
- Pending: launch two real clients in `F4TTestCell01` and confirm whether the
|
||
debug-forced runtime proxy appears near the local player.
|
||
- Pending: use the logged console helper command to run
|
||
`prid <runtimeRef>`, then `moveto player` or `enable`, to inspect the runtime
|
||
reference directly.
|
||
|
||
### Known Issues
|
||
|
||
- Runtime refs are created and moved according to logs, but visual rendering is
|
||
not yet confirmed in-game.
|
||
- The Stage 3.1 debug near-player placement and scale are temporary diagnostics
|
||
and should be disabled after visibility is proven.
|
||
|
||
### Next Steps
|
||
|
||
- Run two real clients, check for the debug-forced runtime proxy near the local
|
||
player, and try the console `prid <runtimeRef>` helper.
|
||
- Use the Stage 3.1 logs to decide whether the runtime actor is disabled,
|
||
missing 3D, unloaded, moving out of view, or valid but not rendered.
|
||
- Keep Stage 4 reserved for later multi-client
|
||
`remotePlayerId -> proxy actor handle` mapping.
|
||
|
||
---
|
||
|
||
## 2026-06-02 - Runtime Proxy Spawning Stage 3.2
|
||
|
||
### Summary
|
||
|
||
Added a temporary runtime proxy visibility hold so the single runtime-spawned
|
||
proxy remains near the local player before remote assignment. This should show
|
||
whether Fallout 4 ever creates visible 3D for the runtime-created actor ref when
|
||
it is kept in a loaded, nearby location.
|
||
|
||
### Files Changed
|
||
|
||
- `plugin/src/F4TProxyActorController.cpp`
|
||
- `docs/architecture.md`
|
||
- `docs/dev-log.md`
|
||
|
||
### Details
|
||
|
||
- Added `kDebugHoldRuntimeProxyNearLocalPlayer` and
|
||
`kDebugRuntimeProxyHoldSeconds` as temporary Stage 3.2 diagnostics.
|
||
- When the hold is active, the runtime proxy remains at the near-player debug
|
||
position for 15 seconds before remote assignment or remote movement resumes.
|
||
- Deferred runtime proxy restore and smooth/snap remote movement during the hold
|
||
while preserving remote-player snapshot storage, lowest `playerId` selection,
|
||
same-cell validation, placed fallback support, and the Stage 3 holding
|
||
lifecycle outside the hold.
|
||
- Added repeated hold diagnostics around every 2 seconds with runtime ref FormID,
|
||
actor base FormID, parent cell, proxy/local positions, local distance, safe
|
||
form-state checks, `Get3D`/`GetFullyLoaded3D`/`GetCurrent3D`, `IsVisible`,
|
||
hold elapsed time, hold remaining time, and whether the proxy had to be
|
||
re-placed near the local player.
|
||
- Added a hard hold conclusion: `PASS` if any safe 3D or visibility check becomes
|
||
true, otherwise `FAIL` with a note that placed fallback refs or another actor
|
||
spawn/init path may be safer before Stage 4.
|
||
- Kept safe post-spawn initialization limited to calls that build in the current
|
||
CommonLibF4 setup: `SetPosition`, `SetHeading`, `SetScale`,
|
||
`initializeScripts = true`, `initiallyDisabled = false`, and guarded
|
||
`Enable(false)` only if the spawned actor reports disabled. Forced 3D/load
|
||
update APIs remain logged as unavailable.
|
||
- Did not implement Stage 4 multi-proxy mapping or any
|
||
`remotePlayerId -> proxy actor handle` table.
|
||
|
||
### Testing
|
||
|
||
- Ran `xmake build` from `plugin`; it succeeded.
|
||
- Pending: run two real clients in `F4TTestCell01` and inspect whether the
|
||
Stage 3.2 hold diagnostics report any `Get3D`, `GetFullyLoaded3D`,
|
||
`GetCurrent3D`, or `IsVisible` transition to true.
|
||
|
||
### Known Issues
|
||
|
||
- The issue under investigation remains: runtime refs exist, are selected, and
|
||
can move in memory, but they may still fail to load visible actor 3D.
|
||
- The near-player hold is temporary diagnostic behavior and should be removed or
|
||
disabled after the runtime visibility path is proven or rejected.
|
||
|
||
### Next Steps
|
||
|
||
- Use the Stage 3.2 PASS/FAIL hold conclusion from an in-game test to decide
|
||
whether runtime-created actors can remain the preferred Stage 3 visual path.
|
||
- If Stage 3.2 fails, consider placed fallback refs or a different actor
|
||
spawn/init path before any Stage 4 multi-proxy work.
|
||
- Keep Stage 4 reserved for later
|
||
`remotePlayerId -> proxy actor handle` mapping.
|
||
|
||
---
|
||
|
||
## 2026-06-02 - Runtime Proxy Spawning Stage 3.3
|
||
|
||
### Summary
|
||
|
||
Added a temporary vanilla visible NPC diagnostic for the Stage 3 runtime spawn
|
||
path. The controller can now use Codsworth as the default diagnostic actor base
|
||
instead of `F4T_RemotePlayerProxy`, while keeping the same runtime spawn,
|
||
handle-validation, near-player hold, and repeated visibility diagnostic path.
|
||
|
||
### Files Changed
|
||
|
||
- `plugin/src/F4TProxyActorController.cpp`
|
||
- `docs/architecture.md`
|
||
- `docs/dev-log.md`
|
||
|
||
### Details
|
||
|
||
- Added `kDebugUseVanillaVisibleNpcForRuntimeSpawnTest` and
|
||
`kDebugVanillaVisibleNpcFormId` for the Stage 3.3 diagnostic. The candidate is
|
||
treated as Codsworth and resolved safely through the data-handler form lookup
|
||
path.
|
||
- Logged the diagnostic actor-base source as either `vanilla diagnostic NPC` or
|
||
`custom F4T_RemotePlayerProxy` so runtime spawn, visibility diagnostics, and
|
||
hold conclusions identify which base was tested.
|
||
- Validated that the Codsworth candidate resolves as `RE::TESNPC`, with logs for
|
||
resolved FormID, form type, editor ID, display name, and accepted state. If it
|
||
does not resolve as a valid `TESNPC`, the controller logs the failure and falls
|
||
back to the normal custom proxy actor base.
|
||
- Preserved the existing Stage 3 runtime spawn path: the selected `TESNPC*` is
|
||
passed into the current spawn function, with the same handle validation,
|
||
runtime proxy state, placed fallback support, Stage 3.2 hold, and repeated
|
||
diagnostics.
|
||
- Improved near-player debug placement to prefer forward-vector placement about
|
||
250 units in front of the local player at `Z + 20`, with a fixed X-offset
|
||
fallback if heading data is not usable.
|
||
- Expanded the hold conclusion for the vanilla diagnostic to report `PASS`,
|
||
`PARTIAL`, or `FAIL` based on whether `IsVisible` becomes true, 3D loads while
|
||
visibility remains false, or no 3D checks become true.
|
||
- Did not implement Stage 4 multi-proxy mapping or any
|
||
`remotePlayerId -> proxy actor handle` table.
|
||
|
||
### Testing
|
||
|
||
- Ran `xmake build` from `plugin`; it succeeded.
|
||
- Pending: launch Fallout 4 through F4SE, enter `F4TTestCell01`, and confirm
|
||
`Fallout4Together.log` reports whether the Stage 3.3 vanilla diagnostic is
|
||
active or fell back to `F4T_RemotePlayerProxy`.
|
||
- Pending: inspect the hold diagnostics and final PASS/PARTIAL/FAIL result to
|
||
determine whether the vanilla actor base appears visually through the same
|
||
runtime spawn path.
|
||
|
||
### Known Issues
|
||
|
||
- Stage 3.3 is temporary diagnostic behavior and does not prove the final proxy
|
||
representation path until a runtime actor is visually confirmed in-game.
|
||
- Stage 4 remains blocked until the visual representation problem is isolated to
|
||
either the custom actor base or the runtime spawn/init path.
|
||
|
||
### Next Steps
|
||
|
||
- Use the Stage 3.3 log result to decide whether to investigate
|
||
`F4T_RemotePlayerProxy` in Creation Kit or revisit the runtime spawn/init path
|
||
before any Stage 4 work.
|
||
- Keep Stage 4 reserved for later
|
||
`remotePlayerId -> proxy actor handle` mapping.
|
||
|
||
---
|
||
|
||
## 2026-06-02 - Runtime Proxy Spawning Stage 3.4
|
||
|
||
### Summary
|
||
|
||
Added an absolute FormID and existing-reference visibility diagnostic for
|
||
Codsworth. Stage 3.4 compares resolving Codsworth as a runtime spawn base against
|
||
moving the existing placed Codsworth reference near the local player as a
|
||
separate visibility control.
|
||
|
||
### Files Changed
|
||
|
||
- `plugin/src/F4TProxyActorController.cpp`
|
||
- `docs/architecture.md`
|
||
- `docs/dev-log.md`
|
||
|
||
### Details
|
||
|
||
- Added `kDebugUseExistingVanillaRefVisibilityTest`,
|
||
`kDebugCodsworthBaseFormId`, and `kDebugCodsworthRefFormId` for the Stage 3.4
|
||
diagnostic.
|
||
- Added absolute FormID lookup diagnostics for Codsworth base `000179FF` and
|
||
Codsworth placed ref `0001CA7D`, including requested FormID, resolved FormID,
|
||
form type, editor ID, display name, and whether the result casts to
|
||
`RE::TESNPC`, `RE::Actor`, or `RE::TESObjectREFR`.
|
||
- Updated the vanilla runtime-spawn diagnostic to use the Codsworth base only if
|
||
absolute FormID lookup resolves `000179FF` as `RE::TESNPC`. If it does not
|
||
resolve, the system logs the failure and keeps falling back to
|
||
`F4T_RemotePlayerProxy`.
|
||
- Added an existing placed-ref visibility control that moves the resolved
|
||
Codsworth placed reference near the local player for the diagnostic hold
|
||
duration, logs the same 3D/visibility checks, and defers normal proxy movement
|
||
while the control is active.
|
||
- Stored Codsworth's original position and original cell ID where available, then
|
||
attempts to restore the original position at the end of the control hold.
|
||
Cross-cell restoration remains logged as unavailable rather than guessed.
|
||
- Added a Stage 3.4 PASS/PARTIAL/FAIL conclusion to distinguish existing placed
|
||
ref visibility from runtime actor creation/init problems.
|
||
- Preserved the custom runtime proxy path, placed fallback support, same-cell
|
||
gating, lowest remote `playerId` selection, smoothing, snap movement, holding
|
||
lifecycle, remote state handling, fake client behavior, and server protocol.
|
||
- Did not implement Stage 4 multi-proxy mapping or any
|
||
`remotePlayerId -> proxy actor handle` table.
|
||
|
||
### Testing
|
||
|
||
- Ran `xmake build` from `plugin`; it succeeded.
|
||
- Pending: launch Fallout 4 through F4SE, enter `F4TTestCell01`, and confirm
|
||
`Fallout4Together.log` shows absolute lookup diagnostics for `000179FF` and
|
||
`0001CA7D`.
|
||
- Pending: confirm that a resolved Codsworth base is used for the vanilla
|
||
runtime-spawn diagnostic, or that the custom proxy fallback is logged clearly.
|
||
- Pending: confirm that a resolved Codsworth placed ref is moved near the local
|
||
player and reports a Stage 3.4 PASS/PARTIAL/FAIL conclusion.
|
||
|
||
### Known Issues
|
||
|
||
- Stage 3.4 is temporary diagnostic behavior and does not establish the final
|
||
remote player representation architecture.
|
||
- The existing placed-ref movement control only runs if `0001CA7D` resolves as
|
||
`RE::Actor`; if it resolves only as `RE::TESObjectREFR`, movement and
|
||
`IsVisible` diagnostics are logged as unavailable because those APIs are not
|
||
exposed on `TESObjectREFR` in the current CommonLibF4 setup.
|
||
- If only position restore is available, the diagnostic logs that cell restore is
|
||
unavailable rather than attempting a risky cross-cell move.
|
||
- Stage 4 remains blocked until visible representation is confirmed.
|
||
|
||
### Next Steps
|
||
|
||
- Use the Stage 3.4 control result to determine whether to investigate runtime
|
||
actor creation/init, the custom proxy actor base, or the visibility/placement
|
||
check itself before any Stage 4 work.
|
||
- Keep Stage 4 reserved for later
|
||
`remotePlayerId -> proxy actor handle` mapping.
|
||
|
||
---
|
||
|
||
## 2026-06-02 - Runtime Proxy Spawning Stage 3.5
|
||
|
||
### Summary
|
||
|
||
Added a temporary PlaceAtMe-equivalent diagnostic after manual console validation
|
||
confirmed `player.placeatme 000179FF 1` spawns visible Codsworth in
|
||
`F4TTestCell01`. The new diagnostic uses the local player as the placement
|
||
reference and compares that path against the existing `CreateReferenceAtLocation`
|
||
runtime spawn path.
|
||
|
||
### Files Changed
|
||
|
||
- `plugin/src/F4TProxyActorController.cpp`
|
||
- `docs/architecture.md`
|
||
- `docs/dev-log.md`
|
||
|
||
### Details
|
||
|
||
- Added `kDebugUseConsolePlaceAtMeSpawnTest` and
|
||
`kDebugPlaceAtMeActorBaseFormId` for the Stage 3.5 diagnostic.
|
||
- Uses the exposed CommonLibF4 `RE::Console::ExecuteCommand(const char*)`
|
||
wrapper to execute `player.placeatme 000179FF 1` once from the game-thread
|
||
proxy controller path.
|
||
- Snapshots pre-existing Codsworth candidate actors in the current cell before
|
||
executing the command, then scans the current cell for a newly created
|
||
Codsworth actor near the local player.
|
||
- Stores the resolved actor handle and runs a separate 15-second visibility hold
|
||
with repeated diagnostics for position, local-player distance, created/deleted
|
||
state, disabled state, available-in-game state, `Get3D`,
|
||
`GetFullyLoaded3D`, `GetCurrent3D`, and `IsVisible`.
|
||
- Defers normal proxy movement while the Stage 3.5 hold is active, without
|
||
changing remote state receive/update, placed fallback support, or the existing
|
||
Stage 3 runtime proxy path.
|
||
- Reports a Stage 3.5 PASS/PARTIAL/FAIL conclusion to distinguish the
|
||
local-player console placement path from `CreateReferenceAtLocation` runtime
|
||
spawning.
|
||
- Did not implement Stage 4 multi-proxy mapping or any
|
||
`remotePlayerId -> proxy actor handle` table.
|
||
|
||
### Testing
|
||
|
||
- Ran `xmake build` from `plugin`; it succeeded.
|
||
- Pending: launch Fallout 4 through F4SE, enter `F4TTestCell01`, and confirm
|
||
`Fallout4Together.log` shows the Stage 3.5 console command execution.
|
||
- Pending: confirm the diagnostic resolves a spawned Codsworth actor near the
|
||
local player and reports a Stage 3.5 PASS/PARTIAL/FAIL conclusion.
|
||
|
||
### Known Issues
|
||
|
||
- Stage 3.5 intentionally uses the console placement path as a temporary
|
||
diagnostic because manual validation proved that path can spawn visible
|
||
Codsworth. It is not the final proxy architecture.
|
||
- The diagnostic leaves the PlaceAtMe-spawned actor in the session; no safe
|
||
cleanup/despawn path is introduced in this stage.
|
||
- Stage 4 remains blocked until visible representation is confirmed and the
|
||
preferred spawn/init path is selected.
|
||
|
||
### Next Steps
|
||
|
||
- Compare the Stage 3.5 PlaceAtMe result against the existing
|
||
`CreateReferenceAtLocation` result to decide whether to replace the runtime
|
||
spawn path or investigate initialization differences before Stage 4.
|
||
- Keep Stage 4 reserved for later
|
||
`remotePlayerId -> proxy actor handle` mapping.
|
||
|
||
---
|
||
|
||
## 2026-06-02 - Runtime Proxy Spawning Stage 3.6
|
||
|
||
### Summary
|
||
|
||
Refined the PlaceAtMe-equivalent diagnostic so it isolates the Codsworth ref
|
||
created by the latest `player.placeatme 000179FF 1` command instead of
|
||
accidentally latching onto an older runtime Codsworth candidate.
|
||
|
||
### Files Changed
|
||
|
||
- `plugin/src/F4TProxyActorController.cpp`
|
||
- `docs/architecture.md`
|
||
- `docs/dev-log.md`
|
||
|
||
### Details
|
||
|
||
- Added Stage 3.6 constants for candidate isolation, post-spawn settle time, and
|
||
candidate search timeout.
|
||
- Snapshots known Codsworth candidates before executing PlaceAtMe, logging the
|
||
candidate count, FormIDs, 3D state, visibility, and position.
|
||
- Executes `player.placeatme 000179FF 1` once per diagnostic attempt, then polls
|
||
for a Codsworth actor whose FormID was not present in the pre-spawn snapshot.
|
||
- Logs each considered candidate with old/new status, position, distance from
|
||
the local player, loaded-3D checks, and `IsVisible`.
|
||
- Holds a newly isolated PlaceAtMe actor at its original console-spawned location
|
||
during the initial settle window instead of immediately moving it near the
|
||
player or assigning it as the active remote proxy.
|
||
- Reports a Stage 3.6 PASS/PARTIAL/FAIL conclusion and reminds manual testers
|
||
that pause-menu or alt-tab visibility changes point toward render/process
|
||
update flushing rather than actor-base selection.
|
||
|
||
### Testing
|
||
|
||
- Ran `xmake build` from `plugin`; it succeeded.
|
||
- Pending: launch Fallout 4 through F4SE, enter `F4TTestCell01`, and confirm the
|
||
log shows a Stage 3.6 pre-placeatme snapshot, one command execution, new
|
||
candidate isolation, settle diagnostics, and a final PASS/PARTIAL/FAIL result.
|
||
|
||
### Known Issues
|
||
|
||
- Stage 3.6 is still diagnostic-only and leaves the PlaceAtMe-spawned actor in
|
||
the session.
|
||
- Manual testing showed newly spawned Codsworth may appear only after opening
|
||
the pause menu or alt-tabbing, so render/process refresh behavior remains the
|
||
main open question.
|
||
- Stage 4 remains blocked until visible proxy spawning is fully understood.
|
||
|
||
### Next Steps
|
||
|
||
- Use the Stage 3.6 logs to determine whether PlaceAtMe visibility depends on a
|
||
delayed render/process update before selecting a final runtime proxy spawn
|
||
strategy.
|
||
- Keep Stage 4 reserved for later
|
||
`remotePlayerId -> proxy actor handle` mapping.
|
||
|
||
---
|
||
|
||
## 2026-06-02 - Runtime Proxy Spawning Stage 3.7
|
||
|
||
### Summary
|
||
|
||
Made the preferred Stage 3 runtime proxy spawn path PlaceAtMe-backed instead of
|
||
`CreateReferenceAtLocation`, reusing the successful Stage 3.6 candidate
|
||
isolation and settle window before the actor is promoted into normal remote
|
||
movement.
|
||
|
||
### Files Changed
|
||
|
||
- `plugin/src/F4TProxyActorController.cpp`
|
||
- `docs/architecture.md`
|
||
- `docs/dev-log.md`
|
||
|
||
### Details
|
||
|
||
- Added Stage 3.7 constants for enabling PlaceAtMe-backed runtime spawning,
|
||
trying the custom proxy base first, retaining a Codsworth diagnostic fallback,
|
||
and controlling candidate search/settle timings.
|
||
- Resolves `F4T_RemotePlayerProxy` through the existing plugin-local FormID path
|
||
and uses the resolved runtime FormID in `player.placeatme <baseFormId> 1`.
|
||
- Snapshots existing actors matching the selected base before spawning, then
|
||
polls for a newly isolated actor whose FormID was not present in the snapshot.
|
||
- Preserves the isolated actor at its original PlaceAtMe spawn location during
|
||
the settle window and defers active proxy selection/remote movement until the
|
||
settle completes.
|
||
- Promotes the settled actor into the existing single runtime proxy handle so
|
||
same-cell gating, lowest remote `playerId` selection, restore movement,
|
||
smoothing, snap movement, holding lifecycle, and movement-state observation
|
||
continue through the existing Stage 3 path.
|
||
- Skips the old `CreateReferenceAtLocation` runtime spawn when Stage 3.7 is
|
||
enabled, while keeping that code path available behind the Stage 3.7 guard.
|
||
- Disables the standalone Stage 3.4 existing-ref control and Stage 3.6 Codsworth
|
||
diagnostic while Stage 3.7 is active so they do not interfere with proxy
|
||
spawning.
|
||
- Leaves the placed CK fallback proxy available if PlaceAtMe-backed spawning
|
||
fails.
|
||
|
||
### Testing
|
||
|
||
- Ran `xmake build` from `plugin`; it succeeded.
|
||
- Pending: launch Fallout 4 through F4SE, enter `F4TTestCell01`, and confirm the
|
||
log shows Stage 3.7 custom proxy base resolution, one PlaceAtMe command per
|
||
attempt, candidate isolation, settle diagnostics, promotion, and normal remote
|
||
movement after promotion.
|
||
|
||
### Known Issues
|
||
|
||
- Codsworth fallback is diagnostic-only and indicates the custom
|
||
`F4T_RemotePlayerProxy` base still needs Creation Kit investigation if it is
|
||
the only PlaceAtMe-backed actor that becomes active.
|
||
- Stage 3.7 remains single-proxy only. It does not add a
|
||
`remotePlayerId -> proxy actor handle` mapping.
|
||
- Stage 4 remains blocked until single PlaceAtMe-backed proxy movement is
|
||
verified in game.
|
||
|
||
### Next Steps
|
||
|
||
- Test the custom PlaceAtMe-backed proxy in game with remote movement and compare
|
||
behavior against the Codsworth fallback if needed.
|
||
- Keep Stage 4 reserved for later
|
||
`remotePlayerId -> proxy actor handle` mapping.
|
||
|
||
---
|
||
|
||
## 2026-06-02 - Runtime Proxy Spawning Stage 4
|
||
|
||
### Summary
|
||
|
||
Added the first runtime proxy actor manager: the plugin now keeps a
|
||
controller-local runtime proxy slot map keyed by remote `playerId`, spawning
|
||
proxy actors only when valid same-cell remote state needs representation.
|
||
|
||
### Files Changed
|
||
|
||
- `plugin/include/F4TProxyActorController.h`
|
||
- `plugin/src/F4TProxyActorController.cpp`
|
||
- `docs/architecture.md`
|
||
- `docs/dev-log.md`
|
||
|
||
### Details
|
||
|
||
- Added `ProxyActorSlot` and `g_proxySlots`, capped by
|
||
`kMaxRuntimeProxyActors = 4` for this test milestone.
|
||
- Moved runtime spawning behind remote-player selection: the controller now
|
||
reads a remote snapshot, sorts by `playerId`, validates transform data, checks
|
||
same-cell compatibility, then creates or updates a slot for that player.
|
||
- Converted the PlaceAtMe-backed spawn/candidate/settle flow into slot-local
|
||
state while keeping a controller-level guard so only one PlaceAtMe spawn
|
||
attempt runs at a time.
|
||
- Stores promoted runtime proxies as `RE::ObjectRefHandle` values on the slot
|
||
and revalidates the handle before movement.
|
||
- Preserves per-proxy smoothing and snap behavior for `cell_change`,
|
||
`worldspace_change`, and `teleport`.
|
||
- Preserves sneak observation as per-slot logging only; no visual sneak/crouch
|
||
application was added.
|
||
- Holds only the affected player’s proxy when that player leaves the cell or
|
||
disappears. Slots stay session-sticky and are not reassigned to other
|
||
`playerId` values yet.
|
||
- Keeps `F4TProxyRemotePlayer01REF` as a single placed fallback for one selected
|
||
remote player if runtime spawning fails.
|
||
- Networking, protocol, Python server, fake-client tooling, gameplay sync,
|
||
animation sync, combat, inventory, quest, settlement, weapon/projectile, and
|
||
interaction sync were intentionally left unchanged.
|
||
|
||
### Testing
|
||
|
||
- Ran `xmake build` from `plugin`; it succeeded.
|
||
- Pending manual validation: launch Fallout 4 through F4SE, load
|
||
`Fallout4Together_Test.esp`, `coc F4TTestCell01`, start the Dev Server GUI,
|
||
add multiple fake clients, and confirm one independent runtime proxy per
|
||
`playerId` up to the max.
|
||
- Pending manual validation: confirm Leave Cell, Return To Cell, and disconnect
|
||
hold/restore only the affected player’s proxy while other proxies remain
|
||
active.
|
||
- Pending manual validation: connect a second real Fallout 4 client and confirm
|
||
it follows the same `RemotePlayerState[playerId]` path as GUI fake clients.
|
||
|
||
### Known Issues
|
||
|
||
- Runtime slots are session-sticky and are not reassigned after disconnect yet,
|
||
so long sessions with many unique player IDs can exhaust the small Stage 4
|
||
test cap.
|
||
- Runtime actors are still held instead of safely despawned or deleted.
|
||
- The placed fallback remains single-player only by design.
|
||
|
||
### Next Steps
|
||
|
||
- Manually validate Stage 4 with two or more fake clients in `F4TTestCell01`.
|
||
- Add a later cleanup/reuse milestone once a safe runtime actor cleanup path is
|
||
confirmed.
|
||
|
||
---
|
||
|
||
## 2026-06-03 - Runtime Proxy Manager Cleanup and Stability Pass
|
||
|
||
### Summary
|
||
|
||
Cleaned up the Stage 4 runtime proxy slot lifecycle so disconnected held slots
|
||
can become reusable after a grace period instead of occupying the small runtime
|
||
proxy cap forever.
|
||
|
||
### Files Changed
|
||
|
||
- `plugin/src/F4TProxyActorController.cpp`
|
||
- `docs/architecture.md`
|
||
- `docs/dev-log.md`
|
||
|
||
### Details
|
||
|
||
- Refined runtime proxy slot lifecycle states to distinguish active, held because
|
||
no valid remote transform, held because the remote player left the cell, held
|
||
because the remote player disconnected/disappeared, reusable, and spawn-failed
|
||
slots.
|
||
- Added a 30 second disconnected-slot reuse delay. Disconnected slots are first
|
||
moved to the holding position and marked held; only after the delay do they
|
||
become reusable.
|
||
- Updated slot acquisition to prefer an existing slot for the same `playerId`,
|
||
create a new slot while under `kMaxRuntimeProxyActors`, and reuse an eligible
|
||
disconnected slot at the cap before logging a throttled max-cap warning.
|
||
- Preserved connected-but-left-cell slots for their original `playerId`, so
|
||
Leave Cell / Return To Cell keeps the same proxy assignment.
|
||
- Reset movement smoothing flags and sneak observation state when a reusable slot
|
||
is reassigned, then snap the proxy to the new remote player's current transform
|
||
before normal smoothing resumes.
|
||
- Kept server `playerId` allocation, networking packets, Python server code, fake
|
||
client tooling, placed fallback behavior, same-cell gating, movement
|
||
smoothing/snap behavior, and data-only sneak observation unchanged.
|
||
- Continued to avoid disabling, deleting, alpha-hiding, or otherwise marking
|
||
runtime actors for deletion; held actors remain at the hidden in-cell holding
|
||
position.
|
||
|
||
### Testing
|
||
|
||
- `xmake build` from the repository root prompts because there is no root
|
||
`xmake.lua`.
|
||
- Ran `xmake build` from `plugin`; it succeeded and rebuilt
|
||
`Fallout4Together.dll`.
|
||
- Pending manual validation: launch Fallout 4 through F4SE, load
|
||
`Fallout4Together_Test.esp`, `coc F4TTestCell01`, start the Dev Server GUI,
|
||
add fake clients up to the cap, disconnect clients, wait for the reuse delay,
|
||
and confirm a later higher `playerId` can reuse an old disconnected slot.
|
||
- Pending manual validation: confirm Leave Cell / Return To Cell preserves a
|
||
connected player's original slot and that Walk Circle, Walk To Player,
|
||
Teleport Test, and sneak-state logging still behave as before.
|
||
|
||
### Known Issues
|
||
|
||
- Runtime actors are still held instead of safely despawned or deleted.
|
||
- The placed fallback remains single-player only by design.
|
||
- In-game validation for this cleanup pass is still pending.
|
||
|
||
### Next Steps
|
||
|
||
- Run the manual GUI fake-client lifecycle test in `F4TTestCell01`.
|
||
- If reuse validates cleanly, keep the next milestone focused on diagnostics or
|
||
safe actor cleanup research rather than gameplay sync expansion.
|
||
|
||
---
|
||
|
||
## 2026-06-03 - Proxy AI Movement Intent Crash-Safety Gate
|
||
|
||
### Summary
|
||
|
||
Default-disabled the experimental AI movement/pathing/package suppression path
|
||
after manual testing showed a crash during runtime proxy spawn/settle with a fake
|
||
client connected.
|
||
|
||
### Files Changed
|
||
|
||
- `plugin/src/F4TProxyActorController.cpp`
|
||
- `docs/dev-log.md`
|
||
|
||
### Details
|
||
|
||
- Added `kEnableProxyAIMovementIntentSuppression = false` so the risky follow-up
|
||
suppression path is compiled behind an explicit safety flag.
|
||
- Gated the `SuppressProxyAIMovementIntent(...)` calls from
|
||
`NeutralizeProxyActor(...)` so the default runtime path no longer executes
|
||
pathing status checks, package interruption, do-nothing package calls,
|
||
command clearing, or AI process movement target writes.
|
||
- Preserved the earlier neutralization behavior that was working better:
|
||
`StopCombat()` only after `IsInCombat()`, combat target reset, attack/hostile
|
||
flag reset, passive actor values, combat-oriented process flags, and throttled
|
||
logs.
|
||
- Left runtime proxy spawning, per-`playerId` slots, spawn-at-remote-position,
|
||
movement smoothing/snapping, held/reusable lifecycle, placed fallback behavior,
|
||
networking, protocol, Python server, and fake-client tooling unchanged.
|
||
- Added a TODO in code noting that AI movement intent suppression caused a crash
|
||
and must be re-investigated in smaller isolated steps before it is enabled
|
||
again.
|
||
|
||
### Testing
|
||
|
||
- Ran `xmake build` from `plugin`; it succeeded and rebuilt
|
||
`Fallout4Together.dll`.
|
||
- Pending manual validation: start server, add fake client, connect Fallout 4,
|
||
and confirm runtime proxy spawn/settle no longer crashes.
|
||
- Pending manual validation: re-test multiple clients, movement, held/reusable
|
||
lifecycle, and attack/bump behavior.
|
||
|
||
### Known Issues
|
||
|
||
- Attacking or bumping a proxy may still briefly trigger walk-away animation
|
||
intent because the experimental movement/pathing/package suppression is now
|
||
disabled by default for crash safety.
|
||
- Runtime actors are still held instead of safely despawned or deleted.
|
||
|
||
### Next Steps
|
||
|
||
- Re-test the fake-client spawn path immediately.
|
||
- Revisit AI movement intent suppression later with a diagnostics-only pass first,
|
||
then re-enable one write/call at a time if each step is stable in-game.
|
||
|
||
---
|
||
|
||
## 2026-06-03 - Proxy AI Movement Intent Suppression Patch
|
||
|
||
### Summary
|
||
|
||
Reduced the remaining autonomous walk-away/flee animation intent that could play
|
||
after a runtime proxy was attacked or bumped, while keeping transform authority
|
||
driven by `RemotePlayerState`.
|
||
|
||
### Files Changed
|
||
|
||
- `plugin/src/F4TProxyActorController.cpp`
|
||
- `docs/architecture.md`
|
||
- `docs/dev-log.md`
|
||
|
||
### Details
|
||
|
||
- Added `SuppressProxyAIMovementIntent(...)` and call it through the existing
|
||
`NeutralizeProxyActor(...)` path after spawn promotion, reusable slot
|
||
reassignment, throttled active maintenance, combat neutralization, combat
|
||
target clearing, and holding.
|
||
- The helper uses only exposed CommonLibF4 APIs/fields: pathing status checks,
|
||
`SetAvoidanceDisabled(true)`, `EndInterruptPackage(false)`,
|
||
`InitiateDoNothingPackage()`, command clearing, and safe `AIProcess` target
|
||
handle resets.
|
||
- Detects flee/alarm/search/avoid/bump/travel/follow/patrol-style package types
|
||
when exposed through the current package and logs throttled suppression events.
|
||
- Leaves transform smoothing, snapping, spawn-at-remote-position, held slots,
|
||
reusable slots, placed fallback behavior, networking, protocol, Python server,
|
||
fake-client tooling, gameplay sync, and animation sync unchanged.
|
||
- Added TODOs for movement-controller, actor-mover, procedure-index, preferred
|
||
speed, and animation-layer resets because no clearly safe direct path-cancel
|
||
or animation reset API has been validated yet.
|
||
|
||
### Testing
|
||
|
||
- Ran `xmake build` from `plugin`; it succeeded and rebuilt
|
||
`Fallout4Together.dll`.
|
||
- Pending manual validation: attack a runtime proxy and confirm walk-away
|
||
animation intent is gone or reduced.
|
||
- Pending manual validation: bump a runtime proxy and confirm no persistent
|
||
autonomous walk/flee animation overrides remote-state transform control.
|
||
- Pending manual validation: re-test multiple clients, spawn-at-remote-position,
|
||
smoothing, snap movement types, held/disconnected/reusable slots, and Leave
|
||
Cell / Return To Cell.
|
||
|
||
### Known Issues
|
||
|
||
- Runtime actors are still held instead of safely despawned or deleted.
|
||
- Direct movement-controller/path-destination clearing is still not implemented
|
||
because the local CommonLibF4 headers do not expose a validated safe API for it.
|
||
- If the engine keeps an animation-layer response active after package
|
||
suppression, a future patch may need a narrowly validated animation reset that
|
||
does not block remote-driven animation sync.
|
||
|
||
### Next Steps
|
||
|
||
- Manually validate attack and bump behavior in `F4TTestCell01`.
|
||
- If walk-away animation still persists, inspect runtime logs for pathing,
|
||
package type, process target, and no-safe-API messages before adding deeper AI
|
||
or animation control.
|
||
|
||
---
|
||
|
||
## 2026-06-03 - Runtime Proxy Neutralization Pass
|
||
|
||
### Summary
|
||
|
||
Added a safe runtime proxy actor neutralization pass so Stage 4 runtime proxies
|
||
remain passive visual representations controlled by remote player state instead
|
||
of entering persistent NPC combat or pursuit behavior.
|
||
|
||
### Files Changed
|
||
|
||
- `plugin/src/F4TProxyActorController.cpp`
|
||
- `docs/architecture.md`
|
||
- `docs/dev-log.md`
|
||
|
||
### Details
|
||
|
||
- Added `NeutralizeProxyActor(...)` on the game-thread proxy controller path and
|
||
only call it on resolved runtime proxy slot actors, never the local player.
|
||
- Neutralization now runs after runtime proxy spawn promotion, when reusable
|
||
slots are reassigned, periodically while active, and when proxies are moved or
|
||
kept in holding.
|
||
- The helper uses confirmed available CommonLibF4 actor APIs/fields to stop
|
||
combat, clear the current combat target handle, reset safe attack/hostility
|
||
flags, set passive aggression/confidence/assistance actor values, and suppress
|
||
combat-oriented AI process flags.
|
||
- Kept runtime proxies renderable and reusable. No `Disable()`, `Enable()`,
|
||
alpha/invisibility, `SetWantsDelete`, deletion, protocol, networking, Python
|
||
server, fake-client, gameplay sync, animation sync, combat sync, inventory
|
||
sync, quest sync, settlement sync, or placed fallback changes were made.
|
||
- Added TODOs for package/procedure clearing and collision/stagger suppression
|
||
because no clearly safe CommonLibF4 API has been validated for those yet.
|
||
|
||
### Testing
|
||
|
||
- Ran `xmake build` from `plugin`; it succeeded and rebuilt
|
||
`Fallout4Together.dll`.
|
||
- Pending manual validation: attack a runtime proxy and confirm it does not chase
|
||
or attack the local player.
|
||
- Pending manual validation: bump a runtime proxy and confirm no persistent AI
|
||
behavior overrides remote-state transform control.
|
||
- Pending manual validation: re-test multiple clients, spawn-at-remote-position,
|
||
smoothing, `cell_change`/`worldspace_change`/`teleport` snapping,
|
||
held/disconnected/reusable slots, and Leave Cell / Return To Cell.
|
||
|
||
### Known Issues
|
||
|
||
- Runtime actors are still held instead of safely despawned or deleted.
|
||
- Package/procedure clearing and collision/stagger suppression need further
|
||
investigation before adding more invasive AI control.
|
||
- The placed fallback remains single-player only by design.
|
||
|
||
### Next Steps
|
||
|
||
- Run the manual attack/bump validation in `F4TTestCell01`.
|
||
- If proxies can still enter transient stagger animations, investigate a
|
||
separate collision/stagger-safe puppet-control pass that does not block future
|
||
animation sync.
|
||
|
||
---
|
||
|
||
## 2026-06-03 - Held Runtime Proxy Stability Patch
|
||
|
||
### Summary
|
||
|
||
After manual testing showed some disconnected held runtime proxies could visually
|
||
reappear even while their remote players were absent, tightened the held-proxy
|
||
positioning path.
|
||
|
||
### Files Changed
|
||
|
||
- `plugin/src/F4TProxyActorController.cpp`
|
||
- `docs/architecture.md`
|
||
- `docs/dev-log.md`
|
||
|
||
### Details
|
||
|
||
- Kept the hidden holding-position approach and continued to avoid disabling,
|
||
deleting, alpha-hiding, or marking actors for deletion.
|
||
- Moved the shared holding base farther away/lower and gave each runtime proxy
|
||
slot a stable holding-position index with 500 unit spacing so held actors do
|
||
not stack on the same coordinates.
|
||
- Added a game-thread held-position correction pass for `HeldNoRemote`,
|
||
`HeldLeftCell`, `HeldDisconnected`, and `Reusable` slots. If a held proxy
|
||
drifts more than 25 units from its assigned holding position, the controller
|
||
moves it back and logs a throttled correction message.
|
||
- Preserved disconnected-slot reuse behavior: `HeldDisconnected` still becomes
|
||
`Reusable` after the delay, reusable slots can still be reassigned, and
|
||
`HeldLeftCell` slots remain reserved for connected players.
|
||
- Networking, protocol, Python server code, fake-client tooling, placed fallback
|
||
behavior, movement smoothing/snap behavior, same-cell gating, and data-only
|
||
sneak observation were unchanged.
|
||
|
||
### Testing
|
||
|
||
- Ran `xmake build` from `plugin`; it succeeded and rebuilt
|
||
`Fallout4Together.dll`.
|
||
- Pending manual validation: repeat the GUI fake-client disconnect test that
|
||
previously caused held proxies to visually reappear.
|
||
|
||
### Known Issues
|
||
|
||
- Runtime actors are still held instead of safely despawned or deleted.
|
||
- The placed fallback remains single-player only by design.
|
||
- In-game validation of the held-position correction is still pending.
|
||
|
||
### Next Steps
|
||
|
||
- Re-test removing all fake clients and confirm held proxies stay out of view.
|
||
- Re-test reuse after the grace period and Leave Cell / Return To Cell.
|
||
|
||
---
|
||
|
||
## 2026-06-03 - Runtime Proxy Spawn Polish Patch
|
||
|
||
### Summary
|
||
|
||
Polished Stage 4 runtime proxy spawning so newly isolated runtime actors are
|
||
placed at the remote player's current transform immediately instead of visually
|
||
starting near the local player before snapping away.
|
||
|
||
### Files Changed
|
||
|
||
- `plugin/src/F4TProxyActorController.cpp`
|
||
- `docs/architecture.md`
|
||
- `docs/dev-log.md`
|
||
|
||
### Details
|
||
|
||
- Added slot-local initial remote spawn transform tracking for runtime proxy
|
||
slots.
|
||
- Captures the remote player's current valid same-cell transform before starting
|
||
the Stage 4 PlaceAtMe-backed spawn path.
|
||
- Applies that transform as soon as the new PlaceAtMe-backed actor is isolated
|
||
and again at promotion, setting both position and heading before normal remote
|
||
movement resumes.
|
||
- Reusable slot reassignment continues to snap directly to the newly assigned
|
||
remote player's current transform and resets movement/sneak observation state.
|
||
- Kept movement smoothing and snap behavior unchanged: normal movement still
|
||
lerps after initialization, while `cell_change`, `worldspace_change`, and
|
||
`teleport` still snap.
|
||
- Networking, protocol, Python server code, fake-client tooling, placed fallback
|
||
behavior, held/reusable slot behavior, same-cell gating, and data-only sneak
|
||
observation were unchanged.
|
||
|
||
### Testing
|
||
|
||
- Ran `xmake build` from `plugin`; it succeeded and rebuilt
|
||
`Fallout4Together.dll`.
|
||
- Pending manual validation: add a GUI fake client and confirm its proxy no
|
||
longer briefly appears near the local player before moving to the fake client's
|
||
remote transform.
|
||
- Pending manual validation: re-test reusable slot reassignment, Walk To Player,
|
||
Walk Circle, Leave Cell, Return To Cell, disconnect, and reconnect.
|
||
|
||
### Known Issues
|
||
|
||
- The Stage 4 spawn path still uses PlaceAtMe-backed candidate isolation, so the
|
||
actor is moved to the remote transform immediately after isolation rather than
|
||
replacing the underlying diagnostic spawn mechanism.
|
||
- Runtime actors are still held instead of safely despawned or deleted.
|
||
|
||
### Next Steps
|
||
|
||
- Manually verify new-client spawn visuals in `F4TTestCell01`.
|
||
- If a visible local-player flash remains before candidate isolation, investigate
|
||
replacing the PlaceAtMe-backed Stage 4 spawn backend in a separate milestone.
|
||
|
||
---
|
||
|
||
## 2026-06-03 - Proxy Animation State Debug Pass
|
||
|
||
### Summary
|
||
|
||
Added data-only per-slot animation state diagnostics for runtime proxy actors so
|
||
the proxy controller can observe movement and animation-related state assigned to
|
||
each remote `playerId` without visually applying animation.
|
||
|
||
### Files Changed
|
||
|
||
- `plugin/src/F4TProxyActorController.cpp`
|
||
- `docs/architecture.md`
|
||
- `docs/dev-log.md`
|
||
|
||
### Details
|
||
|
||
- Added per-runtime-proxy-slot debug tracking for moving, speed bucket,
|
||
sprinting, sneaking, jumping, weapon drawn, and movement type.
|
||
- Added transition-only runtime proxy animation state logs that include the
|
||
remote `playerId` and resolved proxy actor FormID.
|
||
- Movement speed diagnostics use coarse buckets (`idle`, `slow`, `walk`, `run`,
|
||
`sprint`) so small float changes do not spam the log.
|
||
- Reset animation debug state when slots are created, reassigned, held, or pass
|
||
through the existing slot observation reset helper.
|
||
- Kept this pass data/debug only. It does not apply visual animation, force
|
||
animation graph events, write animation graph variables, call `PerformAction`,
|
||
write `ActorState`, or add package/pathing/AI movement changes.
|
||
- Networking, protocol, Python server code, fake-client tooling, placed fallback
|
||
behavior, spawn-at-remote-position, movement smoothing, snap behavior,
|
||
same-cell gating, held/reusable lifecycle, and combat/AI neutralization were
|
||
unchanged.
|
||
|
||
### Testing
|
||
|
||
- Ran `xmake build` from `plugin`; it succeeded and rebuilt
|
||
`Fallout4Together.dll`.
|
||
- Pending manual validation: launch through F4SE, start the server, add fake
|
||
clients in `F4TTestCell01`, and confirm per-player initial and transition
|
||
animation state logs without visual animation changes.
|
||
|
||
### Known Issues
|
||
|
||
- Speed bucket thresholds are conservative diagnostics and still need tuning
|
||
against real Fallout 4 movement units before any future visual locomotion work.
|
||
- Visual animation application remains intentionally unimplemented.
|
||
|
||
### Next Steps
|
||
|
||
- Investigate safe visual sneak/crouch application.
|
||
- Investigate weapon drawn visual application.
|
||
- Investigate locomotion animation graph behavior.
|
||
- Investigate jump animation behavior.
|
||
- Research safe animation graph variable/event reads or writes before any visual
|
||
animation milestone.
|
||
|
||
---
|
||
|
||
## 2026-06-03 - Proxy Visual Sneak Scaffolding
|
||
|
||
### Summary
|
||
|
||
Added default-off runtime proxy visual sneak scaffolding so future sneak/crouch
|
||
visual experiments have isolated slot-local gates and reset tracking without
|
||
mutating actors by default.
|
||
|
||
### Files Changed
|
||
|
||
- `plugin/src/F4TProxyActorController.cpp`
|
||
- `docs/dev-log.md`
|
||
|
||
### Details
|
||
|
||
- Added default-off `kEnableProxyVisualSneakSync` and
|
||
`kEnableProxyVisualSneakExperimentalGraph` flags near the existing proxy
|
||
controller constants.
|
||
- Added runtime-proxy-slot visual sneak tracking separate from existing
|
||
animation debug observation state.
|
||
- Added a runtime-proxy-only game-thread helper that reads proxy-side
|
||
`IsSneaking()`, stance, and `forceSneak` diagnostics and logs once per slot
|
||
when no visual sneak method is enabled.
|
||
- Kept the helper non-mutating with the default flags. It does not call
|
||
animation graph events, write animation graph variables, call `PerformAction`,
|
||
write `ActorState`, or use package/procedure/AI crouch behavior.
|
||
- Left a TODO for a separately approved future experiment using
|
||
`RE::Actor::SetSneaking(remoteState.isSneaking)` behind the existing disabled
|
||
gate.
|
||
- Reset visual sneak tracking through the existing slot observation reset path,
|
||
when runtime proxies are moved to holding, and when disconnected slots become
|
||
reusable.
|
||
- Left networking, protocol, Python server code, fake-client tooling, placed
|
||
fallback behavior, runtime spawning, movement smoothing, snap behavior,
|
||
same-cell gating, held/reusable lifecycle, and combat/AI neutralization
|
||
unchanged.
|
||
|
||
### Testing
|
||
|
||
- Ran `xmake build` from `plugin`; it succeeded and rebuilt
|
||
`Fallout4Together.dll`.
|
||
- Pending manual validation: use the Dev Server GUI to add fake clients, toggle
|
||
sneak on/off, and confirm existing animation debug logs plus one-time disabled
|
||
visual sneak diagnostics without any visual crouch by default.
|
||
|
||
### Known Issues
|
||
|
||
- Visual sneak/crouch application remains intentionally unimplemented.
|
||
- The safe visual writer candidate still needs a manually approved
|
||
`RE::Actor::SetSneaking(...)` experiment before any proxy crouch behavior is
|
||
enabled.
|
||
|
||
### Next Steps
|
||
|
||
- Manually validate fake-client sneak toggles in `F4TTestCell01`.
|
||
- If approved later, test `RE::Actor::SetSneaking(...)` behind the disabled
|
||
visual sneak gate with explicit clear handling for false, disconnect, hold,
|
||
reassignment, and reuse.
|
||
|
||
---
|
||
|
||
## 2026-06-03 - Proxy Visual Sneak Stage 1 SetSneaking Experiment
|
||
|
||
### Summary
|
||
|
||
Enabled the runtime-proxy-only visual sneak experiment that calls
|
||
`RE::Actor::SetSneaking(...)` on assigned runtime proxy actors when the matching
|
||
remote player's `isSneaking` state changes.
|
||
|
||
### Files Changed
|
||
|
||
- `plugin/src/F4TProxyActorController.cpp`
|
||
- `docs/dev-log.md`
|
||
|
||
### Details
|
||
|
||
- Set `kEnableProxyVisualSneakSync` to `true` and kept
|
||
`kEnableProxyVisualSneakExperimentalGraph` set to `false`.
|
||
- Kept `kEnableProxyAIMovementIntentSuppression` disabled.
|
||
- Replaced the enabled visual-sneak TODO path with a per-slot transition writer
|
||
that calls `RE::Actor::SetSneaking(a_remotePlayer.isSneaking)` only when the
|
||
requested visual sneak state differs from the last applied state, or when no
|
||
visual sneak state has been applied to that slot yet.
|
||
- Tracked applied visual sneak state with the existing
|
||
`hasAppliedVisualSneakingState` and `lastAppliedVisualSneakingState` fields.
|
||
- Added transition-only logs for applied visual sneak `true`, applied visual
|
||
sneak `false`, and `SetSneaking(...)` returning `false`.
|
||
- Added lifecycle clear handling that calls `SetSneaking(false)` for a valid
|
||
runtime proxy actor before hold/disconnect, reusable transition, or
|
||
reassignment clears visual sneak tracking.
|
||
- Preserved existing animation debug logs, runtime proxy spawn/movement/snap
|
||
behavior, same-cell gating, held/disconnected/reusable lifecycle, combat/AI
|
||
neutralization, placed fallback availability, and the game-thread proxy
|
||
controller boundary.
|
||
- Left networking, protocol, Python server code, fake-client tooling, local
|
||
player send logic, remote state storage, animation graph events/variables,
|
||
`PerformAction`, `ActorState` writes, package/pathing/AI movement changes, and
|
||
vendored CommonLibF4 unchanged.
|
||
|
||
### Testing
|
||
|
||
- Ran `xmake build` from `plugin`; it succeeded and rebuilt
|
||
`Fallout4Together.dll`.
|
||
- Manual test placeholder: start the Dev Server GUI, launch Fallout 4 through
|
||
F4SE, `coc F4TTestCell01`, add fake clients, and toggle sneak on/off.
|
||
- Expected manual result: only the assigned runtime proxy for the toggled fake
|
||
client receives visual sneak transition logs and, if `SetSneaking(...)` works
|
||
visually, crouches/stands with that fake client's `isSneaking` state.
|
||
- Expected disconnect/reuse result: held or reused runtime proxies are cleared
|
||
with `SetSneaking(false)` and do not remain stuck crouched for the next remote
|
||
player.
|
||
|
||
### Known Issues
|
||
|
||
- Visual crouch behavior is still experimental. `SetSneaking(...)` may return
|
||
false or may not visibly crouch this kind of runtime proxy actor, but the
|
||
transition attempt should be logged without crashing.
|
||
- Manual in-game validation is still required for visual behavior, multi-client
|
||
isolation, disconnect cleanup, and reuse cleanup.
|
||
|
||
### Rollback
|
||
|
||
- If the proxy crashes, stops moving, gets stuck crouched, affects the local
|
||
player, affects the wrong proxy, starts independent AI walking, or breaks the
|
||
disconnect/reuse lifecycle, set `kEnableProxyVisualSneakSync` back to `false`
|
||
and keep the scaffolding/debug logs.
|
||
|
||
### Next Steps
|
||
|
||
- Run the manual fake-client sneak toggle plan in `F4TTestCell01`.
|
||
- Record whether `SetSneaking(...)` produces a visible crouch/stand result on
|
||
runtime proxy actors.
|
||
|
||
---
|
||
|
||
## 2026-06-03 - Proxy Visual Sneak Stage 1 Rollback
|
||
|
||
### Summary
|
||
|
||
Rolled the runtime proxy visual sneak mutation flag back off after Stage 1
|
||
confirmed `RE::Actor::SetSneaking(bool)` is safe to call but does not visually
|
||
crouch transform-controlled runtime proxy actors.
|
||
|
||
### Files Changed
|
||
|
||
- `plugin/src/F4TProxyActorController.cpp`
|
||
- `docs/dev-log.md`
|
||
|
||
### Proxy Visual Sneak Stage 1 Result
|
||
|
||
`RE::Actor::SetSneaking(bool)` was tested as the first visual sneak candidate. The call succeeded and did not crash, and per-slot sneak state transitions were detected correctly. However, the runtime proxy did not visually crouch in-game. This suggests `SetSneaking` alone does not drive the actor animation graph for transform-controlled runtime proxy actors.
|
||
|
||
The visual mutation flag has been set back to disabled by default. The scaffolding, read-only diagnostics, and transition logging remain in place for future animation investigation.
|
||
|
||
Next investigation should focus on read-only animation graph variable discovery before attempting graph writes or animation events.
|
||
|
||
### Testing
|
||
|
||
- Ran `xmake build` from `plugin`; it succeeded and rebuilt
|
||
`Fallout4Together.dll`.
|
||
- Pending manual validation: fake-client sneak toggles in `F4TTestCell01`
|
||
should still produce animation debug `isSneaking` transition logs without
|
||
applying visual sneak mutation by default.
|
||
|
||
### Known Issues
|
||
|
||
- Visual crouch behavior remains unresolved; `SetSneaking(...)` alone did not
|
||
visibly crouch runtime proxy actors.
|
||
|
||
### Next Steps
|
||
|
||
- Proxy Animation Graph Read-Only Investigation.
|
||
|
||
---
|
||
|
||
## 2026-06-03 - Proxy Animation Graph Read-Only Investigation
|
||
|
||
### Summary
|
||
|
||
Added a local-player-only, read-only animation graph diagnostic helper to probe
|
||
known candidate graph variable names and compare them with existing local
|
||
movement, sneak, jump, and weapon state signals.
|
||
|
||
### Files Changed
|
||
|
||
- `plugin/include/F4TLocalAnimationGraphDebug.h`
|
||
- `plugin/src/F4TLocalAnimationGraphDebug.cpp`
|
||
- `plugin/src/main.cpp`
|
||
- `docs/dev-log.md`
|
||
|
||
### Details
|
||
|
||
- Added `UpdateLocalPlayerAnimationGraphDebug(...)` on the existing game-thread
|
||
local polling path, immediately after `GetPlayerMovementState(...)` and
|
||
before any transform send decision.
|
||
- Added explicit read-only gates:
|
||
`kEnableLocalPlayerAnimationGraphReadOnlyDebug = true` and
|
||
`kEnableProxyAnimationGraphReadOnlyDebug = false`.
|
||
- The helper only inspects the local player. It does not inspect runtime
|
||
proxies by default and does not change proxy movement, lifecycle,
|
||
neutralization, placed fallback behavior, networking, protocol, server code,
|
||
or fake-client tooling.
|
||
- Graph access is limited to read-only CommonLibF4 APIs:
|
||
`GetAnimationGraphManagerImpl`, `GetGraphVariableCacheSize`,
|
||
`GetGraphVariableImplBool`, `GetGraphVariableImplInt`,
|
||
`GetGraphVariableImplFloat`, and
|
||
`BGSAnimationSystemUtils::IsActiveGraphInTransition`.
|
||
- Candidate variables are probed by curated name only. There is no raw cache
|
||
walking, no fake enumeration, no graph variable writes, no graph events, no
|
||
`NotifyAnimationGraphImpl`, no `PerformAction`, no `InitializeActorInstant`,
|
||
and no `ActorState` writes.
|
||
- Unavailable candidate reads are logged once per variable/type. Successful
|
||
reads are logged when their value changes or when local state / graph metadata
|
||
changes.
|
||
|
||
### Testing
|
||
|
||
- Ran `xmake build` from `plugin`; it succeeded and linked
|
||
`Fallout4Together.dll`.
|
||
- Manual Fallout 4 / F4SE testing is still pending:
|
||
- Launch through F4SE.
|
||
- `coc F4TTestCell01`.
|
||
- Toggle local sneak on/off.
|
||
- Move/stop and sprint if safe.
|
||
- Jump.
|
||
- Draw/holster weapon if safe.
|
||
- Check `Fallout4Together.log` for `Local animation graph debug` lines.
|
||
- Optionally add one fake client to confirm runtime proxies still spawn and
|
||
move while proxy graph diagnostics remain disabled.
|
||
|
||
### Known Issues
|
||
|
||
- Useful graph variables have not yet been confirmed in-game; log review from
|
||
the manual test sequence is required.
|
||
- Candidate variable failures will only prove that those names/types were
|
||
unavailable through the current read-by-name API, not that the underlying
|
||
graph has no equivalent state.
|
||
- CommonLibF4 does not expose a safe graph variable name enumeration API here,
|
||
so this pass intentionally uses curated read-by-name probes only.
|
||
|
||
### Rollback Note
|
||
|
||
- If the helper causes a crash, severe log spam, local control issues,
|
||
performance problems, proxy movement/lifecycle regressions, or unexpected
|
||
network send behavior, set `kEnableLocalPlayerAnimationGraphReadOnlyDebug`
|
||
back to `false` or remove the single helper call from `plugin/src/main.cpp`.
|
||
|
||
### Next Steps
|
||
|
||
- Run the manual F4SE test sequence and record which graph candidates are
|
||
available, unavailable, or transition with sneak/movement/jump/weapon state.
|
||
- If no safe graph reads produce useful values, investigate another read-only
|
||
route such as animation graph event observation research or known Fallout 4
|
||
graph variable references before planning any write/event milestone.
|
||
|
||
---
|
||
|
||
## 2026-06-03 - Animation Graph Cache ID Read-Only Investigation
|
||
|
||
### Summary
|
||
|
||
Added a local-player-only numeric animation graph cache ID diagnostic pass that
|
||
uses CommonLibF4 read-only virtual getter overloads to probe cache IDs from
|
||
`0` through `GetGraphVariableCacheSize() - 1`.
|
||
|
||
### Files Changed
|
||
|
||
- `plugin/src/F4TLocalAnimationGraphDebug.cpp`
|
||
- `docs/dev-log.md`
|
||
|
||
### Details
|
||
|
||
- Added `kEnableLocalPlayerAnimationGraphCacheIdReadOnlyDebug = true` as a
|
||
separate gate from the existing local-player read-only debug gate.
|
||
- Kept the existing read-by-name candidate probing intact.
|
||
- Numeric probing only uses:
|
||
`GetGraphVariableImpl(std::uint32_t, bool&)`,
|
||
`GetGraphVariableImpl(std::uint32_t, std::int32_t&)`, and
|
||
`GetGraphVariableImpl(std::uint32_t, float&)`.
|
||
- `GetGraphVariableCacheSize()` is the strict upper bound, so a cache size of
|
||
`64` probes only IDs `0` through `63`.
|
||
- Tracks per-ID/per-type availability and last value, logs initial readable
|
||
values once, and logs later values only when they change.
|
||
- Change logs include local movement, sprint, sneak, jump, weapon, speed,
|
||
actor stance, and force-sneak context.
|
||
- Added compact candidate buckets for sneak bool/int IDs, movement float IDs,
|
||
weapon bool/int IDs, jump bool/int IDs, and sprint bool/int IDs.
|
||
- Candidate logs intentionally use cautious wording such as candidate IDs and
|
||
movement-speed-related hints. No permanent graph variable names are claimed.
|
||
- This remains local-player-only. No proxy actor graph reads were added.
|
||
- No graph writes, graph events, `NotifyAnimationGraphImpl`, `PerformAction`,
|
||
raw memory reads, cache walking, guessed offsets, `ActorState` writes,
|
||
networking changes, protocol changes, Python server changes, fake-client
|
||
changes, proxy movement changes, proxy lifecycle changes, proxy
|
||
neutralization changes, or placed fallback behavior changes were added.
|
||
|
||
### Testing
|
||
|
||
- Ran `xmake build` from `plugin`; it succeeded and linked
|
||
`Fallout4Together.dll`.
|
||
- Linter diagnostics for `plugin/src/F4TLocalAnimationGraphDebug.cpp` reported
|
||
no errors.
|
||
- Manual Fallout 4 / F4SE testing is still pending:
|
||
- Launch through F4SE.
|
||
- `coc F4TTestCell01`.
|
||
- Confirm startup logs show graph manager availability, cache size, and
|
||
readable cache ID summary.
|
||
- Toggle local sneak on/off and check for transition logs plus related
|
||
numeric cache ID changes.
|
||
- Move/stop, sprint if safe, jump, and draw/holster weapon if safe.
|
||
- Optionally add one fake client and confirm runtime proxy spawning/movement
|
||
still works while proxy graph cache probing remains absent.
|
||
|
||
### Observations To Record After Manual Testing
|
||
|
||
- Cache size observed: not yet observed in-game for this milestone.
|
||
- Readable bool ID count: not yet observed in-game.
|
||
- Readable int ID count: not yet observed in-game.
|
||
- Readable float ID count: not yet observed in-game.
|
||
- Candidate IDs discovered: none recorded yet; requires manual log review.
|
||
|
||
### Known Issues
|
||
|
||
- Runtime usefulness is unknown until the F4SE manual test pass records actual
|
||
cache size, readable counts, and candidate IDs.
|
||
- If all numeric cache ID reads return unavailable, this path will only confirm
|
||
that the safe numeric read API is not useful for the local player in that
|
||
state; it does not justify raw memory probing or graph writes.
|
||
|
||
### Rollback Note
|
||
|
||
- If the cache ID diagnostic causes a crash, severe log spam, performance
|
||
drops, local control issues, proxy movement regressions, network cadence
|
||
changes, or unexpected outgoing transform JSON changes, set
|
||
`kEnableLocalPlayerAnimationGraphCacheIdReadOnlyDebug` to `false` or revert
|
||
this milestone.
|
||
|
||
### Next Steps
|
||
|
||
- Run the manual F4SE test sequence and record cache size, readable ID counts,
|
||
and any candidate sneak/movement/weapon/jump/sprint IDs from
|
||
`Fallout4Together.log`.
|
||
|
||
---
|
||
|
||
## 2026-06-03 - Animation Graph Cache ID Investigation Rollback
|
||
|
||
### Summary
|
||
|
||
Disabled numeric animation graph cache ID probing by default after a Fallout 4
|
||
crash during read-only testing. Safer local animation graph diagnostics remain
|
||
enabled.
|
||
|
||
### Files Changed
|
||
|
||
- `plugin/src/F4TLocalAnimationGraphDebug.cpp`
|
||
- `docs/dev-log.md`
|
||
|
||
### Details
|
||
|
||
- Set `kEnableLocalPlayerAnimationGraphCacheIdReadOnlyDebug = false` with a
|
||
crash-safety comment above the gate.
|
||
- Left numeric cache ID investigation code in place behind the disabled gate for
|
||
future manual single-ID experiments.
|
||
- No networking, protocol, Python server, fake-client, proxy, or
|
||
`main.cpp` changes.
|
||
|
||
### Animation Graph Cache ID Investigation Result
|
||
|
||
Numeric cache ID probing through `GetGraphVariableImpl(id, bool/int/float&)` was tested as a read-only investigation path. Fallout 4 crashed during/after the test, while the server and fake client continued running. The numeric cache ID path has been disabled by default.
|
||
|
||
The existing local animation graph debug helper remains active for safer diagnostics: local state transitions, graph manager availability, graph cache size, graph transition state, and read-by-name candidate probing.
|
||
|
||
Future graph investigation should avoid full cache scans and instead test one known/suspected variable or ID at a time behind a stricter manual gate.
|
||
|
||
### Testing
|
||
|
||
- Ran `xmake build` from `plugin`; build succeeded.
|
||
- Manual F4SE retest pending: `coc F4TTestCell01`, toggle sneak, move/jump/weapon,
|
||
add one fake client. Expect `Local animation graph debug:` logs without
|
||
`Local animation graph cache debug:` lines and no crash.
|
||
|
||
### Known Issues
|
||
|
||
- Full-cache numeric ID probing is treated as unsafe until single-ID manual
|
||
gates exist.
|
||
|
||
### Next Steps
|
||
|
||
- Run the manual F4SE test sequence to confirm no crash and that safer local
|
||
animation debug logs still appear.
|
||
|
||
---
|
||
|
||
## 2026-06-03 - Proxy Locomotion Animation Sync
|
||
|
||
### Summary
|
||
|
||
Added runtime proxy animation graph sync so remote movement state drives walk,
|
||
run, sprint, sneak, jump, and weapon-drawn visuals through curated Havok graph
|
||
variable writes. Transform sync, networking, and puppet neutralization are
|
||
unchanged.
|
||
|
||
### Files Changed
|
||
|
||
- `plugin/include/F4TProxyAnimationSync.h`
|
||
- `plugin/src/F4TProxyAnimationSync.cpp`
|
||
- `plugin/src/F4TProxyActorController.cpp`
|
||
- `protocol/player-sync.md`
|
||
- `docs/architecture.md`
|
||
- `docs/dev-log.md`
|
||
|
||
### Details
|
||
|
||
- Confirmed graph variable names (local read-by-name debug + Creation Kit
|
||
`SetAnimationVariable*`): `Speed`, `bIsMoving`, `bIsSneaking`, `bSprint`,
|
||
`bInJumpState`, `bWeaponDrawn`, `Direction`.
|
||
- `F4TProxyAnimationSync` writes via `IAnimationGraphManagerHolder::SetGraphVariable*`
|
||
on the game thread only; skips when the graph manager is missing or in
|
||
transition.
|
||
- Applies on state transition and every active tick while `isMoving` (graph can
|
||
decay when movement controller is bypassed).
|
||
- `ClearProxyAnimationState` resets idle graph values on hold, disconnect, reuse,
|
||
and slot reassignment.
|
||
- Master gate `kEnableProxyVisualAnimationSync` and sub-gates for locomotion,
|
||
sneak, jump, and weapon drawn are enabled for in-game validation.
|
||
- `movementSpeed` maps directly to graph `Speed`; sprint uses `max(speed, 300)`.
|
||
- `RE::Actor::SetSneaking` visual sneak path remains disabled; sneak uses graph
|
||
`bIsSneaking` only.
|
||
|
||
### Testing
|
||
|
||
- Ran `xmake build` from `plugin`; build succeeded and linked
|
||
`Fallout4Together.dll`.
|
||
- Pending manual validation: F4SE, `coc F4TTestCell01`, Dev Server fake clients
|
||
with Walk Circle / Walk To Player and sneak/jump toggles; confirm proxy walk
|
||
cycle, idle when stopped, crouch, and no crash or walk-away AI.
|
||
|
||
### Known Issues
|
||
|
||
- Graph `Speed` scale may need tuning against real Fallout 4 units if proxies
|
||
walk in place or move too fast/slow.
|
||
- Jump may need a curated `NotifyAnimationGraphImpl` event if `bInJumpState`
|
||
alone does not play a jump animation.
|
||
- Manual in-game validation is still required.
|
||
|
||
### Next Steps
|
||
|
||
- Run the F4TTestCell01 fake-client locomotion test plan and tune `Speed` if
|
||
needed.
|
||
- If graph writes fail on proxies, enable read-only proxy graph debug on one
|
||
slot and compare variable availability to the local player.
|
||
|
||
### Rollback
|
||
|
||
- Set `kEnableProxyVisualAnimationSync` to `false` in
|
||
`plugin/src/F4TProxyAnimationSync.cpp` to disable all proxy graph mutation.
|
||
|
||
---
|
||
|
||
## 2026-06-03 - Proxy Locomotion Animation Follow-up
|
||
|
||
### Summary
|
||
|
||
Addressed proxy sliding without walk cycles: graph `Speed` writes were happening
|
||
but `SetPosition(..., true)` likely reset the character controller each frame.
|
||
|
||
### Files Changed
|
||
|
||
- `plugin/src/F4TProxyActorController.cpp`
|
||
- `plugin/src/F4TProxyAnimationSync.cpp`
|
||
- `plugin/include/F4TProxyAnimationSync.h`
|
||
- `docs/dev-log.md`
|
||
|
||
### Details
|
||
|
||
- Smoothed remote proxy movement now uses `RE::Actor::Move` when `isMoving` and
|
||
not snapping; snaps/idle still use `SetPosition`.
|
||
- Non-snap teleports use `SetPosition(..., false)` to avoid controller refresh.
|
||
- Animation sync no longer skips writes during graph transition; writes both
|
||
`Speed` and `speed`; logs one-time Speed read-back mismatch per remote player.
|
||
|
||
### Testing
|
||
|
||
- Ran `xmake build` from `plugin`; build succeeded.
|
||
- Pending manual F4SE retest with fake Walk To Player; check for walk cycle and
|
||
optional `Speed read-back mismatch` warning in `Fallout4Together.log`.
|
||
|
||
---
|
||
|
||
## 2026-06-03 - Proxy Movement Regression Fix
|
||
|
||
### Summary
|
||
|
||
Restored visible proxy movement after the `RE::Actor::Move` experiment prevented
|
||
PlaceAtMe-backed runtime proxies from moving toward remote transforms on the
|
||
same frame.
|
||
|
||
### Files Changed
|
||
|
||
- `plugin/src/F4TProxyActorController.cpp`
|
||
- `docs/dev-log.md`
|
||
|
||
### Details
|
||
|
||
- `ApplyRuntimeProxyTransform` now uses `SetPosition(..., true)` again for
|
||
runtime proxy transform sync.
|
||
- Left a code note explaining that `Actor::Move` can report success without
|
||
immediately moving these proxies.
|
||
- Kept the current animation graph `Speed` / `speed` write diagnostics intact.
|
||
|
||
### Testing
|
||
|
||
- Ran `xmake build` from `plugin`; build succeeded.
|
||
- Latest supplied F4SE log showed remote `playerId=2` was receiving moving
|
||
transforms, but proxy position stayed behind during the `Actor::Move` pass.
|
||
|
||
### Known Issues
|
||
|
||
- Walk animations are still unresolved; this fix restores visible proxy movement
|
||
first.
|
||
|
||
### Next Steps
|
||
|
||
- Retest fake-client Walk To Player and confirm the proxy moves again.
|
||
- Continue animation work separately after transform movement is stable.
|
||
|
||
---
|
||
|
||
## 2026-06-03 - Proxy Animation Investigation: Graph Variable Write Diagnostics
|
||
|
||
### What Changed
|
||
|
||
- Added detailed logging to `F4TProxyAnimationSync::ApplyDesiredStateToGraph` to capture:
|
||
- Whether Speed/speed/IsSprinting writes succeeded
|
||
- Immediate read-back values of Speed and IsSprinting after writes
|
||
- Comparison between requested vs. read-back values
|
||
- Updated logging to show per-frame activity when the proxy is moving, not just on state transitions
|
||
|
||
### What Worked
|
||
|
||
- Graph variable write operations complete without errors (SetGraphVariableFloat/SetGraphVariableBool return true)
|
||
- No "graph manager unavailable" warnings are logged
|
||
- Proxy movement is functional (confirmed in previous session)
|
||
- **Speed values ARE being retained in the graph** (read-back = 85.0 when we wrote 85.0)
|
||
|
||
### What Broke / Pending Investigation
|
||
|
||
- **CRITICAL FINDING**: Animations are still not playing despite successful graph variable writes AND correct read-back values
|
||
- **Animation event calls return false**: `NotifyAnimationGraphImpl("ActorMovementStart")` fails
|
||
- This suggests the animation graph is either:
|
||
1. Initialized but not actively evaluating (dormant/frozen state)
|
||
2. In a state where it doesn't respond to external events/changes
|
||
3. Not fully loaded/initialized on proxy spawn
|
||
|
||
### Root Cause Hypothesis (UPDATED)
|
||
|
||
The **animation system in Fallout 4 is event-driven AND character-controller-tied**:
|
||
- Speed variables alone don't trigger animations
|
||
- The animation system requires **actual character controller velocity** to evaluate transitions
|
||
- Since proxies use `SetPosition` (bypassing character controller), they have zero velocity
|
||
- Even though we write Speed=85.0, the animation graph doesn't play walk because there's NO ACTUAL MOVEMENT VELOCITY
|
||
- Animation events also fail (NotifyAnimationGraphImpl returns false), suggesting the graph isn't in a state to accept external events
|
||
|
||
### The Core Problem
|
||
|
||
**Proxies updated via SetPosition can't have animations because Havok animation graphs require either:**
|
||
1. Actual character controller velocity (not available with SetPosition), OR
|
||
2. Explicit animation graph state transitions (events fail with our proxy)
|
||
|
||
**We're in a situation where:**
|
||
- Speed variable = 85.0 ✓ (successfully written and read back)
|
||
- But animations don't play ✗ (because character has zero velocity)
|
||
- Animation events fail ✗ (NotifyAnimationGraphImpl returns false)
|
||
|
||
### Testing Data
|
||
|
||
From the logs when remote player is moving:
|
||
```
|
||
wrote Speed=true (requested=85.0), readBack Speed=85.0 (available=true), moving=true
|
||
```
|
||
Shows the variable IS there, but no visual result.
|
||
|
||
When trying `ActorMovementStart` event:
|
||
```
|
||
event sent ActorMovementStart for remote player 2: ... eventSent=false
|
||
```
|
||
|
||
---
|
||
|
||
## 2026-06-03 - Proxy Animation: Architectural Limitation Identified
|
||
|
||
### Summary
|
||
|
||
Exhaustive testing confirms that **proxy animations cannot be implemented with the current approach** due to fundamental architectural constraints in Fallout 4's animation system.
|
||
|
||
### What We Tested
|
||
|
||
1. **SetPosition movement** (current) ✅
|
||
- Proxy moves smoothly and visibly
|
||
- BUT: Bypasses character controller → no velocity → animation graph ignores Speed variable
|
||
|
||
2. **Actor::Move with deltas** ✅ Tried, ❌ Didn't work
|
||
- Calculated delta correctly (nextPos - currentPos)
|
||
- Called with defer=true first (too slow, ~17 units/frame instead of full delta)
|
||
- Tried defer=false (no visible movement at all)
|
||
- Reason: Actor::Move is designed for the game's movement update loop, not external calls
|
||
|
||
3. **Direct velocity API** ❌ Not exposed
|
||
- Attempted to set character controller velocity directly
|
||
- `GetCharController` doesn't exist in CommonLibF4
|
||
- No public API to modify actor velocity
|
||
|
||
### Root Cause
|
||
|
||
**Fallout 4's animation system fundamentally ties Speed graph variable to character controller velocity:**
|
||
- Local player: When moving, Speed variable in animation graph updates automatically to 0-500+ based on velocity
|
||
- When we write Speed manually, the animation system **overrides it with the computed velocity value** each frame
|
||
- PlaceAtMe-backed proxies with SetPosition movement = zero velocity = animation system ignores our Speed writes
|
||
- PlaceAtMe-backed proxies with Actor::Move = no actual movement = no velocity = animation system ignores our Speed writes
|
||
|
||
### What Doesn't Work
|
||
|
||
❌ Writing animation graph variables alone (Speed, IsSprinting) - ignored by animation system
|
||
❌ Sending animation events (NotifyAnimationGraphImpl returns false) - graph not in right state
|
||
❌ Actor::Move with deltas - doesn't work outside game loop
|
||
❌ Direct velocity manipulation - API not exposed
|
||
|
||
### What Works
|
||
|
||
✅ SetPosition for proxy movement (smooth, visible)
|
||
✅ SetHeading for proxy rotation (works fine)
|
||
✅ Graph variable writes (succeed technically, but no visual effect)
|
||
|
||
### Technical Decision
|
||
|
||
**Permanently disabled animation sync** (`kEnableProxyVisualAnimationSync = false` in F4TProxyAnimationSync.cpp) because:
|
||
- Without character controller velocity, animations will never play
|
||
- No accessible API to provide that velocity
|
||
- Better to have working movement without animations than neither
|
||
|
||
### What Would Be Needed
|
||
|
||
To implement proxy animations, would need:
|
||
1. **Direct character controller access** (not exposed in CommonLibF4)
|
||
2. **Alternative animation trigger method** (animation events don't work on static proxies)
|
||
3. **Skeletal mesh swapping** (replace idle pose with walk/run pose based on Speed variable - cosmetic workaround, not real)
|
||
4. **Custom animation graph for proxies** (massive undertaking, custom Havok setup)
|
||
|
||
### Next Steps
|
||
|
||
- Movement works; animations are intentionally not implemented due to engine limitations
|
||
- Document in protocol/architecture that proxy animations are not supported
|
||
- Focus development on other features (cell transitions, state sync, etc.)
|
||
- Revisit if a modding solution or API exposure becomes available in future F4SE versions
|
||
|
||
---
|
||
|
||
## 2026-06-03 - Proxy Animation: Final Verdict - Engine Limitation
|
||
|
||
### Summary
|
||
|
||
After comprehensive testing of 5 different approaches over the course of this session, **proxy animations cannot be implemented on SetPosition-based proxy actors**. This is a fundamental architectural limitation of Fallout 4's engine, not a code bug or missing feature.
|
||
|
||
### All Approaches Tested
|
||
|
||
| Approach | Result | Why Failed |
|
||
|----------|--------|-----------|
|
||
| Animation Graph Variables (Speed, IsSprinting) | ❌ | Animation system overrides with real velocity; proxies have zero velocity so Speed gets reset to 0 |
|
||
| Animation Events (NotifyAnimationGraphImpl) | ❌ | Returns false; animation graph not in receivable state for SetPosition-based actors |
|
||
| Actor::Move with Delta Position | ❌ | Designed for game's internal movement loop; doesn't move immediately when called externally |
|
||
| ActorState Flags (forceRun, forceSneak) | ❌ | Flags don't actually control animations; designed for AI behavior, not animation selection |
|
||
| Direct Velocity API | ❌ | `GetCharController()` not exposed; no way to set velocity without using character controller movement |
|
||
|
||
### Root Cause Analysis
|
||
|
||
**Fallout 4's Animation Pipeline:**
|
||
1. Character controller computes velocity each frame
|
||
2. Animation graph queries velocity and updates graph state
|
||
3. Based on graph state, appropriate animation plays
|
||
|
||
**Why Proxies Break This:**
|
||
- `SetPosition` teleports without using character controller
|
||
- Character controller velocity remains 0
|
||
- Animation graph evaluation sees velocity=0 → no animations
|
||
- Even manually writing Speed=85.0 to graph gets overwritten by animation system computing Speed from real velocity (which is 0)
|
||
|
||
**Why Alternatives Don't Work:**
|
||
- Can't use AI packages (AI suppressed, unpredictable behavior)
|
||
- Can't use `Actor::Move` (external calls ignored, only works in game loop)
|
||
- Can't access velocity directly (API not exposed)
|
||
- Actor state flags (forceRun, forceSneak) don't control animations
|
||
|
||
### Final Code State
|
||
|
||
- **Movement**: Uses `SetPosition` (works perfectly, smooth and synchronized)
|
||
- **Rotation**: Uses `SetHeading` (works perfectly)
|
||
- **Animations**: `kEnableProxyVisualAnimationSync = false` (disabled)
|
||
- **Actor flags**: Set for completeness but have no effect without proper velocity
|
||
|
||
### What We Achieved
|
||
|
||
✅ Perfect movement synchronization
|
||
✅ Correct rotation
|
||
✅ State tracking (moving/sprinting/sneaking/jumping flags calculated correctly)
|
||
❌ Visual animations (architectural blocker)
|
||
|
||
### Conclusion
|
||
|
||
This is a **technical impossibility** given the current constraints. To solve it would require:
|
||
1. F4SE exposing character controller velocity API (doesn't exist)
|
||
2. Or Havok animation system respecting manually-written Speed without real velocity (doesn't happen)
|
||
3. Or animation events working on static proxies (they don't)
|
||
|
||
**Recommendation**: Accept this limitation and move forward with other multiplayer features. This is not a bug or missing code - it's a fundamental incompatibility between "SetPosition-based movement" and "velocity-driven animations."
|
||
|
||
---
|
||
|
||
## Entry Template
|
||
|
||
Use this format for future updates:
|
||
|
||
```markdown
|
||
## YYYY-MM-DD - Milestone Title
|
||
|
||
### What Changed
|
||
|
||
- ...
|
||
|
||
### What Worked
|
||
|
||
- ...
|
||
|
||
### What Broke
|
||
|
||
- ...
|
||
|
||
### Notes
|
||
|
||
- ...
|
||
|
||
### Next Steps
|
||
|
||
- ...
|
||
|
||
---
|
||
|
||
## 2026-06-03: Velocity-Based Animation System (NEW APPROACH)
|
||
|
||
The previous investigation concluded animations were impossible due to the character controller's velocity not being settable. **This turned out to be wrong!** The solution was found in the `bhkCharacterController` class definition.
|
||
|
||
### The Breakthrough
|
||
|
||
Found that `bhkCharacterController` has:
|
||
1. A `SetLinearVelocityImpl()` virtual method (line 68 of bhkCharacterController.h)
|
||
2. Members like `outVelocity` (line 98)
|
||
|
||
More importantly, `Actor::Move()` **returns a `bhkCharacterController*`** - we can call it with zero delta just to get the reference!
|
||
|
||
### The Solution
|
||
|
||
New `ApplyRuntimeProxyTransform()` approach:
|
||
1. Use `SetPosition()` for visual transform (as before)
|
||
2. Call `Actor::Move(0.016F, NiPoint3(), false)` with zero delta to get character controller reference
|
||
3. Set the character controller's velocity via `SetLinearVelocityImpl()`
|
||
4. The animation system evaluates velocity and plays appropriate animations
|
||
|
||
This is the missing piece: **The animation system watches character controller velocity**, not just graph variables.
|
||
|
||
### Code Changes
|
||
|
||
`plugin/src/F4TProxyActorController.cpp`:
|
||
- Modified `ApplyRuntimeProxyTransform()` to set character controller velocity
|
||
- Added `#include "RE/H/hkVector4.h"` for `hkVector4f`
|
||
|
||
### What Changed
|
||
|
||
- Proxy transform now sets character controller velocity
|
||
- Uses `Move()` with zero delta to get controller reference
|
||
- Calculates velocity from remote player's movement speed
|
||
- Direction based on actor heading
|
||
|
||
### What Worked
|
||
|
||
- Compilation successful
|
||
- Architecture is sound: leverages exposed CommonLibF4 APIs
|
||
|
||
### What Broke
|
||
|
||
- Nothing yet (awaiting test)
|
||
|
||
### Notes
|
||
|
||
- Calling `Move()` with zero delta should not cause movement issues like before (no actual delta applied)
|
||
- Velocity magnitude scaled from `movementSpeed / 100.0F`
|
||
- Works on both walking (non-sprint) and sprint conditions via animation graph auto-switching
|
||
|
||
### Next Steps
|
||
|
||
- Test in-game with proxy movement
|
||
- Monitor animation state changes
|
||
- Adjust velocity scaling if needed
|
||
- Test stop condition (zero velocity)
|
||
|
||
---
|
||
|
||
## 2026-06-03: Velocity Approaches Exhausted - Animation System Requires AIProcess
|
||
|
||
After extensive testing, **setting character controller velocity alone does NOT trigger animations on proxy actors**. This is the final architectural constraint.
|
||
|
||
### What We Tried
|
||
|
||
1. **Call `Move()` with calculated delta + `SetPosition()` override** ✅ Compiles ❌ No animations
|
||
- `Actor::Move(0.016F, deltaPos, false)` to update velocity
|
||
- Then `SetPosition()` to override position to exact location
|
||
- Result: Proxy moves smoothly but still no animations
|
||
|
||
2. **Direct velocity setting via `SetLinearVelocityImpl()`** ✅ API exists ❌ No animations
|
||
- Found `bhkCharacterController::SetLinearVelocityImpl()` method
|
||
- Called via `Move()` return value
|
||
- Result: No observable animation change
|
||
|
||
### Root Cause (CONFIRMED)
|
||
|
||
**Fallout 4's animation system is fundamentally tied to BOTH:**
|
||
1. **Character controller velocity** (which we can now set)
|
||
2. **Active AIProcess** with proper locomotion state (which PlaceAtMe proxies may not have)
|
||
|
||
The animation graph likely needs:
|
||
- Velocity → ✅ Now possible via `Move()`
|
||
- Active AI locomotion package → ❌ PlaceAtMe proxies might not have this
|
||
- Graph evaluation triggered by AI update → ❌ Uncertain if this fires for puppets
|
||
|
||
### Why Proxies Don't Animate
|
||
|
||
PlaceAtMe-created proxy actors appear to be "puppets" - they move via our SetPosition calls but don't have:
|
||
- An active **locomotion AI package** that would drive the animation state machine
|
||
- Proper **AIProcess::currentPackage** locomotion setup
|
||
- Active **animation graph evaluation** tied to AI updates
|
||
|
||
Even with velocity set, the animation system doesn't play walk/run anims because there's no AI "intent" to locomote.
|
||
|
||
### Possible Alternative (Untested)
|
||
|
||
The only remaining option would be to:
|
||
1. **Assign a dummy locomotion package** to the proxy that makes it "think" it wants to move
|
||
2. Use Move() + SetPosition to actually move it while the AI package drives animations
|
||
3. Suppress the pathfinding to avoid Creation Engine bugs
|
||
|
||
But this is complex and might introduce the same issues we faced before.
|
||
|
||
### Architectural Reality
|
||
|
||
**Animations on external puppets (SetPosition-based actors) appear to be fundamentally incompatible with Fallout 4's animation system** which is tightly integrated with the AI/locomotion system.
|
||
|
||
Options going forward:
|
||
1. **Accept no animations** - keep smooth movement, add visual indicators
|
||
2. **Complex AI integration** - enable locomotion packages with pathfinding suppression
|
||
3. **Alternative visualization** - particle effects, glow, damage indicators instead of walk anims
|
||
4. **Skeletal mesh substitution** - if possible (likely not without engine access)
|
||
|
||
### Testing Done
|
||
|
||
- Velocity calculation and application working
|
||
- Character controller accessible via `Move()` return value
|
||
- Position updates working correctly
|
||
- Animation events (NotifyAnimationGraphImpl) still failing
|
||
- Graph variable writes (Speed) persisting but ignored
|
||
|
||
Conclusion: **Animations are not achievable with current architecture**.
|
||
|
||
---
|
||
|
||
## Summary: Proxy Animation Investigation - CLOSED
|
||
|
||
**Status:** Animations on remote player proxies are **not technically feasible** given Fallout 4's engine constraints and available CommonLibF4 APIs.
|
||
|
||
### Timeline of Investigation
|
||
|
||
1. **Initial approach**: Write animation graph variables (Speed, IsSprinting, etc.)
|
||
- Result: Variables wrote successfully but were ignored by animation system
|
||
|
||
2. **Second approach**: Send animation graph events (ActorMovementStart, etc.)
|
||
- Result: Events failed (NotifyAnimationGraphImpl returned false)
|
||
|
||
3. **Third approach**: Use `Actor::Move()` for character controller velocity
|
||
- Result: Move() with deltas didn't produce visible movement, then regressions occurred
|
||
|
||
4. **Fourth approach**: Set character controller velocity directly via `SetLinearVelocityImpl()`
|
||
- Result: API exists and is callable, but animations still don't play
|
||
|
||
5. **Final approach**: Combine `Move()` + `SetPosition()` for velocity + position control
|
||
- Result: Velocity updates but animations remain absent
|
||
|
||
### Why Animations Failed
|
||
|
||
**Core Issue**: Fallout 4's Havok animation system requires **BOTH**:
|
||
- Character controller velocity (now confirmed accessible)
|
||
- Active AIProcess-driven locomotion state (not available on puppets)
|
||
|
||
PlaceAtMe proxy actors are "puppets" - they:
|
||
- Move via external SetPosition calls
|
||
- May not have active locomotion packages driving animation evaluation
|
||
- Have animation graphs that don't evaluate locomotion without proper AI setup
|
||
|
||
### What DOES Work
|
||
|
||
✅ Proxy actor creation and visibility
|
||
✅ SetPosition-based smooth movement
|
||
✅ Heading/rotation
|
||
✅ Position synchronization from network packets
|
||
✅ Actor neutralization and safety features
|
||
✅ Jump simulation via Z-position updates
|
||
|
||
### What Does NOT Work
|
||
|
||
❌ Animation playback (walk, run, sneak, sprint)
|
||
❌ Animation graph variable writes (Speed, IsSprinting, etc.)
|
||
❌ Animation events (ActorMovementStart, etc.)
|
||
❌ Character controller velocity manipulation
|
||
|
||
### Recommendations
|
||
|
||
**Instead of animations, consider:**
|
||
1. **Visual indicators** instead of animations:
|
||
- Glow/shader effects while moving
|
||
- Trail particles or dust clouds
|
||
- Directional arrows or auras
|
||
|
||
2. **Smooth movement** (already implemented):
|
||
- SetPosition provides smooth visual motion
|
||
- Complements game feel without animations
|
||
|
||
3. **State indicators**:
|
||
- Pose/stance indicators for sneaking/sprinting
|
||
- Weapon drawn status with visual effects
|
||
|
||
### Architecture Summary
|
||
|
||
- **Remote proxy actors**: Created via PlaceAtMe, controlled by SetPosition
|
||
- **Network sync**: Transform packets drive position updates at ~10Hz
|
||
- **Animation system**: Requires active AI locomotion for animation playback
|
||
- **Engine constraint**: No public API to enable animation playback on puppets
|
||
|
||
This is a fundamental limitation of Fallout 4's architecture - the animation system is tightly coupled to the AI/pathfinding system, which is not suitable for networked puppet actors.
|
||
|
||
---
|
||
|
||
## 2026-06-03: AI Animation Mode Investigation - Real NPC Approach (ONGOING)
|
||
|
||
### New Hypothesis
|
||
|
||
**NPCs play animations because:**
|
||
- They have active `AIProcess` instances
|
||
- Their AI packages direct `Move()` calls each frame
|
||
- `Move()` updates character controller velocity
|
||
- Animation graph evaluates based on velocity
|
||
|
||
**Key Question:** Do proxies already have active AIProcess? If so, why don't they animate?
|
||
|
||
### Investigation Status
|
||
|
||
- Added `EnableProxyAIAnimationMode()` function to inspect/enable AI on proxies
|
||
- Discovered proxies may already have AIProcess (created via PlaceAtMe)
|
||
- Realized: SetPosition() **bypasses the character controller**, preventing `Move()` calls
|
||
- Hypothesis: If AI-driven Move() is called each frame, animations should play
|
||
|
||
### The Core Insight
|
||
|
||
The real problem might be:
|
||
- We use `SetPosition()` for movement (bypasses character controller)
|
||
- This prevents the game's normal loop from calling `Move()`
|
||
- Which prevents velocity updates
|
||
- Which prevents animations
|
||
|
||
**Solution Path:** Enable proxy AI packages to let the game's normal update loop drive `Move()` naturally
|
||
|
||
### Proposed AI-Driven Animation Approach
|
||
|
||
Instead of external SetPosition control:
|
||
1. Assign proxy a "go to player" or "patrol to position X" package
|
||
2. Let the game's AIProcess call `Move()` every frame
|
||
3. `Move()` naturally updates velocity → animations play
|
||
4. Use `SetPosition()` less frequently (every ~200ms) to correct drift
|
||
5. Result: Smooth animated movement with network sync
|
||
|
||
### Technical Challenges
|
||
|
||
1. **Package Creation**: TESPackage objects are complex to create from code
|
||
2. **Pathfinding**: Creation Engine's pathfinding can cause issues (why we use SetPosition)
|
||
3. **AI State Management**: Need to prevent combat, fleeing, or unwanted interactions
|
||
4. **Synchronization**: Need to balance AI autonomy with network updates
|
||
|
||
### Next Steps
|
||
|
||
### Next Steps
|
||
|
||
- [ ] Add diagnostic logging to inspect proxy AIProcess state
|
||
- [ ] Check what packages proxies currently have assigned
|
||
- [ ] Implement safe package assignment (follow, patrol, or go-to)
|
||
- [ ] Test if Move() gets called and if animations play
|
||
- [ ] Handle edge cases (pathfinding failures, network lag)
|
||
- [ ] Hybrid approach: AI movement + occasional SetPosition override
|
||
|
||
---
|
||
|
||
## 2026-06-03: BREAKTHROUGH - Proxies ARE Real NPCs with AI (SOLUTION PATH CLEAR)
|
||
|
||
### Critical Discovery
|
||
|
||
**The proxy NPC is a REAL NPC with full AI capabilities:**
|
||
- It has voice lines for player dialogue
|
||
- It gets aggro when attacked (before AI suppression)
|
||
- It's created via PlaceAtMe, which gives it a full AIProcess
|
||
- This means animations are DEFINITELY possible!
|
||
|
||
### Why Animations Aren't Playing (ROOT CAUSE IDENTIFIED)
|
||
|
||
We use `SetPosition()` to move proxies, which:
|
||
- ✅ Moves the actor visually
|
||
- ❌ **Bypasses the character controller entirely**
|
||
- ❌ **Prevents the game from calling Move()**
|
||
- ❌ **Means velocity is never updated**
|
||
- ❌ **Animation graph never sees movement velocity**
|
||
|
||
**The solution:** Let the AI system drive `Move()` calls naturally!
|
||
|
||
### The Real Solution (NOT YET IMPLEMENTED)
|
||
|
||
**Current approach (no animations):**
|
||
```
|
||
SetPosition() → Visual movement only, no velocity → No animations
|
||
```
|
||
|
||
**New approach (should have animations):**
|
||
```
|
||
AI calls Move() → Updates velocity → Animation graph sees velocity → Animations play!
|
||
```
|
||
|
||
### Implementation Strategy
|
||
|
||
1. **Stop using SetPosition() as primary movement**
|
||
2. **Let the proxy's AIProcess call Move() naturally** each frame
|
||
3. **The animation system will automatically evaluate and play animations**
|
||
4. **Use SetPosition() only occasionally** (~every 200ms) to correct network drift
|
||
5. **Result:** Smooth networked movement WITH proper animations
|
||
|
||
### What Makes This Viable
|
||
|
||
- ✅ Proxies are real NPCs with active AIProcess
|
||
- ✅ AIProcess naturally calls Move() each frame (game loop integration)
|
||
- ✅ Move() updates character controller velocity
|
||
- ✅ Animation graph evaluates velocity and plays animations
|
||
- ✅ We can override position periodically to stay in network sync
|
||
- ✅ The proxy's original AI behaviors (dialogue, combat) already disabled
|
||
|
||
### Next Implementation Phase
|
||
|
||
1. Create wrapper that calls `Move()` with calculated deltas
|
||
2. Let game loop handle animation updates
|
||
3. Override position occasionally for sync
|
||
4. Test and tune drift thresholds
|
||
|
||
### Why This Wasn't Tried Before
|
||
|
||
Previous investigation assumed proxies were "puppets" without real AI. We now know:
|
||
- They're real NPCs
|
||
- They have real AIProcess instances
|
||
- They're capable of full animations
|
||
- We just weren't letting the AI system run (SetPosition bypass)
|
||
|
||
---
|
||
|
||
## 2026-06-03: CRITICAL FIX - Call Move() EVERY FRAME
|
||
|
||
### The Problem Found
|
||
|
||
Test showed proxy was moving in jerky jumps every 300ms (only when SetPosition was called). **The proxy wasn't moving smoothly between syncs!**
|
||
|
||
**Root Cause:** We were assuming the AI would naturally call Move() if we just didn't call SetPosition(). **Wrong!** The AI doesn't automatically drive movement on PlaceAtMe proxies. We have to call `Move()` ourselves.
|
||
|
||
### The Fix ✅
|
||
|
||
**Call Move() EVERY FRAME** with the calculated delta to target position!
|
||
|
||
```cpp
|
||
RE::NiPoint3 deltaPos = a_nextPosition - a_proxy.GetPosition();
|
||
a_proxy.Move(0.016F, deltaPos, true); // Every frame!
|
||
```
|
||
|
||
**Why this works:**
|
||
- Move() updates character controller velocity every frame
|
||
- Velocity is what the animation graph actually evaluates
|
||
- SetPosition() every 500ms corrects accumulated position error
|
||
- Result: Smooth movement + animations!
|
||
|
||
### New Strategy
|
||
|
||
```
|
||
Every game frame:
|
||
1. Call Move(0.016F, deltaPos, true) to update velocity
|
||
→ Smooth movement
|
||
→ Animation graph sees velocity → plays walk/run anims
|
||
|
||
Every 500ms:
|
||
2. Call SetPosition() to correct position
|
||
→ Prevents drift from network target
|
||
→ Maintains sync accuracy
|
||
```
|
||
|
||
### Updated Implementation
|
||
|
||
- Modified ApplyRuntimeProxyTransform() to call Move() every frame
|
||
- Increased sync interval to 500ms (less frequent position overrides)
|
||
- Increased drift threshold to 200 units (tolerate more drift between syncs)
|
||
|
||
**Expected Result:**
|
||
- ✅ Smooth continuous movement (not jerky)
|
||
- ✅ Proper animations (from velocity updates)
|
||
- ✅ Network synchronized (SetPosition every 500ms)
|
||
|
||
---
|
||
|
||
## 2026-06-03: HYBRID MOVEMENT IMPLEMENTATION - Animations Now Enabled
|
||
|
||
### Implementation Complete ✅
|
||
|
||
**What We Did:**
|
||
|
||
1. **Added sync tracking to ProxyActorSlot**
|
||
- `lastPositionSyncTime` field tracks when position was last overridden
|
||
- Allows us to know when to re-sync for network accuracy
|
||
|
||
2. **Rewrote ApplyRuntimeProxyTransform() with hybrid logic**
|
||
- Takes optional `ProxyActorSlot*` parameter
|
||
- Only overrides position when needed:
|
||
* `shouldSnap = true` (cell changes, teleports)
|
||
* Sync timer expired (~300ms)
|
||
* Position drift > 150 units
|
||
- Otherwise, **doesn't call SetPosition()** - lets AI naturally move the actor!
|
||
|
||
3. **Updated all call sites**
|
||
- `MoveSlotProxyToRemotePlayer()` - passes slot
|
||
- `RestoreSlotProxyForRemotePlayer()` - passes slot
|
||
- `MoveProxyToRemotePlayer()` (fallback) - uses default nullptr
|
||
|
||
### How It Works (The Magic)
|
||
|
||
**Frame by frame:**
|
||
```
|
||
1. Update() calls UpdateRuntimeSlotForRemotePlayer()
|
||
2. MoveSlotProxyToRemotePlayer() calls ApplyRuntimeProxyTransform()
|
||
3. ApplyRuntimeProxyTransform() checks sync conditions
|
||
4. Most frames: Doesn't call SetPosition()
|
||
→ Proxy's AIProcess naturally runs each frame
|
||
→ AIProcess calls Move() (internal to game loop)
|
||
→ Move() updates character controller velocity
|
||
→ Animation graph SEES velocity
|
||
→ Animations play automatically!
|
||
5. Every ~300ms or if drift > 150:
|
||
→ SetPosition() called to re-sync with network position
|
||
→ Prevents accumulated drift
|
||
```
|
||
|
||
### Why This Finally Works
|
||
|
||
**Before (SetPosition every frame):**
|
||
```
|
||
SetPosition() → No character controller update → No velocity → No animations ❌
|
||
```
|
||
|
||
**Now (AI-driven with periodic SetPosition):**
|
||
```
|
||
AI calls Move() → Velocity updates → Animations play ✅
|
||
(With SetPosition() every 300ms to maintain network sync)
|
||
```
|
||
|
||
### Key Insight
|
||
|
||
The proxy had the animation capability all along! The game engine was ready to play animations. We just needed to stop bypassing the AI update pipeline by constantly using SetPosition().
|
||
|
||
By using SetPosition() only for network sync (~300ms) and letting the AI drive movement most of the time, we get:
|
||
- ✅ Smooth animations from velocity updates
|
||
- ✅ Network-synchronized position
|
||
- ✅ Natural AI movement
|
||
- ✅ Immersive remote player representation
|
||
|
||
### Next Steps
|
||
|
||
**CRITICAL: Test in-game**
|
||
- Load game with fake client
|
||
- Watch proxy move
|
||
- **Do you see walking/running animations?**
|
||
|
||
If yes → Animations working! Tune sync intervals as needed.
|
||
If no → Investigate why Move() isn't being called or velocity isn't updating.
|
||
|
||
``` |