Skip to content
k-mods.com

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: cdo
patches:
- target: /Game/FactoryGame/Recipes/Smelter/Recipe_IngotIron.Recipe_IngotIron_C
properties:
- path: mManufactoringDuration
op: multiply
value: 0.5

target: 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:

/Game/FactoryGame/Resource/RawResources/Water/Desc_Water.Desc_Water_C
type: cdo
patches:
- target: Desc_Water # same class as
properties:
- path: mResourceSinkPoints
value: 0

If 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: cdo
debug: true
patches:
- target: MySuperIngot # same as /KDataForge/Gen/MyPack.Gen_MyPack_MySuperIngot
properties:
- path: mResourceSinkPoints
value: 50

This 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).

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: cdo
conditions:
hasMod: KAPI
patches:
- target: /KLib/Assets/MinerDescriptions/DA_MinerDesc_Iron.DA_MinerDesc_Iron
properties:
- path: mIsDisabled
value: true

target: 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: cdo
debug: true
patches:
- target:
- MySuperIngot # generated-content id
- /Game/FactoryGame/Resource/Parts/IronPlate/Desc_IronPlate.Desc_IronPlate_C
properties:
- path: mResourceSinkPoints
value: 50

This 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.

Patches every asset of that class (and its subclasses) found in the asset registry. This is ideal for data-asset families:

type: cdo
conditions:
hasMod: KAPI
patches:
- allAssetsOfClass: /Script/KAPI.KAPICleanerItemDescription
properties:
- path: mProductionTime
op: multiply
value: 0.75

ofClass: 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:

FilterMatches 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.

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: cdo
conditions:
hasMod: KAPI
patches:
- 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.8

Register 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.

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: cdo
patches:
- 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.5

For 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: cdo
patches:
# 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: 2

The 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: 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:

OperatorApplies toMatches when …
equals, notEqualseverythingthe value equals the given YAML value (class references: the class or a subclass)
lessThan, lessOrEqual, greaterThan, greaterOrEqualnumbers, enums (ordinal), booleansnumeric comparison
contains, notContainsarrays, sets, tag containers, strings/names/textan element equals the value (same rules as equals), the container has the tag, or the text contains the substring
in, notIneverythingany listed value matches: equals for a scalar leaf, contains for an array/set/tag container leaf
matcheseverythingthe display text matches a wildcard pattern (*Alternate*); references use their path
isEmpty: true/falsecontainers, text, referencesthe 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: cdo
patches:
# 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 / keyFilter entry selectsDefault ofClass:Details
type: recipe → remove:recipes to take out of the gameFGRecipeRemoving Content
type: schematic → remove:schematics to take out of the gameFGSchematicRemoving Content
type: research / mam → remove:research trees to take out of the gameFGResearchTreeRemoving Content
type: resourcenode → remove:resource descriptors whose placed nodes are purgedFGResourceDescriptorResources
type: sublevel → block:provider data assets to blocknone, ofClass: requiredSublevels
type: sinkpoints → entries:item descriptors that receive the entry’s points:FGItemDescriptorSink Points
type: recipe
remove:
- producedIn: /Game/FactoryGame/Buildable/Factory/HadronCollider/Build_HadronCollider.Build_HadronCollider_C
type: sinkpoints
entries:
- ofClass: /Script/FactoryGame.FGItemDescriptor
where:
path: mForm
in: [RF_LIQUID, RF_GAS]
points: 0

Recipe, 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: cdo
patches:
- 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/multiply rather than absolute set where possible, and check /kdf report to 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: cdo
patches:
- 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: 8

These 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.

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 classtarget: class path
One data-asset instancetarget: object path (no _C)
A handful of unrelated targets, same properties:target: sequence of paths
A whole data-asset familyallAssetsOfClass:
Everything carrying a tagmatchTag: + ofClass:
Every class whose name / folder matches a patternmatchName: + 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 conditionwhere: + ofClass:
A base class and everything derivedclass 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.