using System; using System.Reflection; using Harmony; using HutongGames.PlayMaker; using MSCLoader; using UnityEngine; namespace MSCO { /// /// Schutz vor Abstürzen durch nachgespielte Aktionen: Wird beim Beitreten eine Montage-Aktion in einer /// etwas anderen Welt nachgespielt, kann die "SetParent"-Aktion eines Teils ein Objekt unter sein eigenes /// Kind hängen. Das ergibt einen Kreis in der Hierarchie und Unity stürzt nativ ab (Stack Overflow in /// Transform::GetClassIDStatic). Solche Aufrufe werden hier abgefangen und übersprungen. /// public static class SafetyPatches { static bool applied; static FieldInfo gameObjectField, parentField; static int blocked; public static void Apply() { if (applied) return; applied = true; try { Type action = null; foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies()) { action = asm.GetType("HutongGames.PlayMaker.Actions.SetParent"); if (action != null) break; } MethodInfo onEnter = action != null ? action.GetMethod("OnEnter", BindingFlags.Instance | BindingFlags.Public) : null; if (onEnter == null) { ModConsole.Warning("[MSCO] SetParent action not found - hierarchy guard disabled"); return; } gameObjectField = action.GetField("gameObject"); parentField = action.GetField("parent"); HarmonyInstance.Create("mbm.msco.safety").Patch(onEnter, new HarmonyMethod(typeof(SafetyPatches).GetMethod("SetParentPrefix", BindingFlags.Static | BindingFlags.NonPublic)), null); } catch (Exception e) { ModConsole.Warning("[MSCO] Could not install hierarchy guard: " + e.Message); } } static bool SetParentPrefix(FsmStateAction __instance) { try { var owner = gameObjectField != null ? gameObjectField.GetValue(__instance) as FsmOwnerDefault : null; var parent = parentField != null ? parentField.GetValue(__instance) as FsmGameObject : null; if (owner == null || parent == null || parent.Value == null) return true; GameObject go = __instance.Fsm.GetOwnerDefaultTarget(owner); if (go == null) return true; Transform child = go.transform, newParent = parent.Value.transform; if (newParent != child && !newParent.IsChildOf(child)) return true; blocked++; if (blocked <= 20) ModConsole.Warning("[MSCO] Blocked SetParent that would crash the game: " + go.name + " -> " + parent.Value.name + " (" + __instance.Fsm.Name + ")"); __instance.Finish(); return false; } catch { return true; } } } }