Mixins / Augments#
Mixins are a very powerful approach to powering modding frameworks. While most games end up designing their systems around modding, making mixins less practical, others don't (Twilight Princess, where the developers never intended modding). This is where mixins come in and earn their keep. Minecraft is the big example of mixins being used to power modding, and it WORKS.
So, what is a mixin?#
A mixin allows talented modders to "inject custom code" into a game at runtime. This is fundamentally different from most modding frameworks, where you're usually working with pre-defined hook points within the game's systems. Minecraft is the notable example because the stars align perfectly: they use Java, an interpreted language (which makes this type of "magical" behavior simple), their developers don't and probably won't ever design a built-in framework, and nowadays they have de-obfuscated their symbols, making modding easier than it ever has been. Now, the next question I imagine you'll ask, especially if you know anything about compiled languages, is: how the hell does Courage Reborn use that? Twilight Princess is C++, and doesn't have support for anything like mixins?
How Courage Reborn uses mixins#
Over the course of a few days, our developers got to work on a library they named Augment. Backed by Dobby, this library allows programs to reflect on and introspect their own structure at runtime. This was achieved by exporting debug symbols in builds to an external file, parsing it, and outputting a custom binary format. If you've played Courage Reborn, you may have noticed an augment.bin file sitting next to the binary. This binary format contains extracted / parsed debug information, telling Augment how to read a consumer's program structure at runtime. From there, we use Dobby's trampolines, which allow your program to jump to arbitrary instructions in memory at runtime. Combining that with an intelligent Luau layer, modders can hook into arbitrary function signatures and tell the game to jump to your code. We call any patch to a program an augment.
Strictly speaking, what we do is closer to runtime function hooking than traditional mixins, but the end result for modders is the same: inject your code into the game's existing logic.
Are there limitations?#
Undoubtedly. As with any modding system, augmenting has a few notable drawbacks worth pointing out.
Compiler optimizations. Compiler optimizations are awesome for performance, but one specific optimization doesn't play entirely well with this system. It's called function inlining. Function inlining is great for performance, and in fact it saves ~100fps (yes, we tried disabling it for actors), but is fundamentally incompatible with this system. To make the program trampoline to another point in memory, there must be a point to hook into. Inlining gets rid of that point. There's also tail call optimization which does similar, and it jumps directly to the point in memory, leaving nothing to hook. You can view all inlined functions on the game hooks page by filtering with is:inlined.
iOS. Sorry Apple folk, but you're unable to use our advanced modding system due to Apple themselves. Because they don't allow JIT compilation or runtime code modification on iOS, Dobby's trampolines are unable to do exactly what they're designed to do. It's an App Store / platform security policy, not a hardware limitation, and macOS does not have that restriction.
Game updates. Every update to Courage Reborn potentially shifts function addresses and struct layouts. Mods may need to be updated alongside the game, and this is expected and unavoidable with this approach. We're looking into ways to help with this problem, though.
Now, go use them here!