English
Multiblock Structures
A multiblock structure is a "contraption" that players build out of blocks. You write its shape and coordinates into a definition file at plugins/EmakiStructure/multiblocks/<id>.yml, where the file name is the structure id (a mismatch is rejected on load). Once a player assembles that shape and interacts with it (most often by right-clicking a block), the plugin checks the blocks one by one and, if they match, runs the actions you configured in trigger.actions (send a message, give experience, etc.)
Definition format
yaml
version: 1 # only 1 is supported: older reports version_outdated, newer version_unsupported
id: "teleporter" # must match the file name, otherwise rejected (id_mismatch)
shape:
allow_rotation: true # default true; whether rotation matching is allowed
core_block: [4, 1, 4] # optional: core block relative coordinate (trigger anchor)
blocks:
- pos: [4, 1, 4]
block: "minecraft:obsidian"
- pos: [4, 2, 4]
block: "minecraft:ender_eye" # states via minecraft:...[state=value]
- pos: [5, 1, 4]
block: "craftengine-myns:console" # custom block: source-rawId, states not allowed
- pos: [6, 1, 4]
furniture: "itemsadder:myplugin:chair" # furniture member; mutually exclusive with block
trigger:
targets: "any" # any (any member) / specific (positions below) / all (all members)
positions: # required when targets=specific
- [4, 1, 4]
actions: | # CoreLib action pipeline lines, run one per line
send_message "&aPortal activated!"
action giveexp amount=10
cooldown: # optional
global: "0" # shared across players for this structure; 0 = none, supports 30s/5m
per_player: "0" # per player per structure
condition: "permission=teleport.use" # optional trigger precondition (CoreLib condition syntax)When the plugin loads each file it runs the checks below; any failure rejects the file or skips that entry, and the console always reports the file and entry index:
- Every member declares exactly one of
blockorfurniture; coordinates are non-negative, inside the size, and unique furnitureprefixes are limited toitemsadder:/oraxen:/nexo:/craftengine:blockstarting withminecraft:may carry block states; any other prefix is treated as a custom block id and must not carry states- Invalid
targets,specificwithoutpositions, or no valid members (blocks_empty) reject the whole file - Mismatched
id, aversionother than 1, and parse failures also reject the file - On duplicate ids the later definition is not loaded (
duplicate_id)
Commands
/ems multiblock export <id> [force]: export the current selection as a layout; an existing id needsforce(already_exists)/ems multiblock place <id> [facing]: place manually (vanilla blocks / custom blocks / furniture), then run forming detection; up to 5 failing entries are reported/ems multiblock list: list definitions with instance statistics/ems multiblock validate <id>: validate the world shape once; a failure downgrades that instance to incomplete (verification_failed)
Trigger flow
- The player interaction hits the structure region (or an anchor candidate)
- Incremental shape detection decides whether the shape is assembled: only changed blocks are checked (no full-map scan), chunk changes are debounced (
detection.debounce), and cold-hit anchor inference is bounded bydetection.max_cold_checks - The
conditionis evaluated as a CoreLib condition group (the whole group must pass) and the cooldown is checked — taken from the definition's owncooldownnode, falling back toconfig.yml'scooldown.global/cooldown.per_player MultiblockTriggerEventis published synchronously (non-cancellable, on the thread that handled the interaction; on Folia, the region thread owning the clicked block)trigger.actionslines run through the CoreLib action pipeline
With retrigger_on_reform: false (default), a destroyed structure that is reassembled only becomes an active instance again — it does not re-fire, so players cannot farm rewards by rebuilding it over and over
Incremental detection settings
These keys all live in the detection section of Configuration: sweep_interval (low-frequency fallback sweep, 0 disables and must be ≥ 30s when enabled), instance_limit (per definition per player in-progress cap), instance_ttl (lifetime for unchanged instances), and sample_recheck (low-frequency sampling of active instances, off by default)