diff --git a/1.3/Assemblies/BetterLoading.dll b/1.3/Assemblies/BetterLoading.dll index c113504..512f35e 100644 Binary files a/1.3/Assemblies/BetterLoading.dll and b/1.3/Assemblies/BetterLoading.dll differ diff --git a/1.3/Assemblies/Tomlet.dll b/1.3/Assemblies/Tomlet.dll new file mode 100644 index 0000000..a2dfea7 Binary files /dev/null and b/1.3/Assemblies/Tomlet.dll differ diff --git a/About/About.xml b/About/About.xml index e16671d..3e5ddd9 100755 --- a/About/About.xml +++ b/About/About.xml @@ -3,7 +3,7 @@ BetterLoading Samboy063 me.samboycoding.betterloading.dev - + https://github.com/SamboyCoding/RimworldBetterLoading
  • 1.0
  • 1.1
  • diff --git a/About/Manifest.xml b/About/Manifest.xml index 077d643..829f743 100755 --- a/About/Manifest.xml +++ b/About/Manifest.xml @@ -1,7 +1,7 @@ BetterLoading - 3.2.0.1 + 3.3.0.0
  • Core >= 1.0
  • Startupimpact
  • diff --git a/README.md b/README.md index d7a4e4b..a23bbfe 100755 --- a/README.md +++ b/README.md @@ -1,5 +1,11 @@ -# RimworldBetterLoading +# BetterLoading +## A mod to make RimWorld loading screens look nice See the [steam workshop](https://steamcommunity.com/sharedfiles/filedetails/?id=1999454301) page for a full description. -If you encounter issues, feel free to @ me on the Rimworld discord (my name there is `Samboy [BetterLoading]`) or DM me (Samboy#0063) - I do want to know of them, and will fix them ASAP. +If you encounter issues, I would really appreciate knowing about them. You can reach me via several methods: +- I have a [dedicated discord server](https://discord.gg/https://discord.gg/3d8xvnBJgX) for my code projects, including BetterLoading. +- I'm in the [official RimWorld discord](https://discord.gg/rimworld) as `Samboy [BetterLoading]`, and you can mention me in the mod-general channel. +- You can DM me (Samboy#0063) on discord, I accept random friend requests. +- You can leave a comment on the steam workshop page (though these don't get checked quite as often) +- You can open an issue here on github, which will send a message to my discord server. diff --git a/Source/BetterLoading.csproj b/Source/BetterLoading.csproj index bafcf52..c87506c 100755 --- a/Source/BetterLoading.csproj +++ b/Source/BetterLoading.csproj @@ -3,8 +3,8 @@ TargetFramework Library net48 - 8 - annotations + 10 + enable false @@ -45,5 +45,6 @@ + \ No newline at end of file diff --git a/Source/BetterLoading.sln b/Source/BetterLoading.sln index 60e100f..37b2f27 100644 --- a/Source/BetterLoading.sln +++ b/Source/BetterLoading.sln @@ -5,6 +5,15 @@ VisualStudioVersion = 16.0.30011.22 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BetterLoading", "BetterLoading.csproj", "{E08CFC22-1BA8-41FE-A60B-491C308B8B65}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{46226B08-22BA-455E-8B99-F496E90EDCBC}" + ProjectSection(SolutionItems) = preProject + .gitignore = ..\.gitignore + README.md = ..\README.md + LoadFolders.xml = ..\LoadFolders.xml + About\About.xml = ..\About\About.xml + About\Manifest.xml = ..\About\Manifest.xml + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU diff --git a/Source/BetterLoadingConfig.cs b/Source/BetterLoadingConfig.cs new file mode 100644 index 0000000..0de9c36 --- /dev/null +++ b/Source/BetterLoadingConfig.cs @@ -0,0 +1,33 @@ +using System; +using Tomlet.Attributes; + +namespace BetterLoading; + +public class BetterLoadingConfig +{ + + public static BetterLoadingConfig CreateDefault() + { + return new() + { + TipCache = new() + { + Version = TipCacheConfig.SupportedVersion, + } + }; + } + + [TomlPrecedingComment("The TipCache caches information about loading screen tips so that they can be displayed as soon as the loading screen starts after the first run.")] + public TipCacheConfig TipCache; + + [TomlDoNotInlineObject] + public class TipCacheConfig + { + public static readonly int SupportedVersion = 1; + + [TomlPrecedingComment("The internal version number of the TipCache tip blob. If this number is different from the one expected by the mod, the TipCache will be cleared.")] + public int Version; + [TomlPrecedingComment("The raw tip blob. NOT intended to be manually edited.")] + public byte[] Tips = Array.Empty(); + } +} \ No newline at end of file diff --git a/Source/BetterLoadingConfigManager.cs b/Source/BetterLoadingConfigManager.cs new file mode 100644 index 0000000..a031e83 --- /dev/null +++ b/Source/BetterLoadingConfigManager.cs @@ -0,0 +1,61 @@ +using System; +using System.IO; +using Tomlet; +using Tomlet.Exceptions; +using Tomlet.Models; +using Verse; + +namespace BetterLoading; + +/// +/// This class exists because XML is the spawn of the devil and I refuse to use it for config files. +/// +public static class BetterLoadingConfigManager +{ + private static string _oldCachedLoadingTipsPath = Path.Combine(GenFilePaths.ConfigFolderPath, "BetterLoading_Cached_Tips"); + public static string ConfigFilePath = Path.Combine(GenFilePaths.ConfigFolderPath, "BetterLoading.toml"); + + public static BetterLoadingConfig Config { get; private set; } = new(); + + static BetterLoadingConfigManager() + { + //Register a byte array <=> base64 string converter + TomletMain.RegisterMapper(bytes => new TomlString(Convert.ToBase64String(bytes ?? throw new NullReferenceException("Cannot serialize a null byte array"))), tomlValue => + { + if (tomlValue is not TomlString tomlString) + throw new TomlTypeMismatchException(typeof(TomlString), tomlValue.GetType(), typeof(byte[])); + return Convert.FromBase64String(tomlString.Value); + }); + } + + public static void Load() + { + if(File.Exists(_oldCachedLoadingTipsPath)) + File.Delete(_oldCachedLoadingTipsPath); + + if (!File.Exists(ConfigFilePath)) + { + Config = BetterLoadingConfig.CreateDefault(); + return; + } + + try + { + var doc = TomlParser.ParseFile(ConfigFilePath); + Config = TomletMain.To(doc); + LoadingScreenTipManager.TryReadCachedTipsFromConfig(); + } + catch (TomlException e) + { + Log.Error($"[BetterLoading] {e.GetType()} thrown while parsing config file: {e.Message}. Config will be reset."); + File.Delete(ConfigFilePath); + Config = BetterLoadingConfig.CreateDefault(); + } + } + + public static void Save() + { + var tomlString = TomletMain.TomlStringFrom(Config); + File.WriteAllText(ConfigFilePath, tomlString); + } +} \ No newline at end of file diff --git a/Source/BetterLoadingMain.cs b/Source/BetterLoadingMain.cs index 4288152..2d7fe16 100755 --- a/Source/BetterLoadingMain.cs +++ b/Source/BetterLoadingMain.cs @@ -3,11 +3,10 @@ using System.IO; using System.Linq; using System.Reflection; +using BetterLoading.Compat; using BetterLoading.Stage.SaveLoad; using HarmonyLib; -using JetBrains.Annotations; using RimWorld; -using RimWorld.Planet; using UnityEngine; using Verse; using Object = UnityEngine.Object; @@ -17,10 +16,10 @@ namespace BetterLoading public sealed class BetterLoadingMain : Mod { public static ModContentPack? ourContentPack; - public static Harmony? hInstance; + public static Harmony hInstance = new("me.samboycoding.blm"); public static LoadingScreen? LoadingScreen; - public static readonly Dictionary> DllPathsThatFailedToLoad = new Dictionary>(); + public static readonly Dictionary> DllPathsThatFailedToLoad = new(); public class DllLoadError { @@ -32,10 +31,9 @@ public BetterLoadingMain(ModContentPack content) : base(content) { ourContentPack = content; - hInstance = new Harmony("me.samboycoding.blm"); if (Camera.main == null) return; //Just in case - - hInstance.Patch(AccessTools.Method(typeof(PlayDataLoader), nameof(PlayDataLoader.ClearAllPlayData)), new HarmonyMethod(typeof(BetterLoadingMain), nameof(OnClearPlayData))); + + hInstance.Patch(AccessTools.Method(typeof(PlayDataLoader), nameof(PlayDataLoader.ClearAllPlayData)), new(typeof(BetterLoadingMain), nameof(OnClearPlayData))); LogMsg("[BetterLoading] Verifying all mods loaded properly..."); @@ -73,7 +71,7 @@ public BetterLoadingMain(ModContentPack content) : base(content) ) .ToList(); - Log.Error($"[BetterLoading] {dllsThatShouldBeLoaded.Count - dllsActuallyLoaded.Count} assemblies for {pack.Name} failed to load! The ones that didn't load are: {string.Join(", ",didntLoad)}"); + Log.Error($"[BetterLoading] {dllsThatShouldBeLoaded.Count - dllsActuallyLoaded.Count} assemblies for {pack.Name} failed to load! The ones that didn't load are: {string.Join(", ", didntLoad)}"); Log.Error($"[BetterLoading] Got {failures.Count} messages that identify those failures."); DllPathsThatFailedToLoad[pack] = failures; @@ -82,6 +80,9 @@ public BetterLoadingMain(ModContentPack content) : base(content) if (DllPathsThatFailedToLoad.Count == 0) { + BetterLoadingConfigManager.Load(); + ShitRimworldSaysCompat.PatchShitRimworldSaysIfPresent(); + //Prepatcher re-launches the game... var alreadyCreatedLoadScreens = Object.FindObjectsOfType().Where(c => c.GetType().FullName.Contains("LoadingScreen")).ToList(); if (alreadyCreatedLoadScreens.Count > 0) @@ -90,14 +91,15 @@ public BetterLoadingMain(ModContentPack content) : base(content) alreadyCreatedLoadScreens.ForEach(Object.Destroy); LoadingScreen = null; } + Log.Message("[BetterLoading] Injecting into main UI."); LoadingScreen = Object.FindObjectOfType().gameObject.AddComponent(); - InitLoadingScreenBG(); + InitLoadingScreenBackground(); hInstance.Patch(AccessTools.Method(typeof(LongEventHandler), nameof(LongEventHandler.LongEventsOnGUI)), - new HarmonyMethod(typeof(BetterLoadingMain), nameof(DisableVanillaLoadScreen))); + new(typeof(BetterLoadingMain), nameof(DisableVanillaLoadScreen))); - hInstance.Patch(AccessTools.Method(typeof(Game), nameof(Game.LoadGame)), new HarmonyMethod(typeof(BetterLoadingMain), nameof(OnGameLoadStart))); + hInstance.Patch(AccessTools.Method(typeof(Game), nameof(Game.LoadGame)), new(typeof(BetterLoadingMain), nameof(OnGameLoadStart))); BetterLoadingApi.OnGameLoadComplete += CreateTimingReport; } @@ -105,13 +107,13 @@ public BetterLoadingMain(ModContentPack content) : base(content) { Log.Message("[BetterLoading] Not showing loading screen, not all mods loaded successfully so we would be unstable."); - hInstance.Patch(AccessTools.Method(typeof(UIRoot_Entry), nameof(UIRoot_Entry.Init)), postfix: new HarmonyMethod(typeof(BetterLoadingMain), nameof(DisplayFailedLoadDialog))); + hInstance.Patch(AccessTools.Method(typeof(UIRoot_Entry), nameof(UIRoot_Entry.Init)), postfix: new(typeof(BetterLoadingMain), nameof(DisplayFailedLoadDialog))); } //Harmony.PatchAll(Assembly.GetExecutingAssembly()); } - private static void InitLoadingScreenBG() + private static void InitLoadingScreenBackground() { try { @@ -131,7 +133,7 @@ private void CreateTimingReport() var timeRunningCctors = TimeSpan.FromTicks(GlobalTimingData.TicksStartedPostFinalize - GlobalTimingData.TicksStartedCctors); var timeRunningPostFinalize = TimeSpan.FromTicks(GlobalTimingData.TicksFinishedPostFinalize - GlobalTimingData.TicksStartedPostFinalize); var totalLoadTime = TimeSpan.FromTicks(DateTime.UtcNow.Ticks - GlobalTimingData.TicksStarted); - + Log.Message($"[BetterLoading] Game load has finished. Timing data follows:\n" + $"Spent {timeBuildingXml.TotalMilliseconds}ms reading, building, and patching XML tree.\n" + $"Spent {timeConstructingDefs.TotalMilliseconds}ms turning XML into def instances.\n" + @@ -170,7 +172,7 @@ BetterLoading did not display because not all of your modded .dll files (assembl Log.Warning($"[BetterLoading] \t{dllLoadError.dllName}.dll failed to load, but we couldn't work out why. Possibly intentional? For safety reasons, the loading screen will not show."); continue; } - + var loaderErrors = GetLoaderErrors(dllLoadError.reasonMessage.text); if (loaderErrors.Count > 0) { @@ -198,14 +200,14 @@ BetterLoading did not display because not all of your modded .dll files (assembl if (dependentMods.Count > 0) { - Log.Warning($"[BetterLoading] \t{dllLoadError.dllName} appears to have a dependency on these mod(s): {dependentMods.Select(m => m.Name).ToStringSafeEnumerable()}"); + Log.Warning($"[BetterLoading] \t{dllLoadError.dllName} appears to have a dependency on these mod(s): {dependentMods.Select(m => m?.Name).ToStringSafeEnumerable()}"); - var notLoaded = dependentMods.Where(requiredMod => LoadedModManager.RunningMods.All(runningMod => runningMod.Name != requiredMod.Name)).ToList(); + var notLoaded = dependentMods.Where(requiredMod => LoadedModManager.RunningMods.All(runningMod => runningMod.Name != requiredMod?.Name)).ToList(); if (notLoaded.Count > 0) - notLoaded.ForEach(m => Log.Warning($"[BetterLoading] \t{modThatFailedLoad.Name} depends on {m.Name} which is not enabled, so it didn't load properly.")); + notLoaded.ForEach(m => Log.Warning($"[BetterLoading] \t{modThatFailedLoad.Name} depends on {m?.Name} which is not enabled, so it didn't load properly.")); var modsLoadedAfterTarget = LoadedModManager.RunningMods.Skip(LoadedModManager.RunningModsListForReading.FindIndex(i => i.Name == modThatFailedLoad.Name)).Take(int.MaxValue).ToList(); - var depsLoadedAfterDependent = modsLoadedAfterTarget.Where(loadedAfter => dependentMods.Any(dep => dep.Name == loadedAfter.Name)).ToList(); + var depsLoadedAfterDependent = modsLoadedAfterTarget.Where(loadedAfter => dependentMods.Any(dep => dep?.Name == loadedAfter.Name)).ToList(); if (depsLoadedAfterDependent.Count > 0) depsLoadedAfterDependent.ForEach(m => Log.Warning($"[BetterLoading] \t{modThatFailedLoad.Name} is loaded before {m.Name} but depends on it, so must be loaded after. It didn't load properly because of this.")); } @@ -216,7 +218,7 @@ BetterLoading did not display because not all of your modded .dll files (assembl .Where(asm => ModLister.AllInstalledMods.All(m => !ModContainsAssembly(m, asm))) .Select(asm => $"{asm}.dll") .ToList(); - + Log.Warning($"[BetterLoading] \t{dllLoadError.dllName} (also) depends on these DLL(s) which couldn't be found in any installed mods: {notInAnyMods.ToStringSafeEnumerable()}"); } } @@ -231,9 +233,9 @@ private static bool ModContainsAssembly(ModMetaData mod, string assemblyName) var searchPaths = new List(); //Sourced from ModContentPack#InitLoadFolders - if (mod.LoadFoldersForVersion(VersionControl.CurrentVersionStringWithoutBuild) is {} forBuild) + if (mod.LoadFoldersForVersion(VersionControl.CurrentVersionStringWithoutBuild) is { } forBuild) searchPaths.AddRange(forBuild.Select(p => p.folderName)); - if (mod.LoadFoldersForVersion("default") is {} forDefault) + if (mod.LoadFoldersForVersion("default") is { } forDefault) searchPaths.AddRange(forDefault.Select(p => p.folderName)); if (searchPaths.Count == 0) @@ -264,7 +266,7 @@ private static bool ModContainsAssembly(ModMetaData mod, string assemblyName) private static List<(string type, string asm)> GetLoaderErrors(string messageText) { - if (!messageText.Contains("Loader exceptions:")) return new List<(string type, string asm)>(); + if (!messageText.Contains("Loader exceptions:")) return new(); try { @@ -272,7 +274,7 @@ private static bool ModContainsAssembly(ModMetaData mod, string assemblyName) var split = messageText.Split(new[] {"=> "}, StringSplitOptions.None).Skip(1).Take(int.MaxValue).ToList(); var target = "from typeref, class/assembly "; - var errorDetail = split.Select(e => e.Substring(e.IndexOf(target) + target.Length)).ToList(); + var errorDetail = split.Select(e => e.Substring(e.IndexOf(target, StringComparison.Ordinal) + target.Length)).ToList(); var attemptedLoadOf = errorDetail.Select(e => e.Split(',')).Select(arr => (type: arr[0].Trim(), asm: arr[1].Trim())).ToList(); @@ -282,28 +284,28 @@ private static bool ModContainsAssembly(ModMetaData mod, string assemblyName) { //We really don't want this to fail, it's just gonna be a pain Log.Warning("[BetterLoading] Failed to scrape Loader Errors."); - return new List<(string type, string asm)>(); + return new(); } } public static bool DisableVanillaLoadScreen() { //Disable when our load screen is shown - return !LoadingScreen.shouldShow; + return !LoadingScreen!.shouldShow; } public static void OnGameLoadStart() { - if (ModLister.AllInstalledMods.FirstOrDefault(m => m.enabled && m.Name.ToLowerInvariant().Contains("multiplayer")) is {} mpMod) + if (ModLister.AllInstalledMods.FirstOrDefault(m => m.enabled && m.Name.ToLowerInvariant().Contains("multiplayer")) is { } mpMod) { Log.Warning($"[BetterLoading] Not showing game load/save screen because we've detected what we believe to be a multiplayer mod: {mpMod.Name}"); return; } - - + + LoadingScreen = Object.FindObjectOfType().gameObject .AddComponent(); - InitLoadingScreenBG(); + InitLoadingScreenBackground(); //Try and work out how many maps we have LoadMaps.CountMaps(); @@ -319,45 +321,13 @@ private static void LogMsg(string message) public static void OnClearPlayData() { //Reset our harmony patches. - hInstance?.UnpatchAll("me.samboycoding.blm"); - - if(LoadingScreen == null) + hInstance.UnpatchAll("me.samboycoding.blm"); + + if (LoadingScreen == null) return; - + //Destroy loading screen. Object.Destroy(LoadingScreen); } - - //Following code kept as reference - - #region Save Game Loading Patches - - [HarmonyPatch(typeof(WorldGenStep))] - [HarmonyPatch(nameof(WorldGenStep.GenerateFromScribe))] - [UsedImplicitly] - public class WorldGenStepExecPatch - { - [UsedImplicitly] - public static void Prefix(WorldGenStep __instance) - { - LoadingScreen.Instance.numWorldGeneratorsRun++; - LoadingScreen.Instance.currentWorldGenStep = __instance; - } - } - - [HarmonyPatch(typeof(WorldGenStep))] - [HarmonyPatch(nameof(WorldGenStep.GenerateWithoutWorldData))] - [UsedImplicitly] - public class WorldGenStepExecPatch2 - { - [UsedImplicitly] - public static void Prefix(WorldGenStep __instance) - { - LoadingScreen.Instance.numWorldGeneratorsRun++; - LoadingScreen.Instance.currentWorldGenStep = __instance; - } - } - - #endregion } } \ No newline at end of file diff --git a/Source/BetterLoadingTip.cs b/Source/BetterLoadingTip.cs new file mode 100644 index 0000000..fe1968d --- /dev/null +++ b/Source/BetterLoadingTip.cs @@ -0,0 +1,13 @@ +using Verse; + +namespace BetterLoading +{ + //Class for cross-compatibility between vanilla tips and ShitRimworldSays tips + public class BetterLoadingTip + { +#pragma warning disable CS8618 + public string TipBody; + public string Source; +#pragma warning restore CS8618 + } +} \ No newline at end of file diff --git a/Source/Compat/HugsLib/StageHugsLibInit.cs b/Source/Compat/HugsLib/StageHugsLibInit.cs index bbffb45..af11c56 100755 --- a/Source/Compat/HugsLib/StageHugsLibInit.cs +++ b/Source/Compat/HugsLib/StageHugsLibInit.cs @@ -82,19 +82,19 @@ public override void DoPatching(Harmony instance) var hlAssembly = LoadedModManager.RunningMods.First(m => m.Name == "HugsLib").assemblies.loadedAssemblies.Find(a => a.GetName().Name == "HugsLib"); - var controllerType = hlAssembly.GetTypes().FirstOrDefault(t => t.Name == "HugsLibController") ?? throw new Exception("Type HugsLibController is missing"); + var controllerType = hlAssembly.GetTypes().FirstOrDefault(t => t.Name == "HugsLibController") ?? throw new("Type HugsLibController is missing"); - _modIdentifierProperty = hlAssembly.GetTypes().First(t => t.Name == "ModBase").GetProperty("ModIdentifier") ?? throw new Exception("Property ModBase.ModIdentifier is missing"); + _modIdentifierProperty = hlAssembly.GetTypes().First(t => t.Name == "ModBase").GetProperty("ModIdentifier") ?? throw new("Property ModBase.ModIdentifier is missing"); Log.Message($"[BetterLoading:HugsLib Compat] Resolved required HugsLib types as follows: Controller: {controllerType.FullName} / Mod Identifier (Property): {_modIdentifierProperty.Name}"); hInstance.Patch( - AccessTools.Method(controllerType, "LoadReloadInitialize") ?? throw new Exception("Method HugsLibController.LoadReloadInitialize is missing"), - postfix: new HarmonyMethod(typeof(StageHugsLibInit), nameof(PostLRI)) + AccessTools.Method(controllerType, "LoadReloadInitialize") ?? throw new("Method HugsLibController.LoadReloadInitialize is missing"), + postfix: new(typeof(StageHugsLibInit), nameof(PostLRI)) ); hInstance.Patch( - AccessTools.Method(controllerType, "EnumerateChildMods") ?? throw new Exception("Method HugsLibController.EnumerateChildMods is missing"), - postfix: new HarmonyMethod(typeof(StageHugsLibInit), nameof(PostEnumerateChildren)) + AccessTools.Method(controllerType, "EnumerateChildMods") ?? throw new("Method HugsLibController.EnumerateChildMods is missing"), + postfix: new(typeof(StageHugsLibInit), nameof(PostEnumerateChildren)) ); Log.Message("[BetterLoading:HugsLib Compat] Successfully blind-patched HugsLib."); @@ -120,10 +120,10 @@ public static void PostEnumerateChildren(object ___childMods, Dictionary? TipsFromShitRimWorldSays; + private static Assembly? _srwsAssembly; + + public static void PatchShitRimworldSaysIfPresent() + { + //We can postfix-patch TipDatabase#Notify_TipsUpdated which is called when tips update + //Then we look for the field "_quotes" and read into our tip list + + _srwsAssembly = LoadedModManager.RunningMods.FirstOrDefault(m => m.Name == "Shit Rimworld Says")?.assemblies.loadedAssemblies.Find(a => a.GetName().Name == "ShitRimWorldSays"); + + if(_srwsAssembly == null) + return; + + Log.Message("Shit Rimworld Says found: " + (_srwsAssembly)); + + + var tipDbType = _srwsAssembly.GetType("ShitRimWorldSays.TipDatabase"); + + if(tipDbType == null) + return; + + var tipQuoteType = _srwsAssembly.GetType("ShitRimWorldSays.Tip_Quote"); + + if (tipQuoteType == null) + { + Log.Error("[BetterLoading|ShitRimWorldSays Compat] Found a TipDatabase but couldn't find Tip_Quote? Has the mod been updated? Please report this."); + return; + } + + Log.Message("[BetterLoading|ShitRimWorldSays Compat] Found ShitRimWorldSays, enabling compatibility. Enjoy your warcrime tips."); + + //These are public instance fields, so no binding flags needed + _tipQuoteAuthorField = tipQuoteType.GetField("author"); + _tipQuoteBodyField = tipQuoteType.GetField("body"); + + var srwsModType = _srwsAssembly.GetType("ShitRimWorldSays.ShitRimWorldSays"); + + BetterLoadingMain.hInstance!.Patch(AccessTools.Method(tipDbType, "Notify_TipsUpdated"), postfix: new(typeof(ShitRimworldSaysCompat), nameof(Notify_TipsUpdated_Postfix))); + BetterLoadingMain.hInstance!.Patch(AccessTools.FirstConstructor(srwsModType, ctor => ctor.GetParameters().Length == 1), postfix: new(typeof(ShitRimworldSaysCompat), nameof(ShitRimworldSays_ctor_Postfix))); + } + + public static bool UserWantsToHideVanillaTips() + { + if (_srwsAssembly == null) + //Not loaded, so don't hide tips + return false; + + var settingsProp = _srwsAssembly.GetType("ShitRimWorldSays.ShitRimWorldSays")!.GetProperty("Settings") ?? throw new("Failed to find ShitRimWorldSays.ShitRimWorldSays.Settings property"); + var settings = settingsProp.GetValue(null) ?? throw new("Failed to get ShitRimWorldSays.ShitRimWorldSays.Settings"); + var replaceGameTipsProp = _srwsAssembly.GetType("ShitRimWorldSays.Settings")!.GetField("replaceGameTips") ?? throw new("Failed to find ShitRimWorldSays.ShitRimWorldSays.Settings.replaceGameTips field"); + + return (bool)replaceGameTipsProp.GetValue(settings); + } + + // ReSharper disable once InconsistentNaming + public static void Notify_TipsUpdated_Postfix(HashSet ____quotes) + { + TipsFromShitRimWorldSays = new(); + + //Each of the quotes is a Tip_Quote object. + foreach (var quote in ____quotes) + { + var body = (string) _tipQuoteBodyField!.GetValue(quote); + var author = (string) _tipQuoteAuthorField!.GetValue(quote); + + if(body == "[removed]" || author == "[deleted]") + continue; //Skip removed tips + + if(body.Length > 500) + continue; //Skip too long tips + + TipsFromShitRimWorldSays.Add(new() {Source = $"u/{author}, on r/ShitRimworldSays", TipBody = body}); + } + + Log.Message($"[BetterLoading|ShitRimWorldSays Compat] ShitRimWorldSays loaded {TipsFromShitRimWorldSays.Count} tips from reddit."); + LoadingScreenTipManager.OnAvailableTipSetChanged(); + } + + public static void ShitRimworldSays_ctor_Postfix() + { + if (UserWantsToHideVanillaTips()) + { + Log.Message("[BetterLoading|ShitRimWorldSays Compat] Hiding vanilla loading tips, as you have disabled them in SRWS mod settings."); + LoadingScreenTipManager.HideVanillaTips = true; + } + } +} \ No newline at end of file diff --git a/Source/LoadingScreen.cs b/Source/LoadingScreen.cs index e485700..1636bec 100755 --- a/Source/LoadingScreen.cs +++ b/Source/LoadingScreen.cs @@ -2,7 +2,6 @@ using BetterLoading.Stage.InitialLoad; using System; using System.Collections.Generic; -using System.IO; using System.Linq; using BetterLoading.Stage.SaveLoad; using UnityEngine; @@ -12,24 +11,14 @@ namespace BetterLoading { public sealed class LoadingScreen : MonoBehaviour { - public static LoadingScreen Instance { get; private set; } - - private static string _cachedLoadingTipsPath = Path.Combine(GenFilePaths.ConfigFolderPath, "BetterLoading_Cached_Tips"); - - private static bool _tipsAvailable; - - private static List _tips = File.Exists(_cachedLoadingTipsPath) ? File.ReadAllText(_cachedLoadingTipsPath).Split('\0').ToList() : new List(); - private static string? _currentTip; - private static long _timeLastTipShown; - private const int _ticksPerTip = 5 * 10_000_000; //3 seconds - + public static LoadingScreen? Instance { get; private set; } /// /// The load list used at game boot. /// - internal static List BootLoadList = new List + internal static List BootLoadList = new() { //For all of these stages, vanilla just shows "..." - new StageInitMods(BetterLoadingMain.hInstance!), + new StageInitMods(BetterLoadingMain.hInstance), new StageReadXML(BetterLoadingMain.hInstance), new StageUnifyXML(BetterLoadingMain.hInstance), new StageApplyPatches(BetterLoadingMain.hInstance), @@ -46,10 +35,10 @@ public sealed class LoadingScreen : MonoBehaviour /// /// The load list used at game boot. /// - internal static List LoadSaveFileLoadList = new List + internal static List LoadSaveFileLoadList = new() { //For all of these stages, vanilla just shows "..." - new LoadSmallComponents(BetterLoadingMain.hInstance!), + new LoadSmallComponents(BetterLoadingMain.hInstance), new LoadWorldMap(BetterLoadingMain.hInstance), new LoadMaps(BetterLoadingMain.hInstance), new FinalizeScribeLoad(BetterLoadingMain.hInstance), @@ -57,39 +46,26 @@ public sealed class LoadingScreen : MonoBehaviour new FinalizeGameState(BetterLoadingMain.hInstance) }; - private static Dictionary _loadingStagesByType = new Dictionary(); + private static Dictionary _loadingStagesByType = new(); public Texture2D? Background; - private Texture2D errorBarColor; - private Texture2D warningBarColor; - private Texture2D loadingBarBgColor; - private Texture2D loadingBarDefaultColor; - private Texture2D loadingBarWhiteColor; + private Texture2D? _errorBarColor; + private Texture2D? _warningBarColor; + private Texture2D? _loadingBarBgColor; + private Texture2D? _loadingBarDefaultColor; + private Texture2D? _loadingBarWhiteColor; private LoadingStage _currentStage = BootLoadList[0]; public bool shouldShow = true; - public EnumLoadingStage currentStage = EnumLoadingStage.CreateClasses; - - //------------File Loading-------------- - public int numWorldGeneratorsToRun; - public int numWorldGeneratorsRun; - public WorldGenStep? currentWorldGenStep; - - public List maps = new List(); - - public int mapIndexSpawningItems = -1; - public int numObjectsToSpawnCurrentMap; - public int numObjectsSpawnedCurrentMap; - private float totalLoadLerpSpeed = 1.5f; private float stageLoadLerpSpeed = 3.0f; - private float totalLoadPercentLerp; - private float stageLoadPercentLerp; - private float bgLerp = 0f; - private Texture2D? bgSolidColor; - private Texture2D? bgContrastReducer; + private float _totalLoadPercentLerp; + private float _stageLoadPercentLerp; + private float _bgLerp; + private Texture2D? _bgSolidColor; + private Texture2D? _bgContrastReducer; public void StartSaveLoad() { @@ -104,7 +80,7 @@ public LoadingScreen() BootLoadList.ForEach(s => _loadingStagesByType[s.GetType()] = s); _currentStage.BecomeActive(); - StageTimingData.ExecutedStages.Add(new StageTimingData + StageTimingData.ExecutedStages.Add(new() { start = DateTime.Now, stage = _currentStage @@ -139,21 +115,19 @@ internal static T GetStageInstance() where T: LoadingStage public void Awake() { Log.Message("[BetterLoading] Injected into main UI."); - _tipsAvailable = _tips.Count > 0; } - private void DrawBG() + private void DrawBackground() { - const float TARGET_DARKNESS = 0.25f; - const bool SOLID_COLOR_BG = false; + const float targetDarkness = 0.25f; - bgContrastReducer ??= SolidColorMaterials.NewSolidColorTexture(new Color(1, 1, 1, 1)); - bgSolidColor ??= SolidColorMaterials.NewSolidColorTexture(new Color(0.1f, 0.1f, 0.1f, 1)); + _bgContrastReducer ??= SolidColorMaterials.NewSolidColorTexture(new(1, 1, 1, 1)); + _bgSolidColor ??= SolidColorMaterials.NewSolidColorTexture(new(0.1f, 0.1f, 0.1f, 1)); - if (SOLID_COLOR_BG || this.Background == null) + if (Background == null) { var bgRect = new Rect(0, 0, Screen.width, Screen.height); - GUI.DrawTexture(bgRect, bgSolidColor); + GUI.DrawTexture(bgRect, _bgSolidColor); return; } @@ -164,31 +138,31 @@ private void DrawBG() { float height = Screen.height; var num = Screen.height * (size.x / size.y); - rect = new Rect((Screen.width * 0.5f) - num / 2f, 0f, num, height); + rect = new((Screen.width * 0.5f) - num / 2f, 0f, num, height); } else { float width = Screen.width; var num2 = Screen.width * (size.y / size.x); - rect = new Rect(0f, (Screen.height * 0.5f) - num2 / 2f, width, num2); + rect = new(0f, (Screen.height * 0.5f) - num2 / 2f, width, num2); } // From the moment the loading screen spawns, darken the background gradually. - bgLerp = Mathf.MoveTowards(bgLerp, 1f, Time.deltaTime * Mathf.Abs(1f - bgLerp) * 0.5f); - var bgDarkness = Mathf.Lerp(1f, TARGET_DARKNESS, bgLerp); + _bgLerp = Mathf.MoveTowards(_bgLerp, 1f, Time.deltaTime * Mathf.Abs(1f - _bgLerp) * 0.5f); + var bgDarkness = Mathf.Lerp(1f, targetDarkness, _bgLerp); var oldCol = GUI.color; //Draw default rimworld loading background. - GUI.color = new Color(bgDarkness, bgDarkness, bgDarkness, 1f); + GUI.color = new(bgDarkness, bgDarkness, bgDarkness, 1f); GUI.DrawTexture(rect, Background, ScaleMode.ScaleToFit); // Draw solid color, with transparency - it's the easiest way to reduce background contrast. - const float COL = 0.2f; - var alpha = Mathf.Lerp(0f, 0.5f, bgLerp); - GUI.color = new Color(COL, COL, COL, alpha); + const float graynessLevel = 0.2f; + var alpha = Mathf.Lerp(0f, 0.5f, _bgLerp); + GUI.color = new(graynessLevel, graynessLevel, graynessLevel, alpha); var rect2 = new Rect(0, 0, Screen.width, Screen.height); - GUI.DrawTexture(rect2, bgContrastReducer); + GUI.DrawTexture(rect2, _bgContrastReducer); GUI.color = oldCol; } @@ -207,13 +181,13 @@ public void OnGUI() return; } - if (warningBarColor == null) + if (_warningBarColor == null) { - warningBarColor = SolidColorMaterials.NewSolidColorTexture(new Color(0.89f, 0.8f, 0.11f)); //RGB (226,203,29) - errorBarColor = SolidColorMaterials.NewSolidColorTexture(new Color(0.73f, 0.09f, 0.09f)); //RGB(185, 24, 24) - loadingBarBgColor = SolidColorMaterials.NewSolidColorTexture(new Color(0.5f, 0.5f, 0.5f, 1f)); - loadingBarDefaultColor = SolidColorMaterials.NewSolidColorTexture(new Color(0.2f, 0.8f, 0.85f)); - loadingBarWhiteColor = SolidColorMaterials.NewSolidColorTexture(new Color(1f, 1f, 1f, 1f)); + _warningBarColor = SolidColorMaterials.NewSolidColorTexture(new(0.89f, 0.8f, 0.11f)); //RGB (226,203,29) + _errorBarColor = SolidColorMaterials.NewSolidColorTexture(new(0.73f, 0.09f, 0.09f)); //RGB(185, 24, 24) + _loadingBarBgColor = SolidColorMaterials.NewSolidColorTexture(new(0.5f, 0.5f, 0.5f, 1f)); + _loadingBarDefaultColor = SolidColorMaterials.NewSolidColorTexture(new(0.2f, 0.8f, 0.85f)); + _loadingBarWhiteColor = SolidColorMaterials.NewSolidColorTexture(new(1f, 1f, 1f, 1f)); } try @@ -268,7 +242,7 @@ public void OnGUI() Log.Error($"[BetterLoading] The stage {_currentStage} errored during BecomeActive: {e}"); } - StageTimingData.ExecutedStages.Add(new StageTimingData + StageTimingData.ExecutedStages.Add(new() { start = DateTime.Now, stage = _currentStage @@ -287,13 +261,13 @@ public void OnGUI() if (currentProgress > maxProgress) { - Log.Error( - $"[BetterLoading] Clamping! The stage of type {_currentStage.GetType().FullName} has returned currentProgress {currentProgress} > maxProgress {maxProgress}. Please report this!"); + // Log.Error( + // $"[BetterLoading] Clamping! The stage of type {_currentStage.GetType().FullName} has returned currentProgress {currentProgress} > maxProgress {maxProgress}. Please report this!"); currentProgress = maxProgress; } //Draw background - DrawBG(); + DrawBackground(); //Draw title Text.Font = GameFont.Medium; @@ -321,20 +295,20 @@ public void OnGUI() // Also clamp between 1% and 100% (there was a bug where pct was < 0) var pct = Mathf.Clamp(currentProgress / (float) maxProgress, 0.01f, 1f); var lerpScalar = 1f; - if (pct < stageLoadPercentLerp) + if (pct < _stageLoadPercentLerp) lerpScalar = 3f; - var dst = Mathf.Abs(pct - stageLoadPercentLerp); - stageLoadPercentLerp = Mathf.MoveTowards(stageLoadPercentLerp, pct, Time.deltaTime * dst * stageLoadLerpSpeed * lerpScalar); + var dst = Mathf.Abs(pct - _stageLoadPercentLerp); + _stageLoadPercentLerp = Mathf.MoveTowards(_stageLoadPercentLerp, pct, Time.deltaTime * dst * stageLoadLerpSpeed * lerpScalar); if (subStageText != null) currentStageText = $"{subStageText}"; var rect = new Rect(450, currentBarHeight, Screen.width - 900, 26); - var color = _currentStage.HasError() ? errorBarColor : _currentStage.HasWarning() ? warningBarColor : null; + var color = _currentStage.HasError() ? _errorBarColor : _currentStage.HasWarning() ? _warningBarColor : null; - GUI.DrawTexture(rect.ExpandedBy(2), loadingBarWhiteColor); - Widgets.FillableBar(rect, stageLoadPercentLerp, color != null ? color : loadingBarDefaultColor, loadingBarBgColor, false); + GUI.DrawTexture(rect.ExpandedBy(2), _loadingBarWhiteColor); + Widgets.FillableBar(rect, _stageLoadPercentLerp, color != null ? color : _loadingBarDefaultColor, _loadingBarBgColor, false); Widgets.Label(rect, $"{pct.ToStringPercent()}, {currentProgress} of {maxProgress}"); @@ -345,49 +319,25 @@ public void OnGUI() Widgets.Label(rect, "Current: " + currentStageText); //Draw loading lines + rect.height += 100; Text.Font = GameFont.Medium; rect.y += 120; - if (!_tipsAvailable) - { - Widgets.Label(rect, "Gameplay tips will be shown here once the game loads them (after stage 7 completes)"); - } - else - { - //Load tips if required - if (_currentTip == null || (DateTime.Now.Ticks - _timeLastTipShown) >= _ticksPerTip) - { - //No tip chosen yet, or time for next tip - pick another and reset timer. - - if (_tips.NullOrEmpty()) - { - _currentTip = "BetterLoading Warning: No tips could be located in your game. This is probably a bug with another mod"; - } - else - { - _currentTip = _tips.Pop(); - } - - _timeLastTipShown = DateTime.Now.Ticks; - } - - //Draw tip. - Widgets.Label(rect, _currentTip); - } + Widgets.Label(rect, LoadingScreenTipManager.GetTipToDisplay()); Text.Font = GameFont.Small; - rect.height -= 100; //Remove increased height + rect.height -= 200; //Remove increased height //Render global progress bar. - rect = new Rect(200, globalBarHeight, Screen.width - 400, 36); + rect = new(200, globalBarHeight, Screen.width - 400, 36); Text.Anchor = TextAnchor.MiddleCenter; // Takes the current stage progress to give a more accurate percentage. pct = Mathf.Clamp01((idx + 1) / (float) currentList.Count + currentProgress / (float)maxProgress * 1f / currentList.Count); - dst = Mathf.Abs(pct - totalLoadPercentLerp); - totalLoadPercentLerp = Mathf.MoveTowards(totalLoadPercentLerp, pct, Time.deltaTime * dst * totalLoadLerpSpeed); - GUI.DrawTexture(rect.ExpandedBy(2), loadingBarWhiteColor); - Widgets.FillableBar(rect, totalLoadPercentLerp, loadingBarDefaultColor, loadingBarBgColor, false); + dst = Mathf.Abs(pct - _totalLoadPercentLerp); + _totalLoadPercentLerp = Mathf.MoveTowards(_totalLoadPercentLerp, pct, Time.deltaTime * dst * totalLoadLerpSpeed); + GUI.DrawTexture(rect.ExpandedBy(2), _loadingBarWhiteColor); + Widgets.FillableBar(rect, _totalLoadPercentLerp, _loadingBarDefaultColor, _loadingBarBgColor, false); Widgets.Label(rect, $"{pct.ToStringPercent()}"); Text.Anchor = TextAnchor.UpperLeft; @@ -398,25 +348,5 @@ public void OnGUI() Log.ErrorOnce($"Encountered exception while rendering loading screen: {e}", 0xBEEF99); } } - - private static List LoadGameplayTips() - { - return DefDatabase.AllDefsListForReading.SelectMany(set => set.tips).InRandomOrder().ToList(); - } - - public static void MarkTipsNowAvailable() - { - Log.Message("[BetterLoading] Tips should now be available. Showing..."); - - var tips = LoadGameplayTips(); - var cachedTips = File.Exists(_cachedLoadingTipsPath) ? File.ReadAllText(_cachedLoadingTipsPath).Split('\0').ToList() : new List(); - if (!tips.SequenceEqual(cachedTips)) - { - File.WriteAllText(_cachedLoadingTipsPath, string.Join("\0", tips)); - _tips = tips; - } - - _tipsAvailable = true; - } } } \ No newline at end of file diff --git a/Source/LoadingScreenTipManager.cs b/Source/LoadingScreenTipManager.cs new file mode 100644 index 0000000..32d1d00 --- /dev/null +++ b/Source/LoadingScreenTipManager.cs @@ -0,0 +1,132 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using BetterLoading.Compat; +using Verse; + +namespace BetterLoading +{ + public static class LoadingScreenTipManager + { + private static bool _hasAttemptedToLoadTips; + private static bool _tipDatabaseHasLoadedBackingField; + private static bool _hideVanillaTipsBackingField; + + public static bool GameTipDatabaseHasLoaded + { + get => _tipDatabaseHasLoadedBackingField; + set + { + _tipDatabaseHasLoadedBackingField = value; + if (value) OnAvailableTipSetChanged(); + } + } + + public static bool HideVanillaTips + { + get => _hideVanillaTipsBackingField; + set + { + _hideVanillaTipsBackingField = value; + OnAvailableTipSetChanged(); + } + } + + + public static string? LastShownTip; + public static DateTime TimeLastTipShown = DateTime.MinValue; + public static int TicksPerTip = 5 * 10_000_000; //3 seconds + + public static List Tips = new(); + + public static void OnAvailableTipSetChanged() + { + _hasAttemptedToLoadTips = true; + + Tips.Clear(); + + if (!HideVanillaTips) + ReadTipsFromGameDatabase(); + + if (ShitRimworldSaysCompat.TipsFromShitRimWorldSays != null) + Tips.AddRange(ShitRimworldSaysCompat.TipsFromShitRimWorldSays); + + Tips = Tips.InRandomOrder().ToList(); + + if (Tips.Count == 0) + TryReadCachedTipsFromConfig(); + else + TryWriteCachedTipsToConfig(); + } + + private static void ReadTipsFromGameDatabase() + { + if (!GameTipDatabaseHasLoaded) + return; + + var allTips = DefDatabase.AllDefsListForReading.SelectMany(set => set.tips.Select(t => new BetterLoadingTip {TipBody = t, Source = set.modContentPack.Name})).ToList(); + + Tips = allTips; + } + + public static void TryReadCachedTipsFromConfig() + { + var version = BetterLoadingConfigManager.Config.TipCache.Version; + + if (version != BetterLoadingConfig.TipCacheConfig.SupportedVersion) + return; + + var blobString = Encoding.UTF8.GetString(BetterLoadingConfigManager.Config.TipCache.Tips); + var tips = blobString.Split('\0'); + + Tips = tips + .Select(t => t.Split('\u0001')) + .Select(split => new BetterLoadingTip() {TipBody = split[0], Source = split[1]}) + .InRandomOrder() + .ToList(); + + Log.Message($"[BetterLoading] Read tip cache of {Tips.Count} tips from config."); + } + + public static void TryWriteCachedTipsToConfig() + { + if (Tips.Count == 0) + return; + + var blobString = string.Join("\0", Tips.Select(t => t.TipBody + "\u0001" + t.Source)); + var blob = Encoding.UTF8.GetBytes(blobString); + + BetterLoadingConfigManager.Config.TipCache.Version = BetterLoadingConfig.TipCacheConfig.SupportedVersion; + BetterLoadingConfigManager.Config.TipCache.Tips = blob; + + BetterLoadingConfigManager.Save(); + } + + private static string FormatTip(BetterLoadingTip tip) + { + return $"{tip.TipBody}\n\n-{tip.Source}"; + } + + public static string GetTipToDisplay() + { + if (Tips.Count == 0 && (!GameTipDatabaseHasLoaded || HideVanillaTips)) + return HideVanillaTips ? "Gameplay tips will be shown here once they are loaded" : "Gameplay tips will be shown here once the game loads them (after stage 7 completes)"; + + var timeCurrentTipShownFor = (DateTime.Now - TimeLastTipShown).Ticks; + + if (LastShownTip != null && timeCurrentTipShownFor < TicksPerTip) + //We have a tip and it's not been long enough to change to the next one yet, return the last one + return LastShownTip; + + //No tip chosen yet, or time for next tip - pick another and reset timer. + LastShownTip = Tips.NullOrEmpty() + ? "BetterLoading Warning: No tips could be located in your game. This is probably a bug with another mod" + : FormatTip(Tips.Pop()); + + TimeLastTipShown = DateTime.Now; + + return LastShownTip; + } + } +} \ No newline at end of file diff --git a/Source/Stage/InitialLoad/0StageInitMods.cs b/Source/Stage/InitialLoad/0StageInitMods.cs index 9f23562..f87ceea 100755 --- a/Source/Stage/InitialLoad/0StageInitMods.cs +++ b/Source/Stage/InitialLoad/0StageInitMods.cs @@ -54,7 +54,7 @@ public override void BecomeInactive() public override void DoPatching(Harmony instance) { - instance.Patch(AccessTools.Method(typeof(Activator), nameof(Activator.CreateInstance), new[] {typeof(Type), typeof(object[])}), new HarmonyMethod(typeof(StageInitMods), nameof(OnActivatorCreateInstance))); + instance.Patch(AccessTools.Method(typeof(Activator), nameof(Activator.CreateInstance), new[] {typeof(Type), typeof(object[])}), new(typeof(StageInitMods), nameof(OnActivatorCreateInstance))); } public override bool IsCompleted() @@ -72,9 +72,7 @@ public static void OnActivatorCreateInstance(Type type, params object[] args) { if (_completed) return; //If we're done, don't go again. if (!typeof(Mod).IsAssignableFrom(type)) return; //If we're not constructing a mod bail out. - if (args.Length != 1 || !(args[0] is ModContentPack) && args[0] != null) return; //Check the constructor we're using matches the required pattern. - - var pack = (ModContentPack) args[0]; + if (args.Length != 1 || args[0] is not ModContentPack pack) return; //Check the constructor we're using matches the required pattern. // Log.Message($"[BetterLoading] Class {type?.FullName} is being created for mod {pack}"); _currentModIdx++; diff --git a/Source/Stage/InitialLoad/1StageReadXML.cs b/Source/Stage/InitialLoad/1StageReadXML.cs index 09ccbaa..7ca46d0 100755 --- a/Source/Stage/InitialLoad/1StageReadXML.cs +++ b/Source/Stage/InitialLoad/1StageReadXML.cs @@ -40,8 +40,8 @@ public override int GetMaximumProgress() public override void DoPatching(Harmony instance) { - instance.Patch(AccessTools.Method(typeof(LoadedModManager), nameof(LoadedModManager.LoadModXML)), new HarmonyMethod(typeof(Utils), nameof(Utils.HarmonyPatchCancelMethod)), new HarmonyMethod(typeof(StageReadXML), nameof(AlternativeLoadModXml))); - instance.Patch(AccessTools.Method(typeof(ModContentPack), nameof(ModContentPack.LoadDefs)), postfix: new HarmonyMethod(typeof(StageReadXML), nameof(OnLoadDefsComplete))); + instance.Patch(AccessTools.Method(typeof(LoadedModManager), nameof(LoadedModManager.LoadModXML)), new(typeof(Utils), nameof(Utils.HarmonyPatchCancelMethod)), new(typeof(StageReadXML), nameof(AlternativeLoadModXml))); + instance.Patch(AccessTools.Method(typeof(ModContentPack), nameof(ModContentPack.LoadDefs)), postfix: new(typeof(StageReadXML), nameof(OnLoadDefsComplete))); } public override void BecomeActive() diff --git a/Source/Stage/InitialLoad/2StageUnifyXML.cs b/Source/Stage/InitialLoad/2StageUnifyXML.cs index cfdbf02..42e644c 100755 --- a/Source/Stage/InitialLoad/2StageUnifyXML.cs +++ b/Source/Stage/InitialLoad/2StageUnifyXML.cs @@ -48,10 +48,10 @@ public override int GetMaximumProgress() public override void DoPatching(Harmony instance) { instance.Patch(AccessTools.Method(typeof(LoadedModManager), nameof(LoadedModManager.CombineIntoUnifiedXML)), - new HarmonyMethod(typeof(StageUnifyXML), nameof(PreUnifyXML))); + new(typeof(StageUnifyXML), nameof(PreUnifyXML))); instance.Patch(AccessTools.Method(typeof(XmlDocument), "get_" + nameof(XmlDocument.DocumentElement)), - postfix: new HarmonyMethod(typeof(StageUnifyXML), nameof(PostGetDocumentElement))); + postfix: new(typeof(StageUnifyXML), nameof(PostGetDocumentElement))); } public static void PreUnifyXML(List xmls) diff --git a/Source/Stage/InitialLoad/3StageApplyPatches.cs b/Source/Stage/InitialLoad/3StageApplyPatches.cs index 449c50f..295b708 100755 --- a/Source/Stage/InitialLoad/3StageApplyPatches.cs +++ b/Source/Stage/InitialLoad/3StageApplyPatches.cs @@ -76,11 +76,11 @@ public override bool IsCompleted() public override void DoPatching(Harmony instance) { - instance.Patch(AccessTools.Method(typeof(LoadedModManager), nameof(LoadedModManager.ApplyPatches)), new HarmonyMethod(typeof(StageApplyPatches), nameof(PreApplyPatches))); + instance.Patch(AccessTools.Method(typeof(LoadedModManager), nameof(LoadedModManager.ApplyPatches)), new(typeof(StageApplyPatches), nameof(PreApplyPatches))); - instance.Patch(AccessTools.PropertyGetter(typeof(ModContentPack), nameof(ModContentPack.Patches)), new HarmonyMethod(typeof(StageApplyPatches), nameof(PreLoadPatches)), new HarmonyMethod(typeof(StageApplyPatches), nameof(PostLoadPatches))); + instance.Patch(AccessTools.PropertyGetter(typeof(ModContentPack), nameof(ModContentPack.Patches)), new(typeof(StageApplyPatches), nameof(PreLoadPatches)), new(typeof(StageApplyPatches), nameof(PostLoadPatches))); - instance.Patch(AccessTools.Method(typeof(PatchOperation), nameof(PatchOperation.Apply)), postfix: new HarmonyMethod(typeof(StageApplyPatches), nameof(PostApplyPatch))); + instance.Patch(AccessTools.Method(typeof(PatchOperation), nameof(PatchOperation.Apply)), postfix: new(typeof(StageApplyPatches), nameof(PostApplyPatch))); } public static void PreApplyPatches() diff --git a/Source/Stage/InitialLoad/4StageRegisterDefs.cs b/Source/Stage/InitialLoad/4StageRegisterDefs.cs index 25bb6cd..f955ede 100755 --- a/Source/Stage/InitialLoad/4StageRegisterDefs.cs +++ b/Source/Stage/InitialLoad/4StageRegisterDefs.cs @@ -54,8 +54,8 @@ public override int GetMaximumProgress() public override void DoPatching(Harmony instance) { - instance.Patch(AccessTools.Method(typeof(LoadedModManager), nameof(LoadedModManager.ParseAndProcessXML)), new HarmonyMethod(typeof(StageRegisterDefs), nameof(PreParseProcXml))); - instance.Patch(AccessTools.Method(typeof(XmlInheritance), nameof(XmlInheritance.TryRegister)), new HarmonyMethod(typeof(StageRegisterDefs), nameof(PreRegisterDef))); + instance.Patch(AccessTools.Method(typeof(LoadedModManager), nameof(LoadedModManager.ParseAndProcessXML)), new(typeof(StageRegisterDefs), nameof(PreParseProcXml))); + instance.Patch(AccessTools.Method(typeof(XmlInheritance), nameof(XmlInheritance.TryRegister)), new(typeof(StageRegisterDefs), nameof(PreRegisterDef))); } public static void PreParseProcXml(XmlDocument xmlDoc) diff --git a/Source/Stage/InitialLoad/5StageConstructDefs.cs b/Source/Stage/InitialLoad/5StageConstructDefs.cs index 4e07a42..c31c2d7 100755 --- a/Source/Stage/InitialLoad/5StageConstructDefs.cs +++ b/Source/Stage/InitialLoad/5StageConstructDefs.cs @@ -19,8 +19,8 @@ public class StageConstructDefs : LoadingStage private static StageConstructDefs inst; - private static readonly ConcurrentDictionary> objectFromXmlMethods = new ConcurrentDictionary>(); - private static ConcurrentDictionary typeCache = new ConcurrentDictionary(EqualityComparer.Default); + private static readonly ConcurrentDictionary> objectFromXmlMethods = new(); + private static ConcurrentDictionary typeCache = new(EqualityComparer.Default); private static MethodInfo GetTypeInternal = typeof(GenTypes).GetMethod("GetTypeInAnyAssemblyInt", BindingFlags.Static | BindingFlags.NonPublic); @@ -73,9 +73,9 @@ public override int GetMaximumProgress() public override void DoPatching(Harmony instance) { - instance.Patch(AccessTools.Method(typeof(LoadedModManager), nameof(LoadedModManager.ParseAndProcessXML)), new HarmonyMethod(typeof(StageConstructDefs), nameof(PreParseProcXml)), new HarmonyMethod(typeof(StageConstructDefs), nameof(PostParseProcessXml))/*, new HarmonyMethod(typeof(StageConstructDefs), nameof(ParallelParseAndProcessXML))*/); - instance.Patch(AccessTools.Method(typeof(DirectXmlLoader), nameof(DirectXmlLoader.DefFromNode)), new HarmonyMethod(typeof(StageConstructDefs), nameof(PreDefFromNode))); - instance.Patch(AccessTools.Method(typeof(GenTypes), nameof(GenTypes.GetTypeInAnyAssembly)), new HarmonyMethod(typeof(Utils), nameof(Utils.HarmonyPatchCancelMethod)),new HarmonyMethod(typeof(StageConstructDefs), nameof(ThreadSafeGetTypeInAnyAssembly))); + instance.Patch(AccessTools.Method(typeof(LoadedModManager), nameof(LoadedModManager.ParseAndProcessXML)), new(typeof(StageConstructDefs), nameof(PreParseProcXml)), new(typeof(StageConstructDefs), nameof(PostParseProcessXml))/*, new HarmonyMethod(typeof(StageConstructDefs), nameof(ParallelParseAndProcessXML))*/); + instance.Patch(AccessTools.Method(typeof(DirectXmlLoader), nameof(DirectXmlLoader.DefFromNode)), new(typeof(StageConstructDefs), nameof(PreDefFromNode))); + instance.Patch(AccessTools.Method(typeof(GenTypes), nameof(GenTypes.GetTypeInAnyAssembly)), new(typeof(Utils), nameof(Utils.HarmonyPatchCancelMethod)),new(typeof(StageConstructDefs), nameof(ThreadSafeGetTypeInAnyAssembly))); } public static void ThreadSafeGetTypeInAnyAssembly(string typeName, string namespaceIfAmbiguous, ref Type __result) diff --git a/Source/Stage/InitialLoad/6StageResolveDefDatabases.cs b/Source/Stage/InitialLoad/6StageResolveDefDatabases.cs index 65a8968..5816104 100755 --- a/Source/Stage/InitialLoad/6StageResolveDefDatabases.cs +++ b/Source/Stage/InitialLoad/6StageResolveDefDatabases.cs @@ -24,7 +24,7 @@ public override string GetStageName() return "Reloading Def Databases"; } - public override string? GetCurrentStepName() + public override string GetCurrentStepName() { return _currentDatabase?.FullName ?? ""; } @@ -52,17 +52,18 @@ public override void BecomeActive() public override void BecomeInactive() { base.BecomeInactive(); - LoadingScreen.MarkTipsNowAvailable(); + Log.Message("[BetterLoading] Tips should now be available. Showing..."); + LoadingScreenTipManager.GameTipDatabaseHasLoaded = true; } public override void DoPatching(Harmony instance) { instance.Patch( AccessTools.Method(typeof(GenGeneric), "MethodOnGenericType", new[] {typeof(Type), typeof(Type), typeof(string)}), - new HarmonyMethod(typeof(StageResolveDefDatabases), nameof(PreMOGT)), - new HarmonyMethod(typeof(StageResolveDefDatabases), nameof(PostMOGT))); + new(typeof(StageResolveDefDatabases), nameof(PreMOGT)), + new(typeof(StageResolveDefDatabases), nameof(PostMOGT))); - instance.Patch(AccessTools.Method(typeof(DefGenerator), nameof(DefGenerator.GenerateImpliedDefs_PostResolve)), new HarmonyMethod(typeof(StageResolveDefDatabases), nameof(PreGenImplied))); + instance.Patch(AccessTools.Method(typeof(DefGenerator), nameof(DefGenerator.GenerateImpliedDefs_PostResolve)), new(typeof(StageResolveDefDatabases), nameof(PreGenImplied))); } public static void PreMOGT(Type genericParam, string methodName) diff --git a/Source/Stage/InitialLoad/7StageRunPostLoadPreFinalizeCallbacks.cs b/Source/Stage/InitialLoad/7StageRunPostLoadPreFinalizeCallbacks.cs index 60986a8..b4035c3 100755 --- a/Source/Stage/InitialLoad/7StageRunPostLoadPreFinalizeCallbacks.cs +++ b/Source/Stage/InitialLoad/7StageRunPostLoadPreFinalizeCallbacks.cs @@ -62,8 +62,8 @@ public override void BecomeActive() public override void DoPatching(Harmony instance) { - instance.Patch(AccessTools.Method(typeof(LongEventHandler), "ExecuteToExecuteWhenFinished"), new HarmonyMethod(typeof(StageRunPostLoadPreFinalizeCallbacks), nameof(PreExecToExecWhenFinished))); - instance.Patch(AccessTools.Method(typeof(LongEventHandler), "UpdateCurrentSynchronousEvent"), new HarmonyMethod(typeof(StageRunPostLoadPreFinalizeCallbacks), nameof(PreUpdateCurrentSynchronousEvent))); + instance.Patch(AccessTools.Method(typeof(LongEventHandler), "ExecuteToExecuteWhenFinished"), new(typeof(StageRunPostLoadPreFinalizeCallbacks), nameof(PreExecToExecWhenFinished))); + instance.Patch(AccessTools.Method(typeof(LongEventHandler), "UpdateCurrentSynchronousEvent"), new(typeof(StageRunPostLoadPreFinalizeCallbacks), nameof(PreUpdateCurrentSynchronousEvent))); } public static bool PreExecToExecWhenFinished(List ___toExecuteWhenFinished) @@ -82,7 +82,7 @@ public static bool PreExecToExecWhenFinished(List ___toExecuteWhenFinish _hasBeenCalled = true; - var targetTypeName = typeof(PlayDataLoader).FullName ?? throw new Exception("WTF where has playdataloader gone."); + var targetTypeName = typeof(PlayDataLoader).FullName ?? throw new("WTF where has playdataloader gone."); // var last = ___toExecuteWhenFinished.Skip(2900).Take(int.MaxValue).Select(i => i.Method.DeclaringType).ToList(); // Debug.Log($"BL Debug: last few task defining types: {last.ToStringSafeEnumerable()}"); diff --git a/Source/Stage/InitialLoad/8StageRunStaticCctors.cs b/Source/Stage/InitialLoad/8StageRunStaticCctors.cs index 018116e..f011204 100755 --- a/Source/Stage/InitialLoad/8StageRunStaticCctors.cs +++ b/Source/Stage/InitialLoad/8StageRunStaticCctors.cs @@ -58,7 +58,7 @@ public override bool IsCompleted() public override void DoPatching(Harmony instance) { - instance.Patch(AccessTools.Method(typeof(StaticConstructorOnStartupUtility), nameof(StaticConstructorOnStartupUtility.CallAll)), new HarmonyMethod(typeof(StageRunStaticCctors), nameof(PreCallAll))); + instance.Patch(AccessTools.Method(typeof(StaticConstructorOnStartupUtility), nameof(StaticConstructorOnStartupUtility.CallAll)), new(typeof(StageRunStaticCctors), nameof(PreCallAll))); // instance.Patch(AccessTools.Method(typeof(RuntimeHelpers), nameof(RuntimeHelpers.RunClassConstructor), new []{typeof(RuntimeTypeHandle)}), new HarmonyMethod(typeof(StageRunStaticCctors), nameof(PreRunClassConstructor))); } @@ -181,7 +181,7 @@ public static bool PreCallAll() _queue = result; // Log.Message($"[BetterLoading] Updating field in LEH to a new list of size {result.Count}...", true); - LongEventHandlerMirror.ToExecuteWhenFinished = new List(); + LongEventHandlerMirror.ToExecuteWhenFinished = new(); LongEventHandler.QueueLongEvent(WaitForStaticCtors, null, true, null); diff --git a/Source/Stage/InitialLoad/9StageRunPostFinalizeCallbacks.cs b/Source/Stage/InitialLoad/9StageRunPostFinalizeCallbacks.cs index e4743d1..304a567 100755 --- a/Source/Stage/InitialLoad/9StageRunPostFinalizeCallbacks.cs +++ b/Source/Stage/InitialLoad/9StageRunPostFinalizeCallbacks.cs @@ -60,8 +60,8 @@ public override void BecomeActive() public override void DoPatching(Harmony instance) { - instance.Patch(AccessTools.Method(typeof(LongEventHandler), "ExecuteToExecuteWhenFinished"), new HarmonyMethod(typeof(StageRunPostFinalizeCallbacks), nameof(PreExecToExecWhenFinished))); - instance.Patch(AccessTools.Method(typeof(LongEventHandler), "UpdateCurrentSynchronousEvent"), new HarmonyMethod(typeof(StageRunPostFinalizeCallbacks), nameof(PreUpdateCurrentSynchronousEvent))); + instance.Patch(AccessTools.Method(typeof(LongEventHandler), "ExecuteToExecuteWhenFinished"), new(typeof(StageRunPostFinalizeCallbacks), nameof(PreExecToExecWhenFinished))); + instance.Patch(AccessTools.Method(typeof(LongEventHandler), "UpdateCurrentSynchronousEvent"), new(typeof(StageRunPostFinalizeCallbacks), nameof(PreUpdateCurrentSynchronousEvent))); } public static bool PreExecToExecWhenFinished(List ___toExecuteWhenFinished) diff --git a/Source/Stage/LoadingStage.cs b/Source/Stage/LoadingStage.cs index 38db0b5..4afdd70 100755 --- a/Source/Stage/LoadingStage.cs +++ b/Source/Stage/LoadingStage.cs @@ -6,7 +6,7 @@ namespace BetterLoading.Stage { public abstract class LoadingStage { - private static List _initializedStages = new List(); + private static List _initializedStages = new(); /// /// Required public no-args constructor /// diff --git a/Source/Stage/SaveLoad/0LoadSmallComponents.cs b/Source/Stage/SaveLoad/0LoadSmallComponents.cs index bab33e1..8514d5f 100644 --- a/Source/Stage/SaveLoad/0LoadSmallComponents.cs +++ b/Source/Stage/SaveLoad/0LoadSmallComponents.cs @@ -44,7 +44,7 @@ public override void BecomeInactive() public override void DoPatching(Harmony instance) { - instance.Patch(AccessTools.Method(typeof(World), nameof(World.ExposeData)), new HarmonyMethod(typeof(LoadSmallComponents), nameof(OnLoadWorldStart))); + instance.Patch(AccessTools.Method(typeof(World), nameof(World.ExposeData)), new(typeof(LoadSmallComponents), nameof(OnLoadWorldStart))); } public static void OnLoadWorldStart() diff --git a/Source/Stage/SaveLoad/1LoadWorldMap.cs b/Source/Stage/SaveLoad/1LoadWorldMap.cs index 092105e..21bc5d4 100644 --- a/Source/Stage/SaveLoad/1LoadWorldMap.cs +++ b/Source/Stage/SaveLoad/1LoadWorldMap.cs @@ -62,10 +62,10 @@ public override void BecomeInactive() public override void DoPatching(Harmony instance) { - instance.Patch(AccessTools.Method(typeof(World), nameof(World.ExposeData)), postfix: new HarmonyMethod(typeof(LoadWorldMap), nameof(OnLoadWorldEnd))); - instance.Patch(AccessTools.Method(typeof(WorldGenerator), nameof(WorldGenerator.GenerateFromScribe)), new HarmonyMethod(typeof(LoadWorldMap), nameof(OnStartLoadGeneratedData))); - instance.Patch(AccessTools.Method(typeof(WorldGenerator), nameof(WorldGenerator.GenerateWithoutWorldData)), new HarmonyMethod(typeof(LoadWorldMap), nameof(OnStartGenerateFreshData))); - instance.Patch(AccessTools.Method(typeof(World), nameof(World.FinalizeInit)), postfix: new HarmonyMethod(typeof(LoadWorldMap), nameof(OnFinalizeWorldInitEnd))); + instance.Patch(AccessTools.Method(typeof(World), nameof(World.ExposeData)), postfix: new(typeof(LoadWorldMap), nameof(OnLoadWorldEnd))); + instance.Patch(AccessTools.Method(typeof(WorldGenerator), nameof(WorldGenerator.GenerateFromScribe)), new(typeof(LoadWorldMap), nameof(OnStartLoadGeneratedData))); + instance.Patch(AccessTools.Method(typeof(WorldGenerator), nameof(WorldGenerator.GenerateWithoutWorldData)), new(typeof(LoadWorldMap), nameof(OnStartGenerateFreshData))); + instance.Patch(AccessTools.Method(typeof(World), nameof(World.FinalizeInit)), postfix: new(typeof(LoadWorldMap), nameof(OnFinalizeWorldInitEnd))); } public static void OnStartLoadGeneratedData() diff --git a/Source/Stage/SaveLoad/2LoadMaps.cs b/Source/Stage/SaveLoad/2LoadMaps.cs index 23ce03e..9c5efa7 100644 --- a/Source/Stage/SaveLoad/2LoadMaps.cs +++ b/Source/Stage/SaveLoad/2LoadMaps.cs @@ -98,10 +98,10 @@ public override void BecomeInactive() public override void DoPatching(Harmony instance) { - instance.Patch(AccessTools.Method(typeof(Map), nameof(Map.ExposeData)), new HarmonyMethod(typeof(LoadMaps), nameof(OnMapLoadStart))); - instance.Patch(AccessTools.Method(typeof(Map), "ExposeComponents"), new HarmonyMethod(typeof(LoadMaps), nameof(OnMapComponentsLoadStart))); - instance.Patch(AccessTools.Method(typeof(MapFileCompressor), nameof(MapFileCompressor.ExposeData)), new HarmonyMethod(typeof(LoadMaps), nameof(OnMapCompressedDataLoadStart)), new HarmonyMethod(typeof(LoadMaps), nameof(OnMapCompressedDataLoadEnd))); - instance.Patch(AccessTools.Method(typeof(CameraDriver), nameof(CameraDriver.Expose)), postfix: new HarmonyMethod(typeof(LoadMaps), nameof(OnAllMapsLoaded))); + instance.Patch(AccessTools.Method(typeof(Map), nameof(Map.ExposeData)), new(typeof(LoadMaps), nameof(OnMapLoadStart))); + instance.Patch(AccessTools.Method(typeof(Map), "ExposeComponents"), new(typeof(LoadMaps), nameof(OnMapComponentsLoadStart))); + instance.Patch(AccessTools.Method(typeof(MapFileCompressor), nameof(MapFileCompressor.ExposeData)), new(typeof(LoadMaps), nameof(OnMapCompressedDataLoadStart)), new(typeof(LoadMaps), nameof(OnMapCompressedDataLoadEnd))); + instance.Patch(AccessTools.Method(typeof(CameraDriver), nameof(CameraDriver.Expose)), postfix: new(typeof(LoadMaps), nameof(OnAllMapsLoaded))); } public static void OnMapLoadStart(Map __instance) diff --git a/Source/Stage/SaveLoad/3FinalizeScribeLoad.cs b/Source/Stage/SaveLoad/3FinalizeScribeLoad.cs index 767353d..e446c05 100644 --- a/Source/Stage/SaveLoad/3FinalizeScribeLoad.cs +++ b/Source/Stage/SaveLoad/3FinalizeScribeLoad.cs @@ -44,7 +44,7 @@ public override void BecomeInactive() public override void DoPatching(Harmony instance) { - instance.Patch(AccessTools.Method(typeof(ScribeLoader), nameof(ScribeLoader.FinalizeLoading)), postfix: new HarmonyMethod(typeof(FinalizeScribeLoad), nameof(OnResolvingComplete))); + instance.Patch(AccessTools.Method(typeof(ScribeLoader), nameof(ScribeLoader.FinalizeLoading)), postfix: new(typeof(FinalizeScribeLoad), nameof(OnResolvingComplete))); } public static void OnResolvingComplete() diff --git a/Source/Stage/SaveLoad/4SpawnAllThings.cs b/Source/Stage/SaveLoad/4SpawnAllThings.cs index af72bf2..106bcf6 100644 --- a/Source/Stage/SaveLoad/4SpawnAllThings.cs +++ b/Source/Stage/SaveLoad/4SpawnAllThings.cs @@ -55,16 +55,16 @@ public override void BecomeInactive() public override void DoPatching(Harmony instance) { - instance.Patch(AccessTools.Method(typeof(Map), nameof(Map.FinalizeLoading)), new HarmonyMethod(typeof(SpawnAllThings), nameof(OnMapStartFinalizing)), new HarmonyMethod(typeof(SpawnAllThings), nameof(OnMapFinishFinalizing))); + instance.Patch(AccessTools.Method(typeof(Map), nameof(Map.FinalizeLoading)), new(typeof(SpawnAllThings), nameof(OnMapStartFinalizing)), new(typeof(SpawnAllThings), nameof(OnMapFinishFinalizing))); //Things instance.Patch( AccessTools.Method(typeof(GenSpawn), nameof(GenSpawn.Spawn), new[] {typeof(Thing), typeof(IntVec3), typeof(Map), typeof(Rot4), typeof(WipeMode), typeof(bool)}), - new HarmonyMethod(typeof(SpawnAllThings), nameof(OnThingAboutToSpawn)) + new(typeof(SpawnAllThings), nameof(OnThingAboutToSpawn)) ); //Buildings - instance.Patch(AccessTools.Method(typeof(GenSpawn), nameof(GenSpawn.SpawnBuildingAsPossible)), new HarmonyMethod(typeof(SpawnAllThings), nameof(OnThingAboutToSpawn))); + instance.Patch(AccessTools.Method(typeof(GenSpawn), nameof(GenSpawn.SpawnBuildingAsPossible)), new(typeof(SpawnAllThings), nameof(OnThingAboutToSpawn))); } public static void OnMapStartFinalizing() diff --git a/Source/StageTimingData.cs b/Source/StageTimingData.cs index e027f0b..105fba1 100755 --- a/Source/StageTimingData.cs +++ b/Source/StageTimingData.cs @@ -6,7 +6,7 @@ namespace BetterLoading { public class StageTimingData { - internal static readonly List ExecutedStages = new List(); + internal static readonly List ExecutedStages = new(); public DateTime start; public DateTime end;