-
Notifications
You must be signed in to change notification settings - Fork 5
Mod system overview
This section details the design of the modding system in Phantom Brigade and is intended for mod developers interested in the internal workings of the game. We recommend using the tools provided with the SDK for most mods: manual file editing should ideally be the last resort option.
Due to data-driven structure with exposed config files, most of the Phantom Brigade can be easily modified. It's possible to alter scenarios, rebalance equipment, add new units or localization, etc. by changing the config files loaded by the game. However, we do not recommend directly modifying the files in the install directory for a number of reasons:
- Modifying content of the application directory is invisible to our bug reporter system and risks mods being reverted when the game is updated or validated. It also locks modding on user accounts with limited writing permissions.
- It is not possible to install multiple mods altering the same config when directly editing the game
- It is not possible to modify game logic
- And a whole host of other issues (inability to control mod order, the need to duplicate a bunch of data unrelated to mod intent etc.)
Our modding system is an attempt to solve all these issues. It closely follows the design of the mod systems in other popular moddable Unity games. The core idea is to have a separate folder holding configs and additional content and sideloading that separate data on top of built-in game data. This allows for easy installation, sorting, combining multiple mods etc. This folder is in the same location as save files and game settings: C:/Users/Windows Username/AppData/Local/PhantomBrigade/Mods .
If you can't find the folder, Windows Explorer settings might be set to hide it on your PC. Please refer to the following web page for details. View hidden files and folders in Windows - Microsoft Support. Alternatively, you can press the "Local mods" button in the Mods tab of the main menu to open the folder.

Each mod gets a separate subfolder, e.g. Mods/MyMod1, Mods/MyMod2 and so on:

