diff --git a/Build/InstallerExtensions.dll b/Build/InstallerExtensions.dll index 0a6c6513..6c0d6fa5 100644 Binary files a/Build/InstallerExtensions.dll and b/Build/InstallerExtensions.dll differ diff --git a/Build/QModInstaller.dll b/Build/QModInstaller.dll index 215a5e1f..25a60828 100644 Binary files a/Build/QModInstaller.dll and b/Build/QModInstaller.dll differ diff --git a/Build/QModInstaller.xml b/Build/QModInstaller.xml index c23d3d51..b9b789df 100644 --- a/Build/QModInstaller.xml +++ b/Build/QModInstaller.xml @@ -457,6 +457,41 @@ The color of the text. Whether or not to apply formatting tags to the message, or show it as it is. + + A simple logging class. Can be used for basic logging or to know which logging level is enabled. + + + Possible logging levels. + + + Debugging log level + + + Informational log level + + + Warning log level + + + Error log level + + + Fatal log level + + + Used to know if debug logging is enabled or not. + + + + This function will log given message and/or exception. It can optionally show the message on screen. + You need to provide a message and/or an exception (this function will do nothing if both are set to null). + Warning: You can call this function from any mod but don't call it from QModManager ( would fail). + + The level of the log. + Optional: The message that needs to be logged. + Optional: The exception that needs to be logged. + Optional: Whether to show the message on screen or not. + Container class for the entry point diff --git a/Build/QModManager.exe b/Build/QModManager.exe index 82cf530d..5189fa30 100644 Binary files a/Build/QModManager.exe and b/Build/QModManager.exe differ diff --git a/Build/QModManager_Setup.exe b/Build/QModManager_Setup.exe index 436f6fb6..cb4d5763 100644 Binary files a/Build/QModManager_Setup.exe and b/Build/QModManager_Setup.exe differ diff --git a/Data/latest-version.txt b/Data/latest-version.txt index c2c147f2..ec285fb6 100644 --- a/Data/latest-version.txt +++ b/Data/latest-version.txt @@ -1 +1 @@ -3.2.1.0 +3.3.0.0 diff --git a/Executable/Properties/AssemblyInfo.cs b/Executable/Properties/AssemblyInfo.cs index 9af66fa4..48316489 100644 --- a/Executable/Properties/AssemblyInfo.cs +++ b/Executable/Properties/AssemblyInfo.cs @@ -12,5 +12,5 @@ [assembly: ComVisible(false)] -[assembly: AssemblyVersion("3.2.1.0")] -[assembly: AssemblyFileVersion("3.2.1.0")] +[assembly: AssemblyVersion("3.3.0.0")] +[assembly: AssemblyFileVersion("3.3.0.0")] diff --git a/Installer/Properties/AssemblyInfo.cs b/Installer/Properties/AssemblyInfo.cs index b54c9192..4fcf20da 100644 --- a/Installer/Properties/AssemblyInfo.cs +++ b/Installer/Properties/AssemblyInfo.cs @@ -14,5 +14,5 @@ [assembly: Guid("8c6c9a0b-80c4-43d2-89f2-749e6f09fdda")] -[assembly: AssemblyVersion("3.2.1.0")] -[assembly: AssemblyFileVersion("3.2.1.0")] +[assembly: AssemblyVersion("3.3.0.0")] +[assembly: AssemblyFileVersion("3.3.0.0")] diff --git a/Installer/QModsInstallerScript.iss b/Installer/QModsInstallerScript.iss index 802759cf..e3739f08 100644 --- a/Installer/QModsInstallerScript.iss +++ b/Installer/QModsInstallerScript.iss @@ -5,7 +5,7 @@ #endif #define Name "QModManager" ; The name of the game will be added after it -#define Version "3.2.1" +#define Version "3.3.0" #define Author "QModManager" #define URL "https://github.com/QModManager/QModManager" #define SupportURL "https://discord.gg/UpWuWwq" diff --git a/QModManager/Checks/NitroxCheck.cs b/QModManager/Checks/NitroxCheck.cs index 43457487..111ab3d7 100644 --- a/QModManager/Checks/NitroxCheck.cs +++ b/QModManager/Checks/NitroxCheck.cs @@ -9,7 +9,7 @@ internal static class NitroxCheck { internal static bool IsInstalled { get; set; } = false; - [HarmonyPatch(typeof(GameInput), "Awake")] + [HarmonyPatch(typeof(GameInput), nameof(GameInput.Awake))] internal static class AwakePatch { internal static IEnumerable Transpiler(IEnumerable instructions) diff --git a/QModManager/HarmonyPatches/EnableConsoleSetting.cs b/QModManager/HarmonyPatches/EnableConsoleSetting.cs index de1d0119..6482db63 100644 --- a/QModManager/HarmonyPatches/EnableConsoleSetting.cs +++ b/QModManager/HarmonyPatches/EnableConsoleSetting.cs @@ -2,9 +2,6 @@ { using Harmony; using QModManager.Utility; - using System.Collections.Generic; - using System.Reflection; - using System.Reflection.Emit; using UnityEngine; [HarmonyPatch(typeof(DevConsole), nameof(DevConsole.Awake))] @@ -26,13 +23,12 @@ internal static class PlayerPrefsUtils_PrefsToggle_Patch // This patch syncronizes the "Disable console" UI element in the F3 debug menu [HarmonyPostfix] - public static void Postfix(bool __result, string key) + public static void Postfix(bool defaultVal, string key, string label, ref bool __result) { - if (key != "UWE.DisableConsole") return; - - Config.EnableConsole = !__result; - - return; + if (key == "UWE.DisableConsole") + { + Config.EnableConsole = !__result; + } } } } diff --git a/QModManager/OptionsManager.cs b/QModManager/OptionsManager.cs index 371196b9..9a458f2b 100644 --- a/QModManager/OptionsManager.cs +++ b/QModManager/OptionsManager.cs @@ -8,9 +8,7 @@ internal static class OptionsManager { internal static int ModsTab; - - - [HarmonyPatch(typeof(uGUI_OptionsPanel), "AddTabs")] + [HarmonyPatch(typeof(uGUI_OptionsPanel), nameof(uGUI_OptionsPanel.AddTabs))] internal static class OptionsPatch { [HarmonyPostfix] diff --git a/QModManager/Patching/ManifestValidator.cs b/QModManager/Patching/ManifestValidator.cs index 64d67ac4..0215f663 100644 --- a/QModManager/Patching/ManifestValidator.cs +++ b/QModManager/Patching/ManifestValidator.cs @@ -144,6 +144,8 @@ public void CheckRequiredMods(QMod mod) { versionedDependencies.Add(new RequiredQMod(item.Key)); } + + mod.RequiredDependencies.Add(item.Key); } mod.RequiredMods = versionedDependencies; diff --git a/QModManager/Patching/Patcher.cs b/QModManager/Patching/Patcher.cs index 8880367a..fa8328f8 100644 --- a/QModManager/Patching/Patcher.cs +++ b/QModManager/Patching/Patcher.cs @@ -41,7 +41,7 @@ internal static void Patch() Patched = true; - Logger.Info("Game Version: " + SNUtils.GetPlasticChangeSetOfBuild() + " Build Date: " + SNUtils.GetDateTimeOfBuild().ToLongDateString()); + Logger.Info($"Game Version: {SNUtils.GetPlasticChangeSetOfBuild()} Build Date: {SNUtils.GetDateTimeOfBuild():dd-MMMM-yyyy}"); Logger.Info($"Loading QModManager v{Assembly.GetExecutingAssembly().GetName().Version.ToStringParsed()}..."); Logger.Info($"Today is {DateTime.Today:dd-MMMM-yyyy}"); @@ -64,7 +64,7 @@ internal static void Patch() try { - Logger.Info($"Folder structure:\n{IOUtilities.GetFolderStructureAsTree()}\n"); + Logger.Info($"Folder structure:{IOUtilities.GetFolderStructureAsTree()}"); } catch (Exception e) { @@ -81,15 +81,7 @@ internal static void Patch() CurrentlyRunningGame = gameDetector.CurrentlyRunningGame; - try - { - PatchHarmony(); - } - catch (Exception e) - { - Logger.Error("There was an error while trying to apply Harmony patches."); - Logger.Exception(e); - } + PatchHarmony(); if (NitroxCheck.IsInstalled) { @@ -171,9 +163,19 @@ private static void AddAssemblyResolveEvent() private static void PatchHarmony() { - Logger.Debug("Applying Harmony patches..."); - HarmonyInstance.Create("qmodmanager").PatchAll(); - Logger.Debug("Patched!"); + try + { + Logger.Debug("Applying Harmony patches..."); + + HarmonyInstance.Create("qmodmanager").PatchAll(); + + Logger.Debug("Patched!"); + } + catch (Exception e) + { + Logger.Error("There was an error while trying to apply Harmony patches."); + Logger.Exception(e); + } } } } diff --git a/QModManager/Patching/QMod.cs b/QModManager/Patching/QMod.cs index fec08845..18fbd613 100644 --- a/QModManager/Patching/QMod.cs +++ b/QModManager/Patching/QMod.cs @@ -19,40 +19,40 @@ public QMod() // Empty public constructor for JSON } - [JsonProperty(Required = Required.Always)] + [JsonProperty] public string Id { get; set; } - [JsonProperty(Required = Required.Always)] + [JsonProperty] public string DisplayName { get; set; } - [JsonProperty(Required = Required.Always)] + [JsonProperty] public string Author { get; set; } - [JsonProperty(Required = Required.Always)] + [JsonProperty] public string Version { get; set; } - [JsonProperty(Required = Required.Default)] + [JsonProperty] public string[] Dependencies { get; set; } = new string[0]; - [JsonProperty(Required = Required.Default)] + [JsonProperty] public Dictionary VersionDependencies { get; set; } = new Dictionary(); - [JsonProperty(Required = Required.Default)] + [JsonProperty] public string[] LoadBefore { get; set; } = new string[0]; - [JsonProperty(Required = Required.Default)] + [JsonProperty] public string[] LoadAfter { get; set; } = new string[0]; - [JsonProperty(Required = Required.DisallowNull, DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + [JsonProperty] public bool Enable { get; set; } = true; - [JsonProperty(Required = Required.DisallowNull)] + [JsonProperty] public string Game { get; set; } = $"{QModGame.Subnautica}"; - [JsonProperty(Required = Required.Always)] + [JsonProperty] public string AssemblyName { get; set; } - [JsonProperty(Required = Required.Default)] + [JsonProperty] public string EntryMethod { get; set; } #endregion diff --git a/QModManager/Patching/QModFactory.cs b/QModManager/Patching/QModFactory.cs index 2e63b91b..fc0dc9d0 100644 --- a/QModManager/Patching/QModFactory.cs +++ b/QModManager/Patching/QModFactory.cs @@ -165,14 +165,17 @@ private static QMod CreateFromJsonManifestFile(string subDirectory) try { - var settings = new JsonSerializerSettings + var deserializer = new JsonSerializer { + NullValueHandling = NullValueHandling.Ignore, MissingMemberHandling = MissingMemberHandling.Ignore }; string jsonText = File.ReadAllText(jsonFile); - QMod mod = JsonConvert.DeserializeObject(jsonText); + using StreamReader sr = new StreamReader(jsonFile); + using JsonReader reader = new JsonTextReader(sr); + QMod mod = deserializer.Deserialize(reader); mod.SubDirectory = subDirectory; diff --git a/QModManager/Properties/AssemblyInfo.cs b/QModManager/Properties/AssemblyInfo.cs index 95f310b7..4bc431ac 100644 --- a/QModManager/Properties/AssemblyInfo.cs +++ b/QModManager/Properties/AssemblyInfo.cs @@ -13,8 +13,8 @@ [assembly: ComVisible(false)] -[assembly: AssemblyVersion("3.2.1.0")] -[assembly: AssemblyFileVersion("3.2.1.0")] +[assembly: AssemblyVersion("3.3.0.0")] +[assembly: AssemblyFileVersion("3.3.0.0")] [assembly: InternalsVisibleTo("QMMTests")] [assembly: InternalsVisibleTo("QModManager")] diff --git a/QModManager/Utility/Config.cs b/QModManager/Utility/Config.cs index 8dfa78f3..c46325ef 100644 --- a/QModManager/Utility/Config.cs +++ b/QModManager/Utility/Config.cs @@ -35,18 +35,30 @@ internal static bool EnableDevMode private static Dictionary Cfg = new Dictionary(); private static bool Loaded = false; + private static readonly JsonSerializer serializer = new JsonSerializer + { + NullValueHandling = NullValueHandling.Ignore, + MissingMemberHandling = MissingMemberHandling.Ignore, + DefaultValueHandling = DefaultValueHandling.Ignore + }; - private static void Load() + private static void Load() { try { - if (!File.Exists(ConfigPath)) File.WriteAllText(ConfigPath, "{}"); - string text = File.ReadAllText(ConfigPath); - Cfg = JsonConvert.DeserializeObject>(text); + if (!File.Exists(ConfigPath)) + { + Save(); + } + + using StreamReader sr = new StreamReader(ConfigPath); + using JsonReader reader = new JsonTextReader(sr); + Cfg = serializer.Deserialize>(reader); + if (Cfg == null) { - File.WriteAllText(ConfigPath, "{}"); Cfg = new Dictionary(); + Save(); } Loaded = true; @@ -62,8 +74,9 @@ private static void Save() { try { - string text = JsonConvert.SerializeObject(Cfg, Formatting.Indented); - File.WriteAllText(ConfigPath, text); + using StreamWriter sw = new StreamWriter(ConfigPath); + using JsonWriter writer = new JsonTextWriter(sw); + serializer.Serialize(writer, Cfg); } catch (Exception e) { @@ -74,7 +87,10 @@ private static void Save() private static T Get(string field, T def = default) { - if (!Loaded) Load(); + if (!Loaded) + { + Load(); + } if (!Cfg.TryGetValue(field, out object value)) return def; diff --git a/QModManager/Utility/IOUtilities.cs b/QModManager/Utility/IOUtilities.cs index 95ae1b41..c75d1a52 100644 --- a/QModManager/Utility/IOUtilities.cs +++ b/QModManager/Utility/IOUtilities.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Text; namespace QModManager.Utility { @@ -16,7 +17,9 @@ internal static class IOUtilities "SNAppData", "SNUnmanagedData", "Subnautica_Data", + "SubnauticaZero_Data", "_CommonRedist", + "steam_shader_cache", }; internal static string GetFolderStructureAsTree(string directory = null) @@ -25,7 +28,7 @@ internal static string GetFolderStructureAsTree(string directory = null) { directory ??= Environment.CurrentDirectory; - return GenerateFolderStructure(directory) + "\n"; + return GenerateFolderStructure(directory); } catch (Exception e) { @@ -35,13 +38,15 @@ internal static string GetFolderStructureAsTree(string directory = null) internal static string GenerateFolderStructure(string directory) { + var builder = new StringBuilder(); try { - string toWrite = $"+ {new DirectoryInfo(directory).Name}\n"; + builder.AppendLine(); + builder.AppendLine($"+ {new DirectoryInfo(directory).Name}"); foreach (string dir in Directory.GetDirectories(directory)) { - toWrite += GetFolderStructureRecursively(dir, 0); + GetFolderStructureRecursively(builder, dir, 0); } string[] files = Directory.GetFiles(directory); @@ -49,34 +54,35 @@ internal static string GenerateFolderStructure(string directory) { FileInfo fileinfo = new FileInfo(files[i - 1]); if (i != files.Length) - toWrite += $"{GenerateSpaces(0)}|---- {fileinfo.Name} ({ParseSize(fileinfo.Length)})\n"; - else - toWrite += $"{GenerateSpaces(0)}`---- {fileinfo.Name} ({ParseSize(fileinfo.Length)})\n"; + builder.AppendLine($"{GenerateSpaces(0)}|---- {fileinfo.Name} ({ParseSize(fileinfo.Length)})"); + else + builder.AppendLine($"{GenerateSpaces(0)}|---- {fileinfo.Name} ({ParseSize(fileinfo.Length)})"); } - return toWrite; + builder.AppendLine(); + return builder.ToString(); } catch (Exception e) { throw e; } } - internal static string GetFolderStructureRecursively(string directory, int spaces = 0) + internal static void GetFolderStructureRecursively(StringBuilder builder, string directory, int spaces = 0) { try { DirectoryInfo dirInfo = new DirectoryInfo(directory); - string toWrite = $"{GenerateSpaces(spaces)}|---+ {dirInfo.Name}\n"; + builder.AppendLine($"{GenerateSpaces(spaces)}|---+ {dirInfo.Name}"); if (BannedFolders.Contains(dirInfo.Name) || BannedFolders.Contains($"{dirInfo.Parent.Name}/{dirInfo.Name}")) { - toWrite += $"{GenerateSpaces(spaces + 4)}`---- (Folder content not shown)\n"; - return toWrite; + builder.AppendLine($"{GenerateSpaces(spaces + 4)}`---- (Folder content not shown)"); + return; } foreach (string dir in Directory.GetDirectories(directory)) { - toWrite += GetFolderStructureRecursively(dir, spaces + 4); + GetFolderStructureRecursively(builder, dir, spaces + 4); } string[] files = Directory.GetFiles(directory); @@ -84,12 +90,10 @@ internal static string GetFolderStructureRecursively(string directory, int space { FileInfo fileinfo = new FileInfo(files[i - 1]); if (i != files.Length) - toWrite += $"{GenerateSpaces(spaces + 4)}|---- {fileinfo.Name} ({ParseSize(fileinfo.Length)})\n"; + builder.AppendLine($"{GenerateSpaces(spaces + 4)}|---- {fileinfo.Name} ({ParseSize(fileinfo.Length)})"); else - toWrite += $"{GenerateSpaces(spaces + 4)}`---- {fileinfo.Name} ({ParseSize(fileinfo.Length)})\n"; + builder.AppendLine($"{GenerateSpaces(spaces + 4)}`---- {fileinfo.Name} ({ParseSize(fileinfo.Length)})"); } - - return toWrite; } catch (Exception e) { diff --git a/QModManager/Utility/Logger.cs b/QModManager/Utility/Logger.cs index 2a94821b..dae2ba21 100644 --- a/QModManager/Utility/Logger.cs +++ b/QModManager/Utility/Logger.cs @@ -1,127 +1,171 @@ namespace QModManager.Utility { using System; - using System.Diagnostics; - using System.IO; - internal static class Logger + /// A simple logging class. Can be used for basic logging or to know which logging level is enabled. + public static class Logger { - internal enum Level + /// Possible logging levels. + public enum Level { + /// Debugging log level Debug, + /// Informational log level Info, + /// Warning log level Warn, + /// Error log level Error, + /// Fatal log level Fatal } - private static void Log(string logLevel, params string[] text) + private const string AssemblyName = "QModManager"; + + #region Private functions (used by the Logger) + + private static string GetCallingAssemblyName() => ReflectionHelper.CallingAssemblyByStackTrace()?.GetName().Name; + + private static void Debug(string msg, bool showOnScreen = false, string callingAssembly = null, bool force = false) { - if (text == null || text.Length < 1) + if (!force && !Config.EnableDebugLogs) return; - string from; - Type classType = GetCallingClass(); + Console.WriteLine($"[{callingAssembly ?? GetCallingAssemblyName()}:DEBUG] {msg}"); - if (classType == null) - from = null; - else if (classType.Namespace.Contains("SMLHelper")) - from = "SMLHelper"; - else - from = classType.Name; + if (showOnScreen) + ErrorMessage.AddDebug(msg); + } - string toWrite = "[QModManager] "; - if (!string.IsNullOrEmpty(from) && !from.Contains("<>")) - toWrite += $"[{from}] "; - else if (!string.IsNullOrEmpty(from) && from.Contains("<>")) - toWrite += $"[Anonymous] "; - if (!string.IsNullOrEmpty(logLevel)) - toWrite += $"[{logLevel}] "; + private static void Info(string msg, bool showOnScreen = false, string callingAssembly = null) + { + Console.WriteLine($"[{callingAssembly ?? GetCallingAssemblyName()}:INFO] {msg}"); - int length = toWrite.Length; + if (showOnScreen) + ErrorMessage.AddMessage(msg); + } - Console.WriteLine($"{toWrite}{text[0]}"); + private static void Warn(string msg, bool showOnScreen = false, string callingAssembly = null) + { + Console.WriteLine($"[{callingAssembly ?? GetCallingAssemblyName()}:WARN] {msg}"); - for (int i = 1; i < text.Length; i++) - Console.WriteLine($"{text[i]}"); + if (showOnScreen) + ErrorMessage.AddWarning(msg); } - internal static void Log(params string[] text) + private static void Error(string msg = null, Exception ex = null, bool showOnScreen = false, string callingAssembly = null) { - Log("", text); + if (ex != null) + msg = (string.IsNullOrEmpty(msg) ? ex.ToString() : msg + Environment.NewLine + ex.ToString()); + + Console.WriteLine($"[{callingAssembly ?? GetCallingAssemblyName()}:ERROR] {msg}"); + + if (showOnScreen && !string.IsNullOrEmpty(msg)) + ErrorMessage.AddError(msg); } - internal static void Log(Level logLevel, params string[] text) + private static void Exception(Exception e, bool selfAssembly = false) => Error(null, e, false, selfAssembly ? AssemblyName : GetCallingAssemblyName()); + + private static void Fatal(string msg = null, Exception ex = null, bool showOnScreen = false, string callingAssembly = null) + { + if (ex != null) + msg = (string.IsNullOrEmpty(msg) ? ex.ToString() : msg + Environment.NewLine + ex.ToString()); + Console.WriteLine($"[{callingAssembly ?? GetCallingAssemblyName()}:FATAL] {msg}"); + + if (showOnScreen && !string.IsNullOrEmpty(msg)) + ErrorMessage.AddError(msg); + } + + #endregion + + #region Internal functions (used by QModManager) + + internal static void Debug(string msg) => Debug(msg, false, AssemblyName, false); + + internal static void DebugForce(string msg) => Debug(msg, false, AssemblyName, true); + + internal static void Info(string msg) => Info(msg, false, AssemblyName); + + internal static void Warn(string msg) => Warn(msg, false, AssemblyName); + + internal static void Error(string msg) => Error(msg, null, false, AssemblyName); + + internal static void Exception(Exception e) => Exception(e, true); + + internal static void Fatal(string msg) => Fatal(msg, null, false, AssemblyName); + + internal static void Log(Level logLevel, string msg) { + if (msg == null) // Return if there is no messages + return; + switch (logLevel) { case Level.Debug: - Debug(text); - break; - case Level.Info: - Info(text); + Debug(msg); break; case Level.Warn: - Warn(text); + Warn(msg); break; case Level.Error: - Error(text); + Error(msg); break; case Level.Fatal: - Fatal(text); + Fatal(msg); + break; + default: // Defaults to informational logging + Info(msg); break; } } - internal static void Debug(params string[] text) - { - if (Config.EnableDebugLogs) - Log("Debug", text); - } - - internal static void DebugForce(params string[] text) - { - Log("Debug", text); - } - - internal static void Info(params string[] text) - { - Log("Info", text); - } + #endregion - internal static void Warn(params string[] text) - { - Log("Warn", text); - } + #region Public functions (used by mods) - internal static void Error(params string[] text) - { - Log("Error", text); - } - - internal static void Exception(Exception e) - { - Log("Exception", e.ToString()); - } - - internal static void Fatal(params string[] text) - { - Log("Fatal", text); - } + /// Used to know if debug logging is enabled or not. + public static bool DebugLogsEnabled => Config.EnableDebugLogs; - private static Type GetCallingClass() + /// + /// This function will log given message and/or exception. It can optionally show the message on screen. + /// You need to provide a message and/or an exception (this function will do nothing if both are set to null). + /// Warning: You can call this function from any mod but don't call it from QModManager ( would fail). + /// + /// The level of the log. + /// Optional: The message that needs to be logged. + /// Optional: The exception that needs to be logged. + /// Optional: Whether to show the message on screen or not. + public static void Log(Level logLevel, string msg = null, Exception ex = null, bool showOnScreen = false) { - var stackTrace = new StackTrace(); - StackFrame[] frames = stackTrace.GetFrames(); - - foreach (StackFrame stackFrame in frames) + if (ex != null) { - Type declaringClass = stackFrame.GetMethod().DeclaringType; - if (declaringClass != typeof(Logger)) - return declaringClass; + // If exception was provided, concatenate its message to the log message + if (logLevel != Level.Error && logLevel != Level.Fatal) + msg = (msg == null) ? ex.Message : msg + Environment.NewLine + ex.Message; } + else if (msg == null) // Return if both given message and exception were null + return; - return null; + switch (logLevel) + { + case Level.Debug: + Debug(msg, showOnScreen, null, false); + break; + case Level.Warn: + Warn(msg, showOnScreen, null); + break; + case Level.Error: + Error(msg, ex, showOnScreen, null); + break; + case Level.Fatal: + Fatal(msg, ex, showOnScreen, null); + break; + default: // Defaults to informational logging + Info(msg, showOnScreen, null); + break; + } } + + #endregion } } diff --git a/QModManager/Utility/MainMenuMessages.cs b/QModManager/Utility/MainMenuMessages.cs index 1a20e538..18e79462 100644 --- a/QModManager/Utility/MainMenuMessages.cs +++ b/QModManager/Utility/MainMenuMessages.cs @@ -1,10 +1,10 @@ namespace QModManager.Utility { + using System; using System.Reflection; using System.Reflection.Emit; using System.Collections; using System.Collections.Generic; - using Harmony; using UnityEngine; using UnityEngine.SceneManagement; @@ -63,6 +63,7 @@ private static void Init() if (inited) return; + LoadDynamicAssembly(); messageQueue = new List(); messages = new List(); Patches.Patch(); @@ -77,7 +78,8 @@ private static void AddInternal(string msg) var message = ErrorMessage.main.GetExistingMessage(msg); messages.Add(message); message.timeEnd += 1e6f; - message.entry.rectTransform.sizeDelta = new Vector2(1920f - ErrorMessage.main.offset.x * 2f, 0f); + + GetRectTransform(message).sizeDelta = new Vector2(1920f - ErrorMessage.main.offset.x * 2f, 0f); } private static void OnSceneLoaded(Scene scene, LoadSceneMode _) @@ -98,8 +100,10 @@ static IEnumerator _waitForLoad() messages.ForEach(msg => msg.timeEnd = Time.time + 1f); yield return new WaitForSeconds(1.1f); // wait for messages to dissapear - Vector2 originalSize = ErrorMessage.main.prefabMessage.rectTransform.sizeDelta; - messages.ForEach(msg => msg.entry.rectTransform.sizeDelta = originalSize); + Vector2 originalSize = GetRectTransform(ErrorMessage.main.prefabMessage.GetComponent(SelectedTextType)).sizeDelta; + + messages.ForEach(msg => GetRectTransform(msg).sizeDelta = originalSize); + messages.Clear(); Patches.Unpatch(); @@ -109,6 +113,48 @@ static IEnumerator _waitForLoad() } } + #region Dynamic assembly loading + + private static Type SelectedTextType; + private static Func GetRectTransform; + + private static void LoadDynamicAssembly() + { + if (SelectedTextType == null) + { + Type TxtType = typeof(UnityEngine.UI.Text); + Type TxtProType = Type.GetType("TMPro.TextMeshProUGUI, Unity.TextMeshPro", false, false); + + SelectedTextType = TxtProType ?? TxtType; + + FieldInfo entryField = SelectedTextType.GetField("entry"); + if (TxtProType != null) + { + // Using TextMeshPro + FieldInfo recTransformField = TxtProType.GetField("m_rectTransform"); + + GetRectTransform = (obj) => + { + var entry = entryField.GetValue(obj); + return (RectTransform)recTransformField.GetValue(obj); + }; + } + else + { + // Using Text + PropertyInfo recTransformProperty = TxtType.GetProperty("rectTransform"); + + GetRectTransform = (obj) => + { + var entry = entryField.GetValue(obj); + return (RectTransform)recTransformProperty.GetValue(obj, null); + }; + } + } + } + + #endregion Dynamic assembly loading + private static class Patches { public static HarmonyInstance hInstance { get; private set; } @@ -157,7 +203,7 @@ public static void Postfix() } } - private static float _getVal(float val, ErrorMessage._Message message) => messages.Contains(message)? 1f: val; + private static float _getVal(float val, ErrorMessage._Message message) => messages.Contains(message) ? 1f : val; // we changing result for 'float value = Mathf.Clamp01(MathExtensions.EvaluateLine(...' to 1.0f // so text don't stay in the center of the screen (because of changed 'timeEnd')