/// <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!"); } }
/// <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); } } }
#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); }
/// <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!"); } }
/// <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); } } }
#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)); }
/// <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"); }
/// <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!"); } }
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()); }
/// <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); } } }
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); } }
/// <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); } } } }