English
Experience Sources
Experience source files are written under sources/*.yml: which triggers they provide, which conditions each rule can match on, which formula variables are available, and how anti-abuse is handled
Experience source files are stored under plugins/EmakiLevel/sources/*.yml. Each file can contain multiple source rules. You bind a rule to a Bukkit or MythicMobs event through trigger, then use match conditions to pick the 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
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"Top-level fields:
| Field | Type | Required | Default | Range | Description |
|---|---|---|---|---|---|
id | string | no | file name without .yml | — | Source file identifier, mainly for readability |
enabled | boolean | no | true | true / false | File switch. When false, the whole file is skipped |
sources | mapping | no | {} | — | Source rule map keyed by source ID |
Single source fields:
| Field | Type | Required | Default | Range | Description |
|---|---|---|---|---|---|
enabled | boolean | no | true | true / false | Rule switch |
type | string | no | main | a loaded level type id | Target level type ID |
trigger | string | no | "" | see "Triggers" | Trigger ID. A blank value never matches an event |
include_players | boolean | no | false | true / false | Used by entity_kill; whether killing players can grant exp |
count_result_amount | boolean | no | false | true / 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 | boolean | no | false | true / false | Used by block_break / crop_harvest; whether player-placed blocks are ignored |
anti_abuse.exp_cooldown_ticks | int | no | 0 | negative values are treated as 0 | Per-player, per-source exp cooldown in ticks. The timer starts only after a successful grant |
anti_abuse.ignore_near_spawner | boolean | no | false | true / 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 | int | no | 8 | negative values are treated as 0, effective scan capped at 16 | Scan radius used together with ignore_near_spawner |
attribution.expire_ticks | int | no | 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[] | list<mapping> | no | [] | see "Rule matching" | Match rule list. One source uses the first matching rule |
actions.gain | list<string> | no | [] | CoreLib action line | 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. The table below 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 | item_sources + 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 | item_sources + 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 | Type | Required | Default | Range | Description |
|---|---|---|---|---|---|
entity_types | list<string> | no | [] (unrestricted) | Bukkit EntityType name, e.g. ZOMBIE, WOLF, ENDER_DRAGON | Matches the entity type |
blocks | list<string> | no | [] (unrestricted) | Bukkit Material name, e.g. STONE, WHEAT, OAK_LOG | Matches the block |
states | list<string> | no | [] (unrestricted) | event state name, e.g. CAUGHT_FISH | Matches the event state |
potion_types | list<string> | no | [] (unrestricted) | potion base type, e.g. AWKWARD, SPEED, STRENGTH | Matches the potion base type |
mob_ids | list<string> | no | [] (unrestricted) | MythicMobs internal ID, e.g. SkeletonKing | Matches the MythicMob ID; matching is case-normalized |
item_sources | list<string> | no | [] (unrestricted) | valid CoreLib ItemSource | Allowed item sources for the item involved in the event (crafting and smelting results, and so on) |
matcher | mapping | no | — | see Item Matcher | Non-item-source conditions (component, PDC, lore) for the same item, ANDed with the sibling item_sources |
exp_formula | string | no | 0 | expression | 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 matchingitem_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:
yaml
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"Keep the matching order in one sentence: 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, and are filled in under the rule's 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"If the formula returns 0 or less, the exp grant is skipped
Vanilla kills
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"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
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"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:
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"Breaking a mature crop fires both block_break and crop_harvest, so sources bound to either trigger can each grant exp
Fishing
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% is the PlayerFishEvent.State name. Common states include CAUGHT_FISH, CAUGHT_ENTITY, FISHING, BITE, and FAILED_ATTEMPT; by default exp is granted only when an item or entity is actually caught
Crafting
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%"Formula variables:
| Variable | Description |
|---|---|
%result_amount% | Recipe result stack amount, minimum 1 |
%result_type% | Bukkit Material name |
Brewing
yaml
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
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%"Formula variables:
| Variable | Description |
|---|---|
%result_amount% | Amount extracted from the furnace |
%result_type% | Bukkit Material name |
Taming
yaml
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:
yaml
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:
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"Small main exp for all non-player kills:
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"The last rule has no entity_types, so it matches all entities