CIv6 ModInfo Schema

PlotinusRedux

Warlord
Joined
Jul 11, 2013
Messages
196
After looking at the example mods and DLC, examining Mods.sqlite, testing, and reviewing the main .exe disassembly, I think I've identified all the valid elements, etc., currently allowed in the .modinfo files.

Much of this information is already at least partially known, though some of it has been incomplete or incorrect. Many .modinfo files currently contain incorrect usage that doesn't harm anything but also does nothing, such as using a <Component> element under Settings\Custom\Items, which ignores anything but <File> elements.

Here is an XSD that can be used to validate .modinfo files with any XML schema validator--there's a free online one at http://www.utilities-online.info/xsdvalidation/ that's slow but works if you don't have another tool. There's a faster one at http://www.xmlforasp.net/SchemaValidator.aspx but it seemed to report incorrect line numbers for errors.

Code:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">

  <xs:annotation>
    <xs:documentation>
      Unofficial schema for Civ 6 .modinfo files.  Created by PlotinusRedux.  Version 0.2  1/5/2016
    </xs:documentation>
  </xs:annotation>

  <xs:complexType name="ModRelations">
    <xs:sequence>
      <xs:element name="Mod">
        <xs:complexType>
          <xs:attribute name="id" type="xs:string" use="required"/>
          <xs:attribute name="title" type="xs:string"/>
        </xs:complexType>
      </xs:element>
    </xs:sequence>
  </xs:complexType>

  <xs:complexType name="ItemList">
    <xs:sequence>
      <xs:element name="File" minOccurs="1" maxOccurs="unbounded">
        <xs:complexType>
          <xs:simpleContent>
            <xs:extension base="xs:string">
              <xs:attribute name="Priority" type="xs:integer" default="0">
                <xs:annotation>
                  <xs:documentation>Signed integer determining the load order of files within the component.  Higher numbers load earlier.</xs:documentation>
                </xs:annotation>
              </xs:attribute>
            </xs:extension>
          </xs:simpleContent>
        </xs:complexType>
      </xs:element>
    </xs:sequence>
  </xs:complexType>

  <xs:complexType name="BaseComponent">
    <xs:all>
      <xs:element name="Properties" minOccurs="0">
        <xs:complexType>
          <xs:all>
            <xs:element name="RuleSet" type="xs:string" minOccurs="0"/>
            <xs:element name="LoadOrder" type="xs:integer" minOccurs="0">
              <xs:annotation>
                <xs:documentation>Signed integer determining component load order globally across mods.  Lower numbers load earlier.</xs:documentation>
              </xs:annotation>
            </xs:element>             
          </xs:all>
        </xs:complexType>
      </xs:element>
      <xs:element name="Items" type="ItemList" minOccurs="1"/>
    </xs:all>
    <xs:attribute name="id" type="xs:string" default=""/>
  </xs:complexType>

  <xs:complexType name="ContextComponent">
    <xs:all>
      <xs:element name="Properties" minOccurs="0">
        <xs:complexType>
          <xs:all>
            <xs:element name="RuleSet" type="xs:string" minOccurs="0"/>
            <xs:element name="LoadOrder" type="xs:integer" minOccurs="0">
              <xs:annotation>
                <xs:documentation>Signed integer determining component load order globally across mods.  Lower numbers load earlier.</xs:documentation>
              </xs:annotation>
            </xs:element>           
            <xs:element name="Context" type="xs:string" minOccurs="1">
              <xs:annotation>
                <xs:documentation>UI Context, like "InGame"</xs:documentation>
              </xs:annotation>
            </xs:element>         
          </xs:all>
        </xs:complexType>
      </xs:element>
      <xs:element name="Items" type="ItemList" minOccurs="1"/>
    </xs:all>
    <xs:attribute name="id" type="xs:string" default=""/>
  </xs:complexType>

  <xs:complexType name="BaseSetting">
    <xs:all>   
      <xs:element name="Properties" minOccurs="0">
        <xs:complexType>
          <xs:all> 
            <xs:element name="LoadOrder" type="xs:integer" minOccurs="0">
              <xs:annotation>
                <xs:documentation>Signed integer determining component load order globally across mods.  Lower numbers load earlier.</xs:documentation>
              </xs:annotation>
            </xs:element>             
          </xs:all>
        </xs:complexType>
      </xs:element>   
      <xs:element name="Items" type="ItemList" minOccurs="1"/>
    </xs:all>
    <xs:attribute name="id" type="xs:string" default=""/>
  </xs:complexType>

  <xs:complexType name="MapSetting">
    <xs:all>
      <xs:element name="Properties" minOccurs="0">
        <xs:complexType>
          <xs:all>
            <xs:element name="Group" type="xs:string" minOccurs="1"/>
            <xs:element name="Name" type="xs:string" minOccurs="1"/>
            <xs:element name="Description" type="xs:string" minOccurs="0"/>
          </xs:all>
        </xs:complexType>
      </xs:element>
      <xs:element name="Items" type="ItemList" minOccurs="1"/>
    </xs:all>
    <xs:attribute name="id" type="xs:string" default=""/>
  </xs:complexType>

  <xs:group name="Translations">
    <xs:all>
      <xs:element name="en_US" type="xs:string" minOccurs="0"/>
      <xs:element name="fr_FR" type="xs:string" minOccurs="0"/>
      <xs:element name="de_DE" type="xs:string" minOccurs="0"/>
      <xs:element name="it_IT" type="xs:string" minOccurs="0"/>
      <xs:element name="es_ES" type="xs:string" minOccurs="0"/>
      <xs:element name="ja_JP" type="xs:string" minOccurs="0"/>
      <xs:element name="ru_RU" type="xs:string" minOccurs="0"/>
      <xs:element name="pl_PL" type="xs:string" minOccurs="0"/>
      <xs:element name="ko_KR" type="xs:string" minOccurs="0"/>
      <xs:element name="zh_Hant_HK" type="xs:string" minOccurs="0"/>
      <xs:element name="zh_Hans_CN" type="xs:string" minOccurs="0"/>
      <xs:element name="pt_BR" type="xs:string" minOccurs="0"/>
    </xs:all>
  </xs:group>

  <xs:element name="Mod">
    <xs:complexType>
      <xs:all>
        <xs:element name="Properties" minOccurs="1">
          <xs:complexType>
            <xs:sequence>
              <xs:any minOccurs="0" maxOccurs="unbounded" processContents="skip"/>
            </xs:sequence>
            <!--
            <xs:all>
              <xs:element name="Name" type="xs:string" minOccurs="1"/>
              <xs:element name="Teaser" type="xs:string" minOccurs="1"/>
              <xs:element name="Trusted" type="xs:string" minOccurs="0"/>
              <xs:element name="ShowInBrowser" type="xs:string" minOccurs="0"/>
              <xs:element name="DisabledAtStartup" type="xs:int" minOccurs="0"/>
              <xs:element name="EnabledByDefault" type="xs:int" minOccurs="0"/>
              <xs:element name="Stability" type="xs:string" minOccurs="0"/>
              <xs:element name="Description" type="xs:string" minOccurs="0"/>
              <xs:element name="Authors" type="xs:string" minOccurs="0"/>
              <xs:element name="SpecialThanks" type="xs:string" minOccurs="0"/>
            </xs:all>
             -->
          </xs:complexType>
        </xs:element>
        <xs:element name="References" type="ModRelations" minOccurs="0"/>
        <xs:element name="Dependencies" type="ModRelations" minOccurs="0"/>
        <xs:element name="Blocks" type="ModRelations" minOccurs="0"/>
        <xs:element name="Files" minOccurs="1">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="File" type="xs:string" minOccurs="0" maxOccurs="unbounded"/>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
        <xs:element name="Components" minOccurs="0">
          <xs:annotation>
            <xs:documentation>Items loaded when game first starts</xs:documentation>
          </xs:annotation>
          <xs:complexType>
            <xs:choice minOccurs="1" maxOccurs="unbounded">
              <xs:element name="UpdateDatabase" type="BaseComponent" minOccurs="0" maxOccurs="unbounded">
                <xs:annotation>
                  <xs:documentation>Updates to the main game database (DebugGameplay.sqlite).  FileTypes: xml, sql</xs:documentation>
                </xs:annotation>
              </xs:element>
              <xs:element name="UpdateAudio" type="BaseComponent" minOccurs="0" maxOccurs="unbounded">
                <xs:annotation>
                  <xs:documentation>Adds audio files.  FileTypes: ini, bnk, txt, xml</xs:documentation>
                </xs:annotation>
              </xs:element>
              <xs:element name="UpdateARX" type="BaseComponent" minOccurs="0" maxOccurs="unbounded">
                <xs:annotation>
                  <xs:documentation>Logitech ARX for displaying game info on a mobile device.  FileTypes: html, etc.?</xs:documentation>
                </xs:annotation>
              </xs:element>
              <xs:element name="ModArt" type="BaseComponent" minOccurs="0" maxOccurs="unbounded">
                <xs:annotation>
                  <xs:documentation>.dep file that tells which new .artdef files to load.  The .artdef files must have the same name as base artdefs, and aren't included in the .modinfo file at all, just the mod's directory.  FileTypes: dep</xs:documentation>
                </xs:annotation>
              </xs:element>
              <xs:element name="UserInterface" type="ContextComponent" minOccurs="0" maxOccurs="unbounded">
                <xs:annotation>
                  <xs:documentation>New UI files.  For each XXX.xml, an XXX.lua is implied FileTypes: xml</xs:documentation>
                </xs:annotation>
              </xs:element>
              <xs:element name="LocalizedText" type="BaseComponent" minOccurs="0" maxOccurs="unbounded">
                <xs:annotation>
                  <xs:documentation>In-game localized text.  FileTypes: xml</xs:documentation>
                </xs:annotation>
              </xs:element>
              <xs:element name="GameplayScripts" type="BaseComponent" minOccurs="0" maxOccurs="unbounded">
                <xs:annotation>
                  <xs:documentation>Scripts that run in the main Lua context at the start of the game and on reloads.  FileTypes: lua</xs:documentation>
                </xs:annotation>
              </xs:element>
              <xs:element name="ImportFiles" type="BaseComponent" minOccurs="0" maxOccurs="unbounded">
                <xs:annotation>
                  <xs:documentation>Overrides of files under the main Assets folder.  FileTypes: xml, lua, *?</xs:documentation>
                </xs:annotation>
              </xs:element>
              <xs:element name="Icons" type="BaseComponent" minOccurs="0" maxOccurs="unbounded">
                <xs:annotation>
                  <xs:documentation>XML files to add icons to the game.  FileTypes: xml</xs:documentation>
                </xs:annotation>
              </xs:element>
            </xs:choice>
          </xs:complexType>
        </xs:element>
        <xs:element name="Settings" minOccurs="0">
          <xs:annotation>
            <xs:documentation>Objects loaded on game set up</xs:documentation>
          </xs:annotation>
          <xs:complexType>
            <xs:choice minOccurs="1" maxOccurs="unbounded">
              <xs:element name="Custom" type="BaseSetting" minOccurs="0" maxOccurs="unbounded">
                <xs:annotation>
                  <xs:documentation>Updates to Configuration database (DebugConfiguration.sqlite).  Some people are putting Component elements under Items here--they don't get read into the database.  FileTypes: xml, sql</xs:documentation>
                </xs:annotation>
              </xs:element>
              <xs:element name="Map" type="MapSetting" minOccurs="0" maxOccurs="unbounded">
                <xs:annotation>
                  <xs:documentation>Custom map.  FileTypes: Civ6Map</xs:documentation>
                </xs:annotation>
              </xs:element>
              <xs:element name="LocalizedText" type="BaseSetting" minOccurs="0" maxOccurs="unbounded">
                <xs:annotation>
                  <xs:documentation>Localized text for game setup screens.  FileTypes: xml</xs:documentation>
                </xs:annotation>
              </xs:element>
              <xs:element name="WorldBuilder" type="BaseSetting" minOccurs="0" maxOccurs="unbounded">
                <xs:annotation>
                  <xs:documentation>Not sure, but presumably updates to the world builder tables.  FileTypes: xml, sql?</xs:documentation>
                </xs:annotation>
              </xs:element>
              <xs:element name="Icons" type="BaseComponent" minOccurs="0" maxOccurs="unbounded">
                <xs:annotation>
                  <xs:documentation>XML files to add icons to the game that are needed for the game setup screens.  FileTypes: xml</xs:documentation>
                </xs:annotation>
              </xs:element>
              <xs:element name="ModArt" type="BaseComponent" minOccurs="0" maxOccurs="unbounded">
                <xs:annotation>
                  <xs:documentation>.dep file that tells which new .artdef files to load that are needed for the game setup screens.  The .artdef files must have the same name as base artdefs, and aren't included in the .modinfo file at all, just the mod's directory.  FileTypes: dep</xs:documentation>
                </xs:annotation>
              </xs:element>
            </xs:choice>
          </xs:complexType>
        </xs:element>
        <xs:element name="LocalizedText" minOccurs="0">
          <xs:annotation>
            <xs:documentation>Optional translations for text in .modinfo itself (mod/Properties and mod/Settings/Map/Properties)</xs:documentation>
          </xs:annotation>
          <xs:complexType>
            <xs:sequence>
              <xs:element name="Text" maxOccurs="unbounded">
                <xs:complexType>
                  <xs:group ref="Translations"/>
                  <xs:attribute name="id" type="xs:string"/>
                </xs:complexType>
              </xs:element>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
      </xs:all>
      <xs:attribute name="id" type="xs:string" use="required"/>
      <xs:attribute name="version" type="xs:int" default="0"/>
    </xs:complexType>
  </xs:element>
