Перейти до вмісту

Data packs

Цей контент ще не доступний вашою мовою.

A data pack adds items, recipes, workshops, loot and texts from TOML files, with no code, and changes vanilla values such as how many of an item one slot holds. It is a folder in mods/ with a pack.toml and no DLL:

mods/
you.lanterns/
pack.toml
items.toml
recipes.toml
workshops.toml
loot.toml
texts.toml
icons/
lantern.png

Every file but pack.toml is optional. The loader runs a pack as it runs a mod: it is in mods.toml and the lock, counts in the join check, has its log logs/enshwick/mods/<id>.log, and is turned off alone when it goes wrong. What it adds goes through the same calls a mod makes (add_item, add_recipe, add_workshop, add_loot, add_texts), beside vanilla content. A pack changes vanilla values only through changes, in memory: it never removes a vanilla item, recipe or table, never gives one another id, and never writes the game’s files or saves.

A mod can carry a pack too, see Packs in a mod. A pack with a main.luau is a Luau mod.

The repository’s examples/ folder holds three packs to start from: you.material-stacks, you.recipe-cost-and-time and you.storage-slots.

cargo enshwick new --data you.material-stacks starts a pack: the folder you.material-stacks/ with every file above, each showing its first change commented out, the schema files that let an editor check them, and a README with the steps to check, install and release it. --side sets where it runs, both sides when left out.

id = "you.lanterns"
version = "1.0.0"
name = "Lanterns"
description = "Coloured lanterns, crafted at a bench of their own."
authors = ["Ann"]
license = "MIT"
homepage = "https://example.com/lanterns"
[requires]
"you.trading-post" = ">=0.5, <1.0"
FieldMeaning
id<author>.<name> like a mod id, the folder’s name; the namespace of the pack’s content
versionthe pack’s SemVer version
name, description, authors, license, homepagewhat players read, as a mod’s manifest says it
sideboth (the default), server or client
joinwhether server and players must run the same pack to play together; on unless false, and required for a pack that adds or changes anything players’ games read (everything but loot)
requiresmods and packs it needs, with a version requirement each
enshwickthe Enshwick releases the pack is made for, as a version requirement such as "=1.0.0-alpha.12"; a loader of another release skips the pack and logs both versions. Empty for any
[[setting]]a setting of a Luau mod, one table each

Every name a pack defines lives in its namespace: the item lantern of the pack you.lanterns is the content id you.lockers:locker, with the GUID Guid::named derives. Names are 1 to 128 of [a-z0-9_./-] and unique across the pack’s files. A reference is one of:

  • a name of the pack: lantern;
  • another mod’s content id: you.lockers:locker;
  • a vanilla GUID: 6943053a-32c1-4718-ae22-e61427ddf54d;
  • a vanilla item’s debug name, as the game’s item registry lists it: Craftingstation_Workbench. Where a recipe names a workshop, a crafting station’s debug name names the station’s workshop: workshop = "Prop_Production_T1_Forge".

The pack’s own names come first, so a debug name is looked up only when the pack defines no such name. The loader reads the debug names from the game’s archive when it loads the pack, on the server and the client alike; a name the game lacks, or that several items carry, is a problem at its line, with the names that look like it:

error [you.lanterns] items.toml:7:1: no item "Tool_Torch1" in this pack, and no vanilla item "Tool_Torch1"; near: Tool_Torch01

A change is different. When a change names an item or recipe that the game no longer has, and no name looks like it, the loader skips that part of the change and logs a warning. The rest of the pack still applies, so a game update that removes one item does not turn off the whole pack:

warn [you.stacks] items.toml:3:1: change 1: no vanilla item "Weapon_Old_Sword" in this game build; skipped

cargo enshwick id you.lockers:locker prints a name’s GUID and game id.

