diff --git a/CLre/API/App/Client.cs b/CLre/API/App/Client.cs new file mode 100644 index 0000000..c1c6ca3 --- /dev/null +++ b/CLre/API/App/Client.cs @@ -0,0 +1,88 @@ +using System; +using System.Reflection; +using HarmonyLib; + +namespace CLre.API.App +{ + public static class Client + { + public static event EventHandler InitStart + { + add => FrontEnd_SetupContainer_Patch.preSetup += value; + remove => FrontEnd_SetupContainer_Patch.preSetup -= value; + } + + public static event EventHandler LogInitComplete + { + add => FrontEnd_SetupContainer_Patch.postSyncSetup += value; + remove => FrontEnd_SetupContainer_Patch.postSyncSetup -= value; + } + + public static event EventHandler AsynchronousInitComplete + { + add => FrontEndGuiEngine_SetMainMenuEnabled_Patch.preMenuEnabled += value; + remove => FrontEndGuiEngine_SetMainMenuEnabled_Patch.preMenuEnabled -= value; + } + + public static event EventHandler InitComplete + { + add => FrontEndGuiEngine_SetMainMenuEnabled_Patch.postMenuEnabled += value; + remove => FrontEndGuiEngine_SetMainMenuEnabled_Patch.postMenuEnabled -= value; + } + + public static string Version + { + get => Game.Utilities.VersionReader.GetVersion(); + } + } + + public struct SetupEventArgs {} + + [HarmonyPatch(typeof(FrontEnd.MainFrontEnd), "SetupContainer")] + class FrontEnd_SetupContainer_Patch + { + internal static event EventHandler postSyncSetup; + + internal static event EventHandler preSetup; + + [HarmonyPrefix] + public static void BeforeMethodCall(FrontEnd.MainFrontEnd __instance) + { + if (preSetup != null) preSetup(__instance, new SetupEventArgs { }); + } + + [HarmonyPostfix] + public static void AfterMethodCall(FrontEnd.MainFrontEnd __instance) + { + if (postSyncSetup != null) postSyncSetup(__instance, new SetupEventArgs { }); + } + } + + [HarmonyPatch] + class FrontEndGuiEngine_SetMainMenuEnabled_Patch + { + internal static event EventHandler preMenuEnabled; + + internal static event EventHandler postMenuEnabled; + + [HarmonyPrefix] + public static void BeforeMethodCall(object __instance, bool enabled) + { + if (!enabled) return; + if (preMenuEnabled != null) preMenuEnabled(__instance, new SetupEventArgs { }); + } + + [HarmonyPostfix] + public static void AfterMethodCall(object __instance, bool enabled) + { + if (!enabled) return; + if (postMenuEnabled != null) postMenuEnabled(__instance, new SetupEventArgs { }); + } + + [HarmonyTargetMethod] + public static MethodBase ReflectToGetMethodBase() + { + return AccessTools.Method("FrontEnd.FrontEndGuiEngine:SetMainMenuEnabled"); + } + } +} \ No newline at end of file diff --git a/CLre/API/Engines/FrontEndEngines.cs b/CLre/API/Engines/FrontEndEngines.cs new file mode 100644 index 0000000..c3f9305 --- /dev/null +++ b/CLre/API/Engines/FrontEndEngines.cs @@ -0,0 +1,144 @@ +using HarmonyLib; +using Svelto.DataStructures; +using Svelto.ECS; + +namespace CLre.API.Engines +{ + /// + /// Engine to be registered before vanilla engines are built. + /// This should be called by any other constructor because this alerts CLre of its existence. + /// + public abstract class FrontEndEnginePreBuild : ICLreEngine + { + /// + /// Construct a new instance of a FrontEndEngine. + /// This should be called by any other constructor because this alerts CLre of its existence. + /// + public FrontEndEnginePreBuild() + { + MainFrontEnd_BuildEngines_Patch.beforeBuildEngines.Add(this); + } + + public abstract void Ready(); + public abstract IEntitiesDB entitiesDB { get; set; } + public abstract IEntityFactory entityFactory { get; set; } + } + + /// + /// Engine to be registered before obsolete vanilla engines are built. + /// This should be called by any other constructor because this alerts CLre of its existence. + /// + public abstract class FrontEndObsoleteEnginePreBuild : ICLreEngine + { + /// + /// Construct a new instance of a FrontEndEngine. + /// This should be called by any other constructor because this alerts CLre of its existence. + /// + public FrontEndObsoleteEnginePreBuild() + { + MainFrontEnd_BuildObsoleteEngines_Patch.beforeBuildEngines.Add(this); + } + + public abstract void Ready(); + public abstract IEntitiesDB entitiesDB { get; set; } + public abstract IEntityFactory entityFactory { get; set; } + } + + /// + /// Engine to be registered after vanilla engines are built. + /// + public abstract class FrontEndEnginePostBuild : ICLreEngine + { + /// + /// Construct a new instance of a FrontEndEngine. + /// This should be called by any other constructor because this alerts CLre of its existence. + /// + public FrontEndEnginePostBuild() + { + MainFrontEnd_BuildEngines_Patch.afterBuildEngines.Add(this); + } + + public abstract void Ready(); + public abstract IEntitiesDB entitiesDB { get; set; } + public abstract IEntityFactory entityFactory { get; set; } + } + + /// + /// Engine to be registered after vanilla obsolete engines are built. + /// + public abstract class FrontEndObsoleteEnginePostBuild : ICLreEngine + { + /// + /// Construct a new instance of a FrontEndEngine. + /// This should be called by any other constructor because this alerts CLre of its existence. + /// + public FrontEndObsoleteEnginePostBuild() + { + MainFrontEnd_BuildObsoleteEngines_Patch.afterBuildEngines.Add(this); + } + + public abstract void Ready(); + public abstract IEntitiesDB entitiesDB { get; set; } + public abstract IEntityFactory entityFactory { get; set; } + } + + [HarmonyPatch(typeof(FrontEnd.MainFrontEnd), "BuildEngines")] + class MainFrontEnd_BuildEngines_Patch + { + internal static FasterList beforeBuildEngines = new FasterList(); + + internal static FasterList afterBuildEngines = new FasterList(); + + [HarmonyPrefix] + public static void BeforeMethodCall(FrontEnd.MainFrontEnd __instance) + { + IEntityFactory factory = AccessTools.Field(typeof(FrontEnd.MainFrontEnd), "_entityFactory").GetValue(__instance) as IEntityFactory; + foreach (ICLreEngine e in beforeBuildEngines) + { + e.entityFactory = factory; + __instance.AddEngine(e); + } + } + + [HarmonyPostfix] + public static void AfterMethodCall(FrontEnd.MainFrontEnd __instance) + { + IEntityFactory factory = AccessTools.Field(typeof(FrontEnd.MainFrontEnd), "_entityFactory").GetValue(__instance) as IEntityFactory; + foreach (ICLreEngine e in afterBuildEngines) + { + e.entityFactory = factory; + __instance.AddEngine(e); + } + } + } + + [HarmonyPatch(typeof(FrontEnd.MainFrontEnd), "BuildObsoleteEngines")] + class MainFrontEnd_BuildObsoleteEngines_Patch + { + internal static FasterList beforeBuildEngines = new FasterList(); + + internal static FasterList afterBuildEngines = new FasterList(); + + [HarmonyPrefix] + public static void BeforeMethodCall(FrontEnd.MainFrontEnd __instance) + { + IEntityFactory factory = AccessTools.Field(typeof(FrontEnd.MainFrontEnd), "_entityFactory").GetValue(__instance) as IEntityFactory; + foreach (ICLreEngine e in beforeBuildEngines) + { + e.entityFactory = factory; + __instance.AddEngine(e); + } + } + + [HarmonyPostfix] + public static void AfterMethodCall(FrontEnd.MainFrontEnd __instance) + { + IEntityFactory factory = AccessTools.Field(typeof(FrontEnd.MainFrontEnd), "_entityFactory").GetValue(__instance) as IEntityFactory; + foreach (ICLreEngine e in afterBuildEngines) + { + e.entityFactory = factory; + __instance.AddEngine(e); + } + } + } +} \ No newline at end of file diff --git a/CLre/API/Engines/GameEngines.cs b/CLre/API/Engines/GameEngines.cs new file mode 100644 index 0000000..0c776ee --- /dev/null +++ b/CLre/API/Engines/GameEngines.cs @@ -0,0 +1,60 @@ +using HarmonyLib; +using Svelto.DataStructures; +using Svelto.ECS; + +namespace CLre.API.Engines +{ + public abstract class GameObsoleteEnginePreBuild : ICLreEngine + { + public GameObsoleteEnginePreBuild() + { + MainLevel_BuildDeprecatedEngines_Patch.beforeBuildEngines.Add(this); + } + + public abstract void Ready(); + public abstract IEntitiesDB entitiesDB { get; set; } + public abstract IEntityFactory entityFactory { get; set; } + } + + public abstract class GameObsoleteEnginePostBuild : ICLreEngine + { + public GameObsoleteEnginePostBuild() + { + MainLevel_BuildDeprecatedEngines_Patch.afterBuildEngines.Add(this); + } + + public abstract void Ready(); + public abstract IEntitiesDB entitiesDB { get; set; } + public abstract IEntityFactory entityFactory { get; set; } + } + + [HarmonyPatch(typeof(GameFramework.MainLevel), "BuildDeprecatedEngines")] + class MainLevel_BuildDeprecatedEngines_Patch + { + internal static FasterList beforeBuildEngines = new FasterList(); + + internal static FasterList afterBuildEngines = new FasterList(); + + [HarmonyPrefix] + public static void BeforeMethodCall(GameFramework.MainLevel __instance) + { + IEntityFactory factory = AccessTools.Field(typeof(GameFramework.MainLevel), "_entityFactory").GetValue(__instance) as IEntityFactory; + foreach (ICLreEngine e in beforeBuildEngines) + { + e.entityFactory = factory; + __instance.AddEngine(e); + } + } + + [HarmonyPostfix] + public static void AfterMethodCall(GameFramework.MainLevel __instance) + { + IEntityFactory factory = AccessTools.Field(typeof(GameFramework.MainLevel), "_entityFactory").GetValue(__instance) as IEntityFactory; + foreach (ICLreEngine e in afterBuildEngines) + { + e.entityFactory = factory; + __instance.AddEngine(e); + } + } + } +} \ No newline at end of file diff --git a/CLre/API/Engines/ICLreEngine.cs b/CLre/API/Engines/ICLreEngine.cs new file mode 100644 index 0000000..7b670e7 --- /dev/null +++ b/CLre/API/Engines/ICLreEngine.cs @@ -0,0 +1,9 @@ +using Svelto.ECS; + +namespace CLre.API.Engines +{ + public interface ICLreEngine : IQueryingEntitiesEngine + { + IEntityFactory entityFactory { get; set; } + } +} \ No newline at end of file diff --git a/CLre/API/Utility/Logging.cs b/CLre/API/Utility/Logging.cs new file mode 100644 index 0000000..8366b18 --- /dev/null +++ b/CLre/API/Utility/Logging.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Diagnostics; + +namespace CLre.API.Utility +{ + /// + /// Utility class to access Cardlife's built-in logging capabilities. + /// The log is saved to outputLog#.Log + /// + public static class Logging + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Log(string msg) + { + Svelto.Console.Log(msg); + } + + /// + /// Write a regular message to Cardlife's log + /// + /// The object to log + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Log(object obj) + { + Svelto.Console.Log(obj.ToString()); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void LogError(string msg, Dictionary extraData = null) + { + Svelto.Console.LogError(msg, extraData); + } + + /// + /// Write an error message to Cardlife's log + /// + /// The object to log + /// The extra data to pass to the ILogger + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void LogError(object obj, Dictionary extraData = null) + { + Svelto.Console.LogError(obj.ToString(), extraData); + } + + /// + /// Write an exception to Cardlife's log and to the screen and exit game + /// + /// The exception to log + /// The extra data to pass to the ILogger. + /// This is automatically populated with "OuterException#" and "OuterStacktrace#" entries + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void LogException(Exception e, string msg = null, Dictionary extraData = null) + { + Svelto.Console.LogException(msg, e, extraData); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void LogWarning(string msg) + { + Svelto.Console.LogWarning(msg); + } + + /// + /// Write a warning message to Cardlife's log + /// + /// The object to log + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void LogWarning(object obj) + { + Svelto.Console.LogWarning(obj.ToString()); + } + + // descriptive logging + + /// + /// Write a descriptive message to Cardlife's log only when the API is a Debug build + /// + /// The object to log + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MetaDebugLog(object obj) + { +#if DEBUG + MetaLog($"[MetaDebug]{obj.ToString()}"); +#endif + } + + /// + /// Write a descriptive message to Cardlife's log including the calling method's name + /// + /// The object to log + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void MetaLog(object obj) + { + var method = (new StackTrace()).GetFrame(1).GetMethod(); + Log($"[{method.DeclaringType.FullName}.{method.Name}] {obj.ToString()}"); + } + } +} diff --git a/CLre/MyPlugin.cs b/CLre/CLre.cs similarity index 63% rename from CLre/MyPlugin.cs rename to CLre/CLre.cs index 129e69f..ca7866d 100644 --- a/CLre/MyPlugin.cs +++ b/CLre/CLre.cs @@ -3,9 +3,12 @@ using System.Reflection; using IllusionPlugin; -namespace HelloModdingWorld +using CLre.API.Utility; +using Logging = CLre.API.Utility.Logging; + +namespace CLre { - public class MyPlugin : IEnhancedPlugin // the Illusion Plugin Architecture (IPA) will ignore classes that don't implement IPlugin' + public class CLre : IEnhancedPlugin // the Illusion Plugin Architecture (IPA) will ignore classes that don't implement IPlugin' { public override string Name { get; } = Assembly.GetExecutingAssembly().GetName().Name; @@ -19,7 +22,7 @@ namespace HelloModdingWorld // called when Cardlife starts up public override void OnApplicationStart() { - File.WriteAllText(Name + ".log", "CLre was loaded and started up"); + Logging.MetaLog($"{Name} has been loaded."); } } } diff --git a/CLre/Fixes/EnchantmentTableFloatParseFix.cs b/CLre/Fixes/EnchantmentTableFloatParseFix.cs new file mode 100644 index 0000000..f9a3315 --- /dev/null +++ b/CLre/Fixes/EnchantmentTableFloatParseFix.cs @@ -0,0 +1,7 @@ +namespace CLre.Fixes +{ + public class EnchantmentTableFloatParseFix + { + + } +} \ No newline at end of file