I’ve spoken my praises about mods and the modding community in the past, so it should come as no surprise that I’d want to go out of my way to enable it in my own projects. The first step to enable modding is to embrace what is apparently known as “data-driven design”, which is just a fancy way of saying that you load your data from files. Levels are defined in files, models and textures are stored in files, entity behavior may be stored in script files, etc.
If you’re writing your own engine (as I am), this is pretty easy to accomplish. If you’re using an engine like Unity or Unreal, this is a lot more complicated because those engines like to load THEIR files THEIR way, and doing it yourself can sometimes feel like re-inventing the wheel. This isn’t the path I went down, so I’m afraid I can’t really help you if you do want to use one of these premade professional engines.
In the past, I’ve done quite a bit of mod work with the Elder Scrolls series. Anybody who’s ever worked with Bethesda’s engine can tell you that it’s pretty much a hot mess, but at the very least it was designed to be modded, which is more than I can say about a lot of games. This experience has taught me a lot about modding in general; what works, what doesn’t work, what could work better. Just as an example:
Make everything moddable. As much as possible, write gameplay behavior in scripts. Hard-coded engine functions should only be for the engine’s scaffolding and highly performance-critical code.
Allow the user to install and use an arbitrary number of mods. Needing to cap the number of mods for technical purposes (as the Elder Scrolls games do, to 255) is unfortunate but understandable; avoid if possible, but don’t let it compromise the system.
Do not require the user to replace and overwrite files within the install directory. This makes uninstalling mods a complete pain.
With that in mind, I searched around for solutions, and one of the first that I came upon was a little C library called PhysFS. Go and read about it if you want to learn more. TLDR, it lets you define multiple directories (or archives), request a file from the system, and they’ll give you whichever instance of that file happened to be at the front of the list. This was great, but it had one fatal flaw that I couldn’t seem to get around – if I wanted multiple instances of a single file, so I could aggregate the data together myself, I was out of luck.
So I made my own. It’s still a work in progress. It lets you register any number of archives or directories, you can request either a single file or every version of that file, and it works quite well, to be honest. I’ve also developed a custom archive format specifically to work within this system, but I’ll talk about that another time.
In order to not have to search through every registered module every time you request a file, the module manager keeps its own “file tree”; just a sorted list of every file that it knows about, each with a list of every module that has a version of that file.
Right now I’m mainly doing binary searches using strcmp, but in the future I might change this to a hash table. We’ll see whether or not it’s necessary.
Making a fully moddable engine is a very lofty goal, but it’s one that I’ve had tons of fun building and designing. I’ll keep you posted as I develop it further, stumbling across new insights as we learn together!