/// <summary> /// Schedules a patch method instance to be run. /// </summary> /// <param name="when">When to run the patch.</param> /// <param name="instance">The patch method instance to run.</param> internal static void AddHandler(uint when, IPatchMethodInstance instance) { if (when == RunAt.Immediately) { // Now now now! instance.Run(GetImmediateInstance()); } else { lock (PSharedData.GetLock(PRegistry.KEY_POSTLOAD_LOCK)) { InitMaster(); if (patches == null) { patches = new Dictionary <uint, PrivateRunList>(8); } if (!patches.TryGetValue(when, out PrivateRunList atTime)) { patches.Add(when, atTime = new List <IPatchMethodInstance>(16)); // Register our mod in the master list if (!master.TryGetValue(when, out SharedRunList existing)) { master.Add(when, existing = new List <Action <uint> >(8)); } existing.Add(RunThisMod); } atTime.Add(instance); } } }
/// <summary> /// Registers a method which will be run after PLib and all mods load. It will be /// passed a HarmonyInstance which can be used to make late patches. /// </summary> /// <param name="callback">The method to invoke.</param> public static void RegisterPostload(PostLoadHandler callback) { if (callback == null) { throw new ArgumentNullException("callback"); } // Some others used this call before the library was initialized if (!PLibInit) { InitLibrary(false); LogWarning("PUtil.InitLibrary was not called before using RegisterPostload!"); } lock (PSharedData.GetLock(PRegistry.KEY_POSTLOAD_LOCK)) { // Get list holding postload information var list = PSharedData.GetData <IList <PostLoadHandler> >(PRegistry. KEY_POSTLOAD_TABLE); if (list == null) { PSharedData.PutData(PRegistry.KEY_POSTLOAD_TABLE, list = new List <PostLoadHandler>(16)); } list.Add(callback); string name = Assembly.GetCallingAssembly()?.GetName()?.Name; if (name != null) { PRegistry.LogPatchDebug("Registered post-load handler for " + name); } } }
/// <summary> /// Registers a PAction with the action manager. There is no corresponding Unregister /// call, so avoid spamming PActions. /// /// This call should occur after PUtil.InitLibrary() during the mod OnLoad(). If called /// earlier, it may fail with InvalidOperationException, and if called later, the /// user's custom key bind (if applicable) will be discarded. /// </summary> /// <param name="identifier">The identifier for this action.</param> /// <param name="title">The action's title.</param> /// <param name="binding">The default key binding for this action.</param> /// <returns>The action thus registered.</returns> /// <exception cref="InvalidOperationException">If PLib is not yet initialized.</exception> public static PAction Register(string identifier, LocString title, PKeyBinding binding = null) { // In case this call is used before the library was initialized if (!PUtil.PLibInit) { PUtil.InitLibrary(false); PUtil.LogWarning("PUtil.InitLibrary was not called before using " + "PAction.Register!"); } int actionID; PAction action; lock (PSharedData.GetLock(PRegistry.KEY_ACTION_LOCK)) { actionID = PSharedData.GetData <int>(PRegistry.KEY_ACTION_ID); if (actionID <= 0) { throw new InvalidOperationException("PAction action ID is not set!"); } PSharedData.PutData(PRegistry.KEY_ACTION_ID, actionID + 1); } action = new PAction(actionID, identifier, title); PActionManager.ConfigureTitle(action); action.AddKeyBinding(binding ?? new PKeyBinding()); return(action); }
/// <summary> /// Executes all post-load handlers. /// </summary> internal static void ExecutePostload() { IList <PostLoadHandler> postload = null; lock (PSharedData.GetLock(PRegistry.KEY_POSTLOAD_LOCK)) { // Get list holding postload information var list = PSharedData.GetData <IList <PostLoadHandler> >(PRegistry. KEY_POSTLOAD_TABLE); if (list != null) { postload = new List <PostLoadHandler>(list); } } // If there were any, run them if (postload != null) { var hInst = Harmony.HarmonyInstance.Create("PLib.PostLoad"); PRegistry.LogPatchDebug("Executing {0:D} post-load handler(s)".F(postload. Count)); foreach (var handler in postload) { handler?.Invoke(hInst); } } }
/// <summary> /// Runs all patches for all mods at the given time. Only to be run by the forwarded /// instance! /// </summary> /// <param name="when">The runtime (do not use Immediate) of patches to run.</param> internal static void RunAll(uint when) { SharedRunList toRun; lock (PSharedData.GetLock(PRegistry.KEY_POSTLOAD_LOCK)) { InitMaster(); if (!master.TryGetValue(when, out toRun)) { toRun = null; } } if (toRun != null) { PRegistry.LogPatchDebug("Executing handlers for stage {1} from {0:D} mod(s)". F(toRun.Count, RunAt.ToString(when))); foreach (var modHandler in toRun) { modHandler?.Invoke(when); } } }
/// <summary> /// Registers a method which will be run after PLib and all mods load. It will be /// passed a HarmonyInstance which can be used to make late patches. /// </summary> /// <param name="callback">The method to invoke.</param> public static void RegisterPostload(PostLoadHandler callback) { if (callback == null) { throw new ArgumentNullException("callback"); } lock (PSharedData.GetLock(PRegistry.KEY_POSTLOAD_LOCK)) { // Get list holding postload information var list = PSharedData.GetData <IList <PostLoadHandler> >(PRegistry. KEY_POSTLOAD_TABLE); if (list == null) { PSharedData.PutData(PRegistry.KEY_POSTLOAD_TABLE, list = new List <PostLoadHandler>(16)); } list.Add(callback); string name = Assembly.GetCallingAssembly()?.GetName()?.Name; if (name != null) { PRegistry.LogPatchDebug("Registered post-load handler for " + name); } } }
/// <summary> /// Executes all post-load handlers. /// </summary> internal static void ExecutePostload() { IList <PostLoadHandler> postload = null; lock (PSharedData.GetLock(PRegistry.KEY_POSTLOAD_LOCK)) { // Get list holding postload information var list = PSharedData.GetData <IList <PostLoadHandler> >(PRegistry. KEY_POSTLOAD_TABLE); if (list != null) { postload = new List <PostLoadHandler>(list); } } // If there were any, run them if (postload != null) { var hInst = Harmony.HarmonyInstance.Create("PLib.PostLoad"); PRegistry.LogPatchDebug("Executing {0:D} post-load handler(s)".F(postload. Count)); foreach (var handler in postload) { try { handler?.Invoke(hInst); } catch (Exception e) { var method = handler.Method; // Say which mod's postload crashed if (method != null) { PRegistry.LogPatchWarning("Postload handler for {0} failed:".F( method.DeclaringType.Assembly?.GetName()?.Name ?? "?")); } LogException(e); } } } }