hytale-events-api

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Hytale Events API

Hytale 事件API

Complete guide for handling server events and creating custom event systems.
处理服务器事件与创建自定义事件系统的完整指南。

When to use this skill

何时使用本技能

Use this skill when:
  • Listening for player actions (connect, chat, interact)
  • Reacting to world changes (blocks, chunks)
  • Handling entity events (damage, death, spawn)
  • Creating custom events
  • Managing event priorities and cancellation
  • Implementing async event handlers
在以下场景使用本技能:
  • 监听玩家操作(连接、聊天、交互)
  • 响应世界变更(方块、区块)
  • 处理实体事件(受伤、死亡、生成)
  • 创建自定义事件
  • 管理事件优先级与取消机制
  • 实现异步事件处理器

Event System Architecture

事件系统架构

Hytale has two event systems:
  1. General Event Bus - Server-wide events (players, worlds, assets)
  2. ECS Event System - Entity-specific events (damage, interactions)
Hytale拥有两套事件系统:
  1. 通用事件总线 - 服务器全局事件(玩家、世界、资源)
  2. ECS事件系统 - 实体专属事件(受伤、交互)

General Event Bus

通用事件总线

Event Registration

事件注册

Register in plugin
setup()
:
java
@Override
protected void setup() {
    // Global listener - receives ALL events of this type
    getEventRegistry().registerGlobal(
        PlayerConnectEvent.class, 
        this::onPlayerConnect
    );
    
    // Keyed listener - receives events with specific key
    getEventRegistry().register(
        AddPlayerToWorldEvent.class, 
        "world_name", 
        this::onPlayerAddToWorld
    );
    
    // Priority listener
    getEventRegistry().registerGlobal(
        EventPriority.FIRST, 
        SomeEvent.class, 
        this::onSomeEvent
    );
    
    // Unhandled listener - only if no keyed handler processed
    getEventRegistry().registerUnhandled(
        SomeEvent.class, 
        this::onUnhandledEvent
    );
}
在插件的
setup()
方法中注册:
java
@Override
protected void setup() {
    // 全局监听器 - 接收该类型的所有事件
    getEventRegistry().registerGlobal(
        PlayerConnectEvent.class, 
        this::onPlayerConnect
    );
    
    // 键值监听器 - 接收带有指定键的事件
    getEventRegistry().register(
        AddPlayerToWorldEvent.class, 
        "world_name", 
        this::onPlayerAddToWorld
    );
    
    // 优先级监听器
    getEventRegistry().registerGlobal(
        EventPriority.FIRST, 
        SomeEvent.class, 
        this::onSomeEvent
    );
    
    // 未处理事件监听器 - 仅当没有键值处理器处理时触发
    getEventRegistry().registerUnhandled(
        SomeEvent.class, 
        this::onUnhandledEvent
    );
}

Event Priorities

事件优先级

java
public enum EventPriority {
    FIRST((short)-21844),   // Runs first
    EARLY((short)-10922),
    NORMAL((short)0),       // Default
    LATE((short)10922),
    LAST((short)21844);     // Runs last
}
Handlers execute in priority order. Use custom short values for fine-grained control.
java
public enum EventPriority {
    FIRST((short)-21844),   // 最先执行
    EARLY((short)-10922),
    NORMAL((short)0),       // 默认优先级
    LATE((short)10922),
    LAST((short)21844);     // 最后执行
}
处理器按优先级顺序执行。可使用自定义短整型值实现细粒度控制。

Event Cancellation

事件取消

For cancellable events:
java
private void onPlayerInteract(PlayerInteractEvent event) {
    if (shouldBlock(event)) {
        event.setCancelled(true);
    }
}
Check cancellation:
java
private void onEvent(SomeEvent event) {
    if (event instanceof ICancellable cancellable && cancellable.isCancelled()) {
        return; // Already cancelled by earlier handler
    }
    // Process event
}
对于可取消的事件:
java
private void onPlayerInteract(PlayerInteractEvent event) {
    if (shouldBlock(event)) {
        event.setCancelled(true);
    }
}
检查事件是否已取消:
java
private void onEvent(SomeEvent event) {
    if (event instanceof ICancellable cancellable && cancellable.isCancelled()) {
        return; // 已被更早的处理器取消
    }
    // 处理事件
}

