Repo Cleanup
This commit is contained in:
+5
-5
@@ -1,8 +1,8 @@
|
|||||||
# Contributing
|
# Contributing
|
||||||
|
|
||||||
Commonwealth Online is currently in the research and prototype stage.
|
Commonwealth Online is an active Fallout 4 multiplayer mod in iterative development.
|
||||||
|
|
||||||
At this stage, contributions should focus on documentation, research, setup notes, tooling experiments, and small isolated prototypes.
|
Contributions should stay focused, documented, and safe to test in a clean Fallout 4 profile.
|
||||||
|
|
||||||
## Current Contribution Areas
|
## Current Contribution Areas
|
||||||
|
|
||||||
@@ -19,11 +19,11 @@ Useful contributions include:
|
|||||||
|
|
||||||
## Scope
|
## Scope
|
||||||
|
|
||||||
Please keep contributions focused on the first prototype goal:
|
Please keep contributions aligned with the current project scope:
|
||||||
|
|
||||||
> Two Fallout 4 clients in a controlled test environment, with each player visible as a synced remote actor.
|
> Dedicated-server Fallout 4 multiplayer with transform sync, proxy actors, and expanding presence/infrastructure features.
|
||||||
|
|
||||||
Avoid adding large systems before the core prototype works.
|
Avoid large unrelated rewrites or gameplay systems that are not part of the current milestone.
|
||||||
|
|
||||||
Out of scope for early development:
|
Out of scope for early development:
|
||||||
|
|
||||||
|
|||||||
@@ -136,12 +136,11 @@ plugin/ F4SE/CommonLibF4 C++ plugin
|
|||||||
server/ Python relay, dev GUI, fake clients
|
server/ Python relay, dev GUI, fake clients
|
||||||
creation-kit/ ESP sources, Papyrus scripts, assets
|
creation-kit/ ESP sources, Papyrus scripts, assets
|
||||||
ui/ PrismaUI HTML/JS (server browser)
|
ui/ PrismaUI HTML/JS (server browser)
|
||||||
protocol/ Detailed packet and sync documentation
|
docs/ Architecture, setup, protocol, dev notes, roadmap
|
||||||
docs/ Architecture, setup, protocol overview, dev notes
|
docs/protocol/ Detailed packet and sync field reference
|
||||||
ThirdParty/ Vendored submodules (CommonLibF4/PrismaUI, Ultralight, etc.)
|
ThirdParty/ Vendored submodules (CommonLibF4/PrismaUI, Ultralight, etc.)
|
||||||
Branding/ Project logos
|
Branding/ Project logos
|
||||||
tools/ Helper utilities
|
tools/ Helper utilities and build scripts
|
||||||
scripts/ Build/deploy helpers
|
|
||||||
test-data/ Example packets
|
test-data/ Example packets
|
||||||
screenshots/ Development screenshots
|
screenshots/ Development screenshots
|
||||||
build/ Staged mod output (generated by build-all.bat)
|
build/ Staged mod output (generated by build-all.bat)
|
||||||
@@ -158,9 +157,11 @@ Root batch scripts: `build-all.bat`, `deploy-all.bat`, `build.bat`, `compile-pap
|
|||||||
| [`docs/setup.md`](docs/setup.md) | Build, deploy, submodules, main-menu notes |
|
| [`docs/setup.md`](docs/setup.md) | Build, deploy, submodules, main-menu notes |
|
||||||
| [`docs/architecture.md`](docs/architecture.md) | Plugin, server, proxy, threading model |
|
| [`docs/architecture.md`](docs/architecture.md) | Plugin, server, proxy, threading model |
|
||||||
| [`docs/protocol.md`](docs/protocol.md) | Message types and LAN discovery |
|
| [`docs/protocol.md`](docs/protocol.md) | Message types and LAN discovery |
|
||||||
|
| [`docs/protocol/`](docs/protocol/) | Detailed packet field reference |
|
||||||
| [`docs/limitations.md`](docs/limitations.md) | Technical limits and unsupported systems |
|
| [`docs/limitations.md`](docs/limitations.md) | Technical limits and unsupported systems |
|
||||||
| [`docs/version-targets.md`](docs/version-targets.md) | Supported game/F4SE versions |
|
| [`docs/version-targets.md`](docs/version-targets.md) | Supported game/F4SE versions |
|
||||||
| [`docs/project-comparison.md`](docs/project-comparison.md) | Commonwealth Online vs DoxyCoSync |
|
| [`docs/project-comparison.md`](docs/project-comparison.md) | Commonwealth Online vs DoxyCoSync |
|
||||||
|
| [`docs/ROADMAP.md`](docs/ROADMAP.md) | Vertical-slice roadmap and milestones |
|
||||||
| [`changelog.md`](changelog.md) | Record of code and documentation changes |
|
| [`changelog.md`](changelog.md) | Record of code and documentation changes |
|
||||||
| [`docs/dev-log.md`](docs/dev-log.md) | Milestones, testing notes, next steps |
|
| [`docs/dev-log.md`](docs/dev-log.md) | Milestones, testing notes, next steps |
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -27,7 +27,7 @@ if not exist "%UL_SDK%\include" (
|
|||||||
|
|
||||||
echo Linking Ultralight SDK from:
|
echo Linking Ultralight SDK from:
|
||||||
echo %UL_SDK%
|
echo %UL_SDK%
|
||||||
powershell -NoProfile -ExecutionPolicy Bypass -File "%SCRIPT_DIR%scripts\setup-ultralight-link.ps1" -SdkPath "%UL_SDK%"
|
powershell -NoProfile -ExecutionPolicy Bypass -File "%SCRIPT_DIR%tools\scripts\setup-ultralight-link.ps1" -SdkPath "%UL_SDK%"
|
||||||
if errorlevel 1 (
|
if errorlevel 1 (
|
||||||
echo Failed to link Ultralight SDK.
|
echo Failed to link Ultralight SDK.
|
||||||
exit /b 1
|
exit /b 1
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ For testing notes, milestone summaries, known issues, and next steps, see [`docs
|
|||||||
- Updated `server/requirements.txt` with `typer` and `rich` dependencies.
|
- Updated `server/requirements.txt` with `typer` and `rich` dependencies.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
- Repository cleanup: moved `protocol/` and `ROADMAP.md` under `docs/`, moved decompiled `Interface/` reference assets to `docs/reference/Interface/`, moved `setup-ultralight-link.ps1` to `tools/scripts/`, removed stale phase/milestone docs and unused `steam_interfaces.txt`, and added `docs/README.md` as a documentation index.
|
||||||
- Documentation updated: `docs/setup.md` now includes quick-start CLI hosting instructions.
|
- Documentation updated: `docs/setup.md` now includes quick-start CLI hosting instructions.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
@@ -1,319 +0,0 @@
|
|||||||
# FFDec guide — add Multiplayer button to MainMenu.swf
|
|
||||||
|
|
||||||
> **Deprecated:** Use C++ runtime injection instead. See [`MainMenu_Injection.md`](MainMenu_Injection.md). Do not deploy a patched `MainMenu.swf` for Commonwealth Online.
|
|
||||||
|
|
||||||
Step-by-step instructions for adding the **Multiplayer** main-menu item using [JPEXS FFDec](https://github.com/jindrapetrik/jpexs-decompiler/releases), without breaking **Settings → Gameplay / Display / Audio**.
|
|
||||||
|
|
||||||
This guide is kept for historical reference only.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Before you start
|
|
||||||
|
|
||||||
### Restore vanilla SWF
|
|
||||||
|
|
||||||
Always patch from the vanilla file, not an old broken copy:
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
.\tools\restore-vanilla-mainmenu.ps1
|
|
||||||
```
|
|
||||||
|
|
||||||
Or manually copy `Interface/MainMenu.swf.vanilla.bak` → `Interface/MainMenu.swf`.
|
|
||||||
|
|
||||||
Vanilla size is **61,959 bytes**. If your file is **64 KB+**, it is probably a bad re-import — restore and start again.
|
|
||||||
|
|
||||||
### Requirements
|
|
||||||
|
|
||||||
- **FFDec** installed
|
|
||||||
- Game launched via **F4SE** with `CommonwealthOnline.dll` in `Data/F4SE/Plugins/`
|
|
||||||
- Loose Interface overrides enabled in `%USERPROFILE%/Documents/My Games/Fallout4/Fallout4Custom.ini`:
|
|
||||||
|
|
||||||
```ini
|
|
||||||
[Archive]
|
|
||||||
bInvalidateOlderFiles=1
|
|
||||||
```
|
|
||||||
|
|
||||||
### Critical rule
|
|
||||||
|
|
||||||
| Do | Don't |
|
|
||||||
|----|--------|
|
|
||||||
| Edit **individual methods** (double-click method in traits list) | Paste the full `MainMenu.as` export into the class editor |
|
|
||||||
| Save after each small edit | Use **Import script** on the whole `MainMenu` class |
|
|
||||||
| Keep SWF ~62–63 KB | Re-export the entire decompiled script |
|
|
||||||
|
|
||||||
Full re-import recompiles `RequestOptions` and breaks settings sub-menus. Controls will still work; Gameplay / Display / Audio will crash.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## FFDec navigation
|
|
||||||
|
|
||||||
1. Open **`Interface/MainMenu.swf`** in FFDec.
|
|
||||||
2. Left tree: **scripts → MainMenu**.
|
|
||||||
3. You should see a **traits / members list** (constants, functions).
|
|
||||||
4. **Double-click a method name** (e.g. `InitList`) to open **only that method** in the editor.
|
|
||||||
5. After each edit: apply/save the method, then **File → Save** the SWF.
|
|
||||||
|
|
||||||
Do **not** use **Edit ActionScript** on the entire `MainMenu` class if FFDec offers a single giant class view — use per-method editing instead.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Summary of changes
|
|
||||||
|
|
||||||
| # | Where in FFDec | What |
|
|
||||||
|---|----------------|------|
|
|
||||||
| 1 | Class constants | Add `MULTIPLAYER_INDEX` |
|
|
||||||
| 2 | Method `InitList` | Push Multiplayer row after Load |
|
|
||||||
| 3 | Method `onMainListItemPress` | Add `case` for Multiplayer |
|
|
||||||
| 4 | New method `CloseMultiplayerBrowser` | Helper (recommended) |
|
|
||||||
| 5 | Method `set currentState` | Call close helper when leaving main panel |
|
|
||||||
| 6 | Method `onMainListItemPress` | Close browser when another item is selected |
|
|
||||||
|
|
||||||
Changes **4–6** are recommended so the HTML browser closes immediately when opening Settings, etc. The plugin also hides the browser when `currentState` leaves `MAIN_STATE`, but the SWF hooks make it feel instant.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Change 1 — Add index constant
|
|
||||||
|
|
||||||
**FFDec:** `MainMenu` → constants section (near other `*_INDEX` constants)
|
|
||||||
|
|
||||||
**Find this block** (search for `INSTALLED_CONTENT_INDEX`):
|
|
||||||
|
|
||||||
```actionscript
|
|
||||||
private const INSTALLED_CONTENT_INDEX:Number = 14;
|
|
||||||
|
|
||||||
private const MAIN_PANEL_BACKGROUND:uint = 0;
|
|
||||||
```
|
|
||||||
|
|
||||||
**Insert between those lines:**
|
|
||||||
|
|
||||||
```actionscript
|
|
||||||
private const MULTIPLAYER_INDEX:Number = 15;
|
|
||||||
```
|
|
||||||
|
|
||||||
Index `15` is unused in vanilla (highest defined index is `14`).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Change 2 — Add menu row in `InitList`
|
|
||||||
|
|
||||||
**FFDec:** `MainMenu` → method **`InitList`**
|
|
||||||
|
|
||||||
**Find this block** (search for `"$LOAD"` push — it appears after the `$NEW` block on title screen):
|
|
||||||
|
|
||||||
```actionscript
|
|
||||||
this.MainPanel_mc.List_mc.entryList.push({
|
|
||||||
"text":"$LOAD",
|
|
||||||
"disabled":!this.HasRecentSave || !param6,
|
|
||||||
"index":this.LOAD_INDEX
|
|
||||||
});
|
|
||||||
if(param8 == true)
|
|
||||||
```
|
|
||||||
|
|
||||||
**Replace with** (add Multiplayer block between Load and `if(param8 == true)`):
|
|
||||||
|
|
||||||
```actionscript
|
|
||||||
this.MainPanel_mc.List_mc.entryList.push({
|
|
||||||
"text":"$LOAD",
|
|
||||||
"disabled":!this.HasRecentSave || !param6,
|
|
||||||
"index":this.LOAD_INDEX
|
|
||||||
});
|
|
||||||
if(!this.PauseMode)
|
|
||||||
{
|
|
||||||
this.MainPanel_mc.List_mc.entryList.push({
|
|
||||||
"text":"Multiplayer",
|
|
||||||
"index":this.MULTIPLAYER_INDEX
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if(param8 == true)
|
|
||||||
```
|
|
||||||
|
|
||||||
**Notes:**
|
|
||||||
|
|
||||||
- `"Multiplayer"` is a literal label (no `$` prefix). Strings with `$` are looked up in `Interface/Translate_en.txt`.
|
|
||||||
- `!this.PauseMode` limits the row to the **title screen**, not the in-game pause menu.
|
|
||||||
- Row appears between **Load** and **Add-ons** (or PS5 transfer if enabled).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Change 3 — Handle click in `onMainListItemPress`
|
|
||||||
|
|
||||||
**FFDec:** `MainMenu` → method **`onMainListItemPress`**
|
|
||||||
|
|
||||||
**Find this block** (search for `case this.HELP_INDEX:`):
|
|
||||||
|
|
||||||
```actionscript
|
|
||||||
break;
|
|
||||||
case this.HELP_INDEX:
|
|
||||||
this.StartState(this.HELP_STATE);
|
|
||||||
this.BGSCodeObj.PlayOKSound();
|
|
||||||
break;
|
|
||||||
```
|
|
||||||
|
|
||||||
**Insert immediately before** `case this.HELP_INDEX:`:
|
|
||||||
|
|
||||||
```actionscript
|
|
||||||
break;
|
|
||||||
case this.MULTIPLAYER_INDEX:
|
|
||||||
this.BGSCodeObj.PlayOKSound();
|
|
||||||
if(root.f4se != null && root.f4se.plugins != null && root.f4se.plugins.commonwealthOnline != null)
|
|
||||||
{
|
|
||||||
root.f4se.plugins.commonwealthOnline.openManager();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case this.HELP_INDEX:
|
|
||||||
this.StartState(this.HELP_STATE);
|
|
||||||
this.BGSCodeObj.PlayOKSound();
|
|
||||||
break;
|
|
||||||
```
|
|
||||||
|
|
||||||
This calls the C++ API registered by `CommonwealthOnline.dll` (`openManager` → PrismaUI server browser). **F9** still toggles the same browser.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Change 4 — Add `CloseMultiplayerBrowser` method
|
|
||||||
|
|
||||||
**FFDec:** `MainMenu` → **add new method** (right-click class → Add script / Add method, or paste near other private helpers like `onMainListItemPress`)
|
|
||||||
|
|
||||||
**Add this entire method:**
|
|
||||||
|
|
||||||
```actionscript
|
|
||||||
private function CloseMultiplayerBrowser() : *
|
|
||||||
{
|
|
||||||
if(root.f4se != null && root.f4se.plugins != null && root.f4se.plugins.commonwealthOnline != null)
|
|
||||||
{
|
|
||||||
root.f4se.plugins.commonwealthOnline.closeManager();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Change 5 — Close browser in `set currentState`
|
|
||||||
|
|
||||||
**FFDec:** `MainMenu` → method **`set currentState`** (may appear as `currentState` setter)
|
|
||||||
|
|
||||||
**Find:**
|
|
||||||
|
|
||||||
```actionscript
|
|
||||||
public function set currentState(param1:String) : *
|
|
||||||
{
|
|
||||||
this.strCurrentState = param1;
|
|
||||||
this.UpdateButtons(param1);
|
|
||||||
this.UpdateStateFocus(param1);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Change to:**
|
|
||||||
|
|
||||||
```actionscript
|
|
||||||
public function set currentState(param1:String) : *
|
|
||||||
{
|
|
||||||
this.strCurrentState = param1;
|
|
||||||
if(param1 != this.MAIN_STATE)
|
|
||||||
{
|
|
||||||
this.CloseMultiplayerBrowser();
|
|
||||||
}
|
|
||||||
this.UpdateButtons(param1);
|
|
||||||
this.UpdateStateFocus(param1);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Change 6 — Close browser when another main-menu item is selected
|
|
||||||
|
|
||||||
**FFDec:** `MainMenu` → method **`onMainListItemPress`** (same method as Change 3)
|
|
||||||
|
|
||||||
**Find** the start of the `else` branch, after the `while` loop that returns to main state:
|
|
||||||
|
|
||||||
```actionscript
|
|
||||||
while(this.strCurrentState != this.MAIN_STATE)
|
|
||||||
{
|
|
||||||
this.onCancelPress(true,false);
|
|
||||||
}
|
|
||||||
if(this.MainPanel_mc.List_mc.selectedEntry.index == this.CONTINUE_INDEX && this.PauseMode)
|
|
||||||
```
|
|
||||||
|
|
||||||
**Insert between the `while` loop and the `CONTINUE_INDEX` check:**
|
|
||||||
|
|
||||||
```actionscript
|
|
||||||
while(this.strCurrentState != this.MAIN_STATE)
|
|
||||||
{
|
|
||||||
this.onCancelPress(true,false);
|
|
||||||
}
|
|
||||||
if(this.MainPanel_mc.List_mc.selectedEntry.index != this.MULTIPLAYER_INDEX)
|
|
||||||
{
|
|
||||||
this.CloseMultiplayerBrowser();
|
|
||||||
}
|
|
||||||
if(this.MainPanel_mc.List_mc.selectedEntry.index == this.CONTINUE_INDEX && this.PauseMode)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Save and deploy
|
|
||||||
|
|
||||||
1. **File → Save** in FFDec (overwrite `Interface/MainMenu.swf`).
|
|
||||||
2. Check file size: should be roughly **62,000–63,500 bytes**. If **64,000+**, restore vanilla and redo using **method-only** edits.
|
|
||||||
3. Copy to game:
|
|
||||||
|
|
||||||
```
|
|
||||||
Fallout 4/Data/Interface/MainMenu.swf
|
|
||||||
```
|
|
||||||
|
|
||||||
4. Launch via **F4SE**.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Test checklist
|
|
||||||
|
|
||||||
| Step | Expected |
|
|
||||||
|------|----------|
|
|
||||||
| Main menu (title screen) | **Multiplayer** appears after **Load** |
|
|
||||||
| Pause menu → Settings | **No** Multiplayer row |
|
|
||||||
| Click **Multiplayer** | Server browser opens |
|
|
||||||
| **Settings → Gameplay** | No crash |
|
|
||||||
| **Settings → Display** | No crash |
|
|
||||||
| **Settings → Audio** | No crash |
|
|
||||||
| **Settings → Controls** | Still works |
|
|
||||||
| Open Multiplayer, then **Settings** | Browser closes |
|
|
||||||
| **F9** | Still toggles browser |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### Settings sub-menus crash again
|
|
||||||
|
|
||||||
You likely re-imported the full class. Run `.\tools\restore-vanilla-mainmenu.ps1` and redo changes **1–6** using **per-method** editing only.
|
|
||||||
|
|
||||||
### Multiplayer row missing
|
|
||||||
|
|
||||||
- Confirm `bInvalidateOlderFiles=1` in `Fallout4Custom.ini`.
|
|
||||||
- Confirm loose `Data/Interface/MainMenu.swf` exists and is not overridden by another mod (check MO2 load order).
|
|
||||||
- Confirm you added the `InitList` block with `if(!this.PauseMode)`.
|
|
||||||
|
|
||||||
### Multiplayer row visible but click does nothing
|
|
||||||
|
|
||||||
- Launch via **F4SE**, not vanilla exe.
|
|
||||||
- Confirm `CommonwealthOnline.dll` is loading (check `Documents/My Games/Fallout4/F4SE/CommonwealthOnline.log`).
|
|
||||||
- Confirm Change **3** (`case this.MULTIPLAYER_INDEX`) is present.
|
|
||||||
|
|
||||||
### Button shows `$MULTIPLAYER` or wrong text
|
|
||||||
|
|
||||||
Use literal `"Multiplayer"` (no `$`), or add a translation key to `Interface/Translate_en.txt`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Reference files
|
|
||||||
|
|
||||||
| File | Purpose |
|
|
||||||
|------|---------|
|
|
||||||
| [`MainMenu.swf.vanilla.bak`](../MainMenu.swf.vanilla.bak) | Untouched vanilla backup |
|
|
||||||
| [`scripts/MainMenu.as`](scripts/MainMenu.as) | Full decompiled reference — **do not import wholesale** |
|
|
||||||
| [`PATCH_MainMenu_Multiplayer.md`](PATCH_MainMenu_Multiplayer.md) | Short overview |
|
|
||||||
| [`../../tools/restore-vanilla-mainmenu.ps1`](../../tools/restore-vanilla-mainmenu.ps1) | Restore vanilla SWF from game BA2 |
|
|
||||||
|
|
||||||
Plugin API (already implemented in C++):
|
|
||||||
|
|
||||||
- `root.f4se.plugins.commonwealthOnline.openManager()`
|
|
||||||
- `root.f4se.plugins.commonwealthOnline.closeManager()`
|
|
||||||
@@ -106,7 +106,7 @@ The `index` field is a **logical ID** used by `onMainListItemPress()` — it is
|
|||||||
|
|
||||||
### Vanilla logical indices
|
### Vanilla logical indices
|
||||||
|
|
||||||
From `Interface/exported/scripts/MainMenu.as` (reference only — do not re-import):
|
From `docs/reference/Interface/exported/scripts/MainMenu.as` (reference only — do not re-import):
|
||||||
|
|
||||||
| Constant | Index | Typical label |
|
| Constant | Index | Typical label |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
|
|||||||
@@ -55,9 +55,6 @@ In `CommonwealthOnline.log`, look for `[MainMenuInject]` lines:
|
|||||||
- `Injected Multiplayer row after Load.`
|
- `Injected Multiplayer row after Load.`
|
||||||
- `Registered ITEM_PRESS listener ('…').`
|
- `Registered ITEM_PRESS listener ('…').`
|
||||||
|
|
||||||
## Deprecated approach
|
## Reference material
|
||||||
|
|
||||||
The FFDec SWF patch guide is superseded by this document:
|
Decompiled vanilla `MainMenu` ActionScript for reading only (do not re-import) lives under [`docs/reference/Interface/`](reference/Interface/).
|
||||||
|
|
||||||
- [`FFDEC_MainMenu_Multiplayer_Guide.md`](FFDEC_MainMenu_Multiplayer_Guide.md) — historical reference only
|
|
||||||
- [`PATCH_MainMenu_Multiplayer.md`](../Interface/exported/PATCH_MainMenu_Multiplayer.md) — historical reference only
|
|
||||||
|
|||||||
@@ -1,159 +0,0 @@
|
|||||||
# MainMenu.swf — Multiplayer button patch guide
|
|
||||||
|
|
||||||
> **Full FFDec walkthrough:** see [`FFDEC_MainMenu_Multiplayer_Guide.md`](FFDEC_MainMenu_Multiplayer_Guide.md) for exact search anchors, before/after snippets, and a test checklist.
|
|
||||||
|
|
||||||
Add a **Multiplayer** item to the title-screen main menu. Selecting it calls `root.f4se.plugins.commonwealthOnline.openManager()`, which opens the PrismaUI server browser (same as **F9**).
|
|
||||||
|
|
||||||
## CRITICAL — edit methods, not the whole script
|
|
||||||
|
|
||||||
**Never** paste the full exported `MainMenu.as` into FFDec or use **Import script** on the whole class. That recompiles `RequestOptions` from decompiled source and **breaks Settings → Gameplay / Display / Audio**.
|
|
||||||
|
|
||||||
In FFDec, edit **individual methods** (double-click the method under **MainMenu** traits). FFDec then recompiles only that method and leaves vanilla bytecode (including `RequestOptions`) intact.
|
|
||||||
|
|
||||||
Restore vanilla any time:
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
.\tools\restore-vanilla-mainmenu.ps1
|
|
||||||
```
|
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
|
|
||||||
- [JPEXS FFDec](https://github.com/jindrapetrik/jpexs-decompiler/releases)
|
|
||||||
- Game launched via **F4SE** with `CommonwealthOnline.dll` installed
|
|
||||||
- Loose Interface overrides enabled in `Fallout4Custom.ini`:
|
|
||||||
|
|
||||||
```ini
|
|
||||||
[Archive]
|
|
||||||
bInvalidateOlderFiles=1
|
|
||||||
```
|
|
||||||
|
|
||||||
## FFDec workflow
|
|
||||||
|
|
||||||
1. Copy `Interface/MainMenu.swf.vanilla.bak` → `Interface/MainMenu.swf` (start from vanilla every time).
|
|
||||||
2. Open `Interface/MainMenu.swf` in FFDec.
|
|
||||||
3. In the left tree: **scripts → MainMenu** (class traits list, not “Edit ActionScript” on the whole class).
|
|
||||||
4. Apply the **four method-level edits** below (double-click each method to open its editor, save after each).
|
|
||||||
5. **File → Save**.
|
|
||||||
6. Sanity check: patched SWF should stay **~62–63 KB**. If it jumps to **64 KB+**, settings are likely broken — restore vanilla and retry using **method** editors only.
|
|
||||||
7. Copy to `Fallout 4/Data/Interface/MainMenu.swf`.
|
|
||||||
8. Test **Settings → Gameplay / Display / Audio** first, then **Multiplayer**.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Edit 1 — Add constant (class body)
|
|
||||||
|
|
||||||
Open the **MainMenu** class initializer / constants section (or edit at top of class in trait view). After `INSTALLED_CONTENT_INDEX`:
|
|
||||||
|
|
||||||
```actionscript
|
|
||||||
private const MULTIPLAYER_INDEX:Number = 15;
|
|
||||||
```
|
|
||||||
|
|
||||||
Index `15` is unused in vanilla (highest is `INSTALLED_CONTENT_INDEX = 14`).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Edit 2 — `InitList()` — add menu row
|
|
||||||
|
|
||||||
In **`InitList`**, find the `$LOAD` push (after `$NEW` on title screen). Insert **before** the PS5 transfer block:
|
|
||||||
|
|
||||||
```actionscript
|
|
||||||
this.MainPanel_mc.List_mc.entryList.push({
|
|
||||||
"text":"$LOAD",
|
|
||||||
"disabled":!this.HasRecentSave || !param6,
|
|
||||||
"index":this.LOAD_INDEX
|
|
||||||
});
|
|
||||||
if(!this.PauseMode)
|
|
||||||
{
|
|
||||||
this.MainPanel_mc.List_mc.entryList.push({
|
|
||||||
"text":"Multiplayer",
|
|
||||||
"index":this.MULTIPLAYER_INDEX
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if(param8 == true)
|
|
||||||
```
|
|
||||||
|
|
||||||
Use literal `"Multiplayer"` (no `$` prefix) unless you add a row to `Interface/Translate_en.txt`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Edit 3 — `onMainListItemPress()` — handle click
|
|
||||||
|
|
||||||
In the main `switch(this.MainPanel_mc.List_mc.selectedEntry.index)`, add **before** `case this.HELP_INDEX:`:
|
|
||||||
|
|
||||||
```actionscript
|
|
||||||
case this.MULTIPLAYER_INDEX:
|
|
||||||
this.BGSCodeObj.PlayOKSound();
|
|
||||||
if(root.f4se != null && root.f4se.plugins != null && root.f4se.plugins.commonwealthOnline != null)
|
|
||||||
{
|
|
||||||
root.f4se.plugins.commonwealthOnline.openManager();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Edit 4 (recommended) — auto-close browser
|
|
||||||
|
|
||||||
The plugin also hides the browser when the menu leaves `MAIN_STATE`, but these SWF hooks close it immediately when navigating away.
|
|
||||||
|
|
||||||
### 4a — Add new method `CloseMultiplayerBrowser`
|
|
||||||
|
|
||||||
In FFDec: **MainMenu → right-click → Add script** / add method:
|
|
||||||
|
|
||||||
```actionscript
|
|
||||||
private function CloseMultiplayerBrowser() : *
|
|
||||||
{
|
|
||||||
if(root.f4se != null && root.f4se.plugins != null && root.f4se.plugins.commonwealthOnline != null)
|
|
||||||
{
|
|
||||||
root.f4se.plugins.commonwealthOnline.closeManager();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4b — Edit `set currentState`
|
|
||||||
|
|
||||||
After `this.strCurrentState = param1;`:
|
|
||||||
|
|
||||||
```actionscript
|
|
||||||
this.strCurrentState = param1;
|
|
||||||
if(param1 != this.MAIN_STATE)
|
|
||||||
{
|
|
||||||
this.CloseMultiplayerBrowser();
|
|
||||||
}
|
|
||||||
this.UpdateButtons(param1);
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4c — Edit `onMainListItemPress`
|
|
||||||
|
|
||||||
After the `while(this.strCurrentState != this.MAIN_STATE)` loop, before the `switch`:
|
|
||||||
|
|
||||||
```actionscript
|
|
||||||
if(this.MainPanel_mc.List_mc.selectedEntry.index != this.MULTIPLAYER_INDEX)
|
|
||||||
{
|
|
||||||
this.CloseMultiplayerBrowser();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Test plan
|
|
||||||
|
|
||||||
| Step | Expected |
|
|
||||||
|------|----------|
|
|
||||||
| Main menu shows **Multiplayer** between Load and Add-ons | Yes |
|
|
||||||
| Click **Multiplayer** | Server browser opens |
|
|
||||||
| Open **Settings → Display** | No crash |
|
|
||||||
| Open **Settings → Gameplay / Audio** | No crash |
|
|
||||||
| Select **Continue** while browser open | Browser closes |
|
|
||||||
| **F9** | Still toggles browser |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Reference
|
|
||||||
|
|
||||||
Full decompiled class (do **not** import wholesale): [`scripts/MainMenu.as`](scripts/MainMenu.as)
|
|
||||||
|
|
||||||
Plugin API registered in `CommonwealthOnline.dll`:
|
|
||||||
|
|
||||||
- `root.f4se.plugins.commonwealthOnline.openManager()`
|
|
||||||
- `root.f4se.plugins.commonwealthOnline.closeManager()`
|
|
||||||
@@ -1,390 +0,0 @@
|
|||||||
# Phases 1-6 Complete: TiltedEvolution Alignment Implementation Summary
|
|
||||||
|
|
||||||
## Project Milestone: Animation Synchronization Architecture Complete ✅
|
|
||||||
|
|
||||||
**Date:** June 3, 2026
|
|
||||||
**Status:** Ready for Phase 6 In-Game Testing
|
|
||||||
**Effort:** ~40 hours (Phases 1-5 complete)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
Successfully aligned Commonwealth Online with TiltedEvolution's proven multiplayer animation synchronization architecture. Implemented complete stack from network protocol through animation descriptor system to action replay infrastructure.
|
|
||||||
|
|
||||||
**Key Achievement:** Proxy actors can now theoretically display working animations by:
|
|
||||||
1. Dynamically spawning proxies
|
|
||||||
2. Injecting character controller velocity
|
|
||||||
3. Replicating actor state flags
|
|
||||||
4. Synchronizing animation graph variables via descriptor indexing
|
|
||||||
5. Queuing discrete action events for replay
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Phase-by-Phase Deliverables
|
|
||||||
|
|
||||||
### Phase 1: Blueprint & Research ✅
|
|
||||||
**Status:** Complete
|
|
||||||
**Output:** Architecture documentation + TiltedEvolution alignment analysis
|
|
||||||
|
|
||||||
- Created `docs/f4-animation-descriptor.md`
|
|
||||||
- FO4 humanoid graph variable mapping (9 floats, 6 bools, 3 ints)
|
|
||||||
- Comparison with Skyrim SE model
|
|
||||||
- Variable roles and network sync requirements
|
|
||||||
|
|
||||||
- Created `docs/animation-architecture-alignment.md`
|
|
||||||
- Component-by-component alignment (7 major components)
|
|
||||||
- Data flow diagrams (3 flow models)
|
|
||||||
- FO4-specific adaptations identified
|
|
||||||
- Integration points for Phases 2-5 documented
|
|
||||||
|
|
||||||
- Created `docs/animation-sync-analysis.md`
|
|
||||||
- Detailed analysis of current failure modes
|
|
||||||
- TiltedEvolution solutions explained
|
|
||||||
- Root cause analysis documented
|
|
||||||
|
|
||||||
**Result:** Clear architectural path forward with no blockers identified.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Phase 2: State & Protocol ✅
|
|
||||||
**Status:** Complete
|
|
||||||
**Output:** Network protocol extended + state structure updated
|
|
||||||
|
|
||||||
**Modified Files:**
|
|
||||||
- `plugin/include/F4TRemotePlayerState.h`
|
|
||||||
- Extended struct with actor state flags (flags1, flags2)
|
|
||||||
- Added action event queue
|
|
||||||
- Created `RemoteActionEvent` struct
|
|
||||||
|
|
||||||
- `plugin/src/F4TNetworking.cpp`
|
|
||||||
- Updated JSON parsing for new fields
|
|
||||||
- Maintained backward compatibility (optional fields)
|
|
||||||
- Enhanced logging with flag values
|
|
||||||
|
|
||||||
- `server/fake_client.py`
|
|
||||||
- Added actor state flag parsing
|
|
||||||
- Updated display to show flags in hex format
|
|
||||||
|
|
||||||
**New Files:**
|
|
||||||
- `docs/protocol-phase2-extensions.md`
|
|
||||||
- Full protocol specification
|
|
||||||
- Actor state flag reference table
|
|
||||||
- Action event structure documented
|
|
||||||
|
|
||||||
**Result:** Network protocol extensible for action replay + state sync. Fully backward compatible.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Phase 3: Animation Descriptors ✅
|
|
||||||
**Status:** Complete
|
|
||||||
**Output:** Descriptor-based variable indexing infrastructure
|
|
||||||
|
|
||||||
**Created Files:**
|
|
||||||
- `plugin/include/F4AnimationDescriptor.h`
|
|
||||||
- Class definition with full API
|
|
||||||
- Singleton pattern
|
|
||||||
- Public query/read/write methods
|
|
||||||
|
|
||||||
- `plugin/src/F4AnimationDescriptor.cpp`
|
|
||||||
- Implementation with humanoid graph variable tables
|
|
||||||
- Reverse lookup maps (name → index)
|
|
||||||
- Bulk read/write methods (`SaveAnimationVariablesFromCache`, `LoadAnimationVariablesToCache`)
|
|
||||||
- Thread-safe initialization
|
|
||||||
|
|
||||||
**Features:**
|
|
||||||
- Pre-computed variable tables (one-time init)
|
|
||||||
- Efficient O(1) index-based access
|
|
||||||
- Single graph manager lock for batch operations
|
|
||||||
- Graceful fallback to string-based API
|
|
||||||
|
|
||||||
**Result:** Efficient animation variable synchronization with 3-5x performance gain over string-based writes.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Phase 4: Dynamic Proxy Spawning & Velocity ✅
|
|
||||||
**Status:** Complete
|
|
||||||
**Output:** Dynamic proxy spawning + velocity injection integration
|
|
||||||
|
|
||||||
**Modified Files:**
|
|
||||||
- `plugin/src/F4TProxyActorController.cpp`
|
|
||||||
- Added `g_dynamicProxyPool` (playerId → ActorHandle map)
|
|
||||||
- Implemented `SpawnDynamicProxyActor()` function
|
|
||||||
- Implemented `GetOrSpawnDynamicProxy()` function
|
|
||||||
- Modified `TryResolveSlotProxy()` to prefer dynamic spawn
|
|
||||||
- Enhanced `MoveSlotProxyToRemotePlayer()` with actor state flag application
|
|
||||||
|
|
||||||
**New Infrastructure:**
|
|
||||||
- Dynamic spawning via `Player::PlaceAtMe()`
|
|
||||||
- Proxy reuse system (keep actors, reuse by playerId)
|
|
||||||
- Character controller velocity injection (already present, verified)
|
|
||||||
- Actor state flag synchronization
|
|
||||||
|
|
||||||
**Result:** Proxies spawn dynamically with proper velocity injection + state synchronization. Fallback to pre-placed pool maintained.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Phase 5: Action Replay & Descriptor Sync ✅
|
|
||||||
**Status:** Complete
|
|
||||||
**Output:** Action queue system + descriptor-based animation sync
|
|
||||||
|
|
||||||
**Created Files:**
|
|
||||||
- `plugin/include/F4RemoteActionComponent.h`
|
|
||||||
- `RemoteActionSnapshot` struct
|
|
||||||
- `RemoteActionQueue` class
|
|
||||||
- Enqueue/replay/clear methods
|
|
||||||
|
|
||||||
- `plugin/src/F4RemoteActionComponent.cpp`
|
|
||||||
- Full action queue implementation
|
|
||||||
- Actor state application from snapshot
|
|
||||||
- Animation variable loading via descriptor
|
|
||||||
- Comprehensive logging
|
|
||||||
|
|
||||||
**Modified Files:**
|
|
||||||
- `plugin/src/F4TProxyActorController.cpp`
|
|
||||||
- Added `actionQueue` member to `ProxyActorSlot`
|
|
||||||
- Each proxy has its own action queue
|
|
||||||
|
|
||||||
- `plugin/src/F4TProxyAnimationSync.cpp`
|
|
||||||
- Added `ApplyProxyAnimationFromRemoteStateDescriptorBased()`
|
|
||||||
- Uses descriptor-based bulk writes
|
|
||||||
- Replaces per-frame string lookups
|
|
||||||
|
|
||||||
**Result:** Action replay infrastructure + efficient descriptor-based animation sync ready for integration.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Phase 6: Testing & Iteration Framework ✅
|
|
||||||
**Status:** Ready for In-Game Testing
|
|
||||||
**Output:** Comprehensive testing documentation + quick-start guide
|
|
||||||
|
|
||||||
**Created Files:**
|
|
||||||
- `docs/phase6-testing-guide.md` (8,000+ words)
|
|
||||||
- Complete testing procedures
|
|
||||||
- 5 critical test categories
|
|
||||||
- Performance benchmarking
|
|
||||||
- Debugging checklist
|
|
||||||
- Common issues + fixes
|
|
||||||
- Success criteria
|
|
||||||
|
|
||||||
- `docs/phase6-quick-start.md`
|
|
||||||
- 30-minute critical path for testing
|
|
||||||
- Console log patterns (good vs bad)
|
|
||||||
- Performance expectations
|
|
||||||
- Common fixes quick reference
|
|
||||||
- Results template
|
|
||||||
|
|
||||||
**Test Coverage:**
|
|
||||||
1. Dynamic Proxy Spawning
|
|
||||||
2. Smooth Movement Synchronization
|
|
||||||
3. Velocity Injection & Animation Triggering (CRITICAL)
|
|
||||||
4. Actor State Flags & Sneak Animation
|
|
||||||
5. Multiple Proxy Actors (stability)
|
|
||||||
6. Performance & Stability Profiling
|
|
||||||
7. Velocity Scaling Tuning
|
|
||||||
8. Variable Index Verification
|
|
||||||
|
|
||||||
**Result:** Ready for in-game validation. Testing framework ensures comprehensive coverage of all phases.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Architecture Summary
|
|
||||||
|
|
||||||
### Before (Pre-Phase 1)
|
|
||||||
```
|
|
||||||
SetPosition-only movement
|
|
||||||
↓
|
|
||||||
No character controller velocity
|
|
||||||
↓
|
|
||||||
Animation system sees 0 velocity
|
|
||||||
↓
|
|
||||||
No animations play ❌
|
|
||||||
```
|
|
||||||
|
|
||||||
### After (Post-Phase 5)
|
|
||||||
```
|
|
||||||
Dynamic proxy spawn
|
|
||||||
+ Character controller velocity injection
|
|
||||||
+ Actor state flag synchronization
|
|
||||||
+ Descriptor-based animation variable sync
|
|
||||||
+ Action event replay queue
|
|
||||||
↓
|
|
||||||
Animation system sees real velocity + proper graph state
|
|
||||||
↓
|
|
||||||
Animations should play ✅ (to be verified Phase 6)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Code Statistics
|
|
||||||
|
|
||||||
| Metric | Count |
|
|
||||||
|--------|-------|
|
|
||||||
| Files created | 8 |
|
|
||||||
| Files modified | 6 |
|
|
||||||
| C++ code added | ~800 lines |
|
|
||||||
| Python code updated | ~100 lines |
|
|
||||||
| Documentation added | ~15,000 words |
|
|
||||||
| Total lines of code | ~5,000 lines |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Key Technical Achievements
|
|
||||||
|
|
||||||
1. **Descriptor-Based Animation Sync (Phase 3+5)**
|
|
||||||
- O(1) indexed variable access vs O(n) string lookup
|
|
||||||
- Single graph manager lock vs multiple locks
|
|
||||||
- 3-5x performance improvement
|
|
||||||
|
|
||||||
2. **Dynamic Proxy Spawning (Phase 4)**
|
|
||||||
- Replaced pre-placed pool with dynamic spawning
|
|
||||||
- Maintains slot-based reuse (4 concurrent proxies)
|
|
||||||
- Fallback to pre-placed for compatibility
|
|
||||||
|
|
||||||
3. **Character Controller Velocity Injection (Phase 4)**
|
|
||||||
- Verified working via `Move(0.016f, delta, false)` + `SetLinearVelocityImpl()`
|
|
||||||
- Enables animation system velocity evaluation
|
|
||||||
- Clamps to 400 units/sec max
|
|
||||||
|
|
||||||
4. **Actor State Synchronization (Phase 4)**
|
|
||||||
- Replicates `actorState.flags1/2` from remote player
|
|
||||||
- Applied before animation sync for FSM precedence
|
|
||||||
- Enables state-driven animation transitions
|
|
||||||
|
|
||||||
5. **Action Replay Infrastructure (Phase 5)**
|
|
||||||
- `RemoteActionQueue` per proxy slot
|
|
||||||
- Action snapshots with full state
|
|
||||||
- Max 16 pending actions per queue
|
|
||||||
- Ready for Phase 2.3 action capture
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Testing Readiness
|
|
||||||
|
|
||||||
**Ready for Phase 6 In-Game Testing:**
|
|
||||||
- ✅ Dynamic proxies spawn successfully
|
|
||||||
- ✅ Velocity injection framework in place
|
|
||||||
- ✅ Actor state flags integrated
|
|
||||||
- ✅ Descriptor-based sync implemented
|
|
||||||
- ✅ Action queue infrastructure ready
|
|
||||||
- ✅ Network protocol extended
|
|
||||||
- ✅ Comprehensive testing documentation
|
|
||||||
- ✅ Quick-start guide for testing
|
|
||||||
|
|
||||||
**Critical Test (Test 3):**
|
|
||||||
- Will verify if velocity injection triggers animations
|
|
||||||
- If PASS → Proceed to Phase 7
|
|
||||||
- If FAIL → Iterate Phase 6.3 (tune variables/scaling)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## What's Next
|
|
||||||
|
|
||||||
### Phase 6: In-Game Testing (NOW)
|
|
||||||
1. Build plugin with all changes
|
|
||||||
2. Load Fallout 4 with F4SE
|
|
||||||
3. Run Test 1-5 as per guide
|
|
||||||
4. Document results
|
|
||||||
5. Tune if needed (Phase 6.3)
|
|
||||||
|
|
||||||
### Phase 7: Cleanup & Documentation (After Phase 6)
|
|
||||||
1. Remove debug gates/scaffolding
|
|
||||||
2. Finalize architecture documentation
|
|
||||||
3. Update protocol documentation
|
|
||||||
4. Create animation descriptor guide
|
|
||||||
5. Production-ready code
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Known Unknowns (To Be Verified Phase 6)
|
|
||||||
|
|
||||||
1. **Will velocity injection trigger animations on dynamic proxies?**
|
|
||||||
- Theoretical: Yes (all pieces in place)
|
|
||||||
- Practical: To be verified in-game
|
|
||||||
|
|
||||||
2. **Are animation variable indices correct for FO4?**
|
|
||||||
- Based on: Skyrim SE comparison + FO4 theory
|
|
||||||
- Verification: Phase 6 testing + debug logging
|
|
||||||
|
|
||||||
3. **Is 400 units/sec velocity clamping appropriate?**
|
|
||||||
- Assumption: Yes, based on estimated graph limits
|
|
||||||
- Tuning: Phase 6.3 iteration if needed
|
|
||||||
|
|
||||||
4. **Will actor state flags properly guide animation FSM?**
|
|
||||||
- Theory: Yes (same as Skyrim)
|
|
||||||
- Practice: Phase 6 testing
|
|
||||||
|
|
||||||
5. **Will action replay (Phase 2.3) integrate cleanly?**
|
|
||||||
- Infrastructure: Ready (Phase 5.1)
|
|
||||||
- Capture: Deferred to Phase 7 or later
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Success Metrics
|
|
||||||
|
|
||||||
### Phase 1-5 (Completed)
|
|
||||||
- ✅ Architecture aligned with TiltedEvolution
|
|
||||||
- ✅ Protocol extended without breaking changes
|
|
||||||
- ✅ Descriptor infrastructure implemented
|
|
||||||
- ✅ Dynamic spawning system operational
|
|
||||||
- ✅ Action queue infrastructure ready
|
|
||||||
- ✅ Code compiles without errors
|
|
||||||
- ✅ ~5,000 lines of tested code
|
|
||||||
|
|
||||||
### Phase 6 (In Progress)
|
|
||||||
- Testing framework: ✅ Complete
|
|
||||||
- In-game validation: ⏳ Awaiting developer testing
|
|
||||||
- Performance profiling: ⏳ Awaiting testing
|
|
||||||
- Variable tuning: ⏳ Awaiting testing
|
|
||||||
|
|
||||||
### Phase 7 (Pending)
|
|
||||||
- Code cleanup
|
|
||||||
- Documentation finalization
|
|
||||||
- Production release
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Repository Status
|
|
||||||
|
|
||||||
**All files committed:**
|
|
||||||
```
|
|
||||||
Phases 1-5 code: Complete
|
|
||||||
Phases 1-5 docs: Complete
|
|
||||||
Phase 6 testing framework: Complete
|
|
||||||
Ready for in-game validation
|
|
||||||
```
|
|
||||||
|
|
||||||
**Total effort:** ~40 hours (Phases 1-5)
|
|
||||||
**Estimated Phase 6:** 2-4 hours testing + tuning
|
|
||||||
**Estimated Phase 7:** 2-3 hours cleanup + docs
|
|
||||||
**Total project:** ~48-50 hours
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Critical Success Path for Phase 6
|
|
||||||
|
|
||||||
1. Build + load plugin
|
|
||||||
2. Run Test 3 (Animations)
|
|
||||||
- IF PASS: Proceed to Phase 7
|
|
||||||
- IF FAIL: Debug + iterate Phase 6.3
|
|
||||||
3. Document results
|
|
||||||
4. Proceed to Phase 7 when all tests pass
|
|
||||||
|
|
||||||
**Recommendation:** Run Test 3 first as it's the make-or-break test for the entire animation system.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Conclusion
|
|
||||||
|
|
||||||
Phases 1-5 successfully implement a complete, TiltedEvolution-aligned animation synchronization architecture for Commonwealth Online. All foundational systems are in place:
|
|
||||||
|
|
||||||
- ✅ Descriptor-based efficient variable sync
|
|
||||||
- ✅ Dynamic proxy spawning with reuse
|
|
||||||
- ✅ Character controller velocity injection
|
|
||||||
- ✅ Actor state flag replication
|
|
||||||
- ✅ Action event queue infrastructure
|
|
||||||
|
|
||||||
**Phase 6 will validate whether the theoretical implementation achieves the goal of working proxy animations in-game.**
|
|
||||||
|
|
||||||
Ready to test! 🚀
|
|
||||||
|
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
# Documentation
|
||||||
|
|
||||||
|
Start here for Commonwealth Online project docs.
|
||||||
|
|
||||||
|
## Core
|
||||||
|
|
||||||
|
| Document | Purpose |
|
||||||
|
|---|---|
|
||||||
|
| [`setup.md`](setup.md) | Build, deploy, submodules, main-menu notes |
|
||||||
|
| [`architecture.md`](architecture.md) | Plugin, server, proxy, threading model |
|
||||||
|
| [`protocol.md`](protocol.md) | Message types and LAN discovery overview |
|
||||||
|
| [`protocol/`](protocol/) | Detailed packet and sync field reference |
|
||||||
|
| [`limitations.md`](limitations.md) | Technical limits and unsupported systems |
|
||||||
|
| [`version-targets.md`](version-targets.md) | Supported game/F4SE versions |
|
||||||
|
| [`ROADMAP.md`](ROADMAP.md) | Vertical-slice roadmap and milestones |
|
||||||
|
| [`dev-log.md`](dev-log.md) | Milestones, testing notes, next steps |
|
||||||
|
|
||||||
|
## Feature and integration guides
|
||||||
|
|
||||||
|
| Document | Purpose |
|
||||||
|
|---|---|
|
||||||
|
| [`weapon-animation-sync.md`](weapon-animation-sync.md) | Weapon animation graph reference |
|
||||||
|
| [`f4-animation-descriptor.md`](f4-animation-descriptor.md) | Humanoid animation graph variable mapping |
|
||||||
|
| [`animation-architecture-alignment.md`](animation-architecture-alignment.md) | Animation sync architecture notes |
|
||||||
|
| [`animation-sync-analysis.md`](animation-sync-analysis.md) | Animation sync problem analysis |
|
||||||
|
| [`protocol-phase2-extensions.md`](protocol-phase2-extensions.md) | Phase 2 protocol field extensions |
|
||||||
|
| [`MainMenu_Injection.md`](MainMenu_Injection.md) | Runtime main-menu Multiplayer row injection |
|
||||||
|
| [`MainMenu_Button_Injection_Guide.md`](MainMenu_Button_Injection_Guide.md) | Full main-menu injection how-to |
|
||||||
|
| [`pause-menu-vanilla-settings-plan.md`](pause-menu-vanilla-settings-plan.md) | Planned vanilla settings integration |
|
||||||
|
| [`project-comparison.md`](project-comparison.md) | Commonwealth Online vs DoxyCoSync |
|
||||||
|
|
||||||
|
## Research and reference
|
||||||
|
|
||||||
|
| Location | Purpose |
|
||||||
|
|---|---|
|
||||||
|
| [`research/`](research/) | Engine notes, sync research, related projects |
|
||||||
|
| [`reference/Interface/`](reference/Interface/) | Decompiled vanilla main-menu SWF reference (read-only) |
|
||||||
|
|
||||||
|
## Outside this folder
|
||||||
|
|
||||||
|
| Document | Purpose |
|
||||||
|
|---|---|
|
||||||
|
| [`../README.md`](../README.md) | Project overview and quick start |
|
||||||
|
| [`../changelog.md`](../changelog.md) | Change history |
|
||||||
|
| [`../server/README.md`](../server/README.md) | Relay server and dev GUI |
|
||||||
|
| [`../plugin/setup.md`](../plugin/setup.md) | Plugin build notes |
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
# Roadmap
|
|
||||||
|
|
||||||
## Phase 0: Research and Setup
|
|
||||||
|
|
||||||
- [ ] Create repository structure
|
|
||||||
- [ ] Document project scope
|
|
||||||
- [ ] Document legal boundaries
|
|
||||||
- [ ] Set up Fallout 4 test profile
|
|
||||||
- [ ] Install F4SE
|
|
||||||
- [ ] Install Creation Kit
|
|
||||||
- [ ] Choose CommonLibF4 template
|
|
||||||
- [ ] Create test cell plan
|
|
||||||
|
|
||||||
## Phase 1: F4SE Plugin Bootstrap
|
|
||||||
|
|
||||||
- [ ] Create basic F4SE plugin project
|
|
||||||
- [ ] Confirm plugin loads in Fallout 4
|
|
||||||
- [ ] Create plugin log output
|
|
||||||
- [ ] Detect game version
|
|
||||||
- [ ] Read local player reference
|
|
||||||
- [ ] Read local player position
|
|
||||||
- [ ] Write position to log
|
|
||||||
|
|
||||||
## Phase 2: External Server Prototype
|
|
||||||
|
|
||||||
- [ ] Create basic local server
|
|
||||||
- [ ] Define packet format
|
|
||||||
- [ ] Support client connect/disconnect
|
|
||||||
- [ ] Assign player IDs
|
|
||||||
- [ ] Receive transform packets
|
|
||||||
- [ ] Broadcast transform packets to other clients
|
|
||||||
|
|
||||||
## Phase 3: Fallout 4 Network Connection
|
|
||||||
|
|
||||||
- [ ] Connect F4SE plugin to local server
|
|
||||||
- [ ] Send test packets from Fallout 4
|
|
||||||
- [ ] Send real player transform packets
|
|
||||||
- [ ] Receive remote transform packets
|
|
||||||
- [ ] Store remote player state safely
|
|
||||||
|
|
||||||
## Phase 4: Remote Player Representation
|
|
||||||
|
|
||||||
- [ ] Create test cell in Creation Kit
|
|
||||||
- [ ] Create placeholder remote player actor
|
|
||||||
- [ ] Spawn or place remote actor
|
|
||||||
- [ ] Move remote actor using received transform data
|
|
||||||
- [ ] Smooth remote actor movement
|
|
||||||
- [ ] Clean up actor on disconnect
|
|
||||||
|
|
||||||
## Phase 5: Basic Gameplay Sync
|
|
||||||
|
|
||||||
- [ ] Sync crouch state
|
|
||||||
- [ ] Sync sprint/walk state
|
|
||||||
- [ ] Sync weapon drawn state
|
|
||||||
- [ ] Sync basic fire event
|
|
||||||
- [ ] Sync simple health state
|
|
||||||
- [ ] Add debug UI or console commands
|
|
||||||
|
|
||||||
## Phase 6: Controlled Multiplayer Test
|
|
||||||
|
|
||||||
- [ ] Two clients in same test cell
|
|
||||||
- [ ] Both players visible
|
|
||||||
- [ ] Movement sync working
|
|
||||||
- [ ] Basic animation sync working
|
|
||||||
- [ ] Basic LAN test documented
|
|
||||||
@@ -9,6 +9,36 @@ failed experiments, successful tests, and next steps.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 2026-07-10 - Repository cleanup
|
||||||
|
|
||||||
|
### Summary
|
||||||
|
Reorganized documentation and reference assets to reduce root clutter without changing plugin, server, or game code.
|
||||||
|
|
||||||
|
### Files Changed
|
||||||
|
- `docs/README.md` (new documentation index)
|
||||||
|
- `docs/ROADMAP.md` (moved from repo root)
|
||||||
|
- `docs/protocol/` (moved from repo root `protocol/`)
|
||||||
|
- `docs/reference/Interface/` (moved from repo root `Interface/`)
|
||||||
|
- `tools/scripts/setup-ultralight-link.ps1` (moved from `scripts/`)
|
||||||
|
- Removed stale phase/milestone docs, deprecated FFDec main-menu guides, and unused `steam_interfaces.txt`
|
||||||
|
- Updated `README.md`, `build-prismaui.bat`, `tools/README.md`, `CONTRIBUTING.md`, and related doc links
|
||||||
|
|
||||||
|
### Details
|
||||||
|
- Root now holds entry-point batch scripts and core project files only; detailed protocol specs and roadmap live under `docs/`.
|
||||||
|
- Decompiled vanilla interface files remain available as read-only reference material under `docs/reference/Interface/`.
|
||||||
|
- Historical phase completion reports and deprecated SWF patching guides were removed after confirming they were superseded by current docs.
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
- Not run (documentation and file moves only).
|
||||||
|
|
||||||
|
### Known Issues
|
||||||
|
- None currently known.
|
||||||
|
|
||||||
|
### Next Steps
|
||||||
|
- Continue feature work against the cleaned documentation layout.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 2026-07-09 - Consumer Server CLI v1
|
## 2026-07-09 - Consumer Server CLI v1
|
||||||
|
|
||||||
### Summary
|
### Summary
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
# Project Overview
|
|
||||||
|
|
||||||
Commonwealth Online is a prototype multiplayer framework experiment for Fallout 4.
|
|
||||||
|
|
||||||
The first target is not full co-op. The first target is a controlled test where two clients can connect to a local server and see each other as synced actors in a custom test cell.
|
|
||||||
|
|
||||||
## Core Idea
|
|
||||||
|
|
||||||
Fallout 4 remains the client application. A native F4SE plugin reads local player state and communicates with an external server.
|
|
||||||
|
|
||||||
The server tracks connected players and broadcasts relevant state to other clients.
|
|
||||||
|
|
||||||
A Creation Kit plugin provides test cells, placeholder actors, scripts, and controlled test content.
|
|
||||||
|
|
||||||
## Basic Flow
|
|
||||||
|
|
||||||
```text
|
|
||||||
Fallout 4 Client A
|
|
||||||
|
|
|
||||||
| local player transform
|
|
||||||
v
|
|
||||||
External Server
|
|
||||||
|
|
|
||||||
| remote player transform
|
|
||||||
v
|
|
||||||
Fallout 4 Client B
|
|
||||||
```
|
|
||||||
|
|
||||||
## Early Limitations
|
|
||||||
|
|
||||||
The first prototype should assume:
|
|
||||||
|
|
||||||
- All players are in the same cell
|
|
||||||
- No quest synchronization
|
|
||||||
- No settlement synchronization
|
|
||||||
- No VATS
|
|
||||||
- No dialogue sync
|
|
||||||
- No full inventory sync
|
|
||||||
- No support for large modlists
|
|
||||||
@@ -1,232 +0,0 @@
|
|||||||
# Phase 1-3 Completion Report: TiltedEvolution Alignment
|
|
||||||
|
|
||||||
## Executive Summary
|
|
||||||
|
|
||||||
Successfully completed Phase 1-3 of the Commonwealth Online → TiltedEvolution alignment initiative. Established comprehensive architectural foundation, protocol extensions, and animation descriptor infrastructure to enable working proxy actor animations.
|
|
||||||
|
|
||||||
**Status:** Ready to proceed with Phase 4 (Dynamic Spawn).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Phases Completed
|
|
||||||
|
|
||||||
### Phase 1: Blueprint & Research ✅
|
|
||||||
|
|
||||||
**Deliverables:**
|
|
||||||
- `docs/f4-animation-descriptor.md` — FO4 animation graph variable mapping + comparison with Skyrim SE
|
|
||||||
- `docs/animation-architecture-alignment.md` — Component-by-component alignment between TiltedEvolution and F4T with data flow diagrams
|
|
||||||
- `docs/animation-sync-analysis.md` — Detailed analysis of why current approach fails and how TiltedEvolution solves it
|
|
||||||
|
|
||||||
**Key Findings:**
|
|
||||||
- TiltedEvolution's architecture is compatible with FO4
|
|
||||||
- Character controller velocity injection already tested and works
|
|
||||||
- Main blockers: no descriptor tables, no action replay, no actor state flags
|
|
||||||
- Variable indices need determination via debug extraction (template provided)
|
|
||||||
|
|
||||||
**Artifacts:**
|
|
||||||
- 2 comprehensive alignment documents
|
|
||||||
- 3 architecture diagrams (mermaid)
|
|
||||||
- Comparison tables (TiltedEvolution vs F4T)
|
|
||||||
- Risk mitigation strategy documented
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Phase 2: State & Protocol ✅
|
|
||||||
|
|
||||||
**Deliverables:**
|
|
||||||
- Extended `RemotePlayerState` struct with:
|
|
||||||
- `uint32_t actorStateFlags1/2` — Actor state flags (combat, sneaking, animation state)
|
|
||||||
- `std::vector<RemoteActionEvent> actionEvents` — Action queue for discrete action replay
|
|
||||||
- New `RemoteActionEvent` struct with animation variable snapshots
|
|
||||||
- Updated `F4TNetworking.cpp` to parse new optional JSON fields
|
|
||||||
- Updated `server/fake_client.py` to handle and display new fields
|
|
||||||
- Created `docs/protocol-phase2-extensions.md` — Full protocol specification
|
|
||||||
|
|
||||||
**Features:**
|
|
||||||
- ✅ Fully backward compatible (missing fields = sensible defaults)
|
|
||||||
- ✅ JSON protocol extended (new optional fields)
|
|
||||||
- ✅ Server relay unchanged (transparent pass-through)
|
|
||||||
- ✅ Test client displays actor state flags in hex
|
|
||||||
- ✅ Thread-safe state storage (existing mutex preserved)
|
|
||||||
|
|
||||||
**Protocol Changes:**
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"type": "transform",
|
|
||||||
"playerId": 1,
|
|
||||||
// ... existing fields ...
|
|
||||||
|
|
||||||
// Phase 2 NEW FIELDS (optional):
|
|
||||||
"actorStateFlags1": 0x00000042,
|
|
||||||
"actorStateFlags2": 0x00000001,
|
|
||||||
"actionEvents": [
|
|
||||||
{
|
|
||||||
"type": 0,
|
|
||||||
"eventName": "ActorMovementStart",
|
|
||||||
"animationVariablesFloat": [...],
|
|
||||||
"animationVariablesBool": [...],
|
|
||||||
"animationVariablesInt": [...]
|
|
||||||
// ... more fields ...
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Phase 3: Animation Descriptors ✅
|
|
||||||
|
|
||||||
**Deliverables:**
|
|
||||||
- `plugin/include/F4AnimationDescriptor.h` — Class definition with full API
|
|
||||||
- `plugin/src/F4AnimationDescriptor.cpp` — Implementation with variable tables
|
|
||||||
|
|
||||||
**Features:**
|
|
||||||
- Singleton pattern with one-time initialization
|
|
||||||
- Pre-computed humanoid graph variable tables:
|
|
||||||
- **9 float variables:** Speed, direction, speedDamped, speedSampled, pitchGunAim, weaponAdjust, velocityZ, speedWalk, speedRun
|
|
||||||
- **6 bool variables:** isSprinting, isSneaking, isMoving, bMotionDriven, bInMoveState, bSprintOK
|
|
||||||
- **3 int variables:** iLeftHandType, iRightHandEquipped, iIsInSneak
|
|
||||||
- Reverse lookup maps (name → index) for diagnostics
|
|
||||||
- Public API:
|
|
||||||
- Variable name/index queries
|
|
||||||
- Bulk read/write methods: `SaveAnimationVariablesFromCache()`, `LoadAnimationVariablesToCache()`
|
|
||||||
- Thread-safe (one-time init per process)
|
|
||||||
- Graceful fallback to string-based API if indexed access unavailable
|
|
||||||
|
|
||||||
**Performance Improvement:**
|
|
||||||
- **Old (per-frame per-call):** O(n) string search × number of variables
|
|
||||||
- **New (bulk indexed):** O(1) array access + single graph manager lock
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Code Changes Summary
|
|
||||||
|
|
||||||
### Files Modified
|
|
||||||
- `docs/dev-log.md` — Added Phase 1-3 entries with technical details
|
|
||||||
- `plugin/include/F4TRemotePlayerState.h` — Extended struct with actor state + action queue
|
|
||||||
- `plugin/src/F4TNetworking.cpp` — Parse new JSON fields with backward compatibility
|
|
||||||
- `server/fake_client.py` — Display actor state flags
|
|
||||||
|
|
||||||
### Files Created
|
|
||||||
- `docs/f4-animation-descriptor.md` — FO4 variable mapping template
|
|
||||||
- `docs/animation-architecture-alignment.md` — Component alignment guide
|
|
||||||
- `docs/animation-sync-analysis.md` — Analysis of animation failures + solutions
|
|
||||||
- `docs/protocol-phase2-extensions.md` — Protocol specification for new fields
|
|
||||||
- `plugin/include/F4AnimationDescriptor.h` — Descriptor class definition
|
|
||||||
- `plugin/src/F4AnimationDescriptor.cpp` — Descriptor implementation
|
|
||||||
|
|
||||||
### Net Result
|
|
||||||
- **13 files** created/modified
|
|
||||||
- **~1,200 lines** of C++ code (descriptors + headers)
|
|
||||||
- **~1,500 lines** of documentation
|
|
||||||
- **~300 lines** of Python (protocol extensions + test client)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Architectural Changes
|
|
||||||
|
|
||||||
### Before Phase 1-3 (Current State)
|
|
||||||
```
|
|
||||||
Local Player State → JSON → Network → Remote State
|
|
||||||
↓
|
|
||||||
Proxy Actor (SetPosition only)
|
|
||||||
No velocity → No animations ❌
|
|
||||||
```
|
|
||||||
|
|
||||||
### After Phase 1-3 (Prepared for Phase 4+)
|
|
||||||
```
|
|
||||||
Local Player State + Actor State Flags + Actions → JSON (extended) → Network → Remote State (extended)
|
|
||||||
↓
|
|
||||||
Proxy Actor (Dynamic spawn)
|
|
||||||
+ Velocity Injection
|
|
||||||
+ Descriptor-based sync
|
|
||||||
+ Action Replay
|
|
||||||
= Animations Work ✅
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Next Steps: Phase 4 (Dynamic Spawn)
|
|
||||||
|
|
||||||
### Phase 4.1: Implement Dynamic Proxy Spawning
|
|
||||||
- Replace pre-placed pool with dynamic `PlaceAtMe()` spawning
|
|
||||||
- Implement slot-based reuse (keep actors alive, reuse by playerId)
|
|
||||||
- Start with single proxy, add safeguards incrementally
|
|
||||||
- Keep pre-placed fallback active during testing
|
|
||||||
|
|
||||||
### Phase 4.2: Character Controller Velocity (Verify)
|
|
||||||
- Test `Actor::Move(0.016f, {0,0,0}, false)` on dynamic proxies
|
|
||||||
- Verify `SetLinearVelocityImpl()` works as expected
|
|
||||||
- Set velocity before animation sync for proper precedence
|
|
||||||
|
|
||||||
### Phase 4.3: Actor State Application
|
|
||||||
- Apply `actor→actorState.flags1/2` from remote state
|
|
||||||
- Signal animation graph FSM about combat, sneaking, etc.
|
|
||||||
- This enables state-driven graph transitions
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Testing Criteria (Phase 6)
|
|
||||||
|
|
||||||
- [ ] Plugin compiles with all new code
|
|
||||||
- [ ] Descriptor singleton initializes correctly (9 floats, 6 bools, 3 ints)
|
|
||||||
- [ ] Dynamic proxies spawn and move smoothly
|
|
||||||
- [ ] Character controller velocity set each frame
|
|
||||||
- [ ] Locomotion animations play (idle → walk → run → sprint)
|
|
||||||
- [ ] Actor state flags affect visuals (sneak = crouch)
|
|
||||||
- [ ] Descriptor bulk writes faster than string-based writes
|
|
||||||
- [ ] No crashes with 4+ proxies simultaneously
|
|
||||||
- [ ] Architecture aligns with TiltedEvolution patterns
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Decision Log
|
|
||||||
|
|
||||||
| Decision | Rationale | Impact |
|
|
||||||
|----------|-----------|--------|
|
|
||||||
| Use TiltedEvolution as blueprint | Proven in production (Skyrim SE), matches Creation Engine fundamentals | Full architectural refactor, 28-40 hours total |
|
|
||||||
| Singleton descriptor + tables | Efficient (init once, use always), extensible for creature graphs | Slightly more code, significant performance gain |
|
|
||||||
| Backward-compatible protocol | Avoid breaking existing clients/servers | New fields are optional, defaults sensible |
|
|
||||||
| Dynamic spawn instead of pool | Enables animations (proxy needs AI context), matches TiltedEvolution model | Requires testing for crash safety |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Known Unknowns (Phase 1-3 Research Gaps)
|
|
||||||
|
|
||||||
1. **Exact FO4 animation variable indices** — Template created, values need debug extraction
|
|
||||||
2. **Whether `ActorMediator::ForceAction()` works the same in FO4** — Assume yes, test in Phase 5
|
|
||||||
3. **Whether dynamic proxies can have proper AI without pathfinding issues** — Assume yes, test in Phase 4
|
|
||||||
4. **Performance of descriptor bulk writes vs string-based** — Expect faster, measure in Phase 6
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Success Metrics (Achieved)
|
|
||||||
|
|
||||||
- ✅ Phase 1-3 all completed on schedule
|
|
||||||
- ✅ Architecture documented and aligned with TiltedEvolution
|
|
||||||
- ✅ Protocol extended without breaking compatibility
|
|
||||||
- ✅ Descriptor infrastructure ready for action replay
|
|
||||||
- ✅ No crashes or compilation errors
|
|
||||||
- ✅ Clear integration points for Phase 4-5
|
|
||||||
- ✅ Risk mitigation strategy in place
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Recommendations
|
|
||||||
|
|
||||||
1. **Continue to Phase 4 immediately** — Architecture is solid, no blockers identified
|
|
||||||
2. **Start with single dynamic proxy during Phase 4** — Test spawn/movement/velocity before scaling
|
|
||||||
3. **Prioritize animation debugging logs** — Will be essential for Phase 6 testing
|
|
||||||
4. **Keep pre-placed fallback active** — Safety net if dynamic spawn has issues
|
|
||||||
5. **Coordinate Phase 2.3 action capture after Phase 5 animation sync works** — Get locomotion working first
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Conclusion
|
|
||||||
|
|
||||||
Phases 1-3 successfully establish the foundation for TiltedEvolution-style animation synchronization in F4T. All architectural decisions are well-documented, the protocol is extensible, and descriptor infrastructure is ready. The path to working proxy actor animations is clear. Phase 4 (Dynamic Spawn) is the critical next step that will unlock character controller velocity injection and enable proper animation system integration.
|
|
||||||
|
|
||||||
**Estimated remaining work:** 20-30 hours (Phases 4-7).
|
|
||||||
**Confidence level:** High (architecture proven, integration points clear).
|
|
||||||
**Risk level:** Low-Medium (dynamic spawn needs safety testing, animation variables need verification).
|
|
||||||
|
|
||||||
@@ -1,242 +0,0 @@
|
|||||||
# Phase 2 Interpolation Debugging Guide
|
|
||||||
|
|
||||||
## Problem: "Still Jerky, Not Smooth Movement"
|
|
||||||
|
|
||||||
This guide will help diagnose why interpolation isn't producing smooth movement.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Quick Diagnostics
|
|
||||||
|
|
||||||
### 1. **Check If Interpolation is Even Being Called**
|
|
||||||
|
|
||||||
**Log signature to find:**
|
|
||||||
```
|
|
||||||
[Local Player ID: X] Phase 2 interpolation: remote player Y, actor=ZZZZZZZZ, smooth blending active.
|
|
||||||
```
|
|
||||||
|
|
||||||
**If this log is NOT appearing:**
|
|
||||||
- Interpolation is not being triggered
|
|
||||||
- Check: Is `distanceDrift > 0.01F`? (Remote player must be moving)
|
|
||||||
- Check: Is `a_shouldSnap == false`? (Not a teleport/snap movement)
|
|
||||||
- Check: Does `a_optSlot` exist? (Must be a runtime proxy, not fallback)
|
|
||||||
|
|
||||||
### 2. **Verify Update Rate is Sufficient**
|
|
||||||
|
|
||||||
**What's needed for smooth interpolation:**
|
|
||||||
- Updates must arrive **frequently enough** to queue waypoints
|
|
||||||
- Too infrequent updates = large gaps between waypoints = large lerps = still jerky
|
|
||||||
|
|
||||||
**Check fake client update rate:**
|
|
||||||
```bash
|
|
||||||
# In fake_client.py, look for the movement loop
|
|
||||||
# It should be sending updates at least 10-30 Hz (10-30 per second)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Root Causes of Jerkiness
|
|
||||||
|
|
||||||
### **Root Cause 1: Not Enough Waypoints**
|
|
||||||
|
|
||||||
**Symptom:** Movement is still teleport-like, not smooth
|
|
||||||
**Reason:** Interpolation needs AT LEAST 2 waypoints
|
|
||||||
|
|
||||||
**Diagram:**
|
|
||||||
```
|
|
||||||
Bad (jerky):
|
|
||||||
Frame 1: ╔══ Position A
|
|
||||||
Frame 2: ║ ╔══ Position B (teleport)
|
|
||||||
Frame 3: ║ ║ ╔══ Position C (teleport)
|
|
||||||
|
|
||||||
Good (smooth):
|
|
||||||
Frame 1: ╔══════════════════╗ WayPoint 1 (A) queued
|
|
||||||
Frame 2: ║ Interp between ║ WayPoint 2 (B) queued, lerp A→B at 0.5
|
|
||||||
║ A and B at 25% ║
|
|
||||||
Frame 3: ║ Interp between ║ WayPoint 3 (C) queued, lerp B→C at 0.5
|
|
||||||
║ B and C at 50% ║
|
|
||||||
```
|
|
||||||
|
|
||||||
**Diagnosis:**
|
|
||||||
- Add extra logging to `ApplyRuntimeProxyTransform()`:
|
|
||||||
```cpp
|
|
||||||
LogInfoWithLocalPlayerPrefix(std::format(
|
|
||||||
"Waypoint queue size: {}, distanceDrift: {:.2f}, interpolating: {}",
|
|
||||||
a_optSlot->interpolationComponent.TimePoints.size(),
|
|
||||||
distanceDrift,
|
|
||||||
interpResult.IsInterpolating));
|
|
||||||
```
|
|
||||||
|
|
||||||
### **Root Cause 2: Update Arrival Rate Too Slow**
|
|
||||||
|
|
||||||
**Symptom:** Movement jumps happen, then freeze, then jump again
|
|
||||||
**Reason:** Remote player updates arriving slower than game frame rate
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
```
|
|
||||||
Game FPS: 60 (16ms per frame)
|
|
||||||
Update Rate: 10 Hz (100ms per update)
|
|
||||||
|
|
||||||
Result:
|
|
||||||
Frame 1-6: (100ms of frames) Interpolate slowly
|
|
||||||
Frame 7: NEW UPDATE arrives, big jump
|
|
||||||
Frame 8-13: Interpolate again
|
|
||||||
```
|
|
||||||
|
|
||||||
**Fix:** Increase fake client update frequency
|
|
||||||
```python
|
|
||||||
# In fake_client.py, change sleep duration:
|
|
||||||
time.sleep(0.033) # 30 Hz instead of slower
|
|
||||||
```
|
|
||||||
|
|
||||||
### **Root Cause 3: Alpha Calculation Wrong**
|
|
||||||
|
|
||||||
**Symptom:** Movement speed changes unexpectedly, or stalls
|
|
||||||
**Reason:** Frame counter might not be incrementing correctly
|
|
||||||
|
|
||||||
**Check the alpha calculation:**
|
|
||||||
```cpp
|
|
||||||
// In InterpolationSystem::Update()
|
|
||||||
float alpha = static_cast<float>(tickElapsed) / static_cast<float>(tickDelta);
|
|
||||||
|
|
||||||
// If tickDelta is 0 or 1, alpha calculation breaks
|
|
||||||
// tickDelta should be the FRAME DIFFERENCE between waypoints (e.g., 30-60)
|
|
||||||
```
|
|
||||||
|
|
||||||
### **Root Cause 4: Waypoint Queue Pruning Too Aggressive**
|
|
||||||
|
|
||||||
**Symptom:** Interpolation logs appear but movement still jerky
|
|
||||||
**Reason:** Queue size limited to 3, old waypoints popped too early
|
|
||||||
|
|
||||||
**Current code:**
|
|
||||||
```cpp
|
|
||||||
while (a_optSlot->interpolationComponent.TimePoints.size() > 3) {
|
|
||||||
a_optSlot->interpolationComponent.TimePoints.pop_front();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Try increasing to 5-10 waypoints** for more buffer.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Testing Strategy
|
|
||||||
|
|
||||||
### **Step 1: Add Detailed Logging**
|
|
||||||
|
|
||||||
Edit `F4TProxyActorController.cpp` in `ApplyRuntimeProxyTransform()`:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
if (a_optSlot && !a_shouldSnap && distanceDrift > 0.01F) {
|
|
||||||
static uint64_t frameCounter = 0;
|
|
||||||
frameCounter++;
|
|
||||||
|
|
||||||
// ADD THIS DEBUG LOG:
|
|
||||||
if (frameCounter % 10 == 0) { // Log every 10th frame to avoid spam
|
|
||||||
LogInfoWithLocalPlayerPrefix(std::format(
|
|
||||||
"Phase2-DEBUG: frame={}, distanceDrift={:.2f}, queueSize={}, interpolating={}",
|
|
||||||
frameCounter,
|
|
||||||
distanceDrift,
|
|
||||||
a_optSlot->interpolationComponent.TimePoints.size(),
|
|
||||||
/* will know after Update */
|
|
||||||
false));
|
|
||||||
}
|
|
||||||
|
|
||||||
// ... rest of code
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### **Step 2: Monitor Log Output**
|
|
||||||
|
|
||||||
After recompiling, run fake client and watch log:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Terminal: Watch logs in real-time
|
|
||||||
Get-Content "c:\Users\User\Documents\My Games\Fallout4\F4SE\CommonwealthOnline.log" -Wait -Tail 50 | Select-String "Phase2-DEBUG"
|
|
||||||
```
|
|
||||||
|
|
||||||
**Expected pattern (good interpolation):**
|
|
||||||
```
|
|
||||||
Phase2-DEBUG: frame=10, distanceDrift=345.62, queueSize=2, interpolating=true
|
|
||||||
Phase2-DEBUG: frame=20, distanceDrift=298.45, queueSize=2, interpolating=true
|
|
||||||
Phase2-DEBUG: frame=30, distanceDrift=251.23, queueSize=2, interpolating=true ← Smooth decrease
|
|
||||||
```
|
|
||||||
|
|
||||||
**Bad pattern (not working):**
|
|
||||||
```
|
|
||||||
Phase2-DEBUG: frame=10, distanceDrift=345.62, queueSize=1, interpolating=false
|
|
||||||
Phase2-DEBUG: frame=20, distanceDrift=1000.00, queueSize=1, interpolating=false ← Jumps!
|
|
||||||
Phase2-DEBUG: frame=30, distanceDrift=500.00, queueSize=1, interpolating=false
|
|
||||||
```
|
|
||||||
|
|
||||||
### **Step 3: Check Fake Client Update Rate**
|
|
||||||
|
|
||||||
Add logging to `fake_client.py`:
|
|
||||||
|
|
||||||
```python
|
|
||||||
last_update_time = time.time()
|
|
||||||
while True:
|
|
||||||
# ... send update ...
|
|
||||||
now = time.time()
|
|
||||||
update_interval = now - last_update_time
|
|
||||||
if update_interval > 0.05: # Log if >50ms between updates
|
|
||||||
print(f"WARNING: Long update interval: {update_interval*1000:.1f}ms")
|
|
||||||
last_update_time = now
|
|
||||||
time.sleep(0.033) # ~30 Hz
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Comprehensive Fix Checklist
|
|
||||||
|
|
||||||
- [ ] **Verify waypoints are queuing**: Add frame counter debug log
|
|
||||||
- [ ] **Confirm queue size > 1**: Should see "queueSize=2" in logs during movement
|
|
||||||
- [ ] **Check update frequency**: Fake client sending at 20-30 Hz minimum
|
|
||||||
- [ ] **Verify distanceDrift threshold**: Is `distanceDrift > 0.01F`?
|
|
||||||
- [ ] **Test with moving fake client**: Stationary player won't trigger interpolation
|
|
||||||
- [ ] **Increase waypoint buffer**: Try `queueSize > 5` instead of 3
|
|
||||||
- [ ] **Monitor alpha calculation**: Should go 0.0 → 1.0 as player moves between waypoints
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Quick Fix: Increase Update Frequency
|
|
||||||
|
|
||||||
**Easiest fix:** Make fake client send updates more frequently
|
|
||||||
|
|
||||||
Edit `fake_client.py`:
|
|
||||||
```python
|
|
||||||
# Find the main loop:
|
|
||||||
while True:
|
|
||||||
# ... code ...
|
|
||||||
time.sleep(0.033) # Change from 0.1 or higher to 0.033 (30 Hz)
|
|
||||||
```
|
|
||||||
|
|
||||||
Or increase queue size in `ApplyRuntimeProxyTransform()`:
|
|
||||||
```cpp
|
|
||||||
// Change from:
|
|
||||||
while (a_optSlot->interpolationComponent.TimePoints.size() > 3) {
|
|
||||||
// To:
|
|
||||||
while (a_optSlot->interpolationComponent.TimePoints.size() > 10) {
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## If Still Jerky After All This
|
|
||||||
|
|
||||||
The issue might be **architectural**: Maybe F4 doesn't support sub-frame smooth movement, and we need to adjust strategy.
|
|
||||||
|
|
||||||
**Next options:**
|
|
||||||
1. **Reduce frame times via velocity injection**: Make proxies move via physics instead of SetPosition
|
|
||||||
2. **Use different lerp approach**: Accumulate delta over frames instead of tick-based
|
|
||||||
3. **Accept some jerkiness**: Design animations to hide it (blending animations during transitions)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Need More Help?
|
|
||||||
|
|
||||||
Post the following in your logs:
|
|
||||||
1. Output of "Phase2-DEBUG" log lines (10 consecutive)
|
|
||||||
2. Fake client update interval timing
|
|
||||||
3. FPS you're getting in-game
|
|
||||||
4. Distance between waypoints in the logs
|
|
||||||
|
|
||||||
This will help diagnose the exact cause.
|
|
||||||
@@ -1,365 +0,0 @@
|
|||||||
# Phase 2: Interpolation System Testing Guide
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
Phase 2 tests the waypoint-based smooth movement interpolation system. This replaces direct position application with time-based lerp between waypoints, resulting in smoother remote player movement.
|
|
||||||
|
|
||||||
**Prerequisites:**
|
|
||||||
- F4T plugin built with interpolation system (just completed ✓)
|
|
||||||
- Fallout 4 running with F4SE
|
|
||||||
- Network relay server running (`python server.py`)
|
|
||||||
- Fake client ready (`python server/fake_client.py`)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Architecture Review
|
|
||||||
|
|
||||||
Before testing, understand what we're testing:
|
|
||||||
|
|
||||||
### Current System (Pre-Phase 2)
|
|
||||||
```
|
|
||||||
Remote Player Update
|
|
||||||
↓
|
|
||||||
GetRuntimeProxyVisibleTargetPosition()
|
|
||||||
↓
|
|
||||||
targetPosition = direct network position
|
|
||||||
↓
|
|
||||||
SetPosition(targetPosition) ← IMMEDIATE, no lerp
|
|
||||||
↓
|
|
||||||
Result: Jittery/choppy movement on low update rates
|
|
||||||
```
|
|
||||||
|
|
||||||
### New System (Phase 2)
|
|
||||||
```
|
|
||||||
Remote Player Update
|
|
||||||
↓
|
|
||||||
GetRuntimeProxyVisibleTargetPosition()
|
|
||||||
↓
|
|
||||||
AddWaypoint(targetPosition, angleZ, currentTick) → InterpolationComponent
|
|
||||||
↓
|
|
||||||
InterpolationSystem::Update(currentTick)
|
|
||||||
↓
|
|
||||||
Lerp between waypoints if tick is between them
|
|
||||||
↓
|
|
||||||
SetPosition(interpolatedPosition) ← SMOOTH, tick-based
|
|
||||||
↓
|
|
||||||
Result: Smooth movement even on low update rates
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Testing Phase 2.1: Interpolation System Unit Test
|
|
||||||
|
|
||||||
### Goal
|
|
||||||
Verify interpolation math works correctly before testing in-game.
|
|
||||||
|
|
||||||
### Test: Manual Interpolation Check
|
|
||||||
|
|
||||||
Create a simple test script (`test_interpolation.py`):
|
|
||||||
|
|
||||||
```python
|
|
||||||
# Pseudo-code showing what Phase 2 does
|
|
||||||
# Position at tick 100: (1000, 2000, 500)
|
|
||||||
# Position at tick 200: (1100, 2050, 550)
|
|
||||||
|
|
||||||
# At tick 150 (halfway):
|
|
||||||
# alpha = (150 - 100) / (200 - 100) = 0.5
|
|
||||||
# interpolated_x = 1000 + (1100 - 1000) * 0.5 = 1050
|
|
||||||
# interpolated_y = 2000 + (2050 - 2000) * 0.5 = 2025
|
|
||||||
# interpolated_z = 500 + (550 - 500) * 0.5 = 525
|
|
||||||
|
|
||||||
# Result: (1050, 2025, 525) ← smooth midpoint
|
|
||||||
```
|
|
||||||
|
|
||||||
**Expected behavior:** Movement is linear blend between waypoints.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Testing Phase 2.2: In-Game Setup
|
|
||||||
|
|
||||||
### Setup Step 1: Start Relay Server
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd f:\Repos\Commonwealth-Online\server
|
|
||||||
python server.py
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected output:
|
|
||||||
```
|
|
||||||
Server listening on 127.0.0.1:7777
|
|
||||||
Waiting for connections...
|
|
||||||
```
|
|
||||||
|
|
||||||
### Setup Step 2: Start Fake Client
|
|
||||||
|
|
||||||
In a new terminal:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd f:\Repos\Commonwealth-Online\server
|
|
||||||
python fake_client.py
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected output:
|
|
||||||
```
|
|
||||||
Connecting to server...
|
|
||||||
Connected successfully
|
|
||||||
Fake player registered with ID: <player_id>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Setup Step 3: Launch Fallout 4
|
|
||||||
|
|
||||||
1. Start Fallout 4 with F4SE
|
|
||||||
2. Load into a game with open space (avoid dungeons)
|
|
||||||
3. Check console for F4T startup messages
|
|
||||||
|
|
||||||
Expected log output:
|
|
||||||
```
|
|
||||||
[Local Player ID: X] F4T Plugin initialized
|
|
||||||
[Local Player ID: X] Proxy controller ready
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Testing Phase 2.3: Basic Interpolation Test
|
|
||||||
|
|
||||||
### Test: Remote Player Walks in Circle
|
|
||||||
|
|
||||||
This verifies waypoint buffering and interpolation.
|
|
||||||
|
|
||||||
**Setup:**
|
|
||||||
1. Have fake client move in a small circle around you
|
|
||||||
2. Watch the proxy actor move
|
|
||||||
|
|
||||||
**Expected Behavior (PRE-Phase 2):**
|
|
||||||
- Proxy position updates in discrete jumps
|
|
||||||
- Movement appears jittery/choppy
|
|
||||||
- Proxy teleports between waypoints
|
|
||||||
|
|
||||||
**Expected Behavior (POST-Phase 2):**
|
|
||||||
- Proxy moves smoothly
|
|
||||||
- Movement is continuous, not jerky
|
|
||||||
- Smooth arc instead of sharp jumps
|
|
||||||
|
|
||||||
**How to Verify:**
|
|
||||||
1. Run fake client for 30 seconds with slow movement
|
|
||||||
2. Observe proxy actor
|
|
||||||
3. Compare smoothness to pre-Phase 2 behavior
|
|
||||||
|
|
||||||
**Log Indicators:**
|
|
||||||
```
|
|
||||||
[Local Player ID: 0] First smoothed runtime proxy movement for remote player X: actor=FORMID, alpha=0.15.
|
|
||||||
```
|
|
||||||
|
|
||||||
This log should appear once per remote player.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Testing Phase 2.4: Movement Speed Variations
|
|
||||||
|
|
||||||
### Test: Fast vs Slow Movement
|
|
||||||
|
|
||||||
**Objective:** Verify interpolation works across movement speeds.
|
|
||||||
|
|
||||||
**Test Case 1: Slow Movement**
|
|
||||||
- Fake client walks slowly (1-2 units/sec)
|
|
||||||
- Expected: Smooth continuous movement
|
|
||||||
- Should NOT have position snapping
|
|
||||||
|
|
||||||
**Test Case 2: Fast Movement**
|
|
||||||
- Fake client sprints (100+ units/sec)
|
|
||||||
- Expected: Fast smooth movement
|
|
||||||
- No lag or skipping
|
|
||||||
|
|
||||||
**Test Case 3: Teleport**
|
|
||||||
- Fake client teleports 500 units away
|
|
||||||
- Expected: Proxy snaps (shouldSnap=true for teleport movement type)
|
|
||||||
- Movement type check happens first, no interpolation
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Testing Phase 2.5: Multiple Remote Players
|
|
||||||
|
|
||||||
### Test: Interpolation with 2-4 Remote Players
|
|
||||||
|
|
||||||
**Setup:**
|
|
||||||
1. Start 2-4 fake clients
|
|
||||||
2. Each sends unique movement patterns
|
|
||||||
3. Observe all proxies
|
|
||||||
|
|
||||||
**Expected Behavior:**
|
|
||||||
- Each proxy interpolates independently
|
|
||||||
- No interaction/interference between proxies
|
|
||||||
- Each has its own InterpolationComponent
|
|
||||||
- Smooth movement for all
|
|
||||||
|
|
||||||
**Performance Check:**
|
|
||||||
1. Monitor FPS with 4 remote players
|
|
||||||
2. Should be similar to Phase 1 (baseline)
|
|
||||||
3. Interpolation adds minimal CPU load (just lerp math)
|
|
||||||
|
|
||||||
**Command to simulate:**
|
|
||||||
```bash
|
|
||||||
# Terminal 1: Start main relay
|
|
||||||
python server.py
|
|
||||||
|
|
||||||
# Terminal 2-5: Start 4 fake clients
|
|
||||||
python fake_client.py
|
|
||||||
python fake_client.py
|
|
||||||
python fake_client.py
|
|
||||||
python fake_client.py
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Testing Phase 2.6: Waypoint Queue Management
|
|
||||||
|
|
||||||
### Test: Verify Waypoint Cleanup
|
|
||||||
|
|
||||||
**Objective:** Ensure old waypoints are cleaned up, not accumulated.
|
|
||||||
|
|
||||||
**How to Check:**
|
|
||||||
1. Run fake client for 2 minutes straight
|
|
||||||
2. Monitor memory usage
|
|
||||||
3. Should be stable (not growing)
|
|
||||||
|
|
||||||
**What's Happening:**
|
|
||||||
```
|
|
||||||
Tick 100: Add waypoint
|
|
||||||
Tick 101-150: Lerp between waypoint 0 and 1
|
|
||||||
Tick 151: Pop waypoint 0 (cleanup!)
|
|
||||||
Tick 151: Add new waypoint
|
|
||||||
Tick 152-200: Lerp between waypoint 0 and 1 (still 2 max)
|
|
||||||
```
|
|
||||||
|
|
||||||
The InterpolationSystem::Update() automatically pops old waypoints when they're consumed.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Testing Phase 2.7: Rotation Interpolation
|
|
||||||
|
|
||||||
### Test: Verify Smooth Rotation
|
|
||||||
|
|
||||||
**Objective:** Test angle lerp with wrap-around handling.
|
|
||||||
|
|
||||||
**Test Case 1: Normal Rotation**
|
|
||||||
- Remote player rotates from 0° to 90°
|
|
||||||
- Expected: Smooth rotation from 0° → 45° → 90°
|
|
||||||
|
|
||||||
**Test Case 2: Wrap-Around**
|
|
||||||
- Remote player rotates from 350° to 10° (crossing 0°)
|
|
||||||
- Expected: Shortest path rotation (350° → 0° → 10°), not 350° → 180° → 10°
|
|
||||||
- Our `LerpRotation()` handles this
|
|
||||||
|
|
||||||
**Visual Check:**
|
|
||||||
- Watch proxy heading as it turns
|
|
||||||
- Should be smooth, not jerky
|
|
||||||
- Should take shortest angular path
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Testing Phase 2.8: Integration with Animation Sync
|
|
||||||
|
|
||||||
### Test: Interpolation + Descriptor-Based Animation
|
|
||||||
|
|
||||||
**Setup:**
|
|
||||||
1. Ensure Phase 1 (descriptor-based animation) is working
|
|
||||||
2. Run Phase 2 interpolation in parallel
|
|
||||||
3. Fake client walks around you
|
|
||||||
|
|
||||||
**Expected Behavior:**
|
|
||||||
- Remote player position interpolates smoothly
|
|
||||||
- Remote player animation (Speed, isSprinting) syncs correctly
|
|
||||||
- No conflicts between systems
|
|
||||||
|
|
||||||
**Verification:**
|
|
||||||
```
|
|
||||||
[Local Player ID: 0] Runtime proxy animation sync initial for remote player X:
|
|
||||||
moving=true, sprinting=false, graphSpeed=55.0, direction=0.123
|
|
||||||
```
|
|
||||||
|
|
||||||
This should still appear alongside interpolation.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Testing Checklist
|
|
||||||
|
|
||||||
- [ ] **Unit Test**: Lerp math is correct (waypoint positions smooth)
|
|
||||||
- [ ] **Single Player**: Fake client walks, proxy moves smoothly
|
|
||||||
- [ ] **Slow Movement**: Proxy glides smoothly at 1-2 units/sec
|
|
||||||
- [ ] **Fast Movement**: Proxy glides smoothly at 100+ units/sec
|
|
||||||
- [ ] **Multiple Players**: 2-4 proxies move smoothly independently
|
|
||||||
- [ ] **Memory**: Stable after 2+ minutes (no memory growth)
|
|
||||||
- [ ] **Rotation**: Proxy heading rotates smoothly
|
|
||||||
- [ ] **Wrap-Around**: Rotation handles 0°↔360° correctly
|
|
||||||
- [ ] **Integration**: Works with Phase 1 animation sync
|
|
||||||
- [ ] **Snapping**: Teleports still snap (movement type check first)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Debugging: Common Issues & Fixes
|
|
||||||
|
|
||||||
### Issue: Proxy Still Jerky After Phase 2
|
|
||||||
|
|
||||||
**Cause:** Interpolation not being called
|
|
||||||
**Fix:**
|
|
||||||
1. Check that `AddWaypoint()` is being called
|
|
||||||
2. Verify `Update()` is called before `SetPosition()`
|
|
||||||
3. Check that `shouldSnap` isn't overriding interpolation
|
|
||||||
|
|
||||||
### Issue: Proxy Moves Too Slowly
|
|
||||||
|
|
||||||
**Cause:** Alpha blend factor too small
|
|
||||||
**Fix:**
|
|
||||||
1. Increase waypoint update rate (more frequent updates = smoother)
|
|
||||||
2. Check tick calculation (verify currentTick is advancing)
|
|
||||||
|
|
||||||
### Issue: Memory Grows Over Time
|
|
||||||
|
|
||||||
**Cause:** Waypoints not being popped
|
|
||||||
**Fix:**
|
|
||||||
1. Verify `Update()` is cleaning up old waypoints
|
|
||||||
2. Check that TimePoints.pop_front() is being called
|
|
||||||
|
|
||||||
### Issue: Rotation Wraps Incorrectly
|
|
||||||
|
|
||||||
**Cause:** LerpRotation() not handling 0°/360° boundary
|
|
||||||
**Fix:**
|
|
||||||
- This is handled in our implementation
|
|
||||||
- If still wrong, check angle units (radians vs degrees)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Next Steps After Testing
|
|
||||||
|
|
||||||
1. **If interpolation works:** Move to Phase 3 or other features
|
|
||||||
2. **If issues found:** Document in dev-log.md and fix
|
|
||||||
3. **Performance baseline:** Record FPS with 1, 2, 4 remote players
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Quick Start Commands
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Terminal 1: Start relay server
|
|
||||||
cd f:\Repos\Commonwealth-Online\server
|
|
||||||
python server.py
|
|
||||||
|
|
||||||
# Terminal 2: Start fake client (single remote player)
|
|
||||||
cd f:\Repos\Commonwealth-Online\server
|
|
||||||
python fake_client.py
|
|
||||||
|
|
||||||
# In-game: Launch F4T and observe proxy movement
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Expected Logs for Phase 2
|
|
||||||
|
|
||||||
When interpolation is active, look for:
|
|
||||||
|
|
||||||
```
|
|
||||||
[Local Player ID: 0] First smoothed runtime proxy movement for remote player 1: actor=0x14003ED8, alpha=0.15.
|
|
||||||
[Local Player ID: 0] Moved runtime proxy actor 0x14003ED8 from remote player 1 state for the first time: X=1234.56, Y=2345.67, Z=-100.00, AngleZ=1.570, flags1=00000000, flags2=00000000.
|
|
||||||
```
|
|
||||||
|
|
||||||
These confirm the proxy is spawned and moving smoothly.
|
|
||||||
@@ -1,177 +0,0 @@
|
|||||||
# Phase 6 Quick Start Checklist
|
|
||||||
|
|
||||||
## Before You Start
|
|
||||||
|
|
||||||
- [ ] All Phases 1-5 code committed to git
|
|
||||||
- [ ] F4T plugin builds without errors
|
|
||||||
- [ ] F4SE installed in Fallout 4
|
|
||||||
- [ ] Server relay running (`python server/server.py`)
|
|
||||||
- [ ] Ready to launch Fallout 4 with F4SE
|
|
||||||
|
|
||||||
## Test Execution Order
|
|
||||||
|
|
||||||
### Critical Path (30 minutes)
|
|
||||||
|
|
||||||
1. **[5 min] Test 1: Proxy Spawning**
|
|
||||||
- Start game, load F4TTestCell01
|
|
||||||
- Connect fake client
|
|
||||||
- [ ] Proxy appears near player
|
|
||||||
- [ ] No crashes
|
|
||||||
- **Result:** PASS / FAIL
|
|
||||||
|
|
||||||
2. **[5 min] Test 2: Movement**
|
|
||||||
- Move fake client position
|
|
||||||
- [ ] Proxy smoothly moves to new position
|
|
||||||
- [ ] No jittering/snapping
|
|
||||||
- **Result:** PASS / FAIL
|
|
||||||
|
|
||||||
3. **[10 min] TEST 3 - CRITICAL: Animations**
|
|
||||||
- Fake client: stand still → should see idle
|
|
||||||
- Fake client: walk speed → should see walk animation
|
|
||||||
- Fake client: run speed → should see run animation
|
|
||||||
- Fake client: sprint speed → should see sprint animation
|
|
||||||
- [ ] At least idle/walk/run animations visible
|
|
||||||
- **Result:** PASS / FAIL / PARTIAL
|
|
||||||
- **If FAIL:** Debug with console logs
|
|
||||||
|
|
||||||
4. **[5 min] Test 4: Sneak**
|
|
||||||
- Fake client: activate sneak
|
|
||||||
- [ ] Proxy crouches
|
|
||||||
- **Result:** PASS / FAIL / PARTIAL
|
|
||||||
|
|
||||||
5. **[5 min] Test 5: Multiple Proxies**
|
|
||||||
- Connect 3 fake clients (3 proxies total)
|
|
||||||
- All moving simultaneously
|
|
||||||
- [ ] All animate correctly
|
|
||||||
- [ ] No crashes
|
|
||||||
- **Result:** PASS / FAIL
|
|
||||||
|
|
||||||
### If Test 3 FAILS (Animations not playing)
|
|
||||||
|
|
||||||
**Debug Checklist (10 minutes):**
|
|
||||||
1. [ ] Check console logs for velocity being set
|
|
||||||
- Search: `ApplyRuntimeProxyTransform`
|
|
||||||
- Should show character controller access
|
|
||||||
|
|
||||||
2. [ ] Check animation graph manager available
|
|
||||||
- Search: `animation graph manager`
|
|
||||||
- Should NOT show "unavailable" messages
|
|
||||||
|
|
||||||
3. [ ] Check actor state flags
|
|
||||||
- Search: `flags1=`
|
|
||||||
- Should show valid hex values
|
|
||||||
|
|
||||||
4. [ ] Check Speed variable is changing
|
|
||||||
- Search: `graphSpeed=`
|
|
||||||
- Should see values > 0 when moving
|
|
||||||
|
|
||||||
5. [ ] Manual test: Set Speed directly in console
|
|
||||||
- If manual setting works → indices correct, velocity issue
|
|
||||||
- If manual setting doesn't work → index problem
|
|
||||||
|
|
||||||
**If still failing:** Document issue and proceed to Phase 6.3 iteration
|
|
||||||
|
|
||||||
## Key Logging Commands
|
|
||||||
|
|
||||||
### View Real-Time Logs
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
# In separate terminal, tail latest logs
|
|
||||||
Get-Content "Documents\My Games\Fallout4\Logs\*.log" -Wait
|
|
||||||
```
|
|
||||||
|
|
||||||
### Search Logs for Specific Info
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
# Proxy spawn
|
|
||||||
Select-String "SpawnDynamicProxyActor" (Get-ChildItem "Documents\My Games\Fallout4\Logs\*")
|
|
||||||
|
|
||||||
# Animations
|
|
||||||
Select-String "animation sync" (Get-ChildItem "Documents\My Games\Fallout4\Logs\*")
|
|
||||||
|
|
||||||
# Errors
|
|
||||||
Select-String "WARNING\|ERROR" (Get-ChildItem "Documents\My Games\Fallout4\Logs\*")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Expected Console Log Patterns
|
|
||||||
|
|
||||||
### Good (Animations Working)
|
|
||||||
|
|
||||||
```
|
|
||||||
[Local Player ID: 0] SpawnDynamicProxyActor: Spawned proxy actor 0xABCD1234 for remote player 1
|
|
||||||
[Local Player ID: 0] Assigned dynamically spawned proxy 0xABCD1234 to remote player 1
|
|
||||||
[Local Player ID: 0] First smoothed runtime proxy movement for remote player 1: actor=0xABCD1234, alpha=0.15
|
|
||||||
[Local Player ID: 0] ApplyRuntimeProxyTransform: Proxy 0xABCD1234 has AIProcess: true
|
|
||||||
[Local Player ID: 0] Runtime proxy animation sync initial for remote player 1: moving=true, sprinting=false, graphSpeed=55.0
|
|
||||||
```
|
|
||||||
|
|
||||||
### Bad (Animations NOT Working)
|
|
||||||
|
|
||||||
```
|
|
||||||
[Local Player ID: 0] animation graph manager unavailable
|
|
||||||
[Local Player ID: 0] Failed to load animation variables for remote player 1
|
|
||||||
[Local Player ID: 0] Proxy 0xABCD1234 has NO AIProcess
|
|
||||||
```
|
|
||||||
|
|
||||||
## Performance Expectations
|
|
||||||
|
|
||||||
| Metric | Expected | Alert If |
|
|
||||||
|--------|----------|----------|
|
|
||||||
| Proxy spawn time | < 500ms | > 1 second |
|
|
||||||
| Per-proxy update | < 1ms | > 5ms |
|
|
||||||
| Frame rate | 60+ fps | < 30 fps with 1 proxy |
|
|
||||||
| Animation smooth | No stutter | Visible jitter |
|
|
||||||
|
|
||||||
## Results Template
|
|
||||||
|
|
||||||
Copy this after testing:
|
|
||||||
|
|
||||||
```
|
|
||||||
## Phase 6 Test Results - [DATE]
|
|
||||||
|
|
||||||
### Tests
|
|
||||||
- Test 1 (Spawning): PASS / FAIL
|
|
||||||
- Test 2 (Movement): PASS / FAIL
|
|
||||||
- Test 3 (Animations): PASS / FAIL / PARTIAL
|
|
||||||
- Test 4 (Sneak): PASS / FAIL / PARTIAL
|
|
||||||
- Test 5 (Multiple): PASS / FAIL
|
|
||||||
|
|
||||||
### Issues Found
|
|
||||||
1. <Issue>
|
|
||||||
- Severity: Critical / Major / Minor
|
|
||||||
- Fix: <action needed>
|
|
||||||
|
|
||||||
### Next Action
|
|
||||||
- [ ] PASS ALL → Proceed to Phase 7
|
|
||||||
- [ ] PARTIAL → Iterate Phase 6.3 (tune)
|
|
||||||
- [ ] FAIL → Debug and retest
|
|
||||||
```
|
|
||||||
|
|
||||||
## Common Fixes
|
|
||||||
|
|
||||||
### Animations not playing
|
|
||||||
- **Try:** Increase velocity scale (multiply by 1.2x)
|
|
||||||
- **Try:** Check animation variable indices in F4AnimationDescriptor
|
|
||||||
- **Try:** Enable debug logging for Speed variable
|
|
||||||
|
|
||||||
### Proxy doesn't move smoothly
|
|
||||||
- **Try:** Reduce lerp alpha (currently 0.15)
|
|
||||||
- **Try:** Increase update frequency (more network packets)
|
|
||||||
- **Try:** Check for network lag
|
|
||||||
|
|
||||||
### Multiple proxies crash
|
|
||||||
- **Try:** Reduce from 4 to 2 proxies
|
|
||||||
- **Try:** Check for memory leaks in action queue
|
|
||||||
- **Try:** Verify proxy pool cleanup on disconnect
|
|
||||||
|
|
||||||
## Success = All PASS
|
|
||||||
|
|
||||||
When you see:
|
|
||||||
1. ✅ Proxy spawns
|
|
||||||
2. ✅ Proxy moves smoothly
|
|
||||||
3. ✅ Proxy animates (idle/walk/run/sprint)
|
|
||||||
4. ✅ Multiple proxies work
|
|
||||||
5. ✅ No crashes
|
|
||||||
|
|
||||||
**Then Phase 6 is COMPLETE and Phase 7 ready!**
|
|
||||||
|
|
||||||
@@ -1,426 +0,0 @@
|
|||||||
# Phase 6: Testing & Iteration - Animation Testing Guide
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
Phase 6 validates that the TiltedEvolution-aligned animation synchronization works end-to-end in Fallout 4. This document provides testing procedures, expected results, and debugging guidance.
|
|
||||||
|
|
||||||
**Prerequisites:**
|
|
||||||
- F4T plugin built and installed
|
|
||||||
- Fallout 4 running with F4SE
|
|
||||||
- Network relay server running (`python server.py`)
|
|
||||||
- Fake client or second F4T instance ready
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Testing Phase 6.1: In-Game Animation Testing
|
|
||||||
|
|
||||||
### Setup
|
|
||||||
|
|
||||||
1. **Start the relay server:**
|
|
||||||
```bash
|
|
||||||
cd server/
|
|
||||||
python server.py
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Launch F4T with logging enabled:**
|
|
||||||
- Start Fallout 4 with F4SE
|
|
||||||
- Plugin loads (check console for logs)
|
|
||||||
- Remote player should spawn near you
|
|
||||||
|
|
||||||
3. **If testing with second instance:**
|
|
||||||
```bash
|
|
||||||
# Terminal 1: Relay server
|
|
||||||
python server/server.py
|
|
||||||
|
|
||||||
# Terminal 2: Fake client
|
|
||||||
python server/fake_client.py
|
|
||||||
|
|
||||||
# In-game: Two separate F4T instances, each with F4SE plugin loaded
|
|
||||||
```
|
|
||||||
|
|
||||||
### Test 1: Dynamic Proxy Spawning
|
|
||||||
|
|
||||||
**What to test:**
|
|
||||||
- [ ] Remote player proxy spawns successfully
|
|
||||||
- [ ] Proxy appears near your position (not at world origin)
|
|
||||||
- [ ] Multiple proxies can spawn (up to 4 concurrent)
|
|
||||||
|
|
||||||
**Expected results:**
|
|
||||||
- Proxy NPC visible in-game
|
|
||||||
- Name/ID visible in console logs
|
|
||||||
- No crashes on spawn
|
|
||||||
|
|
||||||
**Debug logs to check:**
|
|
||||||
```
|
|
||||||
[Local Player ID: 0] SpawnDynamicProxyActor: Spawned proxy actor <FORMID> for remote player <ID> with base <BASE_FORMID>.
|
|
||||||
[Local Player ID: 0] Assigned dynamically spawned proxy <FORMID> to remote player <ID>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Test 2: Smooth Movement Synchronization
|
|
||||||
|
|
||||||
**What to test:**
|
|
||||||
- [ ] Remote proxy moves smoothly to player position
|
|
||||||
- [ ] Movement is not jerky/teleporting
|
|
||||||
- [ ] Heading (rotation) updates correctly
|
|
||||||
|
|
||||||
**Expected results:**
|
|
||||||
- Proxy walks/runs toward network position
|
|
||||||
- Smooth interpolation (lerp alpha 0.15)
|
|
||||||
- No position snapping
|
|
||||||
|
|
||||||
**Debug logs to check:**
|
|
||||||
```
|
|
||||||
[Local Player ID: 0] First smoothed runtime proxy movement for remote player <ID>: actor=<FORMID>, alpha=0.15.
|
|
||||||
[Local Player ID: 0] Moved runtime proxy actor <FORMID> from remote player <ID> state for the first time
|
|
||||||
```
|
|
||||||
|
|
||||||
### Test 3: Velocity Injection & Animation Triggering
|
|
||||||
|
|
||||||
**CRITICAL TEST:** Does velocity injection cause animations to play?
|
|
||||||
|
|
||||||
**What to test:**
|
|
||||||
- [ ] Proxy plays idle animation when stationary
|
|
||||||
- [ ] Proxy plays walk animation when moving slowly
|
|
||||||
- [ ] Proxy plays run animation when moving fast
|
|
||||||
- [ ] Proxy plays sprint animation when sprinting
|
|
||||||
|
|
||||||
**Expected results:**
|
|
||||||
```
|
|
||||||
Movement State | Expected Animation
|
|
||||||
---------------------|---------------------
|
|
||||||
Stationary | Idle
|
|
||||||
Speed 0-50 units/s | Walk
|
|
||||||
Speed 50-100 units/s | Run
|
|
||||||
Speed 100+ units/s | Sprint
|
|
||||||
```
|
|
||||||
|
|
||||||
**Debug logs to check:**
|
|
||||||
```
|
|
||||||
[Local Player ID: 0] ApplyRuntimeProxyTransform: Proxy <FORMID> has AIProcess: true
|
|
||||||
[Local Player ID: 0] Runtime proxy animation sync <changed/initial> for remote player <ID>:
|
|
||||||
moving=<bool>, sprinting=<bool>, graphSpeed=<float>
|
|
||||||
```
|
|
||||||
|
|
||||||
**If animations DON'T play:**
|
|
||||||
1. Check velocity is being set (see console logs)
|
|
||||||
2. Verify character controller is accessible
|
|
||||||
3. Check animation graph manager is available
|
|
||||||
4. Look for "Failed to load animation variables" warnings
|
|
||||||
|
|
||||||
### Test 4: Actor State Flags & Sneak Animation
|
|
||||||
|
|
||||||
**What to test:**
|
|
||||||
- [ ] Remote player sneaking → proxy crouches
|
|
||||||
- [ ] Remote player normal → proxy stands
|
|
||||||
- [ ] State transitions are smooth
|
|
||||||
|
|
||||||
**Expected results:**
|
|
||||||
- Proxy visibly crouches when remote player crouches
|
|
||||||
- Proxy crouch pose visible (animation, not just flag)
|
|
||||||
- No lag in state transition
|
|
||||||
|
|
||||||
**Debug logs to check:**
|
|
||||||
```
|
|
||||||
[Local Player ID: 0] Moved runtime proxy actor <FORMID> ... flags1=<FLAGS1>, flags2=<FLAGS2>
|
|
||||||
```
|
|
||||||
|
|
||||||
**If sneak animation doesn't work:**
|
|
||||||
- Sneak might require more than just flags
|
|
||||||
- May need dedicated action event (Phase 2.3 capture)
|
|
||||||
- Document in iteration log
|
|
||||||
|
|
||||||
### Test 5: Multiple Proxy Actors
|
|
||||||
|
|
||||||
**What to test:**
|
|
||||||
- [ ] 2 simultaneous proxies move smoothly
|
|
||||||
- [ ] 3 simultaneous proxies move smoothly
|
|
||||||
- [ ] 4 simultaneous proxies (max) move smoothly
|
|
||||||
- [ ] No crashes with multiple proxies
|
|
||||||
|
|
||||||
**Expected results:**
|
|
||||||
- Each proxy moves independently to its network position
|
|
||||||
- All proxies animate correctly
|
|
||||||
- Performance acceptable (no frame rate drops)
|
|
||||||
|
|
||||||
**Debug logs to check:**
|
|
||||||
```
|
|
||||||
[Local Player ID: 0] Runtime proxy slot created for remote player <ID> with holding index <INDEX>.
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Testing Phase 6.2: Performance & Stability Profiling
|
|
||||||
|
|
||||||
### Benchmark: Per-Proxy Update Cost
|
|
||||||
|
|
||||||
**Metric:** Time to update one proxy per frame
|
|
||||||
|
|
||||||
**Measure:**
|
|
||||||
1. Enable logging: `UpdateProxyAnimationStateDebug` calls
|
|
||||||
2. Run single proxy moving
|
|
||||||
3. Monitor console for timing data
|
|
||||||
4. Expected: < 1ms per proxy update
|
|
||||||
|
|
||||||
**Command (in console):**
|
|
||||||
```
|
|
||||||
help F4T # Check if debug timers are available
|
|
||||||
```
|
|
||||||
|
|
||||||
### Benchmark: Descriptor vs String-Based Writes
|
|
||||||
|
|
||||||
**Phase 5.3 improvement verification:**
|
|
||||||
|
|
||||||
**Old way (string-based):**
|
|
||||||
- Multiple `TrySetGraphFloat()` calls
|
|
||||||
- Each call: string allocation + graph lookup
|
|
||||||
|
|
||||||
**New way (descriptor-based):**
|
|
||||||
- Single `LoadAnimationVariablesToCache()` call
|
|
||||||
- Array indexing + one graph manager lock
|
|
||||||
|
|
||||||
**Expected result:** Descriptor approach should be ~3-5x faster for animation variable writes.
|
|
||||||
|
|
||||||
### Stability: Crash Testing
|
|
||||||
|
|
||||||
**What to test:**
|
|
||||||
- [ ] No crash with 1 proxy moving
|
|
||||||
- [ ] No crash with 4 proxies moving
|
|
||||||
- [ ] No crash on proxy disconnect/reconnect
|
|
||||||
- [ ] No crash on actor reload
|
|
||||||
|
|
||||||
**Procedure:**
|
|
||||||
1. Start with 1 proxy moving smoothly
|
|
||||||
2. Add proxies one by one
|
|
||||||
3. Monitor for crashes
|
|
||||||
4. Try disconnecting/reconnecting
|
|
||||||
5. Run for 5-10 minutes per configuration
|
|
||||||
|
|
||||||
**If crashes occur:**
|
|
||||||
- Note exact conditions
|
|
||||||
- Check console for error logs
|
|
||||||
- Review actor handle validity
|
|
||||||
- Check for memory leaks
|
|
||||||
|
|
||||||
### Stability: Lag Simulation
|
|
||||||
|
|
||||||
**What to test:**
|
|
||||||
- [ ] Proxy handles high-frequency position updates (no lag)
|
|
||||||
- [ ] Proxy handles low-frequency updates (lerp smoothing works)
|
|
||||||
- [ ] Proxy handles packet loss gracefully
|
|
||||||
|
|
||||||
**Procedure:**
|
|
||||||
1. Normal case: Remote sends ~10 Hz (working)
|
|
||||||
2. High frequency: Send 30 Hz updates
|
|
||||||
3. Low frequency: Send 2 Hz updates
|
|
||||||
4. Simulate packet loss: Drop 50% of packets
|
|
||||||
5. Verify proxy still animates correctly
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Testing Phase 6.3: Iterate & Tune
|
|
||||||
|
|
||||||
### Variable Index Verification
|
|
||||||
|
|
||||||
**Goal:** Confirm animation graph variable indices are correct for FO4
|
|
||||||
|
|
||||||
**Current assumptions (from Phase 3):**
|
|
||||||
```cpp
|
|
||||||
snapshot.floats[0] = Speed;
|
|
||||||
snapshot.bools[0] = isSprinting;
|
|
||||||
snapshot.bools[1] = isSneaking;
|
|
||||||
```
|
|
||||||
|
|
||||||
**How to verify:**
|
|
||||||
1. If animations work → Indices likely correct
|
|
||||||
2. If animations don't play:
|
|
||||||
- Enable `F4TLocalAnimationGraphDebug.cpp` debug logging
|
|
||||||
- Dump actual variable indices from local player
|
|
||||||
- Compare with descriptor table
|
|
||||||
- Update `docs/f4-animation-descriptor.md`
|
|
||||||
|
|
||||||
**Debug logging (add if needed):**
|
|
||||||
```cpp
|
|
||||||
// In ApplyProxyAnimationFromRemoteStateDescriptorBased
|
|
||||||
LogInfo(std::format("Variable index 'Speed': {}, value: {}",
|
|
||||||
descriptor.GetFloatVariableIndex("Speed"), desired.graphSpeed));
|
|
||||||
```
|
|
||||||
|
|
||||||
### Velocity Scaling Tuning
|
|
||||||
|
|
||||||
**Current scaling (from Phase 4):**
|
|
||||||
```cpp
|
|
||||||
float speed = distanceDrift / 0.016F; // Convert to units/sec
|
|
||||||
float clampedSpeed = min(speed, 400.0F); // Cap at 400 units/sec
|
|
||||||
```
|
|
||||||
|
|
||||||
**Adjustment procedure:**
|
|
||||||
1. Watch proxy animations while moving
|
|
||||||
2. If walk animation plays but shouldn't → velocity too high
|
|
||||||
3. If sprint animation doesn't trigger → velocity too low
|
|
||||||
4. Adjust multipliers:
|
|
||||||
```cpp
|
|
||||||
// Try different scaling
|
|
||||||
float speed = (distanceDrift / 0.016F) * 0.8F; // Reduce by 20%
|
|
||||||
```
|
|
||||||
|
|
||||||
**Testing different speeds:**
|
|
||||||
```
|
|
||||||
Network Speed | Graph Speed | Expected Animation | Adjust If...
|
|
||||||
2 units/frame | 125 units/s | Sprint | Too low → reduce divisor
|
|
||||||
1 unit/frame | 62 units/s | Run | Too high → increase divisor
|
|
||||||
0.5 units | 31 units/s | Walk | Not triggering → reduce
|
|
||||||
```
|
|
||||||
|
|
||||||
### Animation Variable Tuning
|
|
||||||
|
|
||||||
**If animations partially work:**
|
|
||||||
|
|
||||||
1. **Speed blending isn't smooth:**
|
|
||||||
- Check `speedDamped` variable (if implemented)
|
|
||||||
- May need to scale Speed over time (lerp, not snap)
|
|
||||||
|
|
||||||
2. **Weapon drawn/sneak animation missing:**
|
|
||||||
- Confirm variable indices in descriptor
|
|
||||||
- May need dedicated action events (Phase 2.3)
|
|
||||||
- Document as limitation
|
|
||||||
|
|
||||||
3. **Transitions are jerky:**
|
|
||||||
- Velocity might be changing too rapidly
|
|
||||||
- Add smoothing: lerp velocity over 0.1-0.2 seconds
|
|
||||||
- Or increase lerp alpha on position (currently 0.15)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Test Results Template
|
|
||||||
|
|
||||||
Create a new dev-log entry (example):
|
|
||||||
|
|
||||||
```
|
|
||||||
## Test Session: <DATE> - <TESTER>
|
|
||||||
|
|
||||||
### Session Setup
|
|
||||||
- Number of proxies: 1/2/4
|
|
||||||
- Duration: 5 minutes
|
|
||||||
- Network condition: Normal/Lag/Packet Loss
|
|
||||||
|
|
||||||
### Test 1: Dynamic Spawning
|
|
||||||
- [x] Proxy spawned successfully
|
|
||||||
- [ ] Proxy appeared at correct location
|
|
||||||
- Result: PASS/FAIL/PARTIAL
|
|
||||||
|
|
||||||
### Test 2: Smooth Movement
|
|
||||||
- [x] Movement is smooth
|
|
||||||
- [ ] No visible jitter
|
|
||||||
- Result: PASS/FAIL/PARTIAL
|
|
||||||
|
|
||||||
### Test 3: Animation Triggering (CRITICAL)
|
|
||||||
- [ ] Idle animation plays
|
|
||||||
- [ ] Walk animation plays
|
|
||||||
- [ ] Run animation plays
|
|
||||||
- [ ] Sprint animation plays
|
|
||||||
- Result: PASS/FAIL/PARTIAL
|
|
||||||
- Notes: <describe any issues>
|
|
||||||
|
|
||||||
### Test 4: Sneak Animation
|
|
||||||
- [ ] Crouch plays when sneaking
|
|
||||||
- Result: PASS/FAIL/PARTIAL
|
|
||||||
|
|
||||||
### Test 5: Multiple Proxies (4x)
|
|
||||||
- [ ] All 4 proxies move smoothly
|
|
||||||
- [ ] No crashes
|
|
||||||
- [ ] Frame rate acceptable
|
|
||||||
- Result: PASS/FAIL/PARTIAL
|
|
||||||
|
|
||||||
### Performance Metrics
|
|
||||||
- Per-proxy update time: <N> ms
|
|
||||||
- Descriptor bulk write time: <N> ms
|
|
||||||
- Frame rate: <N> fps
|
|
||||||
|
|
||||||
### Issues Found
|
|
||||||
1. <Issue description>
|
|
||||||
- Severity: Critical/Major/Minor
|
|
||||||
- Workaround: <if any>
|
|
||||||
- Fix needed in: <file/phase>
|
|
||||||
|
|
||||||
### Conclusion
|
|
||||||
Overall result: READY FOR PHASE 7 / NEEDS ITERATION / NEEDS FIXES
|
|
||||||
|
|
||||||
### Next Steps
|
|
||||||
- <Action items>
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Debugging Checklist
|
|
||||||
|
|
||||||
If animations aren't working:
|
|
||||||
|
|
||||||
1. **Verify velocity is being set:**
|
|
||||||
```
|
|
||||||
Search logs for: "ApplyRuntimeProxyTransform"
|
|
||||||
Should show: Character controller velocity being set
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Verify actor state flags are applied:**
|
|
||||||
```
|
|
||||||
Search logs for: "Moved runtime proxy actor ... flags1="
|
|
||||||
Should show non-zero flags if remote player in combat/sneak
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **Verify animation variables are being written:**
|
|
||||||
```
|
|
||||||
Search logs for: "Runtime proxy animation sync"
|
|
||||||
Should show Speed values changing (not always 0)
|
|
||||||
```
|
|
||||||
|
|
||||||
4. **Check if animation graph manager is available:**
|
|
||||||
```
|
|
||||||
Search logs for: "animation graph manager unavailable"
|
|
||||||
If present: Graph manager not accessible
|
|
||||||
```
|
|
||||||
|
|
||||||
5. **Enable full animation debugging:**
|
|
||||||
- Set logging interval to 100ms (from 2s)
|
|
||||||
- Enables detailed animation state transitions
|
|
||||||
- Generates large log files
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Common Issues & Fixes
|
|
||||||
|
|
||||||
| Issue | Possible Cause | Fix |
|
|
||||||
|-------|---|---|
|
|
||||||
| Proxy doesn't spawn | PlaceAtMe failed | Check proxy base actor exists |
|
|
||||||
| Proxy stands still | Velocity not set | Verify character controller access |
|
|
||||||
| Walk/run doesn't play | Speed variable wrong index | Check f4-animation-descriptor.md indices |
|
|
||||||
| Sneak doesn't work | Needs action replay | Phase 2.3 action capture needed |
|
|
||||||
| Multiple proxies crash | Handle leak | Check proxy pool cleanup |
|
|
||||||
| Frame rate drops | Too many descriptor writes | Already optimized; check log frequency |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Success Criteria (Phase 6 Complete)
|
|
||||||
|
|
||||||
- ✅ Proxy spawns and moves smoothly (dynamic)
|
|
||||||
- ✅ Velocity injection triggers animation system
|
|
||||||
- ✅ At least idle/walk/run animations visible
|
|
||||||
- ✅ Multiple proxies (4x) stable and animating
|
|
||||||
- ✅ No crashes in 10-minute test session
|
|
||||||
- ✅ Performance acceptable (< 1ms per proxy update)
|
|
||||||
- ✅ Actor state flags affect proxy (sneak at minimum)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Ready for Phase 7?
|
|
||||||
|
|
||||||
If all tests PASS:
|
|
||||||
→ Phase 7: Cleanup & Documentation
|
|
||||||
|
|
||||||
If tests FAIL/PARTIAL:
|
|
||||||
→ Iterate Phase 6.3 (tune velocity/variables)
|
|
||||||
→ Return to Phase 5 if infrastructure issues found
|
|
||||||
|
|
||||||
If tests CRASH:
|
|
||||||
→ Debug and fix issues
|
|
||||||
→ Return to Phase 4-5 as needed
|
|
||||||
|
|
||||||
@@ -96,7 +96,7 @@ This document describes the Phase 2 protocol extensions to support actor state f
|
|||||||
## World State Packet
|
## World State Packet
|
||||||
|
|
||||||
Host-authoritative time and weather sync uses a separate `worldState` packet.
|
Host-authoritative time and weather sync uses a separate `worldState` packet.
|
||||||
Full field definitions live in [`protocol/world-state.md`](../protocol/world-state.md).
|
Full field definitions live in [`protocol/world-state.md`](protocol/world-state.md).
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
|||||||
+1
-1
@@ -63,4 +63,4 @@ The protocol should be testable outside Fallout 4 before it is used inside the F
|
|||||||
`transform` packets carry position, movement state, optional `equippedItems`
|
`transform` packets carry position, movement state, optional `equippedItems`
|
||||||
apparel snapshots, and optional `appearance` snapshots for best-effort proxy
|
apparel snapshots, and optional `appearance` snapshots for best-effort proxy
|
||||||
body/face visuals. The canonical field list and compatibility rules live in
|
body/face visuals. The canonical field list and compatibility rules live in
|
||||||
[`protocol/packets.md`](../protocol/packets.md).
|
[`protocol/packets.md`](protocol/packets.md).
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# Vanilla Interface reference
|
||||||
|
|
||||||
|
Decompiled Fallout 4 interface assets kept for **reading and research only**.
|
||||||
|
|
||||||
|
Commonwealth Online injects the main-menu **Multiplayer** row at runtime from C++. Do **not** re-import or deploy patched SWFs from this folder.
|
||||||
|
|
||||||
|
See [`../../MainMenu_Injection.md`](../../MainMenu_Injection.md) for the supported approach.
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
SteamClient017
|
|
||||||
SteamClient020
|
|
||||||
SteamGameServer014
|
|
||||||
SteamGameServerStats001
|
|
||||||
SteamUser021
|
|
||||||
SteamFriends017
|
|
||||||
SteamUtils010
|
|
||||||
SteamMatchMaking009
|
|
||||||
SteamMatchMakingServers002
|
|
||||||
STEAMUSERSTATS_INTERFACE_VERSION012
|
|
||||||
STEAMAPPS_INTERFACE_VERSION008
|
|
||||||
SteamNetworking006
|
|
||||||
STEAMREMOTESTORAGE_INTERFACE_VERSION016
|
|
||||||
STEAMSCREENSHOTS_INTERFACE_VERSION003
|
|
||||||
STEAMHTTP_INTERFACE_VERSION003
|
|
||||||
STEAMUGC_INTERFACE_VERSION016
|
|
||||||
STEAMAPPLIST_INTERFACE_VERSION001
|
|
||||||
STEAMMUSIC_INTERFACE_VERSION001
|
|
||||||
STEAMMUSICREMOTE_INTERFACE_VERSION001
|
|
||||||
STEAMHTMLSURFACE_INTERFACE_VERSION_005
|
|
||||||
STEAMINVENTORY_INTERFACE_V003
|
|
||||||
SteamController008
|
|
||||||
STEAMVIDEO_INTERFACE_V002
|
|
||||||
+11
-8
@@ -1,14 +1,17 @@
|
|||||||
# Tools
|
# Tools
|
||||||
|
|
||||||
This folder is for helper tools and development scripts.
|
Helper utilities and development scripts for Commonwealth Online.
|
||||||
|
|
||||||
Possible future tools:
|
## Scripts
|
||||||
|
|
||||||
- Build scripts
|
| Script | Purpose |
|
||||||
- Packaging scripts
|
|---|---|
|
||||||
- Log parsers
|
| [`scripts/setup-ultralight-link.ps1`](scripts/setup-ultralight-link.ps1) | Junction-link Ultralight SDK for PrismaUI builds (called by `build-prismaui.bat`) |
|
||||||
- Packet replay tools
|
| [`restore-vanilla-mainmenu.ps1`](restore-vanilla-mainmenu.ps1) | Restore vanilla `MainMenu.swf` from game archives |
|
||||||
- Test client scripts
|
| [`extract_ba2.py`](extract_ba2.py) | BA2 archive extraction helper |
|
||||||
- Development environment helpers
|
|
||||||
|
## Reference assets
|
||||||
|
|
||||||
|
Decompiled vanilla interface reference files live under [`docs/reference/Interface/`](../docs/reference/Interface/). Do not deploy patched SWFs from that folder.
|
||||||
|
|
||||||
Do not place Fallout 4 game files or proprietary tools in this folder.
|
Do not place Fallout 4 game files or proprietary tools in this folder.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ param(
|
|||||||
$ErrorActionPreference = "Stop"
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||||
$repoRoot = Split-Path -Parent $scriptDir
|
$repoRoot = Split-Path -Parent (Split-Path -Parent $scriptDir)
|
||||||
|
|
||||||
if ([string]::IsNullOrWhiteSpace($SdkPath)) {
|
if ([string]::IsNullOrWhiteSpace($SdkPath)) {
|
||||||
$SdkPath = Join-Path $repoRoot "ThirdParty\Ultralight-SDK"
|
$SdkPath = Join-Path $repoRoot "ThirdParty\Ultralight-SDK"
|
||||||
Reference in New Issue
Block a user