예제 #1
0
        /// <summary>
        /// Retrieves a method using reflection, or returns null if it does not exist.
        /// </summary>
        /// <param name="type">The base type.</param>
        /// <param name="methodName">The method name.</param>
        /// <param name="isStatic">true to find static methods, or false to find instance
        /// methods.</param>
        /// <param name="arguments">The method argument types. If null is provided, any
        /// argument types are matched, whereas no arguments match only void methods.</param>
        /// <returns>The method, or null if no such method could be found.</returns>
        public static MethodInfo GetMethodSafe(this Type type, string methodName,
                                               bool isStatic, params Type[] arguments)
        {
            MethodInfo method = null;

            if (type != null && arguments != null)
            {
                try {
                    var flag = isStatic ? BindingFlags.Static : BindingFlags.Instance;
                    if (arguments.Length == 1 && arguments[0] == null)
                    {
                        // AnyArguments
                        method = type.GetMethod(methodName, BASE_FLAGS | flag);
                    }
                    else
                    {
                        method = type.GetMethod(methodName, BASE_FLAGS | flag, null, arguments,
                                                new ParameterModifier[arguments.Length]);
                    }
                } catch (AmbiguousMatchException e) {
                    PUtil.LogException(e);
                }
            }
            return(method);
        }
예제 #2
0
        /// <summary>
        /// Adds a logger to all failed assertions. The assertions will still fail, but a stack
        /// trace will be printed for each failed assertion.
        ///
        /// Not for production use.
        /// </summary>
        internal static void LogAllFailedAsserts()
        {
            var        inst = new Harmony("PeterHan.PLib.LogFailedAsserts");
            MethodBase assert;
            var        handler = new HarmonyMethod(typeof(PTranspilerTools), nameof(OnAssertFailed));

            try {
                // Assert(bool)
                assert = typeof(Debug).GetMethodSafe("Assert", true, typeof(bool));
                if (assert != null)
                {
                    inst.Patch(assert, handler);
                }
                // Assert(bool, object)
                assert = typeof(Debug).GetMethodSafe("Assert", true, typeof(bool), typeof(
                                                         object));
                if (assert != null)
                {
                    inst.Patch(assert, handler);
                }
                // Assert(bool, object, UnityEngine.Object)
                assert = typeof(Debug).GetMethodSafe("Assert", true, typeof(bool), typeof(
                                                         object), typeof(UnityEngine.Object));
                if (assert != null)
                {
                    inst.Patch(assert, handler);
                }
            } catch (Exception e) {
                PUtil.LogException(e);
            }
        }
예제 #3
0
        /// <summary>
        /// Gets the data from this component, serialized to the specified type. The data is
        /// retrieved from the base component, serialized with JSON, and reconstituted as type
        /// T in the memory space of the caller.
        ///
        /// The target type must exist and be a [JsonObject] in both this assembly and the
        /// target component's assembly.
        ///
        /// This method is somewhat slow and memory intensive, and should be used sparingly.
        /// </summary>
        /// <typeparam name="T">The data type to retrieve and into which to convert.</typeparam>
        /// <param name="defValue">The default value if the instance data is unset.</param>
        /// <returns>The data, or defValue if the instance data has not been set or cannot be serialized.</returns>
        public T GetInstanceDataSerialized <T>(T defValue = default)
        {
            var remoteData = InstanceData;
            T   result     = defValue;

            using (var buffer = new MemoryStream(1024)) {
                try {
                    var writer = new StreamWriter(buffer, Encoding.UTF8);
                    SerializationSettings.Serialize(writer, remoteData);
                    writer.Flush();
                    buffer.Position = 0L;
                    var reader = new StreamReader(buffer, Encoding.UTF8);
                    if (SerializationSettings.Deserialize(reader, typeof(T)) is T decoded)
                    {
                        result = decoded;
                    }
                } catch (JsonException e) {
                    PUtil.LogError("Unable to serialize instance data for component " + ID +
                                   ":");
                    PUtil.LogException(e);
                    result = defValue;
                }
            }
            return(result);
        }