Player Events

玩家事件

Connection Events

连接事件

java
// Player connecting (before entering world)
getEventRegistry().registerGlobal(PlayerConnectEvent.class, event -> {
    Player player = event.getPlayer();
    getLogger().atInfo().log("Player connecting: %s", player.getName());
});

// Player setup connect (cancellable)
getEventRegistry().registerGlobal(PlayerSetupConnectEvent.class, event -> {
    if (isBanned(event.getPlayer())) {
        event.setCancelled(true);
        event.setDisconnectReason("You are banned!");
    }
});

// Player disconnecting
getEventRegistry().registerGlobal(PlayerDisconnectEvent.class, event -> {
    Player player = event.getPlayer();
    savePlayerData(player);
});

// Player added to world
getEventRegistry().register(AddPlayerToWorldEvent.class, "main", event -> {
    event.getPlayer().sendMessage("Welcome to the main world!");
});

// Player ready (fully loaded)
getEventRegistry().registerGlobal(PlayerReadyEvent.class, event -> {
    Player player = event.getPlayer();
    showWelcomeScreen(player);
});
java
// 玩家连接中(进入世界前)
getEventRegistry().registerGlobal(PlayerConnectEvent.class, event -> {
    Player player = event.getPlayer();
    getLogger().atInfo().log("Player connecting: %s", player.getName());
});

// 玩家初始化连接(可取消)
getEventRegistry().registerGlobal(PlayerSetupConnectEvent.class, event -> {
    if (isBanned(event.getPlayer())) {
        event.setCancelled(true);
        event.setDisconnectReason("You are banned!");
    }
});

// 玩家断开连接
getEventRegistry().registerGlobal(PlayerDisconnectEvent.class, event -> {
    Player player = event.getPlayer();
    savePlayerData(player);
});

// 玩家加入世界
getEventRegistry().register(AddPlayerToWorldEvent.class, "main", event -> {
    event.getPlayer().sendMessage("Welcome to the main world!");
});

// 玩家加载完成(完全就绪)
getEventRegistry().registerGlobal(PlayerReadyEvent.class, event -> {
    Player player = event.getPlayer();
    showWelcomeScreen(player);
});

Chat Event

聊天事件

java
// Async event - returns CompletableFuture
getEventRegistry().registerAsyncGlobal(
    PlayerChatEvent.class,
    future -> future.thenApply(event -> {
        String message = event.getMessage();
        
        // Modify message
        event.setMessage("[Custom] " + message);
        
        // Or cancel
        if (containsBadWords(message)) {
            event.setCancelled(true);
        }
        
        return event;
    })
);
java
// 异步事件 - 返回CompletableFuture
getEventRegistry().registerAsyncGlobal(
    PlayerChatEvent.class,
    future -> future.thenApply(event -> {
        String message = event.getMessage();
        
        // 修改消息
        event.setMessage("[Custom] " + message);
        
        // 或取消事件
        if (containsBadWords(message)) {
            event.setCancelled(true);
        }
        
        return event;
    })
);

Interaction Events

交互事件

java
getEventRegistry().registerGlobal(PlayerInteractEvent.class, event -> {
    Player player = event.getPlayer();
    InteractionType type = event.getInteractionType();
    
    switch (type) {
        case USE -> handleUse(event);
        case ATTACK -> handleAttack(event);
        case LOOK -> handleLook(event);
    }
});
java
getEventRegistry().registerGlobal(PlayerInteractEvent.class, event -> {
    Player player = event.getPlayer();
    InteractionType type = event.getInteractionType();
    
    switch (type) {
        case USE -> handleUse(event);
        case ATTACK -> handleAttack(event);
        case LOOK -> handleLook(event);
    }
});

World Events

世界事件

World Lifecycle

世界生命周期

java
// World being added (cancellable)
getEventRegistry().register(AddWorldEvent.class, "new_world", event -> {
    World world = event.getWorld();
    initializeWorld(world);
});

