Ejemplo n.º 1
0
 /// <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);
         }
     }
 }
Ejemplo n.º 2
0
 /// <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);
         }
     }
 }
Ejemplo n.º 3
0
        /// <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);
                }
            }
        }
Ejemplo n.º 4
0
        /// <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);
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Registers a PAction with the action manager. There is no corresponding Unregister
        /// call, so avoid spamming PActions.
        ///
        /// This call should occur after PUtil.LogModInit() 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)
        {
            object locker = PSharedData.GetData <object>(PRegistry.KEY_ACTION_LOCK);
            int    actionID;

            if (locker == null)
            {
                throw new InvalidOperationException("PAction.Register called before PLib loaded!");
            }
            PAction action;

            lock (locker) {
                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);
        }
Ejemplo n.º 6
0
#pragma warning disable IDE0051 // Remove unused private members
        /// <summary>
        /// Finds the latest patch and applies only it.
        /// </summary>
        private static void ApplyLatest()
        {
            if (instance != null)
            {
                object  latest    = null;
                Version latestVer = null;
                try {
                    foreach (var pair in instance.Patches)
                    {
                        var patch    = pair.Value;
                        var patchVer = new Version(pair.Key);
                        if (latestVer == null || latestVer.CompareTo(patchVer) < 0)
                        {
                            // First element or newer version
                            latest    = patch;
                            latestVer = patchVer;
                        }
                    }
                } catch (ArgumentOutOfRangeException e) {
                    // .NET 3.5 please
                    PUtil.LogException(e);
                } catch (FormatException e) {
                    PUtil.LogException(e);
                } catch (OverflowException e) {
                    PUtil.LogException(e);
                }
                if (latest != null)
                {
                    // Store the winning version
                    PSharedData.PutData(KEY_VERSION, latestVer.ToString());
                    try {
                        Traverse.Create(latest).CallMethod("Apply", instance.PLibInstance);
                    } catch (ArgumentException e) {
                        PUtil.LogException(e);
                    } catch (Exception e) {
                        if (e.Message?.ToLower() == "cannot get method value without method")
                        {
                            // Cannot get method value without method
                            PUtil.LogError("The first PLib mod in the load order did not " +
                                           "use PUtil.InitLibrary(). PLib cannot patch correctly!");
                        }
                        else
                        {
                            PUtil.LogException(e);
                        }
                    }
                }
                // Reduce memory usage by cleaning up the patch list
                instance.Patches.Clear();
            }
            else
            {
#if DEBUG
                LogPatchWarning("ApplyLatest invoked with no Instance!");
#endif
            }
        }
Ejemplo n.º 7
0
        /// <summary>
        /// Updates the action system based on the current actions registered.
        /// </summary>
        public void UpdateMaxAction()
        {
            object locker = PSharedData.GetData <object>(PRegistry.KEY_ACTION_LOCK);

            if (locker != null)
            {
                lock (locker) {
                    maxPAction = Math.Max(0, PSharedData.GetData <int>(PRegistry.
                                                                       KEY_ACTION_ID));
                }
            }
        }
Ejemplo n.º 8
0
 /// <summary>
 /// Initializes the master list of post load patches to apply.
 /// </summary>
 private static void InitMaster()
 {
     if (master == null)
     {
         var newMaster = PSharedData.GetData <IDictionary <uint, SharedRunList> >(
             PRegistry.KEY_POSTLOAD_ENHANCED);
         if (newMaster == null)
         {
             PSharedData.PutData(PRegistry.KEY_POSTLOAD_ENHANCED, newMaster = new
                                                                              Dictionary <uint, SharedRunList>(8));
         }
         master = newMaster;
     }
 }
Ejemplo n.º 9
0
        /// <summary>
        /// Applies the latest patch version.
        /// </summary>
        private static void DoApplyLatest()
        {
            object  latest    = null;
            Version latestVer = null;

            try {
                foreach (var pair in instance.Patches)
                {
                    var patch    = pair.Value;
                    var patchVer = new Version(pair.Key);
                    if (latestVer == null || latestVer.CompareTo(patchVer) < 0)
                    {
                        // First element or newer version
                        latest    = patch;
                        latestVer = patchVer;
                    }
                }
            } catch (ArgumentOutOfRangeException e) {
                // .NET 3.5 please
                PUtil.LogException(e);
            } catch (FormatException e) {
                PUtil.LogException(e);
            } catch (OverflowException e) {
                PUtil.LogException(e);
            }
            if (latest != null)
            {
                // Store the winning version
                PSharedData.PutData(KEY_VERSION, latestVer.ToString());
                try {
                    var applyMethod = Traverse.Create(latest).Method(nameof(PLibPatches.Apply),
                                                                     new Type[] { typeof(HarmonyInstance) });
                    // Raise warning if a bad patch made it in somehow
                    if (applyMethod.MethodExists())
                    {
                        applyMethod.GetValue(instance.PLibInstance);
                    }
                    else
                    {
                        LogPatchWarning("The first PLib mod in the load order did not use " +
                                        "PUtil.InitLibrary()!");
                    }
                } catch (Exception e) {
                    PUtil.LogException(e);
                }
            }
            // Reduce memory usage by cleaning up the patch list
            instance.Patches.Clear();
        }
Ejemplo n.º 10
0
#pragma warning disable IDE0051 // Remove unused private members
        /// <summary>
        /// Finds the latest patch and applies only it.
        /// </summary>
        private static void ApplyLatest()
        {
            if (instance != null)
            {
                object  latest    = null;
                Version latestVer = null;
                try {
                    foreach (var pair in instance.Patches)
                    {
                        var patch    = pair.Value;
                        var patchVer = new Version(pair.Key);
                        if (latestVer == null || latestVer.CompareTo(patchVer) < 0)
                        {
                            // First element or newer version
                            latest    = patch;
                            latestVer = patchVer;
                        }
                    }
                } catch (ArgumentOutOfRangeException e) {
                    // .NET 3.5 please
                    PUtil.LogException(e);
                } catch (FormatException e) {
                    PUtil.LogException(e);
                } catch (OverflowException e) {
                    PUtil.LogException(e);
                }
                if (latest != null)
                {
                    // Store the winning version
                    PSharedData.PutData(KEY_VERSION, latestVer.ToString());
                    try {
                        Traverse.Create(latest).CallMethod("Apply", instance.PLibInstance);
                    } catch (ArgumentException e) {
                        PUtil.LogException(e);
                    }
                }
                // Reduce memory usage by cleaning up the patch list
                instance.Patches.Clear();
            }
            else
            {
#if DEBUG
                LogPatchWarning("ApplyLatest invoked with no Instance!");
#endif
            }
        }
Ejemplo n.º 11
0
        /// <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);
                }
            }
        }
Ejemplo n.º 12
0
 /// <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);
         }
     }
 }
Ejemplo n.º 13
0
        /// <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);
                    }
                }
            }
        }