示例#1
0
        /// <summary>Merges annotations</summary>
        /// <param name="attributes">The list of <see cref="HarmonyMethod"/> to merge</param>
        /// <returns>The merged <see cref="HarmonyMethod"/></returns>
        ///
        public static HarmonyMethod Merge(List <HarmonyMethod> attributes)
        {
            var result = new HarmonyMethod();

            if (attributes is null)
            {
                return(result);
            }
            var resultTrv = Traverse.Create(result);

            attributes.ForEach(attribute =>
            {
                var trv = Traverse.Create(attribute);
                HarmonyFields().ForEach(f =>
                {
                    var val = trv.Field(f).GetValue();
                    // The second half of this if is needed because priority defaults to -1
                    // This causes the value of a HarmonyPriority attribute to be overriden by the next attribute if it is not merged last
                    // should be removed by making priority nullable and default to null at some point
                    if (val is object && (f != nameof(HarmonyMethod.priority) || (int)val != -1))
                    {
                        HarmonyMethodExtensions.SetValue(resultTrv, f, val);
                    }
                });
            });
            return(result);
        }
示例#2
0
        /// <summary>Create a patch processor from an annotated class</summary>
        /// <param name="type">The class</param>
        ///
        public PatchProcessor ProcessorForAnnotatedClass(Type type)
        {
            var parentMethodInfos = HarmonyMethodExtensions.GetFromType(type);

            if (parentMethodInfos != null && parentMethodInfos.Any())
            {
                var info = HarmonyMethod.Merge(parentMethodInfos);
                return(new PatchProcessor(this, type, info));
            }
            return(null);
        }
示例#3
0
 void ImportMethod(MethodInfo theMethod)
 {
     method = theMethod;
     if (method != null)
     {
         var infos = HarmonyMethodExtensions.GetFromMethod(method);
         if (infos != null)
         {
             Merge(infos).CopyTo(this);
         }
     }
 }
示例#4
0
        /// <summary>Creates an empty patch class processor</summary>
        /// <param name="instance">The Harmony instance</param>
        /// <param name="type">The class to process</param>
        ///
        public PatchClassProcessor(Harmony instance, Type type)
        {
            if (instance == null)
            {
                throw new ArgumentNullException(nameof(instance));
            }
            if (type == null)
            {
                throw new ArgumentNullException(nameof(type));
            }

            this.instance = instance;
            containerType = type;

            var harmonyAttributes = HarmonyMethodExtensions.GetFromType(type);

            if (harmonyAttributes == null || harmonyAttributes.Count == 0)
            {
                return;
            }

            containerAttributes = HarmonyMethod.Merge(harmonyAttributes);
            if (containerAttributes.methodType == null)             // MethodType default is Normal
            {
                containerAttributes.methodType = MethodType.Normal;
            }

            auxilaryMethods = new Dictionary <Type, MethodInfo>();
            foreach (var auxType in auxilaryTypes)
            {
                var method = PatchTools.GetPatchMethod(containerType, auxType.FullName);
                if (method != null)
                {
                    auxilaryMethods[auxType] = method;
                }
            }

            patchMethods = PatchTools.GetPatchMethods(containerType);
            foreach (var patchMethod in patchMethods)
            {
                var method = patchMethod.info.method;
                patchMethod.info        = containerAttributes.Merge(patchMethod.info);
                patchMethod.info.method = method;
            }
        }
示例#5
0
        /// <summary>Merges annotations</summary>
        /// <param name="attributes">The list of <see cref="HarmonyMethod"/> to merge</param>
        /// <returns>The merged <see cref="HarmonyMethod"/></returns>
        ///
        public static HarmonyMethod Merge(List <HarmonyMethod> attributes)
        {
            var result = new HarmonyMethod();

            if (attributes == null)
            {
                return(result);
            }
            var resultTrv = Traverse.Create(result);

            attributes.ForEach(attribute =>
            {
                var trv = Traverse.Create(attribute);
                HarmonyFields().ForEach(f =>
                {
                    var val = trv.Field(f).GetValue();
                    if (val != null)
                    {
                        HarmonyMethodExtensions.SetValue(resultTrv, f, val);
                    }
                });
            });
            return(result);
        }
