A wrapper can select a realm-specific filename
Inspect the import behind ResMgr before attributing data to a literal XML filename. The realm-aware Python wrapper tries a realm-suffixed candidate outside editor mode: for an illustrative EU realm, config.xml becomes config.EU.xml. Only a None result selects the base-path fallback. The variant call uses the underlying default creation behavior; only fallback receives the caller's createIfMissing. Editor mode skips variant opening.
A concrete settings reader imports this wrapper and purges its base path after reading. The wrapper forwards that purge to both original and realm-specific paths, including in editor mode. Other attributes such as isFile are delegated without this Python path rewrite. An existence probe on the base name therefore does not establish which candidate a wrapped open will return. This is a wrapper policy, not a universal native VFS rule or a merge of both sections.
Selected Python methods with inert resource sinks checked variant preference, fallback arguments, editor bypass and both purge requests. These controls establish requested paths and arguments; no native resource read, real XML/package lookup, cache invalidation, client/editor execution, installed bytecode or Python 2.7 runtime was exercised. Resource availability and native effects require separate evidence.
DataSection cautions
A section is a structured resource view, not a durable application model. Treat returned values as snapshots or handles whose meaning belongs to the system that consumes the resource.
- Check the current caller before assuming child names, value types, or defaults.
- Do not write through
ResMgrunless the current client owner explicitly uses that path for mod-safe persistence. - Keep missing sections feature-local: disable the dependent visual, resource, or optional integration rather than aborting unrelated mod behavior.
- When a section exists but appears empty, distinguish a real empty resource from a cache or synchronization boundary in the higher-level owner.
Typed reads follow the resource schema
Choose the node before choosing its type. resource_helper.readIntItem reads an explicit value child when present and otherwise reads the current section's asInt. Its Boolean, float, string and vector readers make the same structural distinction. Opening a resource does not identify the expected value node or validate your application schema.
Source excerpt from readIntItem:
if 'value' in section.keys():
value = section.readInt('value')
else:
value = section.asInt
The messages-panel reader supplies its own default settings, iterates setting records and assigns parsed values by record name. Those defaults belong to that reader, rather than to every DataSection or every resource using the same value type.
Collection projection can discard information
Preserve the collection shape expected by the consumer. In resource_helper, readList appends parsed values, readSeqPairs returns a tuple of name/value pairs, and readDict requires nonempty item names and assigns result[item.name] = item.value. Repeated parsed names replace earlier dictionary entries; the sequence-of-pairs projection retains the separate entries yielded by the iterator.
These are version-sensitive Python reader policies. They do not establish universal native iteration ordering, duplicate-tag lookup, conversion-error or cache-lifetime behavior. Avoid converting section children to a dictionary merely for convenience.
Reader helpers need their own acceptance checks
resource_helper.root_iterator yields parsed records lazily. Its root_generator context logs and suppresses errors raised inside that parsing context, then purges the resource. An error can therefore end iteration after earlier records have been yielded. Exhausting that iterator does not by itself validate a complete configuration. A consumer's conversions and assignments run outside the generator's parsing body and have their own error boundary.
The helper's customReaders argument also updates a shared module-level dispatch mapping. Cleanup removes the supplied keys rather than restoring previous values. Overriding a built-in type is therefore not an isolated per-call reader replacement. The GuiSettings reader adds a distinct macros type and accumulates values over its defaults; it is not a general atomic reload operation.
For editable mod-owned data, use an owned schema, check all required values in a fresh candidate and publish only after those checks pass. The explicit XML reader below keeps that acceptance step visible. These are current Python helper contracts, not a native XML failure or live reload test.
Reload mod-owned loose XML from res_mods
You can design an already-loaded mod to reread its own data and update its current overlay through an explicit reload action. This avoids restarting just to change a caption or another supported configuration value, provided the running client's resource lookup resolves the edited loose file.
- Save the file as
res_mods/x.x.x.x/gui/mods/example/settings.xmlunder the client. Replacex.x.x.xwith the running game version. - Pass
gui/mods/example/settings.xmlto ResMgr. The virtual path omits theres_modsdirectory and version prefix. - After saving, invoke your mod's reload button or existing input action. Purge that specific section, open it again, copy and validate a fresh candidate, then publish it to the live overlay.
- Keep this action on the client thread and within the current overlay's lifetime. Do not keep an old DataSection as the supposedly refreshed configuration.
Illustrative XML for that mod-owned file:
<root>
<caption>Updated overlay caption</caption>
</root>
Illustrative Python 2.7 fragment: put this reader in the same mod file as its overlay. The 256-character limit is an example policy, not an engine limit. Opening or validation failure raises before the caller changes its displayed caption.
import ResMgr
_SETTINGS_PATH = 'gui/mods/example/settings.xml'
def read_overlay_caption():
ResMgr.purge(_SETTINGS_PATH, True)
section = ResMgr.openSection(_SETTINGS_PATH)
if section is None:
raise ValueError('Cannot open overlay settings')
caption_section = section['caption']
if caption_section is None:
raise ValueError('Missing caption')
caption = caption_section.asWideString
if len(caption) > 256:
raise ValueError('Caption exceeds example limit')
return caption
The purge/open/parse/publish sequence is demonstrated by ClientHangarSpace._readHangarSettings; the example applies that shape to a mod-owned XML file. It does not request a hangar reload. Follow the GUI refresh example to apply the returned string.
Saving a file alone does not run this reader. The reload action must already exist in the loaded mod. This is a source-backed development pattern, not a live-client test: visibility of a newly added loose file, previously missing paths and same-path texture/model freshness still need verification on the target client.