예제 #4
0
 /// <summary>
 /// Patches a method manually with a transpiler.
 /// </summary>
 /// <param name="instance">The Harmony instance.</param>
 /// <param name="type">The class to modify.</param>
 /// <param name="methodName">The method to patch.</param>
 /// <param name="transpiler">The transpiler to apply.</param>
 public static void PatchTranspile(this Harmony instance, Type type,
                                   string methodName, HarmonyMethod transpiler)
 {
     if (type == null)
     {
         throw new ArgumentNullException(nameof(type));
     }
     if (string.IsNullOrEmpty(methodName))
     {
         throw new ArgumentNullException(nameof(methodName));
     }
     // Fetch the method
     try {
         var method = type.GetMethod(methodName, PPatchTools.BASE_FLAGS |
                                     BindingFlags.Static | BindingFlags.Instance);
         if (method != null)
         {
             instance.Patch(method, null, null, transpiler);
         }
         else
         {
             PUtil.LogWarning("Unable to find method {0} on type {1}".F(methodName,
                                                                        type.FullName));
         }
     } catch (AmbiguousMatchException e) {
         PUtil.LogException(e);
     } catch (FormatException e) {
         PUtil.LogWarning("Unable to transpile method {0}: {1}".F(methodName,
                                                                  e.Message));
     }
 }
예제 #5
0
 /// <summary>
 /// Applies a bootstrapper patch which will complete forwarded component initialization
 /// before mods are post-loaded.
 /// </summary>
 internal void ApplyBootstrapper()
 {
     try {
         PLibInstance.Patch(typeof(KMod.Mod), nameof(KMod.Mod.PostLoad), prefix:
                            new HarmonyMethod(typeof(PRegistryComponent), nameof(ApplyLatest)));
     } catch (AmbiguousMatchException e) {
         PUtil.LogException(e);
     } catch (ArgumentException e) {
         PUtil.LogException(e);
     } catch (TypeLoadException e) {
         PUtil.LogException(e);
     }
 }
예제 #6
0
        /// <summary>
        /// Retrieves a field using reflection, or returns null if it does not exist.
        /// </summary>
        /// <param name="type">The base type.</param>
        /// <param name="fieldName">The field name.</param>
        /// <param name="isStatic">true to find static fields, or false to find instance
        /// fields.</param>
        /// <returns>The field, or null if no such field could be found.</returns>
        public static FieldInfo GetFieldSafe(this Type type, string fieldName,
                                             bool isStatic)
        {
            FieldInfo field = null;

            if (type != null)
            {
                try {
                    var flag = isStatic ? BindingFlags.Static : BindingFlags.Instance;
                    field = type.GetField(fieldName, BASE_FLAGS | flag);
                } catch (AmbiguousMatchException e) {
                    PUtil.LogException(e);
                }
            }
            return(field);
        }
예제 #7
0
        /// <summary>
        /// Retrieves an indexed property using reflection, or returns null if it does not
        /// exist.
        /// </summary>
        /// <param name="type">The base type.</param>
        /// <param name="propName">The property name.</param>
        /// <param name="isStatic">true to find static properties, or false to find instance
        /// properties.</param>
        /// <param name="arguments">The property indexer's arguments.</param>
        /// <typeparam name="T">The property field type.</typeparam>
        /// <returns>The property, or null if no such property could be found.</returns>
        public static PropertyInfo GetPropertyIndexedSafe <T>(this Type type, string propName,
                                                              bool isStatic, params Type[] arguments)
        {
            PropertyInfo field = null;

            if (type != null && arguments != null)
            {
                try {
                    var flag = isStatic ? BindingFlags.Static : BindingFlags.Instance;
                    field = type.GetProperty(propName, BASE_FLAGS | flag, null, typeof(T),
                                             arguments, new ParameterModifier[arguments.Length]);
                } catch (AmbiguousMatchException e) {
                    PUtil.LogException(e);
                }
            }
            return(field);
        }
예제 #8
0
        /// <summary>
        /// Retrieves a property using reflection, or returns null if it does not exist.
        /// </summary>
        /// <param name="type">The base type.</param>
        /// <param name="propName">The property name.</param>
        /// <param name="isStatic">true to find static properties, or false to find instance
        /// properties.</param>
        /// <typeparam name="T">The property field type.</typeparam>
        /// <returns>The property, or null if no such property could be found.</returns>
        public static PropertyInfo GetPropertySafe <T>(this Type type, string propName,
                                                       bool isStatic)
        {
            PropertyInfo field = null;

            if (type != null)
            {
                try {
                    var flag = isStatic ? BindingFlags.Static : BindingFlags.Instance;
                    field = type.GetProperty(propName, BASE_FLAGS | flag, null, typeof(T),
                                             Type.EmptyTypes, null);
                } catch (AmbiguousMatchException e) {
                    PUtil.LogException(e);
                }
            }
            return(field);
        }
