Legendary Tracker¶
Runs wild legendary spawns on this network: it spawns a set number of legendaries each day on a schedule, keeps each one from despawning for 30 minutes, tells players where it is if nobody catches it, and announces spawns on Discord.
What it does¶
- Daily schedule. At the start of each day (Eastern time) it rolls how many legendaries will spawn (2 or 3 by default, even odds) and picks the times, at least 90 minutes apart. Pixelmon's own random legendary roll is held off while the schedule is on.
- Finds a spot. A spawn is placed near an online player. If that player has no valid spot, it tries the other players, then keeps retrying every 15 seconds for up to 2 hours.
- Tracking. A spawned legendary cannot despawn for 30 minutes, and its chunk stays loaded.
- Coordinates for uncaught legendaries. Every 5 minutes an uncaught legendary is announced in chat with its coordinates and dimension. After 30 minutes it is released and despawns normally.
- Fleeing. If a legendary teleports or flees a battle (Teleport, Roar, Whirlwind), it is put back nearby instead of vanishing.
- No suffocation. Legendaries never take in-wall damage.
- Discord. Each spawn is posted through DiscordSRV, naming the biome:
A wild Rayquaza has spawned in a Bamboo Jungle biome! - History.
/lastlegendaryshows recent legendaries and who caught them (needs the database). - Exclusions. Staff can list players that legendaries will never spawn near.
Server-side only. Needs DiscordSRV for the Discord post.
How players use it¶
| Command | What it does |
|---|---|
/lastlegendary [page] (also /ll) |
Recent legendary spawns: species, biome, time, and whether it was caught |
Watch chat for the spawn message (biome only) and, if nobody catches it within 5 minutes, the coordinates.
Staff guide¶
Permissions are Bukkit/LuckPerms nodes. legendarytracker.admin grants all of them. Ops (level 2)
have everything. Ops skip CommandWhitelist, so test as a non-op.
| Task | Command | Permission |
|---|---|---|
| List tracked legendaries | /legendarytracker list |
legendarytracker.command.list |
| Teleport to one | /legendarytracker tp <legendary> |
legendarytracker.command.tp |
| Stop tracking one (leave it, or remove it) | /legendarytracker untrack <legendary> [release\|despawn] |
legendarytracker.command.untrack |
| See today's spawn times | /legendarytracker schedule |
legendarytracker.command.schedule |
| Throw away today's plan and roll a new one | /legendarytracker schedule reroll |
legendarytracker.command.schedule.reroll |
| See or change the exclusion list | /legendarytracker exclude [list\|add\|remove\|check] <player> |
legendarytracker.command.exclude (view), .exclude.manage (change) |
| Reload the config | /legendarytracker reload |
legendarytracker.command.reload |
| Turn spawn debug logging on or off | /legendarytracker debug [on\|off] |
legendarytracker.command.debug |
untrack ... release(the default) leaves the legendary in the world on Pixelmon's normal despawn timer.despawnremoves it right away. Either way it is no longer tracked.schedule rerollalso forgets what already spawned today, so it can add spawns mid-day. Use it on purpose.- Excluded players never host a spawn on any route (schedule, retry, Pixelmon's own cycle). An exclusion is not a cancellation: if every online player is excluded, the spawn waits and retries.
Configuration¶
All files are in config/legendarytracker/. /legendarytracker reload applies changes. The
config is written only when missing, so new keys use their defaults until you add them.
config.properties:
| Setting | Default | Meaning |
|---|---|---|
schedule.enabled |
true |
Use the daily schedule |
schedule.suppressNaturalSpawns |
true |
Hold Pixelmon's own legendary roll off |
schedule.timeZone |
America/New_York |
When the day starts |
schedule.dailyCountWeights |
2:50,3:50 |
How many per day, with relative weights. 3 alone means always three. 0 is allowed |
schedule.windowStart, schedule.windowEnd |
00:00, 24:00 |
The part of the day spawns can land in. Narrow it (for example 10:00 to 24:00) if nights are empty |
schedule.minGapMinutes |
90 |
Smallest gap between two spawns |
schedule.slotExpiryMinutes |
120 |
How long a due spawn keeps retrying before it is dropped |
spawnRetry.* |
on, every 15 s | Retry a spawn that found no spot |
relocate.* |
on | Put back a legendary that fled |
suffocationGuard.enabled |
true |
Block in-wall damage for legendaries |
discord.enabled, discord.channel, discord.spawnMessage |
on, global |
The Discord post. Placeholders: {pokemon}, {biome} |
database.* |
off | MariaDB for /lastlegendary history |
debug.spawnLogging |
false |
Detailed spawn logging |
A reload does not change a day already in progress. Today's times stay; new settings apply at
the next day. Use schedule reroll to apply them today.
While the schedule is on, Pixelmon's legendary-spawn-ticks and legendary-spawn-chance in
config/pixelmon/spawning/legendary.yml do nothing. They are kept so turning the schedule off
restores the old cadence.
Data and storage¶
| Where | Holds |
|---|---|
config/legendarytracker/spawn-schedule.properties |
Today's plan and which slots already spawned |
config/legendarytracker/spawn-exclusions.properties |
The exclusion list |
MariaDB table legendary_spawns |
Spawn history for /lastlegendary (only with database.enabled=true) |
Edit the exclusion list with the command, not by hand. Deleting the plan file makes the mod roll a new plan for today.
Troubleshooting¶
| Symptom | Cause | Fix |
|---|---|---|
| No legendaries today | Rolled 0, no valid spots (slots expired), or every player excluded | /legendarytracker schedule shows each slot's state |
| Two legendaries close together | Schedule off, so Pixelmon's random roll is back | Check schedule.enabled |
/lastlegendary says history is unavailable |
Database disabled | Set database.enabled=true and the login |
| Discord post has no biome | Old discord.spawnMessage without {biome} |
Add {biome} and reload |
| Legendary vanished mid-battle | It fled before 1.3.0, or relocation is off | Check relocate.enabled |
Boot and runtime lines:
[LegendaryTracker] Database connected.
[LegendaryTracker] Now tracking <species> (pokemonUUID: ...). Coordinates: ...
[LegendaryTracker] Broadcast uncaught <species> at <x y z> in <dimension>
Maintainer notes¶
- Anti-despawn. Pixelmon has its own despawn system, so
setPersistenceRequired()is not enough. The tracker setscanDespawn=false, pinslegendaryTicksanddespawnCounter, sets vanilla persistence, and force-loads the chunk, re-applying all of it every second. - Tracking is keyed by the Pokemon data UUID, not the entity UUID, because
CaptureEventonly exposes the Pokemon data. - Fleeing. Teleport, Roar, Whirlwind and
WildPixelmonParticipant.endBattlecallremoveAfterChangingDimensions(), which bypassesupdateDespawn().LegendaryRelocatorrespawns the legendary nearby instead of treating it as a chunk unload. - Exclusions on three routes. Pixelmon's natural cycle is redirected by cancelling
ChoosePlayer(the only way; there is no "skip this player"), failover drops excluded players, and every forced spawn (schedule and retry) goes throughForcedLegendarySpawn. - Biome names. Pixelmon's biome word is a translation component the client resolves. Discord has
no client, so the mod builds an English name itself (
BiomeNames). - Mod jars only load at boot. Upgrading a running server by renaming the old jar and adding a new file name is safe; overwriting the jar in place is not.
- Docs in the ops repo:
documentation/mods/legendary-spawn-schedule.md,legendary-spawn-exclusions.md,legendary-spawn-biome-in-discord.md,legendary-spawn-cadence.md.
Building¶
./gradlew build -Ppixelmon_version=<server's Pixelmon version>
Output: build/libs/legendarytracker-<version>.jar.
Full command, permission and config tables: Reference.