Hey guys,

The entire past month (wow, it’s mid-March already??) I spent on the fascinating journey with re-implementing Windows®™ Registry in expandable way for mods.

How it was?

A bit painful, I must admit. But I loved every minute of that pain (as any genuine engineer would, I believe).

First steps

First of all, I entrusted the amazing ChatGPT to build quite a naive implementation. Of course, this required a lot of prompting and directing, because the task I tried to solve was not trivial at all: loading an XML file with XSD schema validation, whereas could be custom XSD schemas registered in-flight by plugins, all bound with XsdGen’ed classes which are also subject for dynamic registration and binding to XML namespaces… Basically, I had to craft the entire SOAP framework without remote calls and on modern .NET.

I finally made it (I thought).

And the resulting config.xml automatically generated by the game engine after all plugins loaded was… spaghettic (if any Italian reads me, I apologize for blatant desecration of your exciting cuisine):

First <code>config.xml</code>
First config.xml

Just look at that mess:

  • xsi:type="..."
  • <q1:...>
  • <q2:...>
  • xmlns:q1
  • xmlns:q2
  • <PluginConfig> which is abstract and is never supposed to be used

“Looks like a usual XML garbage,” you might say.

Well, this is not how I imagined my configs would look. I consciously opted for XML instead of widely adopted JSON in the modding world. I did it for benefits of natural way of documenting, rigid but extensible schema definitions, strong type system resembling the best practices of the modern programming languages.

And I wasn’t going to do it giving up readability and clarity.

“But this is just a config! Who would ever care?”

Not you as a player, for sure. And not you as a modder (unless you want to debug configs which, ideally, should never happen, as it’s the engine burden to supply a convenient object to your program code).

And not I, frankly. For config files, the internal representation mess couldn’t bother me less. In the end of the day, it’s the agreed standard way of handling XML. It exists in .NET since 2003. Billions of software products in the world use exactly the same System.Xml classes for doing that. The config XML is written, it’s parsed, what else could I desire?

But then I remembered why I do all the staff. The matter is not only about configs. XML has been selected for declarative syntax of mods. I was going to re-use the same parsing-generating engine for the entire XML/XSD framework in the Mod SDK.

As a modder, you probably wouldn’t like to deal with mysterious unreadable <q257:AbstractParameter xsi:type="q257:...">.

So, I needed to work harder to get rid of that mess.

Final solution

A couple of weekends elapsed, and I finally found out some sophisticated, rarely spoken mechanisms in the standard System.Xml allowing me to achieve what I wanted:

  • Abstract tag <PluginSupport> never appears
  • Tags can be inherited and look naturally
    • Not <PluginSupport xsi:type="q1:ModSupportConfig"> but just <ModSupportConfig>
  • Leverage standard XSD 1.0 capabilities and don’t pull any redundant external dependency
    • More deps – less control over vulnerabilities
  • Dynamic load of custom XSD with binding XML namespaces to XML-annotated classes
  • Allow plugins to handle XSD migration
    • If a previous version of the plugin has written XML tags which are non-compliant with the new XSD of the new version of the plugin, then delegate this error to the plugin bootstrapper instead of silent overwrite

Resulting <code>config.xml</code>
Resulting config.xml

Looks way better, doesn’t it?

Let me reiterate: it’s not only for configs. It’s for the whole underlying XML system backing the Mod SDK. Configs are just the first bird I have to play around with.

Just yet other screenshots from the debugger to proudly show off how a plugin could catch & handle XSD mismatches:

Added knowingly invalid tag <code><ModsPath2></code>…
Added knowingly invalid tag <ModsPath2>
…while no <code><ModsPath2></code> ever defined
…while no <ModsPath2> ever defined
Config is loaded fine with a soft validation error report, which could be handled by a plugin
Config is loaded fine with a soft validation error report, which could be handled by a plugin
While <code>ModsPath2</code> is non-existent in the <code>Config</code> class, the plugin may still locate it via the exposed raw <code>XmlDocument</code>\nand do appropriate migration, if needed
While ModsPath2 is non-existent in the Config class, the plugin may still locate it via the exposed raw XmlDocument and do appropriate migration, if needed

Where am I now

The roadmap from the February.

This is what has been succeeded:

  • Configs schemas migration. It shall happen once upon a time that your mod would have to break the XSD schema of config. Removal of deprecated parameter, restructuring config tree… I’m going to provide you a safe way of migration between inherently incompatible schemas.
  • File access API. Apart from configs, your mod might desire to store something else in the user’s app data folder, or (naturally) access its own files from the game installation directory. I’m going to reinforce security even for “unsafe” API flavor, making it more trustworthy for players to install your mod if you decide to go that way. Usage of that API would be optional, but a player would know whether your mod code plays outside that secure API.

What’s ahead

  • Game exit hooks. A mod might need to know a player is about to exit the game. I’m going to implement two-phases game exit process:
    • First, all concerned mods are queried (asynchronously) whether they allow exiting. This is a moment when you mod could ask a user some confirmation.
    • Second, (if all concerned mods asynchronously confirmed) all concerned mods receive pre-exit hook. This is a moment for your mod to make clean up, save subconfig and so on.
  • XML loaders of mods. The main part of my work is yet ahead.

Bonus: Modding Tutorial!

I have done some documenting by the past February post, as I mentioned. But now I’m ready to share my work.

Meet and welcome: The Official Penal Engineer Modding Guide (may move the URL later on)

There, I strived to shed some lights on:

  • ECS concepts
  • Map/grid/world architecture (tiles, floors, walls)
  • Practical guidelines on actual modding: prefabs patching, supplying new contents

There are a lot of diagrams, even 3D sketches made with Blender, as well as a lot of XML samples with readable syntax highlighting, comments and explanation. Trust, I put efforts to make it usable and modder-friendly.

I plan to organize that guide as a book tutorial, gradually leading a reader through their first mod creation experience. I am inspired by the old-school printed manuals from 1990s, when the technical documentation was of a real use, unlike the garbage we have to deal with modern days… Hopefully my Penal Engineer won’t be a “sink or swim” swamp.

Conclusion

Feel free to share in the comments below:

  • whether the XML stuff was worth the efforts?
  • whether exit hooks are ever demanded?
  • should I focus more on gameplay or pursue Mod SDK?

Any other thoughts crossing your mind are warmly welcome!