예제 #9
0
        /// <summary>
        /// Goes through the forwarded components, and picks the latest version of each to
        /// instantiate.
        /// </summary>
        public void Instantiate()
        {
            foreach (var pair in forwardedComponents)
            {
                // Sort value by version
                var versions = pair.Value.Components;
                int n        = versions.Count;
                if (n > 0)
                {
                    string id = pair.Key;
                    versions.Sort();
                    var component = versions[n - 1];
                    latestComponents.GetOrAdd(id, component);
#if DEBUG
                    PRegistry.LogPatchDebug("Instantiating component {0} using version {1} from assembly {2}".F(
                                                id, component.Version, component.GetOwningAssembly().FullName));
#endif
                    try {
                        instantiatedComponents.GetOrAdd(id, component?.DoInitialize(
                                                            PLibInstance));
                    } catch (Exception e) {
                        PRegistry.LogPatchWarning("Error when instantiating component " + id +
                                                  ":");
                        PUtil.LogException(e);
                    }
                }
            }
            // Post initialize for component compatibility
            foreach (var pair in latestComponents)
            {
                try {
                    pair.Value.PostInitialize(PLibInstance);
                } catch (Exception e) {
                    PRegistry.LogPatchWarning("Error when instantiating component " +
                                              pair.Key + ":");
                    PUtil.LogException(e);
                }
            }
        }
예제 #10
0
        /// <summary>
        /// Gets the shared data between components with this ID, serialized to the specified
        /// type. The shared data is retrieved, serialized with JSON, and reconstituted as type
        /// T in the memory space of the caller.
        ///
        /// The target type must exist and be a [JsonObject] in both this assembly and the
        /// target component's assembly.
        ///
        /// This method is somewhat slow and memory intensive, and should be used sparingly.
        /// </summary>
        /// <typeparam name="T">The data type to retrieve and into which to convert.</typeparam>
        /// <param name="defValue">The default value if the shared data is unset.</param>
        /// <returns>The data, or defValue if the shared data has not been set or cannot be serialized.</returns>
        public T GetSharedDataSerialized <T>(T defValue = default)
        {
            var remoteData = PRegistry.Instance.GetSharedData(ID);
            T   result     = defValue;

            using (var buffer = new MemoryStream(1024)) {
                try {
                    SerializationSettings.Serialize(new StreamWriter(buffer, Encoding.UTF8),
                                                    remoteData);
                    buffer.Position = 0L;
                    if (SerializationSettings.Deserialize(new StreamReader(buffer,
                                                                           Encoding.UTF8), typeof(T)) is T decoded)
                    {
                        result = decoded;
                    }
                } catch (JsonException e) {
                    PUtil.LogError("Unable to serialize shared data for component " + ID +
                                   ":");
                    PUtil.LogException(e);
                    result = defValue;
                }
            }
            return(result);
        }
예제 #11
0
 /// <summary>
 /// Patches a constructor manually.
 /// </summary>
 /// <param name="instance">The Harmony instance.</param>
 /// <param name="type">The class to modify.</param>
 /// <param name="arguments">The constructor's argument types.</param>
 /// <param name="prefix">The prefix to apply, or null if none.</param>
 /// <param name="postfix">The postfix to apply, or null if none.</param>
 public static void PatchConstructor(this Harmony instance, Type type,
                                     Type[] arguments, HarmonyMethod prefix = null, HarmonyMethod postfix = null)
 {
     if (type == null)
     {
         throw new ArgumentNullException(nameof(type));
     }
     // Fetch the constructor
     try {
         var cons = type.GetConstructor(PPatchTools.BASE_FLAGS | BindingFlags.Static |
                                        BindingFlags.Instance, null, arguments, null);
         if (cons != null)
         {
             instance.Patch(cons, prefix, postfix);
         }
         else
         {
             PUtil.LogWarning("Unable to find constructor on type {0}".F(type.
                                                                         FullName));
         }
     } catch (ArgumentException e) {
         PUtil.LogException(e);
     }
 }