MSCO 2.9.0: standalone without MSCLoader (Unity Doorstop)

- Bootstrap: Doorstop entry point, loads Harmony/Newtonsoft from the MSCO folder, hooks
  PlayMakerArrayListProxy.Awake and starts MSCO in the main menu; skips the news screen
- No MSCLoader reference anymore; key bindings in PlayerPrefs, bind console command
- Harmony 1.2 and Newtonsoft.Json in lib/ (MIT notices); build copies to <game>\MSCO
- README/CHANGELOG updated

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-02 23:36:28 +02:00
co-authored by Claude Opus 5.5
parent ee162bd64f
commit 461d94c5fc
26 changed files with 307 additions and 156 deletions
+57 -66
View File
@@ -1,22 +1,24 @@
using MSCLoader;
using UnityEngine;
namespace MSCO
{
public class MSCO : Mod
/// <summary>
/// MSCO - My Summer Car Online. Läuft eigenständig (ohne MSCLoader): gestartet über Unity Doorstop, siehe Bootstrap.
/// </summary>
public static class MSCO
{
/// <summary>
/// Version der Mod (MSCLoader / MSC Launcher). Kleine Updates: nur die dritte Stelle erhöhen (2.8.1, 2.8.2 ...) -
/// sie bleiben mit 2.8-Servern und -Mitspielern kompatibel.
/// Version der Mod. Kleine Updates: nur die dritte Stelle erhöhen (2.9.1, 2.9.2 ...) - sie bleiben mit
/// Servern und Mitspielern derselben ersten zwei Stellen kompatibel.
/// </summary>
public const string ModVersion = "2.8.1";
public const string ModVersion = "2.9.0";
/// <summary>
/// Netzwerk-Version (die ersten beiden Stellen). Nur erhöhen, wenn sich das Protokoll inkompatibel ändert - dann
/// auch GameServer.ModVersion im Server und eine neue Mod-Version 2.9.0 o.ä.
/// auch GameServer.ModVersion im Server.
/// </summary>
public const string ProtocolVersion = "2.8";
/// <summary>Passt eine andere Version (Server, Host, Mitspieler) zu uns? Verglichen werden nur die ersten zwei Stellen.</summary>
/// <summary>Passt eine andere Version (Server, Host, Mitspieler) zu uns? Verglichen wird die Netzwerk-Version.</summary>
public static bool Compatible(string other)
{
return Protocol(other) == ProtocolVersion;
@@ -30,78 +32,67 @@ namespace MSCO
return parts.Length >= 2 ? parts[0] + "." + parts[1] : version.Trim();
}
public override string ID => "MSCO"; // Your (unique) mod ID
public override string Name => "MSCO"; // Your mod name
public override string Author => "MBM"; // Name of the Author (your name)
public override string Version => ModVersion; // Version
public override string Description => "Online mod by mbm.wtf"; // Short description of your mod
public override Game SupportedGames => Game.MySummerCar;
// Logo in der MSCLoader-Modliste (branding/logo-128.png, in die DLL eingebettet)
public override byte[] Icon { get { return Branding.LogoPng; } set { } }
/// <summary>Beifahrer ein-/aussteigen (Standard P), Konsole (F7), Chat (T) - änderbar mit "bind" in der Konsole.</summary>
internal static readonly KeyBinding PassengerKey = new KeyBinding("passenger", "Ride along / get out", KeyCode.P);
internal static readonly KeyBinding ConsoleKey = new KeyBinding("console", "Open the MSCO console", KeyCode.F7);
internal static readonly KeyBinding ChatKey = new KeyBinding("chat", "Open the chat", KeyCode.T);
internal static readonly KeyBinding[] Keys = { PassengerKey, ConsoleKey, ChatKey };
}
bool commandAdded;
/// <summary>Tastenbelegung, gespeichert in den PlayerPrefs (früher in den MSCLoader-Einstellungen).</summary>
public class KeyBinding
{
public readonly string Id, Description;
public readonly KeyCode Default;
bool loaded;
KeyCode key;
public override void ModSetup()
public KeyBinding(string id, string description, KeyCode defaultKey)
{
SetupFunction(Setup.OnMenuLoad, Mod_OnMenuLoad);
SetupFunction(Setup.OnLoad, Mod_OnLoad);
SetupFunction(Setup.PostLoad, Mod_PostLoad);
SetupFunction(Setup.OnSave, Mod_OnSave);
SetupFunction(Setup.OnGUI, Mod_OnGUI);
SetupFunction(Setup.Update, Mod_Update);
SetupFunction(Setup.FixedUpdate, Mod_FixedUpdate);
SetupFunction(Setup.ModSettings, Mod_Settings);
Id = id;
Description = description;
Default = defaultKey;
}
/// <summary>Taste zum Ein-/Aussteigen als Beifahrer (in den MSCLoader-Einstellungen änderbar).</summary>
internal static SettingsKeybind PassengerKey;
/// <summary>MSCO-Konsole (Standard F7) und Chat (Standard T), ebenfalls änderbar.</summary>
internal static SettingsKeybind ConsoleKey, ChatKey;
string Pref { get { return "MSCO_Key_" + Id; } }
private void Mod_Settings()
public KeyCode Key
{
// All settings should be created here.
// DO NOT put anything that isn't settings or keybinds in here!
PassengerKey = Keybind.Add("passenger", "Ride as passenger (toggle)", KeyCode.P);
ChatKey = Keybind.Add("chat", "Open chat", KeyCode.T);
// Nicht F8: im Pausenmenü öffnet F8 ein Test-Menü des Spiels. Neue ID, damit alte F8-Belegungen nicht übernommen werden
ConsoleKey = Keybind.Add("console_f7", "Open MSCO console", KeyCode.F7);
}
private void Mod_OnMenuLoad()
{
MscoLog.Info("Welcome to MSCO by mbm.wtf");
// Netzwerk + Lobby leben in einem eigenen Objekt, das Szenenwechsel überlebt
MultiplayerManager.Ensure();
if (!commandAdded)
get
{
ConsoleCommand.Add(new MpCommand());
commandAdded = true;
if (!loaded)
{
// Erst im Spiel lesen (PlayerPrefs gehen nicht in statischen Initialisierern)
KeyCode parsed;
key = TryParse(PlayerPrefs.GetString(Pref, ""), out parsed) ? parsed : Default;
loaded = true;
}
return key;
}
set
{
key = value;
loaded = true;
PlayerPrefs.SetString(Pref, value.ToString());
PlayerPrefs.Save();
}
}
private void Mod_OnLoad()
public bool GetKeybindDown()
{
// Called once, when mod is loading after game is fully loaded
return Key != KeyCode.None && Input.GetKeyDown(Key);
}
private void Mod_PostLoad()
public static bool TryParse(string text, out KeyCode key)
{
// Called once, after all mods finished OnLoad
}
private void Mod_OnSave()
{
// Called once, when save and quit
}
private void Mod_OnGUI()
{
// Lobby/Status zeichnet der MultiplayerManager
}
private void Mod_Update()
{
// Netzwerk läuft im MultiplayerManager
}
private void Mod_FixedUpdate()
{
// FixedUpdate is called once per fixed frame
key = KeyCode.None;
if (string.IsNullOrEmpty(text)) return false;
try
{
key = (KeyCode)System.Enum.Parse(typeof(KeyCode), text.Trim(), true);
return true;
}
catch { return false; }
}
}
}