Ejemplo n.º 1
0
        public static bool Patch(IPatchable patchable)
        {
            var patchableName = patchable?.GetType()?.Name;
            var repatchTries  = 1;

            while (repatchTries <= REPATCH_TRY_TIMES)
            {
                try
                {
                    var original = patchable.BaseMethod;
                    var prefix   = patchable.Prefix;
                    var postfix  = patchable.Postfix;

                    try
                    {
                        if (original != null && (prefix != null || postfix != null))
                        {
                            var originalInstanceString = $"{original.Name}({string.Join(", ", original.GetParameters().Select(parameter => parameter.ParameterType.Name).ToArray())})";
                            var prefixInstanceString   = prefix != null ? $"{prefix.Name}({string.Join(", ", prefix.GetParameters().Select(parameter => parameter.ParameterType.Name).ToArray())})" : "";
                            var postfixInstanceString  = postfix != null ? $"{postfix.Name}({string.Join(", ", postfix.GetParameters().Select(parameter => parameter.ParameterType.Name).ToArray())})" : "";

                            LoggingWrapper.Log(LoggingWrapper.LogArea.File, LoggingWrapper.LogType.Message, $"Attempting to patch {originalInstanceString} to prefix: {prefixInstanceString} or postfix: {postfixInstanceString} (try {repatchTries})");

                            HarmonyInstanceHolder.Instance.Patch(original, new HarmonyMethod(prefix), new HarmonyMethod(postfix));

                            LoggingWrapper.Log(LoggingWrapper.LogArea.File, LoggingWrapper.LogType.Message, $"Patched {originalInstanceString}");

                            return(true);
                        }
                        else
                        {
                            LoggingWrapper.Log(LoggingWrapper.LogArea.Hidden, LoggingWrapper.LogType.Error, $"Couldn't patch {patchableName} onto {original?.Name ?? "null"}!");
                        }
                    }
                    catch (Exception ex)
                    {
                        LoggingWrapper.Log(LoggingWrapper.LogArea.Hidden, LoggingWrapper.LogType.Error, $"Couldn't patch {patchableName} onto {original?.Name ?? "null"}!");
                        LoggingWrapper.Log(LoggingWrapper.LogArea.Hidden, ex);
                    }
                }
                catch (Exception ex)
                {
                    LoggingWrapper.Log(LoggingWrapper.LogArea.Hidden, LoggingWrapper.LogType.Error, $"Patchable {patchableName ?? "unknown"} is invalid!");
                    LoggingWrapper.Log(LoggingWrapper.LogArea.Hidden, ex);
                }

                ++repatchTries;

                Thread.Sleep(REPATCH_WAIT_TIME);
            }

            return(false);
        }
Ejemplo n.º 2
0
        private bool GetPatchAtPath(TargetPath path, out IPatchable patch)
        {
            var targetId = TargetMap[path.Placeholder];

            if (path.AnimatibleType == "actor")
            {
                // pull current value
                var        actor      = (Actor)manager.App.FindActor(targetId);
                ActorPatch actorPatch = (ActorPatch)manager.AnimInputPatches.GetOrCreate(targetId, () => new ActorPatch(targetId));
                if (actor?.GeneratePatch(actorPatch, path) != null)
                {
                    patch = actorPatch;
                    return(true);
                }
            }

            patch = null;
            return(false);
        }