</xs:schema>

All example of a well formed .modinfo file correctly using all available elements:

Code:
<?xml version="1.0" encoding="utf-8"?>
<Mod id="79a96bee-c555-45b4-ae34-5221c115ef92" version = "1">

  <!--Localizable mod property text.  Only Name and Description seem to be shown in game-->
  <Properties>
    <!--Required and displayed in game-->
    <Name>LOC_NAME</Name>
    <Teaser>LOC_TEASER</Teaser>

    <!--Optional, really for Fixaris Use Only
    <Trusted>1784197304</Trusted>
    <ShowInBrowser>AlwaysHidden</ShowInBrowser>
    <DisabledAtStartup>0</DisabledAtStartup>
    <EnabledByDefault>0</EnabledByDefault>
     -->

    <!--Common but not used by game-->
    <Stability>LOC_STABILITY</Stability>
    <Description>LOC_DESCRIPTION</Description>
    <Authors>LOC_AUTHORS</Authors>
    <SpecialThanks>LOC_SPECIAL_THANKS</SpecialThanks>
  </Properties>

  <!--Referenced mods.  This mod will load after referenced mods if both are enabled-->
  <References>
    <Mod id="79a96bee-c555-45b4-ae34-5221c115efa0" title="somemod0" />
  </References>

  <!--Required mods-->
  <Dependencies>
    <Mod id="79a96bee-c555-45b4-ae34-5221c115efa2" title="somemod2" />
  </Dependencies>

  <!--Incompatible mods.-->
  <Blocks>
    <Mod id="79a96bee-c555-45b4-ae34-5221c115efa4" title="somemod4" />
  </Blocks>

  <!--List of all files used in the mod-->
  <Files>
    <File>SomeDatabaseUpdate1.xml</File>
    <File>SomeDatabaseUpdate2.sql</File>
    <File>SomeSound.wem</File>
    <File>SomeIcon.pgn</File>
    <File>SomeArtdef.artdef</File>
    <File>SomeUI.xml</File>
    <File>SomeUI.lua</File>
    <File>SomeText.xml</File>
    <File>SomeScript.lua</File>
    <File>SomeFileOverride1.xml</File>
    <File>SomeFileOverride2.lua</File>
    <File>SomeConfigDatabaseUpdate1.xml</File>
    <File>SomeConfigDatabaseUpdate2.sql</File>
    <File>SomeMap.Civ6Map</File>
    <File>SomeWorldbuilderFile.xml</File>
  </Files>

  <!--Objects loaded after the game starts-->
  <Components>
    <!--Updates to the main game database (DebugGameplay.sqlite)-->
    <UpdateDatabase id="Arbitrary_UpdateDatabase">
      <Properties>
        <RuleSet>RULESET_SOME_RULESET</RuleSet>
        <!--No idea what LoadOrder does, it's optional-->
        <LoadOrder>100</LoadOrder>
      </Properties>
      <Items>
        <File>SomeDatabaseUpdate1.xml</File>
        <File Priority="2">SomeDatabaseUpdate2.sql</File>
      </Items>
    </UpdateDatabase>

    <!--Some Audio thing, Wem files under Base/Platforms/Windows/Audio maybe?-->
    <!--No idea how this would work with different platforms-->
    <UpdateAudio id="Arbitrary_UpdateAudio">
      <Properties>
        <RuleSet>RULESET_SOME_RULESET</RuleSet>
      </Properties>
      <Items>
        <File>SomeSound.wem</File>
      </Items>
    </UpdateAudio>

    <!--Files related to Logitech ARX for view game info on mobile devices-->
    <UpdateARX id="Arbitrary_UpdateARX">
      <Properties>
        <RuleSet>RULESET_SOME_RULESET</RuleSet>
      </Properties>
      <Items>
        <File>SomeIcon.pgn</File>
      </Items>
    </UpdateARX>

    <!--.dep file for .artdef files.  The .artdef files themselves aren't included
        in .modinfo at all, only in the mod's directory -->
    <ModArt id="Arbitrary_UpdateModArt">
      <Properties>
        <RuleSet>RULESET_SOME_RULESET</RuleSet>
      </Properties>
      <Items>
        <File>SomeArtdef.artdef</File>
      </Items>
    </ModArt>

    <!--New UI files-->
    <UserInterface id="Arbitrary_UserInterface">
      <Properties>
        <RuleSet>RULESET_SOME_RULESET</RuleSet>
        <Context>InGame</Context>
      </Properties>
      <Items>
        <File>SomeUI.xml</File>
        <!--SomeUI.lua is implied-->
      </Items>
    </UserInterface>

    <!--Localized Text-->
    <LocalizedText id="Arbitrary_LocalizedText">
      <Properties>
        <RuleSet>RULESET_SOME_RULESET</RuleSet>
      </Properties>
      <Items>
        <File>SomeText.xml</File>
      </Items>
    </LocalizedText>

    <!--Scripts to run when the mod starts or reloads-->
    <GameplayScripts id="Arbitrary_GameplayScripts">
      <Properties>
        <RuleSet>RULESET_SOME_RULESET</RuleSet>
      </Properties>
      <Items>
        <File>SomeScript.lua</File>
      </Items>
    </GameplayScripts>

    <!--Overrides of files under the main Assets folder-->
    <ImportFiles id="Arbitrary_ImportFiles">
      <Properties>
        <RuleSet>RULESET_SOME_RULESET</RuleSet>
      </Properties>
      <Items>
        <File>SomeFileOverride1.xml</File>
        <File>SomeFileOverride2.lua</File>
      </Items>
    </ImportFiles>

    <!--Adds icons-->
    <Icons id="Arbitrary_Icons">
      <Properties>
        <RuleSet>RULESET_SOME_RULESET</RuleSet>
      </Properties>
      <Items>
        <File>SomeIcons.xml</File>
      </Items>
    </Icons>
 
  </Components>

  <!--Objects loaded on game set up-->
  <Settings>
    <!--Updates to Configuration database (DebugConfiguration.sqlite)-->
    <Custom id="Arbitrary_Custom">
      <Items>
        <!--Some peopler are putting <Component> tags here,-->
        <!--But they're not even read into the Mods.sqlite database.-->
        <File>SomeConfigDatabaseUpdate1.xml</File>
        <File>SomeConfigDatabaseUpdate2.sql</File>
      </Items>
    </Custom>

    <!--Custom map-->
    <Map id="Arbitrary_Custom_Map">
      <Properties>
        <Group>Some_Map_Group</Group>
        <Name>LOC_MAP_NAME1</Name>
        <Description>LOC_MAP_DESCRIPTION1</Description>
      </Properties>
      <Items>
        <File>SomeMap.Civ6Map</File>
      </Items>
    </Map>

    <!-- Localized Text for configuration screens -->
    <LocalizedText id="Arbitrary_LocalizedText2">
      <Items>
        <File>ConfigTextFile.xml</File>
      </Items>
    </LocalizedText>

    <!--No real idea-->
    <WorldBuilder id="Arbitrary_WorldBuilder">
      <Items>
        <File>SomeWorldbuilderFile.xml</File>
      </Items>
    </WorldBuilder>

    <!--Adds icons needed during game set up-->
    <Icons id="Arbitrary_Icons2">
      <Items>
        <File>SomeIcons.xml</File>
      </Items>
    </Icons>

    <!--.dep file for .artdef files needed during game setup.
        The .artdef files themselves aren't included
        in .modinfo at all, only in the mod's directory -->
    <ModArt id="Arbitrary_UpdateModArt2">
      <Items>
        <File>SomeArtdef.artdef</File>
      </Items>
    </ModArt>
  </Settings>

  <!--Localized text for values used in .modinfo itself-->
  <!--Seems limited to Mod/Properties and Mod/Settings/Map/Properties-->
  <LocalizedText>
    <Text id="LOC_NAME">
      <en_US>Mod Name</en_US>
    </Text>
    <Text id="LOC_STABILITY">
      <en_US>Beta, etc.</en_US>
    </Text>
    <Text id="LOC_TEASER">
      <en_US>Description Shown In Game</en_US>
    </Text>
    <Text id="LOC_DESCRIPTION">
      <en_US>Full Description (Not Shown Anywhere)</en_US>
    </Text>
    <Text id="LOC_AUTHORS">
      <en_US>Authors</en_US>
    </Text>
    <Text id="LOC_SPECIAL_THANKS">
      <en_US>All the little people</en_US>
    </Text>
    <Text id="LOC_MAP_NAME1">
      <en_US>Custom Map Name</en_US>
    </Text>
    <Text id="LOC_MAP_DESCRIPTION1">
      <en_US>Custom Map Description</en_US>
    </Text>
  </LocalizedText>
