bw logo

Chapter 14. XML Data File Access

14.1. ResMgr.DataSection

Server component scripting can access custom data stored in XML files. These would typically be used to store game data resources, for example, anything from gameplay tables to configuration data. The data is stored in an XML hierarchy, accessible by traversing the tree defined in the each XML file.

14.2. Accessing Data

Suppose that a data file is defined as in the example below:

<root>
   <character>     Sir Manfred
      <description> White knight                       </description>
      <modelName>   sets/main/characters/knight.model  </modelName>
      <race>        human                              </race>
      <gender>      0                                  </gender>
   </character>

   <character>     Sofia
      <description> Evil queen                         </description>
      <modelName>   sets/main/characters/queen.model   </modelName>
      <race>        undead                             </race>
      <gender>      1                                  </gender>

      <slaves>
         <character>     Underling
           <description> Hapless underling                   </description>
           <modelName>   sets/main/characters/guard.model    </modelName>
           <race>        undead                              </race>
           <gender>      1                                    </gender>
         </character>

         <character>      Servant
            <description> Unpaid slave                       </description>
            <modelName>   sets/main/characters/servant.model </modelName>
            <race>        undead                             </race>
            <gender>      1                                  </gender>
         </character>
      </slaves>
   </character>
</root>

Example XML file <res>/scripts/data/Characters.xml

You can access this data by creating a new DataSection using the ResMgr.openSection method. The path argument used is relative to the resources path. This is illustrated in the example below:

ds = ResMgr.openSection( 'scripts/data/Characters.xml' )

# this will retrieve "Sir Manfred"
ds.child( 0 ).asString

# this will retrieve "White knight"
ds.child( 0 )['description'].asString

# this will retrieve 1
ds.child( 0 )['gender'].asInt

Reading an XML data file

14.2.1. Opening a Section Within an XML File

You can access a section within the XML file by adding the name of the section to the end of the path given to ResMgr.openSection:

dsChild = ResMgr.openSection(
  'scripts/data/Characters.xml/character' )

Reading an XML data file Accessing a specific section

If there are multiple elements with the same under the root element, then the first one is returned.

14.3. Data Types

The available data types are:

Data type Accessed by
64-bit floating-point numbers .asDouble
64-bit integers .asInt64
Data blob .asBlob
Floating-point numbers .asFloat
Integers .asInt
Matrix .asMatrix
Raw binary representation of the XML node .asBinary
String .asString
Vector2 .asVector2
Vector3 .asVector3
Vector4 .asVector4
Wide strings .asWideString

Available data types in XML

For more details, see the Client Python API's entry Class List DataSection.

14.4. Writing Data

You can write to properties by referencing the appropriate .as<data type> property, then saving the XML file.

Note

This feature is for use only on server tools, and you should avoid using it in game scripts.

An important limitation to be aware of is that it is only possible to save a DataSection that has been opened by reference to an XML document. It is not possible to directly save a section that is retrieved by a path to a sub-element within a file.

For example, the code below will not work:

# this will not work, throws IOError
dsChild = ResMgr.openSection( 'scripts/data/Characters.xml/character' )
dsChild.asString = "Sir Lancelot"
dsChild.save()

Example of incorrect procedure for writing to XML

The code excerpt below, on the other, will work:

# this will work
dsRoot = ResMgr.openSection( 'scripts/data/Characters.xml' )
dsChild = dsRoot.child( 0 )
dsChild.asString = "Sir Lancelot"
dsRoot.save()

Example of correct procedure for writing to XML

You can also add or remove child elements from each data section:

# get the document data section
dsRoot = ResMgr.openSection( 'scripts/data/Characters.xml' )

# this will delete the first character
dsRoot.deleteSection( 'character' )

# create a new character, which is appended to the top-level
newChild = dsRoot.createSection( 'character' )
newChild.asString = "King Arthur"

newChild.createSection( 'description' )
newChild.createSection( 'modelName' )
newChild.createSection( 'race' )
newChild.createSection( 'gender' )
newChild.createSection( 'slaves' )

newChild['description'].asString = "The King of Camelot"
newChild['modelName'].asString = 'sets/main/character/knight.model'
newChild['race'].asString = 'human'
newChild['gender'].asInt = 0
dsRoot.save()

Deleting sections and adding new ones

Running the code excerpt below, and assuming a Characters.xml as described in Accessing Data, the result will be the file below:

<root>
   <character>      Sofia
      <description> Evil queen                       </description>
      <modelName>   sets/main/characters/queen.model </modelName>
      <race>        undead                           </race>
      <gender>      1                                </gender>

      <slaves>
         <character>      Underling
            <description> Hapless underling                  </description>
            <modelName>   sets/main/characters/guard.model   </modelName>
            <race>        undead                             </race>
            <gender>      1                                  </gender>
         </character>

         <character>      Servant
            <description> Unpaid slave                       </description>
            <modelName>   sets/main/characters/servant.model </modelName>
            <race>        undead                             </race>
            <gender>      1                                  </gender>
         </character>
      </slaves>
   </character>


   <character>       King Arthur
      <description>  The King of Camelot              </description>
      <modelName>    sets/main/character/knight.model </modelName>
      <race>         human                            </race>
      <gender>       0                                </gender>
      <slaves>                                        </slaves>
   </character>

</root>

Resulting <res>/scripts/data/Characters.xml

14.5. Performance Issues

Accessing the XML files on disk can potentially halt game processing. This can occur from disk I/O and parsing of the resulting data. This halt to processing can occur for both reading as well as writing of XML files and should be avoided as much as possible.

Due to the adverse impact this can cause to game development and resulting behaviour, a separate document has been written to address these issues. For more details please refer to the document How To Avoid Files Being Loaded in the Main Thread.

14.6. API Reference

ResMgr documents the DataSection's methods, and can be found in the BaseApp Python API, CellApp Python API, and Client Python API.