private void GenerateHookDict() { List <String> modsToUnload = new List <String> (); modHooks = new Dictionary <string, MethodDefinition[]> (); hooks = new Dictionary <string, Dictionary <string, List <BaseModWithId> > > (); foreach (String modId in loader.modOrder) { BaseMod mod = null; try { mod = loader.modInstances [modId]; } catch { continue; } if (mod != null) { Dictionary <string, List <BaseModWithId> > methodHooks; List <BaseModWithId> hookedMods; MethodDefinition[] requestedHooks; try { requestedHooks = (MethodDefinition[])mod.GetType().GetMethod("GetHooks").Invoke(null, new object[] { types, SharedConstants.getExeVersionInt() }); } catch (Exception ex) { Console.WriteLine(ex); modsToUnload.Add(modId); continue; } modHooks.Add(modId, requestedHooks); foreach (MethodDefinition hookedMethod in requestedHooks) { //TODO: FIx for overloaded methods! if (!hooks.TryGetValue(hookedMethod.DeclaringType.Name, out methodHooks)) { methodHooks = new Dictionary <string, List <BaseModWithId> > (); hooks.Add(hookedMethod.DeclaringType.Name, methodHooks); } if (!methodHooks.TryGetValue(hookedMethod.Name, out hookedMods)) { hookedMods = new List <BaseModWithId> (); methodHooks.Add(hookedMethod.Name, hookedMods); } hookedMods.Add(new BaseModWithId(mod, modId)); } } } Unload(modsToUnload); Console.WriteLine("Hooks:"); foreach (string hookedTypeName in hooks.Keys) { Console.WriteLine(hookedTypeName); foreach (string hookedMethodName in hooks[hookedTypeName].Keys) { Console.WriteLine("\t" + hookedMethodName); foreach (BaseModWithId modWithId in hooks[hookedTypeName][hookedMethodName]) { Console.WriteLine("\t\t" + modWithId.id); } } } }
protected bool LoadModAssembly(ModAssemblyInfo assemblyInfo) { if (Mod != null) { throw new InvalidOperationException("Cannot load a mod if one has been loaded already"); } if (assemblyInfo == null) { throw new ArgumentNullException(nameof(assemblyInfo)); } if (string.IsNullOrWhiteSpace(assemblyInfo.AssemblyName)) { throw new ArgumentException("Assembly name must be valid", nameof(assemblyInfo)); } var path = $"{EngineShared.GameDirectory}/{assemblyInfo.AssemblyName}"; Logger.Instance.Information($"Loading mod from path \"{path}\""); Mod = Assembly.LoadFrom(path); if (Mod == null) { Logger.Instance.Error($"Couldn't load mod {assemblyInfo.AssemblyName}"); return(false); } //Exclude abstract classes; developers may place common code in abstract base classes var modInterfaces = Mod.GetTypes().Where(type => type.IsSubclassOf(typeof(BaseMod)) && !type.IsAbstract).ToList(); if (modInterfaces.Count == 0) { Logger.Instance.Error("Couldn't find the mod interface class"); return(false); } var modInterfaceClass = modInterfaces[0]; if (modInterfaces.Count > 1) { Logger.Instance.Warning("Found more than one candidate for the mod interface class"); Logger.Instance.Warning("The types found are:"); modInterfaces.ForEach(t => Logger.Instance.Warning(t.FullName)); Logger.Instance.Warning($"Using the first found class \"{modInterfaceClass.FullName}\""); } var instance = Activator.CreateInstance(modInterfaceClass); ModInterface = (BaseMod)instance; Logger.Instance.Information($"Mod \"{assemblyInfo.AssemblyName}\" loaded"); return(true); }
protected virtual void Dispose(bool disposing) { if (disposing) { CoreMod.Dispose(); BaseMod.Dispose(); } }
public override void OnLoad(SimAirport.Modding.Data.GameState state) { Debug.Log("ModRunner OnLoad"); mod = findMod(); if (mod != null) { mod.SettingManager = settings; OnSettingsLoaded(); mod.OnLoad(state); } }
public void Shutdown() { if (ModInterface != null) { ModInterface.Shutdown(); ModInterface = null; } if (Mod != null) { Mod = null; } }
public void loadMods() { BaseMod.Initialize(publicAPI); foreach (String id in modOrder) { LocalMod lmod = (LocalMod)modManager.installedMods.Find(delegate(Item mod) { return((mod as LocalMod).localId.Equals(id)); }); if (lmod.enabled) { this.loadMod(lmod); } } }
public string OwnFolder(BaseMod mod) { String installpath = null; foreach (String id in loader.modInstances.Keys) { if (loader.modInstances [id].Equals(mod)) { installpath = (loader.modManager.installedMods.Find(delegate(Item lmod) { return((lmod as LocalMod).localId.Equals(id)); }) as LocalMod).installPath; } } if (installpath == null && currentlyLoading != null) { return(Path.GetDirectoryName(currentlyLoading.installPath)); } if (installpath == null) { return(Platform.getModLoaderPath() + Path.DirectorySeparatorChar + "mods" + Path.DirectorySeparatorChar + "Unknown" + Path.DirectorySeparatorChar); } return(Path.GetDirectoryName(installpath)); }
public static void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs e) { if ((e.ExceptionObject as Exception).TargetSite.Module.Assembly.GetName().Name.Equals("UnityEngine") || (e.ExceptionObject as Exception).TargetSite.Module.Assembly.GetName().Name.Equals("Assembly-CSharp") || (e.ExceptionObject as Exception).TargetSite.Module.Assembly.GetName().Name.Equals("ScrollsModLoader") || (e.ExceptionObject as Exception).TargetSite.Module.Assembly.Location.ToLower().Equals(Platform.getGlobalScrollsInstallPath().ToLower()) || (e.ExceptionObject as Exception).TargetSite.Module.Assembly.Location.Equals("")) //no location or Managed => mod loader crash //log { Console.WriteLine(e.ExceptionObject); new ExceptionLogger().logException((Exception)e.ExceptionObject); //unload ScrollsModLoader MethodBodyReplacementProviderRegistry.SetProvider(new NoMethodReplacementProvider()); //check for frequent crashes if (!System.IO.File.Exists(Platform.getGlobalScrollsInstallPath() + System.IO.Path.DirectorySeparatorChar + "check.txt")) { System.IO.File.CreateText(Platform.getGlobalScrollsInstallPath() + System.IO.Path.DirectorySeparatorChar + "check.txt"); Platform.RestartGame(); } else { try { foreach (String id in instance.modOrder) { BaseMod mod = instance.modInstances [id]; if (mod != null) { try { instance.unloadMod((LocalMod)instance.modManager.installedMods.Find(delegate(Item lmod) { return((lmod as LocalMod).id.Equals(id)); })); } catch (Exception exp) { Console.WriteLine(exp); } } } } catch (Exception exp) { Console.WriteLine(exp); } instance.repatch(); } } else if (instance != null && logger != null && logger.Count > 0) { Console.WriteLine(e.ExceptionObject); Assembly asm = (e.ExceptionObject as Exception).TargetSite.Module.Assembly; Type modClass = (from _modClass in asm.GetTypes() where _modClass.InheritsFrom(typeof(BaseMod)) select _modClass).First(); //no mod classes?? if (modClass == null) { return; } foreach (String id in instance.modOrder) { BaseMod mod = null; try { mod = instance.modInstances [id]; } catch (Exception exp) { Console.WriteLine(exp); } if (mod != null) { if (modClass.Equals(mod.GetType())) { String folder = Path.GetDirectoryName(asm.Location); if (File.Exists(folder + Path.DirectorySeparatorChar + "config.json")) { JsonReader reader = new JsonReader(); LocalMod lmod = (LocalMod)reader.Read(File.ReadAllText(folder + Path.DirectorySeparatorChar + "config.json"), typeof(LocalMod)); if (!lmod.localInstall) { logger [lmod.localId].logException((Exception)e.ExceptionObject); } } } } } } }
public BaseModWithId(BaseMod mod, string id) { this.mod = mod; this.id = id; }
public object Intercept(IInvocationInfo info) { if (lastModInstancesCount != loader.modInstances.Count) { lastModInstancesCount = loader.modInstances.Count; hooks = null; } if (hooks == null) { GenerateHookDict(); } //list for unloading List <String> modsToUnload = new List <String> (); String replacement = ""; //Find Mods that hooked into Method Dictionary <string, List <BaseModWithId> > methodHooks; List <BaseModWithId> hookedMods; string declaringTypeName = info.TargetMethod.DeclaringType.Name; string targetMethodName = info.TargetMethod.Name; if (hooks.TryGetValue(declaringTypeName, out methodHooks)) { if (methodHooks.TryGetValue(targetMethodName, out hookedMods)) { //determine replacement foreach (BaseModWithId modWithId in hookedMods) { BaseMod mod = modWithId.mod; string id = modWithId.id; try { if (mod.WantsToReplace(new InvocationInfo(info))) { replacement = id; } } catch (Exception ex) { Console.WriteLine(ex); modsToUnload.Add(id); } } //unload Unload(modsToUnload); //load beforeinvoke foreach (BaseModWithId modWithId in hookedMods) { BaseMod mod = modWithId.mod; string id = modWithId.id; if (id.Equals(replacement)) { continue; } try { mod.BeforeInvoke(new InvocationInfo(info)); } catch (Exception ex) { Console.WriteLine(ex); modsToUnload.Add(id); } } } } //unload Unload(modsToUnload); //TODO: Simplify the Patch-Finding Process - Implement as Mod? //check for patch call object ret = null; bool patchFound = false; foreach (Patch patch in loader.patches) { if (patch.patchedMethods().Any(item => ((item.Name.Equals(info.TargetMethod.Name)) && (item.DeclaringType.Name.Equals(info.TargetMethod.DeclaringType.Name))))) { try { ret = patch.Intercept(info); patchFound = true; } catch (Exception ex) { Console.WriteLine(ex); } } } if (!patchFound) { if (replacement.Equals("")) { ret = info.TargetMethod.Invoke(info.Target, info.Arguments); } else { try { BaseMod mod = loader.modInstances [replacement]; mod.ReplaceMethod(new InvocationInfo(info), out ret); } catch (Exception exp) { Console.WriteLine(exp); modsToUnload.Add(replacement); } } } //Additional unload? Unload(modsToUnload); //load afterinvoke if (hooks.TryGetValue(declaringTypeName, out methodHooks)) { if (methodHooks.TryGetValue(targetMethodName, out hookedMods)) { foreach (BaseModWithId modWithId in hookedMods) { BaseMod mod = modWithId.mod; string id = modWithId.id; try { mod.AfterInvoke(new InvocationInfo(info), ref ret); } catch (Exception exp) { Console.WriteLine(exp); modsToUnload.Add(id); } } } } //unload Unload(modsToUnload); return(ret); }
public Patcher(BaseMod <Mod> mod) : base(mod) { }
public Patcher(BaseMod mod) : base(mod) { }
private BaseMod findMod() { //find location of Mod.cs file //the scripting dir is completely loaded as assembly, so can't use it, //use modrunner dir instead string cslocation = Path.GetDirectoryName(Assembly.GetAssembly(this.GetType()).Location) + Path.DirectorySeparatorChar + ".." + Path.DirectorySeparatorChar + "modrunner" + Path.DirectorySeparatorChar + "Mod.cs"; Debug.Log(1); //find SimAirport.Modding.Base assembly string locModding = Assembly.GetAssembly(this.GetType().BaseType).Location; Debug.Log(2); //find the ref Type we can use to find the Assembly-CSharp assembly //depending on whether we are in the game or menu //this could probably be done way better Type refType; if (MainMenu_UI.instance != null) { refType = MainMenu_UI.instance.GetType(); } else { refType = Game.current.GetType(); } //find Assembly-CSharp assembly String locAssembly = Assembly.GetAssembly(refType).Location; //find UnityEngine.CoreModule assembly String locUnity = Assembly.GetAssembly(refType.BaseType).Location; Debug.Log(3); //add all of the assemblies to a Set for the compiler HashSet <string> assemblies = new HashSet <string>(); assemblies.Add(locModding); assemblies.Add(locAssembly); //find all dependencies/references of main assembly AssemblyName[] assemblyNames = Assembly.GetAssembly(refType).GetReferencedAssemblies(); foreach (AssemblyName assname in assemblyNames) { //system seems to reference the mscorlib already, so do not add it again if (assname.Name != "mscorlib") { string loc = Assembly.Load(assname.Name).Location; assemblies.Add(loc); } } Debug.Log(4); //prepare in-memory assembly compiler... CSharpCodeProvider provider = new CSharpCodeProvider(new Dictionary <string, string>() { { "CompilerVersion", "v4.7.1" } }); //...add the references... CompilerParameters parameters = new CompilerParameters(assemblies.ToArray()); parameters.GenerateInMemory = true; parameters.GenerateExecutable = false; parameters.TreatWarningsAsErrors = false; //...and compile --- here you could also provide multiple source files, it's an array --- CompilerResults compilerResults = provider.CompileAssemblyFromFile(parameters, new[] { cslocation }); compilerResults.Errors.Cast <CompilerError>().ToList().ForEach(error => Debug.Log("Error from compilation: " + error.ErrorText)); //CompiledAssembly is either null (then the errors above are interesting), or a real assembly (then you can just treat the errors as warnings) var asm = compilerResults.CompiledAssembly; if (asm == null) { Debug.Log("Generated assembly is null"); } else { //TODO probably should search for all types which extend BaseMod Type t = asm.GetType("qcs2017.modrunner.ScriptMod"); mod = (BaseMod)Activator.CreateInstance(t); return(mod); } return(null); }