</Mod>

And finally some useful SQL statements to run against the Mods.sqlite database to verify your mod's values really are getting into the system the way you want:

Code:
SELECT * FROM Mods WHERE ModId = "79a96bee-c555-45b4-ae34-5221c115ef92";

SELECT Name, Value, Locale, Text
FROM ModProperties mp
    INNER JOIN Mods m on mp.ModRowId = m.ModRowId
    LEFT OUTER JOIN LocalizedText l ON l.Tag = mp.Value
WHERE m.ModId = "79a96bee-c555-45b4-ae34-5221c115ef92"
ORDER BY Name;

SELECT ComponentType, ComponentId, Path, Priority
FROM ModFiles f
    INNER JOIN ComponentFiles cf ON f.FileRowId = cf.FileRowId
    INNER JOIN Components c ON cf.ComponentRowId = c.ComponentRowId
    INNER JOIN Mods m on c.ModRowId = m.ModRowId
WHERE m.ModId = "79a96bee-c555-45b4-ae34-5221c115ef92"
ORDER BY ComponentType, ComponentId, Path;

SELECT ComponentType, ComponentId, Name, Value
FROM ComponentProperties cp
    INNER JOIN Components c ON cp.ComponentRowId = c.ComponentRowId
    INNER JOIN Mods m on c.ModRowId = m.ModRowId
