Content Registry
UModContentRegistry is a GameInstance subsystem that parses each mod's ModContributions.json and makes their contributions available to your game. It handles three types of contributions automatically.
Contribution Types
1. New Content
Mods can add entirely new assets — new maps, new items, new characters — using their plugin mount point (/Plugins/ModId/). These are available via the Asset Registry without any special handling.
Use QueryModAssets() on UModLoaderSubsystem to find new content from mods:
FModAssetFilter Filter;
Filter.AssetClass = UStaticMesh::StaticClass();
Filter.NameContains = "chair";
TArray<FAssetData> Assets = ModLoader->QueryModAssets(Filter);
2. Class Overrides
Mods can replace one of your Blueprint classes with their own version. Declared in ModContributions.json:
{
"ClassOverrides": [
{
"Original": "/Game/Characters/BP_Enemy.BP_Enemy_C",
"Replacement": "/Game/Mods/MyMod/Characters/BP_BetterEnemy.BP_BetterEnemy_C"
}
]
}
When you spawn actors using SpawnResolvedActor(), ModKit transparently substitutes the override:
UModContentRegistry* Registry = GetGameInstance()->GetSubsystem<UModContentRegistry>();
// Spawns BP_BetterEnemy if a mod overrides BP_Enemy
AActor* Enemy = Registry->SpawnResolvedActor(
GetWorld(),
BP_Enemy_Class,
SpawnTransform
);
Conflict Policy
When multiple mods override the same class, EModConflictPolicy determines what happens.
It is configured by you, the game developer, in Project Settings → Plugins → ModKit SDK
(ClassConflictPolicy for classes, CollectionConflictPolicy for DataTables) — it is not
set per-override in ModContributions.json:
| Policy | Behavior |
|---|---|
Priority | Respect the configured mod load order (default for class overrides) |
LastWins | The last-loaded mod's override wins |
Error | Log an error and reject the conflicting override |
Querying Overrides
UModContentRegistry* Registry = GetGameInstance()->GetSubsystem<UModContentRegistry>();
// Resolve what class would be spawned for a given original class
UClass* Resolved = Registry->ResolveClass(BP_Enemy_Class);
// Get all registered overrides
TArray<FModClassOverride> AllOverrides = Registry->GetAllClassOverrides();
3. DataTable Merging
Mods can add or replace rows in your DataTables without shipping the whole table. ModKit detects DataTable assets in the mod's PAK at the same path as the original table, then merges them row-by-row at runtime.
Original game table at /Game/Data/DT_Items:
| Row | Name | Damage |
|---|---|---|
| sword | Iron Sword | 15 |
| bow | Wooden Bow | 8 |
Mod adds /Plugins/MyMod/DT_Items (mirrors the original path layout via contributions):
| Row | Name | Damage |
|---|---|---|
| bow | Elven Bow | 20 |
| staff | Magic Staff | 25 |
Merged result (accessible via UModContentRegistry):
| Row | Source | Name | Damage |
|---|---|---|---|
| sword | game | Iron Sword | 15 |
| bow | mod (override) | Elven Bow | 20 |
| staff | mod (new) | Magic Staff | 25 |
{
"CollectionMerges": [
{
"TargetDataTable": "/Game/Data/DT_Items",
"Rows": [
{ "RowName": "bow", "DataPath": "/Game/Mods/MyMod/Data/Row_Bow.Row_Bow" },
{ "RowName": "staff", "DataPath": "/Game/Mods/MyMod/Data/Row_Staff.Row_Staff" }
]
}
]
}
Explicit CollectionMerges are optional — a DataTable shipped at the mirror path (see the
tip below) is merged automatically without any declaration.
Accessing Merged Tables
UModContentRegistry* Registry = GetGameInstance()->GetSubsystem<UModContentRegistry>();
// Rows contributed to a DataTable via JSON CollectionMerges declarations
TArray<FModTableRow> Rows = Registry->GetMergedCollectionRows(
FSoftObjectPath(TEXT("/Game/Data/DT_Items.DT_Items")));
// Paths of all DataTables that received mod row merges
TArray<FString> MergedTables = Registry->GetMergedDataTablePaths();
ModContributions.json Format
Full example of a ModContributions.json:
{
"ClassOverrides": [
{
"Original": "/Game/Characters/BP_Enemy.BP_Enemy_C",
"Replacement": "/Game/Mods/MyMod/Characters/BP_BetterEnemy.BP_BetterEnemy_C"
}
],
"CollectionMerges": [
{
"TargetDataTable": "/Game/Data/DT_Items",
"Rows": [
{ "RowName": "staff", "DataPath": "/Game/Mods/MyMod/Data/Row_Staff.Row_Staff" }
]
}
]
}
New content (maps, items, etc.) needs no declaration — any asset in the mod plugin is available automatically. Only class overrides and DataTable merges are declared.
ModKit can auto-detect DataTable merges by path mirroring — if a mod has a DataTable at the same /Game/ path as an existing table, it's treated as a merge candidate even without a CollectionMerges entry.
UModContentRegistry Reference
| Function | Returns | Description |
|---|---|---|
SpawnResolvedActor(World, Class, Transform) | AActor* | Spawn with class override applied |
ResolveClass(OriginalClass) | UClass* | Get the effective class (mod override or original) |
GetAllClassOverrides() | TArray<FModClassOverride> | All registered class overrides |
GetContentByType(Type) | TArray<FModContentEntry> | New-content entries of a given type (e.g. "Map"); empty = all |
GetMergedCollectionRows(DataTable) | TArray<FModTableRow> | Rows contributed to a DataTable via CollectionMerges |
GetMergedDataTablePaths() | TArray<FString> | Paths of DataTables that received mod row merges |
GetContributionsByMod(ModId) | FModContributions | All contributions registered by a mod |