// World being removed (cancellable except EXCEPTIONAL)
getEventRegistry().register(RemoveWorldEvent.class, "old_world", event -> {
    if (event.getReason() != RemoveWorldEvent.Reason.EXCEPTIONAL) {
        saveWorldData(event.getWorld());
    }
});

// World started
getEventRegistry().register(StartWorldEvent.class, "main", event -> {
    spawnInitialEntities(event.getWorld());
});

// All worlds loaded
getEventRegistry().registerGlobal(AllWorldsLoadedEvent.class, event -> {
    getLogger().atInfo().log("All worlds ready!");
});
java
// 世界即将被添加(可取消)
getEventRegistry().register(AddWorldEvent.class, "new_world", event -> {
    World world = event.getWorld();
    initializeWorld(world);
});

// 世界即将被移除(除EXCEPTIONAL外均可取消)
getEventRegistry().register(RemoveWorldEvent.class, "old_world", event -> {
    if (event.getReason() != RemoveWorldEvent.Reason.EXCEPTIONAL) {
        saveWorldData(event.getWorld());
    }
});

// 世界启动完成
getEventRegistry().register(StartWorldEvent.class, "main", event -> {
    spawnInitialEntities(event.getWorld());
});

// 所有世界加载完成
getEventRegistry().registerGlobal(AllWorldsLoadedEvent.class, event -> {
    getLogger().atInfo().log("All worlds ready!");
});

Chunk Events

区块事件

java
// Chunk pre-load processing
getEventRegistry().registerGlobal(
    EventPriority.FIRST, 
    ChunkPreLoadProcessEvent.class, 
    event -> {
        // Modify chunk before it's fully loaded
        Chunk chunk = event.getChunk();
    }
);
java
// 区块预加载处理
getEventRegistry().registerGlobal(
    EventPriority.FIRST, 
    ChunkPreLoadProcessEvent.class, 
    event -> {
        // 在区块完全加载前修改
        Chunk chunk = event.getChunk();
    }
);

Entity Events

实体事件

Entity Removal

实体移除

java
getEventRegistry().register(EntityRemoveEvent.class, "world_name", event -> {
    Entity entity = event.getEntity();
    if (entity instanceof NPCEntity npc) {
        logNPCRemoval(npc);
    }
});
java
getEventRegistry().register(EntityRemoveEvent.class, "world_name", event -> {
    Entity entity = event.getEntity();
    if (entity instanceof NPCEntity npc) {
        logNPCRemoval(npc);
    }
});

Inventory Events

物品栏事件

java
getEventRegistry().register(
    LivingEntityInventoryChangeEvent.class, 
    "world_name", 
    event -> {
        LivingEntity entity = event.getEntity();
        ItemStack oldItem = event.getOldItem();
        ItemStack newItem = event.getNewItem();
        int slot = event.getSlot();
    }
);
java
getEventRegistry().register(
    LivingEntityInventoryChangeEvent.class, 
    "world_name", 
    event -> {
        LivingEntity entity = event.getEntity();
        ItemStack oldItem = event.getOldItem();
        ItemStack newItem = event.getNewItem();
        int slot = event.getSlot();
    }
);

ECS Events

ECS事件系统

For entity-specific events, use the ECS event system.
对于实体专属事件,使用ECS事件系统。

Block Events

方块事件

java
@Override
protected void setup() {
    // Register event systems
    getEntityStoreRegistry().registerSystem(new BreakBlockHandler());
    getEntityStoreRegistry().registerSystem(new PlaceBlockHandler());
    getEntityStoreRegistry().registerSystem(new UseBlockHandler());
}

public class BreakBlockHandler extends EntityEventSystem<EntityStore, BreakBlockEvent> {
    public BreakBlockHandler() {
        super(BreakBlockEvent.class);
    }
    