WHERE m.ModId = "79a96bee-c555-45b4-ae34-5221c115ef92"
ORDER BY ComponentType, ComponentId, Name;

SELECT SettingType, SettingId, Path, Priority
FROM ModFiles f
    INNER JOIN SettingFiles sf ON f.FileRowId = sf.FileRowId
    INNER JOIN Settings s ON sf.SettingRowId = s.SettingRowId
    INNER JOIN Mods m on s.ModRowId = m.ModRowId
WHERE m.ModId = "79a96bee-c555-45b4-ae34-5221c115ef92"
ORDER BY SettingType, SettingId, Path;

SELECT SettingType, SettingId, Name, Value, Locale, Text
FROM SettingProperties sp
    INNER JOIN Settings s ON sp.SettingRowId = s.SettingRowId
    INNER JOIN Mods m on s.ModRowId = m.ModRowId
    LEFT OUTER JOIN LocalizedText l ON l.Tag = sp.Value
WHERE m.ModId = "79a96bee-c555-45b4-ae34-5221c115ef92"
ORDER BY SettingType, SettingId, Name;

SELECT OtherModId, OtherModTitle, Relationship
FROM ModRelationships r
    INNER JOIN Mods m on r.ModRowId = m.ModRowId
WHERE m.ModId = "79a96bee-c555-45b4-ae34-5221c115ef92"
ORDER BY OtherModId, OtherModTitle;

