Experience Sources
Experience source files are stored under plugins/EmakiLevel/sources/*.yml. Each file can contain multiple source rules. A rule binds to a Bukkit or MythicMobs event through trigger, then uses match conditions and an exp formula.
Default source files:
| File | Default source | Description |
|---|---|---|
combat.yml | vanilla_entity_kill | Vanilla entity kill exp. |
mythicmobs.yml | mythic_mob_kill | MythicMob kill exp. |
block_actions.yml | mining, logging, gathering, farming | Block break, gathering, and mature crop harvest. |
fishing.yml | player_fish | Fishing event exp. |
crafting.yml | craft_item | Craft result exp. |
brewing.yml | brew_complete | Brewing completion exp. |
smelting.yml | furnace_extract | Furnace extraction exp. |
taming.yml | entity_tame | Taming exp. |
File structure
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"Top-level fields:
| Field | Description |
|---|---|
id | Source file identifier, mainly for readability. |
enabled | File switch. When false, the whole file is skipped. |
sources | Source rule map keyed by source ID. |
Single source fields:
| Field | Description |
|---|---|
enabled | Rule switch. |
type | Target level type ID. |
trigger | Trigger ID. See the trigger table below. |
include_players | Used by entity_kill; whether killing players can grant exp. Default false. |
count_result_amount | Default false. Parsed by the current implementation but not used in exp calculation; result counts are consumed through the %result_amount% formula variable. |
anti_abuse.ignore_player_placed_blocks | Default false. Used by block_break / crop_harvest; whether player-placed blocks are ignored. |
anti_abuse.exp_cooldown_ticks | Default 0 (no cooldown). Per-player, per-source exp cooldown in ticks. The timer starts only after a successful grant. |
anti_abuse.ignore_near_spawner | Default false. Used by entity_kill / mythic_mob_kill; skips the source when a spawner is found near the kill location. |
anti_abuse.spawner_scan_radius | Default 8; the effective scan radius is clamped to 0–16. Used together with ignore_near_spawner. |
attribution.expire_ticks | Default 1200. Used by brew_complete only; the source is skipped when the brewing completion is older than this many ticks since the player's last interaction with the stand. |
rules | Match rule list. One source uses the first matching rule. |
actions.gain | Reserved config entry. The current implementation does not execute this list; the type-level upgrade.actions.gain is the action path that actually runs. |
Triggers
For the complete shared event ID reference, see CoreLib Gameplay Event IDs. This page lists the fields commonly used by EmakiLevel experience sources.
| trigger | Event source | Match fields | Formula variables | Default target type |
|---|---|---|---|---|
entity_kill | CoreLib shared event; killer or recent damager | entity_types | %entity_type% | combat |
mythic_mob_kill | CoreLib shared event; MythicMobs mob detected in the same death | mob_ids | %mythic_id%, %mythic_level% | combat |
block_break | CoreLib shared event; player breaks a block | blocks | %block_type% | mining / logging / gathering |
crop_harvest | Derived from block_break; the broken block is a mature Ageable crop | blocks | %block_type% | farming |
player_fish | CoreLib shared event; player fishing | states | %fish_state% | fishing |
craft_item | CoreLib shared event; player crafts an item | matcher | %result_amount%, %result_type% | crafting |
brew_complete | CoreLib shared event; attributed to the most recent brewing stand user | potion_types | %potion_type% | brewing |
furnace_extract | CoreLib shared event; player extracts a furnace result | matcher | %result_amount%, %result_type% | smelting |
entity_tame | CoreLib shared event; player tames an entity | entity_types | %entity_type% | taming |
CoreLib also publishes a block_place shared event, but EmakiLevel only uses it to record player-placed block locations. Writing block_place as a source trigger never grants exp.
Rule matching
Supported match fields:
| Field | Purpose |
|---|---|
entity_types | Bukkit EntityType, e.g. ZOMBIE, WOLF, ENDER_DRAGON. |
blocks | Bukkit Material, e.g. STONE, WHEAT, OAK_LOG. |
states | Event state names, e.g. CAUGHT_FISH. |
potion_types | Potion base types, e.g. AWKWARD, SPEED, STRENGTH. |
mob_ids | MythicMobs internal IDs, e.g. SkeletonKing. Matching is case-normalized. |
item_sources | Allowed item sources for the item involved in the event (crafting and smelting results, and so on). Omit it and the item source is unrestricted. |
matcher | Non-item-source conditions (component, PDC, lore, variable) for the same item, ANDed with the sibling item_sources. Item source conditions must not go inside it. See Item Matcher. |
exp_formula | Exp formula. Values less than or equal to 0 do not grant exp. |
Behavior:
- Multiple sources can share the same
trigger; all matching sources are processed. - Inside one source, rules are checked in order and the first matching rule is used.
- An empty match field means no restriction for that field.
entity_types,blocks,states, andpotion_typesare normalized to uppercase for matching.item_sourcesuses CoreLib ItemSource matching, not only vanilla materials.- The old per-rule
result_item_sourceskey is now spelleditem_sources./emakilevel convert-legacystill handles that rename; run it without arguments for a dry run, then withconfirmto write the change out with a backup.
DANGER
A rule that declares neither item_sources nor matcher matches every item, making it an unconditional catch-all. Rules use the first match in declaration order, so once such a rule appears, no rule below it is ever reached.
Keep catch-all rules last, and declare at least one of item_sources and matcher whenever a rule should be restricted to specific items.
Granting exp by item data
item_sources selects the item and matcher adds data conditions on top; both must hold. For example, granting extra exp only for crafting an enchanted result:
rules:
- item_sources:
- minecraft-diamond_sword
matcher:
type: component
component: enchantments
operator: exists
exp_formula: "50"
# Fallback: a plain diamond sword
- item_sources: ["minecraft-diamond_sword"]
exp_formula: "10"Rules use the first match in declaration order, so narrow conditions must be declared before the fallback. Full syntax is in Item Matcher.
Exp formulas
Exp formulas use the CoreLib expression engine and %key% variables.
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"If the formula returns 0 or less, the exp grant is skipped.
Vanilla kills
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"Notes:
- Kill attribution is resolved by the CoreLib shared event, which prefers the Bukkit killer.
- When the kill was not direct and
anti_abuse.last_damager_tracking.enabled: false, EmakiLevel skips the event. - The recent damager window is controlled by CoreLib
config.yml > gameplay_events.last_damager_expire_ticks, default 200 ticks. include_players: falseprevents player kills from granting exp.- With
ignore_near_spawner: true, blocks withinspawner_scan_radius(clamped to 0–16) around the kill location are scanned, and the source is skipped when a spawner is found. Larger radii scan many more blocks; keeping the default 8 is recommended.
Block break and crop harvest
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"Anti-abuse behavior:
anti_abuse.placed_block_tracking: truerecords player-placed block locations.- If a source sets
ignore_player_placed_blocks: true, that source ignores player-placed blocks. - Even without source-level
ignore_player_placed_blocks,anti_abuse.placed_block_exp: falseprevents player-placed blocks from granting break exp. - The placed-block record is removed when the block is broken; records older than
anti_abuse.placed_block_record_ttl_ticksare pruned on the next read/write.
Mature crops use the same break event, but only trigger when block data is Ageable and fully grown:
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"Breaking a mature crop fires both block_break and crop_harvest, so sources bound to either trigger can each grant exp.
Fishing
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% is the PlayerFishEvent.State name.
Crafting
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%"Formula variables:
| Variable | Description |
|---|---|
%result_amount% | Recipe result stack amount, minimum 1. |
%result_type% | Bukkit Material name. |
Brewing
sources:
brew_complete:
enabled: true
type: "brewing"
trigger: "brew_complete"
attribution:
expire_ticks: 1200
rules:
- potion_types: ["AWKWARD", "SPEED", "STRENGTH"]
exp_formula: "15"Brewing events do not directly include a player. CoreLib records the most recent player who interacted with the brewing stand and attributes the completed brew to that player. Each source can set its own window through attribution.expire_ticks (default 1200 ticks); brews older than that window are skipped by the source.
| Variable | Description |
|---|---|
%potion_type% | First recognized potion base type in the brewing inventory, or UNKNOWN. |
Smelting
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%"Formula variables:
| Variable | Description |
|---|---|
%result_amount% | Amount extracted from the furnace. |
%result_type% | Bukkit Material name. |
Taming
sources:
entity_tame:
enabled: true
type: "taming"
trigger: "entity_tame"
rules:
- entity_types: ["WOLF", "CAT", "HORSE", "PARROT"]
exp_formula: "20"Only player owners receive exp.
| Variable | Description |
|---|---|
%entity_type% | Bukkit EntityType of the tamed entity. |
Custom examples
Main exp for fishing events:
id: "treasure_sources"
enabled: true
sources:
fishing_treasure_main:
enabled: true
type: "main"
trigger: "player_fish"
rules:
- states: ["CAUGHT_FISH"]
exp_formula: "2"All-log logging exp:
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"Small main exp for all non-player kills:
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"The last rule has no entity_types, so it matches all entities.