wavedefense-0.5.3.jar
Curse Maven Snippet
What's new
Changelog
[Unreleased] — audit fixes
Follow-up work on the findings of the full read-only audit, plus three problems found in play. No version bump yet.
Upgrading: the network protocol moved to version 11 (RenameLocationPacket was appended), so client and server must be updated together.
Fixed — a player with 973 points was told 900 was too expensive
Two separate defects, both rooted in the same thing: a location's identity is its name, but several places held a reference to the object instead.
- The editor's Save wiped live points.
UpdateLocationPacketrebuilds the whole location from the tag the client sends — and that tag includesplayerPointsas the editor client saw it when it opened. Anything players earned since was overwritten.MergeLocationPackethas always protected these runtime keys; this path never did. It does now. - Everyone kept reading the old object.
LocationManager.updateLocationswaps the entry in its map, but everyPlayerWaveDatastill pointed at the instance the player joined with. The HUD read its points from there; the shop read them from the map. The two numbers drifted apart, which is exactly what the report described.updateLocationnow rebinds live players onto the new instance and pushes a corrected figure immediately.
New test RuntimeStateProtectionTest fails if either handler stops consulting the runtime classification, or if playerPoints ever leaks into an editor section.
Fixed — deleting a shop item did nothing, and an imported shop looked like it had not arrived
Three defects stacked on top of each other here; each alone was enough to make shop editing feel broken.
- The chunked list channel was throttling itself to death.
ReplaceLocationListPacketcarried a 50 ms per-packet rate limit, but it is one slice of a burst:sendListemits every chunk back to back, then the editor does the same for the next list. So chunk 0 went through and everything after it was dropped — a shop small enough for one chunk saved but lost its shop points, and a shop needing two chunks never completed at all, so the whole save was discarded 30 s later. The limit is gone; a cap on concurrent transfers per player and on chunks per transfer bounds the ingress instead, which is the right shape for a stream. - Deleting was the only shop operation that was not sent to the server. Adding and editing have gone through
ShopItemOpPacketsince the shop got its own per-item protocol; deleting only mutated the client's copy and depended on that broken save. It is now the same kind of immediate server operation as the other two. - The shop editor read a stale location for its whole lifetime. A shop is stripped out of the location broadcast, so the editor asks for one on open — and the reply arrives as a different object, which replaces the entry in the client cache while the screen keeps the original. That is why importing a shop from another location appeared to do nothing: the editor was showing the pre-import snapshot, and saving from it pushed that snapshot back, undoing the import. The editor now follows the sync.
Added — shop tiles can be sorted
- A cycle button in both the player's shop and the admin's editor: as configured, by name, by mod, by type, or by price. A generated Tacz pack arrives in the generator's order, which is no order at all at several hundred entries.
- "As configured" stays the default, because a hand-built shop is usually arranged deliberately and that intent is worth keeping until someone asks otherwise.
- There is no separate "by datapack" mode and none is missing: a datapack's items carry its own namespace exactly as a mod's do, so by mod covers both. Nothing on the item records which datapack loaded it.
- Only the view is reordered. Buy, sell, edit and delete all address an entry by its index in the real list, so sorting can never make a tile act on the wrong item —
ShopSortTestfails if a sort ever drops, duplicates or reorders anything but the view.
Added — locations can be renamed
- A rename could not ride on the normal save: that handler resolves its target by the name inside the payload, so an edited name matched nothing and the save was dropped without a word. It now has its own packet, its own button on the editor's General tab, and
/wda location rename <old> <new>. - Leaderboard history moves with the name instead of being stranded.
- Refused while a run is in progress, with the reason stated. The name is the session key and the string stamped on every spawned mob; swapping it mid-run would orphan all of them at once — those mobs would stop paying points and never be cleaned up.
- The editor closes after a rename, because every other change it sends is addressed by the old name and would silently stop applying.
Added — admin commands for the things that had none
/wda points give|set|get <players> <amount> [location]— there was no way to hand out points for testing, compensate a player after a crash, or even see what the server thinks someone has. With nolocationit uses the arena the player is standing in./wda location list— names, mode, and whether a run is live; click a row to fill in the info command./wda location create <name>and/wda location delete <name>(delete asks to confirm and sends everyone out first, so nobody is left holding a location that no longer exists)./wdanow exposes the whole tree. It offered three of sixteen subcommands; everything else lived only under/wavedefense-admin, which is long enough that nobody finds it by accident. Each subcommand still checks its own permission level — this widens discoverability, not access.- Every
<location>argument now tab-completes from the locations that actually exist, including on the subcommands that already existed.
Fixed — items no longer vanish into a full inventory
Inventory.addreturns a boolean saying whether the stack fitted, and mutates the stack it is given so a partial fit leaves the remainder behind. Five places called it and dropped that answer: shop purchases, completion rewards, starting kits (PvE and PvP) and per-team starting items. With a full pack the item simply ceased to exist — you paid the points and got nothing, or finished a run and never saw the reward, which is exactly the moment a pack is most likely to be full.- All five now go through one helper,
util/ItemGrant: whatever does not fit lands at the player's feet, the way vanilla behaves everywhere else.
Fixed — a location with totalWaves = 0 handed out the completion reward on entry
- Victory is decided by
currentWave > totalWaves, andcurrentWavestarts at 1. At zero the very first tick counted as "all waves cleared": players walked in, instantly "completed" the location and collected the completion points and reward items without a single mob spawning. The wave-config validator never looked at the field, so nothing caught it. setTotalWavesnow clamps to 1–9999,setTimeBetweenWavesto 0–86400 (zero stays legal — it means "next wave immediately"), andsetCompletionPointsRewardcannot go negative.LocationSerializer.loadroutes these three through the setters instead of assigning the fields directly, so a file saved by an older build or edited by hand is repaired on load.- The validator now also rejects
totalWaves < 1outright, as a second line of defence.
Fixed — shop packets trusted whatever the client sent
- Neither
PurchaseItemPacketnorSellItemPacketchecked that the sender was actually in the location, or anywhere near the shop point they were trading with. The client's shop screen enforces both, but the packet alone did not — so a hand-sent packet could trade in any location, from anywhere in the world, without ever entering. - Both now go through
network/ShopAccess, with a 3-block grace on the point radius so a step or a lag spike does not reject a purchase the player considers legitimate.
Changed — silence removed from three places that hid real misconfigurations
- Config warnings were logged at DEBUG, i.e. seen by nobody. They are the list that explains "why is my location behaving strangely" — unknown entity types, waves with no mobs, absurd timers. They now log at WARN, once per run rather than once per player.
PLAYER_HAS_ITEMtriggers swallowed bad item ids. A typo likemincraft:diamondmeant the trigger silently never fired, with nothing in the log. Each unresolvable id is now reported once. The same loop existed in three copies and is now one method.- Trigger waves swallowed spawn failures. Picking an entity type that is not a mob (an arrow, a dropped item) made the wave quietly not happen. Failures are now collected per mob type and reported once per wave, instead of per attempted spawn.
- Five subsystems in the main tick loop caught their own errors and said nothing — HUD refresh, teammate sync, bbox outline, mob retargeting and autosave.
WaveManageralready hadsafeTick, which contains a failure and reports it at most once per 10 s per subsystem; those five now use it. A frozen HUD or an autosave that has been failing for an hour now leaves a trace.
Changed — dead code removed from the wave-config validator
- The class carried eight fluent threshold setters that nothing called, and a whole adaptive-performance subsystem — tick sampling, an overload flag, a scale factor — whose result no code ever read. Real auto-scaling is done by
WaveAutoScaler. All of it is gone; the thresholds are constants. - Three rules that were declared and never enforced now actually run: duplicate wave numbers, mob types per wave against
maxMobTypes, and spawn points againstmaxMobSpawns.maxBossMobsPerWavewas dropped — there is no boss flag in the data model for it to check.
Performance
BoundaryManager.tickBorderParticlesran a filtered stream over every online player once per boundary location, every second — O(locations × online). It now groups players in a single pass and allocates nothing when nobody is in a location.
Internal
- Eighteen new lang keys across all 8 locales (1537 each, parity checked).
- Test suite: 101 tests, all passing.
This mod has no additional files

