internal static void Work() { var mod = LoadedModManager.RunningMods.First(m => m.Name == "No Water, No Life."); CreateCompatibilityXml(mod); var list = DirectXmlLoader.XmlAssetsInModFolder(mod, DirectoryName + @"\").ToList <LoadableXmlAsset>(); foreach (var item in list) { if (item == null || item.xmlDoc == null || item.xmlDoc.DocumentElement == null) { Log.Error(string.Format("{0}: unknown parse failure", item.fullFolderPath + @"\" + item.name)); } else if (item.xmlDoc.DocumentElement.Name != "Defs") { Log.Error(string.Format("{0}: root element named {1}; should be named Defs", item.fullFolderPath + @"\" + item.name, item.xmlDoc.DocumentElement.Name)); } XmlInheritance.TryRegisterAllFrom(item, mod); } XmlInheritance.Resolve(); //TODO: wtf is this? foreach (var item in list) { // TODO NoWaterNoLife compability //foreach (Def def in item.defPackage) //{ // Log.Message($"Added def {def.defName}"); // DefDatabase<ThinkTreeDef>.Add(def as ThinkTreeDef); //} } foreach (var def in DefDatabase <ThinkTreeDef> .AllDefs.Where(d => d.insertTag == "Humanlike_PostDuty").OrderByDescending(d => d.insertPriority)) { Log.Message($" ThinkTree {def.defName}"); } }
private static IEnumerable <UpdateFeatureDef> LoadAndParseNewsFeatureDefs() { XmlInheritance.Clear(); // As we're moving the updates out of /Defs and into /News, we can no longer rely on the DefDatabase to magically // load all the UpdateFeatureDefs. Instead, we'll have to manually point the reader to the relevant folders. // Overall, we'll stick as much as we can to the vanilla def loading experience, albeit without patches. // Patch metadata has already been cleared, and parsing it again would add too much overhead. // First, gather all XML nodes that represent an UpdateFeatureDef, and remember where they came from // We can't parse them info defs on the spot, because there is inheritance to consider. var newsItemNodes = new List <(ModContentPack pack, XmlNode node, LoadableXmlAsset asset)>(); foreach (var modContentPack in LoadedModManager.RunningMods) { try { var modNewsXmlAssets = DirectXmlLoader.XmlAssetsInModFolder(modContentPack, UpdateFeatureDefFolder); foreach (var xmlAsset in modNewsXmlAssets) { var rootElement = xmlAsset.xmlDoc?.DocumentElement; if (rootElement != null) { foreach (var childNode in rootElement.ChildNodes.OfType <XmlNode>()) { newsItemNodes.Add((modContentPack, childNode, xmlAsset)); } } } } catch (Exception e) { HugsLibController.Logger.Error("Failed to load UpdateFeatureDefs for mod " + $"{modContentPack.PackageIdPlayerFacing}: {e}"); throw; } } // deal with inheritance foreach (var(modContent, node, _) in newsItemNodes) { if (node != null && node.NodeType == XmlNodeType.Element) { XmlInheritance.TryRegister(node, modContent); } } XmlInheritance.Resolve(); var parsedFeatureDefs = new List <UpdateFeatureDef>(); foreach (var(pack, node, asset) in newsItemNodes) { // parse defs try { var def = DirectXmlLoader.DefFromNode(node, asset) as UpdateFeatureDef; if (def != null) { def.modContentPack = pack; def.ResolveReferences(); parsedFeatureDefs.Add(def); } } catch (Exception e) { HugsLibController.Logger.Error($"Failed to parse UpdateFeatureDef from mod {pack.PackageIdPlayerFacing}:\n" + $"{GetExceptionChainMessage(e)}\n" + $"Context: {node?.OuterXml.ToStringSafe()}\n" + $"File: {asset?.FullFilePath.ToStringSafe()}\n" + $"Exception: {e}"); } } XmlInheritance.Clear(); return(parsedFeatureDefs); }
/// <summary> /// INTENT: Decrease compatibility issues caused by modders overwriting (abstract) bases, by making it easy to detect the existence of these bad practices. /// /// Run once on startup. Creates list of all (abstract) bases in vanilla, then compares with bases in mods. Warns with mod name, base and exact filename for any matches found. /// </summary> static XmlSharedBaseDetection() { //since some people have such wonderfully organised mod lists they hit the 1k limit on error logging: Log.ResetMessageCount(); //get all bases in vanilla. List <string> vanillaXmlAttributes = new List <string>(); //Dictionary<string, string> vanillaXmlAttributes = new Dictionary<string, string>(); foreach (ModContentPack mod in LoadedModManager.RunningMods.Where(mod => mod.IsCoreMod)) { foreach (LoadableXmlAsset asset in DirectXmlLoader.XmlAssetsInModFolder(mod, "Defs/")) { if (asset.xmlDoc?.DocumentElement == null) { continue; } XmlNodeList childNodes = asset.xmlDoc.DocumentElement.ChildNodes; { for (int i = 0; i < childNodes.Count; i++) { if (childNodes[i].NodeType != XmlNodeType.Element) { continue; } if (childNodes[i]?.Attributes?["Name"] != null) { vanillaXmlAttributes.Add(childNodes[i].Attributes.GetNamedItem("Name").Value /*, childNodes[i].Name*/); } } } } } if (Prefs.LogVerbose) { Log.Error("Verbose mode detected. AllYourBase will attempt to fix compatibility errors. If problem persists or gets worse, Verify File Integrity or redownload affected mods.", true); } //get all bases in mods and compare them to the vanilla list. foreach (ModContentPack mod in LoadedModManager.RunningMods.Where(mod => !mod.IsCoreMod)) { foreach (LoadableXmlAsset asset in DirectXmlLoader.XmlAssetsInModFolder(mod, "Defs/")) { bool dirty = false; if (asset.xmlDoc?.DocumentElement == null) { continue; } XmlNodeList childNodes = asset.xmlDoc.DocumentElement.ChildNodes; { for (int i = childNodes.Count - 1; i >= 0; i--) { if (childNodes[i].NodeType != XmlNodeType.Element) { continue; } if (childNodes[i]?.Attributes?["Name"] != null && vanillaXmlAttributes.Contains(childNodes[i].Attributes.GetNamedItem("Name").Value) /*&& vanillaXmlAttributes[childNodes[i].Attributes.GetNamedItem("Name").Value] == childNodes[i].Name*/) { Log.Warning("[" + asset.mod.Name + "]" + " causes compatibility errors by overwriting " + childNodes[i].Attributes.GetNamedItem("Name").Value + " in file " + asset.FullFilePath, true); if (Prefs.LogVerbose) { Log.Message($"Attempting fix for {childNodes[i].Attributes.GetNamedItem("Name").Value}", true); dirty = true; asset.xmlDoc.DocumentElement.RemoveChild(childNodes[i]); } } } } if (dirty) { asset.xmlDoc.Save(asset.FullFilePath); } } } foreach (ChemicalDef item in DefDatabase <ChemicalDef> .AllDefsListForReading) { if (item.addictionHediff?.hediffClass == null) { Log.Error($"{item.defName} from mod {item.modContentPack.Name} has no addictionHediff (or missing hediffClass). This will break raids and worldgen. Misconfigured XML or parent."); } } }
static List <NewFieldData> CollectFields(int inCrc, out int outCrc) { outCrc = inCrc; var fieldsToAdd = new List <NewFieldData>(); foreach (var mod in LoadedModManager.RunningModsListForReading) { var assets = DirectXmlLoader.XmlAssetsInModFolder(mod, PrepatchesFolder); foreach (var ass in assets) { if (ass.name != FieldsXmlFile) { continue; } foreach (var e in ass.xmlDoc["Fields"].OfType <XmlElement>()) { var parsed = ParseFieldData(e, out bool success); parsed.ownerMod = mod.Name; if (success) { fieldsToAdd.Add(parsed); } else { ErrorXML($"{mod.Name}: Error parsing {parsed}"); } } outCrc = Gen.HashCombineInt(outCrc, new CRC32().GetCrc32(new MemoryStream(Encoding.UTF8.GetBytes(ass.xmlDoc.InnerXml)))); } } foreach (var fieldsPerType in fieldsToAdd.Where(f => f.isEnum).GroupBy(f => f.targetType)) { var targetType = GenTypes.GetTypeInAnyAssembly(fieldsPerType.Key); var enumType = Enum.GetUnderlyingType(targetType); var max = Util.ToUInt64(enumType.GetField("MaxValue").GetRawConstantValue()); var taken = AccessTools.GetDeclaredFields(targetType).Where(f => f.IsLiteral).Select(f => Util.ToUInt64(f.GetRawConstantValue())).ToHashSet(); foreach (var f in fieldsPerType) { var free = Util.FindNotTaken(f.enumPreferred, max, taken); if (free == null) { ErrorXML($"{f.ownerMod}: Couldn't assign a value for {f}"); fieldsToAdd.Remove(f); continue; } f.defaultValue = Util.FromUInt64(free.Value, enumType); taken.Add(free.Value); } } foreach (var f in fieldsToAdd) { InfoXML($"{f.ownerMod}: Parsed {f}"); } return(fieldsToAdd); }