Ejemplo n.º 3
0
        public static bool OptionalPatch(IPatchable patchable, ref bool featureToggle)
        {
            featureToggle = Patch(patchable);

            return(featureToggle);
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Load collection for ParserTargetCollection
        /// </summary>
        /// <param name="member">Member to load data for</param>
        /// <param name="o">Instance of the object which owns member</param>
        /// <param name="node">Configuration node from which to load data</param>
        /// <param name="configName">The name of the mod that corresponds to the entry in ParserOptions</param>
        /// <param name="getChildren">Whether getters on the object should get called</param>
        private static void LoadCollectionMemberFromConfigurationNode(MemberInfo member, Object o, ConfigNode node,
                                                                      String configName = "Default", Boolean getChildren = true)
        {
            // Get the target attributes
            ParserTargetCollection[] targets =
                (ParserTargetCollection[])member.GetCustomAttributes(typeof(ParserTargetCollection), true);

            // Process the target attributes
            foreach (ParserTargetCollection target in targets)
            {
                // Figure out if this field exists and if we care
                Boolean isNode  = node.HasNode(target.FieldName) || target.FieldName == "self";
                Boolean isValue = node.HasValue(target.FieldName);

                // Obtain the type the member is (can only be field or property)
                Type   targetType;
                Object targetValue = null;
                if (member.MemberType == MemberTypes.Field)
                {
                    targetType  = ((FieldInfo)member).FieldType;
                    targetValue = getChildren ? ((FieldInfo)member).GetValue(o) : null;
                }
                else
                {
                    targetType = ((PropertyInfo)member).PropertyType;
                    try
                    {
                        if (((PropertyInfo)member).CanRead && getChildren)
                        {
                            targetValue = ((PropertyInfo)member).GetValue(o, null);
                        }
                    }
                    catch (Exception)
                    {
                        // ignored
                    }
                }

                // Get settings data
                ParserOptions.Data data = ParserOptions.Options[configName];

                // Log
                data.LogCallback("Parsing Target " + target.FieldName + " in (" + o.GetType() + ") as (" + targetType +
                                 ")");

                // If there was no data found for this node
                if (!isNode && !isValue)
                {
                    if (!target.Optional && !(target.AllowMerge && targetValue != null))
                    {
                        // Error - non optional field is missing
                        throw new ParserTargetMissingException(
                                  "Missing non-optional field: " + o.GetType() + "." + target.FieldName);
                    }

                    // Nothing to do, so return
                    continue;
                }

                // If we are dealing with a generic collection
                if (targetType.IsGenericType)
                {
                    // If the target is a generic dictionary
                    if (typeof(IDictionary).IsAssignableFrom(targetType))
                    {
                        // We need a node for this decoding
                        if (!isNode)
                        {
                            throw new Exception("Loading a generic dictionary requires sources to be nodes");
                        }

                        // Get the target value as a dictionary
                        IDictionary collection = targetValue as IDictionary;

                        // Get the internal type of this collection
                        Type genericTypeA = targetType.GetGenericArguments()[0];
                        Type genericTypeB = targetType.GetGenericArguments()[1];

                        // Create a new collection if merge is disallowed or if the collection is null
                        if (collection == null || !target.AllowMerge)
                        {
                            collection  = Activator.CreateInstance(targetType) as IDictionary;
                            targetValue = collection;
                        }

                        // Process the node
                        ConfigNode targetNode = target.FieldName == "self" ? node : node.GetNode(target.FieldName);

                        // Check the config type
                        RequireConfigType[] attributes =
                            (RequireConfigType[])genericTypeA.GetCustomAttributes(typeof(RequireConfigType), true);
                        if (attributes.Length > 0)
                        {
                            if (attributes[0].Type == ConfigType.Node)
                            {
                                throw new ParserTargetTypeMismatchException(
                                          "The key value of a generic dictionary must be a Value");
                            }
                        }

                        attributes =
                            (RequireConfigType[])genericTypeB.GetCustomAttributes(typeof(RequireConfigType), true);
                        if (attributes.Length > 0 || genericTypeB == typeof(String))
                        {
                            ConfigType type = genericTypeB == typeof(String) ? ConfigType.Value : attributes[0].Type;
                            if (type == ConfigType.Node)
                            {
                                // Iterate over all of the nodes in this node
                                foreach (ConfigNode subnode in targetNode.nodes)
                                {
                                    // Check for the name significance
                                    switch (target.NameSignificance)
                                    {
                                    case NameSignificance.None:
                                        // Just processes the contents of the node
                                        collection.Add(ProcessValue(genericTypeA, subnode.name),
                                                       CreateObjectFromConfigNode(genericTypeB, subnode, configName,
                                                                                  target.GetChild));
                                        break;

                                    case NameSignificance.Type:
                                        throw new Exception(
                                                  "NameSignificance.Type isn't supported on generic dictionaries.");

                                    case NameSignificance.Key:
                                        throw new Exception(
                                                  "NameSignificance.Key isn't supported on generic dictionaries");

                                    default:
                                        throw new ArgumentOutOfRangeException();
                                    }
                                }
                            }
                            else
                            {
                                // Iterate over all of the values in this node
                                foreach (ConfigNode.Value value in targetNode.values)
                                {
                                    // Check for the name significance
                                    switch (target.NameSignificance)
                                    {
                                    case NameSignificance.None:
                                        collection.Add(ProcessValue(genericTypeA, value.name),
                                                       ProcessValue(genericTypeB, value.value));
                                        break;

                                    case NameSignificance.Type:
                                        throw new Exception(
                                                  "NameSignificance.Type isn't supported on generic dictionaries.");

                                    case NameSignificance.Key:
                                        throw new Exception(
                                                  "NameSignificance.Key isn't supported on generic dictionaries");

                                    default:
                                        throw new ArgumentOutOfRangeException();
                                    }
                                }
                            }
                        }
                    }

                    // If the target is a generic collection
                    else if (typeof(IList).IsAssignableFrom(targetType))
                    {
                        // We need a node for this decoding
                        if (!isNode)
                        {
                            throw new Exception("Loading a generic list requires sources to be nodes");
                        }

                        // Get the target value as a collection
                        IList collection = targetValue as IList;

                        // Get the internal type of this collection
                        Type genericType = targetType.GetGenericArguments()[0];

                        // Create a new collection if merge is disallowed or if the collection is null
                        if (collection == null || !target.AllowMerge)
                        {
                            collection  = Activator.CreateInstance(targetType) as IList;
                            targetValue = collection;
                        }

                        // Store the objects that were already patched
                        List <Object> patched = new List <Object>();

                        // Process the node
                        ConfigNode targetNode = target.FieldName == "self" ? node : node.GetNode(target.FieldName);

                        // Check the config type
                        RequireConfigType[] attributes =
                            (RequireConfigType[])genericType.GetCustomAttributes(typeof(RequireConfigType), true);
                        if (attributes.Length > 0 || genericType == typeof(String))
                        {
                            ConfigType type = genericType == typeof(String) ? ConfigType.Value : attributes[0].Type;
                            if (type == ConfigType.Node)
                            {
                                // Iterate over all of the nodes in this node
                                foreach (ConfigNode subnode in targetNode.nodes)
                                {
                                    // Check for the name significance
                                    switch (target.NameSignificance)
                                    {
                                    case NameSignificance.None:
                                    case NameSignificance.Key when subnode.name == target.Key:

                                        // Check if the type represents patchable data
                                        Object current = null;
                                        if (typeof(IPatchable).IsAssignableFrom(genericType) &&
                                            collection.Count > 0)
                                        {
                                            for (Int32 i = 0; i < collection.Count; i++)
                                            {
                                                if (collection[i].GetType() != genericType)
                                                {
                                                    continue;
                                                }

                                                if (patched.Contains(collection[i]))
                                                {
                                                    continue;
                                                }

                                                IPatchable patchable = (IPatchable)collection[i];
                                                PatchData  patchData =
                                                    CreateObjectFromConfigNode <PatchData>(subnode, "Internal");
                                                if (patchData.name == patchable.name)
                                                {
                                                    // Name matches, check for an index
                                                    if (patchData.index == collection.IndexOf(collection[i]))
                                                    {
                                                        // Both values match
                                                        current = collection[i];
                                                        break;
                                                    }

                                                    if (patchData.index > -1)
                                                    {
                                                        // Index doesn't match, continue
                                                        continue;
                                                    }

                                                    // Name matches, and no index exists
                                                    current = collection[i];
                                                    break;
                                                }

                                                if (patchData.name != null)
                                                {
                                                    // The name doesn't match, continue the search
                                                    continue;
                                                }

                                                // We found the first object that wasn't patched yet
                                                current = collection[i];
                                                break;
                                            }
                                        }

                                        // If no object was found, check if the type implements custom constructors
                                        if (current == null)
                                        {
                                            current = Activator.CreateInstance(genericType);
                                            collection?.Add(current);
                                        }

                                        // Parse the config node into the object
                                        LoadObjectFromConfigurationNode(current, subnode, configName,
                                                                        target.GetChild);
                                        patched.Add(current);
                                        if (collection != null)
                                        {
                                            collection[collection.IndexOf(current)] = current;
                                        }

                                        break;

                                    case NameSignificance.Type:

                                        // Generate the type from the name
                                        Type elementType = ModTypes.FirstOrDefault(t =>
                                                                                   t.Name == subnode.name &&
                                                                                   !Equals(t.Assembly, typeof(HighLogic).Assembly) &&
                                                                                   genericType.IsAssignableFrom(t));

                                        // Check if the type represents patchable data
                                        current = null;
                                        if (typeof(IPatchable).IsAssignableFrom(elementType) &&
                                            collection.Count > 0)
                                        {
                                            for (Int32 i = 0; i < collection.Count; i++)
                                            {
                                                if (collection[i].GetType() != elementType)
                                                {
                                                    continue;
                                                }

                                                if (patched.Contains(collection[i]))
                                                {
                                                    continue;
                                                }

                                                IPatchable patchable = (IPatchable)collection[i];
                                                PatchData  patchData =
                                                    CreateObjectFromConfigNode <PatchData>(subnode, "Internal");
                                                if (patchData.name == patchable.name)
                                                {
                                                    // Name matches, check for an index
                                                    if (patchData.index == i)
                                                    {
                                                        // Both values match
                                                        current = collection[i];
                                                        break;
                                                    }

                                                    if (patchData.index > -1)
                                                    {
                                                        // Index doesn't match, continue
                                                        continue;
                                                    }

                                                    // Name matches, and no index exists
                                                    current = collection[i];
                                                    break;
                                                }

                                                if (patchData.name != null)
                                                {
                                                    // The name doesn't match, continue the search
                                                    continue;
                                                }

                                                // We found the first object that wasn't patched yet
                                                current = collection[i];
                                                break;
                                            }
                                        }

                                        // If no object was found, check if the type implements custom constructors
                                        if (current == null)
                                        {
                                            current = Activator.CreateInstance(elementType);
                                            collection?.Add(current);
                                            if (typeof(ICreatable).IsAssignableFrom(elementType))
                                            {
                                                ICreatable creatable = (ICreatable)current;
                                                creatable.Create();
                                            }
                                        }

                                        // Parse the config node into the object
                                        LoadObjectFromConfigurationNode(current, subnode, configName,
                                                                        target.GetChild);
                                        patched.Add(current);
                                        if (collection != null)
                                        {
                                            collection[collection.IndexOf(current)] = current;
                                        }

                                        break;

                                    default:
                                        continue;
                                    }
                                }
                            }
                            else
                            {
                                // Iterate over all of the nodes in this node
                                foreach (ConfigNode.Value value in targetNode.values)
                                {
                                    // Check for the name significance
                                    switch (target.NameSignificance)
                                    {
                                    case NameSignificance.None:

                                        // Just processes the contents of the node
                                        collection?.Add(ProcessValue(genericType, value.value));
                                        break;

                                    case NameSignificance.Type:

                                        // Generate the type from the name
                                        Type elementType = ModTypes.FirstOrDefault(t =>
                                                                                   t.Name == value.name &&
                                                                                   !Equals(t.Assembly, typeof(HighLogic).Assembly) &&
                                                                                   genericType.IsAssignableFrom(t));

                                        // Add the object to the collection
                                        collection?.Add(ProcessValue(elementType, value.value));
                                        break;

                                    case NameSignificance.Key when value.name == target.Key:

                                        // Just processes the contents of the node
                                        collection?.Add(ProcessValue(genericType, value.value));
                                        break;

                                    default:
                                        continue;
                                    }
                                }
                            }
                        }
                    }
                }

                // If we are dealing with a non generic collection
                else
                {
                    // Check for invalid scenarios
                    if (target.NameSignificance == NameSignificance.None)
                    {
                        throw new Exception(
                                  "Can not infer type from non generic target; can not infer type from zero name significance");
                    }
                }

                // If the member type is a field, set the value
                if (member.MemberType == MemberTypes.Field)
                {
                    ((FieldInfo)member).SetValue(o, targetValue);
                }

                // If the member wasn't a field, it must be a property.  If the property is writable, set it.
                else if (((PropertyInfo)member).CanWrite)
                {
                    ((PropertyInfo)member).SetValue(o, targetValue, null);
                }
            }
        }
Ejemplo n.º 5
0
 public static bool ShouldUpdate(this IPatchable obj, string name)
 {
     return(obj == null || obj.PatchFields == null || obj.PatchFields.Contains(name));
 }