English
Station Definitions
Crafting stations live under plugins/EmakiStation/stations/, one file per station. Keep one rule in mind: the file name matches id
Full Example
yaml
id: "blacksmith"
display_name: "<gold>Blacksmith Workshop</gold>"
layout: "station_catalog"
preview_layout: "station_preview"
queue_layout: "station_queue"
permission: ""
queue:
base_length: 3
max_length: 16
progress_mode: "offline"
cancel_refund_rate: 1.0
speed_multiplier: 1.0
allow_purchase: false
channels:
backpack: true
storage: true
default: "backpack"
output:
default: "storage_first"
player_switchable: true
condition:
invalid_as_failure: trueFields
| Field | Type | Required | Default | Accepted range | Description |
|---|---|---|---|---|---|
id | string | Yes | — | — | Station id, matching the file name. A missing id refuses the file (station.station_missing_id) |
display_name | string | No | the id | MiniMessage text | Display name used in window titles |
layout | string | No | the id | a gui/<name>.yml id | Recipe catalog layout. When omitted the station id is used as the layout id, which is why the example writes station_catalog |
preview_layout | string | No | "station_preview" | a gui/<name>.yml id | Material preview layout |
queue_layout | string | No | "station_queue" | a gui/<name>.yml id | Craft queue layout |
permission | string | No | "" | a permission node | Permission required to open this station. When empty, falls back to emakistation.access.<id> |
queue | section | No | inherited from config.yml queue | see below | Queue parameters; omitted keys inherit the main config value |
channels | section | No | both channels enabled | see below | Material source channels |
output | section | No | storage_first, switchable | see below | Output routing |
condition | section | No | empty | see below | Condition gate; the syntax matches other Emaki modules, see Conditions. It is parsed and stored, but no runtime path evaluates it, so it neither restricts opening nor refuses anything |
All three layout files must exist. If one is missing, the startup precheck reports missing_layout
The dismantle page does not belong to a station: dismantle stations are configured separately, see Dismantling
yaml
# Example: a new station following the example layouts with its own permission node
id: "alchemy"
display_name: "<light_purple>Alchemy Bench</light_purple>"
layout: "station_catalog"
preview_layout: "station_preview"
queue_layout: "station_queue"
permission: "emakistation.access.alchemy"Recipe Ownership
Station files have no recipes section. The recipe decides where it shows up: list the owning stations in station_ids inside recipes/<recipe>.yml. A recipe without station_ids is offered by every station
The queue Section
A station can override the same keys as the main config's queue section, plus its own allow_purchase key. tick_interval and purchase come only from the main config
| Field | Type | Required | Default | Accepted range | Description |
|---|---|---|---|---|---|
base_length | integer | No | inherited (3) | 1–64, clamped | This station's base queue length |
permission_tiers | boolean | No | inherited (true) | true, false | Whether permission tiers apply at this station |
max_length | integer | No | inherited (32) | base_length–64, clamped | This station's hard queue-length ceiling |
progress_mode | string | No | inherited ("offline") | offline, online | How this station's head is timed |
cancel_refund_rate | double | No | inherited (1.0) | 0.0–1.0, clamped | Refund rate for materials and paid currency on cancellation |
speed_multiplier | double | No | inherited (1.0) | 0 or below becomes 1.0; capped at 100.0 | This station's recipe duration multiplier |
allow_purchase | boolean | No | false | true, false | Whether this station sells extra queue slots. queue.purchase.enabled in config.yml must also be true |
When you cancel an entry, any currency already paid is refunded at cancel_refund_rate -- the same rate as materials
channels — Material Channels
| Field | Type | Required | Default | Accepted range | Description |
|---|---|---|---|---|---|
backpack | boolean | No | true | true, false | Whether to take materials from the player's inventory |
storage | boolean | No | true | true, false | Whether to take materials from the EmakiStorage warehouse; it also needs storage.enabled and the emakistorage:atomic_batch capability to be active |
default | string | No | "backpack" | backpack, storage | Retained key, no longer effective: the source reads the value but no window or debit path consumes it |
Turn both off and the station is refused at load time (station.station_no_channel) -- a station that opens but can never be used is worse than a startup error
With both on, materials come from one merged pool of inventory and warehouse, spent inventory-first. You will not see a channel toggle, and you do not need one: the "owned" figure in the window is already the combined total
default is retained only so old configuration files do not report an unknown key, and it may be removed in a later version. Never treat it as usable configuration
output — Output Routing
| Field | Type | Required | Default | Accepted range | Description |
|---|---|---|---|---|---|
default | string | No | "storage_first" | storage_first, backpack_first, storage_only, backpack_only | Default output routing; an unrecognised value falls back to storage_first |
player_switchable | boolean | No | true | true, false | Whether players may switch their own routing preference in the window. Once off, clicking output_toggle does nothing |
Here is what each default value does:
| Value | Behaviour |
|---|---|
storage_first | Warehouse first, falling back to the inventory when full |
backpack_first | Inventory first, falling back to the warehouse when full |
storage_only | Warehouse only |
backpack_only | Inventory only |
When every target is full, the entry enters the pending-claim state, and nothing is dropped or destroyed
condition Gate
The condition block uses CoreLib's generic condition block; its fields are described in Conditions
The station-level condition is not evaluated by the current runtime: the section is parsed, stored and structurally validated by the precheck, but opening a station (/emakistation open or the openGui API) never checks it, and an unsatisfied condition does not block anything. The keys below are listed so you can pre-stage configuration for the intended semantics
| Field | Type | Required | Default | Accepted range | Description |
|---|---|---|---|---|---|
condition.type | string | No | all_of | all_of, any_of, none_of, at_least, exactly | Combination mode; an unrecognised value falls back to all_of |
condition.entries | list | No | [] | string expressions, type: expression nodes, nested condition groups | Condition node list; an empty list makes the group always true |
condition.required_count | integer | No | 0 | non-negative integer | Number of conditions that must hold under at_least / exactly |
condition.invalid_as_failure | boolean | No | true | true, false | Whether an invalid or unevaluable condition counts as failure |
condition.on_pass.actions | list<string> | No | [] | action pipeline lines | Actions run when the condition passes. Not executed today |
condition.on_fail.actions | list<string> | No | [] | action pipeline lines | Actions run when the condition fails. Not executed today |
condition.on_fail.block_output | boolean | No | false | true, false | Whether a failed condition blocks output. A station produces nothing, so this has no effect |
condition.on_fail.message | string | No | "" | MiniMessage text | Message sent to the player on failure. Not sent today |