    @Override
    public void handle(
        int index,
        ArchetypeChunk<EntityStore> chunk,
        Store<EntityStore> store,
        CommandBuffer<EntityStore> buffer,
        BreakBlockEvent event
    ) {
        BlockType blockType = event.getBlockType();
        Vector3i position = event.getPosition();
        Player player = event.getPlayer();
        
        // Cancel if protected
        if (isProtected(position)) {
            event.setCancelled(true);
            player.sendMessage("This area is protected!");
            return;
        }
        
        // Custom drops
        if (blockType.getId().equals("MyPlugin:CustomOre")) {
            event.setDrops(createCustomDrops());
        }
    }
}
java
@Override
protected void setup() {
    // 注册事件系统
    getEntityStoreRegistry().registerSystem(new BreakBlockHandler());
    getEntityStoreRegistry().registerSystem(new PlaceBlockHandler());
    getEntityStoreRegistry().registerSystem(new UseBlockHandler());
}

public class BreakBlockHandler extends EntityEventSystem<EntityStore, BreakBlockEvent> {
    public BreakBlockHandler() {
        super(BreakBlockEvent.class);
    }
    
    @Override
    public void handle(
        int index,
        ArchetypeChunk<EntityStore> chunk,
        Store<EntityStore> store,
        CommandBuffer<EntityStore> buffer,
        BreakBlockEvent event
    ) {
        BlockType blockType = event.getBlockType();
        Vector3i position = event.getPosition();
        Player player = event.getPlayer();
        
        // 若区域受保护则取消事件
        if (isProtected(position)) {
            event.setCancelled(true);
            player.sendMessage("This area is protected!");
            return;
        }
        
        // 自定义掉落物
        if (blockType.getId().equals("MyPlugin:CustomOre")) {
            event.setDrops(createCustomDrops());
        }
    }
}

Place Block Event

放置方块事件

java
public class PlaceBlockHandler extends EntityEventSystem<EntityStore, PlaceBlockEvent> {
    public PlaceBlockHandler() {
        super(PlaceBlockEvent.class);
    }
    
    @Override
    public void handle(..., PlaceBlockEvent event) {
        BlockType blockType = event.getBlockType();
        Vector3i position = event.getPosition();
        
        // Prevent placing in certain areas
        if (isRestrictedArea(position)) {
            event.setCancelled(true);
        }
    }
}
java
public class PlaceBlockHandler extends EntityEventSystem<EntityStore, PlaceBlockEvent> {
    public PlaceBlockHandler() {
        super(PlaceBlockEvent.class);
    }
    
    @Override
    public void handle(..., PlaceBlockEvent event) {
        BlockType blockType = event.getBlockType();
        Vector3i position = event.getPosition();
        
        // 禁止在特定区域放置方块
        if (isRestrictedArea(position)) {
            event.setCancelled(true);
        }
    }
}

Use Block Event

使用方块事件

java
public class UseBlockHandler extends EntityEventSystem<EntityStore, UseBlockEvent.Pre> {
    public UseBlockHandler() {
        super(UseBlockEvent.Pre.class);
    }
    
    @Override
    public void handle(..., UseBlockEvent.Pre event) {
        BlockType blockType = event.getBlockType();
        Player player = event.getPlayer();
        
        // Custom interaction
        if (blockType.getId().equals("MyPlugin:TeleportBlock")) {
            teleportPlayer(player);
            event.setCancelled(true); // Prevent default behavior
        }
    }
}
java
public class UseBlockHandler extends EntityEventSystem<EntityStore, UseBlockEvent.Pre> {
    public UseBlockHandler() {
        super(UseBlockEvent.Pre.class);
    }
    
    @Override
    public void handle(..., UseBlockEvent.Pre event) {
        BlockType blockType = event.getBlockType();
        Player player = event.getPlayer();
        
        // 自定义交互逻辑
        if (blockType.getId().equals("MyPlugin:TeleportBlock")) {
            teleportPlayer(player);
            event.setCancelled(true); // 阻止默认行为
        }
    }
}

Damage Event

受伤事件

java
public class DamageHandler extends EntityEventSystem<EntityStore, Damage> {
    private ComponentAccess<EntityStore, TransformComponent> transforms;
    
    public DamageHandler() {
        super(Damage.class);
    }
    
    @Override
    protected void register(Store<EntityStore> store) {
        transforms = registerComponent(TransformComponent.class);
    }
    
