Station Details
EmakiCooking provides four world stations, each with its own interaction method, state persistence, and special mechanics.
Chopping Board
Workflow
- Player left-clicks the chopping board block while holding an ingredient to place it on the board (consumes 1)
- An
ItemDisplayentity spawns above the board to display the ingredient - Player left-clicks the board while holding a tool (items defined in
tool_sources) to cut - Each cut increments
cut_count, and the tool takestool_damagedurability loss - When
cut_count >= cuts_required, the recipe completes and results are distributed - Player can right-click the board to retrieve unfinished ingredients
Interactions
| Action | Condition | Effect |
|---|---|---|
| Left-click + Ingredient | Board is empty | Place ingredient on the board |
| Left-click + Tool | Board has ingredient | Cut once |
| Left-click + Empty hand | Board has ingredient | Retrieve ingredient |
| Right-click | Board has ingredient | Retrieve ingredient |
| Break block | Board has ingredient | Drop ingredient, clear state |
State File Format
Storage path: data/stations/{world}/{x}_{y}_{z}.yml
schema_version: 1
station_type: chopping_board
world: world
x: 100
y: 64
z: 200
input_item:
source: "minecraft-carrot"
display_entity:
uuid: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
timestamps:
last_interaction_ms: 1700000000000
chopping_board:
cut_count: 2Special Mechanics
cut_damage: Each cut has achance% probability of dealingvaluedamage to the player. Recipes can override the global setting viadamage_overrideinteraction_delay_ms: Minimum interval between two interactions, prevents rapid clickingspace_restriction: When enabled, the space above the board must be an air block to interacttool_sources: Only items in this list can be used as cutting toolsItemDisplay: A display entity spawns above the board when an ingredient is placed, and is removed on retrieval or completion
Wok
Workflow
- Confirm the wok has a heat source block below it (defined in
heat_levels) to determine heat level - Player left-clicks while holding ingredients to add them to the wok (added in order; identical ingredients added consecutively will merge quantities)
- Player left-clicks while holding a spatula (
spatula_sources) to stir. Each stir increments the globaltotal_stir_countand each ingredient'sstir_times - After stirring is complete, player left-clicks while holding a bowl (
need_bowl: true) or empty-handed (need_bowl: false) to serve - The system matches a recipe and determines the result branch based on stir count and
stir_rule
Interactions
| Action | Condition | Effect |
|---|---|---|
| Left-click + Ingredient | Has heat source | Add ingredient to wok |
| Left-click + Spatula | Has ingredients | Stir once |
| Right-click + Spatula | Has ingredients | View wok contents |
| Left-click + Bowl/Empty hand | Has ingredients and has been stirred | Serve (match recipe) |
| Left-click + Empty hand | Has ingredients | Retrieve last ingredient (may take scald damage) |
| Break block | Has ingredients | Drop all ingredients, clear state |
State File Format
schema_version: 1
station_type: wok
world: world
x: 100
y: 64
z: 200
wok:
total_stir_count: 5
ingredients:
- source: "minecraft-beef"
amount: 2
stir_times: 5
- source: "minecraft-carrot"
amount: 1
stir_times: 3
timestamps:
last_stir_time_ms: 1700000000000
stir_fried_time_ms: 1700000000000Special Mechanics
heat_levels: The block below the wok determines the heat level; recipes can require a specific heat levelstir_rule: Each ingredient can define a stir rule (e.g.,>=3); the system compares actual stir count per ingredienttimeout_ms: If no stir occurs within this time after the last stir, the result is burnt (overcooked)- Result branches:
success,undercooked,overcooked,invalid scald_damage: When retrieving ingredients bare-handed after stirring, the player takes scald damagefailure: Even if a recipe matches successfully, there is still achance% probability of producing the failure item specified byoutput_source
Grinder
Workflow
- Player left-clicks the grinder block while holding an ingredient; the ingredient is consumed and grinding begins
- The system starts a background timer (at
check_delay_ticksintervals) to continuously check grinding progress - Smoke particle effects appear above the block during grinding
- After
grind_time_secondsseconds, grinding completes and results are distributed - Other players cannot use the same grinder during grinding
Interactions
| Action | Condition | Effect |
|---|---|---|
| Left-click + Ingredient | Grinder is idle | Start grinding |
| Left-click + Ingredient | Grinder is busy | Prompt that grinding is in progress |
| Break block | Currently grinding | Drop input item, clear state |
State File Format
schema_version: 1
station_type: grinder
world: world
x: 100
y: 64
z: 200
input_item:
source: "minecraft-bone"
grinder:
recipe_id: "bone_meal"
start_time_ms: 1700000000000
player_uuid: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
player_name: "Steve"Special Mechanics
check_delay_ticks: Background timer check interval, default 20 ticks (1 second)- Background Ticker: The grinder uses a
BukkitTasktimer that automatically cancels when there are no active grinders to save performance - The initiating player is automatically notified upon grinding completion if they are online
Steamer
Workflow
- Confirm the steamer has a heat source block below it (
heat_sources) - Player sneaks and right-clicks the steamer to open the GUI, then places ingredients into GUI slots
- Player sneaks and left-clicks the heat source block below the steamer while holding fuel to add burn time
- Player sneaks and left-clicks the steamer while holding a water bucket or similar item to add moisture
- System ticks every second: burning + has moisture → produces steam → steam drives ingredient cooking progress
- When an ingredient's progress reaches
required_steam, it completes; the output replaces the original ingredient or drops
Interactions
| Action | Condition | Effect |
|---|---|---|
| Right-click steamer | Sneaking | Open steamer GUI |
| Left-click heat source + Fuel | Sneaking | Add fuel, extend burn time |
| Left-click steamer + Water bucket | Sneaking | Add moisture |
| Left-click steamer + Empty hand | Sneaking | View steamer status info |
| Break block | Has contents | Drop all items, clear state |
State File Format
schema_version: 1
station_type: steamer
world: world
x: 100
y: 64
z: 200
steamer:
burning_until_ms: 1700000060000
moisture: 50
steam: 30
player_uuid: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
player_name: "Steve"
gui_slots:
- index: 0
source: "minecraft-cod"
item: { ... } # Serialized ItemStack data
- index: 1
source: "minecraft-salmon"
slot_progress:
- index: 0
progress: 80
- index: 1
progress: 20Special Mechanics
fuelsystem: Fuel provides burn time (burning_until_ms); while burning, the heat source block is ignited (campfire lit / furnace burning)moisturesystem: Moisture is the raw material for steam; each tick consumessteam_production_efficiencyunits of moisture to convert into steamsteamsystem: Steam drives cooking; each tick has a base consumption ofsteam_consumption_efficiency, plus each ingredient consumes an additionalsteam_conversion_efficiencyreset_progress_when_steam_empty: Whether to reset all ingredient cooking progress when steam is depleted- GUI: Uses Bukkit native containers (default HOPPER type), configurable as CHEST with custom slot count
- Dirty state flushing: The steamer uses an in-memory cache + periodic flush mechanism to avoid frequent IO
CookingBlockMatcher
CookingBlockMatcher is responsible for determining whether a block in the world matches a station's configured block_source.
Matching Logic
| Source Type | Matching Method |
|---|---|
VANILLA | Compares the block's Material with the vanilla material parsed from block_source |
CRAFTENGINE | Delegates to CraftEngineBlockBridge for custom block matching |
ITEMSADDER | Delegates to ItemsAdderBlockBridge for custom block matching |
NEXO | Delegates to NexoBlockBridge for custom block matching |
# Vanilla block example
block_source: "minecraft-oak_slab"
# CraftEngine custom block example
block_source: "craftengine-my_namespace:my_block"
# ItemsAdder custom block example
block_source: "itemsadder-my_namespace:my_block"
# Nexo custom block example
block_source: "nexo-my_block_id"Note
Custom block types (CRAFTENGINE, ITEMSADDER, NEXO) always return false when the corresponding plugin is not installed. Make sure the required plugin is properly installed when using custom blocks for stations.
CookingRewardService
CookingRewardService handles result distribution for all stations uniformly.
Distribution Modes
| Mode | Condition | Behavior |
|---|---|---|
| Drop | drop_result: true or player offline | Naturally drops items above the station |
| Give | drop_result: false and player online | Directly placed in inventory; drops if full |
Output Fields
| Field | Type | Description |
|---|---|---|
source | string | Item source identifier |
amount | int | Fixed quantity (default 1) |
amount_range | object | Random quantity range {min, max} |
chance | int | Output probability 0-100 (default 100) |
actions | list | Action list executed on output |
Tip
amount and amount_range are mutually exclusive; when both are present, amount_range takes priority. chance is a percentage where 100 means guaranteed output.
LegacyImportService
LegacyImportService provides configuration migration capabilities from the legacy JiuWu's Kitchen to EmakiCooking 2.0.
Two Modes
| Mode | Command | Behavior |
|---|---|---|
dryrun | /ecooking convert old dryrun | Parses legacy config, generates import report, writes no files |
apply | /ecooking convert old apply | Backs up current resources → writes converted recipes/station states/config updates → reloads |
Import Flow
- Read legacy
Config.yml,Recipe/, andData.ymlfrom theold/directory - Convert recipes: Chopping board, wok, grinder, and steamer recipes are converted to the new format respectively
- Convert station states: Convert legacy coordinate format to new YAML files
- Convert configuration: Merge legacy station settings into the new
config.yml - Convert display adjustments: Convert legacy item display settings to
item_adjustments/files - Generate import report at
data/legacy-import-report.yml
Import Report
The report contains the following statistics:
converted_recipe_count— Number of successfully converted recipesconverted_station_state_count— Number of successfully converted station statesskipped_count— Number of skipped entriesconflict_count— Number of conflicts (e.g., duplicate IDs)unknown_source_count— Number of unrecognizable item sourcesaction_context_issue_count— Number of uncertain command execution contexts
Note
The apply mode automatically backs up current resources to backup/pre-import-{timestamp}/ before writing. It is recommended to run dryrun first to check the report, and only execute apply after confirming everything is correct.