An IntelliJ plugin that understands the .cfg files STALKER 2 uses for game data.
Get it on the JetBrains Marketplace
Install it from the IDE with Settings | Plugins | Marketplace, and search for "STALKER 2 Cfg".
Reading
- Syntax highlighting, with a configurable color scheme (Settings | Editor | Color Scheme | STALKER 2 Cfg)
- Folding for every
struct.begin…struct.end, with a placeholder showing field and child counts - Structure view of the struct tree, annotated with
→ ../Base/Armor.cfg:Battle_Varta_Armorinheritance hints - Ctrl+Q on a record shows its fields, what it inherits, and its nested structs
Navigating
- Ctrl-click a
SIDreference to jump to the record it names, across files refkeyresolves to the base struct (in therefurlfile, else the same file, else project-wide);refurlopens the target cfg- Find Usages and Rename for records — renaming rewrites the struct name, its
SID, and every reference - Go to Symbol (Ctrl+Alt+Shift+N) by SID
- Navigate | Related Symbol moves between
Foo_patch_MyMod.cfgand theFoo.cfgit patches
Localization assets
- A Mod SDK
*-localization.uassetpackage opens as the JSON itsLocalizedTextsexport describes, and is written back into the binary package on save — no SDK round trip to change a line of dialogue - Recognised by the package header, not the file name
- Copy JSON / Paste JSON in the editor toolbar, in the same shape the S2Mods
src/localization/uasset.mtsdump prints, so a document moves between the two tools - Any language can be added, including one the package was never saved with: the package's name table is rebuilt around whatever the document needs, and every header offset behind it patched
- A package whose own name does not match where it sits in the SDK is called out in a banner, with
a one-click fix — an asset copied from another mod keeps that mod's name, and its strings never
load. Set the SDK
Contentdirectory under Settings | Tools | STALKER 2 Cfg to turn this on - A document that is not valid JSON, or not a localization document, is not saved: the banner under the editor says why
Writing
- Completion for enum literals, narrowed to the ones the corpus actually assigns to that key
- Completion for
SIDvalues andrefkey - Inspections: unresolved
refurlpath, duplicate key in a struct, and (off by default) unresolved record reference
Derived from s2cfgtojson's Struct.mts, which is the
authority on how the game reads these files:
file := (comment | struct)*
struct := [key ':'] 'struct.begin' [ '{' refs '}' ] body 'struct.end'
| key ':' 'removenode'
body := (comment | struct | entry)*
entry := key '=' value [ '{' refs '}' ]
key := IDENT | '[' (INT | '*') ']'
refs := ref (';' ref)*
ref := ('refurl'|'refkey'|'bskipref'|'bpatch'|'removenode') ['=' text]
value := bool | number('f'?) | 'EEnum::Member' | text | <empty>
comment := a line starting with '#' or '//'
Two things the corpus taught that the docs don't say: Key : removenode is a bodyless struct that
deletes an inherited one, and some SpawnActorPrototypes files start with a UTF-8 BOM.
The rule that makes navigation useful: SID = X directly inside a top-level struct declares that
record, while the same line nested deeper (inside Launchers → Connections) is a reference to
another record. Only the former is indexed.
JAVA_HOME=~/.jdks/jbr-17.0.14 ./gradlew buildPlugin # -> build/distributions/S2CfgSupport-0.3.0.zip
JAVA_HOME=~/.jdks/jbr-17.0.14 ./gradlew test # 50 tests, incl. a parse of the whole S2Mods corpus
JAVA_HOME=~/.jdks/jbr-17.0.14 ./gradlew runIde # sandbox IDEGradle fetches its own JDK 21 toolchain (via the foojay resolver), because the JVMs installed
system-wide here are JREs with no javac.
Install a locally built zip with Settings | Plugins | ⚙ | Install Plugin from Disk — or take the released build from the Marketplace listing.
Publishing an update, once a Marketplace permanent token is in hand:
PUBLISH_TOKEN=... ./gradlew publishPlugin