File versions can be downloaded from:

http://qskoz.com/Civ6/Civ6ModInfoSchema.xsd
http://qskoz.com/Civ6/Test.modinfo
http://qskoz.com/Civ6/ModInfo.sql

[Edit: Changed <ModArt> filetype to .dep]
[Edit: Update ARX comments to indicate it relates to the Logitech Arx app for showing game information on a phone]
[Edit: Updated for Winter patch:
*Added new <Icons> element;
*Updated description of <ModArt>
*Added <LoadOrder> property to <UpdateDatabase>
*Added <Icons> and <ModArt> to Settings, as they can appear under <Components> or <Settings>]
[Edit 1/5/17: Added <LoadOrder> to all components and updated its annotation.]
[Edit 1/24/17: If the optional "id" attribute is used for a component, it must start with a letter and contain no spaces, as it is sent directly to sqlite to create a save point.]
 
Last edited:
Common .modinfo mistakes I'm seeing:

  • The id attribute of the root Mod element can technically be any unique string, but following the example of the Firaxis mods should be a GUID without the enclosing brackets. https://www.guidgenerator.com/online-guid-generator.aspx is a quick way to get a GUID in the correct format.
  • The version attribute of the root Mod element is an integer. The game reads <Mod ... version="1.5"> as version 1. My suggestion would be to treat it as MajorVersion * 100 + MinorVersion. Of course, the game doesn't currently do anything with it other than store it in Mods.sqlite, it doesn't even display it anywhere, so it doesn't really matter at this point--mods are either completely reloaded every time the game starts, or it's using the file timestamps in the database to decide when to reload them, I'm not sure which.
  • Only <File> elements under <Items> do anything for any component or setting element. Some people have been doing something like this, which does nothing as far as the game is concerned, and there is no reason to have a <Settings> element at all if this is all it's being used for:
    Code:
        <Settings>
            <Custom id="SomeID">
                <Items>
                    <!--This does nothing-->
                    <Component>SomeComponentID</Component>
                </Items>
            </Custom>
        </Settings>
  • <File> elements under <Items> can have a Priority attribute. No other attributes do anything. <File> elements under <Files> don't read any attributes. <File import="1">, for instance, doesn't add anything to <File> in either case.
  • <Mod> elements under <References>, <Dependencies>, and <Blocks> only have an "id" and "title" attribute. Any other attribute (like "minVersion") is ignored.
  • <Properties> under Components only use a <RuleSet> element, except for <UserInterface>, which also has a <Context> element. Settings elements use no <Properties> element, except for <Map>, which uses <Group>, <Name>, and <Description>. Everything else is ignored by the game.
  • Under the root <Mod> element, the only elements under <Properties> used by the game are <Name>, <Teaser>, and the DLC properties <Trusted>, <ShowInBrowser>, <DisabledAtStartup>, and <EnabledByDefault>. Other properties get read into Mods.sqlite but aren't ever displayed in the game, so are really just informative for human viewers of the .modinfo file. So if you're putting in the same text for <Description> and <Teaser>, you can just leave <Description> out. Also, there's no reason to localize anything but <Name> and <Teaser>.
 