    @Override
    public void handle(..., Damage event) {
        float amount = event.getAmount();
        DamageCause cause = event.getCause();
        Entity source = event.getSource();
        
        // Modify damage
        if (isInSafeZone(transforms.get(chunk, index).getPosition())) {
            event.setAmount(0);
            event.setCancelled(true);
        }
        
        // Increase damage for critical
        if (event.isCritical()) {
            event.setAmount(amount * 1.5f);
        }
    }
}
java
public class DamageHandler extends EntityEventSystem<EntityStore, Damage> {
    private ComponentAccess<EntityStore, TransformComponent> transforms;
    
    public DamageHandler() {
        super(Damage.class);
    }
    
    @Override
    protected void register(Store<EntityStore> store) {
        transforms = registerComponent(TransformComponent.class);
    }
    
    @Override
    public void handle(..., Damage event) {
        float amount = event.getAmount();
        DamageCause cause = event.getCause();
        Entity source = event.getSource();
        
        // 修改伤害值
        if (isInSafeZone(transforms.get(chunk, index).getPosition())) {
            event.setAmount(0);
            event.setCancelled(true);
        }
        
        // 暴击时提升伤害
        if (event.isCritical()) {
            event.setAmount(amount * 1.5f);
        }
    }
}

Item Events

物品事件

java
// Drop item
public class DropHandler extends EntityEventSystem<EntityStore, DropItemEvent> {
    public DropHandler() {
        super(DropItemEvent.class);
    }
    
    @Override
    public void handle(..., DropItemEvent event) {
        ItemStack item = event.getItemStack();
        
        if (isSoulbound(item)) {
            event.setCancelled(true);
        }
    }
}

// Pickup item
public class PickupHandler extends EntityEventSystem<EntityStore, InteractivelyPickupItemEvent> {
    public PickupHandler() {
        super(InteractivelyPickupItemEvent.class);
    }
    
    @Override
    public void handle(..., InteractivelyPickupItemEvent event) {
        ItemStack item = event.getItemStack();
        Entity entity = event.getEntity();
        
        if (!canPickup(entity, item)) {
            event.setCancelled(true);
        }
    }
}
java
// 掉落物品
public class DropHandler extends EntityEventSystem<EntityStore, DropItemEvent> {
    public DropHandler() {
        super(DropItemEvent.class);
    }
    
    @Override
    public void handle(..., DropItemEvent event) {
        ItemStack item = event.getItemStack();
        
        if (isSoulbound(item)) {
            event.setCancelled(true);
        }
    }
}

// 拾取物品
public class PickupHandler extends EntityEventSystem<EntityStore, InteractivelyPickupItemEvent> {
    public PickupHandler() {
        super(InteractivelyPickupItemEvent.class);
    }
    
    @Override
    public void handle(..., InteractivelyPickupItemEvent event) {
        ItemStack item = event.getItemStack();
        Entity entity = event.getEntity();
        
        if (!canPickup(entity, item)) {
            event.setCancelled(true);
        }
    }
}

Craft Event

合成事件

java
public class CraftHandler extends EntityEventSystem<EntityStore, CraftRecipeEvent> {
    public CraftHandler() {
        super(CraftRecipeEvent.class);
    }
    
    @Override
    public void handle(..., CraftRecipeEvent event) {
        CraftingRecipe recipe = event.getRecipe();
        Player player = event.getPlayer();
        
        // Check permissions
        if (!hasRecipeUnlocked(player, recipe)) {
            event.setCancelled(true);
            player.sendMessage("Recipe not unlocked!");
        }
    }
}
java
public class CraftHandler extends EntityEventSystem<EntityStore, CraftRecipeEvent> {
    public CraftHandler() {
        super(CraftRecipeEvent.class);
    }
    
    @Override
    public void handle(..., CraftRecipeEvent event) {
        CraftingRecipe recipe = event.getRecipe();
        Player player = event.getPlayer();
        
        // 检查权限
        if (!hasRecipeUnlocked(player, recipe)) {
            event.setCancelled(true);
            player.sendMessage("Recipe not unlocked!");
        }
    }
}

