promotional bannermobile promotional banner

Admin instruments: Wave Defense

A versatile PvE and PvP engine featuring wave defense, customizable shops, and three competitive game modes for Minecraft
Back to Files

wavedefense-0.3.0.jar (untest)

File namewavedefense-0.3.0.jar
Uploader
Rayman_GCRayman_GC
Uploaded
Jul 30, 2026
Downloads
23
Size
1.1 MB
Mod Loaders
Forge
File ID
8541039
Type
R
Release
Supported game versions
  • 1.20.1

Curse Maven Snippet

Forge

implementation fg.deobf("curse.maven:wave-defense-1492909:8541039")

Learn more about Curse Maven

What's new

Changelog

## [Unreleased] — gameplay depth: endless, modifiers, difficulty, progression

Four features that give a location a reason to be played more than once. All are
opt-in per location and default to off, so existing arenas behave exactly as
before. Existing saves load unchanged; the new keys simply take their defaults.

### Added — endless mode

- A PvE location can now be marked **endless**: waves never run out, there is no
  victory, and the score becomes *how far did you get*.
- The wave list already cycled internally (`(currentWave - 1) % waves.size()`),
  so this needed no new spawn logic — only the two victory gates now defer to
  the flag.
- Mob health and damage scale by **+N% per completed loop** through the wave
  list, configurable (default 10%). Scaling is **linear, not compounding**:
  compounding turns loop 5 into an unplayable wall, which reads as a bug rather
  than a challenge. Mob *counts* already grow through each wave's existing
  `growthPerWave`, so they are deliberately left out of the loop multiplier.
- Endless runs get their own leaderboard (`PvE_ENDLESS`) ranked by wave reached
  rather than points — the two are not comparable numbers.
- **The record is written on death, not on victory.** Endless never reaches
  `triggerVictory`, so without this an endless location would silently never
  produce a leaderboard entry at all. Non-endless deaths are still unranked:
  dying on wave 3 of 10 is not a result worth listing beside a finished run.

### Added — wave modifiers

- Every *N*-th wave (default 3) rolls a random twist, announced in chat with a
  one-line explanation of what it does. The waves in between run clean, which is
  what makes a modifier feel like an event rather than ambient noise — and gives
  players a wave to recover on.
- Eight modifiers: **Swift**, **Armored**, **Regenerating**, **Enraged**,
  **Tough**, **Phantom** (invisible), **Volatile** (explodes on death), and
  **Venomous** (poisons on hit).
- Admins can restrict the pool per location; selecting none means all are
  eligible, and the editor label says so rather than leaving eight empty boxes
  looking like a misconfiguration.
- **Volatile explosions never damage terrain** (`ExplosionInteraction.NONE`).
  Arenas are hand-built, and a modifier that quietly demolishes the map over a
  few runs would be worse than no modifier at all. The blast still hurts players,
  and it fires on *any* death — an environment kill is just as dangerous to be
  standing next to.

### Added — difficulty presets

- Easy / Normal / Hard / Nightmare, set per location, scaling mob health,
  damage, count, and point rewards.
- **Rewards scale with difficulty**, or nobody would ever pick anything above
  Normal.
- Each tier ranks on its own leaderboard so a Nightmare clear never has to
  compete with an Easy one. Normal keeps the unsuffixed key, so existing records
  stay exactly where they are.
- This composes with, rather than replaces, the existing `WaveAutoScaler`: the
  preset sets the baseline that the adaptive scaler then nudges around, so
  Nightmare stays harder than Easy even after the scaler settles.

### Added — lifetime player progression

- A new per-player profile persists across runs, locations, and restarts:
  waves survived, best wave, kills, points, matches played/won, deaths, playtime,
  and XP.
- Level curve is quadratic (`1 + floor(sqrt(xp / 100))`) — early levels arrive
  fast enough to notice, later ones stay meaningful.
- Stored in `world/data/wavedefense_profiles.dat` through the same atomic,
  debounced write path as locations and leaderboards, so a crash mid-write
  cannot corrupt it.
- Profiles are never pruned: a player returning after months keeps their level.

### Added — tests

- `ProgressionTest` — 18 tests covering difficulty parsing and monotonicity,
  endless loop arithmetic and its linear-not-compounding guarantee, modifier
  interval and pool rolling, the level curve, and profile NBT round-tripping.

