Targeting
Every way to select targets, including class CDOs, asset instances, all assets of a class, tag / name / property filters, recipe shortcuts (producedIn, ingredient, product), subclasses, spawned actors, and the same filters in remove:, block:, and sinkpoints entries.
Every type: cdo patch needs at least one target selector. The current validator accepts multiple
selectors and GatherTargets accumulates their matches, but one clear selector per patch is easier
to reason about. This page shows all selectors and the modifiers that widen a selection.
target: class path: the class default object (CDO)
Section titled “target: class path: the class default object (CDO)”The most common form. Blueprint asset classes use the _C suffix (auto-retried if forgotten);
native classes use /Script/Module.Class:
type: cdopatches: - target: /Game/FactoryGame/Recipes/Smelter/Recipe_IngotIron.Recipe_IngotIron_C properties: - path: mManufactoringDuration op: multiply value: 0.5target: bare class name: shortcut for the full class path
Section titled “target: bare class name: shortcut for the full class path”Anywhere a class path is accepted, a bare class name (no / or .) resolves against every
class currently loaded in memory, so you don’t have to spell out the full asset path:
type: cdopatches: - target: Desc_Water # same class as properties: - path: mResourceSinkPoints value: 0If the short name matches more than one loaded class, KDataForge logs an Error to
LogKDataForge listing every match. It still applies the patch, using the first match sorted by
full class path, so the result stays deterministic. Prefer the full class path in published packs
to avoid relying on load order. This shortcut is meant for quick local tweaks.
target: generated-content id: shortcut for /KDataForge/Gen/...
Section titled “target: generated-content id: shortcut for /KDataForge/Gen/...”A bare id also resolves to a class your own pack generated, so you can target your own
item/recipe/schematic/… content without spelling out /KDataForge/Gen/<PackRef>.Gen_...:
type: cdodebug: truepatches: - target: MySuperIngot # same as /KDataForge/Gen/MyPack.Gen_MyPack_MySuperIngot properties: - path: mResourceSinkPoints value: 50This is checked before the bare class-name shortcut above, and works the same way everywhere a
class or object reference is accepted, not just target:. See
Generated Content Short Ids
for the full write-up (resolution order, cross-pack matches, and which reference kinds support it).
target: object path: one asset instance
Section titled “target: object path: one asset instance”The same key also accepts an object path (PrimaryDataAsset instances etc.) and patches that
object directly. Instances have no _C suffix: they are objects, not classes.
type: cdoconditions: hasMod: KAPIpatches: - target: /KLib/Assets/MinerDescriptions/DA_MinerDesc_Iron.DA_MinerDesc_Iron properties: - path: mIsDisabled value: truetarget: a sequence of paths: patch several targets in one entry
Section titled “target: a sequence of paths: patch several targets in one entry”target: also accepts a list of paths instead of a single scalar. Every entry is resolved the
same way a scalar target: would be (class path, bare class name, generated-content id, or object
path), and the same properties:/applyToSubclasses:/applyToSpawnedActors: apply to each one:
type: cdodebug: truepatches: - target: - MySuperIngot # generated-content id - /Game/FactoryGame/Resource/Parts/IronPlate/Desc_IronPlate.Desc_IronPlate_C properties: - path: mResourceSinkPoints value: 50This is purely a convenience for “these unrelated targets happen to want the exact same
properties: block”, it does not widen a single class the way applyToSubclasses: does, and
mixing target kinds (a class path alongside an object path) in one list is fine, each entry is
resolved independently. Prefer applyToSubclasses: or matchTag: instead when the targets share a
common base class or tag; reach for a target: list when they don’t.
allAssetsOfClass: every asset of a class
Section titled “allAssetsOfClass: every asset of a class”Patches every asset of that class (and its subclasses) found in the asset registry. This is ideal for data-asset families:
type: cdoconditions: hasMod: KAPIpatches: - allAssetsOfClass: /Script/KAPI.KAPICleanerItemDescription properties: - path: mProductionTime op: multiply value: 0.75ofClass: scope + filters: select by tag, name, recipe shape, or property values
Section titled “ofClass: scope + filters: select by tag, name, recipe shape, or property values”The remaining selectors all work the same way: ofClass: names a scope (that class and every
subclass of it, plus data-asset instances of it unless matchAssets: false), and one or more
filters on the same patch entry pick the members of that scope to patch. Every filter present on
an entry must pass, so the filters are AND-combined:
| Filter | Matches when … |
|---|---|
matchTag: (+ tagProperty:) | the object carries every listed gameplay tag |
matchName: | the class name (or full path) matches any listed wildcard pattern |
producedIn: | a recipe’s mProducedIn references any listed building class or a subclass of it |
ingredient: | a recipe’s mIngredients[*].ItemClass references any listed item class or a subclass of it |
product: | a recipe’s mProduct[*].ItemClass references any listed item class or a subclass of it |
where: | every listed path / operator clause holds |
The three recipe shortcuts (producedIn:, ingredient:, product:) default the scope to
/Script/FactoryGame.FGRecipe when ofClass: is omitted, and any matchTag:, matchName: or
where: on the same entry shares that default. Without a recipe shortcut those three require an
explicit ofClass:. Set ofClass: anyway to narrow the scope to a mod’s own recipe base class.
Filters never narrow a target: or allAssetsOfClass: selector on the same entry, those still
match everything they name. When no object in the scope passes, KDataForge logs one warning that
repeats the filters it evaluated, so you can see which one was too strict.
matchTag: select by gameplay tag
Section titled “matchTag: select by gameplay tag”Selects everything in the scope that carries the tag(s). matchTag: accepts a scalar or a sequence,
and when it’s a sequence, all tags must match. Two optional keys are available: tagProperty:
names the tag container property explicitly, and matchAssets: also matches data-asset instances
(default true):
type: cdoconditions: hasMod: KAPIpatches: - matchTag: KMods.AlternateRecipe # scalar; use a block sequence for multiple tags ofClass: /Script/FactoryGame.FGRecipe # required scope tagProperty: mGameplayTags # optional explicit tag container property matchAssets: true # also match data-asset instances (default true) properties: - path: mManufactoringDuration op: multiply value: 0.8Register your own tags first using type: gametag. The GameTags stage always runs before
everything else. Then match on them in the same pack: see
Game Tags.
matchName: select by class name pattern
Section titled “matchName: select by class name pattern”Matches the class name against a wildcard pattern (* any run of characters, ? one character,
case-insensitive). The trailing _C of Blueprint classes is optional in the pattern. When the
pattern contains a /, it is matched against the full class path instead, which lets you select
by content folder. A sequence matches when any pattern matches:
type: cdopatches: - ofClass: /Script/FactoryGame.FGRecipe matchName: Recipe_Alternate_* # every vanilla alternate recipe, no tags needed properties: - path: mManufactoringDuration op: multiply value: 0.8 - ofClass: /Script/FactoryGame.FGSchematic matchName: /Game/FactoryGame/Schematics/Progression/* # by folder: the milestones properties: - path: mTimeToComplete op: multiply value: 0.5For data-asset instances the pattern is matched against the asset’s own name and path, not its class.
producedIn: / ingredient: / product: recipe shortcuts
Section titled “producedIn: / ingredient: / product: recipe shortcuts”The three shortcuts answer the questions pack authors ask most often about recipes, without
spelling out a where: clause. Each accepts a class path (any form that target: accepts: full
path, bare class name, or generated-content id) or a sequence of them, and a recipe matches when
any entry is referenced or one of its subclasses is:
type: cdopatches: # every recipe crafted in a Constructor is 25% faster - producedIn: /Game/FactoryGame/Buildable/Factory/ConstructorMk1/Build_ConstructorMk1.Build_ConstructorMk1_C properties: - path: mManufactoringDuration op: multiply value: 0.75
# every recipe crafted in ANY manufacturing building (Constructor, Assembler, Manufacturer, # Refinery, Blender, Particle Accelerator, modded machines deriving from the same base, …) - producedIn: /Script/FactoryGame.FGBuildableManufacturer properties: - path: mVariablePowerConsumptionFactor op: multiply value: 0.9
# every recipe that consumes Plastic OR Rubber - ingredient: [Desc_Plastic_C, Desc_Rubber_C] properties: - path: mManufactoringDuration op: multiply value: 0.9
# every recipe that produces a Screw, but only when it is crafted in an Assembler - product: Desc_IronScrew_C producedIn: Build_AssemblerMk1_C properties: - path: mProduct[0].Amount op: multiply value: 2The subclass rule is what makes the native base classes useful as values: FGBuildableManufacturer
covers every machine that derives from it, /Script/FactoryGame.FGBuildGun covers every build-gun
(buildable) recipe, and /Script/FactoryGame.FGWorkBench covers every hand-craftable recipe (the
vanilla Craft Bench is BP_WorkBenchComponent_C, a subclass). A class that is not loaded yet when
the patch runs is matched by exact path only (a warning tells you), the subclass rule needs the class
resolved.
where: filter by property values
Section titled “where: filter by property values”where: compares reflected properties of every candidate in the scope. It takes one map, or a
sequence of maps that must all hold. Each map names a path: (the same
property path grammar as properties:, plus
[*] for “any element”) and exactly one operator:
| Operator | Applies to | Matches when … |
|---|---|---|
equals, notEquals | everything | the value equals the given YAML value (class references: the class or a subclass) |
lessThan, lessOrEqual, greaterThan, greaterOrEqual | numbers, enums (ordinal), booleans | numeric comparison |
contains, notContains | arrays, sets, tag containers, strings/names/text | an element equals the value (same rules as equals), the container has the tag, or the text contains the substring |
in, notIn | everything | any listed value matches: equals for a scalar leaf, contains for an array/set/tag container leaf |
matches | everything | the display text matches a wildcard pattern (*Alternate*); references use their path |
isEmpty: true/false | containers, text, references | the container/text is empty, or the reference is null |
A candidate that does not have the property at all simply does not match. A comparison that cannot
apply to the property’s type (greaterThan on an array, [*] on a float, …) is reported once
per patch entry as a warning naming the path. Remember that the scope class’s own CDO is a
candidate as well, and it only holds default values: isEmpty: true on mProducedIn matches
FGRecipe itself, not just the orphaned Blueprint recipes you were thinking of. /kdf report lists
every object a patch touched.
type: cdopatches: # long recipes (more than 8 seconds) that use Iron Ingot get a bit faster - ofClass: /Script/FactoryGame.FGRecipe where: - path: mManufactoringDuration greaterThan: 8 - path: mIngredients[*].ItemClass # [*] = any element equals: Desc_IronIngot_C properties: - path: mManufactoringDuration op: multiply value: 0.8
# every schematic up to tier 3 completes instantly - ofClass: /Script/FactoryGame.FGSchematic where: path: mTechTier # a single clause can be written as one map lessOrEqual: 3 properties: - path: mTimeToComplete value: 0
# liquids and gases never earn sink points (`in` = any of the listed values) - ofClass: /Script/FactoryGame.FGItemDescriptor where: path: mForm in: [RF_LIQUID, RF_GAS] properties: - path: mResourceSinkPoints value: 0
# recipes crafted in a Constructor OR an Assembler (`in` on an array = contains any) - ofClass: /Script/FactoryGame.FGRecipe where: path: mProducedIn in: [Build_ConstructorMk1_C, Build_AssemblerMk1_C] properties: - path: mManufactoringDuration op: multiply value: 0.9
# recipes nobody can craft anywhere (empty mProducedIn) get the Craft Bench - ofClass: /Script/FactoryGame.FGRecipe where: path: mProducedIn isEmpty: true properties: - path: mProducedIn op: append value: /Game/FactoryGame/Buildable/-Shared/WorkBench/BP_WorkBenchComponent.BP_WorkBenchComponent_C[*] may appear more than once in a path (mUnlocks[*].mRecipes[*]) and descends through struct
members and object references alike; a where: clause with [*] matches when any element path
satisfies the operator. [*] is read-only: a properties: op still has to address one concrete
element (mIngredients[0].Amount).
Inside a where: path a class reference reads through to its defaults: mProduct[0].ItemClass.mForm
compares the produced item’s form, and mUnlocks[*].mRecipes[*].mProducedIn on a schematic walks
into every unlocked recipe’s producer list (soft references are loaded on demand). This is a filter
convenience only; properties: ops never write through a class reference.
Filters and the lazy class watch: like applyToSubclasses:, every scope-filter patch keeps watching
the session. A Blueprint recipe or item class that loads later (common for content the game only
references on demand) is tested against the same filters the moment it becomes available, and
patched when it passes.
The same filters in remove:, block:, and sinkpoints entries
Section titled “The same filters in remove:, block:, and sinkpoints entries”The scope filters are not a cdo feature. Every document type that selects existing content takes
the same keys in place of one exact reference, with a default scope that fits the type:
| Document / key | Filter entry selects | Default ofClass: | Details |
|---|---|---|---|
type: recipe → remove: | recipes to take out of the game | FGRecipe | Removing Content |
type: schematic → remove: | schematics to take out of the game | FGSchematic | Removing Content |
type: research / mam → remove: | research trees to take out of the game | FGResearchTree | Removing Content |
type: resourcenode → remove: | resource descriptors whose placed nodes are purged | FGResourceDescriptor | Resources |
type: sublevel → block: | provider data assets to block | none, ofClass: required | Sublevels |
type: sinkpoints → entries: | item descriptors that receive the entry’s points: | FGItemDescriptor | Sink Points |
type: reciperemove: - producedIn: /Game/FactoryGame/Buildable/Factory/HadronCollider/Build_HadronCollider.Build_HadronCollider_Ctype: sinkpointsentries: - ofClass: /Script/FactoryGame.FGItemDescriptor where: path: mForm in: [RF_LIQUID, RF_GAS] points: 0Recipe, schematic, and research removal rules are stored as predicates and re-checked against every class the game presents, so late-loading Blueprint content is covered. Resource-node, sublevel, and sink-point filters expand once, when the document applies, against what is loaded at that moment; the load report tells you how many matched.
applyToSubclasses: widen a class target to every derived class
Section titled “applyToSubclasses: widen a class target to every derived class”With a class target, applyToSubclasses: true also patches every derived class’s CDO:
type: cdopatches: - target: /Script/FactoryGame.FGBuildableResourceExtractor applyToSubclasses: true # every miner, oil extractor, water pump, … properties: - path: mExtractMultiplier op: min value: 2⚠️ Wide blast radius. Subclass expansion covers every derived class, including ones added by other mods you may never have heard of. Prefer the most-derived class that still covers what you mean. Use
min/max/clamp/multiplyrather than absolutesetwhere possible, and check/kdf reportto see exactly which classes were patched.
applyToSpawnedActors: runtime actor patches
Section titled “applyToSpawnedActors: runtime actor patches”For actor classes, saves and archetypes override CDO values on already-placed instances. Setting
applyToSpawnedActors: true re-applies the ops to every instance the moment it spawns (world
begin play and every later spawn):
type: cdopatches: - target: /Game/FactoryGame/Buildable/Factory/StorageContainerMk1/Build_StorageContainerMk1.Build_StorageContainerMk1_C applyToSpawnedActors: true # re-applied to every instance the moment it spawns applyToSubclasses: false # exact class only properties: - path: mInventorySizeX value: 12 - path: mInventorySizeY value: 8These instance writes are unconditional. They do not compare a value against the vanilla snapshot or preserve a value changed by a player or save.
Resource Nodes are an important consequence of class-only actor matching. KDataForge cannot select one placed node by location, descriptor, purity, amount, or node type. See Resources and Resource Nodes.
Choosing a selector
Section titled “Choosing a selector”The selector keys cover targets from a single class to every actor in a class family. Use the table below to choose the narrowest useful selector:
| I want to patch… | Selector |
|---|---|
| One recipe / item / building class | target: class path |
| One data-asset instance | target: object path (no _C) |
A handful of unrelated targets, same properties: | target: sequence of paths |
| A whole data-asset family | allAssetsOfClass: |
| Everything carrying a tag | matchTag: + ofClass: |
| Every class whose name / folder matches a pattern | matchName: + ofClass: |
| Every recipe crafted in a building (or building family) | producedIn: |
| Every recipe consuming / producing an item (or item family) | ingredient: / product: |
| Everything whose property values meet a condition | where: + ofClass: |
| A base class and everything derived | class target: + applyToSubclasses: true |
| Values on placed/spawning actors | + applyToSpawnedActors: true |
Filters combine: producedIn: + ingredient: + where: on one entry select recipes that satisfy
all three. Prefer the narrowest combination that still describes what you mean, and check
/kdf report to see exactly which classes were patched.