Asset Events

资源事件

Asset Loading

资源加载

java
getEventRegistry().register(
    LoadedAssetsEvent.class, 
    BlockType.class, 
    event -> {
        for (BlockType block : event.getLoadedAssets()) {
            processBlockType(block);
        }
    }
);

getEventRegistry().register(
    LoadedAssetsEvent.class, 
    Item.class, 
    event -> {
        for (Item item : event.getLoadedAssets()) {
            processItem(item);
        }
    }
);
java
getEventRegistry().register(
    LoadedAssetsEvent.class, 
    BlockType.class, 
    event -> {
        for (BlockType block : event.getLoadedAssets()) {
            processBlockType(block);
        }
    }
);

getEventRegistry().register(
    LoadedAssetsEvent.class, 
    Item.class, 
    event -> {
        for (Item item : event.getLoadedAssets()) {
            processItem(item);
        }
    }
);

Asset Removal

资源移除

java
getEventRegistry().register(
    RemovedAssetsEvent.class, 
    BlockType.class, 
    event -> {
        for (String removedId : event.getRemovedIds()) {
            cleanupBlockType(removedId);
        }
    }
);
java
getEventRegistry().register(
    RemovedAssetsEvent.class, 
    BlockType.class, 
    event -> {
        for (String removedId : event.getRemovedIds()) {
            cleanupBlockType(removedId);
        }
    }
);

Asset Pack Events

资源包事件

java
getEventRegistry().registerGlobal(AssetPackRegisterEvent.class, event -> {
    AssetPack pack = event.getAssetPack();
    getLogger().atInfo().log("Asset pack registered: %s", pack.getName());
});

getEventRegistry().registerGlobal(AssetPackUnregisterEvent.class, event -> {
    AssetPack pack = event.getAssetPack();
    getLogger().atInfo().log("Asset pack unregistered: %s", pack.getName());
});
java
getEventRegistry().registerGlobal(AssetPackRegisterEvent.class, event -> {
    AssetPack pack = event.getAssetPack();
    getLogger().atInfo().log("Asset pack registered: %s", pack.getName());
});

getEventRegistry().registerGlobal(AssetPackUnregisterEvent.class, event -> {
    AssetPack pack = event.getAssetPack();
    getLogger().atInfo().log("Asset pack unregistered: %s", pack.getName());
});

Plugin Events

插件事件

java
// When another plugin completes setup
getEventRegistry().register(
    PluginSetupEvent.class, 
    OtherPlugin.class, 
    event -> {
        OtherPlugin plugin = (OtherPlugin) event.getPlugin();
        integrateWith(plugin);
    }
);
java
// 其他插件完成初始化时
getEventRegistry().register(
    PluginSetupEvent.class, 
    OtherPlugin.class, 
    event -> {
        OtherPlugin plugin = (OtherPlugin) event.getPlugin();
        integrateWith(plugin);
    }
);

Creating Custom Events

创建自定义事件

Simple Event

简单事件

java
public class MyCustomEvent implements IEvent<Void> {
    private final Player player;
    private final String data;
    
    public MyCustomEvent(Player player, String data) {
        this.player = player;
        this.data = data;
    }
    
    public Player getPlayer() { return player; }
    public String getData() { return data; }
}

// Dispatch the event
HytaleServer.get().getEventBus()
    .dispatchFor(MyCustomEvent.class)
    .dispatch(new MyCustomEvent(player, "some data"));
java
public class MyCustomEvent implements IEvent<Void> {
    private final Player player;
    private final String data;
    
    public MyCustomEvent(Player player, String data) {
        this.player = player;
        this.data = data;
    }
    
    public Player getPlayer() { return player; }
    public String getData() { return data; }
}

// 分发事件
HytaleServer.get().getEventBus()
    .dispatchFor(MyCustomEvent.class)
    .dispatch(new MyCustomEvent(player, "some data"));

Keyed Event

带键事件

java
public class MyWorldEvent implements IEvent<String> {
    private final World world;
    
    public MyWorldEvent(World world) {
        this.world = world;
    }
    