Last edited:
Thank you :)

<Description> and <Authors> are defined in the Firaxis scenarios, they may be used in the future.

From my test <File>SomeUI.lua</File> is not required under <UserInterface>, the XML is enough (but the XML must define a context for the Lua file IIRC)
 
I think this should be moved to the Modding Tutorials and Reference subforum.
 
@Gedemon, yeah, I just noticed Base\Assets\UI\Text\en_US\UI_Mods_Text.xml, which does have localized text for things like "Author(s):" and "Special Thanks:", as well as "BROWSE WORKSHOP", though nothing references them yet, so maybe they will use them once they get the Workshop stuff set up.

@Deliverator, anyone feel free to move it, or edit and add the information to the existing .modinfo post, etc.
 
Moderator Action: Moved to Tutorial & Reference, I've closed the outdated thread
 
Ok, now we have two DLC that add arts, time to examine their structure (and yes, seems some code was missing to allow it)
 
How to make a different version of a mod?
I was just changing the number in the version field
Before:
Code:
<Mod id="79a96bee-c555-45b4-ae34-5221c115ef92" version = "6">
After:
Code:
<Mod id="79a96bee-c555-45b4-ae34-5221c115ef92" version = "16">
But apparently i cant reload the saved game. It said, not every mod installed (i forget the exact words).
It ll work if i change the version to 7.
 
Last edited:
@TimurB that's odd, my guess would be it won't load older versions of the mod with the saved game, and even though the field is saved as an int in the DB, it's doing a string comparison to check if the version is higher, since in a string comparison, "16" < "6". If I'm right, 59 shouldn't load and 60 should and you're stuck using versions that start with the digit "6" or higher now...

A good reason to go with Major Version * 100 + Minor version, I guess, at least until you get to major version 10.... Left padding with 0's wouldn't help, because they'd get thrown out when it's converted to an int for the DB.
 
So does anyone know of a reason for the <Files> element off the root to exist? Would it ever contain a file that hadn't already been listed in the <Items> section of some element under <Components> or <Settings>?

I'm working on my auto-merge and I was working on linking up the <Files> element with the various <Items> elements when I realized I can't think of a single reason to even parse <Files> since I've already built the list of files from the various <Items> and even included the "implied" files that wouldn't be under the <Files> element--like *.artdef, the *.lua files that go with UI *.xml files, etc.

I know in Civ 5 the <Files> element included tags like the MD5 hash and whether to import the file, but you can see from the ModFiles table all it's reading is the filename and path. I could see it having a use if it included the files like *.artdef that don't show up under any <Items> elements, but in Firaxis's own DLC they didn't included those. It definitely has to be there or the game doesn't load them into ModFiles, but isn't it completely redundant at this point, or am I missing something?
 
