Ejemplo n.º 1
0
        /// <summary>
        /// Initializes the PLib patch bootstrapper for shared code. <b>Must</b> be called in
        /// OnLoad for proper PLib functionality.
        ///
        /// Optionally logs the mod name and version when a mod initializes.
        /// </summary>
        public static void InitLibrary(bool logVersion = true)
        {
            var assembly = Assembly.GetCallingAssembly();

            if (assembly != null)
            {
                bool needInit;
                // Check if PLib was already initialized
                lock (assembly) {
                    needInit = !PLibInit;
                    if (needInit)
                    {
                        PLibInit = true;
                    }
                }
                if (needInit)
                {
                    // Only if not already initialized
                    PRegistry.Init();
                    if (logVersion)
                    {
                        Debug.LogFormat("[PLib] Mod {0} initialized, version {1}",
                                        assembly.GetName()?.Name, assembly.GetFileVersion() ?? "Unknown");
                    }
                }
            }
            else
            {
                // Probably impossible
                Debug.LogError("[PLib] Somehow called from null assembly!");
            }
        }
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
#pragma warning disable IDE0051 // Remove unused private members

        /// <summary>
        /// Applied to modify SteamUGCService to silence "Preview image load failed".
        /// </summary>
        private static IEnumerable <CodeInstruction> LoadPreviewImage_Transpile(
            IEnumerable <CodeInstruction> body)
        {
            const string BLACKLIST  = "LogFormat";
            var          returnBody = new List <CodeInstruction>(body);
            int          n          = returnBody.Count;

            // Look for "call Debug.LogFormat" and wipe it with NOP
            for (int i = 0; i < n; i++)
            {
                var instr = returnBody[i];
                if (instr.opcode.Name == "call" && (instr.operand as MethodBase)?.Name ==
                    BLACKLIST && i > 3)
                {
                    // Patch this instruction and the 3 before it (ldstr, ldc, newarr)
                    for (int j = i - 3; j <= i; j++)
                    {
                        instr         = returnBody[j];
                        instr.opcode  = OpCodes.Nop;
                        instr.operand = null;
                    }
                    PRegistry.LogPatchDebug("No more preview image load failure ({0:D})".F(i));
                }
            }
            return(returnBody);
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Registers a class containing methods for [PLibPatch] and [PLibMethod] handlers.
        /// All methods, public and private, of the type will be searched for annotations.
        /// However, nested and derived types will not be searched, nor will inherited methods.
        ///
        /// This method cannot be used to register a class from another mod, as the annotations
        /// on those methods would have a different assembly qualified name and would thus
        /// not be recognized.
        /// </summary>
        /// <param name="type">The type to register.</param>
        public static void RegisterPatchClass(Type type)
        {
            if (type == null)
            {
                throw new ArgumentNullException("type");
            }
            // Some others used this call before the library was initialized
            if (!PLibInit)
            {
                InitLibrary(false);
                LogWarning("PUtil.InitLibrary was not called before using RegisterPatchClass!");
            }
            int count = 0;

            foreach (var method in type.GetMethods(PPatchManager.FLAGS | BindingFlags.Static))
            {
                foreach (var attrib in method.GetCustomAttributes(true))
                {
                    if (attrib is IPLibAnnotation pm)
                    {
                        PPatchManager.AddHandler(pm.Runtime, pm.CreateInstance(method));
                        count++;
                    }
                }
            }
            if (count > 0)
            {
                PRegistry.LogPatchDebug("Registered {0:D} handler(s) for {1}".F(count,
                                                                                Assembly.GetCallingAssembly().GetNameSafe() ?? "?"));
            }
            else
            {
                PRegistry.LogPatchWarning("RegisterPatchClass could not find any handlers!");
            }
        }
Ejemplo n.º 5
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.º 6
0
#pragma warning restore IDE0051 // Remove unused private members

        #endregion

        #region Infrastructure

        /// <summary>
        /// Returns a patch method from this class. It must be static.
        /// </summary>
        /// <param name="name">The patch method name.</param>
        /// <returns>The matching method.</returns>
        private static HarmonyMethod PatchMethod(string name)
        {
            var method = typeof(PLibPatches).GetMethod(name, BindingFlags.
                                                       NonPublic | BindingFlags.Static);

            if (method == null)
            {
                PRegistry.LogPatchWarning("No PLibPatches method found: " + name);
            }
            return(new HarmonyMethod(method));
        }
Ejemplo n.º 7
0
 /// <summary>
 /// Applies the patches for this version of PLib.
 /// </summary>
 /// <param name="instance">The Harmony instance to use for patching.</param>
 public void Apply(HarmonyInstance instance)
 {
     PRegistry.LogPatchDebug("Using version " + MyVersion);
     try {
         PatchAll(instance);
     } catch (TypeLoadException e) {
         PUtil.LogException(e);
     } catch (TargetInvocationException e) {
         PUtil.LogException(e);
     }
     PActionManager.Instance.Init();
     PRegistry.LogPatchDebug("PLib patches applied");
 }
Ejemplo n.º 8
0
        /// <summary>
        /// Logs the mod name and version when a mod initializes. Also initializes the PLib
        /// patch bootstrapper for shared code.
        ///
        /// <b>Must</b> be called in Mod_OnLoad for proper PLib functionality.
        /// </summary>
        public static void LogModInit()
        {
            var assembly = Assembly.GetCallingAssembly();

            if (assembly != null)
            {
                PRegistry.Init();
                Debug.LogFormat("[PLib] Mod {0} initialized, version {1}",
                                assembly.GetName()?.Name, assembly.GetFileVersion() ?? "Unknown");
            }
            else
            {
                // Probably impossible
                Debug.LogError("[PLib] Somehow called from null assembly!");
            }
        }
Ejemplo n.º 9
0
        public PRegistry()
        {
            if (instance == null)
            {
                instance = this;
            }
            else
            {
#if DEBUG
                LogPatchWarning("Multiple PLibRegistry created!");
#endif
            }
            modData      = new Dictionary <string, object>(64);
            Patches      = new Dictionary <string, object>(32);
            PLibInstance = HarmonyInstance.Create(PLIB_HARMONY);

            // Action 0 is reserved
            modData.Add(KEY_ACTION_ID, 1);
            modData.Add(KEY_ACTION_LOCK, new object());
        }
Ejemplo n.º 10
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.º 11
0
        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!");
            }
            PPatchManager.AddHandler(RunAt.AfterModsLoad, new LegacyPostloadMethod(
                                         callback));
            string name = Assembly.GetCallingAssembly().GetNameSafe();

            if (name != null)
            {
                PRegistry.LogPatchDebug("Registered post-load handler for " + name);
            }
        }
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);
                    }
                }
            }
        }