Suite

联动

CombatEffect EventBus 事件、Capability、跨模块联动与 Chronos 集成

联动

CombatEffect 模块通过 Suite 的 EventBus、Capability 机制与其他模块联动。模块本身不硬依赖任何 ArcartX 模块,所有联动均为可选的运行时行为。模块为纯事件驱动型,不涉及数据库持久化与跨服同步。

EventBus 事件发布

CombatEffect 模块在启动时自动注册 3 个 EventBus 发布者主题,供其他模块(如 BattlePass、EventPacket)订阅:

@Override
protected List<String> publishedTopics() {
    return List.of(
        "axs.combateffect.kill_entity",
        "axs.combateffect.damage_dealt",
        "axs.combateffect.player_death"
    );
}

事件主题

事件主题触发时机payload 字段说明
axs.combateffect.kill_entity玩家击杀非玩家活体实体entityTypeentityName击杀怪物/动物等
axs.combateffect.player_death玩家击杀其他玩家entityTypeentityNamevictimPVP 击杀
axs.combateffect.damage_dealt玩家对活体实体造成伤害attackertargetTypetargetNamedamage含 TACZ 枪械伤害

payload 字段说明

kill_entity / player_death

字段类型说明
entityTypestring目标实体 Bukkit EntityType 名称(如 ZOMBIEPLAYER
entityNamestring目标实体名称
victimstringplayer_death 主题包含,死亡玩家名称

damage_dealt

字段类型说明
attackerstring攻击者玩家名
targetTypestring目标实体 Bukkit EntityType 名称
targetNamestring目标实体名称
damagestring最终伤害值(String.valueOf(event.getFinalDamage())
headshotstring仅 TACZ 枪械伤害包含,是否爆头(true / false
sourcestring仅 TACZ 枪械伤害包含,固定为 tacz

TACZ 枪械伤害与 Bukkit 原版伤害发布到同一主题(axs.combateffect.damage_dealt),payload 结构一致,下游消费者无需区分伤害来源。TACZ 伤害额外包含 headshotsource 字段。

事件发布流程

# 击杀事件发布流程
EntityDeathEvent / TaczGunKillEvent
  → 解析 killer 与 victim
  → 判断 isPlayerKill
  → publishKillEvent(killer, victim, isPlayerKill)
    → isPlayerKill=true: topic=axs.combateffect.player_death
    → isPlayerKill=false: topic=axs.combateffect.kill_entity
    → payload={entityType, entityName, victim?}
 
# 伤害事件发布流程
EntityDamageByEntityEvent / TaczGunDamageEvent
  → 解析 attacker 与 target
  → publishDamageEvent(attacker, target, event)
    → topic=axs.combateffect.damage_dealt
    → payload={attacker, targetType, targetName, damage}

在其他模块中订阅

其他模块可通过 EventBusCapability 订阅 CombatEffect 发布的事件主题:

// 在 BattlePass 任务模板中订阅击杀事件
event-topic: "axs.combateffect.kill_entity"
// 在 EventPacket 规则中订阅伤害事件
event-topic: "axs.combateffect.damage_dealt"
conditions:
  - type: event_payload
    key: damage
    operator: greater_than
    value: "100"

事件主题支持通配符订阅,如 axs.combateffect.* 可匹配所有 CombatEffect 事件。通配符仅支持末尾 .*

Capability 注册

CombatEffect 模块注册了 CombatEffectTriggerable 跨模块能力,供其他模块(如 EventPacket、OnlineRewards)主动触发战斗特效。

CombatEffectTriggerable

方法参数返回值说明
triggerPacketString packetId, Player recipient, Map<String, String> variablesboolean按包 ID 触发已注册的包定义,绕过事件匹配
triggerDirectString uiId, String packetHandler, Player recipient, Object payloadboolean直接向玩家发送 UI 包,绕过包定义系统

triggerPacket 使用示例

// 获取 CombatEffectTriggerable 能力
CombatEffectTriggerable triggerable = module.getCapability(CombatEffectTriggerable.class);
 
// 触发 kill-effect 包定义
Map<String, String> variables = new HashMap<>();
variables.put("custom_var", "hello");
triggerable.triggerPacket("kill-effect", player, variables);

triggerDirect 使用示例

// 直接向玩家发送 UI 包(完全自定义)
Map<String, Object> payload = new HashMap<>();
payload.put("killer", "Steve");
payload.put("victim", "Zombie");
triggerable.triggerDirect("AXS:combat_kill_effect", "kill", player, payload);

triggerPacket 会查找已注册的包定义并渲染 pack 模板,附加的 variables 会合并到模板渲染中。triggerDirect 完全绕过包定义系统,payload 直接作为发包内容发送。

能力注册时机

// CombatEffectModule.startService() 中注册
registerCapability(CombatEffectTriggerable.class, new CombatEffectTriggerable() {
    @Override
    public boolean triggerPacket(String packetId, Player recipient, Map<String, String> variables) {
        return packetService.triggerPacketById(packetId, recipient, variables);
    }
 
    @Override
    public boolean triggerDirect(String uiId, String packetHandler, Player recipient, Object payload) {
        return packetService.triggerDirect(uiId, packetHandler, recipient, payload);
    }
});

Chronos 集成

CombatEffect 与 ArcartX Chronos 插件深度集成,通过 ChronosHook 隔离层实现松耦合。Chronos 未安装时所有 Chronos 相关功能自动降级,不影响模块正常加载。

集成场景

功能Chronos 用途降级行为
连击追踪监听 PlayerEnterStateEvent,状态组匹配时累加连击回退到 Bukkit EntityDamageByEntityEvent
死亡缓冲缓冲期间强制玩家进入指定 Chronos 状态不进入 Chronos 状态,仅应用 Shader/相机
状态触发器监听状态进入/离开与控制器切换事件触发器不启用
控制器触发器监听 PlayerControllerChangeEvent触发器不启用

ChronosHook 隔离层

ChronosHook 是模块中唯一直接引用 ChronosAPI 的类。通过 ChronosHookFactory 延迟加载,确保 Chronos 未安装时不触发 NoClassDefFoundError

// ChronosHookFactory 通过 Class.forName 延迟加载
public static ChronosHook tryCreate(JavaPlugin plugin, Logger logger, boolean debug) {
    if (Bukkit.getPluginManager().getPlugin("ArcartX_Chronos_Plugin") == null
        && Bukkit.getPluginManager().getPlugin("Chronos") == null) {
        return null;
    }
    try {
        Class<?> hookClass = Class.forName(
            "xuanmo.arcartxsuite.combateffect.chronos.ChronosHook",
            true, ChronosHookFactory.class.getClassLoader()
        );
        Method tryCreate = hookClass.getMethod("tryCreate",
            JavaPlugin.class, Logger.class, boolean.class);
        return (ChronosHook) tryCreate.invoke(null, plugin, logger, debug);
    } catch (Exception | LinkageError e) {
        return null;
    }
}

Chronos 事件监听

Chronos 事件用途回调参数
PlayerEnterStateEvent连击追踪 / 状态进入触发玩家、状态 ID、控制器 ID
PlayerLeaveStateEvent状态离开触发玩家、状态 ID、控制器 ID
PlayerControllerChangeEvent控制器切换触发玩家、控制器 ID

ChronosHook API

方法参数返回值说明
getPlayerStateGroupIdPlayerString查询玩家当前状态组 ID
getPlayerControllerIdPlayerString查询玩家当前控制器 ID
tryEnterControlledStatePlayer, String stateId, long durationMsboolean尝试让玩家进入受控状态
registerComboListenerSet<String> groups, Consumer<Player>Listener注册连击监听,状态组匹配时回调
registerStateListenersBiConsumer<Player, StateChange>void注册状态进入/离开监听
registerControllerListenerBiConsumer<Player, String>void注册控制器变更监听

属性插件集成

伤害飘字子系统通过 AttributeBridgeRegistry 与四种属性插件集成,支持自动检测与回退。

伤害来源优先级

优先级来源配置值检测条件
1MythicLib / MMOItemsmythiclib插件可用 + mythiclib.enabled=true
2CraneAttributecraneattribute插件可用 + craneattribute.enabled=true
3AttributePlusattributeplus插件可用 + original.ap-compatible=true
4Symphonysymphony插件可用 + symphony.enabled=true
5Bukkit 原版bukkitoriginal.enabled=trueplayer.enabled=true

伤害事件监听

监听器监听事件说明
CombatDisplayDamageListenerEntityDamageByEntityEventBukkit 原版伤害(近战/Projectile)
CombatDisplayTaczListenerTaczGunDamageEventTACZ 枪械伤害
AttributeDamageListenerAttributeDamageEvent统一属性伤害事件(由属性桥接分发)

治疗事件监听

监听器监听事件说明
CombatDisplayHealListenerEntityRegainHealthEvent原版治疗(药水/生命恢复)
CombatDisplayMythicHealListenerMythicHealMechanicEventMythicMobs 技能治疗
AttributeHealListenerAttributeHealEvent统一属性治疗事件

MythicMobs 治疗监听器仅在 MythicMobs / MythicBukkit 插件安装时注册,通过 isAnyPluginEnabled 检测。

数据库表结构

CombatEffect 模块为纯事件驱动型,不涉及任何数据库持久化。连击追踪、死亡缓冲等状态均保存在内存中(ConcurrentHashMap),玩家退出或模块卸载时自动清理。

子模块存储方式清理时机
包分发冷却内存 ConcurrentHashMap<String, Long>玩家退出 / 模块卸载
连击追踪内存 ConcurrentHashMap<UUID, ComboState>超时重置 / 玩家退出 / 模块卸载
死亡缓冲内存 ConcurrentHashMap<UUID, BufferState>缓冲结束 / 玩家退出 / 模块卸载
伤害合并内存 ConcurrentHashMap<MergeKey, MergeEntry>合并窗口结束 / 模块卸载
治疗合并内存 ConcurrentHashMap<HealMergeKey, HealMergeEntry>合并窗口结束 / 模块卸载

模块不注册 DatabaseMigratablePlayerDataPurgeable 能力,不参与数据库迁移与玩家数据清理。

跨服同步

CombatEffect 模块不涉及跨服数据同步。所有战斗特效均为单服实时事件,不通过 Redis 或 BungeeCord/Velocity Forward 同步。

特性跨服支持说明
击杀/攻击特效单服 UI 包通讯,不跨服
连击追踪单服内存状态,不跨服
死亡缓冲单服内存状态,不跨服
伤害飘字单服客户端通讯,不跨服
EventBus 事件单服 EventBus,不跨服发布

模块集成示例

BattlePass 任务订阅

BattlePass 模块可订阅 CombatEffect 发布的 EventBus 事件主题,追踪玩家击杀与伤害行为:

# 每日击杀任务
daily-kill-10:
  display-name: "日常狩猎"
  description: "击杀10个任意生物"
  difficulty: easy
  event-topic: "axs.combateffect.kill_entity"
  required-count: 10
  base-xp-reward: 120
  increment-strategy:
    type: fixed
    value: 1
  weight: 4
# PVP 击杀任务
daily-pvp-kill:
  display-name: "竞技场高手"
  description: "PVP 击杀3名玩家"
  difficulty: hard
  event-topic: "axs.combateffect.player_death"
  required-count: 3
  base-xp-reward: 300
  increment-strategy:
    type: fixed
    value: 1
  weight: 1

EventPacket 规则订阅

EventPacket 模块可订阅伤害事件,根据伤害值触发自定义规则:

# 高伤害成就
high-damage-achievement:
  event-topic: "axs.combateffect.damage_dealt"
  conditions:
    - type: event_payload
      key: damage
      operator: greater_than
      value: "500"
  actions:
    - type: command
      command: "say {attacker} 造成了 {damage} 点伤害!"

通过 Capability 主动触发

其他模块可通过 CombatEffectTriggerable 能力主动触发战斗特效:

// 在在线奖励模块中,玩家领取奖励时触发庆祝特效
public void onRewardClaim(Player player) {
    CombatEffectTriggerable triggerable = combateffectModule.getCapability(CombatEffectTriggerable.class);
    if (triggerable != null) {
        Map<String, String> variables = new HashMap<>();
        variables.put("reward_type", "daily");
        triggerable.triggerPacket("kill-effect", player, variables);
    }
}
// 在 EventPacket 模块中,根据事件直接发送自定义 UI 包
public void onCustomEvent(Player player, String eventType) {
    CombatEffectTriggerable triggerable = combateffectModule.getCapability(CombatEffectTriggerable.class);
    if (triggerable != null) {
        Map<String, Object> payload = new HashMap<>();
        payload.put("event_type", eventType);
        payload.put("player", player.getName());
        triggerable.triggerDirect("AXS:combat_kill_effect", "kill", player, payload);
    }
}

相关文档