You seem very knowledgable about matters, so maybe you can help me with a vexing problem in my mod.

I've got a large mod package including overhauls to many game systems, including the Tech and Civics trees. The Tech and Civics tree xml files are formulated in the exact same way, and both are well-formed (passes all checks with XML editor, and has worked over hundreds of games), but only the Civics tree consistently has a UI display error where some of the civics overlap each other some of the time.

On some loads everything is hunky dory and all the civics display perfectly. On other loads, the tech prereqs function normally, but the UI "erases" some of them by overlapping two different into columns together.

Does this have something to do with the different natures of the Tech and Civics tree? You mentioned that it's not handled as a traditional XML file, but a UI one? I'm at my wit's end trying to figure this out and would really appreciate any assistance :)
 
@anansethespider , if you were talking to me, I wouldn't say I'm "very knowledgeable", I only started Civ modding with Civ 6 and I've never used Lua before now. Really my previous experience is with hacking games that weren't meant to be modded rather than modding games that were. Mainly I read what the more experienced people around here post avidly and do a lot of banging my head into walls until something breaks.

Short Answer: From looking at the lua code, within each era, only include civics on the same UITreeRow if they are related to each other in a prerequisite chain. I.e., if C1, C2, and C3 are civics of the same era on the same UITreeRow, it has to be the case that you can't get C2 without having C1, and you can't get C3 without having C2. They don't have to be immediate prerequisites of each other, just somewhere in the same chain.

Long Answer: I haven't actually messed with the civics or tech trees yet myself, but the relevant code for formatting the civics tree is in base/assets/ui/screens/CivicsTree.lua[252] in the AllocateUI() routine, and it's got a clear bug in it. Starting at line 307 of CivicsTree.lua instead of:

Code:
            -- No prereqs? Just put at start, otherwise push forward past them all.
            if item.Prereqs ~= nil then
                for _,prereq in ipairs(item.Prereqs) do                            -- For every prereq                
                    local isPrereqMatched :boolean = false;                
                    for x=pos,maxColumns,1 do                                    -- For every column (from last highest found start position)
                        for y=ROW_MIN, ROW_MAX,1 do                                -- For every row in the column
                            if grid.rows[y].columns[x] ~= nil then                -- Is a prereq in that position of the grid?
                                if prereq == grid.rows[y].columns[x] then        -- And is it a prereq for this item?
                                    pos = x + 1;                                -- If so, this item can only exist at least 1 column further over from the prereq
                                    isPrereqMatched = true;
                                    break;
                                end
                            end
                        end
                        if isPrereqMatched then                         
                            -- Ensuring this node wasn't just placed on top of another:
                            while( grid.rows[item.UITreeRow].columns[pos] ~= nil ) do
                                pos = pos + 1;
                            end
                            break;
                        end
                    end
                end
            end

it should be:

Code:
            -- No prereqs? Just put at start, otherwise push forward past them all.
            if item.Prereqs ~= nil then
                for _,prereq in ipairs(item.Prereqs) do                            -- For every prereq                
                    local isPrereqMatched :boolean = false;                
                    for x=pos,maxColumns,1 do                                    -- For every column (from last highest found start position)
                        for y=ROW_MIN, ROW_MAX,1 do                                -- For every row in the column
                            if grid.rows[y].columns[x] ~= nil then                -- Is a prereq in that position of the grid?
                                if prereq == grid.rows[y].columns[x] then        -- And is it a prereq for this item?
                                    pos = x + 1;                                -- If so, this item can only exist at least 1 column further over from the prereq
                                    isPrereqMatched = true;
                                    break;
                                end
                            end
                        end
                        if isPrereqMatched then                         
                            break;
                        end
                    end
                end
            end
            -- Ensuring this node wasn't just placed on top of another:
            while( grid.rows[item.UITreeRow].columns[pos] ~= nil ) do
                pos = pos + 1;
            end

You can try changing that in CivicsTree.lua under base and if it works, either adjust your UITreeRow values to work around the bug or include the fixed version of CivicsTree.lua in your mod in the <ImportFiles> section of your .modinfo.

The bug would occur if you had 2 civics C1 and C2 of the same era with the same UITreeRow value where either (1) neither C1 nor C2 had a prerequisite from within their era, or (2) C1 didn't have a prerequisite from within it's era and C1 wasn't in a prerequisite chain leading up to C2.

(2) is likely the case here, as (1) would always cause the issue, while (2) would appear randomly based on the undeterministic order of for...in on non-sequence tables in Lua.

So the work-around would be to insure that within each era you only include civics on the same UITreeRow that belong to the same prerequisite chain. Of course that limits you to a total of 7 independent chains since there are only 7 rows.

On the other hand, using the code fix rather than the work-around will have an undesirable side effect--civics will never overlap, but in both cases above C1 and C2 will appear before or after each other randomly.

A more involved code fix could get around that by running the while loop twice, first adding only items for which isPrereqMatched was false after the for loops (meaning it had no same-era prereq), then adding only the remaining items. That would insure C1 always appeared before C2 in case (2). However, if there was then a C3 on the same row in the same era which wasn't related to C2 by a prerequisite chain but did have a prerequisite from within the same, C2 and C3 would still appear in random order.

