Skip to content

经验来源 ​

经验来源文件写在 sources/*.yml: 里面有哪些触发器, 每条规则能按什么条件匹配, 公式里能用哪些变量, 以及防刷是怎么处理的

经验来源文件位于 plugins/EmakiLevel/sources/*.yml. 每个文件里可以放多条来源规则. 你用 trigger 把规则挂到某个 Bukkit 或 MythicMobs 事件上, 再用匹配条件挑选要给多少经验的公式

默认来源文件:

文件默认来源说明
combat.ymlvanilla_entity_kill原版实体击杀经验
mythicmobs.ymlmythic_mob_killMythicMob 击杀经验
block_actions.ymlmining, logging, gathering, farming方块破坏, 采集, 成熟作物收获
fishing.ymlplayer_fish钓鱼事件经验
crafting.ymlcraft_item合成结果经验
brewing.ymlbrew_complete炼药完成经验
smelting.ymlfurnace_extract熔炉提取结果经验
taming.ymlentity_tame驯养实体经验

文件结构 ​

yaml
id: "combat_sources"
enabled: true
sources:
  vanilla_entity_kill:
    enabled: true
    type: "combat"
    trigger: "entity_kill"
    include_players: false
    anti_abuse:
      exp_cooldown_ticks: 5
      ignore_near_spawner: true
      spawner_scan_radius: 8
    rules:
      - entity_types: ["ZOMBIE", "SKELETON"]
        exp_formula: "10"
      - entity_types: ["WITHER", "ENDER_DRAGON"]
        exp_formula: "500"

顶层字段:

字段类型必填默认值取值范围说明
idstring否文件名 (去掉 .yml)—来源文件标识, 主要用于方便看懂配置
enabledboolean否truetrue / false这个文件的总开关. 设成 false 时整个文件都不读
sourcesmapping否{}—这个文件里的来源规则集合, 里面的键名就是来源规则的 ID

单个来源规则字段:

字段类型必填默认值取值范围说明
enabledboolean否truetrue / false这一条来源的开关
typestring否main已加载的等级类型 ID获得经验的等级类型 ID
triggerstring否""见"触发器总览"触发器 ID. 为空时不会命中任何事件
include_playersboolean否falsetrue / false只有 entity_kill 会用到; 决定击杀玩家时给不给经验
count_result_amountboolean否falsetrue / false当前代码会读这个键, 但不拿它算经验; 结果数量是通过公式变量 %result_amount% 用的
anti_abuse.ignore_player_placed_blocksboolean否falsetrue / false只有 block_break / crop_harvest 来源会用到; 决定要不要跳过玩家自己放过的方块
anti_abuse.exp_cooldown_ticksint否0负值按 0 处理同一玩家在同一来源上两次发经验之间的冷却, 单位是 tick, 也就是 1/20 秒 (一个游戏刻). 发放成功后才会开始计时
anti_abuse.ignore_near_spawnerboolean否falsetrue / false只有 entity_kill / mythic_mob_kill 会用到; 击杀点附近有刷怪笼时跳过这条来源
anti_abuse.spawner_scan_radiusint否8负值按 0 处理, 实际扫描上限 16配合 ignore_near_spawner 使用, 决定扫多大范围找刷怪笼
attribution.expire_ticksint否1200—只有 brew_complete 会用到; 炼药完成事件距离玩家最后一次操作炼药台超过这么多 tick 时, 跳过这条来源
rules[]list<mapping>否[]见"规则匹配"匹配规则列表. 每条来源会从上往下找, 用第一条匹配上的规则
actions.gainlist<string>否[]CoreLib Action 行这个配置入口保留着, 但当前代码不执行它. 真正生效的获得经验动作是等级类型自己的 upgrade.actions.gain

触发器总览 ​

完整共享事件 ID 说明见 CoreLib 的 共享玩法事件 ID. 下表只列 EmakiLevel 经验来源常用字段

trigger事件来源常用匹配字段公式变量默认目标类型
entity_killCoreLib 共享事件; 击杀者或最近伤害玩家entity_types%entity_type%combat
mythic_mob_killCoreLib 共享事件; 同次死亡中识别到的 MythicMobs 怪物mob_ids%mythic_id%, %mythic_level%combat
block_breakCoreLib 共享事件; 玩家破坏方块blocks%block_type%mining / logging / gathering
crop_harvestblock_break 的派生触发; 被破坏方块是已成熟 Ageable 作物blocks%block_type%farming
player_fishCoreLib 共享事件; 玩家钓鱼states%fish_state%fishing
craft_itemCoreLib 共享事件; 玩家合成物品item_sources + matcher%result_amount%, %result_type%crafting
brew_completeCoreLib 共享事件; 归属到最近操作炼药台的玩家potion_types%potion_type%brewing
furnace_extractCoreLib 共享事件; 玩家从熔炉取出产物item_sources + matcher%result_amount%, %result_type%smelting
entity_tameCoreLib 共享事件; 玩家驯服实体entity_types%entity_type%taming

CoreLib 还会发出 block_place 共享事件, EmakiLevel 只用它记录玩家放置过的方块位置; 把 block_place 写进来源规则的 trigger 不会产生任何经验

规则匹配 ​

每条规则支持以下匹配字段:

字段类型必填默认值取值范围说明
entity_typeslist<string>否[] (不限制)Bukkit EntityType 名, 如 ZOMBIE, WOLF, ENDER_DRAGON匹配实体类型
blockslist<string>否[] (不限制)Bukkit Material 名, 如 STONE, WHEAT, OAK_LOG匹配方块
stateslist<string>否[] (不限制)事件状态名, 如钓鱼的 CAUGHT_FISH匹配事件状态
potion_typeslist<string>否[] (不限制)药水基础类型名, 如 AWKWARD, SPEED, STRENGTH匹配药水基础类型
mob_idslist<string>否[] (不限制)MythicMobs 内部 ID, 如 SkeletonKing匹配 MythicMob ID, 匹配时会把大小写统一
item_sourceslist<string>否[] (不限制)合法 CoreLib ItemSource事件涉及的物品 (比如合成与冶炼的结果) 必须来自这里列出的来源
matchermapping否—见 物品匹配器通用的物品匹配器, 只按组件, PDC, Lore 等跟物品来源无关的条件进一步收窄. 和同一级的 item_sources 是"都要满足"的关系
exp_formulastring否0表达式经验公式. 返回值小于等于 0 时不会给经验

匹配行为:

  • 同一个 trigger 可以有多条来源; 事件触发时会遍历所有同 trigger 来源
  • 每条来源内部按 rules 顺序寻找第一条匹配规则
  • 某个匹配字段为空时, 表示该字段不限制; 例如没有写 entity_types 的规则可以匹配所有实体
  • entity_types, blocks, states, potion_types 会转为大写后匹配
  • item_sources 使用 CoreLib ItemSource 识别物品来源, 不只是原版 Material

DANGER

item_sources 与 matcher 都没写的规则会匹配任意物品, 等于一条不设任何条件的规则. 规则按从上到下的顺序取第一条匹配上的, 所以这种规则一旦写在前面, 后面所有规则就永远轮不到

这种不设条件的规则一定要放在最后. 想限制物品就写 item_sources, 需要的话再加 matcher

用 matcher 按物品数据给经验 ​

item_sources 限定物品种类, matcher 再看物品上的具体数据, 两者是"都要满足"的关系. 例如只有合成出附了魔的产物才给额外经验:

yaml
rules:
  - item_sources:
      - minecraft-diamond_sword
    matcher:
      type: component
      component: enchantments
      operator: exists
    exp_formula: "50"
  # 兜底: 普通钻石剑
  - item_sources: ["minecraft-diamond_sword"]
    exp_formula: "10"

一句话记住匹配顺序: 规则按从上到下的顺序取第一条匹配上的, 所以条件更窄的规则要写在前面, 范围宽或不设条件的规则放在最后

WARNING

matcher 内部不能写物品源条件 (type: item_source / item_sources / source / sources 会被拒绝并告警). 完整语法见 物品匹配器

旧键 result_item_sources

旧的 rules[].result_item_sources 已改名为 item_sources, /emakilevel convert-legacy 可自动完成这一项改名 (先不带参数跑一遍看会改哪些地方, 加 confirm 真正写入并自动备份)

经验公式 ​

经验公式使用 CoreLib 表达式引擎和 %key% 变量写法, 填在规则的 exp_formula 里

示例:

yaml
rules:
  - blocks: ["DIAMOND_ORE", "DEEPSLATE_DIAMOND_ORE"]
    exp_formula: "25"
  - item_sources: ["minecraft-diamond_pickaxe"]
    exp_formula: "40 * %result_amount%"
  - mob_ids: ["ForestBoss"]
    exp_formula: "300 + %mythic_level% * 20"

如果公式返回 0 或负数, 插件会跳过本次经验发放

原版击杀 ​

默认配置:

yaml
sources:
  vanilla_entity_kill:
    enabled: true
    type: "combat"
    trigger: "entity_kill"
    include_players: false
    anti_abuse:
      exp_cooldown_ticks: 5
      ignore_near_spawner: true
      spawner_scan_radius: 8
    rules:
      - entity_types: ["ZOMBIE", "SKELETON", "SPIDER", "CREEPER"]
        exp_formula: "10"
      - entity_types: ["ENDERMAN", "BLAZE", "WITHER_SKELETON"]
        exp_formula: "25"
      - entity_types: ["WITHER", "ENDER_DRAGON"]
        exp_formula: "500"

行为说明:

  • 击杀归属由 CoreLib 共享事件解析: 优先使用 Bukkit 记录的直接击杀者
  • 如果不是直接击杀, 并且 anti_abuse.last_damager_tracking.enabled: false, EmakiLevel 会跳过本次事件
  • 最近伤害归属窗口由 CoreLib config.yml > gameplay_events.last_damager_expire_ticks 控制, 默认 200 tick
  • include_players: false 时, 击杀玩家不会给经验
  • ignore_near_spawner: true 时, 会以击杀点为中心扫描 spawner_scan_radius (0–16) 范围内的方块, 发现刷怪笼即跳过该来源. 半径越大扫描的方块越多, 建议保持默认 8

方块破坏与作物收获 ​

默认挖掘来源:

yaml
sources:
  mining:
    enabled: true
    type: "mining"
    trigger: "block_break"
    anti_abuse:
      ignore_player_placed_blocks: true
      exp_cooldown_ticks: 2
    rules:
      - blocks: ["STONE", "DEEPSLATE"]
        exp_formula: "1"
      - blocks: ["COAL_ORE", "DEEPSLATE_COAL_ORE", "IRON_ORE", "DEEPSLATE_IRON_ORE"]
        exp_formula: "5"
      - blocks: ["DIAMOND_ORE", "DEEPSLATE_DIAMOND_ORE", "EMERALD_ORE", "DEEPSLATE_EMERALD_ORE"]
        exp_formula: "25"

防刷行为:

  • anti_abuse.placed_block_tracking: true 时, 玩家放置过的方块位置会被记录
  • 如果来源设置 ignore_player_placed_blocks: true, 该来源会跳过玩家放置过的方块
  • 即使来源没有写 ignore_player_placed_blocks, 当 anti_abuse.placed_block_exp: false 时, 玩家放置过的方块也不会给破坏经验
  • 记录在方块被破坏后移除; 超过 anti_abuse.placed_block_record_ttl_ticks 的记录会在下一次读写时清理

成熟作物来源使用同一个破坏事件, 但只有方块数据是 Ageable 且已成熟时才触发:

yaml
sources:
  farming:
    enabled: true
    type: "farming"
    trigger: "crop_harvest"
    anti_abuse:
      ignore_player_placed_blocks: true
      exp_cooldown_ticks: 2
    rules:
      - blocks: ["WHEAT", "CARROTS", "POTATOES", "BEETROOTS", "NETHER_WART"]
        exp_formula: "4"

一次破坏成熟作物会同时触发 block_break 和 crop_harvest 两个触发器, 两类来源都可能各自发放经验

钓鱼 ​

yaml
sources:
  player_fish:
    enabled: true
    type: "fishing"
    trigger: "player_fish"
    rules:
      - states: ["CAUGHT_FISH"]
        exp_formula: "10"
      - states: ["CAUGHT_ENTITY"]
        exp_formula: "5"

%fish_state% 对应 PlayerFishEvent.State 名称. 常见状态包括 CAUGHT_FISH, CAUGHT_ENTITY, FISHING, BITE, FAILED_ATTEMPT 等; 默认只对真正钓到物品或实体给经验

合成 ​

yaml
sources:
  craft_item:
    enabled: true
    type: "crafting"
    trigger: "craft_item"
    count_result_amount: true
    rules:
      - item_sources:
          - "minecraft-crafting_table"
          - "minecraft-furnace"
        exp_formula: "5 * %result_amount%"
      - item_sources:
          - "minecraft-diamond_pickaxe"
          - "minecraft-diamond_sword"
        exp_formula: "40 * %result_amount%"

合成来源会读取配方结果物品并使用 CoreLib ItemSource 识别. 公式变量:

变量说明
%result_amount%配方结果堆叠数量, 至少为 1
%result_type%Bukkit Material 名称

炼药 ​

yaml
sources:
  brew_complete:
    enabled: true
    type: "brewing"
    trigger: "brew_complete"
    attribution:
      expire_ticks: 1200
    rules:
      - potion_types: ["AWKWARD", "SPEED", "STRENGTH"]
        exp_formula: "15"

炼药台事件本身不直接提供玩家, CoreLib 会记录最近操作该炼药台的玩家, 并在炼药完成时把经验归属给该玩家. 来源可用 attribution.expire_ticks 设置自己的归属窗口, 默认 1200 tick; 事件距离最后一次操作超过该窗口时跳过该来源. 公式变量:

变量说明
%potion_type%炼药台产物中第一个可识别药水的基础类型; 无法识别时为 UNKNOWN

冶炼 ​

yaml
sources:
  furnace_extract:
    enabled: true
    type: "smelting"
    trigger: "furnace_extract"
    rules:
      - item_sources:
          - "minecraft-iron_ingot"
          - "minecraft-copper_ingot"
          - "minecraft-gold_ingot"
        exp_formula: "3 * %result_amount%"
      - item_sources: ["minecraft-netherite_scrap"]
        exp_formula: "20 * %result_amount%"

公式变量:

变量说明
%result_amount%玩家从熔炉中取出的物品数量
%result_type%Bukkit Material 名称

驯养 ​

yaml
sources:
  entity_tame:
    enabled: true
    type: "taming"
    trigger: "entity_tame"
    rules:
      - entity_types: ["WOLF", "CAT", "HORSE", "PARROT"]
        exp_formula: "20"

只有驯养者是玩家时才会发放经验. 公式变量:

变量说明
%entity_type%被驯养实体的 Bukkit EntityType

自定义来源示例 ​

新增一个钓到宝藏时给主等级经验的来源:

yaml
id: "treasure_sources"
enabled: true
sources:
  fishing_treasure_main:
    enabled: true
    type: "main"
    trigger: "player_fish"
    rules:
      - states: ["CAUGHT_FISH"]
        exp_formula: "2"

新增一个所有原木都给砍伐经验的来源:

yaml
id: "custom_logging"
enabled: true
sources:
  all_logs:
    enabled: true
    type: "logging"
    trigger: "block_break"
    anti_abuse:
      ignore_player_placed_blocks: true
    rules:
      - blocks:
          - "OAK_LOG"
          - "BIRCH_LOG"
          - "SPRUCE_LOG"
          - "JUNGLE_LOG"
          - "ACACIA_LOG"
          - "DARK_OAK_LOG"
          - "MANGROVE_LOG"
          - "CHERRY_LOG"
        exp_formula: "5"

新增一个所有非玩家实体击杀都给少量主等级经验的来源:

yaml
id: "main_kill_bonus"
enabled: true
sources:
  main_entity_kill_bonus:
    enabled: true
    type: "main"
    trigger: "entity_kill"
    include_players: false
    rules:
      - exp_formula: "1"

最后一条规则没有写 entity_types, 因此会匹配所有实体