English
Event API
EmakiStation publishes three Bukkit events in the emaki.jiuwu.craft.station.api.event package
| Event | Cancellable | Fires | Thread |
|---|---|---|---|
StationCraftSubmitEvent | Yes | Before any charge or material deduction | See the note below (the API path does not guarantee the owner thread) |
StationCraftCancelEvent | Yes | Before the queue entry is removed and refunded | The queue owner's owner thread |
StationCraftCompletedEvent | No | After settlement completes and outputs are routed | The crafting player's owner thread |
The latter two always fire through the entity's owner dispatch: on Folia that is the entity scheduler owner, on Paper the main server thread
StationCraftSubmitEvent also fires on the owner thread for the GUI and command paths, but the API's submitAsync accepts any calling thread and dispatches the event directly on that calling thread, so listeners must not assume it always runs on the owner thread -- switch back to the owner yourself before touching player state
StationCraftSubmitEvent
java
public class StationCraftSubmitEvent extends Event implements Cancellable| Member | Type | Description |
|---|---|---|
getPlayer() | Player | The submitting player |
getStationId() | String | Station id |
getRecipeId() | String | Recipe id |
getBatch() | long | The multiplier of this submission |
getChannel() | MaterialChannel | The channel carried by the event. The bridge derives it from the station configuration: STORAGE when the station enables channels.storage and the warehouse channel is usable, otherwise BACKPACK. It does not report which channel this submission actually debits |
isCancelled() / setCancelled(boolean) | boolean | Cancellation control |
Cancelling stops the whole submission: nothing is charged, nothing is deducted, no queue entry is created. This is the only point where a craft can be vetoed at zero compensation
Coverage: any submission that passed pre-validation (station/recipe resolution, batch, recipe availability, permission, condition gate, queue capacity, pending-claim ceiling) and reached the charging stage, whether it came from the GUI, a command or StationOperations#submitAsync. The event fires before the material and balance checks, so submissions that later fail on insufficient materials or balance still fire it
It does not fire for submissions refused earlier by permissions, conditions, queue capacity or the pending-claim ceiling, and it does not fire when a queue entry later advances or settles
StationCraftCancelEvent
java
public class StationCraftCancelEvent extends Event implements Cancellable| Member | Type | Description |
|---|---|---|
getPlayer() | Player | The queue owner |
getStationId() | String | Station id |
getRecipeId() | String | Recipe id |
getIndex() | int | Queue position, zero-based |
getConsumedMaterials() | List<ConsumedMaterial> | The materials this entry consumed |
getRefundRate() | double | The refund fraction about to be applied |
isCancelled() / setCancelled(boolean) | boolean | Cancellation control |
Cancelling keeps the entry in the queue and refunds nothing, so the player's materials stay invested in that craft. A vetoed cancellation reports FailureKind.CANCELLED with the reason key station.cancel_vetoed
getRefundRate() comes from the station's queue.cancel_refund_rate
Coverage: cancellations a player triggers from the GUI or a command, plus StationOperations#cancelAsync. It does not fire when an entry completes normally, nor when queue data is discarded administratively
Entries in the PENDING_CLAIM state are refused before this event fires, returning station.cancel_pending_claim, so listeners never observe that kind of cancellation
StationCraftCompletedEvent
java
public class StationCraftCompletedEvent extends EventNot cancellable: by the time it fires the materials are long spent and the outputs are already delivered or parked, leaving nothing to veto
| Member | Type | Description |
|---|---|---|
getPlayer() | Player | The crafting player |
getStationId() | String | Station id |
getRecipeId() | String | Recipe id |
getBatch() | long | The multiplier settled |
getDeliveredOutputs() | List<PendingOutput> | Outputs delivered successfully |
getPendingOutputs() | List<PendingOutput> | Outputs that could not be delivered and became pending-claim |
Coverage: settlement of a queue entry, plus immediate settlement of a zero-duration recipe
A non-empty getPendingOutputs() means no configured target could accept the outputs, and they now await a manual claim. "Completed" therefore describes the craft itself, not successful delivery
This event does not fire when the player later claims parked outputs, and it is not an audit log: cancelled entries never reach it
Examples
java
@EventHandler(ignoreCancelled = true)
public void onSubmit(StationCraftSubmitEvent event) {
if (isBlacklisted(event.getRecipeId())) {
event.setCancelled(true);
}
}
@EventHandler
public void onCompleted(StationCraftCompletedEvent event) {
if (!event.getPendingOutputs().isEmpty()) {
event.getPlayer().sendMessage("Some outputs could not be delivered. Free up space and claim them.");
}
}Both event classes defensively copy the lists passed to their constructors, turning null into an empty list