diff --git a/Builds/Managed/Assembly-CSharp.dll b/Builds/Managed/Assembly-CSharp.dll new file mode 100644 index 0000000..52ccdd3 Binary files /dev/null and b/Builds/Managed/Assembly-CSharp.dll differ diff --git a/Builds/preview-mod/TestMod1.dll b/Builds/preview-mod/TestMod1.dll new file mode 100644 index 0000000..4d4ac59 Binary files /dev/null and b/Builds/preview-mod/TestMod1.dll differ diff --git a/ModLoader/Attributes/MyModEntryPoint.cs b/ModLoader/Attributes/MyModEntryPoint.cs new file mode 100644 index 0000000..9744e21 --- /dev/null +++ b/ModLoader/Attributes/MyModEntryPoint.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace SFSML.Attributes +{ + public class MyModEntryPoint : Attribute + { + } +} diff --git a/ModLoader/Exceptions/MyCoreException.cs b/ModLoader/Exceptions/MyCoreException.cs new file mode 100644 index 0000000..cd3fe4d --- /dev/null +++ b/ModLoader/Exceptions/MyCoreException.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace SFSML.Exceptions +{ + class MyCoreException : Exception + { + public MyCaller caller; + public readonly string file; + public readonly string msg; + public MyCoreException(string message, string myFile) : base("Whoops something went wrong!") + { + this.file = myFile; + this.msg = message; + } + + public class MyCaller + { + public readonly string function; + public readonly string file; + public MyCaller(string functionName, string fileName) + { + this.function = functionName; + this.file = fileName; + } + public string construct() + { + return function + "()" + " [" + file + "]"; + } + } + } +} diff --git a/ModLoader/HookSystem/HookExceptions/NotHookedException.cs b/ModLoader/HookSystem/HookExceptions/NotHookedException.cs new file mode 100644 index 0000000..48235ab --- /dev/null +++ b/ModLoader/HookSystem/HookExceptions/NotHookedException.cs @@ -0,0 +1,26 @@ +/* + * Created by SharpDevelop. + * User: JordivdMolen + * Date: 2/15/2018 + * Time: 10:35 AM + * + * Using this file for commercial purposes can result + * in violating the license! + */ +using System; +using SFSML.HookSystem; + +namespace SFSML.HookSystem.HookExceptions +{ + /// + /// Description of NotHookedException. + /// + public class NotHookedException : Exception + { + public MyInitialHook target; + public NotHookedException(MyInitialHook tgt) : base("This hook is not registered in a MyBaseHookable") + { + this.target = tgt; + } + } +} diff --git a/ModLoader/HookSystem/MainHooks/MyGameLoadedHook.cs b/ModLoader/HookSystem/MainHooks/MyGameLoadedHook.cs new file mode 100644 index 0000000..6627d98 --- /dev/null +++ b/ModLoader/HookSystem/MainHooks/MyGameLoadedHook.cs @@ -0,0 +1,17 @@ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace SFSML.HookSystem.MainHooks +{ + class MyGameLoadedHook : MyBaseHook + { + public ModLoader core; + public MyGameLoadedHook(ModLoader coreLoader, String test) + { + this.core = coreLoader; + } + } +} diff --git a/ModLoader/HookSystem/MyBaseHook.cs b/ModLoader/HookSystem/MyBaseHook.cs new file mode 100644 index 0000000..6b440cc --- /dev/null +++ b/ModLoader/HookSystem/MyBaseHook.cs @@ -0,0 +1,81 @@ +/* + * Created by SharpDevelop. + * User: JordivdMolen + * Date: 2/14/2018 + * Time: 9:26 PM + * + * Using this file for commercial purposes can result + * in violating the license! + */ +using System; +using System.Collections.Generic; +using SFSML.HookSystem.HookExceptions; +using System.Reflection; + +namespace SFSML.HookSystem +{ + /// + /// Event-like system, baseclass. + /// + public abstract class MyBaseHook : MyInitialHook + { + private MyBaseHookable infested = null; + protected Func onInvoke = null; + public MyBaseHook() + { + this.baseType = typeof(T); + } + + /// + /// setOnInvoke AKA Register hook as hookListener + /// + /// This function will be ran when the hook is casted + /// This object should be the object you are registering the hook on. + public void setOnInvoke(Func hook, MyBaseHookable root) + { + if (this.onInvoke == null) + { + this.onInvoke = hook; + root.registerListener(this); + } + else + { + throw new Exception("OnInvoke is already set @ setOnInvoke"); + } + } + + public T invoke(T e) + { + if (!(e is MyBaseHook)) + { + throw new Exception("event has to be an instace of MyBaseHook @ Invoke"); + } + if (!this.isListener()) + { + throw new Exception("This hook is not a listener! @ Invoke"); + } + MyBaseHook hookT = ((object) e) as MyBaseHook; + T invokeResult = this.onInvoke(e); + return invokeResult; + } + + + public Dictionary getEventArgumets() + { + Dictionary args = new Dictionary(); + foreach (FieldInfo fi in this.GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance)) + { + if (fi.DeclaringType == typeof(T)) + { + args[fi.Name] = fi; + } + } + return args; + } + + public bool isListener() + { + return this.onInvoke != null; + } + } +} diff --git a/ModLoader/HookSystem/MyBaseHookable.cs b/ModLoader/HookSystem/MyBaseHookable.cs new file mode 100644 index 0000000..748ee84 --- /dev/null +++ b/ModLoader/HookSystem/MyBaseHookable.cs @@ -0,0 +1,70 @@ +/* + * Created by SharpDevelop. + * User: JordivdMolen + * Date: 2/14/2018 + * Time: 9:39 PM + * + * Using this file for commercial purposes can result + * in violating the license! + */ +using SFSML.Exceptions; +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace SFSML.HookSystem +{ + /// + /// Description of MyBaseHookable. + /// + public class MyBaseHookable + { + private List hooks = new List(); + public MyBaseHookable() + { + } + + public void registerListener(MyBaseHook e) + { + if (!e.isListener()) + { + throw new MyCoreException("This hook is not a listener! @ RegisterListener","registerListener()"); + } + this.hooks.Add(e); + } + public void removeListener(MyInitialHook e) + { + this.hooks.Remove(e); + } + public T castHook(T e) + { + T usedCaller = (T) ((MyInitialHook)(object)e).Clone(); + MyBaseHook convertedBase = (Object) e as MyBaseHook; + Dictionary initialFields = convertedBase.getEventArgumets(); + foreach (MyInitialHook initHook in this.hooks) + { + T ih = (T) (object) initHook; + MyBaseHook convertedHook = (MyBaseHook) initHook; + T afterInvoke = convertedHook.invoke(usedCaller); + convertedHook = (object) afterInvoke as MyBaseHook; + Dictionary initHookFields = convertedHook.getEventArgumets(); + if (convertedHook.isCanceled()) + { + convertedBase.forceCanceled(true); + } + foreach (String fieldName in initialFields.Keys) + { + FieldInfo orginField = initialFields[fieldName]; + FieldInfo newField = initHookFields[fieldName]; + object orginValue = orginField.GetValue(e); + object newValue = newField.GetValue(afterInvoke); + if (orginValue != newValue) + { + orginField.SetValue(e, newValue); + } + } + } + return (T) (object) convertedBase; + } + } +} diff --git a/ModLoader/HookSystem/MyInitialHook.cs b/ModLoader/HookSystem/MyInitialHook.cs new file mode 100644 index 0000000..5314a2d --- /dev/null +++ b/ModLoader/HookSystem/MyInitialHook.cs @@ -0,0 +1,38 @@ +using SFSML.HookSystem; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace SFSML.HookSystem +{ + public class MyInitialHook : ICloneable + { + private MyBaseHookable infested = null; + protected Type baseType; + protected bool cancel = false; + public MyInitialHook() + { + } + + public Type getInitialType() + { + return this.baseType; + } + + public bool isCanceled() + { + return this.cancel; + } + + public void forceCanceled(bool state) + { + this.cancel = state; + } + + public object Clone() + { + return this.MemberwiseClone(); + } + } +} diff --git a/ModLoader/ModLoader.cs b/ModLoader/ModLoader.cs new file mode 100644 index 0000000..d11771a --- /dev/null +++ b/ModLoader/ModLoader.cs @@ -0,0 +1,164 @@ +/* + * Created by SharpDevelop. + * User: JordivdMolen + * Date: 2/14/2018 + * Time: 10:06 PM + * + * Using this file for commercial purposes can result + * in violating the license! + */ +using System; +using System.Diagnostics; +using System.IO; +using UnityEngine; +using System.Reflection; +using SFSML.HookSystem; +using SFSML.HookSystem.MainHooks; +using SFSML.Exceptions; +using SFSML.Attributes; + +namespace SFSML +{ + /// + /// The coreclass of SFSML. + /// + [MyModEntryPoint] + public class ModLoader : MyBaseHookable + { + public static MyConsole mainConsole; + + public int loadedMods = 0; + private Canvas overlayObject = null; + public MyConsole myConsole; + + public ModLoader() + { + if (Application.platform == RuntimePlatform.WindowsPlayer) + this.myConsole = new MyConsole(); + mainConsole = this.myConsole; + + } + + public void startLoadProcedure() + { + mainConsole.log("Initiating load procedure", "Core"); + this.performDirCheck(); + + this.loadPriorityMods(); + this.loadMods(); + } + + public void toggleOverlay() + { + overlayObject.enabled = !overlayObject.enabled; + } + + public string getMyBaseDirectory() + { + return Application.dataPath + "/SFSML/"; + } + + public string getMyModDirectory() + { + return Application.dataPath + "/SFSML/Mods/"; + } + + public string getMyDataDirectory() + { + return Application.dataPath + "/SFSML/Data/"; + } + + private void performDirCheck() + { + if (!Directory.Exists(this.getMyBaseDirectory())) + { + Directory.CreateDirectory(this.getMyBaseDirectory()); + mainConsole.log("Created SFSML directory.", "DirChecker"); + } + if (!Directory.Exists(this.getMyModDirectory())) + { + Directory.CreateDirectory(this.getMyModDirectory()); + Directory.CreateDirectory(this.getMyModDirectory()+ "priority/"); + Directory.CreateDirectory(this.getMyModDirectory() + "normal/"); + mainConsole.log("Created Mods directory.", "DirChecker"); + } + if (!Directory.Exists(this.getMyDataDirectory())) + { + Directory.CreateDirectory(this.getMyDataDirectory()); + mainConsole.log("Created Data directory.", "DirChecker"); + } + + } + private void loadPriorityMods() + { + try + { + string[] priorityMods = Directory.GetFiles(this.getMyModDirectory() + "priority/"); + foreach (string mod in priorityMods) + { + if (Path.GetExtension(mod) != ".dll") continue; + this.loadModFromFile(mod); + } + } + catch (MyCoreException e) + { + e.caller = new MyCoreException.MyCaller("loadPriorityMods", "ModLoader.cs"); + } + catch (Exception e) + { + mainConsole.logError(e); + } + + } + private void loadMods() + { + try + { + string[] normalMods = Directory.GetFiles(this.getMyModDirectory() + "normal/"); + foreach (string mod in normalMods) + { + this.loadModFromFile(mod); + } + } + catch (MyCoreException e) + { + e.caller = new MyCoreException.MyCaller("loadMods", "ModLoader.cs"); + } + catch (Exception e) + { + mainConsole.logError(e); + } + } + private void loadModFromFile(String modFile) + { + try + { + if (Path.GetExtension(modFile) != ".dll") return; + string modFileName = Path.GetFileNameWithoutExtension(modFile); + mainConsole.log("Loading mod: " + modFileName,"SFSML"); + Assembly modAssembly = Assembly.LoadFrom(modFile); + MyMod entryObject = null; + foreach (Type modType in modAssembly.GetTypes()) + { + object[] attributeList = modType.GetCustomAttributes(typeof(MyModEntryPoint), true); + if (attributeList.Length == 1) + { + entryObject = Activator.CreateInstance(modType) as MyMod; + } + } + string dataPath = this.getMyDataDirectory() + modFileName; + entryObject.assignDataPath(dataPath); + entryObject.Load(); + mainConsole.log("Loaded " + entryObject.myName+".\n"+entryObject.myDescription+"\nVersion "+entryObject.myVersion, entryObject.myName); + } + catch (MyCoreException e) + { + e.caller = new MyCoreException.MyCaller("loadModFromFile", "ModLoader.cs"); + } + catch (Exception e) + { + mainConsole.logError(e); + } + } + } +} diff --git a/ModLoader/MyConfig.cs b/ModLoader/MyConfig.cs new file mode 100644 index 0000000..e9e4718 --- /dev/null +++ b/ModLoader/MyConfig.cs @@ -0,0 +1,74 @@ +using SFSML.Exceptions; +using SFSML.HookSystem; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using UnityEngine; + +namespace SFSML +{ + public class MyConfig + { + Type t; + private object configuration = null; + public readonly string configurationPath; + public MyConfig(string path, Type baseType) + { + this.t = baseType; + this.configurationPath = path; + this.loadConfiguration(baseType); + } + + public T getConfiguration() + { + return (T) this.configuration; + } + public void loadConfiguration(Type configType) + { + if (!File.Exists(this.configurationPath)) + { + object obj = Activator.CreateInstance(configType); + if (!(obj is IMyConfig)) + { + throw new MyCoreException("configType is not part of a IMyConfig!", "MyConfig.cs"); + } + IMyConfig cfg = obj as IMyConfig; + cfg.SetupDefaults(); + this.configuration = cfg; + ((IMyConfig)this.configuration).setParent(this); + this.save(); + } + String json = File.ReadAllText(this.configurationPath); + this.configuration = JsonUtility.FromJson(json, configType); + ((IMyConfig)this.configuration).setParent(this); + } + + public void save() + { + string json = JsonUtility.ToJson(this.configuration); + string path = this.configurationPath; + File.WriteAllText(path, json); + } + } + + public abstract class IMyConfig + { + [NonSerialized] + private MyConfig parent = null; + abstract public void SetupDefaults(); + public void save() + { + this.parent.save(); + } + public void setParent(MyConfig par) + { + if (this.parent == null) + { + this.parent = par; + } + } + } + +} diff --git a/ModLoader/MyConsole.cs b/ModLoader/MyConsole.cs new file mode 100644 index 0000000..d7781c9 --- /dev/null +++ b/ModLoader/MyConsole.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; + +namespace SFSML +{ + public class MyConsole + { + [DllImport("kernel32.dll")] + static extern IntPtr GetConsoleWindow(); + + [DllImport("user32.dll")] + static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); + + [DllImport("Kernel32.dll")] + private static extern bool AllocConsole(); + + const int SW_HIDE = 0; + const int SW_SHOW = 5; + + private bool visible = false; + + public MyConsole() + { + AllocConsole(); + this.hideConsole(); + Console.SetOut(new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = true }); + this.visible = false; + } + public void hideConsole() + { + ShowWindow(GetConsoleWindow(), SW_HIDE); + this.visible = false; + } + public void showConsole() + { + ShowWindow(GetConsoleWindow(), SW_SHOW); + this.visible = true; + } + public void toggleConsole() + { + if (this.visible) + { + this.hideConsole(); + } else + { + this.showConsole(); + } + } + public void logError(Exception e) + { + StackTrace st = new StackTrace(e, true); + StackFrame sf = st.GetFrame(0); + int line = sf.GetFileLineNumber(); + string file = sf.GetFileName(); + Console.WriteLine("##[ERROR]##"); + Console.WriteLine(e.Message); + Console.WriteLine(e.StackTrace); + Console.WriteLine(line + "@"+file); + Console.WriteLine("##[ERROR]##"); + } + public void log(String msg, String tag) + { + Console.WriteLine("["+ tag + "]: " + msg); + } + public void log(String msg) + { + this.log(msg, "Unkwn"); + } + + } +} diff --git a/ModLoader/MyMod.cs b/ModLoader/MyMod.cs new file mode 100644 index 0000000..b5e8488 --- /dev/null +++ b/ModLoader/MyMod.cs @@ -0,0 +1,61 @@ +using SFSML.HookSystem; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace SFSML +{ + public abstract class MyMod : MyBaseHookable + { + public readonly String myName; + public readonly String myDescription; + public readonly String myVersion; + private string dataPath; + private string configPath; + private MyConfig configurationObject; + private Type cfgType = null; + public MyMod(String name, String description, string version) + { + this.myName = name; + this.myDescription = description; + this.myVersion = version; + ModLoader.mainConsole.log("No MyConfig type specefied.", "MyMod"); + } + public MyMod(String name, String description, string version, Type configurationType) + { + this.myName = name; + this.myDescription = description; + this.myVersion = version; + this.cfgType = configurationType; + ModLoader.mainConsole.log("Instanciating MyConfig.", "MyMod"); + } + protected abstract void onLoad(); + protected abstract void onUnload(); + public void Load() + { + this.onLoad(); + } + public void Unload() + { + this.onUnload(); + } + public void assignDataPath(string path) + { + if (this.dataPath == null) + { + this.dataPath = path; + if (cfgType != null) + this.configurationObject = new MyConfig(path + ".cfg",this.cfgType); + } + } + public string getDataPath() + { + return this.dataPath; + } + public MyConfig config() + { + return configurationObject; + } + } +}