Battle Debug¶
Records a detailed timeline of every Pixelmon battle so staff can find out why a battle got stuck on Waiting.... It only watches. It never changes, advances or ends a battle.
What it does¶
- Writes one timeline file per battle: battle events, every battle packet in both directions, and regular snapshots of the full server-side battle state.
- Flags anything that looks wrong, and writes a
stuck_waiting_detectedrecord once a battle has been waiting longer than the limit (30 seconds by default). - Keeps recording for 30 seconds after a battle ends, to catch problems during teardown.
- Deletes old files by itself (14 days or 2 GiB by default).
- Does not record chat.
Runs on the server only. Players install nothing.
How players use it¶
Players do not interact with this mod.
Staff guide¶
There are no commands. When a player reports a stuck battle:
- Write down the player name and the time (with time zone) it got stuck.
- Find the battle in
battledebug/player-index/<date>.jsonlby player name or UUID. Dates and times in the files are UTC. - Open the matching file in
battledebug/battles/<date>/. File names are<time>_b<index>_<players>.jsonl. - Look for
stuck_waiting_detectedand the anomaly labels near it, then read backwards to the last packet each side sent.
Do not end the battle on the mod's behalf. The stuck battle is the evidence. End it the normal way
(/endbattle) after the timeline has the state you need.
Configuration¶
config/battledebug.properties. It is created on first boot with every key filled in.
Changes need a restart.
| Key | What it controls |
|---|---|
stateSampleTicks |
How often a full battle snapshot is taken (20 ticks = 1 second) |
waitingSampleTicks |
How often while a battle is waiting |
suspiciousWaitingSeconds |
How long a wait counts as stuck |
postEndCaptureSeconds |
How long to keep recording packets after a battle ends |
retentionDays, maxDiskMiB |
When old files are deleted |
maxPacketChars, queueCapacity |
Size limits that keep output and memory bounded |
Invalid values fall back to the default with a warning in the log.
Data and storage¶
Plain files in the server folder, all UTC:
| Path | Holds |
|---|---|
battledebug/battles/YYYY-MM-DD/ |
One .jsonl timeline per battle |
battledebug/player-index/YYYY-MM-DD.jsonl |
Which player was in which battle file |
battledebug/orphan-packets/YYYY-MM-DD.jsonl |
Battle packets seen just outside a tracked battle |
Every line has a UTC time, epoch milliseconds, a sequence number, the server tick, the session id and the record type. Everything here is safe to delete. The mod also prunes it by itself.
Troubleshooting¶
| Symptom | Cause | Fix |
|---|---|---|
No files in battledebug/battles/ |
Mod not loaded, or an old version | Check mods/ for battledebug-*.jar. Version 1.0.1 recorded nothing; use 1.0.2 or later |
Log flooded with JsonIOException from BattleDebugManager |
Version 1.0.1 | Update |
| Players cannot join after installing it | Version 1.0.0 | Update. 1.0.1 and later fix it |
A field shows <unreduced:...> |
That object type could not be turned into JSON | Expected. Only that field is lost, not the record |
Boot line that shows it loaded:
Battle Debug <version> loaded: writing read-only Pixelmon telemetry under battledebug/
It also creates
config/battledebug.properties and the battledebug/ folder.
Limit: this mod sees only the server. It can prove what the server sent, what the client sent back, and what the server's state was. It cannot prove whether the player's client showed or applied a packet. That would need a client-side companion mod and a modpack update.
Maintainer notes¶
- The rule for this mod: every call into game state can throw, so all of it goes inside the
guard. Both early bugs were a
trythat did not cover the line that threw. - 1.0.0 made
mainunjoinable.InboundBattlePacketMixincalledcontext.player()outside itstry. During login, NeoForge throws there, which killed Pixelmon'sFinishSyncDataEntryPacketfor every joining player. The server looked up but nobody could join. - 1.0.1 recorded nothing.
BattleStateSnapshotput live game objects into the JSON map, and Gson tried to reflect intojava.util.concurrent.atomic, which is not open. One bad field killed the whole record. 1.0.2 stops leaking raw objects, andSafeJsonclaims every type Gson would reflect over and writes<unreduced:<class>> <toString>instead. - Private fields are read by reflection, guarded. A missing or renamed field is recorded as such.
- Object graphs are depth- and size-limited to avoid cycles and huge lines.
- Output goes through one writer thread with a bounded queue, flushed per line, so a crash keeps almost everything.
- Two mixins hook Pixelmon packet handling:
InboundBattlePacketMixinandOutboundBattlePacketMixin.
Building¶
./gradlew clean build
Output: build/libs/battledebug-<version>.jar. Server mods/ only. Restart to load.
Full command, permission and config tables: Reference.