Asset Streaming¶
Work in progress
Asset streaming is under active development the supported features constantly change.
If a mod fails to load, the client log names the file and the reason. Reporting the mod that caused it in our discord bug tracker, is the fastest way to get it supported.
A bundle can replace or extend game content. It does that with files placed in
an assets/ folder at the bundle root. On the client the bundle's assets are
extracted and mounted, and the contents decide what happens: .archive files
add or replace content, .xl files patch existing resources, and .tweak or
.yaml files change TweakDB records.
Note
We aim to eventually support the full feature set that the popular modding tools ArchiveXL and TweakXL support, but currently only a partial set of features is supported
Only files directly inside assets/ are picked up. Recognised extensions are
.archive, .xl, .tweak, .yaml and .yml. Anything else in that folder
is ignored.
Which file to use¶
| You want to | Use |
|---|---|
| Add new content: items, entities, world sectors, archives of your own | .archive |
| Point an existing resource at your own file | .xl with resource.patch |
| Add or modify localization strings | .xl with localization |
| Change existing TweakDB records | .tweak or .yaml |
Replace an AppearanceDefinition |
.xl with resource.patch targeting an .app |
If you are porting an existing mod, keep it in the format it already uses.
A .xl from ArchiveXL and a .yaml from TweakXL are read as-is when the
feature they use is supported.
Archives¶
Any .archive file in assets/ is mounted as a mod depot. Content inside it
becomes available to the game under the resource paths it declares, which is
how new entities, items and streaming blocks are added.
Packing works the same as for the base game. Build a normal WolvenKit project
and put the resulting .archive in assets/. A bundle can ship several.
Resource patches¶
An .xl file with a resource block points a game resource at a file you
ship. This is how you replace an appearance without overwriting the original
archive:
resource:
patch:
# your file, relative to assets/, then the resource it replaces
"my_bundle\\appearances\\cool_jacket.app": "base\\characters\\garment\\jacket_01.app"
The left side is a file you ship, the right side is the game resource it replaces. Both use the game's backslash resource path syntax, and quoting them keeps YAML from treating a backslash as an escape.
What can be patched¶
| Target | Supported |
|---|---|
.app (appearance definitions) |
Yes |
03_night_city.devices |
Yes |
03_night_city.psrep (persistent state) |
Yes |
| Any other resource path | No, the log names the target |
Targets outside those three are rejected rather than silently ignored, so a patch that does nothing is visible in the log.
World streaming¶
The streaming block adds streaming blocks and removes nodes from existing
sectors:
streaming:
# archives whose blocks become part of the world
blocks:
- "my_bundle\\world\\my_blocks.streamingblock"
# remove nodes from an existing sector
sectors:
- path: "base\\worlds\\03_night_city\\_compiled\\default\\03_night_city.streamingsector"
expectedNodes: 128
nodeDeletions:
- index: 12
type: worldStaticMeshNode
- index: 47
type: worldInstancedMeshNode
expectedNodes is a guard: the patch only applies when the sector really has
that many nodes, so a game update that shifts the node order cannot silently
delete the wrong thing. Always set it when you delete nodes.
Localization¶
The localization block adds or replaces on-screen text and subtitles:
localization:
onscreens:
en-us:
- "my_bundle\\localization\\onscreens.json"
subtitles:
en-us:
- "my_bundle\\localization\\subtitles.json"
onscreens and subtitles take either a language keyed map, a plain list,
or a single path. A plain list or single path is applied as en-us. The
supported language codes are the ones the game ships: ar-ar, cz-cz,
db-db, de-de, en-us, es-es, es-mx, fr-fr, hu-hu, it-it,
jp-jp, kr-kr, pl-pl, pt-br, ru-ru, th-th, tr-tr, ua-ua,
zh-cn, zh-tw.
Patches merge into the existing localization resources instead of replacing them, so two bundles can extend the same file.
extend, lipmaps and vomaps are parsed but not applied yet. They are
logged and skipped rather than failing the bundle.
Tweaks¶
.tweak (RED format) and .yaml / .yml (TweakXL YAML format) files change
existing TweakDB records. Both are read from assets/:
MyMod.MyWeapon:
$type: Weapon_Data
damage: 42
# Fields on an existing record are replaced by name
Items.Preset_MyWeapon:
powerLevel: 5
Ordering¶
Files are applied in a fixed order so results are deterministic when several bundles touch the same record. The first character of the file name decides the tier:
| First character | Order |
|---|---|
_, #, $, ! |
Before normal files |
| anything else | Normal |
^ |
After normal files |
Within a tier files are applied in name order. A leading underscore is the usual way to make a patch win over a mod you are building on.
Supported directives¶
$type and $base are understood. These TweakXL directives are recognised
but not yet implemented, and are reported rather than ignored:
| Directive | Purpose | Status |
|---|---|---|
$type |
Record type, same as $base |
Supported |
$base |
Record type or inline inheritance | Supported |
$props |
Spread properties into a record | Not yet |
$instances |
Declare inline instances | Not yet |
$dlc |
Scope a record to a DLC | Not yet |
$game |
Scope a record to a game version | Not yet |
Any other $ prefixed key is rejected as unknown.
What is not supported yet¶
The following exist in ArchiveXL or TweakXL but have no equivalent here. A file using them loads partially or is rejected, and the log names the reason:
- Journal patches (
journalin an.xl) resource.meta,resource.linkand factory index blocksextend,lipmapsandvomapslocalization- The
$props,$instances,$dlcand$gametweak directives - RED script extensions and hot reloading
Related¶
- Bundle Config for the
assets/folder and the rest of the manifest - Bundle IO for the server-side file area
- Project Layout for where assets sit in a bundle