[[item]]
id = "lantern"
like = "6943053a-32c1-4718-ae22-e61427ddf54d"
name = "locker.name"
description = "locker.description"
meter = { max = 3, start = 3 }
stats = [{ label = "lantern.light", value = 5, format = "percent" }]
icon = { tint = [255, 170, 60], amount = 60 }

Each item is a clone of like, as add_item makes it: name, description and each stat’s label are keys of texts.toml; meter gives each item a count of its own (start is max when left out); format is number, signed or percent. An icon either recolours a vanilla icon (tint, amount in percent, like the icon’s item, the item’s like when left out) or names a PNG of the pack: icon = { png = "icons/lantern.png" }.

items.toml also changes values of items that exist already, vanilla or another mod’s, with a [[change]] table:

[[change]]
items = ["Material_T1_Wood_Logs", "Build_TerrainMaterial_T1_Stone", "6943053a-32c1-4718-ae22-e61427ddf54d"]
max_stack = 999
[[change]]
items = ["Material_T1_Resin"]
max_stack = { times = 4 }

items names the items as references do. A number sets the value; { times = x } multiplies it, with x from 0 to 100.

FieldMeaningValues
max_stackhow many of the item one inventory slot holds1 to 65535
slotshow many slots the container the item places holds, or the backpack it is, never fewer than it has8 to 128, in steps of 8
damage_moda weapon’s damage factor0 to 100
speed_moda weapon’s attack speed factor0 to 10
armorevery armour value of the item, scaled only: { times = x }a factor
burn_timehow long the item burns as fuel, in seconds0 to 86400
energy_outputthe energy the item gives as fuel0 to 1000000
comfort_amountthe comfort the placed item gives0 to 1000

A change that names no item, changes nothing, gives a value out of range or changes one field of one item a second time is a problem at its line, like every other.

A stack size or container slots can also change for every vanilla item a selection picks, read from the game’s data when the game starts:

[[change]]
category = ["Materials", "Ammunition"]
max_stack = { times = 4 }
[[change]]
items = "stackable"
max_stack = { times = 2 }
[[change]]
items = "containers"
slots = { times = 2 }
[[change]]
items = ["Material_T1_Wood_Logs"]
max_stack = 999
SelectionPicks
category = [...]every item of these categories, by the game’s names: Customization, Equipment, Weapons, Tools, Instrument, FishingRod, BuildTools, Consumables, Ammunition, Materials, Blueprints, Currency, Collectibles, AnimalFood, PetFood, WeaponGem, ColorPalette
items = "stackable"every item one slot already holds more than one of
items = "containers"every item that places a container
items = "backpacks"every backpack, an item worn in the game’s Backpack slot

A selection changes only max_stack and slots. A stack size reaches only items that stack and keep no state of their own, such as durability, so weapons and armour stay at one; slots reach only items that place a container or are backpacks. A backpack’s size reaches only backpacks made after the change: one a player has keeps the size it was made with, which the save holds. An item the pack also names in items keeps that change alone, so the last table above sets Wood Logs to 999 while other materials take four times their size. A change gives items or category, never both. The pack’s log says how many items each selection reached.

The stats change only on items that have them: a damage_mod on an item that deals no damage is left, and the pack’s log says so. A weapon deals the changed damage once the game sets it up again; the number its tooltip shows was written when the item was made and stays.

slots changes the chests placed from then on, and chests upgraded to the item, since an upgrade places a new chest: a chest placed before keeps the size it was placed with, which the save holds. Removing the pack leaves every chest as it is. A container only grows, as an upgraded chest moves every item it holds; an item that places no container is a problem at its line. The change reaches every item that places the same container, and the log names them; when such items ask for different sizes, the last in load order counts and the log says so. The largest vanilla chest holds 72 slots.

recipes.toml changes vanilla recipes the same way:

[[change]]
recipes = ["Tool_Torch01_T0_PK"]
output_count = 3
[[change]]
output = ["Material_T2_Wood_Planks"]
cost = { times = 0.5 }
time = { times = 0.5 }
[[change]]
output = ["Tool_Torch01"]
ingredients = { Material_T1_Wood_Logs = 1, Material_T1_Resin = 0, Build_TerrainMaterial_T1_Stone = 2 }