    public World getWorld() { return world; }
}

// Dispatch with key
HytaleServer.get().getEventBus()
    .dispatchFor(MyWorldEvent.class, world.getName())
    .dispatch(new MyWorldEvent(world));
java
public class MyWorldEvent implements IEvent<String> {
    private final World world;
    
    public MyWorldEvent(World world) {
        this.world = world;
    }
    
    public World getWorld() { return world; }
}

// 带键分发事件
HytaleServer.get().getEventBus()
    .dispatchFor(MyWorldEvent.class, world.getName())
    .dispatch(new MyWorldEvent(world));

Cancellable Event

可取消事件

java
public class MyCancellableEvent implements IEvent<Void>, ICancellable {
    private boolean cancelled = false;
    private final Player player;
    
    public MyCancellableEvent(Player player) {
        this.player = player;
    }
    
    public Player getPlayer() { return player; }
    
    @Override
    public boolean isCancelled() { return cancelled; }
    
    @Override
    public void setCancelled(boolean cancelled) { 
        this.cancelled = cancelled; 
    }
}

// Dispatch and check
MyCancellableEvent event = HytaleServer.get().getEventBus()
    .dispatchFor(MyCancellableEvent.class)
    .dispatch(new MyCancellableEvent(player));

if (!event.isCancelled()) {
    // Proceed with action
}
java
public class MyCancellableEvent implements IEvent<Void>, ICancellable {
    private boolean cancelled = false;
    private final Player player;
    
    public MyCancellableEvent(Player player) {
        this.player = player;
    }
    
    public Player getPlayer() { return player; }
    
    @Override
    public boolean isCancelled() { return cancelled; }
    
    @Override
    public void setCancelled(boolean cancelled) { 
        this.cancelled = cancelled; 
    }
}

// 分发并检查状态
MyCancellableEvent event = HytaleServer.get().getEventBus()
    .dispatchFor(MyCancellableEvent.class)
    .dispatch(new MyCancellableEvent(player));

if (!event.isCancelled()) {
    // 执行后续操作
}

Async Event

异步事件

java
public class MyAsyncEvent implements IAsyncEvent<Void>, ICancellable {
    private boolean cancelled = false;
    private final Player player;
    private String result;
    
    public MyAsyncEvent(Player player) {
        this.player = player;
    }
    
    public Player getPlayer() { return player; }
    public String getResult() { return result; }
    public void setResult(String result) { this.result = result; }
    
    @Override
    public boolean isCancelled() { return cancelled; }
    
    @Override
    public void setCancelled(boolean cancelled) { 
        this.cancelled = cancelled; 
    }
}

// Dispatch async
HytaleServer.get().getEventBus()
    .dispatchForAsync(MyAsyncEvent.class)
    .dispatch(new MyAsyncEvent(player))
    .thenAccept(event -> {
        if (!event.isCancelled()) {
            processResult(event.getResult());
        }
    });
java
public class MyAsyncEvent implements IAsyncEvent<Void>, ICancellable {
    private boolean cancelled = false;
    private final Player player;
    private String result;
    
    public MyAsyncEvent(Player player) {
        this.player = player;
    }
    
    public Player getPlayer() { return player; }
    public String getResult() { return result; }
    public void setResult(String result) { this.result = result; }
    
    @Override
    public boolean isCancelled() { return cancelled; }
    
    @Override
    public void setCancelled(boolean cancelled) { 
        this.cancelled = cancelled; 
    }
}

// 异步分发事件
HytaleServer.get().getEventBus()
    .dispatchForAsync(MyAsyncEvent.class)
    .dispatch(new MyAsyncEvent(player))
    .thenAccept(event -> {
        if (!event.isCancelled()) {
            processResult(event.getResult());
        }
    });

Custom ECS Event

自定义ECS事件

java
public class MyEntityEvent extends CancellableEcsEvent {
    private final ItemStack item;
    private float modifier = 1.0f;
    
    public MyEntityEvent(ItemStack item) {
        this.item = item;
    }
    
    public ItemStack getItem() { return item; }
    public float getModifier() { return modifier; }
    public void setModifier(float modifier) { this.modifier = modifier; }
}