All in all, just using the work-around is probably the best way to go--within each era, only include civics on the same row if they are part of the same prerequisite chain. I haven't looked at the tech tree code but I'm sure the same rule applies to it.
 
Last edited:
I believe I've followed all of those constraints, but no luck. I'd love for you to take a look at the file and let me know what you think!

https://forums.civfanatics.com/resources/ananses-bfg-modpack.25825/


Here's the file. All of the Civics changes are contained in LinearCivics.xml - at the very bottom of the file, there are a few era changes. And those should be the only things that effect the Civics Tree.

The error seems to be randomly placed throughout the Civics tree, but most often in the Classical Era. Sometimes it does not occur at all. And even when it does occur, the actual Civics progression still works.



EDIT, upon re-reading what you wrote, I think that my error is the first one - having C1 and C2 both in the same UITreeRow, neither of which have a pre-req within the era. However, the error is very inconsistent, so I don't know...

Do you believe that your .lua fix would address this?
 
Last edited:
@anansethespider no wonder, their sort routine that is supposed to make sure civics come after their prerequisites in eraGrids[].sortRow.columns[] is fubar'ed, you can't use swap there. I don't really know how it isn't broken in the base game--probably because they don't have more than 3 columns per era, so that crazy swap thing somehow works--but just doing a print shows it sorts your civics in the wrong order.

Here's a fix for the bug I verified fixes the problem using your mod. In Base/Assets/UI/Screens/CivicsTree.lua beginning at line 278 replace:
Code:
    -- Manually sort based on prereqs, 2(N log N)
    for eraType,grid in pairs(eraGrids) do      
        local numEraItems:number = table.count(grid.sortRow.columns);
        if numEraItems > 1 then          
            for pass=1,2,1 do                    -- Make 2 passes so the first swapped item is checked.
                for a=1,numEraItems do
                    for b=a,numEraItems do
                        if a ~= b then
                            for _,prereq in ipairs(m_kItemDefaults[grid.sortRow.columns[a] ].Prereqs) do
                                if prereq == grid.sortRow.columns[b] then
                                    grid.sortRow.columns[a], grid.sortRow.columns[b] = grid.sortRow.columns[b], grid.sortRow.columns[a];    -- swap LUA style
                                end
                            end                  
                        end
                    end
                end
            end
        end
    end
with:
Code:
    -- Manually sort based on prereqs
    for eraType,grid in pairs(eraGrids) do      
        local numEraItems:number = table.count(grid.sortRow.columns);
        if numEraItems > 1 then
            local a : number = 1;
            while ( a < numEraItems ) do
                local found = false;
                for b = numEraItems, a + 1, -1 do
                    for _,prereq in ipairs(m_kItemDefaults[grid.sortRow.columns[a]].Prereqs) do
                        if prereq == grid.sortRow.columns[b] then
                            found = true;
                            table.insert(grid.sortRow.columns, b, table.remove(grid.sortRow.columns, a));
                            break;
                        end
                    end
                    if found then
                        break;
                    end;
                end;

                if ( not found ) then
                    a = a + 1;
                end
            end
        end
    end

In the tech tree view they didn't even try to sort by prereqs, they just set the column directly based on the tech's cost, so just make sure you don't have any techs with the same cost and UITreeRow. I didn't see any problems viewing your tech tree, though.

Someone should probably get the above to Firaxis somehow so they can fix it in the next patch, that's going to bite every mod with 4+ civics columns in a single era even if it somehow works for 3-.
 
you sir are a gentleman and a scholar. Best thing to happen to me so far this year! TYVM


Edit, I'm thinking I should add this fix to my mod, but I'm not sure how to update lua from a modinfo. Would this be a simple addition?
 
Last edited:
I am working on adding a civilization and am following the guidelines set by Firaxis with Poland yet I have a question: When I am providing the directory for my files, do I put that it is in the mods file or not? If not, then where do I put it? Can someone help me with this? Thanks.
 
I am working on adding a civilization and am following the guidelines set by Firaxis with Poland yet I have a question: When I am providing the directory for my files, do I put that it is in the mods file or not? If not, then where do I put it? Can someone help me with this? Thanks.
In .modinfo, you put in the path relative to your mod's root. So if you have:

Code:
/Documents
    /My Games
        /Sid Meier's Civilization VI
            /Mods
                /YourMod
                    YourMod.modinfo
                    YourModFile1.xml
                    /Data
                        YourModFile2.xml

In YourMod.modinfo, you would refer to YourModFile1.xml as just "YourModFile1.xml", and to YourModFile2.xml as "Data/YourModFile2.xml".

Is that what you were asking?
 
I am a bit confused. I am referring to this:
Code:
<File>Data/Poland_Jadwiga_GameplayData.xml</File>
The directory you provided is correct so would I put
Code:
<File>Mods/Data/Poland_Jadwiga_GameplayData.xml</File>
or would it just be the same as Firaxis put?
 
Whatever directory your .modinfo file is in is the mod's base directory, so you just provide whatever path is needed to get from that directory to the file you're referencing. If the file is in the same directory as the .modinfo file, you just use the filename itself. If the file is in a subdirectory below that file, you include that subdirectory with the filename.
 
Top Bottom