English
Spawn Rules
spawn_rules/*.yml spawn rules define under which conditions a custom mob appears. Spawn rule files live in plugins/EmakiMobs/spawn_rules/*.yml. Each file can contain multiple rules under a rules: list. A missing file, a missing rules key, or a rules value that is not a list produces no rules at all. The rules released on first start are spawn_rules/overworld_elites.yml (a single natural rule)
File structure
yaml
rules:
- mob_id: <mob id>
type: <spawn type>
# type-specific fields...Only two type values are accepted
The current loader recognises only type: natural and type: autonomous. Writing structure, biome, player_relative, day_interval, custom or anything else does not raise an error — it is logged as Unknown spawn type and the whole rule is skipped silently. A missing mob_id or type skips the rule with a WARN
Two rules in the bundled reference file spawn_rules/overworld.yml are never loaded
That file lives inside the plugin jar and is not released into plugins/EmakiMobs/spawn_rules/ (the first-start release is overworld_elites.yml). It holds three rules: the natural one (elite_zombie) works, while type: structure (bone_lord) and type: biome (jungle_stalker) hit the unknown-type branch above and are skipped silently. Rewrite those two using the natural / autonomous forms documented here if you copy the file
natural — intercept vanilla spawn events
Replaces naturally spawning mobs with a custom mob by probability once the conditions hold. Natural spawns are intercepted at PreCreatureSpawnEvent (before the entity is created); other spawn reasons that are neither NATURAL nor SPAWNER are intercepted at CreatureSpawnEvent, which is cancelled
yaml
- mob_id: elite_zombie
type: natural
biomes: [plains, forest] # optional biome restriction; empty means any
y_range: [-64, 50] # only replace within this Y range
light_level_max: 7 # inclusive light level ceiling
replacement_chance: 0.08 # replacement probability, 0.0–1.0
max_nearby: 3 # max same-id mobs within 64 blocks; 0 = unlimited
count:
min: 1
max: 1| Field | Type | Required | Default | Accepted values | Description |
|---|---|---|---|---|---|
mob_id | string | Yes | none | a loaded mob definition id | The mob to spawn; a missing or non-string value skips the whole rule. An unregistered id spawns nothing |
type | string | Yes | none | natural | Fixed value |
worlds | string list | No | [] (any) | world names (World#getName) | Restrict to these worlds |
biomes | string list | No | [] (any) | biome keys, optionally namespaced (the part after : is taken, lower-cased) | Restrict to these biomes; an unrecognised name logs a WARN and is dropped while the rest of the rule still applies |
y_range | int list [min, max] | No | [-64, 320] | read positionally: item 1 is min, item 2 is max; missing items fall back to the default | Y range of the spawn position (inclusive) |
light_level_max | int | No | 15 | 0–15 | The spawn position's combined light level must not exceed this; the default 15 means no restriction |
replacement_chance | double | No | 1.0 | — | Replacement probability once conditions match: 0.0 never replaces, ≥ 1.0 always replaces (not clamped) |
max_nearby | int | No | 0 | ≥ 0; 0 = unlimited | Skip the trigger once this many same-id managed mobs exist within 64 blocks |
count | {min, max} | No | {1, 1} | min ≤ max | Mobs spawned per trigger (random range, inclusive). min > max throws when the rule fires, so that replacement fails |
condition | section | No | none | a CoreLib condition block | Extra conditions. There is no player context, so player placeholders cannot be used here (unlike autonomous) |
autonomous — self-scheduled spawning
Runs entirely outside vanilla spawn events, driven by its own scheduler. trigger selects the scheduling mode. Candidate positions are generated at a random angle at distance from the player, with up to 6 attempts, requiring a passable block above solid ground — so not every attempt produces a spawn
yaml
- mob_id: elite_zombie
type: autonomous
trigger: interval
interval_ticks: 1200
worlds:
- world
biomes:
- plains
distance:
min: 16
max: 64
max_nearby: 4
max_global: 10
count:
min: 1
max: 2The four triggers
trigger | Scheduling | Key fields |
|---|---|---|
interval | Global fixed rate (first run 1 tick after start); each round scans every online player and attempts once per player | interval_ticks, worlds, max_global |
player_follow | Global fixed rate; each round picks one random online player and attempts once | interval_ticks, worlds, max_global |
day_interval | Checks each world's in-game day counter every 20 ticks (last trigger day stored per world) and attempts once for every player in that world when due | interval_days, on_day_start, worlds, max_global |
cron | Fires on real system time via a Cron expression and attempts once per online player | cron, worlds, max_global |
trigger values are case-insensitive. cron uses the 6-field Quartz format (second minute hour day-of-month month day-of-week), documented in CoreLib expressions - Cron; a blank or unparsable expression is logged as a warning and the rule never fires
autonomous fields
| Field | Type | Required | Default | Accepted values | Description |
|---|---|---|---|---|---|
mob_id | string | Yes | none | a loaded mob definition id | The mob to spawn; a missing or non-string value skips the whole rule |
type | string | Yes | none | autonomous | Fixed value |
trigger | string | Yes | none | interval / player_follow / day_interval / cron (case-insensitive) | Scheduling mode. Missing or unknown skips the whole rule with a WARN |
interval_ticks | long | No | 600 | ≥ 1 (ticks) | Scheduling period for interval / player_follow |
interval_days | int | No | 1 | ≥ 1 (in-game days) | Interval in in-game days for day_interval |
on_day_start | boolean | No | false | true / false | When true, only fires while that world's time is < 1000 (just after sunrise); when false, it fires as soon as the counter is due |
cron | string | No | "" | a 6-field Quartz expression | Expression for trigger: cron; when blank a warning is logged and the rule never fires |
worlds | string list | No | [] (any) | world names | Restrict to these worlds |
biomes | string list | No | [] (any) | biome keys (same as natural) | Restrict the spawn position's biome |
structures | string list | No | [] (any) | NamespacedKeys such as minecraft:stronghold | Restrict to positions inside that structure's bounding box in the chunk; an unknown structure logs a WARN and is dropped |
y_range | int list [min, max] | No | [-64, 320] | same as natural | Y range of the spawn position (inclusive) |
light_level_max | int | No | 15 | 0–15 | Combined light ceiling. Only checked when the value is below 15; the default 15 skips the light check entirely |
time_of_day | string | No | any | any / day (1000–12999) / night (≥ 13000 or < 1000) (case-insensitive) | Restrict the time of day; an unknown value behaves like any |
require_surface | boolean | No | false | true / false | When true, the spawn position must have sky light 15 |
distance | {min, max} | No | {16, 64} | ≥ 0 (blocks) | Spawn distance from the player (inclusive); when min ≥ max the value is fixed to min |
max_nearby | int | No | 0 | ≥ 0; 0 = unlimited | Max same-id managed mobs within 64 blocks |
max_global | int | No | 0 | ≥ 0; 0 = unlimited | Global cap on same-id mobs across all worlds; when reached the whole round is skipped |
count | {min, max} | No | {1, 1} | ≥ 1 | Mobs spawned per trigger (random range, inclusive); when min ≥ max the value is fixed to min |
condition | section | No | none | a CoreLib condition block | Extra conditions; the player that triggered this attempt is the context, so PlaceholderAPI placeholders are rendered |
How this differs from natural
A natural rule's condition has no player context, while an autonomous rule renders placeholders from the player being attempted. Both types collapse count and distance to a fixed value when min and max are equal