### Notes

- 35 new translation keys across all 8 supported locales.
- Version deliberately **not** bumped — 0.3.0 has not shipped yet, so this lands
  on top of it.

## [0.3.0] - 2026-06-19 — HUD fixes, cleanup + concurrent multi-admin editing

A maintenance release that fixes two long-standing HUD bugs reported by
players, pays down tech debt, and lets multiple admins edit locations at the
same time safely. No data-format changes — existing saves load unchanged.

### Fixed — HUD showed stale data (player-reported)

- **The next-wave countdown never counted down.** `PlayerWaveData.timeUntilNextWave`
  was only ever written when a player *joined* a location, and `syncPlayerData`
  ran solely on discrete events (purchase, death, join) — there was no periodic
  sync. The HUD therefore displayed whatever number was captured at join time
  forever, which players saw as the timer "freezing" and the wave-start `0:30`
  lingering on screen.
- **The wave counter never advanced** for the same reason: `setCurrentWave` was
  only called on join (and by `PvpRoundManager` for PvP), never when a PvE
  location moved to the next wave. The HUD stayed on the wave you arrived at.
- Both are fixed by a new `WaveManager.refreshHudState()`, run once per second,
  which derives the live wave number and countdown from the authoritative
  `LocationSession` (`startTimerMs` for the lobby phase, `waveTimerTicks`
  between waves) and syncs each PvE player — **only when a rendered value
  actually changed**, so an idle lobby sends nothing. PvP locations are skipped
  because `PvpRoundManager` already owns and syncs that state.
- **Smooth countdown:** `ClientPlayerDataManager.tickClient()` now decrements the
  timer locally between the server's 1 Hz syncs (each incoming packet resets the
  interpolation window, so the client can never drift more than a second). The
  timer keeps moving even if a sync packet is late.

### Fixed — a crash no longer eats player inventories

- When a player entered an arena their real inventory was stashed in a
  `PlayerBackup` that lived **only in memory**. A clean `/stop` was survivable
  (every player fires a logout event, which surrenders them and hands the gear
  back), but a crash or `kill -9` destroyed those backups along with the
  process — the player logged back in with an arena loadout and no way to
  recover their belongings.
- Live match state is now persisted to `world/data/wavedefense_runtime.dat`:
  on a clean stop, and autosaved every 30 s while any session is running, so a
  crash costs at most half a minute.
- On login, `recoverCrashedPlayer` restores any pending backup — inventory,
  armour, position, health, XP and game mode — and tells the player what
  happened.
- **Sessions are deliberately not resumed** after a restart: the mobs a wave
  spawned are gone and the world has moved on, so a half-finished wave could
  never complete. Abandoned sessions are discarded (and logged) while the
  inventory backups are kept.

### Added — onboarding hints

- Joining a location now prints a short intro: the objective for that specific
  mode (waves to survive / kills to win / last one standing / capture the
  points / rounds to win) and the mod's hotkeys — **V** menu, **B** shop,
  **G** leave, plus **R** to ready up in PvP.
- Previously these keybinds were undiscoverable outside the vanilla controls
  menu, so new players had no idea the mod had a UI at all.
- Toggle with `showJoinHints` in the config for servers that explain it
  themselves. Translated into all 8 supported languages.

### Added — tests & CI

- `PvpRoundStateTest` — 13 tests covering the PvP state machine: phase
  transitions, timer clamping, ready-check bookkeeping, Deathmatch and
  objective win conditions, KotH hold timers, the Battle Royale
  last-survivor/draw split, and NBT round-tripping. This is the highest-risk
  class in the mod and had no coverage.
- `.github/workflows/build.yml` — builds and tests on every push and PR
  (JDK 17, cached ForgeGradle/Minecraft artifacts, JUnit report surfaced in the
  checks tab, jar uploaded as an artifact).

### Changed — HUD status panel

- The three free-floating white `drawString` lines in the bottom-right corner
  (points / wave / timer) are now one grouped panel with a `GuiTheme` backdrop
  and border, matching the editor screens. This fixes readability against bright
  terrain and the "UI needs refining" feedback.
- The countdown gained a **progress bar** that drains as the timer runs out, plus
  urgency colouring — white → amber under 10 s → red under 5 s.
