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); }
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); }
public static bool OptionalPatch(IPatchable patchable, ref bool featureToggle) { featureToggle = Patch(patchable); return(featureToggle); }
/// <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); } } }
public static bool ShouldUpdate(this IPatchable obj, string name) { return(obj == null || obj.PatchFields == null || obj.PatchFields.Contains(name)); }