wavedefense-0.7.2.jar
Curse Maven Snippet
What's new
Changelog
[0.7.2] - 2026-08-11 — one scroll implementation
Eleven screens kept their own scroll state next to two base classes that already provided it. That is not only duplication: each copy had drifted, and the drift was the bug.
Six screens computed how many rows fit twice, differently — once when laying rows out and once when handling the wheel — so the wheel ran past the end of the list. One drew a divider at y=100 above a list starting at 116. One placed its ▼ button at a fixed offset that assumed exactly five rows fit, putting the arrow in the middle of the list on a tall window. One drew item icons two pixels below their own rows. StartingItemsScreen had reimplemented the base class's three-pass scissor render and picked up two faults doing it: a ▼ button hardcoded off the bottom of a short window, and widgets straddling the list edge satisfying both passes and drawing twice.
Two screens now use ScrollableScreen directly rather than ListEditorScreen, because they show several mutually exclusive lists of different types. Both had one scroll offset per list where the lists are never on screen together — so two page counts, two clamps and two branches in mouseScrolled for what is always "the list currently shown".
ImportExportScreen had a real trap: two sections, only the second scrolling. The list of locations to export grew without bound, so past about a dozen arenas the import half was pushed off the bottom and became unreachable — the scroll applied only to the part you could no longer see. It is now one list of mixed rows.
Fixed
- Mouse wheel now works on the four screens that had scroll state but no
mouseScrolled - Row counts, clip bounds and scroll-button anchors are computed once per screen, not two or three times with different answers
ShopImportScreennever released its static listing callback, keeping the closed screen alive and rebuilding widgets on it when a late packet arrived- Deleting the last entries no longer leaves the view scrolled past the end
- Backups being disabled no longer writes
last_state.jsoninto a directory that was deliberately not created, logging an error on every server stop
Added
ScrollableScreenreports, once per screen class, any non-static widget lying outside the scroll band. Pass 1 draws non-static widgets inside the band and passes 2 and 3 draw static ones outside it, so such a widget is drawn by nothing and its clicks are discarded — silently. This caught two screens of my own making where the mode tabs and the entire footer had become invisible.
Notes
- No protocol change (still version 14); test suite unchanged at 128
[0.7.1] - 2026-08-10 — two races, both quiet
An audit of the whole mod turned these up. Neither shows in testing; both bite in production.
The backup system read live server state from its own thread. It runs on a private scheduler and called locationManager.save(), waveManager.save() and — the sharp one — player.saveWithoutId() on live player entities, while the tick loop was moving them and changing their inventories. There was not one server.execute in the file. The result was a race that either threw or wrote a torn snapshot: a backup that looks fine until the day you need it.
It now captures first and writes second. Every read of live state happens on the server thread and produces plain NBT in memory; the disk work then runs off-thread against that copy. Restoring is the mirror image — read the files anywhere, apply them to the world on the server thread — which matters more, because restoring writes into the live world.
Ten of thirteen client caches were never cleared on disconnect. Each is a few static fields keyed by nothing, so they followed the player to the next server: the previous world's arenas still listed in the hub, its statistics on the stats screen, its teammates in the HUD, and half an interrupted shop upload still aimed at a location that no longer existed. ClientShopUploadQueue even had a cancel() whose javadoc said "used when the player disconnects" — nothing called it.
Fixed
- Backup and restore no longer touch live server state off the server thread
updateLastKnownStatehashes live locations and players on the server thread too- All thirteen client caches are cleared on disconnect, not three
ClientShopUploadQueue.cancel()now also resets the shop-point target, which otherwise filed the next global upload into a point from the previous server
Added
BackupThreadingTest— fails if any live-state read escapes a capture block, and if the two helpers reached indirectly stop declaring the preconditionClientCacheCoverageTest— a newgui/Client*cache fails the build until it is wired into the disconnect handler, or listed as deliberately exemptrequireServerThread()reports a wrong-thread caller in the log instead of racing silently
Notes
- No protocol change (still version 14)
- Test suite: 128 tests
[0.7.0] - 2026-08-10 — the hub
The location menu listed names. Choosing between three arenas meant joining one to find out whether anybody was in it, and if the answer was no, leaving again.
Each row now carries what is actually happening there: how many players are inside, whether the arena is empty, gathering in the lobby, mid-match or finishing, and which wave or round it has reached. A search box appears once there are enough locations to need one, and the Join button greys out on a full arena instead of letting a player click and be refused.
The status arrives on its own small packet, refreshed every two seconds while the hub is open — the full location tree is far too heavy to resend on a timer, and it does not carry live state anyway. Nothing about the way in changed: the hub sends the same TeleportPacket the menu always did, so it inherits the rate limit, the player cap, the PvP spawn choice, team auto-balance and Battle Royale's random spawn. A second join path is exactly how the player cap once came to live in one entry point while the portal and the activation zone walked past it.
A row also says when a location cannot be entered at all — no waves, no spawn point, a PvP arena with fewer than two team spawns — instead of letting a player click and be told by the server. The check is deliberately narrow: it reports only what the server would certainly refuse, because a false "not ready" locks someone out of a working arena while a missed one costs a click.
Added
- Live per-location status in the hub: players, phase, current wave or round
- Search box in the hub, shown once a server has 8 or more locations
- Sort by state, players or name — state first, since "where can I get into a game" is the question a player actually has
- Rows name the reason a location cannot be joined, and the Join button greys out with it as a tooltip
- The row you are standing in is marked, and the footer offers Leave — the hub says you must leave before joining elsewhere, so it should be where you can
LocationStatus+RequestHubStatusPacket/HubStatusPacket— a few dozen bytes a rowClientHubStatus, cleared on logout so the next server does not inherit stale rows
Changed
- Hub rows are taller and split into a name line and a status line
- Join is now its own button; the whole row is no longer a click target that teleports you
Fixed
- The hub's search box was drawn at y=30, overlapping the header panel that ends at y=31
- With no search box the list still started as if there were one, wasting a row of height on the servers least likely to have space to spare
- The hub no longer claims "you are already here" after a match ends underneath it
Notes
- Protocol version 14 — client and server must be updated together
- Twenty new lang keys across all 8 locales (1599 each, parity checked)
- Test suite: 123 tests
- The lobby / match customizer is a separate piece and is not in this release
- Compile targets unchanged: Forge 47.2.0, Minecraft 1.20.1, Java 17, official mappings
[0.6.1] - 2026-08-09 — import/export works again, backups stop shouting
Two things that were quietly wrong.
Import and export were refused on any non-ASCII installation path. The containment guard that keeps a client-supplied filename inside the export directory compared canonical path strings. On a Windows profile named in Cyrillic — C:\Users\Руслан\… — the JDK spelled the same parent directory two different ways depending on whether it was resolving the directory or a file inside it, so the prefix test failed for every legitimate path and every import and export was rejected. The guard now compares normalised Paths element by element, which cannot disagree with itself that way, and closes the sibling-prefix bypass by construction rather than by remembering to append a separator.
Backups ran hourly and kept a week of them. Every value was hardcoded: a snapshot every 60 minutes, 24 full and 168 incremental copies retained. The mod's data lives inside the world folder, so any server that backs the world up was already covering it — this was duplicating that work and filling the disk doing it. There is now a [backup] section in wavedefense-common.toml, and it ships disabled. Turned on, the defaults are one snapshot every 6 hours keeping 3 full and 12 incremental, plus one write at shutdown.
Fixed
FilePathGuard.isInsiderejected every path on installations whose path Java cannot round- trip throughsun.jnu.encoding, breaking all six import/export packets- Backup scheduler no longer starts, and creates no directories, when backups are off
Changed
- New
[backup]config section:enabled(defaultfalse),intervalMinutes(360),maxFullBackups(3),maxIncrementalBackups(12),backupOnShutdown(true) - A shutdown snapshot is written after all saves have flushed, so it captures final state
Known gap
The backup subsystem has no command to trigger or restore a backup — it is scheduler-driven only. Left as is while the feature ships off by default.
Upgrading: no protocol change (still version 13). Compile targets unchanged — Forge 47.2.0, Minecraft 1.20.1, Java 17, official mappings.
This mod has no additional files