recipes names vanilla recipes by GUID or by debug name. output names items and picks every vanilla recipe that makes one of them. A change gives at least one of the two. A recipe a mod or the pack adds gets its values where it is defined, so naming one here is a problem.

FieldMeaningValues
output_counthow many of each item the recipe makes1 to 65535
costhow many of each ingredient it takes1 to 65535
timehow long it takes, in seconds; 0 is at once0 to 86400
ingredientshow many of each named ingredient it takes: 0 takes it out, and one the recipe lacks is added0 to 65535 each
stationthe crafting station it is made at, named by the station’s itema vanilla station

cost applies first, then ingredients, so a named ingredient wins over the cost. A scaled count of 1 or more stays at least 1. An ingredient that takes any item of a kind keeps its count. Most workbench recipes take no time; production stations such as the kiln do. A changed time reaches a production that runs already from its next step on.

A change can also pick every vanilla recipe of some kinds, by what it makes, or every vanilla recipe made at some crafting stations, named by their items:

[[change]]
kind = ["building-block", "roof"]
cost = { times = 0.5 }
[[change]]
workshop = ["Prop_Production_T2_Kiln", "Prop_Production_T2_Smelter"]
time = { times = 0.5 }
SelectionPicks
kind = [...]every recipe whose item is of these kinds: building-block, roof, overgrowth, terrain, decorative, comfort-furniture or other
workshop = [...]every recipe made at these stations; naming one needs the game’s data at hand

A selection changes output_count, cost, time and station, never ingredients. A change gives one of recipes or output, kind and workshop. A recipe the pack also names in recipes or output keeps that change alone, and the pack’s log says how many recipes each selection reached.

station moves a recipe to another crafting station, such as charcoal from the kiln to the smelter:

[[change]]
recipes = ["Material_T1_Factory_03_Charcoal"]
station = "Prop_Production_T2_Smelter"

The player’s crafting menu then lists the recipe at the new station alone. A move takes effect when the game starts, also in a development reload, and needs the game’s data at hand to find the station. Two packs that move one recipe are a conflict like two sets: the later in load order wins.

The server crafts by its own recipes and each player’s crafting menu shows the player’s own, so every side runs the same pack, which the join check proves. Game rates multiply what a pack sets.

The loader takes every pack’s and mod’s changes of one value in load order. The last set wins; then every factor multiplies it. A whole number rounds half away from zero and stays in the field’s range, and a factor never takes it below the smallest value. Two factors never conflict, since their order does not matter. A set that overrides another mod’s set is a warning in both logs and in the conflict report, naming both values and both lines; when the later pack requires the other, it overrides it on purpose and the line is information:

warn [you.stacks] max_stack of 3f4093ae-75ed-4e5d-bd0b-784ba83b88f5 is 999 from you.stacks items.toml:2 (Material_T1_Wood_Logs), which overrides 500 from ann.bags items.toml:7 (Material_T1_Wood_Logs)

A Rust mod declares the same changes with Load::change, during init. With Load::change_items and Load::change_recipes it makes the selections a pack makes. The loader reads the selection from the game’s data and merges one change per item in the mod’s place in load order, as it does for a pack:

use enshwick::content::categories::ItemCategoryMask;
use enshwick::content::changes::{Field, Items, ItemsChange, Value};
fn init(load: &mut enshwick::Load<'_>) -> Result<(), enshwick::Off> {
let materials = Items::Category(ItemCategoryMask::MATERIALS);
load.change_items(&ItemsChange::new(materials, Field::MaxStackSize, Value::Times(4.0)), "stacks")
}

A stack size reaches only items that already stack and keep no state of their own, such as durability; weapons and armour stay at one. Overlapping selections of two mods report one conflict line per pair, with the count of items it covers.