In each mod folder, there should be a metadata.yaml file that provides some essential information about content of the mod to the game, allowing for loading it and for drawing of mod management UI
- Identifier (unique internal name for the mod used to confirm the game isn't loading multiple copies of the same mod, used to store settings etc.)
- Version
- Loading priority (which mods load first)
- Included types of content (more on that later)
- Compatible versions of the game (e.g.
gameVersionMin: 2.0is required for PB 2.0 to recognize and load any mods) - Name & description
Here's an example of a metadata.yaml file featuring all the fields mentioned above:

The game scans the Mods folder on startup. It starts by attempting to read metadata.yaml. From there, it attempts to load content declared by the metadata. For example, if the metadata says the mod includes config overrides, the game will attempt to find the ConfigOverrides folder and load its contents. If a mod encounters an error (e.g. version incompatibility, mismatch between metadata and content or bad data), it will be recorded to make debugging easier.
You can see the list of loaded mods in the top right of the main menu and in the Mods window available through the main menu. The Mods window lets you inspect errors that might prevent a mod from loading, inspect the metadata and optionally disable mods. By default, the game considers all Workshop subscriptions and all folders under the Mods folder to be active, but disabling them through the Mods menu is a convenient way to manage them without needing to unsubscribe or delete anything.

If you install, change, disable or re-enable a mod while the game is running, you will need to restart the game to see the effects.
Mods can include different content types. These are represented by subfolders in the mod folder and their presence is declared in metadata.yaml. The set of supported content types has expanded over time to include configs, localizations, images and more.

- Metadata flag:
includesConfigOverrides - Folder:
ConfigOverrides/ - Refer to the Config modifications to learn about making this type of modification with the SDK.
This is the simplest possible type of mod content, quite similar to editing Configs folder directly.
- Duplicates parts of Configs folder hierarchy under Mods/[ModName]/ConfigOverrides and you can replace anything or add new files.
- The files you place there have to be modified copies of .yaml configs from the game, with exact same internal layout
- The changes are injected whenever database is reloaded, not just on game initialization, so they are resistant to databases loading late, databases loading multiple times etc.
- For instance, you can put new weapon at
Mods/[ModName]/ConfigOverrides/DataDecomposed/Equipment/Part_Presets/my_new_weapon.yamland the game will load it - Another example: you can put your own version of global settings config at
Mods/[ModName]/ConfigOverrides/Data/Settings/simulation.yamland it will be the version of global settings game will start using - Limitation: Only contents of Data and DataDecomposed folders are supported. We have some other files under Configs that can't be modified with this mod content type for the time being. Applying a mod with config overrides is not a disk operation modifying files in PB/Configs directly; it's an injection operation during loading steps of
DataLinkerandDataMultiLinkerdata managers. The files that can be overwritten, however, are what you'd usually want to modify anyway - other files are a small minority and aren't a good fit for modding (some old binary files, unused configs predating current database system etc).

- Metadata flag:
includesConfigOverrides - Folder:
ConfigOverrides/ - Refer to the Config editing system to learn about making this type of modification with the SDK.
This content type allows for much more fine grained modifications resistant to multiple mods modifying the same file, file changing shape when game is updated etc.
- On the surface it looks similar to config overrides: duplicate parts of Configs folder hierarchy under
Mods/[ModName]/ConfigEditsand put your .yaml matching path/name of existing - However, the content of these files is different. Internally, these files have a different structure. Instead of duplicating real config of the same name at the same path, they store a list of changes. The edit config allows you request removal or, what's more exciting, allow you to edit individual fields and collection entries on a config of the same name.
- This is a big game changer for config mods. One of the big pain points in modding is conflicts between different mods modifying same file and the need to constantly update the modified copy of extracted file every time developers update a game. For instance, our
simulation.yamlcontains hundreds of parameters, and many different mods will definitely want to modify it, whether it's to change allowed squad size of physics settings or loot generation multipliers. - Ordinarily, the changes have to be merged manually and turned into a separate compilation mod - there is no other way to let multiple mods changing the same file coexist. With this feature, one mod can have a single line
simulation.yamlfile modifying a line calledsquadSizeand another can havesimulation.yamldescribing changes to couple other fields - and every change will be seamlessly merged. It's as if every single collection entry or field in our configs is a separate .yaml or a separate folder, allowing for very targeted changes. - Another good example is a request to change squad size frequently popping up in our modding channel. Without this feature, it requires maintaining full copies of every single scenario in the game and a copy of global simulation settings file, which is not nice and blocks an uncountable number of other possible mods related to these files. With the config edit feature, squad size mod will not conflict with much else and requires no updates when we edit simulation config or scenarios in a new release.
The format of these edit configs is relatively simple. The main limitation is that a path needs to be entered for every individual edit. There is no support for multiline edits such as declaring a whole object at once. Key elements of each ConfigEdit config:
- Set
removedtotrueto remove targeted config from the game (for instance, if your mod replaces parts of the arsenal and needs to remove built-in weapons from the game) - Fill
editsfield to perform fine grained changes in targeted config- Each line must begin with
-, just like any serializedList<T>in other configs. - Each line must be wrapped in
'quotation marks, like so:- 'hidden: true'. This enables you to use reserved characters anywhere within the line. - Each edit consists of two sides separated by a colon
:- left side is a path, and right side is a value- For top level fields, the path is just the field name. For instance, to modify a top level field named
squadSizeto a value of3you can just type:- 'squadSize: 3'. - Some fields are nested within other fields - to reach them, include all steps into the path, separated by full stops
.- for instance, here is a modification to a field nested 1 level deep:- 'customPlayerSlot.spawnTagsUsed: true'. - You can even access collections as if keys in dictionaries or indexes in lists were field names, like so:
- 'states.no_hostiles.visible: false'(no_hostilesis a dictionary key here);unitChecks.0.value: 1(0is a list index here!)
- For top level fields, the path is just the field name. For instance, to modify a top level field named
- You can repeat edits with same path multiple times, since the
editsfield is not a dictionary. For instance, it's totally fine to modify atagscollection on some config (e.g. removing something) and then modify it again further down (e.g. to add something)
- Each line must begin with
- The set of value types that can be parsed is for now limited to the following set:
-
string,bool,int,float,Vector2,Vector3,Vector4 - For string fields, right-side value will be used as is
- For bools, it will be parsed using equality to
true - For floats and integers, refer to expected string format in C# documentation for
int.TryParseandfloat.TryParse. An example would be6,8.62etc. - For vectors, the expected format is a correct number of floats in expected float format, separated by commas and wrapped by parentheses. An example would be
(3.1, 4, 7.2)etc. - However, not every case requires parsing. For instance, what if all you want to do is fill a field with a default value, e.g. fill a reference field with a fresh instance of its type? There is a special reserved keyword for doing so,
!d(for “default”). Use it like so:- 'customExitBehaviour: !d'
-
For normal fields, the following operations are supported:
- Overwriting a value with specified new value
-
- 'field: 7'- overwrite field named fieldwith value of 7
-
- For
List<T>, the following operations are supported:- Overwriting a value with specified new value
-
- 'list.4: 7'- overwrite index 4 in list named list with a value of 7
-
- Adding a new value at specified index:
-
- 'list.3: !+'- create a new object (whatever the type may be) and add it to the list at index 3 -
- 'list.0: 7 !+'- insert value of 7 to the very beginning of the list -
- 'list.8: 7 !+'- insert value of 7 to index 8 (if list has more entries) or just to the very end of the list (if list has fewer entries than 8)
-
- Removing a specified index:
-
- 'list.3: !-'- remove entry at index 3
-
- Overwriting a value with specified new value
- For
HashSet<string>, the following operations are supported:- Adding a value:
-
- 'tags: context_facility !+'- attempt to insert the stringcontext_facilityto the hash set tags
-
- Removing a value:
-
- 'tags: context_fort !-'- attempt to remove the stringcontext_fortfrom the hash set
-
- Adding a value:
- For
Dictionary<T>, the following operations are supported:- Overwriting a value with specified new value
-
- 'dictionary.existing_key: true'- overwrite value at keyexisting_keyin dictionary named dictionary with value oftrue
-
- Adding a new entry:
-
- 'dictionary.new_key: !+'- create a new object (whatever the type may be) and try to insert it to the dictionary at keynew_key -
- 'dictionary.new_key: true !+'- attempt to insert value of true with new keynew_keyinto the dictionary
-
- Removing an entry:
-
- 'dictionary.existing_key: !-'- attempt to remove the entry with key withexisting_key
-
- Overwriting a value with specified new value
Full set of supported operation tags:
-
!+- add new entry -
!-- remove entry -
!d- set to default value (for reference types - a new instance, not null) -
!n- set to null
The set of supported operations is subject to change. However, if you encounter a case warranting large changes to the config edit system (e.g. if you need ability to parse complex data types), consider trying to implement what you're after using libraries, not config edits.

- Metadata flag:
includesLibraries - Folder:
Libraries/ - Refer to the Creating library mods to learn about making this type of modification with the SDK.
Custom libraries are an very powerful tool that can facilitate nearly any modification to Phantom Brigade. The game is powered by Unity/C# and reads IL (interpreted language) at runtime. The IL code is modifiable at runtime, which means that it's possible to patch game logic and execute new code from mods. Follow the tutorial linked above to learn the full process of creating a library mod, from setting up the project to building and exporting your mod.
- Metadata flag:
includesTextures - Folder:
Textures/ - Refer to the Including textures to learn about making this type of modification with the SDK.
This content type allows you to overwrite existing textures or extend the set of available textures:
- Place your textures under under Mods/[ModName]/Textures.
- If the folder is not recognized by the game, it will be skipped (see PhantomBrigade_Data/StreamingAssets/UI to see what folders the game knows about and samples of content in them)
- If a texture doesn't match the requirements game has defined for a given folder, it will be skipped (inspect existing textures in StreamingAssets to determine the required format)
- If a texture with the same name was already loaded (for a given folder), it will be replaced. If no such name existed before, new texture will be registered under it.

- Metadata flag:
includesAssetBundles - Folder:
AssetBundles/ - Refer to the Creating asset mods to learn about making this type of modification with the SDK.
- Further reading:
This content type allows you to sideload Asset Bundles: packaged Unity Engine content like prefabs and 3D models. The sideloaded assets can be used by library mods or (if belong to some known types) be automatically picked up by the game. Check the article above and the Introduction to Asset Bundles article in the Unity documentation to learn more.

- Metadata flag:
includesLocalizationEdits - Folder:
LocalizationEdits/ - Refer to the Creating text mods to learn about making this type of modification with the SDK.
This type of content payload enables you to add text to the localization database. It is rare (and will be even rare in the future) that data files under Confgs/Data and Configs/DataDecomposed contain any player facing text, since storing text that way prevents the possibility of localization and easy text modification. Most of the text in the game will eventually be stored in localization database, under Configs/Text, split into versions for different languages. This poses a problem to mods, however - if you add a new item, and the text for it is not stored in the main data config, how do you add player facing text for it?
For an example, if you're adding a new thruster subsystem, you definitely want it to have a name and a description. But name and description do not come from subsystem config, since the game supports their localization - the game takes the subsystem name like internal_aux_thruster_mod, decides on localization keys based on it, like internal_aux_thruster_mod__name and internal_aux_thruster_mod__text and tries to fetch text using these keys from the appropriate text sector of the loaded language (e.g. Configs/Text/English/Sectors/equipment_subsystems.yaml. Without ability to mod these text sectors, your new item can't have a user-facing name/description.
How this type of content can be used is highlighted on the screenshot above, but just in case, here is a step by step overview:
- Lets say your mod contains a new config at
ConfigOverrides\DataDecomposed\Equipment\Subsystemscalledinternal_aux_thruster_mod.yaml. Such a file doesn't exist in the base game - it's a new item, not a modified one. The game can't show any text for it. - You find an example of how other subsystems in the game are localized. The easiest two ways are:
- If you have IL inspection tools like dotPeek or Rider and know how to open game codebase, you can read
ResolveTextmethods in a database class you're interested in, and learn what text sector and text key pattern is used for every item - Alternatively, you can use an editor like Notepad++ or Sublime Text and search
Configs\Text\English\Sectorsin the game install folder for name of an existing subsystem, likeinternal_aux_thruster_hotrod, to see which sector file such text is stored in and how the text keys are structured. - You add another folder to your mod, right next to
ConfigOverridesfolder:LocalizationEdits. You put a file into it that declares you want to add 2 new text strings (for name and description) into English localization, intoequipment_subsystemstext sector, because that's where text for subsystems is stored based on your investigation. - If you placed everything to the right folder and included the localization edits flag into your mod metadata, you're done - your new item will have new player facing text in game!
Internally, localization edits are applied every time the game loads language database and a registered per language key, which means that:
- Even if you reload a language, the changes are still applied
- Even if you switch a language, the changes are still applied
- You can even support multiple localizations and unofficial localizations loaded via other mods - for instance, you can create
MyMod/LocalizationEdits/English/equipment_subsystems.yamlfile to add text to players using English but you can also add a second file atMyMod/LocalizationEdits/Japanese/equipment_subsystems.yamlto add support for Japanese localization. Or someone can make a new mod extending your mod, containing localization for a first mod for a specific language.
Important note: despite its name, the LocalizationEdits system is also utilized for adding new text. A lot of game content like items, pilot traits, interactions, scenarios etc. require localization strings, and new content of these types can be accompanied by new strings through use of the text editing system.
- Metadata flag:
includesLocalizations - Folder:
Localizations/
This content type allows you to add entire new localizations to the game. General principle is as follows:
- Copy official English folder from game installation folder
Configs/TextintoMyMod/Localizations, rename it to the language you are localizing the game into, e.g.MyMod/Localizaions/French - Translate all text in the included files
- If a mod with localization is loaded, game settings window will have a new Language setting that will let you switch to localization language
Some additional notes:
- To test coverage of the localization system, there is a console command
data.toggle-localization-debug, which will render all localized text in a different color and randomize its content - To determine which part of a game uses which portion of localization database, it's recommended to use IL inspection tools like ILSpy or dotPeek, open
Assembly-CSharpfrom game install folder and look for methods namedResolveTextin files inheriting fromDataContainerclass. Majority of databases in the game resolve localized text in such methods, and reading them will allow you to learn how localization keys are generated and used from data keys. This can also be useful for localization editing mods discussed above.
Keep an eye out on game log - it will contain plenty of information from mod manager in case anything goes wrong, or confirmation of successful loading if everything goes well. Look for messages towards very beginning of the log, prefixed with “Mod Manager”. To look at the log, press Ctrl+Shift+F11 or open the log file in application data folder.
It might also be useful to enable developerMode: true property in debug.yaml in AppData/Local/PhantomBrigade/Settings to get access to additional options, debug information and console commands while developing a mod.
Note that no bug reports or crashes from a game in developer mode or with active mods will be processed by BYG.
This wiki is a work in progress. Please make sure to check the built-in tutorials within the SDK or the modding articles on the game wiki to supplement this page. If you get stuck or experience a bug, please don't hesitate to ask questions in the #phantom-modding channel of the official Discord server. We can't wait to see what you create!