Skip to content

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. /lastlegendary shows 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. despawn removes it right away. Either way it is no longer tracked.
  • schedule reroll also 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 sets canDespawn=false, pins legendaryTicks and despawnCounter, 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 CaptureEvent only exposes the Pokemon data.
  • Fleeing. Teleport, Roar, Whirlwind and WildPixelmonParticipant.endBattle call removeAfterChangingDimensions(), which bypasses updateDespawn(). LegendaryRelocator respawns 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 through ForcedLegendarySpawn.
  • 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.