Unstable: stack sizes and slots may change in any release; a mod changing them needs the unstable feature (Stability).

The game cuts every stack above an item’s stack size when a world loads and deletes the rest. So a world saved with a pack that raised a stack size depends on that pack. The first time such a world loads, the pack sets a hold on that world: if the world is opened again without the pack, the game stops before the world loads and the log says which pack and items. Other worlds open without the pack as usual.

error you.stacks holds this game (it lets Material_T1_Wood_Logs and 12 more items stack past the game's own size (20 to 999); ..., in world mygame-1a2b3c4d) and the mod does not run: the game stops before the world loads

To remove such a pack safely, split every stack larger than the game’s size first, then delete mod-data/<id>/worlds/<key>/hold.txt, the file the log names. When several packs raise one item, each of them holds. A pack that only lowers stack sizes sets no hold. Beside the holds, the loader keeps every raised size a world was saved with and never serves less, so a lower size never cuts a saved stack: see kept stack sizes.

[[workshop]]
id = "lantern-bench"
like = "workbench"
name = "lantern-bench.name"
recipe = { cost = 2 }

A station with a workshop of its own, as add_workshop makes it: the station item is <id>, its template <id>.template, its workshop <id>.workshop, and with recipe it is crafted like the vanilla station for cost times its ingredients. like names the vanilla station cloned: workbench, or any crafting station by its item’s debug name or GUID, such as Prop_Production_T1_Forge, Prop_Production_T2_Kiln or Prop_Production_T3_Alchemy_Station. The loader checks that the game lists a workshop of its own for the item, that it is no survivor’s, and that the template the item places has the crafting components; any other item is a problem at its line.

[[recipe]]
id = "lantern.recipe"
like = "6943053a-32c1-4718-ae22-e61427ddf54d"
output = "lantern"
count = 1
cost = 2
workshop = "lantern-bench"

A copy of the recipe that makes like, making count of output for cost times the copied ingredients, as add_recipe makes it. workshop names a workshop of the pack, another by GUID or content id, or a vanilla station’s by the station item’s debug name; the copied recipe’s when left out.

[[drop]]
items = ["lantern"]
chance = 0.05
from = "locked-chests"
count = [1, 2]
meter = [1, 3]

Each drop gives one of items at chance per roll, as add_loot adds it. from is bosses, locked-chests, corpses, chests, containers or destroyed, or a list of the last four. meter rolls the fill of items with a meter: every item of the drop must be one of the pack’s own with a meter of the same maximum, and the range must not pass that maximum.

loot.toml also changes how the game’s own loot gives items that exist already, with a [[change]] table:

[[change]]
items = ["Material_T1_Wood_Logs", "Material_T1_Resin"]
stack_size = { times = 2 }
count = { times = 2 }
FieldMeaningValues
stack_sizehow many of the item random loot gives in one stack, at least and at most alike1 to 65535
counthow many of the item a container’s fill table gives, at least and at most alike0 to 65535
spawn_probabilitythe chance a fill table gives the item0 to 1
weightthe item’s weight among the ones a fill table picks from0 to 1000000

Random loot fills world chests, corpses and animals by the item’s labels; fill tables are the fixed lists a container names. A change reaches every fill table that lists the item. Only the server rolls loot, so a pack of loot changes alone may run side = "server" and turn join off.

table narrows a change to the fill tables of some containers, named by their templates, and needs the game’s data at hand:

[[change]]
items = ["Material_T1_Resin"]
table = ["LootContainer_Enemy_Corpse_Vukah"]
count = { times = 2 }

Containers often share one table: the Vukah corpse and the Vukah warrior roll the same one, so the change above reaches both, and the change’s source in the conflict report and the logs says how many containers share it. A change of one table applies after the item’s changes of every table. stack_size belongs to random loot, which has no tables.

A pack changes the table itself, before any roll, and each factor after it applies once. For items the game scales, the world’s resource drop setting times the resource drop rate scales the pack’s least and largest stack, up to a ceiling that stack_size keeps in proportion; the loot rate then scales the rolled stack, held to the item’s stack size, which an items.toml change may have raised.

The pack’s texts, in the form of a mod’s texts file: a table per language, en the fallback. A server owner’s mod-configs/<id>.texts.toml overrides them as it does a mod’s.

The loader checks every file before the pack adds anything: unknown fields, names defined twice, references to names neither the pack nor the game has, stations that cannot be cloned, text keys without text, icons the pack lacks, chances outside 0 to 1, changes out of range or made twice, and join = false on a pack that players’ games read. Each problem is a line in the pack’s log with its file, line and column, and the pack stays off:

error [you.lanterns] items.toml:7:1: no item "torch" in this pack; name another mod's as <mod>:<name>, or a vanilla one by its GUID or, where the game's data is at hand, its debug name

cargo enshwick pack check mods/you.lanterns --game <game folder> gives the same lines before you ship, reading vanilla names from that game’s data (ENSHROUDED_DIR when --game is left out). Without a game folder it checks everything else, and a debug name or a station other than the workbench is a problem that asks for the game’s data. cargo enshwick pack schema writes the JSON Schema of every file but texts.toml; name one at the top of a file, #:schema ./items.schema.json, and an editor with TOML support completes and checks fields as you type.

The dedicated server watches a pack’s files. When they change it reads them again and logs any problem with its line; with none, it applies the new loot.toml at once: chances, counts and items of drops change for the next roll. Items, recipes, workshops, texts and changes of values are built into the game’s data at start, on the server and on every player’s client, so a change to them waits for the next start, and the log says so:

info [you.lanterns] data pack reloaded: 2 loot drops
warn [you.lanterns] data pack changed beyond its loot: changes of values change at the next start, or at once with dev_reload = true

Loot of a kind no pack or mod had at start, such as the first drop from destroyed objects, also waits for a restart.

With dev_reload = true in mod-configs/enshwick.toml (see reloading a mod), the server and the client both watch the packs’ files, and a change that only moves values applies at once: stack sizes, recipe counts and times, and chest slots. The loader merges every pack’s changes again, reports overridden values as at start, and builds each changed resource again in place:

info [you.stacks] data pack reloaded: 3 changes of values, 4 resources built again

Some changes still take a restart, and the log names them: the first change of an item or recipe that no change touched at start, and an ingredient taken out or added, which changes the recipe list’s length. Chest slots apply to chests placed after the reload. On a server, players who joined before the change keep the old values until they join again, so reload on a test server of your own.

A pack is released like a mod: an archive <component>.zip holding the pack’s files under mods/<id>/. cargo enshwick pack zip mods/you.lanterns --out lanterns.zip checks the pack and writes the archive; attach it to the release, as Releasing mods describes.

In the join check a pack’s version carries the hash of its files, lanterns@1.0.0+3f2a9c1e: a server and a player with other files of one version differ, and the player’s game stays out of the server.

A mod adds a pack it embeds with Load::add_pack; the names live in the mod’s namespace:

use enshwick::packs::{Pack, PackFiles};
// In a mod: include_str!("pack/items.toml"), and so on.
const ITEMS: &str = r#"
[[item]]
id = "lantern"
like = "6943053a-32c1-4718-ae22-e61427ddf54d"
name = "locker.name"
"#;
const TEXTS: &str = "[en]\n\"locker.name\" = \"Locker\"\n";
fn init(load: &mut enshwick::Load<'_>) -> Result<(), enshwick::Off> {
let files = PackFiles::new().with("items.toml", ITEMS).with("texts.toml", TEXTS);
load.add_pack(&Pack::parse("you.my-mod", &files)?)
}

Pack::parse returns every problem with its file and line, which ? turns into the mod’s off reason. A mod’s pack is not watched: change it and rebuild.