using System; using System.IO; using System.Reflection; using Harmony; using UnityEngine; namespace Doorstop { /// /// Einstieg ohne MSCLoader: Unity Doorstop (winhttp.dll im Spielordner) ruft beim Spielstart diese Methode auf - der /// MSCO Launcher startet das Spiel mit "--doorstop-target-assembly ...\MSCO\MSCO.dll", MSCLoader wird dann gar nicht /// geladen. Hier läuft noch nichts von Unity: nur vorbereiten, gestartet wird im Hauptmenü (siehe MSCO.Bootstrap). /// public static class Entrypoint { public static void Start() { MSCO.Bootstrap.Start(); } } } namespace MSCO { /// /// Startet MSCO eigenständig: Abhängigkeiten (Harmony, Newtonsoft.Json) aus dem MSCO-Ordner laden, warten bis das /// Spiel seinen Code geladen hat, dann beim ersten PlayMakerArrayListProxy.Awake (Hauptmenü, Hauptthread) den /// MultiplayerManager anlegen. Protokoll: <Spiel>\MSCO\MSCO_Loader.txt. /// public static class Bootstrap { static string folder; static bool patched, started; static StreamWriter log; public static void Start() { try { folder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); log = new StreamWriter(Path.Combine(folder, "MSCO_Loader.txt"), false) { AutoFlush = true }; Log("MSCO " + MSCO.ModVersion + " loader started (standalone, without MSCLoader)"); Log("Command line: " + string.Join(" ", Environment.GetCommandLineArgs())); AppDomain.CurrentDomain.AssemblyResolve += Resolve; AppDomain.CurrentDomain.AssemblyLoad += (_, e) => OnAssemblyLoaded(e.LoadedAssembly); foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies()) OnAssemblyLoaded(a); } catch (Exception e) { Log("Loader failed: " + e); } } public static void Log(string line) { try { if (log != null) log.WriteLine(DateTime.Now.ToString("HH:mm:ss") + " " + line); } catch { } } /// Unsere Abhängigkeiten liegen neben MSCO.dll (das Spiel sucht nur in Managed). static Assembly Resolve(object sender, ResolveEventArgs args) { try { string name = new AssemblyName(args.Name).Name; string file = Path.Combine(folder, name + ".dll"); if (File.Exists(file)) return Assembly.LoadFrom(file); } catch (Exception e) { Log("Resolve " + args.Name + " failed: " + e.Message); } return null; } static void OnAssemblyLoaded(Assembly assembly) { if (patched) return; string name; try { name = assembly.GetName().Name; } catch { return; } if (name != "Assembly-CSharp") return; patched = true; try { Patch(assembly); } catch (Exception e) { Log("Could not hook into the game: " + e); } } /// Haken in PlayMakerArrayListProxy.Awake (läuft im Hauptmenü früh auf dem Hauptthread). static void Patch(Assembly game) { Type proxy = game.GetType("PlayMakerArrayListProxy"); MethodInfo awake = proxy != null ? proxy.GetMethod("Awake", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) : null; if (awake == null) { Log("PlayMakerArrayListProxy.Awake not found - MSCO cannot start"); return; } HarmonyInstance.Create("mbm.msco.bootstrap").Patch(awake, new HarmonyMethod(typeof(Bootstrap).GetMethod("BeforeAwake", BindingFlags.Static | BindingFlags.NonPublic)), null); Log("Hooked into the game, waiting for the main menu"); } static void BeforeAwake() { if (started) return; started = true; try { var go = new GameObject("MSCO_Host"); UnityEngine.Object.DontDestroyOnLoad(go); go.AddComponent(); Log("MSCO started"); } catch (Exception e) { Log("Start failed: " + e); } } } /// Lebt über alle Szenen: legt im Hauptmenü den MultiplayerManager an (früher MSCLoaders OnMenuLoad). public class MscoHost : MonoBehaviour { void Start() { MscoLog.Info("Welcome to MSCO by mbm.wtf"); // Den Nachrichten-Bildschirm des Spiels ("Latest news ... press any key") überspringen - direkt ins Hauptmenü string scene = Application.loadedLevelName; Bootstrap.Log("First scene: " + scene); if (scene != "MainMenu" && scene != "GAME") { Bootstrap.Log("Skipping the intro/news screen"); Application.LoadLevel("MainMenu"); return; } MultiplayerManager.Ensure(); } void OnLevelWasLoaded(int level) { Bootstrap.Log("Level loaded: " + Application.loadedLevelName); MultiplayerManager.Ensure(); } void OnApplicationQuit() { // Wer beendet das Spiel? (Diagnose) Bootstrap.Log("Game is quitting (scene " + Application.loadedLevelName + "): " + System.Environment.StackTrace); } } }