Mod support has been discussed many times before, and it's something that can really increase the longevity of a game. See Warcraft 3 or Cities: Skylines. Therefore I'm creating this issue here for a discussion about an actual implementation of mods in Citybound.
Why mods and not X?
Any implementation of mods will make the experience much more customisable from a player perspective, by letting the player enable and disable any mod. It will also enable people to create very experimental mods that are too unstable or not popular enough to be included in the main game.
Forks? Would make it difficult to combine modifications, and essentially impossible for the end user. Forks are better for bigger mods.
Pull requests? All pull requests has to go through some kind of vetting process for quality before they are included. Something which will take resources from the development of the game.
Possible Solutions
All of the alternatives requires some sort of interface to create hooks into the game routines. I can't say anything about any details, so Anselm or someone else might have to fill in here.
Scripting
Scripting would probably be the most obvious solution to this. It would require us to chose a language, probably something like Lua (which has good Rust bindings already). Here we need to figure out a good way to efficiently send objects back and forth between the game and the scripting engine. This is the most difficult part, also because if something is left out (like building parameters) it will make certain mods impossible.
Pros
- Easy to make a basic implementation.
- Can be made safe (disabling file access, etc).
- Cross platform by default.
Cons
- There will be a performance penalty.
- Code from mods and the game engine will not be compatible.
- Existing interfaces needs to have additional scripting interfaces added.
Dynamic libraries
These would written in Rust, and be compiled directly from source with Cargo and rustc. I'm thinking that the same version of rustc that was used to compile the game would be included in the game package. Cargo already exists as a library, so we should be able to use it to compile mods at run time. The mods would be compiled into dylib's, so that they could be dynamically loaded and called from the game at run time.
The game engine should be included as a library and contain ways to hook into the game world.
Pros
- Native performance.
- Existing interfaces should be possible to re-use.
- Mod and mainline code will be compatible.
- Should make odd things, possible. (Running web servers, hooking up python scripts, etc.)
Cons
- Not safe (same access to the system as the game has).
- Relatively easy to mess up cross platform support. (Depends on what you are doing.)
- Rust can be a bit tricky to use.
Hosting
Mods should contain a package specification, with information about what it is (a mod, a model, etc) and it's dependencies. I'm thinking mods should be pullable from a central repository, something like the Steam Workshop. This should preferably be backed by a REST API with a publicly available specification so that it's possible to get packages from unofficial repos, etc. Getting packages directly from git repos would be a great addition to this, and of course from the local file system.
Summary
Personally I think that the second option of using dynamic libraries would be the best option because it would give us the most freedom to create whatever we want. For example, it would be possible to create a mod which enables Lua scripting.
I'd be willing to build the basis for packages, and if others like the second idea for mods (dynamic libs), I could try to implement that too.
Obviously something like this requires native support in the game engine, that's why I'm bringing it up this early in the process.
Suggestions and general feedback is the reason I made this, so feel free to comment below.