Add FFDec docs/tools and server-browser fixes

Add comprehensive FFDec walkthrough and short patch docs for safely adding a Multiplayer item to MainMenu.swf, plus helper scripts to extract the vanilla SWF and restore it (tools/extract_ba2.py, tools/restore-vanilla-mainmenu.ps1). Update exported MainMenu.as with a warning comment and remove an outdated exported PATCH markdown. Patch binary Interface/MainMenu.swf (updated).

Hardening fixes in the plugin: include GFx headers, avoid leaving the PrismaUI overlay focused after menu changes, only call OnBrowserHidden when the browser was actually visible, and add IsMainMenuOnMainPanel() so WatchMainMenuState ignores MainMenu when not on the MAIN_STATE. Ensure events are only dispatched when the browser is valid & visible (CanDispatchToBrowser) and make Scaleform UI hide the browser immediately during transitions. These changes prevent the overlay from staying focused during Settings transitions and stop JS events from being sent to a hidden/invalid view.
This commit is contained in:
2026-06-08 22:26:00 +12:00
parent 50d48f1f99
commit 9c84c9d4bc
10 changed files with 636 additions and 165 deletions
Binary file not shown.
@@ -1,144 +0,0 @@
# MainMenu.swf — Multiplayer button patch guide
Reference export: [`scripts/MainMenu.as`](scripts/MainMenu.as)
Edit the script **inside FFDec** on the SWF (or paste changes back into FFDec after editing the export). Saving only the `.as` file does not update `MainMenu.swf`.
## Why texts/sprites only showed CONTINUE
The main menu is a **dynamic list** (`MainPanel_mc.List_mc`). Items like `$NEW`, `$LOAD`, `$SETTINGS` are pushed in **`InitList()`**, not stored as static text tags.
## Three edits in `MainMenu` class
### 1. Add index constant (~line 214)
After `INSTALLED_CONTENT_INDEX`:
```actionscript
private const MULTIPLAYER_INDEX:Number = 15;
```
Index `15` is unused in vanilla (highest defined is `INSTALLED_CONTENT_INDEX = 14`).
### 2. Add list entry in `InitList()` (~line 589)
Insert **after** the `$LOAD` push and **before** the PS5 transfer / DLC block — only on the title screen (`!PauseMode`):
```actionscript
this.MainPanel_mc.List_mc.entryList.push({
"text":"$LOAD",
"disabled":!this.HasRecentSave || !param6,
"index":this.LOAD_INDEX
});
// --- ADD BELOW ---
if(!this.PauseMode)
{
this.MainPanel_mc.List_mc.entryList.push({
"text":"Multiplayer",
"index":this.MULTIPLAYER_INDEX
});
}
// --- ADD ABOVE ---
if(param8 == true)
```
Using literal `"MULTIPLAYER"` avoids needing a `.strings` entry for the PoC. Later switch to `"$COSYNC_MULTIPLAYER"` + `Interface/Translate_en.txt` if you want localization.
**Label text:** For the PoC use a **literal** label (no `$` prefix):
```actionscript
"text":"Multiplayer",
```
Strings starting with `$` are looked up in `Interface/Translate_en.txt` (from `Fallout4 - Interface.ba2`). A lone `Interface/Translations/MainMenu_en.txt` is **not** how vanilla resolves main-menu keys, so `$MULTIPLAYER` shows as-is until you merge the key into `Translate_en.txt` (extract from BA2, append `$MULTIPLAYER Multiplayer`, redeploy loose file).
Regenerate optional translation stub: `python tools/write_mainmenu_translation.py` (for future i18n / Translate_en merge only).
### Fallout 4 must load loose Interface files
Loose `Data/Interface/*.swf` overrides are **ignored** unless archive invalidation is on. Add to `%USERPROFILE%/Documents/My Games/Fallout4/Fallout4Custom.ini`:
```ini
[Archive]
bInvalidateOlderFiles=1
```
(MO2 enables this automatically; manual `Data/` copies need it.)
Without invalidation, the game keeps using `Fallout4 - Interface.ba2` — your patched SWF never loads even though the file is on disk.
Suggested placement: between **Load** and **Add-ons** so it reads naturally on the main menu.
### 3. Handle selection in `onMainListItemPress()` switch (~line 864)
Add **before** `case this.HELP_INDEX:` (or anywhere in the switch):
```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;
```
Requires the C++ plugin to register `commonwealthOnline.openManager()` and `commonwealthOnline.closeManager()` via F4SE Scaleform.
### 4. Auto-close when leaving Multiplayer (~line 716 and ~onMainListItemPress)
Add a helper and call it when the main menu navigates to any other screen (Settings, Load, Continue, etc.):
```actionscript
private function CloseMultiplayerBrowser() : *
{
if(root.f4se != null && root.f4se.plugins != null && root.f4se.plugins.commonwealthOnline != null)
{
root.f4se.plugins.commonwealthOnline.closeManager();
}
}
```
In the `currentState` setter, after assigning `strCurrentState`:
```actionscript
if(param1 != this.MAIN_STATE)
{
this.CloseMultiplayerBrowser();
}
```
At the start of `onMainListItemPress()` (after the cancel/reset loop), before the `switch`:
```actionscript
if(this.MainPanel_mc.List_mc.selectedEntry.index != this.MULTIPLAYER_INDEX)
{
this.CloseMultiplayerBrowser();
}
```
This closes the HTML server browser when sub-menus open (Settings, Add-ons, etc.) and when any other main-menu item is selected.
## FFDec workflow
1. Open `Interface/MainMenu.swf` in FFDec.
2. **scripts → MainMenu** → right-click → **Replace / Edit** (or open script, paste from [`scripts/MainMenu.as`](scripts/MainMenu.as)).
3. **File → Save** (overwrites SWF).
4. Deploy to `Fallout 4/Data/Interface/MainMenu.swf` or your mod folder.
5. Launch via F4SE and test.
**Re-import from edited export:** In FFDec, select the **MainMenu** script under **scripts**, use **Edit ActionScript** and paste the full contents of `Interface/exported/scripts/MainMenu.as`, then save the SWF. FFDec may also offer **Import script** / **Replace script** from file depending on version.
Keep `MainMenu.swf.vanilla.bak` unchanged.
## Related functions (no edit needed for PoC)
| Function | Role |
|----------|------|
| `InitList()` | Builds `entryList` with `$CONTINUE`, `$NEW`, `$LOAD`, … |
| `onMainListItemPress()` | `switch` on `selectedEntry.index` |
| `onListItemPress()` | Routes list press events to `onMainListItemPress()` |
| Index constants (~184–212) | `CONTINUE_INDEX` … `INSTALLED_CONTENT_INDEX` |
## Next step (code)
Implement `commonwealthOnline.openManager()` in `CommonwealthOnline.dll` and `CoSyncServerBrowser.swf` per [docs/custom-swf-ui.md](../../docs/custom-swf-ui.md).
+2 -4
View File
@@ -1,5 +1,7 @@
package
{
// REFERENCE ONLY — edit the vanilla MainMenu.swf surgically in FFDec.
// Do NOT paste this entire file back into FFDec; full re-import breaks settings sub-menus.
import Shared.AS3.BSButtonHint;
import Shared.AS3.BSButtonHintBar;
import Shared.AS3.BSButtonHintData;
@@ -2281,10 +2283,6 @@ package
"text":"$Settings_FrameCap",
"movieType":1,
"options":["$Settings_FrameCap_On","$Settings_FrameCap_60"]
},{
"text":"$Settings_FrameCap",
"movieType":1,
"options":["$Settings_FrameCap_On","$Settings_FrameCap_40","$Settings_FrameCap_60"]
},{
"text":"$Settings_VisualFidelity",
"movieType":1,