// Register event type
EntityEventType<EntityStore, MyEntityEvent> eventType = 
    getEntityStoreRegistry().registerEntityEventType(MyEntityEvent.class);

// Invoke on entity
store.invoke(entityRef, new MyEntityEvent(itemStack));
java
public class MyEntityEvent extends CancellableEcsEvent {
    private final ItemStack item;
    private float modifier = 1.0f;
    
    public MyEntityEvent(ItemStack item) {
        this.item = item;
    }
    
    public ItemStack getItem() { return item; }
    public float getModifier() { return modifier; }
    public void setModifier(float modifier) { this.modifier = modifier; }
}

// 注册事件类型
EntityEventType<EntityStore, MyEntityEvent> eventType = 
    getEntityStoreRegistry().registerEntityEventType(MyEntityEvent.class);

// 在实体上触发事件
store.invoke(entityRef, new MyEntityEvent(itemStack));

Event Best Practices

事件最佳实践

Performance

性能优化

java
// Check for listeners before creating event
IEventDispatcher dispatcher = HytaleServer.get().getEventBus()
    .dispatchFor(ExpensiveEvent.class);

if (dispatcher.hasListener()) {
    dispatcher.dispatch(new ExpensiveEvent(computeExpensiveData()));
}
java
// 在创建事件前检查是否存在监听器
IEventDispatcher dispatcher = HytaleServer.get().getEventBus()
    .dispatchFor(ExpensiveEvent.class);

if (dispatcher.hasListener()) {
    dispatcher.dispatch(new ExpensiveEvent(computeExpensiveData()));
}

Cleanup

资源清理

Event registrations are automatically cleaned up when plugin disables. For manual cleanup:
java
private EventRegistration registration;

@Override
protected void setup() {
    registration = getEventRegistry().registerGlobal(
        SomeEvent.class, 
        this::handler
    );
}

public void disableFeature() {
    if (registration != null) {
        registration.unregister();
        registration = null;
    }
}
插件禁用时会自动清理事件注册。如需手动清理:
java
private EventRegistration registration;

@Override
protected void setup() {
    registration = getEventRegistry().registerGlobal(
        SomeEvent.class, 
        this::handler
    );
}

public void disableFeature() {
    if (registration != null) {
        registration.unregister();
        registration = null;
    }
}

Error Handling

错误处理

java
private void onPlayerConnect(PlayerConnectEvent event) {
    try {
        processPlayer(event.getPlayer());
    } catch (Exception e) {
        getLogger().atSevere().withCause(e).log("Error processing player connect");
        // Don't rethrow - let other handlers run
    }
}
java
private void onPlayerConnect(PlayerConnectEvent event) {
    try {
        processPlayer(event.getPlayer());
    } catch (Exception e) {
        getLogger().atSevere().withCause(e).log("Error processing player connect");
        // 不要抛出异常 - 让其他处理器继续执行
    }
}

Troubleshooting

故障排查

Events Not Firing

事件未触发

  1. Verify registration is in
    setup()
  2. Check event class matches exactly
  3. Verify key matches for keyed events
  4. Ensure plugin is enabled
  1. 确认注册代码在
    setup()
    方法中
  2. 检查事件类是否完全匹配
  3. 验证带键事件的键值是否匹配
  4. 确保插件已启用

Handler Not Called

处理器未被调用

  1. Check priority vs other handlers
  2. Verify event not cancelled by earlier handler
  3. Check for exceptions in handler
  1. 检查优先级与其他处理器的关系
  2. 确认事件未被更早的处理器取消
  3. 检查处理器中是否存在异常

Async Events Hanging

异步事件挂起

  1. Always complete the CompletableFuture
  2. Add timeout handling
  3. Check for deadlocks
See
references/event-list.md
for complete event catalog. See
references/ecs-events.md
for ECS event patterns.
  1. 务必完成CompletableFuture
  2. 添加超时处理
  3. 检查是否存在死锁
完整事件目录请查看
references/event-list.md
。 ECS事件模式请查看
references/ecs-events.md