MSCO 2.10.0: mod support, ESC no longer quits from the main menu

- MscoMod API (ModApi.cs) and ModLoader: own mods from MSCO\Mods, hooks for menu/game/update/GUI,
  session and player events, Send/OnMessage between players (MsgType.ModMessage)
- Server mods: ModList before Welcome, consent prompt (INSTALL & JOIN), download with SHA-256 check
  into MSCO\Mods\Servers, active only on that server; game start waits for the download
- Mods page in the MSCO menu, console command "mods"
- Server browser: MODS pill, VANILLA/MODDED filters, mod list in the server profile
- PlayMaker ApplicationQuit is ignored in the main menu (ESC used to close the game)
- MODDING.md and examples/HelloMod

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-03 01:00:16 +02:00
co-authored by Claude Opus 5.5
parent 027296d27c
commit 4c1366b5d9
16 changed files with 815 additions and 15 deletions
+35
View File
@@ -42,6 +42,41 @@ namespace MSCO
{
MscoLog.Warning("[MSCO] Could not install hierarchy guard: " + e.Message);
}
PatchMenuQuit();
}
/// <summary>
/// ESC im Hauptmenü beendet beim Spiel sofort das Programm (PlayMaker-Aktion "ApplicationQuit" im Menü). Im
/// MSCO-Hauptmenü gibt es dafür den QUIT-Knopf - dort also nicht über die Spiel-Logik beenden. Im Spiel selbst
/// (Pausemenü) bleibt alles wie gehabt.
/// </summary>
static void PatchMenuQuit()
{
try
{
Type action = null;
foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies())
{
action = asm.GetType("HutongGames.PlayMaker.Actions.ApplicationQuit");
if (action != null) break;
}
MethodInfo onEnter = action != null ? action.GetMethod("OnEnter", BindingFlags.Instance | BindingFlags.Public) : null;
if (onEnter == null) return;
HarmonyInstance.Create("mbm.msco.menuquit").Patch(onEnter,
new HarmonyMethod(typeof(SafetyPatches).GetMethod("QuitPrefix", BindingFlags.Static | BindingFlags.NonPublic)), null);
}
catch (Exception e)
{
MscoLog.Warning("[MSCO] Could not block ESC-quit in the main menu: " + e.Message);
}
}
static bool QuitPrefix(FsmStateAction __instance)
{
if (!MenuHelper.InMenu) return true;
MscoLog.Info("[MSCO] The game tried to quit from the main menu (ESC) - use QUIT in the MSCO menu to exit");
try { __instance.Finish(); } catch { }
return false;
}
static bool SetParentPrefix(FsmStateAction __instance)