diff --git a/FrameworkMod.cs b/FrameworkMod.cs index 4ec8fb7..2480727 100644 --- a/FrameworkMod.cs +++ b/FrameworkMod.cs @@ -5,15 +5,16 @@ namespace PlanetbaseFramework { public class FrameworkMod : ModBase { - public const string AssemblyVersion = "2.2.2.0"; + public const string AssemblyVersion = "2.3.2.0"; public new static readonly Version ModVersion = new Version(AssemblyVersion); + public const string DefaultModName = "Planetbase Framework"; public FrameworkMod() { Utils.ErrorTexture = ModTextures.Find(x => x.name.Equals("error.png")); } - public override string ModName { get; } = "Planetbase Framework"; + public override string ModName { get; } = DefaultModName; public override void Init() { @@ -24,7 +25,7 @@ public override void Init() private class ModTitleButton : TitleButton { - public ModTitleButton() : base("mod_titlemenu") { } + public ModTitleButton() : base("fmod_titlemenu") { } public override void HandleAction(GameStateTitle gst) { diff --git a/GuiRenderer.cs b/GuiRenderer.cs new file mode 100644 index 0000000..db9b66b --- /dev/null +++ b/GuiRenderer.cs @@ -0,0 +1,33 @@ +using System; +using UnityEngine; +using Planetbase; + +namespace PlanetbaseFramework +{ + class GuiRenderer : Planetbase.GuiRenderer + { + + public bool renderBigButton(Rect rect, string text, FontSize fontSize, GUIStyle style, SoundDefinition sound = null) + { + if (style == null) + { + float aspect = rect.width / rect.height; + style = (aspect <= 1.5f) ? Singleton.getInstance().getBigButtonStyle(fontSize, aspect) : Singleton.getInstance().getBigTextButtonStyle(fontSize, aspect); + } + if (!GUI.Button(rect, text, style)) + { + return false; + } + Singleton.getInstance().play((sound != null) ? sound : SoundListMenu.getInstance().ButtonClick, null); + return true; + } + + public bool renderTitleButton(Rect rect, string text, FontSize fontSize, GUIStyle style, bool playSound = false) + { + return this.renderBigButton(rect, text, fontSize, style, null); + } + + } +} + + diff --git a/IModConfig.cs b/IModConfig.cs new file mode 100644 index 0000000..d58e888 --- /dev/null +++ b/IModConfig.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using UnityEngine; + +namespace PlanetbaseFramework +{ + + public interface IModConfig + { + + bool isActive(string modName); + void setActive(string modName, bool state); + + } + + public class SimpleFileBasedModConfig : IModConfig + { + + private string configFile; + private Dictionary modConfigMap = null; + + public SimpleFileBasedModConfig(string configFile) + { + this.configFile = configFile; + } + + private void loadConfig() + { + modConfigMap = new Dictionary(); + if (!System.IO.File.Exists(configFile)) + { + Debug.Log("Config file {0} not found. empty config.."); + return; + } + System.IO.StreamReader reader = new System.IO.StreamReader(configFile); + String line = null; + while ((line = reader.ReadLine()) != null) + { + Regex commentRegex = new Regex(@"^#.*"); + Regex propertyRegex = new Regex(@".+=.*"); + if (commentRegex.Match(line).Success || !propertyRegex.Match(line).Success) + continue; + + String[] parts = line.Split(new char[] { '=' }, 2); + modConfigMap.Add(parts[0], (parts.Length > 1) ? parts[1] : ""); + } + reader.Close(); + } + + private void saveConfig() + { + System.IO.StreamWriter writer = new System.IO.StreamWriter(configFile, false); + + foreach (KeyValuePair entry in modConfigMap) + { + writer.WriteLine("{0}={1}", entry.Key, entry.Value); + } + writer.Flush(); + writer.Close(); + } + + public bool isActive(string modName) + { + if (modConfigMap == null) + loadConfig(); + return (modConfigMap.ContainsKey(modName) && modConfigMap[modName].Equals("1")); + } + + public void setActive(string modName, bool state) + { + if (modConfigMap == null) + loadConfig(); + + if (state) + { + if (!modConfigMap.ContainsKey(modName)) + modConfigMap.Add(modName, "1"); + } + else + { + if (modConfigMap.ContainsKey(modName)) + modConfigMap.Remove(modName); + } + saveConfig(); + } + + } + +} diff --git a/IModMetaData.cs b/IModMetaData.cs new file mode 100644 index 0000000..3669c56 --- /dev/null +++ b/IModMetaData.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace PlanetbaseFramework +{ + + + public interface IModMetaData + { + + string GetName(); + Version GetVersion(); + string GetRootPath(); + ModBase GetMod(); + List GetTextures(); + List GetObjects(); + + // TODO: dependencies + + } + + public class SelfModMetaData : IModMetaData + { + + private ModBase mod; + + public SelfModMetaData(ModBase mod) + { + this.mod = mod; + } + + public ModBase GetMod() + { + return mod; + } + + public string GetName() + { + return mod.ModName; + } + + public Version GetVersion() + { + return mod.ModVersion; + } + + public string GetRootPath() + { + return mod.ModPath; + } + + public List GetTextures() + { + return mod.ModTextures; + } + + public List GetObjects() + { + return mod.ModObjects; + } + + + } + + /* + // TODO: implement xml based meta data + public class XmlModMetaData { + + } + */ +} diff --git a/ModBase.cs b/ModBase.cs index d0cf23f..13137c3 100644 --- a/ModBase.cs +++ b/ModBase.cs @@ -15,6 +15,7 @@ public abstract class ModBase public List ModTextures { get; protected set; } public List ModObjects { get; protected set; } + public virtual Version ModVersion => new Version(0, 0, 0, 0); private HarmonyInstance Harmony { get; set; } @@ -100,7 +101,7 @@ protected ModBase() { ModObjects = LoadAllObjs("obj"); - if(ModObjects.Count > 0) + if (ModObjects.Count > 0) { Debug.Log($"Successfully loaded {ModObjects.Count} object(s)"); } @@ -130,6 +131,8 @@ public virtual void Update() { } + public virtual void Cleanup() { } + public int LoadAllStrings(string subfolder = null) { var files = GetAssetsMatchingFileType("xml", subfolder); diff --git a/ModListGameState.cs b/ModListGameState.cs index ff80bb3..a9a1086 100644 --- a/ModListGameState.cs +++ b/ModListGameState.cs @@ -1,9 +1,11 @@ using Planetbase; using UnityEngine; +using System.Collections.Generic; namespace PlanetbaseFramework { //This class was thrown together fairly quickly, but should be a decent example on making new gamestates. + //qp: followed instructions above.. public class ModListGameState : GameState { private GuiRenderer Renderer { get; } = new GuiRenderer(); @@ -20,10 +22,16 @@ public override void onGui() return; } + List datas = ModManager.getInstance().GetMetaDatas(); PrintLine("Loaded Mods:", 0); - for(var i = 0; i < ModLoader.ModList.Count; i++) + for (var i = 0; i < datas.Count; i++) { - PrintLine(ModLoader.ModList[i].ModName, i + 1); + string name = datas[i].GetName(); + string text = string.Format("{0} - ({1})", name, datas[i].GetVersion().ToString()); + if (name.Equals(Utils.GetFrameworkMod().ModName)) + PrintLine(text, i + 1, (ModManager.getInstance().isModActive(name)) ? Color.green : Color.red, FontSize.Normal); + else + PrintLine(text, i + 1, (ModManager.getInstance().isModActive(name)) ? Color.green : Color.gray, FontSize.Normal, name); } if (Renderer.renderBackButton( @@ -39,13 +47,36 @@ public override void onGui() private void PrintLine(string text, int lineNumber) { - Vector2 textLocation = new Vector2(50, 80); - GUIStyle labelStyle = Renderer.getLabelStyle(FontSize.Huge, FontStyle.Bold, TextAnchor.LowerLeft, FontType.Title); - labelStyle.normal.textColor = Color.blue; + PrintLine(text, lineNumber, Color.blue); + } - GUI.Label(new Rect(textLocation.x, textLocation.y + (GuiRenderer.getMenuButtonSize(FontSize.Huge).y )* lineNumber, Screen.width, GuiRenderer.getMenuButtonSize(FontSize.Huge).y - 30), text, labelStyle); + private void PrintLine(string text, int lineNumber, Color color, FontSize size = FontSize.Huge, string modName = null) + { + float deltaY = (GuiRenderer.getMenuButtonSize(FontSize.Huge).y); - labelStyle.normal.textColor = Color.white; + if (modName != null) + { + Vector2 startLocation = new Vector2(50, 200); + Rect rect = new Rect(startLocation.x, startLocation.y + deltaY * lineNumber, GuiRenderer.getMenuButtonSize(FontSize.Huge).x - 30, GuiRenderer.getMenuButtonSize(FontSize.Huge).y - 30); + float aspect = rect.width / rect.height; + GUIStyle btnStyle = Singleton.getInstance().getBigTextButtonStyle(FontSize.Normal, aspect); + Color c = btnStyle.normal.textColor; + btnStyle.normal.textColor = color; + if (Renderer.renderTitleButton(rect, text, FontSize.Normal)) + { + ModManager.getInstance().setModActive(modName, !(color == Color.green)); + } + btnStyle.normal.textColor = c; + } + else + { + Vector2 startLocation = new Vector2(55, 200); + GUIStyle labelStyle = Renderer.getLabelStyle(size, FontStyle.Bold, TextAnchor.LowerLeft, FontType.Title); + labelStyle.normal.textColor = color; + GUI.Label(new Rect(startLocation.x, startLocation.y + deltaY * lineNumber, Screen.width, GuiRenderer.getMenuButtonSize(FontSize.Huge).y - 30), text, labelStyle); + + labelStyle.normal.textColor = Color.white; + } } } } \ No newline at end of file diff --git a/ModManager.cs b/ModManager.cs new file mode 100644 index 0000000..76fd4e1 --- /dev/null +++ b/ModManager.cs @@ -0,0 +1,177 @@ +using System; +using System.Collections.Generic; +using UnityEngine; +using Planetbase; + +namespace PlanetbaseFramework +{ + + + + public class ModManager + { + + private static ModManager instance = null; + + public static ModManager getInstance() + { + if (instance == null) + { + instance = new ModManager(); + } + return instance; + } + + + + private Dictionary modMetaDatas = new Dictionary(); + + private Dictionary mods = new Dictionary(); + private Dictionary activeMods = new Dictionary(); + + private IModConfig modConfig; + //private IModLoader modLoader; + + public ModManager() + { + init(); + } + + + private void init() + { + modConfig = new SimpleFileBasedModConfig(ModBase.BasePath + "/" + FrameworkMod.DefaultModName + "/.mods.cfg"); + + // register framework mod + FrameworkMod mod = new FrameworkMod(); + RegisterMod(new SelfModMetaData(mod), mod); + //leagcy support + ModLoader.ModList.Add(mod); + } + + /// + /// Set the implementation to use. + /// + /// The mod config implementation. + public void setModConfig(IModConfig modConfig) + { + this.modConfig = modConfig; + } + + + /// + /// Registering a mod to the ModManager. + /// If the mod is newer + /// + /// + /// + public bool RegisterMod(IModMetaData metaData, ModBase mod) + { + string name = metaData.GetName(); + if (modMetaDatas.ContainsKey(name)) + { + /// TODO: Can't get the original version?! always 0.0.0.0 on both sides.. + Debug.Log($"{modMetaDatas[name].GetVersion()} <> {metaData.GetVersion()} = {modMetaDatas[name].GetVersion().CompareTo(metaData.GetVersion())}"); + if (modMetaDatas[name].GetVersion().CompareTo(metaData.GetVersion()) >= 0) + { + Debug.Log($"Newer or equal version ({modMetaDatas[name].GetVersion()} <- {metaData.GetVersion()}) of mod {name} already exists , skipping registration."); + return false; + } + Debug.Log($"Older version of mod {name} already exists, removing.."); + if (isModActive(name)) + { + cleanupMod(mods[name]); + activeMods.Remove(name); + } + modMetaDatas.Remove(name); + mods.Remove(name); + } + modMetaDatas[name] = metaData; + mods[name] = mod; + Debug.Log($"Mod {name} registered with version {metaData.GetVersion()}"); + if (mod.GetType() == typeof(FrameworkMod) || modConfig.isActive(name)) + { + activeMods[name] = mod; + initMod(mod); + Debug.Log($"Mod {name} auto activated."); + } + return true; + } + + private void initMod(ModBase mod) + { + try + { + mod.Init(); + } + catch (Exception e) + { + Debug.Log($"Error initializing mod \"{mod.ModName}\""); + Utils.LogException(e); + } + } + + private void cleanupMod(ModBase mod) + { + try + { + mod.Cleanup(); + } + catch (Exception e) + { + Debug.Log($"Error cleanup mod \"{mod.ModName}\""); + Utils.LogException(e); + } + } + /// + /// Update hook for the mods. + /// TODO: Changing patcher, called from , can be direct. + /// + // copied from ModLoader + public void UpdateMods() + { + foreach (ModBase mod in activeMods.Values) + { + try + { + mod.Update(); + } + catch (Exception e) + { + Debug.Log($"Error updating mod {mod.ModName}"); + Utils.LogException(e); + } + } + } + + + public List GetMetaDatas() + { + return new List(modMetaDatas.Values); + } + + public bool isModActive(string name) + { + return (activeMods.ContainsKey(name)); + } + + public void setModActive(string modName, bool state = true) + { + ModBase mod = mods[modName]; + if (state) + { + initMod(mod); + activeMods.Add(modName, mod); + } + else + { + activeMods.Remove(modName); + cleanupMod(mod); + } + modConfig.setActive(modName, state); + } + + } + + +} diff --git a/Modloader.cs b/Modloader.cs index 2ec665b..9b5762c 100644 --- a/Modloader.cs +++ b/Modloader.cs @@ -31,7 +31,13 @@ public static void LoadMods() if (Directory.Exists(ModBase.BasePath)) { + //legacy support modDLLs.AddRange(Directory.GetFiles(ModBase.BasePath, "*.dll")); + + foreach (string dir in Directory.GetDirectories(ModBase.BasePath)) + { + modDLLs.AddRange(Directory.GetFiles(dir, "*.dll")); + } } else { @@ -39,11 +45,11 @@ public static void LoadMods() Directory.CreateDirectory(ModBase.BasePath); //Create the planetbase mod folder and extract the assets - + } Debug.Log($"Found {modDLLs.Count} mods"); - + foreach (var file in modDLLs) { Type[] types; @@ -100,9 +106,13 @@ public static void LoadMods() try { - mod.Init(); - ModList.Add(mod); - Debug.Log($"Loaded mod \"{modName}\""); + // nope, just load.. + //mod.Init(); + if (ModManager.getInstance().RegisterMod(createMetaData(mod), mod)) + { + ModList.Add(mod); + Debug.Log($"Loaded mod \"{modName}\""); + } } catch (Exception e) { @@ -122,23 +132,22 @@ public static void LoadMods() Debug.Log($"Successfully loaded {modDLLs.Count} mods"); } + /// + /// Give a chance to override the implementation by other implementations. + /// + /// The mod + /// A implementation of the given mod. + static internal IModMetaData createMetaData(ModBase mod) + { + return new SelfModMetaData(mod); + } + /// /// Update the mods in the order they were loaded on each game tick. /// public static void UpdateMods() { - foreach(var mod in ModList) - { - try - { - mod.Update(); - } - catch (Exception e) - { - Debug.Log($"Error updating mod {mod.ModName}"); - Utils.LogException(e); - } - } + ModManager.getInstance().UpdateMods(); } /// diff --git a/Patches/Planetbase/GameManager/Update.cs b/Patches/Planetbase/GameManager/Update.cs index 1bde001..22ac82f 100644 --- a/Patches/Planetbase/GameManager/Update.cs +++ b/Patches/Planetbase/GameManager/Update.cs @@ -8,7 +8,8 @@ public class Update { public static void Postfix() { - ModLoader.UpdateMods(); + //ModLoader.UpdateMods(); + ModManager.getInstance().UpdateMods(); } } } \ No newline at end of file diff --git a/PlanetbaseFramework.csproj b/PlanetbaseFramework.csproj index beab27b..69126a6 100644 --- a/PlanetbaseFramework.csproj +++ b/PlanetbaseFramework.csproj @@ -20,7 +20,7 @@ true full false - bin\Debug\ + ..\..\games\steam\steamapps\common\Planetbase\Planetbase_Data\Managed\ DEBUG;TRACE prompt 4 @@ -51,24 +51,32 @@ - packages\Lib.Harmony.1.2.0.1\lib\net35\0Harmony.dll + ..\..\games\steam\steamapps\common\Planetbase\Planetbase_Data\Managed\0Harmony.dll + False False - ..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\Planetbase\Planetbase_Data\Managed\Assembly-CSharp.dll + ..\..\games\steam\steamapps\common\Planetbase\Planetbase_Data\Managed\Assembly-CSharp.dll + False packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll + False - ..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\Planetbase\Planetbase_Data\Managed\UnityEngine.dll + ..\..\games\steam\steamapps\common\Planetbase\Planetbase_Data\Managed\UnityEngine.dll + False + + + + @@ -99,6 +107,7 @@ + diff --git a/PlanetbaseFramework.csproj.user b/PlanetbaseFramework.csproj.user index ca1d04b..1764eb9 100644 --- a/PlanetbaseFramework.csproj.user +++ b/PlanetbaseFramework.csproj.user @@ -3,4 +3,8 @@ ProjectFiles + + Program + D:\games\steam\steamapps\common\Planetbase\Planetbase.exe + \ No newline at end of file diff --git a/assets/strings/framework_de.xml b/assets/strings/framework_de.xml new file mode 100644 index 0000000..ed610af --- /dev/null +++ b/assets/strings/framework_de.xml @@ -0,0 +1,5 @@ + + + Mod Liste + + \ No newline at end of file diff --git a/assets/strings/framework_en.xml b/assets/strings/framework_en.xml index c794f6d..efe9074 100644 --- a/assets/strings/framework_en.xml +++ b/assets/strings/framework_en.xml @@ -1,5 +1,5 @@ - Mod List + Mod List \ No newline at end of file