public void Awake() { Instance = this; Logger = base.Logger; ModManager = new DetourModManager(); AddHookLogging(); CheckForIncompatibleAssemblies(); if (Environment.GetEnvironmentVariable("R2API_DEBUG") == "true") { EnableDebug(); } var pluginScanner = new PluginScanner(); var submoduleHandler = new APISubmoduleHandler(Logger); LoadedSubmodules = submoduleHandler.LoadRequested(pluginScanner); pluginScanner.ScanPlugins(); var networkCompatibilityHandler = new NetworkCompatibilityHandler(); networkCompatibilityHandler.BuildModList(); On.RoR2.RoR2Application.Awake += CheckIfUsedOnRightGameVersion; R2APIContentPackProvider.Init(); }
public R2API() { Logger = base.Logger; ModManager = new DetourModManager(); AddHookLogging(); CheckForIncompatibleAssemblies(); CheckR2APIMonomodPatch(); Environment.SetEnvironmentVariable("MONOMOD_DMD_TYPE", "Cecil"); On.RoR2.UnitySystemConsoleRedirector.Redirect += orig => { }; var pluginScanner = new PluginScanner(); var submoduleHandler = new APISubmoduleHandler(GameBuild, Logger); LoadedSubmodules = submoduleHandler.LoadRequested(pluginScanner); pluginScanner.ScanPlugins(); var networkCompatibilityHandler = new NetworkCompatibilityHandler(); networkCompatibilityHandler.BuildModList(); RoR2Application.isModded = true; SteamworksClientManager.onLoaded += CheckIfUsedOnRightGameVersion; VanillaFixes(); }
public R2API() { Logger = base.Logger; ModManager = new DetourModManager(); AddHookLogging(); CheckForIncompatibleAssemblies(); CheckR2APIMonomodPatch(); Environment.SetEnvironmentVariable("MONOMOD_DMD_TYPE", "Cecil"); On.RoR2.RoR2Application.UnitySystemConsoleRedirector.Redirect += orig => { }; var submoduleHandler = new APISubmoduleHandler(GameBuild, Logger); loadedSubmodules = submoduleHandler.LoadRequested(); //Currently disabled until manifest v2 //ModListAPI.Init(); RoR2Application.isModded = true; // Temporary fix as the new quickplay button currently don't have the DisableIfGameModded Script attached to it On.RoR2.UI.QuickPlayButtonController.Start += (orig, self) => { orig(self); self.gameObject.SetActive(false); }; On.RoR2.DisableIfGameModded.OnEnable += (orig, self) => { // TODO: If we can enable quick play without regrets, uncomment. //if (self.name == "Button, QP") // return; self.gameObject.SetActive(false); }; On.RoR2.Networking.SteamLobbyFinder.CCSteamQuickplayStart += (orig, args) => { Debug.Log("QuickPlay is disabled in mods due to social contracts and lack of general support"); }; SteamworksClientManager.onLoaded += () => { var buildId = SteamworksClientManager.instance.GetFieldValue <Client>("steamworksClient").BuildId; if (GameBuild == buildId) { return; } Logger.LogWarning($"This version of R2API was built for build id \"{GameBuild}\", you are running \"{buildId}\"."); Logger.LogWarning("Should any problems arise, please check for a new version before reporting issues."); }; // Make sure that modded dedicated servers are recognizable from the server browser On.RoR2.SteamworksServerManager.UpdateHostName += (orig, self, hostname) => { var server = ((SteamworksServerManager)self).GetFieldValue <Server>("steamworksServer"); server.GameTags = "mod," + server.GameTags; }; }
internal static void Dispose(object sender, EventArgs args) { Audio.Unload(); // This exists but never gets called by the vanilla game. if (_DetourModManager != null) { foreach (Assembly asm in _DetourOwners) { _DetourModManager.Unload(asm); } _DetourModManager.Dispose(); _DetourModManager = null; _DetourOwners.Clear(); } }
public R2API() { Logger = base.Logger; ModManager = new DetourModManager(); AddHookLogging(); CheckForIncompatibleAssemblies(); CheckR2APIMonomodPatch(); Environment.SetEnvironmentVariable("MONOMOD_DMD_TYPE", "Cecil"); On.RoR2.RoR2Application.UnitySystemConsoleRedirector.Redirect += orig => { }; var submoduleHandler = new APISubmoduleHandler(GameBuild, Logger); submoduleHandler.LoadRequested(); RoR2Application.isModded = true; //This needs to always be enabled, regardless of module dependency, or it is useless ModListAPI.Init(); On.RoR2.DisableIfGameModded.OnEnable += (orig, self) => { // TODO: If we can enable quick play without regrets, uncomment. //if (self.name == "Button, QP") // return; self.gameObject.SetActive(false); }; SteamworksClientManager.onLoaded += () => { var buildId = SteamworksClientManager.instance.GetFieldValue <Client>("steamworksClient").BuildId; if (GameBuild == buildId) { return; } Logger.LogWarning($"This version of R2API was built for build id \"{GameBuild}\", you are running \"{buildId}\"."); Logger.LogWarning("Should any problems arise, please check for a new version before reporting issues."); }; On.RoR2.SteamworksServerManager.UpdateHostName += (orig, self, hostname) => { orig(self, $"[MOD] {hostname}"); var server = ((SteamworksServerManager)self).GetFieldValue <Server>("steamworksServer"); server.GameTags = "mod," + server.GameTags; }; }
public static void Init() { if (Initialized) { return; } Initialized = true; Manager = new DetourModManager(); Manager.Ignored.Add(Assembly.GetExecutingAssembly()); // Load the cecil module generator. // Adding this more than once shouldn't hurt. HookEndpointManager.OnGenerateCecilModule += GenerateCecilModule; // Some mods might forget to undo their hooks. HookOnUnloadContent = new Hook( typeof(Mod).GetMethod("UnloadContent", BindingFlags.NonPublic | BindingFlags.Instance), typeof(TerrariaHooksManager).GetMethod("OnUnloadContent", BindingFlags.NonPublic | BindingFlags.Static) ); // All of our own hooks need to be undone last. HookOnUnloadAll = new Hook( typeof(ModLoader).GetMethod("Unload", BindingFlags.NonPublic | BindingFlags.Static), typeof(TerrariaHooksManager).GetMethod("OnUnloadAll", BindingFlags.NonPublic | BindingFlags.Static) ); // Try to hook the logger to avoid logging "silent" exceptions thrown by TerrariaHooks. MethodBase m_LogSilentException = typeof(Mod).Assembly.GetType("Terraria.ModLoader.ModCompile+<>c") ?.GetMethod("<ActivateExceptionReporting>b__15_0", BindingFlags.NonPublic | BindingFlags.Instance); if (m_LogSilentException != null) { HookOnLogSilentException = new Hook( m_LogSilentException, typeof(TerrariaHooksManager).GetMethod("OnLogSilentException", BindingFlags.NonPublic | BindingFlags.Static) ); } }
public void Awake() { Instance = this; Logger = base.Logger; ModManager = new DetourModManager(); AddHookLogging(); CheckForIncompatibleAssemblies(); Environment.SetEnvironmentVariable("MONOMOD_DMD_TYPE", "Cecil"); if (Environment.GetEnvironmentVariable("R2API_DEBUG") == "true") { EnableDebug(); } On.RoR2.UnitySystemConsoleRedirector.Redirect += orig => { }; LoadRoR2ContentEarly.Init(); var pluginScanner = new PluginScanner(); var submoduleHandler = new APISubmoduleHandler(GameBuild, Logger); LoadedSubmodules = submoduleHandler.LoadRequested(pluginScanner); pluginScanner.ScanPlugins(); var networkCompatibilityHandler = new NetworkCompatibilityHandler(); networkCompatibilityHandler.BuildModList(); RoR2Application.isModded = true; SteamworksClientManager.onLoaded += CheckIfUsedOnRightGameVersion; VanillaFixes(); R2APIContentPackProvider.Init(); }
public void Awake() { Logger = base.Logger; ModManager = new DetourModManager(); AddHookLogging(); CheckForIncompatibleAssemblies(); CheckR2APIPatch(); Environment.SetEnvironmentVariable("MONOMOD_DMD_TYPE", "Cecil"); On.RoR2.UnitySystemConsoleRedirector.Redirect += orig => { }; var pluginScanner = new PluginScanner(); var submoduleHandler = new APISubmoduleHandler(GameBuild, Logger); LoadedSubmodules = submoduleHandler.LoadRequested(pluginScanner); pluginScanner.ScanPlugins(); var networkCompatibilityHandler = new NetworkCompatibilityHandler(); networkCompatibilityHandler.BuildModList(); RoR2Application.isModded = true; SteamworksClientManager.onLoaded += CheckIfUsedOnRightGameVersion; VanillaFixes(); // Load RoR2Content early so that modders // can take prefabs refs from the fields directly. RoR2Content = new RoR2Content(); // We dont want the game code to remake the RoR2Content instance again IL.RoR2.RoR2Application.OnLoad += TakeOurInstanceInstead; R2APIContentPackProvider.Init(); }
public R2API() { Logger = base.Logger; ModManager = new DetourModManager(); AddHookLogging(); CheckForIncompatibleAssemblies(); Environment.SetEnvironmentVariable("MONOMOD_DMD_TYPE", "Cecil"); On.RoR2.RoR2Application.UnitySystemConsoleRedirector.Redirect += orig => { }; var submoduleHandler = new APISubmoduleHandler(GameBuild, Logger); submoduleHandler.LoadAll(GetType().Assembly); RoR2Application.isModded = true; On.RoR2.DisableIfGameModded.OnEnable += (orig, self) => { // TODO: If we can enable quick play without regrets, uncomment. //if (self.name == "Button, QP") // return; self.gameObject.SetActive(false); }; On.RoR2.RoR2Application.OnLoad += (orig, self) => { orig(self); if (GameBuild == self.steamworksClient.BuildId) { return; } Logger.LogWarning($"This version of R2API was built for build id \"{GameBuild}\", you are running \"{self.steamworksClient.BuildId}\"."); Logger.LogWarning("Should any problems arise, please check for a new version before reporting issues."); }; }
internal static void Boot() { Logger.Log(LogLevel.Info, "core", "Booting Everest"); Logger.Log(LogLevel.Info, "core", $"AppDomain: {AppDomain.CurrentDomain.FriendlyName ?? "???"}"); Logger.Log(LogLevel.Info, "core", $"VersionCelesteString: {VersionCelesteString}"); if (Type.GetType("Mono.Runtime") != null) { // Mono hates HTTPS. ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => { return(true); }; } // enable TLS 1.2 to fix connecting to everestapi.github.io ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12; PathGame = Path.GetDirectoryName(typeof(Celeste).Assembly.Location); // .NET hates it when strong-named dependencies get updated. AppDomain.CurrentDomain.AssemblyResolve += (asmSender, asmArgs) => { AssemblyName asmName = new AssemblyName(asmArgs.Name); if (!asmName.Name.StartsWith("Mono.Cecil") && !asmName.Name.StartsWith("YamlDotNet") && !asmName.Name.StartsWith("NLua")) { return(null); } Assembly asm = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(other => other.GetName().Name == asmName.Name); if (asm != null) { return(asm); } return(Assembly.LoadFrom(Path.Combine(PathGame, asmName.Name + ".dll"))); }; // .NET hates to acknowledge manually loaded assemblies. AppDomain.CurrentDomain.AssemblyResolve += (asmSender, asmArgs) => { AssemblyName asmName = new AssemblyName(asmArgs.Name); foreach (Assembly asm in _RelinkedAssemblies) { if (asm.GetName().Name == asmName.Name) { return(asm); } } return(null); }; // Preload some basic dependencies. Assembly.Load("MonoMod.RuntimeDetour"); Assembly.Load("MonoMod.Utils"); Assembly.Load("Mono.Cecil"); Assembly.Load("YamlDotNet"); Assembly.Load("Newtonsoft.Json"); Assembly.Load("Jdenticon"); if (!File.Exists(Path.Combine(PathGame, "EverestXDGFlag"))) { XDGPaths = false; PathEverest = PathGame; } else { XDGPaths = true; string dataDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); Directory.CreateDirectory(PathEverest = Path.Combine(dataDir, "Everest")); Directory.CreateDirectory(Path.Combine(dataDir, "Everest", "Mods")); // Make sure it exists before content gets initialized } // Old versions of Everest have used a separate ModSettings folder. string modSettingsOld = Path.Combine(PathEverest, "ModSettings"); string modSettingsRIP = Path.Combine(PathEverest, "ModSettings-OBSOLETE"); if (Directory.Exists(modSettingsOld) || Directory.Exists(modSettingsRIP)) { Logger.Log(LogLevel.Warn, "core", "THE ModSettings FOLDER IS OBSOLETE AND WILL NO LONGER BE USED!"); if (Directory.Exists(modSettingsOld) && !Directory.Exists(modSettingsRIP)) { Directory.Move(modSettingsOld, modSettingsRIP); } } _DetourModManager = new DetourModManager(); _DetourModManager.OnILHook += (owner, from, to) => { _DetourOwners.Add(owner); object target = to.Target; _DetourLog.Add($"new ILHook by {owner.GetName().Name}: {from.GetID()} -> {to.Method?.GetID() ?? "???"}" + (target == null ? "" : $" (target: {target})")); }; _DetourModManager.OnHook += (owner, from, to, target) => { _DetourOwners.Add(owner); _DetourLog.Add($"new Hook by {owner.GetName().Name}: {from.GetID()} -> {to.GetID()}" + (target == null ? "" : $" (target: {target})")); }; _DetourModManager.OnDetour += (owner, from, to) => { _DetourOwners.Add(owner); _DetourLog.Add($"new Detour by {owner.GetName().Name}: {from.GetID()} -> {to.GetID()}"); }; _DetourModManager.OnNativeDetour += (owner, fromMethod, from, to) => { _DetourOwners.Add(owner); _DetourLog.Add($"new NativeDetour by {owner.GetName().Name}: {fromMethod?.ToString() ?? from.ToString("16X")} -> {to.ToString("16X")}"); }; HookEndpointManager.OnAdd += (from, to) => { Assembly owner = HookEndpointManager.GetOwner(to) as Assembly ?? typeof(Everest).Assembly; _DetourOwners.Add(owner); object target = to.Target; _DetourLog.Add($"new On.+= by {owner.GetName().Name}: {from.GetID()} -> {to.Method?.GetID() ?? "???"}" + (target == null ? "" : $" (target: {target})")); return(true); }; HookEndpointManager.OnModify += (from, to) => { Assembly owner = HookEndpointManager.GetOwner(to) as Assembly ?? typeof(Everest).Assembly; _DetourOwners.Add(owner); object target = to.Target; _DetourLog.Add($"new IL.+= by {owner.GetName().Name}: {from.GetID()} -> {to.Method?.GetID() ?? "???"}" + (target == null ? "" : $" (target: {target})")); return(true); }; // Before even initializing anything else, make sure to prepare any static flags. Flags.Initialize(); // 0.1 parses into 1 in regions using , // This also somehow sets the exception message language to English. CultureInfo.DefaultThreadCurrentCulture = CultureInfo.InvariantCulture; CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.InvariantCulture; if (!Flags.IsHeadless) { // Initialize the content helper. Content.Initialize(); // Initialize all main managers before loading any mods. TouchInputManager.Instance = new TouchInputManager(Celeste.Instance); // Don't add it yet, though - add it in Initialize. } MainThreadHelper.Instance = new MainThreadHelper(Celeste.Instance); // Register our core module and load any other modules. new CoreModule().Register(); // Note: Everest fulfills some mod dependencies by itself. new NullModule(new EverestModuleMetadata() { Name = "Celeste", VersionString = $"{Celeste.Instance.Version.ToString()}-{(typeof(Game).Assembly.FullName.Contains("FNA") ? "fna" : "xna")}" }).Register(); new NullModule(new EverestModuleMetadata() { Name = "DialogCutscene", VersionString = "1.0.0" }).Register(); new NullModule(new EverestModuleMetadata() { Name = "UpdateChecker", VersionString = "1.0.2" }).Register(); LuaLoader.Initialize(); Loader.LoadAuto(); if (!Flags.IsHeadless) { // Load stray .bins afterwards. Content.Crawl(new MapBinsInModsModContent(Path.Combine(PathEverest, "Mods"))); } // Also let all mods parse the arguments. Queue <string> args = new Queue <string>(Args); while (args.Count > 0) { string arg = args.Dequeue(); foreach (EverestModule mod in _Modules) { if (mod.ParseArg(arg, args)) { break; } } } // Start requesting the version list ASAP. Updater.RequestAll(); // Request the mod update list as well. ModUpdaterHelper.RunAsyncCheckForModUpdates(excludeBlacklist: true); }
public static void Init() { if (Initialized) { return; } Initialized = true; Manager = new DetourModManager(); Manager.Ignored.Add(Assembly.GetExecutingAssembly()); // Load the cecil module generator. // Adding this more than once shouldn't hurt. HookEndpointManager.OnGenerateCecilModule += GenerateCecilModule; // Some mods might forget to undo their hooks. HookOnUnloadContent = new Hook( typeof(Mod).GetMethod("UnloadContent", BindingFlags.NonPublic | BindingFlags.Instance), typeof(TerrariaHooksManager).GetMethod("OnUnloadContent", BindingFlags.NonPublic | BindingFlags.Static) ); // All of our own hooks need to be undone last. MethodBase m_Unload = typeof(ModLoader).GetMethod("Unload", BindingFlags.NonPublic | BindingFlags.Static); if (m_Unload != null) { HookOnUnloadAll = new Hook( m_Unload, typeof(TerrariaHooksManager).GetMethod("OnUnloadAll", BindingFlags.NonPublic | BindingFlags.Static) ); } else { /* The unofficial tML x64 form is a hot mess of tML 0.10 and 0.11 pre-beta. * As such, mods are only unloaded when they're reloaded. * Luckily, ModContnet.Unload runs right after unloading all mods. * Even more luckily, it pretty much shares the same signature as the old Unload method. */ MethodBase m_UnloadContent = typeof(Mod).Assembly.GetType("Terraria.ModLoader.ModContent") ?.GetMethod("Unload", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); if (m_UnloadContent != null) { HookOnUnloadAll = new Hook( m_UnloadContent, typeof(TerrariaHooksManager).GetMethod("OnUnloadAll", BindingFlags.NonPublic | BindingFlags.Static) ); } else { throw new Exception("Incompatible tML version: Can't find unload hook point"); } } // Try to hook the logger to avoid logging "silent" exceptions thrown by TerrariaHooks. MethodBase m_LogSilentException = typeof(Mod).Assembly.GetType("Terraria.ModLoader.ModCompile+<>c") ?.GetMethod("<ActivateExceptionReporting>b__15_0", BindingFlags.NonPublic | BindingFlags.Instance); if (m_LogSilentException != null) { HookOnLogSilentException = new Hook( m_LogSilentException, typeof(TerrariaHooksManager).GetMethod("OnLogSilentException", BindingFlags.NonPublic | BindingFlags.Static) ); } }