Skip to content
k-mods.com

Editing and Validation

Use code and visual YAML editing, class lookup, Problems, and recovery.

Open Explorer and editor to browse project files, create a YAML document or folder, and edit source in the code editor. File → New document lists supported KDataForge document types. Tabs show open files and error counts. Use Catalog for one reflected class path or Array catalog to review a replacement for a list. Rename ID (F2) previews generated IDs and affected project references before applying. Save with Ctrl+S; Save all is Ctrl+Alt+S.

Visual editor offers typed fields for a selected YAML document and a live YAML preview. Its class pickers use the bound reflection archive. Multi-document files have document tabs. If a YAML construct cannot be represented safely in the form, the editor asks you to open Code editor and leaves source unchanged. Raw YAML remains the source of truth in both modes.

For pack configuration, choose File → New document and the type Config. The dialog adds the required .config.yml ending when it is missing (and refuses that ending for any other type) and starts the file from a skeleton with a slider and a checkbox. The Document template list offers Config category class, which creates the UFGCategory subclass that groups the fields. The Visual editor shows the category and fields of a config document, quotes values that start with {, and leaves typed references such as cfg:balance.ironSpeed unquoted. See Configuration, references and templates.

Both editors detect when the disk version changes after a file was opened. Compare or reload before overwriting it. A recoverable unsaved buffer appears under Recovery; see Operations and releases.

When opening a Visual editor link or switching projects, the requested file is checked against the current project’s refreshed file list. A missing file or folder target falls back to the first YAML file; a project without YAML opens no document. Project-bound Code and Visual views accept existing files only when their resolved physical paths stay inside the project root. Symlinks and junctions pointing inside that root are supported; outside or unresolved targets are refused.

Pack manager → Documents also edits YAML inline with Monaco, using the file’s actual native URI and the same dirty buffer as the full editor. Save writes explicitly; Revert returns to the current loaded baseline, which advances to the saved text after a successful Save. Open file in editor keeps the latest changes unsaved. See Projects and Packs for conflict handling and other supported text files. Inline YAML reuses completion, hover, signature help, definitions, semantic highlighting, and diagnostics from the existing language server. These results require the enabled, ready language-server module. Text editing remains available when it is disabled; the enabled linter module provides explicit lint runs.

In the standalone Code-OSS editor, Ctrl+Space opens live suggestions at the cursor. A property path: uses the current patch’s target and inherited reflected fields. A target list offers fields with matching names and types on every selected class. Already used fields stay selectable, sort after unused fields and show in use, line … for the current patch. This also works while the next YAML value is incomplete.

The suggestion list shows property names and C++ types. Its adjacent details panel shows the declaring class, exported description and units when available, supported operation chips, and a YAML value example. The source heading uses the bound datapack’s actual name. Missing reflection metadata stays absent. Class lists containing more than 200 matches request fresh results as you refine the text.

ActionResult
EnterInsert the selected field
Tab on a reflected field that supports setInsert the field and add op: set if no sibling operation exists
Click an operation chipInsert the field with that operation; replace an existing simple sibling operation while preserving its quotes and comment
Ctrl+ZUndo the field and operation together
Ctrl+SpaceRequest suggestions for the current field, operation, class reference or supported value

For example, open a cdo document and use the bound archive to select a recipe target:

type: cdo
patches:
- target: /Game/FactoryGame/Recipes/Packager/Recipe_PackagedSulfuricAcid.Recipe_PackagedSulfuricAcid_C
properties:
- path: mManufactoringDuration
op: multiply
value: 0.5
- path: m

Place the cursor after the final m, press Ctrl+Space, then choose mProduct. Enter inserts its name; Tab adds op: set; its append chip inserts op: append. Add the actual value before enabling the pack. The details example explains the value shape and is not a default copied from the class. The exported game field above is spelled mManufactoringDuration; use names from your bound archive.

Inline badges show verified field types and exported units, and class references can show friendly names. Hover a property badge for its exported description, declaring class and common operations. Missing target metadata suppresses a badge, and an unresolvable nested path does not display the root property’s type as its leaf type. A real lint error can show an inline quick-fix button; for example multipy can offer Change to multiply. The valid multiply operation is supported.

In the full native code editor, a Catalog chip appears beside supported class-reference values when Catalog and the language server are enabled and current metadata is available. This includes scalar or list target values, ofClass, allAssetsOfClass, generated-class parent values, and reflected class properties such as mProducedIn array entries. Click the chip to browse classes appropriate to that value and review insertion. Ctrl+Space uses the same class constraints for completion; inserting a match replaces the current value. Moving the cursor alone does not request suggestions.

The destination determines the class family: a building’s inheritance parent uses a building descriptor, while a recipe’s mProducedIn uses a buildable actor. Abstract inheritance parents remain selectable; fields requiring concrete classes exclude them. Missing or conflicting reflection metadata does not create a guessed picker or class constraint. Bind the correct datapack before relying on these results. The Pack Documents inline view does not promise Catalog chips, rename, or code actions.

For Wwise sounds, create a sound document or use the Wwise sound wizard. Visual editor exposes sound ID/file plus optional name, description, category and subcategory fields for generated volume controls. The shared linter checks these fields and the actual recording’s PCM16 WAV header, mono/stereo channels, sample rate, file size and pack containment. Sound IDs participate in definition lookup and renaming, including ADA’s mAudioEvent references. See the complete ADA voice-over pack.

The language server reports live diagnostics while editing. Open Problems to filter by file, active pack, severity, source, or search text. Group results by file, code, or severity; select a diagnostic to jump to its source. Supported quick fixes appear on the selected diagnostic.

Quick fix in Problems requests a fix for the selected diagnostic and previews the exact YAML change. Choose Apply to update the buffer, then save explicitly. Changing the selected diagnostic cancels a pending fix or review. A changed project, file, or source text invalidates the preview; run Quick fix again. Fixes touching multiple files require review in Code editor.

Use Run lint in Problems for a complete project check. You can restart the language server there and export the displayed diagnostics as text, JSON, GitHub annotations, or SARIF. Set lint trigger, debounce, schema paths, and warning policy under Settings → Language server. A successful local lint is followed by an in-game test of the enabled pack; runtime behavior depends on installed mods and game data.

With no bound .kdfdata, schema checks still work. Reflected class, property, and default-value checks report missing or limited metadata instead of inventing values. See Datapacks and Codex.

The editor understands pack configuration: the config document type, cfg: references, {prefix:body} templates and config: conditions. It knows every field of your pack and every pack in the project, so it can complete, explain and check them while you type. Field ids, providers, keys and operators work from your files alone. Classes, properties, functions, assets and game settings come from a bound reflection export; without one those lists stay empty and the editor does not guess.

Completion opens as you type :, a space, ., {, (, , or a quote. Matches that fit the destination come first, then conversions, and your own pack’s items before other packs’. Lists that could hold thousands of entries stop at 200 and refine as you keep typing.

Where the cursor isWhat is offered
The value of an operation (value, min, max, index, with, including members nested in a struct, list or map value) or a where: operandcfg:<id> for every field whose type fits the destination property (a boolean field for a bool, any number field for a float, a list field for a set, …), cfg:<Pack>:<id> for fitting fields of other packs, and the stubs cfg:, session:, ags:, option:. Fields that would only convert lossily are not offered. When the destination is unknown, every field is listed.
After cfg:The fitting ids of your pack, then the other packs as Pack: entries. After cfg:Pack: the ids of that pack.
After session:, ags:, option:Setting ids from the settings index, filtered by the destination type. session: lists session settings and advanced game settings, ags: the advanced game settings, option: the local game options.
In a string, after {All 14 providers with a short description, pre-pass providers first
After {cfg:Field ids, then Pack: entries. After {session:, {ags:, {option: the setting ids.
After {item:, {resource:, {building:, {recipe:, {schematic:, {research:, {category:Concrete classes below the provider’s base class, and the classes your project generates below it
After {class:Every class, including function libraries, and the classes your pack generates
After {dataasset:, {asset:Your project’s generated assets (data assets for dataasset, every generated asset for asset) and the assets of the export’s asset index
After {<provider>:<target>:The target’s properties, with their type and a cold or newActors tag where it applies, and its callable functions as Name(a: T) -> R, marked (implicit class) or (implicit object) when KDataForge passes that argument itself. A function with parameters inserts Name( and opens the parameter hints.
Inside Name(Enum names, true and false, or class paths for the active parameter
After path: of an operationThe properties of the patch target, and after a dot the members of a struct or object property. Top-level properties show their kind and a cold or newActors tag, see below.
After category: in a config documentUFGCategory subclasses and the category classes your project generates
Inside config:Field ids and settings, operators, and option values (see below)
Keys and values of a config documentDocument keys, the keys of each field (they depend on the field’s type), tooltip and option keys, values for type, valueType, apply, display, multiple and multiline, and for default: true and false for a boolean, the option values for a select

When a template is the whole value, members whose type does not fit the destination sort last.

In a config: expression the editor reads the left operand and offers what can follow it. Operators are filtered by its type: a list offers contains; a boolean or string offers ==, != (a string also in); a number offers all comparisons and in; && and || are always available. After a comparison it offers ids that fit, the option values of a select field, and true or false for a boolean. String literals are written with the quote that suits the YAML style of the value.

Hover shows a card for:

  • a field id, in cfg:, {cfg:}, an expression, or the id of the field’s own definition: name, type and value type, hot or cold, description, tooltip, default, range, options, limits, category, pack and file;
  • a setting id (session:, ags:, option:): display name, value type, tooltip, default, range, options, whether it is session-wide and relevant for dedicated servers, and its source;
  • an operation path:: the property with its type, the class it is declared in, and its reload classification with the reason;
  • a template provider prefix, a target class, and a member (property or function with flags and whether it is callable);
  • a class in category:, parent:, class:, target:, ofClass: or allAssetsOfClass:.

While you type the arguments of a template function, the signature help (opened by ( and ,) shows Name(a: T, b: U) -> R and highlights the active parameter. A parameter that KDataForge fills in for you is left out and mentioned in the note. On an op: value the signature help shows what the operation needs.

Go to definition on a config id in a cfg: reference, a {cfg:} template or a config: expression jumps to the fields[].id of its *.config.yml, also across packs (cfg:<pack>:<id>). Find references, started on a reference or on the id of a field, lists every use in the project. Config ids cannot be renamed with Rename ID, which is only available for generated ids; change the definition and its uses together.

Some properties do not update live, see cold properties. The editor reads the classification from the reflection export and its cold-property catalog:

SituationDiagnostic
An operation path: targets a cold or newActors propertyproperty.cold, information by default, with the reason
A hot config value (typed reference or template) feeds such an operationconfig.hot-into-cold-property (warning)
A hot field sits where only load-time values applyconfig.hot-in-cold-stage, config.hot-in-condition (warnings)

A newActors property is not flagged when the patch sets applyToSpawnedActors: true, or when the document is not a cdo patch (it generates a class, so no actor exists yet). Choose how property.cold appears per project in .kdf-editor.toml:

[lint]
coldProperties = "info" # the default; "hint" shows it as a hint, "off" hides it
DiagnosticFix offered
template.unquoted-start, config.expression-quoteWrap the value in quotes
config.case-mismatchChange to the exact spelling of the field id
config.unknown-idChange to the closest existing field id (one fix per near match)
config.unknown-packChange the pack to the closest pack in the project
setting.unknown-idChange to the closest setting id of the settings index
template.unknown-providerChange the provider to the registered prefix that is one edit away

The full list of diagnostics is on Lint Your Pack.

Edit → Format document formats the active buffer through the enabled formatter. Configure Prettier and optional format-on-save under Settings → Editor; the Prettier provider must be available. The status panel checks global prettier, npm, Deno, and Bun. The install action uses one explicit global install command and asks for confirmation before running it. Format-on-save supports per-project YAML-only, Docs-only, and All supported text trust profiles. YAML semantic guard is enabled by default: every YAML document is parsed before and after formatting, and changed data or invalid output keeps the original buffer. File scope does not sandbox trusted workspace Prettier plugins or configuration. Formatting then saves through the normal file hash check; newer typed text is not overwritten by a delayed formatter result. Codex browses classes, hierarchy, properties, and references from reflection data. Its optional catalog-usage scan locates class paths in saved project YAML.

Implementation checkpoint: selector fixes have passed focused Rust checks and real-archive queries; native GUI confirmation for the current bundled host is pending. Exports without reflection metadata produce Limited previews even when individual matches are definite. Missing defaults, reference metadata or asset coverage can leave candidates Possible; an empty preview cannot prove that no runtime targets exist.

Click Affected (crosshair icon) beside Format to inspect the current entry. The same Affected elements section appears at the bottom of Inspector. Enable the bundled Affected Elements module and bind a project .kdfdata archive.

The list uses the current unsaved YAML entry at the cursor. Results show exported names, paths, sources and selection reasons. Icons come from the bound archive; recipe rows use the exported product icon. Missing PNGs show an explicit fallback icon. Click a row to open class information. The full view supports search and paged results.

For example, this patch changes manufacturing duration of recipes produced in an Oil Refinery:

type: cdo
patches:
- producedIn: /Game/FactoryGame/Buildable/Factory/OilRefinery/Build_OilRefinery.Build_OilRefinery_C
properties:
- path: mManufactoringDuration
op: multiply
value: 0.5

Place the cursor inside the patch, then click Affected. Results are matching recipes. Exact names and counts depend on the bound game/mod export. Multiple values in one filter such as producedIn, ingredient or product are alternatives; different filter keys must all match. Direct target entries are added to filtered selection. applyToSubclasses includes exported descendants.

matchName filters an existing candidate scope; it does not create one. For example, pair it with ofClass to search a class family; ofClass alone selects nothing. Patterns containing / match object paths, while other patterns match class/object names. Matching ignores case and supports * and ? wildcards. Arrays of patterns are alternatives.

Preview supports CDO targets and recipe filters, matchName, common where comparisons, allAssetsOfClass, existing content class entries and recipe/schematic/research removal rules. Possible marks missing defaults, unresolved reference types, asset instance defaults or runtime conditions. Owned gameplay tags can come from runtime interfaces, so matchTag stays Possible. Generated content and live actor instances cannot be enumerated from an export. Syntax errors clear previous results. Disabled modules, missing data and unsupported entries explain their state.

Earlier pack changes and runtime conditions can change selection beyond the exported defaults. The view initially queries up to 500 candidates, can load up to 2,000, and shows matched/possible totals when the visible list is capped.

Recipe names follow the game rule: mDisplayName is used when mDisplayNameOverride is true; otherwise the first product’s item name is used. If the export cannot resolve that name, including an unresolved LOCTABLE entry, the row falls back to its class name and keeps the class path as its stable identity. Native-host confirmation of the recipe-name correction is pending.