Introduce a C++ runtime injection that adds a Multiplayer row to the Fallout 4 main menu instead of patching MainMenu.swf. Adds F4TMainMenuInject.h/cpp implementing injection, ITEM_PRESS handling, accept/button hooks, resync tasks and logging; integrates installation by calling MainMenuInject::Install() from PrismaUI init. Update PrismaUI to use the new helper and remove a duplicate IsMainMenuOnMainPanel implementation. Documentation updated to deprecate FFDec SWF patching (new docs/MainMenu_Injection.md, note in FFDEC guide, and setup.md) and restore-vanilla-mainmenu.ps1 messaging updated to instruct using the runtime injection.
10 KiB
FFDec guide — add Multiplayer button to MainMenu.swf
Deprecated: Use C++ runtime injection instead. See
MainMenu_Injection.md. Do not deploy a patchedMainMenu.swffor Commonwealth Online.
Step-by-step instructions for adding the Multiplayer main-menu item using JPEXS FFDec, 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:
.\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.dllinData/F4SE/Plugins/ - Loose Interface overrides enabled in
%USERPROFILE%/Documents/My Games/Fallout4/Fallout4Custom.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
- Open
Interface/MainMenu.swfin FFDec. - Left tree: scripts → MainMenu.
- You should see a traits / members list (constants, functions).
- Double-click a method name (e.g.
InitList) to open only that method in the editor. - 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):
private const INSTALLED_CONTENT_INDEX:Number = 14;
private const MAIN_PANEL_BACKGROUND:uint = 0;
Insert between those lines:
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):
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)):
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 inInterface/Translate_en.txt.!this.PauseModelimits 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:):
break;
case this.HELP_INDEX:
this.StartState(this.HELP_STATE);
this.BGSCodeObj.PlayOKSound();
break;
Insert immediately before case this.HELP_INDEX::
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:
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:
public function set currentState(param1:String) : *
{
this.strCurrentState = param1;
this.UpdateButtons(param1);
this.UpdateStateFocus(param1);
}
Change to:
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:
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:
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
-
File → Save in FFDec (overwrite
Interface/MainMenu.swf). -
Check file size: should be roughly 62,000–63,500 bytes. If 64,000+, restore vanilla and redo using method-only edits.
-
Copy to game:
Fallout 4/Data/Interface/MainMenu.swf -
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=1inFallout4Custom.ini. - Confirm loose
Data/Interface/MainMenu.swfexists and is not overridden by another mod (check MO2 load order). - Confirm you added the
InitListblock withif(!this.PauseMode).
Multiplayer row visible but click does nothing
- Launch via F4SE, not vanilla exe.
- Confirm
CommonwealthOnline.dllis loading (checkDocuments/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 |
Untouched vanilla backup |
scripts/MainMenu.as |
Full decompiled reference — do not import wholesale |
PATCH_MainMenu_Multiplayer.md |
Short overview |
../../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()