Understanding Moddability in Enfusion-Powered Games
Modding has become a cornerstone of modern game development, providing players and creators the ability to personalize, enhance, and extend the lifespan of games. For developers, understanding how to make a game moddable is just as crucial as understanding how to design core gameplay mechanics. Moddability begins with the foundational architecture of the game engine itself. In the case of Enfusion-powered games, this foundation is the Enfusion Engine, which is meticulously crafted to support and encourage extensive modding capabilities.
This blog post delves deep into the design principles of the Enfusion Engine as they relate to modding. We will explore the tools it offers to modders.
Arma Reforger Modding Boot Camp #1 - Introduction to Enfusion
Arma Reforger Modding Boot Camp #2 -Modding Patterns and Workflow
Core Principles of Moddability in the Enfusion Engine
For a game to be considered highly moddable, it must provide mechanisms to:
Inject new data, configurations, and resources.
Modify existing assets dynamically.
Alter the game’s behavior to enable new functionality.
The Enfusion Engine addresses these needs through:
Dynamic Filesystem Architecture:
A flexible system that supports complex and reliable physical and virtual asset file architecture.
Resource Database:
A centralized system to manage, reference, and organize game assets.
Containers:
The fundamental data structures for storing and merging game data.
Enforce Scripting Language:
A versatile scripting language designed to control and extend game behavior.
Each of these elements contributes to an engine environment where moddability is not just possible but integral to its design.
Four Core Aspects of Moddability
The Enfusion Engine is designed with moddability as a fundamental principle. To achieve this, it supports four core moddability operations:
Override an Asset
Override refers to modifying an existing asset while retaining its core functionality. This approach allows the original asset to interpret modifications as if they were part of the base asset itself. Key features of this process include:
Multiple mods can override the same asset, with their results dynamically merged to create a unified version.
Overrides can target specific aspects of an asset, allowing for granular control.
It is also possible to override an existing override, enabling layered modifications.
How to override an asset can be found in https://community.bistudio.com/wiki/Arma_Reforger:Data_Modding_Basics#Using_%22Override_in...%22_function
Inherit an Asset
Inherit enables the creation of new assets based on existing ones, following an object-oriented approach. Instead of copying attributes from the parent asset, the new asset dynamically references them. This approach allows:
The inherited asset to assimilate any future overrides applied to the parent asset.
The creation of modular and reusable content.
How to inherit an asset can be found in https://community.bistudio.com/wiki/Arma_Reforger:Data_Modding_Basics#Using_%22Inherit_in...%22_function
Duplicate an Asset
Duplicate involves creating an independent, full copy of an existing asset. Unlike inheritance, duplication creates a static snapshot of the asset at the time of duplication. This approach is useful when:
Developers or modders require an unlinked version of an asset for standalone modifications.
The Role of Enfusion Containers
At the heart of the Enfusion Engine’s moddability framework lies the concept of Containers. Containers serve as the base abstraction for storing, organizing, and merging data. Virtually every file type related to game configuration and assets is implemented using containers, including:
Materials.
World Files.
Prefabs.
Configuration files.
Animation Graph.
Behavior Tree files.
and many more!.
This container-based architecture ensures consistency, modularity, and ease of modification across all game systems.The original asset’s future changes should not affect the duplicated asset.
Replace an Asset
Replace completely swaps an asset with another, effectively removing the original version of an asset from use. This operation:
Targets specific files or overrides.
Does not affect existing overrides unless explicitly specified.
Is particularly useful for wholesale changes to an asset’s functionality or appearance.
How to replace an asset can be found in https://community.bistudio.com/wiki/Arma_Reforger:Data_Modding_Basics#Replacing_assets
The Role of Enfusion Containers
At the heart of the Enfusion Engine’s moddability framework lies the concept of Containers. Containers serve as the base abstraction for storing, organizing, and merging data. Virtually every file type related to game configuration and assets is implemented using containers, including:
Materials.
World Files.
Prefabs.
Configuration files.
Animation Graph.
Behavior Tree files.
and many more!.
This container-based architecture ensures consistency, modularity, and ease of modification across all game systems.
Container Typing and Definitions in Enforce Script
Containers in Enfusion Engine are strongly typed to a significant extent. The type of a container determines its structure and properties, which are defined using classes. These classes can be created either in C++ or via Enforce Script, allowing developers to define custom container schemas with the assurance that the engine will enforce the defined structure and behavior.
A key feature of this system is its modularity and extensibility. Container definitions can inherit properties and behaviors from other containers, enabling a hierarchical and reusable design. This is achieved by extending base container classes within the scripting environment.
Defining a Container
The following is an example of defining a minimalist container schema using Enforce Script:
[BaseContainerProps(configRoot : true)]
class ExampleConfig : ScriptAndConfig
{
[Attribute(uiwidget: UIWidgets.Slider)]
float m_fAttribute1;
[Attribute(uiwidget: UIWidgets.EditBox)]
string m_sAttribute2;
[Attribute(uiwidget: UIWidgets.Coords)]
vector m_vAttribute3;
[Attribute(uiwidget: UIWidgets.ColorPicker)]
Color m_Attribute4;
[Attribute(uiwidget: UIWidgets.Object)]
ref ExampleConfig m_Attribute5;
}
This schema defines a container ExampleConfig with the following attributes:
m_fAttribute1: A floating-point value linked to a slider UI widget.
m_sAttribute2: A string linked to an editable text box.
m_vAttribute3: A vector represented with coordinate inputs.
m_Attribute4: A color value managed via a color picker widget.
m_Attribute5: A nested container of the same type (ExampleConfig).
The [BaseContainerProps(configRoot : true)] annotation indicates that this schema can serve as a root for configurations.
Properties are annotated with Attribute metadata to specify their associated UI representation and behaviors.
Non Attribute annotated properties will not be visible on the property grids within Workbench, and would be class only properties.
Using the Defined Container
When a configuration file (e.g., ExampleConfig.conf
) is created with this container, the engine ensures the file adheres to the structure defined in the container class. Upon opening the configuration in the Workbench, the user is presented with a UI reflecting the defined attributes and their associated widgets.
Default Configuration Behavior
By default, configuration files contain only the container class name. For example, the default state of ExampleConfig.conf
would look like this:
![](https://cms-cdn.bistudio.com/cms-static--reforger/images/8ffb6f23-fdec-495c-a234-4a98616d36b7-Modding_Boot_Camp_Introduction_1.png)
ExampleConfig {
}
Modifying and Saving Configurations
If changes are made to the configuration via the UI and subsequently saved, only values differing from the default schema are written to the file. For instance, modifying and saving the configuration might result in the following:
![](https://cms-cdn.bistudio.com/cms-static--reforger/images/019018db-3671-462b-8f5d-1ad5667d975e-Modding_Boot_Camp_Introduction_2.png)
ExampleConfig {
m_fAttribute1 125
m_Attribute4 0.891 0.015 0.015 0
m_Attribute5 ExampleConfig "{6318420A9B890289}" {
m_sAttribute2 "I am a nested container!"
}
}
In this example:
m_fAttribute1 has been updated to 125.
m_Attribute4 represents a color value.
m_Attribute5 contains a nested ExampleConfig instance with its own modified attribute, m_sAttribute2.
It is apparent now that only the changes from the default values are saved. This behavior is similar when overriding or inheriting from another container. The resulting override/derived asset on it's own will only have the data that differs from it's default saved, the default can be either the container defaults or the values previously imposed in the inheritance chain.
More information can be found in https://community.bistudio.com/wiki/Arma_Reforger:Create_a_Config_Class
Enforce Scripting Language
The Enforce Scripting Language is a cornerstone of the Enfusion Engine’s moddability framework. It provides the tools necessary to script, control, and extend game behavior. The game operates on a bridge between features implemented in C++ and those exposed via the Enforce scripting API. Key features of Enforce Script include:
A robust syntax designed for flexibility and accessibility.
The ability to implement new gameplay features, modify existing ones, or disable undesired functionality.
Direct interaction with game assets and engine features through an API.
While not all game features are scriptable some are implemented exclusively in C++ without exposed APIs, Enforce scripts provide a powerful layer of customization for modders and developers alike.
It is recommended that the contents of https://community.bistudio.com/wiki/Category:Arma_Reforger/Modding/Tutorials/Scripting are studied first.
Introduction of Modability of Enforce Script classes.
Modding a class behaves similarly to extending a class, with the difference that instead of inheriting from another class, a class would inherit from itself or the previously modded version of itself. The keyword super in this case would point to the last modded version of the class or the class itself. So, it is to expect that private methods from the class could also be accessible or overridden.
Defining a modded class
Defining a modded class is exactly the same as when defining a normal class with the difference that instead of doing for example "class ClassB : ClassA" it would then be "modded class ClassB".
The modded class definition can be anywhere, under the same file as many other classes or modded classes, in another file, under the same file as the original class, in another addon, etc. But it should always be under the same script module of the original class that is being modded or otherwise the compiler will either not find the class to mod or it will treat it as sealed.
// Original class.
class ClassB : ClassA
{
// Class member definitions and overrides here.
}
// Modded class.
modded class ClassB
{
// Class member definitions and overrides here.
}
A class will not be moddable if it is sealed.
A sealed class is a class that will not allow for further inheritance, and by modded classes being a sort of inheritance, they will also be blocked.
// Original class, which is sealed.
sealed class ClassB : ClassA
{
// Class member definitions and overrides here.
}
// A compilation error will be thrown.
modded class ClassB
{
// Class member definitions and overrides here.
}
Introduction of the Vanilla Keyword
Sometimes when modding a class, a requirement for calling specifically the original implementation of the method being modified is wanted. In other words this means to have the ability to be able to call the logic that was defined on the original definition of the method being modded. This contrasts from the super keyword given that vanilla does not check parent or previous modded class implementations at all.
Example usage:
// Original implementation.
class ClassA
{
void Method()
{
Print("First");
}
}
// Modded implementation with super.
modded class ClassA
{
override void Method()
{
super.Method();
Print("Second");
}
}
// Modded implementation with vanilla.
modded class ClassA
{
override void Method()
{
vanilla.Method();
Print("Third");
}
}
// Modded implementation with super.
modded class ClassA
{
override void Method()
{
super.Method();
Print("Fourth");
}
}
Now when the method is called, it will print the following:
First
Third
Fourth
Notice how it ignored the second print, this is because on the 2nd override through modded class, vanilla was used instead of super causing all previous implementations with the exception of the original one to not be called as super was not used anywhere else on that override but notice how on the 3rd override super was used but only the 2nd override was called because of super.
More information can be found in https://community.bistudio.com/wiki/Arma_Reforger:Scripting_Modding
Enfusion Workbench
The Enfusion Workbench is a comprehensive suite of tools designed for both developers and modders to work seamlessly within the Enfusion Engine. This powerful toolkit is provided "as is" to the modding community, in the same state and capacity as it is used internally by the developers.
Within the Enfusion Engine, users will find all the editors required to develop custom content. Each editor is equipped with a variety of built-in plugins to enhance functionality. Additionally, the system offers the flexibility to create custom plugins, allowing users to tailor the tools to their specific development needs.
Whether you are crafting game assets, designing worlds, or building new mechanics, the Enfusion Workbench provides a robust and adaptable environment to support your creative endeavors.
Some of the Enfusion Workbench editors and previews are:
AI Behavior Tree Editor
![](https://cms-cdn.bistudio.com/cms-static--reforger/images/6261dfe9-301a-40ce-a325-9dcf03ce71a5-Modding_Boot_Camp_Introduction_3.gif)
Animation Editor
![](https://cms-cdn.bistudio.com/cms-static--reforger/images/e328ac1d-482d-4be8-a039-fb6c0252ba15-Modding_Boot_Camp_Introduction_4.gif)
World Editor
![](https://cms-cdn.bistudio.com/cms-static--reforger/images/6d38b233-2270-41a8-a650-186926cf7b8b-Modding_Boot_Camp_Introduction_5.gif)
Audio File Preview
![](https://cms-cdn.bistudio.com/cms-static--reforger/images/ea9c9a58-c768-465e-8c43-66b6ef9b8215-Modding_Boot_Camp_Introduction_6.gif)
Audio Project Editor
![](https://cms-cdn.bistudio.com/cms-static--reforger/images/4e5477a6-bfc4-4e37-8d64-f1ff8c7b3939-Modding_Boot_Camp_Introduction_7.gif)
Config Editor
![](https://cms-cdn.bistudio.com/cms-static--reforger/images/2aa9daef-0b76-4bd0-8c31-68176579a9d5-Modding_Boot_Camp_Introduction_8.gif)
Font Editor
![](https://cms-cdn.bistudio.com/cms-static--reforger/images/d931942f-f364-484e-a720-70e00a17934e-Modding_Boot_Camp_Introduction_9.gif)
Image Preview
![](https://cms-cdn.bistudio.com/cms-static--reforger/images/70f10e9e-317e-4766-b499-a3c9f6de0a4f-Modding_Boot_Camp_Introduction_10.gif)
Image Set (Atlas) Editor
![](https://cms-cdn.bistudio.com/cms-static--reforger/images/b33e18a5-e61b-4d95-926c-54767bfb7045-Modding_Boot_Camp_Introduction_11.gif)
Material Editor
![](https://cms-cdn.bistudio.com/cms-static--reforger/images/157f6d30-bf8b-4f43-a706-07d2362de5f1-Modding_Boot_Camp_Introduction_12.gif)
Model Preview
![](https://cms-cdn.bistudio.com/cms-static--reforger/images/7583f69c-98b1-4e26-bcd8-cae43ac35bb8-Modding_Boot_Camp_Introduction_13.gif)
Navmesh Preview
![](https://cms-cdn.bistudio.com/cms-static--reforger/images/9899d50e-4471-4328-b739-19200aeba023-Modding_Boot_Camp_Introduction_14.gif)
Particle Editor
![](https://cms-cdn.bistudio.com/cms-static--reforger/images/114a8546-b181-4890-88c6-7d07459eb70a-Modding_Boot_Camp_Introduction_15.gif)
Particle Preview
![](https://cms-cdn.bistudio.com/cms-static--reforger/images/16d5a4da-aa74-4c1e-b1f2-d87f8fa1381e-Modding_Boot_Camp_Introduction_16.gif)
Prefab Preview
![](https://cms-cdn.bistudio.com/cms-static--reforger/images/c692127f-0481-4f92-94d6-da32f1bc1e12-Modding_Boot_Camp_Introduction_17.gif)
Procedural Animation Editor
![](https://cms-cdn.bistudio.com/cms-static--reforger/images/5656d20a-c10d-4d28-8afe-52e239d2faf6-Modding_Boot_Camp_Introduction_18.gif)
Script Editor
![](https://cms-cdn.bistudio.com/cms-static--reforger/images/34d3f7e6-512f-42f2-9bff-22d6c7641234-Modding_Boot_Camp_Introduction_19.gif)
Script Preview
![](https://cms-cdn.bistudio.com/cms-static--reforger/images/dcb1c03f-86db-4027-8635-ae182f50f5a3-Modding_Boot_Camp_Introduction_20.gif)
UI Layout Editor
![](https://cms-cdn.bistudio.com/cms-static--reforger/images/c3c88d53-50ec-443f-8a1e-b1969d907044-Modding_Boot_Camp_Introduction_21.gif)