示例#6
0
        void EmitCallParameter(MethodInfo patch, Dictionary <string, LocalBuilder> variables, bool allowFirsParamPassthrough, out LocalBuilder tmpObjectVar)
        {
            tmpObjectVar = null;
            var isInstance             = original.IsStatic is false;
            var originalParameters     = original.GetParameters();
            var originalParameterNames = originalParameters.Select(p => p.Name).ToArray();

            // check for passthrough using first parameter (which must have same type as return type)
            var parameters = patch.GetParameters().ToList();

            if (allowFirsParamPassthrough && patch.ReturnType != typeof(void) && parameters.Count > 0 && parameters[0].ParameterType == patch.ReturnType)
            {
                parameters.RemoveRange(0, 1);
            }

            foreach (var patchParam in parameters)
            {
                if (patchParam.Name == ORIGINAL_METHOD_PARAM)
                {
                    if (EmitOriginalBaseMethod())
                    {
                        continue;
                    }

                    emitter.Emit(OpCodes.Ldnull);
                    continue;
                }

                if (patchParam.Name == INSTANCE_PARAM)
                {
                    if (original.IsStatic)
                    {
                        emitter.Emit(OpCodes.Ldnull);
                    }
                    else
                    {
                        var instanceIsRef  = original.DeclaringType is object && AccessTools.IsStruct(original.DeclaringType);
                        var parameterIsRef = patchParam.ParameterType.IsByRef;
                        if (instanceIsRef == parameterIsRef)
                        {
                            emitter.Emit(OpCodes.Ldarg_0);
                        }
                        if (instanceIsRef && parameterIsRef is false)
                        {
                            emitter.Emit(OpCodes.Ldarg_0);
                            emitter.Emit(OpCodes.Ldobj, original.DeclaringType);
                        }
                        if (instanceIsRef is false && parameterIsRef)
                        {
                            emitter.Emit(OpCodes.Ldarga, 0);
                        }
                    }
                    continue;
                }

                if (patchParam.Name.StartsWith(INSTANCE_FIELD_PREFIX, StringComparison.Ordinal))
                {
                    var       fieldName = patchParam.Name.Substring(INSTANCE_FIELD_PREFIX.Length);
                    FieldInfo fieldInfo;
                    if (fieldName.All(char.IsDigit))
                    {
                        // field access by index only works for declared fields
                        fieldInfo = AccessTools.DeclaredField(original.DeclaringType, int.Parse(fieldName));
                        if (fieldInfo is null)
                        {
                            throw new ArgumentException($"No field found at given index in class {original.DeclaringType.FullName}", fieldName);
                        }
                    }
                    else
                    {
                        fieldInfo = AccessTools.Field(original.DeclaringType, fieldName);
                        if (fieldInfo is null)
                        {
                            throw new ArgumentException($"No such field defined in class {original.DeclaringType.FullName}", fieldName);
                        }
                    }

                    if (fieldInfo.IsStatic)
                    {
                        emitter.Emit(patchParam.ParameterType.IsByRef ? OpCodes.Ldsflda : OpCodes.Ldsfld, fieldInfo);
                    }
                    else
                    {
                        emitter.Emit(OpCodes.Ldarg_0);
                        emitter.Emit(patchParam.ParameterType.IsByRef ? OpCodes.Ldflda : OpCodes.Ldfld, fieldInfo);
                    }
                    continue;
                }

                // state is special too since each patch has its own local var
                if (patchParam.Name == STATE_VAR)
                {
                    var ldlocCode = patchParam.ParameterType.IsByRef ? OpCodes.Ldloca : OpCodes.Ldloc;
                    if (variables.TryGetValue(patch.DeclaringType.FullName, out var stateVar))
                    {
                        emitter.Emit(ldlocCode, stateVar);
                    }
                    else
                    {
                        emitter.Emit(OpCodes.Ldnull);
                    }
                    continue;
                }

                // treat __result var special
                if (patchParam.Name == RESULT_VAR)
                {
                    var returnType = AccessTools.GetReturnedType(original);
                    if (returnType == typeof(void))
                    {
                        throw new Exception($"Cannot get result from void method {original.FullDescription()}");
                    }
                    var resultType = patchParam.ParameterType;
                    if (resultType.IsByRef && returnType.IsByRef == false)
                    {
                        resultType = resultType.GetElementType();
                    }
                    if (resultType.IsAssignableFrom(returnType) is false)
                    {
                        throw new Exception($"Cannot assign method return type {returnType.FullName} to {RESULT_VAR} type {resultType.FullName} for method {original.FullDescription()}");
                    }
                    var ldlocCode = patchParam.ParameterType.IsByRef && returnType.IsByRef == false ? OpCodes.Ldloca : OpCodes.Ldloc;
                    if (returnType.IsValueType && patchParam.ParameterType == typeof(object).MakeByRefType())
                    {
                        ldlocCode = OpCodes.Ldloc;
                    }
                    emitter.Emit(ldlocCode, variables[RESULT_VAR]);
                    if (returnType.IsValueType)
                    {
                        if (patchParam.ParameterType == typeof(object))
                        {
                            emitter.Emit(OpCodes.Box, returnType);
                        }
                        else if (patchParam.ParameterType == typeof(object).MakeByRefType())
                        {
                            emitter.Emit(OpCodes.Box, returnType);
                            tmpObjectVar = il.DeclareLocal(typeof(object));
                            emitter.Emit(OpCodes.Stloc, tmpObjectVar);
                            emitter.Emit(OpCodes.Ldloca, tmpObjectVar);
                        }
                    }
                    continue;
                }

                // handle __args var special
                if (patchParam.Name == ARGS_VAR)
                {
                    if (originalParameters.Length == 0)
                    {
                        emitter.Emit(OpCodes.Ldnull);
                        continue;
                    }

                    int shift = (isInstance ? 1 : 0) + (useStructReturnBuffer ? 1 : 0);

                    emitter.Emit(OpCodes.Ldc_I4, originalParameters.Length);
                    emitter.Emit(OpCodes.Newarr, typeof(object));

                    for (int i = 0; i < originalParameters.Length; i++)
                    {
                        emitter.Emit(OpCodes.Dup);
                        emitter.Emit(OpCodes.Ldc_I4, i);
                        emitter.Emit(OpCodes.Ldarg, i + shift);

                        if (originalParameters[i].ParameterType.IsValueType)
                        {
                            emitter.Emit(OpCodes.Box, originalParameters[i].ParameterType);
                        }

                        emitter.Emit(OpCodes.Stelem_Ref);
                    }

                    continue;
                }

                // any other declared variables
                if (variables.TryGetValue(patchParam.Name, out var localBuilder))
                {
                    var ldlocCode = patchParam.ParameterType.IsByRef ? OpCodes.Ldloca : OpCodes.Ldloc;
                    emitter.Emit(ldlocCode, localBuilder);
                    continue;
                }

                int idx;
                if (patchParam.Name.StartsWith(PARAM_INDEX_PREFIX, StringComparison.Ordinal))
                {
                    var val = patchParam.Name.Substring(PARAM_INDEX_PREFIX.Length);
                    if (!int.TryParse(val, out idx))
                    {
                        throw new Exception($"Parameter {patchParam.Name} does not contain a valid index");
                    }
                    if (idx < 0 || idx >= originalParameters.Length)
                    {
                        throw new Exception($"No parameter found at index {idx}");
                    }
                }
                else
                {
                    idx = patch.GetArgumentIndex(originalParameterNames, patchParam);
                    if (idx == -1)
                    {
                        var harmonyMethod = HarmonyMethodExtensions.GetMergedFromType(patchParam.ParameterType);
                        if (harmonyMethod.methodType is null)                         // MethodType default is Normal
                        {
                            harmonyMethod.methodType = MethodType.Normal;
                        }
                        var delegateOriginal = harmonyMethod.GetOriginalMethod();
                        if (delegateOriginal is MethodInfo methodInfo)
                        {
                            var delegateConstructor = patchParam.ParameterType.GetConstructor(new[] { typeof(object), typeof(IntPtr) });
                            if (delegateConstructor is object)
                            {
                                var originalType = original.DeclaringType;
                                if (methodInfo.IsStatic)
                                {
                                    emitter.Emit(OpCodes.Ldnull);
                                }
                                else
                                {
                                    emitter.Emit(OpCodes.Ldarg_0);
                                    if (originalType.IsValueType)
                                    {
                                        emitter.Emit(OpCodes.Ldobj, originalType);
                                        emitter.Emit(OpCodes.Box, originalType);
                                    }
                                }

                                if (methodInfo.IsStatic is false && harmonyMethod.nonVirtualDelegate is false)
                                {
                                    emitter.Emit(OpCodes.Dup);
                                    emitter.Emit(OpCodes.Ldvirtftn, methodInfo);
                                }
                                else
                                {
                                    emitter.Emit(OpCodes.Ldftn, methodInfo);
                                }
                                emitter.Emit(OpCodes.Newobj, delegateConstructor);
                                continue;
                            }
                        }

                        throw new Exception($"Parameter \"{patchParam.Name}\" not found in method {original.FullDescription()}");
                    }
                }

                //   original -> patch     opcode
                // --------------------------------------
                // 1 normal   -> normal  : LDARG
                // 2 normal   -> ref/out : LDARGA
                // 3 ref/out  -> normal  : LDARG, LDIND_x
                // 4 ref/out  -> ref/out : LDARG
                //
                var originalIsNormal = originalParameters[idx].IsOut is false && originalParameters[idx].ParameterType.IsByRef is false;
                var patchIsNormal    = patchParam.IsOut is false && patchParam.ParameterType.IsByRef is false;
                var patchArgIndex    = idx + (isInstance ? 1 : 0) + (useStructReturnBuffer ? 1 : 0);

                // Case 1 + 4
                if (originalIsNormal == patchIsNormal)
                {
                    emitter.Emit(OpCodes.Ldarg, patchArgIndex);
                    continue;
                }

                // Case 2
                if (originalIsNormal && patchIsNormal is false)
                {
                    emitter.Emit(OpCodes.Ldarga, patchArgIndex);
                    continue;
                }

                // Case 3
                emitter.Emit(OpCodes.Ldarg, patchArgIndex);
                emitter.Emit(LoadIndOpCodeFor(originalParameters[idx].ParameterType));
            }
示例#7
0
        void PrepareType()
        {
            var mainPrepareResult = RunMethod <HarmonyPrepare, bool>(true);

            if (mainPrepareResult == false)
            {
                return;
            }

            var customOriginals = RunMethod <HarmonyTargetMethods, IEnumerable <MethodBase> >(null);

            if (customOriginals != null)
            {
                originals.Clear();
                originals.AddRange(customOriginals);
            }
            else
            {
                var originalMethodType = containerAttributes.methodType;

                // MethodType default is Normal
                if (containerAttributes.methodType == null)
                {
                    containerAttributes.methodType = MethodType.Normal;
                }

                var isPatchAll = container.GetCustomAttributes(true).Any(a => a.GetType().FullName == typeof(HarmonyPatchAll).FullName);
                if (isPatchAll)
                {
                    var type = containerAttributes.declaringType;
                    originals.AddRange(AccessTools.GetDeclaredConstructors(type).Cast <MethodBase>());
                    originals.AddRange(AccessTools.GetDeclaredMethods(type).Cast <MethodBase>());
                    var props = AccessTools.GetDeclaredProperties(type);
                    originals.AddRange(props.Select(prop => prop.GetGetMethod(true)).Where(method => method != null).Cast <MethodBase>());
                    originals.AddRange(props.Select(prop => prop.GetSetMethod(true)).Where(method => method != null).Cast <MethodBase>());
                }
                else
                {
                    var original = RunMethod <HarmonyTargetMethod, MethodBase>(null);

                    if (original == null)
                    {
                        original = GetOriginalMethod();
                    }

                    if (original == null)
                    {
                        var info = "(";
                        info += "declaringType=" + containerAttributes.declaringType + ", ";
                        info += "methodName =" + containerAttributes.methodName + ", ";
                        info += "methodType=" + originalMethodType + ", ";
                        info += "argumentTypes=" + containerAttributes.argumentTypes.Description();
                        info += ")";
                        throw new ArgumentException("No target method specified for class " + container.FullName + " " + info);
                    }

                    originals.Add(original);
                }
            }

            PatchTools.GetPatches(container, out var prefixMethod, out var postfixMethod, out var transpilerMethod, out var finalizerMethod);
            if (prefix != null)
            {
                prefix.method = prefixMethod;
            }
            if (postfix != null)
            {
                postfix.method = postfixMethod;
            }
            if (transpiler != null)
            {
                transpiler.method = transpilerMethod;
            }
            if (finalizer != null)
            {
                finalizer.method = finalizerMethod;
            }

            if (prefixMethod != null)
            {
                if (prefixMethod.IsStatic == false)
                {
                    throw new ArgumentException("Patch method " + prefixMethod.FullDescription() + " must be static");
                }

                var prefixAttributes = HarmonyMethodExtensions.GetFromMethod(prefixMethod);
                containerAttributes.Merge(HarmonyMethod.Merge(prefixAttributes)).CopyTo(prefix);
            }

            if (postfixMethod != null)
            {
                if (postfixMethod.IsStatic == false)
                {
                    throw new ArgumentException("Patch method " + postfixMethod.FullDescription() + " must be static");
                }

                var postfixAttributes = HarmonyMethodExtensions.GetFromMethod(postfixMethod);
                containerAttributes.Merge(HarmonyMethod.Merge(postfixAttributes)).CopyTo(postfix);
            }

            if (transpilerMethod != null)
            {
                if (transpilerMethod.IsStatic == false)
                {
                    throw new ArgumentException("Patch method " + transpilerMethod.FullDescription() + " must be static");
                }

                var transpilerAttributes = HarmonyMethodExtensions.GetFromMethod(transpilerMethod);
                containerAttributes.Merge(HarmonyMethod.Merge(transpilerAttributes)).CopyTo(transpiler);
            }

            if (finalizerMethod != null)
            {
                if (finalizerMethod.IsStatic == false)
                {
                    throw new ArgumentException("Patch method " + finalizerMethod.FullDescription() + " must be static");
                }

                var finalizerAttributes = HarmonyMethodExtensions.GetFromMethod(finalizerMethod);
                containerAttributes.Merge(HarmonyMethod.Merge(finalizerAttributes)).CopyTo(finalizer);
            }
        }
示例#8
0
        private void PrepareType()
        {
            var mainPrepareResult = RunMethod <HarmonyPrepare, bool>(true);

            if (mainPrepareResult == false)
            {
                return;
            }

            var originalMethodType = containerAttributes.methodType;

            // MethodType default is Normal
            if (containerAttributes.methodType == null)
            {
                containerAttributes.methodType = MethodType.Normal;
            }

            var reversePatchAttr    = typeof(HarmonyReversePatch).FullName;
            var reversePatchMethods = container.GetMethods(AccessTools.all).Where(m => m.GetCustomAttributes(true).Any(a => a.GetType().FullName == reversePatchAttr)).ToList();

            foreach (var reversePatchMethod in reversePatchMethods)
            {
                var attr           = containerAttributes.Merge(new HarmonyMethod(reversePatchMethod));
                var originalMethod = GetOriginalMethod(attr);
                var reversePatcher = instance.CreateReversePatcher(originalMethod, reversePatchMethod);
                reversePatcher.Patch();
            }

            var customOriginals = RunMethod <HarmonyTargetMethods, IEnumerable <MethodBase> >(null);

            if (customOriginals != null)
            {
                originals.Clear();
                originals.AddRange(customOriginals);
            }
            else
            {
                var isPatchAll = container.GetCustomAttributes(true).Any(a => a.GetType().FullName == typeof(HarmonyPatchAll).FullName);
                if (isPatchAll)
                {
                    var type = containerAttributes.declaringType;
                    originals.AddRange(AccessTools.GetDeclaredConstructors(type).Cast <MethodBase>());
                    originals.AddRange(AccessTools.GetDeclaredMethods(type).Cast <MethodBase>());
                    var props = AccessTools.GetDeclaredProperties(type);
                    originals.AddRange(props.Select(prop => prop.GetGetMethod(true)).Where(method => method != null)
                                       .Cast <MethodBase>());
                    originals.AddRange(props.Select(prop => prop.GetSetMethod(true)).Where(method => method != null)
                                       .Cast <MethodBase>());
                }
                else
                {
                    var original = RunMethod <HarmonyTargetMethod, MethodBase>(null) ?? GetOriginalMethod(containerAttributes);

                    if (original == null)
                    {
                        var info = "(";
                        info += $"declaringType={containerAttributes.declaringType}, ";
                        info += $"methodName ={containerAttributes.methodName}, ";
                        info += $"methodType={originalMethodType}, ";
                        info += $"argumentTypes={containerAttributes.argumentTypes.Description()}";
                        info += ")";
                        throw new ArgumentException(
                                  $"No target method specified for class {container.FullName} {info}");
                    }

                    originals.Add(original);
                }
            }

            GetPatches(container, out var prefixMethod, out var postfixMethod, out var transpilerMethod,
                       out var finalizerMethod);
            if (prefix != null)
            {
                prefix.method = prefixMethod;
            }
            if (postfix != null)
            {
                postfix.method = postfixMethod;
            }
            if (transpiler != null)
            {
                transpiler.method = transpilerMethod;
            }
            if (finalizer != null)
            {
                finalizer.method = finalizerMethod;
            }

            if (prefixMethod != null)
            {
                if (prefixMethod.IsStatic == false)
                {
                    throw new ArgumentException($"Patch method {prefixMethod.GetID()} must be static");
                }

                var prefixAttributes = HarmonyMethodExtensions.GetFromMethod(prefixMethod);
                containerAttributes.Merge(HarmonyMethod.Merge(prefixAttributes)).CopyTo(prefix);
            }

            if (postfixMethod != null)
            {
                if (postfixMethod.IsStatic == false)
                {
                    throw new ArgumentException($"Patch method {postfixMethod.GetID()} must be static");
                }

                var postfixAttributes = HarmonyMethodExtensions.GetFromMethod(postfixMethod);
                containerAttributes.Merge(HarmonyMethod.Merge(postfixAttributes)).CopyTo(postfix);
            }

            if (transpilerMethod != null)
            {
                if (transpilerMethod.IsStatic == false)
                {
                    throw new ArgumentException($"Patch method {transpilerMethod.GetID()} must be static");
                }

                var transpilerAttributes = HarmonyMethodExtensions.GetFromMethod(transpilerMethod);
                containerAttributes.Merge(HarmonyMethod.Merge(transpilerAttributes)).CopyTo(transpiler);
            }

            if (finalizerMethod != null)
            {
                if (finalizerMethod.IsStatic == false)
                {
                    throw new ArgumentException($"Patch method {finalizerMethod.GetID()} must be static");
                }

                var finalizerAttributes = HarmonyMethodExtensions.GetFromMethod(finalizerMethod);
                containerAttributes.Merge(HarmonyMethod.Merge(finalizerAttributes)).CopyTo(finalizer);
            }
        }
示例#9
0
        /// <summary>
        /// Applies all patches specified in the type.
        /// </summary>
        /// <param name="type">The type to scan.</param>
        public void PatchAll(Type type)
        {
            if (type == null)
            {
                throw new ArgumentNullException(nameof(type));
            }
            foreach (var method in type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic))
            {
                var patchAttributeMethods = HarmonyMethodExtensions.GetFromMethod(method);
                if (patchAttributeMethods != null && patchAttributeMethods.Any())
                {
                    var attributes = method.GetCustomAttributes(true);

                    var combinedInfo = HarmonyMethod.Merge(patchAttributeMethods);

                    var completeMethods = patchAttributeMethods.Where(x => x.declaringType != null && x.methodName != null).ToList();

                    if (patchAttributeMethods.All(x => x.declaringType != combinedInfo.declaringType && x.methodName != combinedInfo.methodName))
                    {
                        completeMethods.Add(combinedInfo);
                    }

                    var originalMethods = new List <MethodBase>();

                    foreach (var methodToPatch in completeMethods)
                    {
                        foreach (var index in attributes.OfType <ParameterByRefAttribute>().SelectMany(x => x.ParameterIndices))
                        {
                            if (!methodToPatch.argumentTypes[index].IsByRef)
                            {
                                methodToPatch.argumentTypes[index] = methodToPatch.argumentTypes[index].MakeByRefType();
                            }
                        }

                        if (!methodToPatch.methodType.HasValue)
                        {
                            methodToPatch.methodType = MethodType.Normal;
                        }

                        if (methodToPatch.method == null)
                        {
                            methodToPatch.method = method;
                        }

                        var originalMethod = PatchProcessor.GetOriginalMethod(methodToPatch);

                        if (originalMethod != null)
                        {
                            originalMethods.Add(originalMethod);
                        }
                    }

                    var processor = new PatchProcessor(this);

                    foreach (var originalMethod in originalMethods)
                    {
                        processor.AddOriginal(originalMethod);
                    }

                    if (attributes.Any(x => x is HarmonyPrefix))
                    {
                        processor.AddPrefix(new HarmonyMethod(method));
                    }

                    if (attributes.Any(x => x is HarmonyTranspiler))
                    {
                        processor.AddTranspiler(new HarmonyMethod(method));
                    }

                    if (attributes.Any(x => x is HarmonyPostfix))
                    {
                        processor.AddPostfix(new HarmonyMethod(method));
                    }

                    if (attributes.Any(x => x is HarmonyFinalizer))
                    {
                        processor.AddFinalizer(new HarmonyMethod(method));
                    }

                    processor.Patch();
                }
                else
                {
                    // Only check when logging warnings
                    if ((Logger.ChannelFilter & Logger.LogChannel.Warn) != 0)
                    {
                        if (method.GetCustomAttributes(typeof(HarmonyAttribute), true).Any())
                        {
                            Logger.LogText(Logger.LogChannel.Warn, "Method " + method.FullDescription() + " has an invalid combination of Harmony attributes and will be ignored");
                        }
                    }
                }
            }
        }