- The panel sits clear of the hotbar and auto-sizes to its widest row.

### Added — concurrent multi-admin location editing

- **Section-level merge saves.** The location editor no longer replaces the
  whole location on Save (which silently clobbered a second admin's edits —
  classic lost-update). It now diffs the working copy against the snapshot it
  opened and sends only the **changed sections** (`MergeLocationPacket`). The
  server merges those keys onto the *current live* location, so two admins
  editing different aspects of the same arena (e.g. one tunes waves while the
  other edits the shop) both keep their work.
- New `data/LocationSection` partitions all 113 persisted NBT keys into the six
  editor sections (General / Gameplay / Area / Economy / Visual / Compat) plus
  a `RUNTIME` group (play-lock, lifetime stats, live points/teams). RUNTIME
  keys are **never** written by an editor save — fixing a latent bug where
  saving the editor mid-match reset live points/stats to the editor's snapshot.
- `LocationSectionTest` enforces the safety invariant: every persisted key maps
  to exactly one section, so a new field can't be added without being placed
  (an unmapped key would silently drop on merge — the test fails loudly).
- Editing **different** locations concurrently already worked; this makes the
  **same** location safe too (different sections merge; same section is
  last-write-wins, acceptable).

### Changed — build / tests / perf

- Enabled JUnit 5 (`useJUnitPlatform()`) in `build.gradle` — the existing
  Mockito/Jupiter tests (and the new section-coverage test) were silently never
  run by Gradle before.
- **Per-tick allocation removed.** The five server managers that scan every
  location each tick (Portal, Zone, InfoPanel, Trigger ×2) now use a new
  non-copying `LocationManager.getAllLocationsView()` (an unmodifiable view of
  the live map) instead of `getAllLocations()`, which since v0.2.66 allocated a
  fresh `ArrayList` copy on every call — ~60 short-lived lists/sec at idle.
  One-off / backup callers keep the defensive-copy variant.

### Removed

- **Legacy location editors deleted** — `LocationEditorScreen` (1609 LOC) and
  `PvpLocationEditorScreen` (1417 LOC), both `@Deprecated(forRemoval)` since
  v0.2.56, are gone. The unified `UniversalLocationEditor` (6 tabs, PvE+PvP)
  has been the default since v0.2.58 and covers every workflow the old screens
  did. **−3026 LOC** of duplicated editor code.
- The `📜 Legacy` button and `editLocationLegacy()` routing were removed from
  the admin menu; the location-row layout reclaimed the freed 35 px for the
  name button.
- Dropped the now-unused `wavedefense.tooltip.edit_location_legacy` lang key
  from all 8 locales (1433 → 1432 keys, still Δ+0 parity).

### Changed

- **`TriggerEvaluator` god-method refactored.** `checkWaveTriggerCondition`
  (130-line switch with nested inventory lambdas, cx/LOC 0.36) was split into
  named, behaviour-identical helpers: `hasDiamondGear`, `hasIronGear`,
  `hasSword`, `anyPlayerHasConfiguredItem`, and `customTriggerValue` (the last
  also de-duplicates the MOBS_KILLED_N / WAVES_SURVIVED_N value-lookup blocks).
  The switch is now a clean dispatcher.
- **Particle resolution de-duplicated.** Three copies of `resolveParticle`
  (in `BattleRoyaleManager`, `BoundaryManager`, `CapturePointManager`) had
  drifted apart with inconsistent fallback tables. Consolidated into a single
  `wave/ParticleHelper.resolveParticle(id, fallback)` that merges the union of
  all handled particles, so border / boundary / capture-point visuals now
  resolve the same set consistently. Each caller passes its own default
  (FLAME for the BR border, SMOKE for zones). Removed the now-orphaned
  `BuiltInRegistries` / `ResourceLocation` imports from the three managers.

### Deferred (intentionally not in this release)

- **Lang-key pruning** — ~360 keys appear unused to a static scan, but many are
  built dynamically (`key + suffix`) and pruning risks runtime
  missing-translation breakage. Skipped; orphan keys are ~50 KB and harmless.
- Unit tests + CI, and the `SavedData` persistence migration, remain on the
  roadmap for a later release.

This mod has no additional files