hytale-events-api
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseHytale 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:
- General Event Bus - Server-wide events (players, worlds, assets)
- ECS Event System - Entity-specific events (damage, interactions)
Hytale拥有两套事件系统:
- 通用事件总线 - 服务器全局事件(玩家、世界、资源)
- 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
事件未触发
- Verify registration is in
setup() - Check event class matches exactly
- Verify key matches for keyed events
- Ensure plugin is enabled
- 确认注册代码在方法中
setup() - 检查事件类是否完全匹配
- 验证带键事件的键值是否匹配
- 确保插件已启用
Handler Not Called
处理器未被调用
- Check priority vs other handlers
- Verify event not cancelled by earlier handler
- Check for exceptions in handler
- 检查优先级与其他处理器的关系
- 确认事件未被更早的处理器取消
- 检查处理器中是否存在异常
Async Events Hanging
异步事件挂起
- Always complete the CompletableFuture
- Add timeout handling
- Check for deadlocks
See for complete event catalog.
See for ECS event patterns.
references/event-list.mdreferences/ecs-events.md- 务必完成CompletableFuture
- 添加超时处理
- 检查是否存在死锁
完整事件目录请查看。
ECS事件模式请查看。
references/event-list.mdreferences/ecs-events.md