From b110d69fdda97457324879d2ab46b53949ff993c Mon Sep 17 00:00:00 2001 From: Lee Siwoo Date: Thu, 28 May 2026 15:00:40 +0900 Subject: [PATCH 1/3] Add type forwarders and HookGen steps for TeamCherry.* assemblies Unity 6 moved tk2d sprite types to TeamCherry.TK2D.dll and video player types to TeamCherry.Cinematics.dll. Mods compiled against the old API expect these types in Assembly-CSharp, causing TypeLoadException at runtime. - PrePatcher now injects ExportedType forwarders from TeamCherry.TK2D and TeamCherry.Cinematics into the patched Assembly-CSharp.dll - New --mmhook mode in PrePatcher injects forwarders from MMHOOK_TeamCherry.* into MMHOOK_Assembly-CSharp.dll for On./IL. hook backward compat - PostBuild target runs HookGen for TeamCherry.TK2D and TeamCherry.Cinematics, then patches MMHOOK_Assembly-CSharp with the resulting forwarders Co-Authored-By: Claude Sonnet 4.6 --- Assembly-CSharp/Assembly-CSharp.csproj | 26 ++++-- PrePatcher/Program.cs | 113 +++++++++++++++++++++++++ 2 files changed, 132 insertions(+), 7 deletions(-) diff --git a/Assembly-CSharp/Assembly-CSharp.csproj b/Assembly-CSharp/Assembly-CSharp.csproj index 337c16c9..7330320f 100644 --- a/Assembly-CSharp/Assembly-CSharp.csproj +++ b/Assembly-CSharp/Assembly-CSharp.csproj @@ -20,6 +20,8 @@ + + @@ -45,11 +47,16 @@ - + - - + + + + + + + @@ -72,10 +79,9 @@ - + + - - @@ -148,7 +154,7 @@ ..\override\mscorlib.dll - + ../Vanilla/netstandard.dll @@ -200,5 +206,11 @@ ../Vanilla/UnityEngine.UIModule.dll + + ../Vanilla/TeamCherry.Localization.dll + + + ../Vanilla/TeamCherry.TK2D.dll + diff --git a/PrePatcher/Program.cs b/PrePatcher/Program.cs index e9180f40..256c86da 100644 --- a/PrePatcher/Program.cs +++ b/PrePatcher/Program.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Linq; using Mono.Cecil; using Mono.Cecil.Cil; @@ -14,6 +15,18 @@ internal class Program { private static void Main(string[] args) { + // --mmhook mode: add type forwarders from one MMHOOK assembly into another + if (args.Length >= 1 && args[0] == "--mmhook") + { + if (args.Length < 3) + { + Console.WriteLine("Usage: PrePatcher.exe --mmhook "); + return; + } + PatchMMHook(args[1], args[2]); + return; + } + if (args.Length < 2) { Console.WriteLine("Usage: PrePatcher.exe "); @@ -134,6 +147,41 @@ private static void Main(string[] args) } } + // Add type forwarders for types that moved from Assembly-CSharp to TeamCherry.*.dll in Unity 6. + // This covers TeamCherry.TK2D (tk2d sprite system) and TeamCherry.Cinematics (video player). + // This allows mods compiled against the old API to load without TypeLoadException. + string baseDir = Path.GetDirectoryName(Path.GetFullPath(args[0])); + string[] teamCherryDlls = { "TeamCherry.TK2D.dll", "TeamCherry.Cinematics.dll" }; + foreach (string dllName in teamCherryDlls) + { + string dllPath = Path.Combine(baseDir, dllName); + if (!File.Exists(dllPath)) + { + Console.WriteLine($"{dllName} not found, skipping type forwarders"); + continue; + } + using ModuleDefinition tcModule = ModuleDefinition.ReadModule(dllPath); + var tcRef = new AssemblyNameReference( + tcModule.Assembly.Name.Name, + tcModule.Assembly.Name.Version + ); + module.AssemblyReferences.Add(tcRef); + + int forwardersAdded = 0; + foreach (TypeDefinition type in tcModule.Types) + { + if (!type.IsPublic) continue; + if (module.GetType(type.Namespace, type.Name) != null) continue; + if (module.ExportedTypes.Any(e => e.Namespace == type.Namespace && e.Name == type.Name)) continue; + + var exported = new ExportedType(type.Namespace, type.Name, module, tcRef); + exported.Attributes = Mono.Cecil.TypeAttributes.Forwarder; + module.ExportedTypes.Add(exported); + forwardersAdded++; + } + Console.WriteLine($"Added {forwardersAdded} type forwarders to {dllName}"); + } + module.Write(args[1]); Console.WriteLine("Changed " + changes + " get/set calls"); @@ -266,6 +314,71 @@ ILProcessor il instr.Operand = ldstr.Operand; } + // Adds type forwarders into targetPath for all public types in sourcePath that don't already exist in target. + // Used to forward On.tk2dSprite etc. from MMHOOK_Assembly-CSharp → MMHOOK_TeamCherry.TK2D. + private static void PatchMMHook(string targetPath, string sourcePath) + { + try + { + string targetFull = Path.GetFullPath(targetPath); + string sourceFull = Path.GetFullPath(sourcePath); + + if (!File.Exists(sourceFull)) + { + Console.WriteLine($"Source MMHOOK not found: {sourceFull}, skipping."); + return; + } + if (!File.Exists(targetFull)) + { + Console.WriteLine($"Target MMHOOK not found: {targetFull}, skipping."); + return; + } + + var resolver = new DefaultAssemblyResolver(); + resolver.AddSearchDirectory(Path.GetDirectoryName(targetFull)); + var readerParams = new ReaderParameters { AssemblyResolver = resolver }; + + string tempPath = targetFull + ".tmp"; + int added = 0; + + // Scope the using blocks so files are released before we rename + using (ModuleDefinition target = ModuleDefinition.ReadModule(targetFull, readerParams)) + using (ModuleDefinition source = ModuleDefinition.ReadModule(sourceFull, readerParams)) + { + var sourceRef = new AssemblyNameReference( + source.Assembly.Name.Name, + source.Assembly.Name.Version + ); + if (!target.AssemblyReferences.Any(r => r.Name == sourceRef.Name)) + target.AssemblyReferences.Add(sourceRef); + + foreach (TypeDefinition type in source.Types) + { + if (!type.IsPublic) continue; + if (target.GetType(type.Namespace, type.Name) != null) continue; + if (target.ExportedTypes.Any(e => e.Namespace == type.Namespace && e.Name == type.Name)) continue; + + var exported = new ExportedType(type.Namespace, type.Name, target, sourceRef); + exported.Attributes = Mono.Cecil.TypeAttributes.Forwarder; + target.ExportedTypes.Add(exported); + added++; + } + + target.Write(tempPath); + } + + // Both modules are now closed; safely replace the target + File.Delete(targetFull); + File.Move(tempPath, targetFull); + Console.WriteLine($"Added {added} type forwarders from {Path.GetFileName(sourceFull)} into {Path.GetFileName(targetFull)}"); + } + catch (Exception ex) + { + Console.Error.WriteLine($"PatchMMHook failed: {ex}"); + Environment.Exit(1); + } + } + private static MethodDefinition GenerateSwappedMethod(TypeDefinition methodParent, MethodReference oldMethod) { MethodDefinition swapped = new From 44810c9321e6affa9f4a7bd04fb59ca917245c21 Mon Sep 17 00:00:00 2001 From: Lee Siwoo Date: Thu, 28 May 2026 15:00:54 +0900 Subject: [PATCH 2/3] Simplify save loading for Unity 6 JSON save format Unity 6 replaced the BinaryFormatter-encrypted save format with plain UTF-8 JSON. The old LoadGame override manually handled decryption and BinaryFormatter deserialization; this is now handled entirely by the vanilla Platform layer. Remove the custom GetSaveStatsForSlot override (vanilla handles JSON correctly) and simplify LoadGame to call orig_LoadGame after hooking mod save data, removing ~200 lines of now-dead BinaryFormatter code. Co-Authored-By: Claude Sonnet 4.6 --- Assembly-CSharp/Patches/GameManager.cs | 238 ++----------------------- 1 file changed, 15 insertions(+), 223 deletions(-) diff --git a/Assembly-CSharp/Patches/GameManager.cs b/Assembly-CSharp/Patches/GameManager.cs index 9510c2d5..9a2b9d94 100644 --- a/Assembly-CSharp/Patches/GameManager.cs +++ b/Assembly-CSharp/Patches/GameManager.cs @@ -2,7 +2,6 @@ using System.Collections; using System.Collections.Generic; using System.IO; -using System.Runtime.Serialization.Formatters.Binary; using System.Text; using MonoMod; using Newtonsoft.Json; @@ -196,28 +195,6 @@ public void SaveGame(int saveSlot, Action callback) text = JsonUtility.ToJson(obj); } - bool flag = this.gameConfig.useSaveEncryption && !Platform.Current.IsFileSystemProtected; - - if (flag) - { - string graph = Encryption.Encrypt(text); - BinaryFormatter binaryFormatter = new BinaryFormatter(); - MemoryStream memoryStream = new MemoryStream(); - binaryFormatter.Serialize(memoryStream, graph); - byte[] binary = memoryStream.ToArray(); - memoryStream.Close(); - Platform.Current.WriteSaveSlot - ( - saveSlot, - binary, - delegate (bool didSave) - { - this.HideSaveIcon(); - callback(didSave); - } - ); - } - else { Platform.Current.WriteSaveSlot ( @@ -289,27 +266,18 @@ public void SetupSceneRefs(bool refreshTilemapInfo) #region LoadGame - [MonoModReplace] + public extern void orig_LoadGame(int saveSlot, Action callback); + public void LoadGame(int saveSlot, Action callback) { if (!Platform.IsSaveSlotIndexValid(saveSlot)) { - Debug.LogErrorFormat - ( - "Cannot load from invalid save slot index {0}", - new object[] - { - saveSlot - } - ); - if (callback != null) - { - CoreLoop.InvokeNext(delegate { callback(false); }); - } - + Debug.LogErrorFormat("Cannot load from invalid save slot index {0}", new object[] { saveSlot }); + if (callback != null) CoreLoop.InvokeNext(delegate { callback(false); }); return; } + // Load modded save data from our separate file try { var path = ModdedSavePath(saveSlot); @@ -346,199 +314,23 @@ public void LoadGame(int saveSlot, Action callback) } ModHooks.OnLoadLocalSettings(this.moddedData); - Platform.Current.ReadSaveSlot - ( - saveSlot, - delegate (byte[] fileBytes) + // Let vanilla handle Platform file reading (format, decryption, etc.) + orig_LoadGame(saveSlot, (success) => + { + if (success) { - bool obj; - try - { - bool flag = this.gameConfig.useSaveEncryption && !Platform.Current.IsFileSystemProtected; - string json; - if (flag) - { - BinaryFormatter binaryFormatter = new BinaryFormatter(); - MemoryStream serializationStream = new MemoryStream(fileBytes); - string encryptedString = (string)binaryFormatter.Deserialize(serializationStream); - json = Encryption.Decrypt(encryptedString); - } - else - { - json = Encoding.UTF8.GetString(fileBytes); - } - - SaveGameData saveGameData; - - try - { - saveGameData = JsonConvert.DeserializeObject(json, new JsonSerializerSettings() - { - ContractResolver = ShouldSerializeContractResolver.Instance, - TypeNameHandling = TypeNameHandling.Auto, - ObjectCreationHandling = ObjectCreationHandling.Replace, - Converters = JsonConverterTypes.ConverterTypes - }); - } - catch (Exception e) - { - Logger.APILogger.LogError("Failed to read save using Json.NET (GameManager::LoadGame), falling back."); - Logger.APILogger.LogError(e); - - saveGameData = JsonUtility.FromJson(json); - } - - global::PlayerData instance = saveGameData.playerData; - SceneData instance2 = saveGameData.sceneData; - global::PlayerData.instance = instance; - this.playerData = instance; - SceneData.instance = instance2; - ModHooks.OnAfterSaveGameLoad(saveGameData); - this.sceneData = instance2; - this.profileID = saveSlot; - this.inputHandler.RefreshPlayerData(); - ModHooks.OnSavegameLoad(saveSlot); - obj = true; - } - catch (Exception ex) - { - Debug.LogFormat - ( - "Error loading save file for slot {0}: {1}", - new object[] - { - saveSlot, - ex - } - ); - obj = false; - } - - if (callback != null) - { - callback(obj); - } + var saveGameData = new SaveGameData(this.playerData, this.sceneData); + ModHooks.OnAfterSaveGameLoad(saveGameData); + ModHooks.OnSavegameLoad(saveSlot); } - ); + callback?.Invoke(success); + }); } #endregion #region GetSaveStatsForSlot - - [MonoModReplace] - public void GetSaveStatsForSlot(int saveSlot, Action callback) - { - if (!Platform.IsSaveSlotIndexValid(saveSlot)) - { - Debug.LogErrorFormat - ( - "Cannot get save stats for invalid slot {0}", - new object[] - { - saveSlot - } - ); - if (callback != null) - { - CoreLoop.InvokeNext(delegate { callback(null); }); - } - - return; - } - - Platform.Current.ReadSaveSlot - ( - saveSlot, - delegate (byte[] fileBytes) - { - if (fileBytes == null) - { - if (callback != null) - { - CoreLoop.InvokeNext(delegate { callback(null); }); - } - - return; - } - - try - { - bool flag = this.gameConfig.useSaveEncryption && !Platform.Current.IsFileSystemProtected; - string json; - if (flag) - { - BinaryFormatter binaryFormatter = new BinaryFormatter(); - MemoryStream serializationStream = new MemoryStream(fileBytes); - string encryptedString = (string)binaryFormatter.Deserialize(serializationStream); - json = Encryption.Decrypt(encryptedString); - } - else - { - json = Encoding.UTF8.GetString(fileBytes); - } - - SaveGameData saveGameData; - try - { - saveGameData = JsonConvert.DeserializeObject(json, new JsonSerializerSettings() - { - ContractResolver = ShouldSerializeContractResolver.Instance, - TypeNameHandling = TypeNameHandling.Auto, - ObjectCreationHandling = ObjectCreationHandling.Replace, - Converters = JsonConverterTypes.ConverterTypes - }); - } - catch (Exception) - { - // Not a huge deal, this happens on saves with mod data which haven't been converted yet. - Logger.APILogger.LogWarn($"Failed to get save stats for slot {saveSlot} using Json.NET, falling back"); - - saveGameData = JsonUtility.FromJson(json); - } - - global::PlayerData playerData = saveGameData.playerData; - SaveStats saveStats = new SaveStats - ( - playerData.GetInt(nameof(PlayerData.maxHealthBase)), - playerData.GetInt(nameof(PlayerData.geo)), - playerData.GetVariable(nameof(PlayerData.mapZone)), - playerData.GetFloat(nameof(PlayerData.playTime)), - playerData.GetInt(nameof(PlayerData.MPReserveMax)), - playerData.GetInt(nameof(PlayerData.permadeathMode)), - playerData.GetBool(nameof(PlayerData.bossRushMode)), - playerData.GetFloat(nameof(PlayerData.completionPercentage)), - playerData.GetBool(nameof(PlayerData.unlockedCompletionRate)) - ); - if (callback != null) - { - CoreLoop.InvokeNext(delegate { callback(saveStats); }); - } - } - catch (Exception ex) - { - Debug.LogError - ( - string.Concat - ( - new object[] - { - "Error while loading save file for slot ", - saveSlot, - " Exception: ", - ex - } - ) - ); - if (callback != null) - { - CoreLoop.InvokeNext(delegate { callback(null); }); - } - } - } - ); - } - + // No mod hooks needed here; vanilla implementation handles Platform file reading correctly. #endregion #region LoadSceneAdditive From c6d6079684dd79ed60924b10a3902d74c7fd9f6b Mon Sep 17 00:00:00 2001 From: Lee Siwoo Date: Thu, 28 May 2026 15:01:09 +0900 Subject: [PATCH 3/3] Fix Unity 6 API deprecations and localization migration - Rigidbody2D.velocity -> linearVelocity (HeroController) - Object.FindObjectOfType -> FindFirstObjectByType (UIManager, GameCameras) - MenuButtonList.RecalculateNavigation: null-conditional to prevent NRE when menuButtonLists is not yet initialized - Language.Language moved to TeamCherry.Localization in Unity 6: add backward-compat stub in Assembly-CSharp namespace and update internal usages to TeamCherry.Localization.Language directly Co-Authored-By: Claude Sonnet 4.6 --- Assembly-CSharp/Menu/MenuUtils.cs | 2 +- Assembly-CSharp/Mod.cs | 2 +- Assembly-CSharp/ModHooks.cs | 27 +++++++++-- Assembly-CSharp/ModListMenu.cs | 2 +- Assembly-CSharp/ModLoader.cs | 1 + Assembly-CSharp/Patches/HeroController.cs | 7 ++- Assembly-CSharp/Patches/Language.cs | 47 ++++++++----------- Assembly-CSharp/Patches/MenuButtonList.cs | 2 +- .../SuppressPreloadException/GameCameras.cs | 2 +- Assembly-CSharp/Patches/UIManager.cs | 2 +- 10 files changed, 52 insertions(+), 42 deletions(-) diff --git a/Assembly-CSharp/Menu/MenuUtils.cs b/Assembly-CSharp/Menu/MenuUtils.cs index af0a1de8..0bd47d9a 100644 --- a/Assembly-CSharp/Menu/MenuUtils.cs +++ b/Assembly-CSharp/Menu/MenuUtils.cs @@ -8,7 +8,7 @@ using UnityEngine; using UnityEngine.UI; using Patch = Modding.Patches; -using Lang = Language.Language; +using Lang = TeamCherry.Localization.Language; namespace Modding.Menu diff --git a/Assembly-CSharp/Mod.cs b/Assembly-CSharp/Mod.cs index 3e476940..717bc889 100644 --- a/Assembly-CSharp/Mod.cs +++ b/Assembly-CSharp/Mod.cs @@ -188,7 +188,7 @@ public virtual void Initialize() { } /// change the text of the button to jump to this mod's menu. /// /// - public virtual string GetMenuButtonText() => $"{GetName()} {Language.Language.Get("MAIN_OPTIONS", "MainMenu")}"; + public virtual string GetMenuButtonText() => $"{GetName()} {TeamCherry.Localization.Language.Get("MAIN_OPTIONS", "MainMenu")}"; private void HookSaveMethods() { diff --git a/Assembly-CSharp/ModHooks.cs b/Assembly-CSharp/ModHooks.cs index 1ba7815f..0e0ad49b 100644 --- a/Assembly-CSharp/ModHooks.cs +++ b/Assembly-CSharp/ModHooks.cs @@ -7,6 +7,7 @@ using JetBrains.Annotations; using Modding.Patches; using MonoMod; +using MonoMod.RuntimeDetour; using Newtonsoft.Json; using UnityEngine; using System.Linq; @@ -219,20 +220,36 @@ internal static void LogConsole(string message, LogLevel level) /// N/A public static event LanguageGetProxy LanguageGetHook; + private static Hook _languageGetHook; + + internal static void InitLanguageHook() + { + var method = typeof(TeamCherry.Localization.Language).GetMethod( + "Get", + System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static, + null, + new[] { typeof(string), typeof(string) }, + null + ); + if (method == null) return; + _languageGetHook = new Hook(method, + new Func, string, string, string>( + (orig, key, sheet) => LanguageGet(key, sheet, orig) + )); + } + /// /// Called whenever localization specific strings are requested /// /// N/A - internal static string LanguageGet(string key, string sheet) + internal static string LanguageGet(string key, string sheet, Func orig) { - string res = Patches.Language.GetInternal(key, sheet); + string res = orig(key, sheet); if (LanguageGetHook == null) return res; - Delegate[] invocationList = LanguageGetHook.GetInvocationList(); - - foreach (LanguageGetProxy toInvoke in invocationList) + foreach (LanguageGetProxy toInvoke in LanguageGetHook.GetInvocationList()) { try { diff --git a/Assembly-CSharp/ModListMenu.cs b/Assembly-CSharp/ModListMenu.cs index fec0f388..0a7b6082 100644 --- a/Assembly-CSharp/ModListMenu.cs +++ b/Assembly-CSharp/ModListMenu.cs @@ -6,7 +6,7 @@ using UnityEngine.UI; using static Modding.ModLoader; using Patch = Modding.Patches; -using Lang = Language.Language; +using Lang = TeamCherry.Localization.Language; namespace Modding { diff --git a/Assembly-CSharp/ModLoader.cs b/Assembly-CSharp/ModLoader.cs index 6a0a19e6..aaec24b0 100644 --- a/Assembly-CSharp/ModLoader.cs +++ b/Assembly-CSharp/ModLoader.cs @@ -84,6 +84,7 @@ public static IEnumerator LoadModsInit(GameObject coroutineHolder) } Logger.APILogger.Log("Starting mod loading"); + ModHooks.InitLanguageHook(); string managed_path = SystemInfo.operatingSystemFamily switch { diff --git a/Assembly-CSharp/Patches/HeroController.cs b/Assembly-CSharp/Patches/HeroController.cs index f0430b60..7cd8e070 100644 --- a/Assembly-CSharp/Patches/HeroController.cs +++ b/Assembly-CSharp/Patches/HeroController.cs @@ -638,7 +638,6 @@ public void TakeDamage(GameObject go, CollisionSide damageSide, int damageAmount if (this.cState.wallSliding) { this.cState.wallSliding = false; - this.wallSlideVibrationPlayer.Stop(); } if (this.cState.touchingWall) @@ -654,13 +653,13 @@ public void TakeDamage(GameObject go, CollisionSide damageSide, int damageAmount if (this.cState.bouncing) { this.CancelBounce(); - this.rb2d.velocity = new Vector2(this.rb2d.velocity.x, 0f); + this.rb2d.linearVelocity = new Vector2(this.rb2d.linearVelocity.x, 0f); } if (this.cState.shroomBouncing) { this.CancelBounce(); - this.rb2d.velocity = new Vector2(this.rb2d.velocity.x, 0f); + this.rb2d.linearVelocity = new Vector2(this.rb2d.linearVelocity.x, 0f); } if (!flag) @@ -879,7 +878,7 @@ private void Dash() Vector2 vector = OrigDashVector(); vector = ModHooks.DashVelocityChange(vector); - rb2d.velocity = vector; + rb2d.linearVelocity = vector; dash_timer += Time.deltaTime; } diff --git a/Assembly-CSharp/Patches/Language.cs b/Assembly-CSharp/Patches/Language.cs index 5fbd34c2..aa54b045 100644 --- a/Assembly-CSharp/Patches/Language.cs +++ b/Assembly-CSharp/Patches/Language.cs @@ -1,37 +1,30 @@ -using System.Collections.Generic; -using MonoMod; -using UnityEngine; - // ReSharper disable All -#pragma warning disable 1591, CS0649 +#pragma warning disable 1591 namespace Modding.Patches { - [MonoModPatch("global::Language.Language")] - public static class Language + // Language class moved to TeamCherry.Localization.dll in Unity 6. + // Hooking is now done via MonoMod.RuntimeDetour in ModHooks.InitLanguageHook(). + internal static class Language { - [MonoModIgnore] - private static Dictionary> currentEntrySheets; - - public static string GetInternal(string key, string sheetTitle) + internal static string GetInternal(string key, string sheetTitle) { - if (currentEntrySheets == null || !currentEntrySheets.ContainsKey(sheetTitle)) - { - Debug.LogError($"The sheet with title \"{sheetTitle}\" does not exist!"); - return string.Empty; - } - - if (currentEntrySheets[sheetTitle].ContainsKey(key)) - { - return currentEntrySheets[sheetTitle][key]; - } - - return "#!#" + key + "#!#"; + return TeamCherry.Localization.Language.Get(key, sheetTitle); } + } +} +// Backward-compatibility shim: mods compiled against Unity 5 API reference +// [Assembly-CSharp]Language.Language. We inject this stub so those mods +// don't get TypeLoadException at runtime. +namespace Language +{ + public static class Language + { public static string Get(string key, string sheetTitle) - { - return ModHooks.LanguageGet(key, sheetTitle); - } + => TeamCherry.Localization.Language.Get(key, sheetTitle); + + public static bool Has(string key, string sheetTitle) + => TeamCherry.Localization.Language.Has(key, sheetTitle); } -} \ No newline at end of file +} diff --git a/Assembly-CSharp/Patches/MenuButtonList.cs b/Assembly-CSharp/Patches/MenuButtonList.cs index 57b084e7..57c125f1 100644 --- a/Assembly-CSharp/Patches/MenuButtonList.cs +++ b/Assembly-CSharp/Patches/MenuButtonList.cs @@ -65,7 +65,7 @@ public void ClearSelectables() public void RecalculateNavigation() { - menuButtonLists.Remove(this); + menuButtonLists?.Remove(this); Start(); } diff --git a/Assembly-CSharp/Patches/SuppressPreloadException/GameCameras.cs b/Assembly-CSharp/Patches/SuppressPreloadException/GameCameras.cs index 5956293c..ac835a8e 100644 --- a/Assembly-CSharp/Patches/SuppressPreloadException/GameCameras.cs +++ b/Assembly-CSharp/Patches/SuppressPreloadException/GameCameras.cs @@ -18,7 +18,7 @@ public static GameCameras instance { if (GameCameras._instance == null) { - GameCameras._instance = UnityEngine.Object.FindObjectOfType(); + GameCameras._instance = UnityEngine.Object.FindFirstObjectByType(); if (GameCameras._instance == null) { Debug.LogError("Couldn't find GameCameras, make sure one exists in the scene."); diff --git a/Assembly-CSharp/Patches/UIManager.cs b/Assembly-CSharp/Patches/UIManager.cs index 0de510dd..75980c91 100644 --- a/Assembly-CSharp/Patches/UIManager.cs +++ b/Assembly-CSharp/Patches/UIManager.cs @@ -25,7 +25,7 @@ public static UIManager get_instance() { if (UIManager._instance == null) { - UIManager._instance = UnityEngine.Object.FindObjectOfType(); + UIManager._instance = UnityEngine.Object.FindFirstObjectByType(); if (UIManager._instance == null) {