English
Configuration
plugins/EmakiStation/config.yml is the main configuration. It controls queue defaults, capacity ceilings, the EmakiStorage channel, persistence and GUI throttling
The version key is only a configuration-structure marker: the plugin uses it to merge newly added default keys. It tracks separately from the module release version, so you never need to edit it to match the plugin version
Shipped Defaults
This is what the config.yml released from the Jar contains:
yaml
version: "1.1.8"
language: "zh_CN"
release_default_data: true
queue:
base_length: 3
permission_tiers: true
max_length: 32
progress_mode: "offline"
cancel_refund_rate: 1.0
tick_interval: 20
speed_multiplier: 1.0
purchase:
enabled: true
cost_file: "queue_costs.yml"
limits:
max_pending_claim: 100
warn_material_types: 50
batch_multiplier_max: 0
storage:
enabled: true
batch_max_ops: 200
persistence:
autosave_interval_seconds: 60
save_on_submit: true
gui:
click_throttle_ms: 200
refresh_interval_ticks: 20Top-Level Keys
| Field | Type | Required | Default | Accepted range | Description |
|---|---|---|---|---|---|
version | string | No | "1.1.8" | — | Configuration-structure marker, maintained by default-resource sync. The internal migration constant is 1.0.2 and is not a release coordinate |
language | string | No | "zh_CN" | — | Language file name, resolving to lang/<language>.yml |
release_default_data | boolean | No | true | true, false | Whether to release example data on first start (the built-in default files under stations/, gui/, recipes/). Once false, existing files are not overwritten and missing examples are not restored |
queue — Queue Defaults
Each station can override these same keys in its own stations/<id>.yml (purchase is configured only here; a station has its own separate queue.allow_purchase switch)
| Field | Type | Required | Default | Accepted range | Description |
|---|---|---|---|---|---|
base_length | integer | No | 3 | 1–64, clamped | Base queue length. Only WAITING / RUNNING occupy length; PENDING_CLAIM does not |
permission_tiers | boolean | No | true | true, false | Whether emakistation.queue.<n> tiers apply; when enabled the player's highest tier wins, and with no node under that prefix the base length is kept |
max_length | integer | No | 32 | base_length–64, clamped | Hard queue-length ceiling that neither permission tiers nor purchases can exceed |
progress_mode | string | No | "offline" | offline, online | How the head is timed; an unrecognised value falls back to offline. offline follows real time, online only accumulates while the player is online |
cancel_refund_rate | double | No | 1.0 | 0.0–1.0, clamped | Fraction of materials refunded when cancelling an entry; materials and any paid currency use the same rate |
tick_interval | integer | No | 20 | 10–100, clamped | Interval of the queue advance task, in ticks. There is exactly one server-wide timer |
speed_multiplier | double | No | 1.0 | 0 or below becomes 1.0; capped at 100.0 | Global recipe duration multiplier applied on top of duration_seconds |
purchase.enabled | boolean | No | true | true, false | Server-wide master switch for paid queue slots; the station also needs queue.allow_purchase: true to sell slots |
purchase.cost_file | string | No | "queue_costs.yml" | —, blank falls back to the default name | Price tier file name, relative to the plugin data folder. When the file is missing or carries no usable price, purchases are refused |
Leaving pending_claim out of queue length is deliberate: otherwise a player whose warehouse and inventory are both full would be blocked by their own undelivered output
cancel_refund_rate is not prorated by progress. In a serial single-line queue only the head has progress, and per-entry progress accounting for an almost non-existent case is not worth it. This is an intentional simplification
progress_mode: offline only means "the timer keeps running", not "delivery while offline". EmakiStorage refuses any write for an offline player, so entries that come due while offline are merely "due"; settlement happens at the player's next login
If the price file is missing, empty or carries no usable price, purchases are refused rather than granted for free
yaml
# Example: shorten the advance interval to 10 ticks and raise the base length to 5
queue:
tick_interval: 10
base_length: 5limits — Capacity Ceilings
| Field | Type | Required | Default | Accepted range | Description |
|---|---|---|---|---|---|
max_pending_claim | integer | No | 100 | 1–10000, clamped | How many unclaimed outputs one player may accumulate (summed across all stations). New submissions are refused at the ceiling with station.pending_claim_full |
warn_material_types | integer | No | 50 | 1–10000, clamped | Above this many material kinds in one recipe only a station.recipe_many_materials warning is logged; loading is not blocked |
batch_multiplier_max | long | No | 0 | 0 or positive; negatives clamp to 0 | Maximum multiplier per submission; 0 means no extra limit. The current source only parses and stores this value — the submission path performs no check against it, so the real ceiling is still set by materials and balance |
The number of material kinds is not limited by GUI slots; warn_material_types is only a guard against typos
storage — Warehouse Channel
| Field | Type | Required | Default | Accepted range | Description |
|---|---|---|---|---|---|
enabled | boolean | No | true | true, false | Master switch. Once off, EmakiStation never calls EmakiStorage at all |
batch_max_ops | integer | No | 200 | 1–10000, clamped | Maximum operations per batch, matching EmakiStorage's behavior.batch_max_ops; exceeding it refuses the batch with station.storage_batch_too_large |
enabled: true alone is not enough: the warehouse channel also needs EmakiStorage to provide the emakistorage:atomic_batch capability (a batch operation that either does everything or nothing). Without that capability there is no fallback to withdrawing item by item: that would make materials flash through your inventory once, creating an exploitable window instead
When materials come from both the inventory and the warehouse, the plugin prefers the emakistorage:reservation capability: place a reservation in the warehouse (without actually withdrawing), spend from the inventory, then commit the reservation. If something fails midway, only the reservation needs to be released, and releasing cannot fail on warehouse capacity. On older EmakiStorage versions lacking that capability, it degrades to "withdraw from the warehouse first, deposit back on failure", where the deposit itself may be refused for lack of capacity -- hence inferior to the reservation path
yaml
# Example: supply everything from the inventory and never touch the warehouse
storage:
enabled: falsepersistence
| Field | Type | Required | Default | Accepted range | Description |
|---|---|---|---|---|---|
autosave_interval_seconds | integer | No | 60 | 5–3600, clamped | Autosave interval in seconds. The actual schedule runs every max(100 ticks, seconds × 20). The legacy key autosave_interval is still read, logs a warning at startup, and is migrated to the new key |
save_on_submit | boolean | No | true | true, false | Whether to flush immediately after a successful submission instead of waiting for the next autosave |
Entries in the queue file correspond to materials that have already been deducted, so this interval is shorter than the warehouse's autosave
The queue file and the warehouse file are two independent files, so a hard crash can still leave them inconsistent (materials deducted but the entry unsaved, or the entry saved but the deduction unflushed). Every entry records its full consumption list so you can reconcile manually. Closing this window entirely would need a cross-plugin two-phase commit, which costs too much
gui
| Field | Type | Required | Default | Accepted range | Description |
|---|---|---|---|---|---|
click_throttle_ms | integer | No | 200 | 0–5000, clamped | Minimum interval between two clicks in one session, in milliseconds; 0 disables throttling |
refresh_interval_ticks | long | No | 20 | 5–200, clamped | How often an open window repaints its progress display, in ticks; at runtime this is treated as at least 50 milliseconds. The legacy key refresh_interval is still read, logs a warning at startup, and is migrated to the new key |
yaml
# Example: loosen click throttling and repaint every 10 ticks
gui:
click_throttle_ms: 300
refresh_interval_ticks: 10