
HungerGameSSS
HungerGameSSS is a high-performance Hunger Games plugin featuring unique Legendary Weapons with special abilities, dynamic gameplay systems, and full arena customization — built for competitive PvP servers.
- Загрузки
- 866
- Подписчики
- 3
- Обновлён
- 1 июля 2026 г.
- Лицензия
- Apache-2.0
Опубликован 1 января 2026 г.
HungerGamesSSS
A full-featured Hunger Games survival plugin for Paper 1.21+
v4.6.4 — Architecture & Gameplay Bug-Fix Patch released 2026-06-04. 11 structural bugs fixed: contradictory
ArenaEndEventcomments, duplicateLootTableManagerandCornucopiaBuilderinstances causing config-reload drift,startMatch()bypassing the FSMtransition()contract, spawn cages placed inside terrain at Y=100, players falling through piston floors due to async physics, deadcheckCompatibility()method, emptyMatchSessionclass, stale nested language directory, and disconnected players holding matches open indefinitely (now auto-expired after configurablegame.reconnect-timeoutseconds, default 60). No database migrations — drop-in replacement for 4.6.3. Upgrade strongly recommended. See CHANGELOG.md for the full list.
45 legendary weapons. Procedural dungeons. Last player standing.
📖 Table of Contents
- ✨ Features
- 📦 Requirements
- 🚀 Installation
- 🏟️ Arena System
- ⏱️ Match Lifecycle
- 🎮 Game Modes
- ⚡ Legendary Weapons
- 🏰 Dungeons
- 🌽 Cornucopia Builder
- 🎁 Feast Event
- 👥 Teams
- 💾 Persistence
- 🌐 Localisation
- ⌨️ Commands & Permissions
- 🔌 Developer API
- 🔍 Troubleshooting
- 📄 Credits
✨ Features
🏟️ Arena System
- Multi-arena support — run unlimited concurrent arenas in separate worlds
- Per-arena toggles — PvP, lifesteal, hardcore, veinminer, nether, end
- Glass cage countdown — players locked in cages at spawn until the match begins
- Auto-reset — arena world and all blocks fully restored after each match
- Legendary spawn points — holograms float above each marked item location
⚡ 45 Legendary Weapons
| Category | Count | Examples |
|---|---|---|
| Swords & Melee | 15 | Cloud Sword, Excalibur, Dragon Katana |
| Bows & Ranged | 8 | Artemis Bow, Phantom Longbow, Warden Crossbow |
| Staves & Magic | 8 | Void Staff, Lich Staff, Hypnosis Staff |
| Tridents & Special | 9 | Aiglos, Mjolnir, Ravager Horn |
| Armor | 3 | Hades' Helm, Hermes Boots, Reinforced Elytra |
| Utility / Healing | 2 | Fountain of Youth, Warpick |
✔️ Each weapon has unique active + passive abilities
✔️ All stats tunable in legend-items/<id>.yml
✔️ Hot-reload with /perf reload — no restart needed
🏰 Procedural Dungeons
| Type | Terrain |
|---|---|
| 🕷️ Spider Nest | Cobweb cave |
| 💀 Crypt | Stone brick chamber |
| ⛏️ Gold Mine | Mineshaft tunnels |
| 🔥 Lava Temple | Nether-brick structure |
| 🧊 Ice Temple | Packed-ice biome |
| 🏛️ Surface Temple | Sandstone ruin |
| 🗼 Surface Outpost | Watchtower |
| 🌤️ Sky Island | Floating grass |
| 🏯 Floating Castle | Castle 30 blocks up |
✔️ No external schematics required ✔️ Loot configurable per dungeon type ✔️ All blocks restored after match ends
📈 Match Lifecycle
WAITING → STARTING → PLAYING → DEATHMATCH → ENDED
| Phase | Description |
|---|---|
| ⏳ Waiting | Players queue in lobby |
| 🔢 Starting | Countdown; players teleport into cages |
| ⚔️ Playing | Grace period → main phase → feast → border shrink |
| 💥 Deathmatch | Border closes; final players fight |
| 🏆 Ended | Winner announced; arena fully reset |
✔️ Disconnect/reconnect mid-match restores last location, inventory, and scoreboard
🎮 Game Modes
❤️ Lifesteal
Kill a player → they drop a heart item. Pick it up to raise your max HP. Convert hearts back with /withdraw <amount>.
💀 Hardcore
Eliminated players are kicked instead of moved to spectator mode.
⛏️ Veinminer
Sneak + break an ore → chain-mines the entire vein. Max vein size is hot-configurable.
🌽 Cornucopia Themes
13 procedural themes for the arena centrepiece:
classic · ultimate · speedsilver · ancient · void · royal · industrial · modern · crystal · jungle · end · nether · grand
🌐 Localisation
10 built-in language files:
🇬🇧 en · 🇻🇳 vi · 🇷🇺 ru · 🇩🇪 de · 🇫🇷 fr · 🇪🇸 es · 🇵🇹 pt · 🇯🇵 ja · 🇰🇷 ko · 🇨🇳 zh
Players change language in-game with /hg lang <code>.
💾 Persistence — SQLite / MySQL
All arena state, player stats, team data, and match history persist across restarts. Switch backends in config.yml:
database:
type: sqlite # or "mysql"
sqlite-file: database.db
mysql:
host: localhost
port: 3306
database: hungergames
username: root
password: password
✔️ All writes are async ✔️ Auto schema migration on startup — no data loss
📦 Requirements
| Component | Version |
|---|---|
| Java | 21+ |
| Paper | 1.21+ |
| PlaceholderAPI | Optional |
| Vault | Optional |
| ProtocolLib | Optional |
| FastAsyncWorldEdit | Optional (.nbt / .schem support) |
🚀 Installation
- Download the
.jar - Drop it into
plugins/ - Start the server — configs generate automatically
- Stand at the centre of your arena →
/hg create <name> /hg setcornucopia <name> classic- Walk to each spawn point →
/hg addspawn <name>(repeat 2–24×) - Stand in your lobby →
/hg setlobby /hg start <name>
Players join with /hg join <name>.
🏟️ Arena System
Per-arena toggles (changed live with /hg setuparena <name> <flag>):
| Flag | Default | What it does |
|---|---|---|
pvp |
on | Enable / disable PvP damage |
lifesteal |
off | Players drop heart items on death |
hardcore |
off | Eliminated players are kicked |
veinminer |
off | Sneak + break to chain-mine ores |
nether |
on | Isolated Nether world per arena |
theend |
on | Access to The End dimension |
Legendary spawn points:
/hg setlegendary <arena> <itemId> — mark spawn at your feet
/hg deletelegendary <arena> — remove nearest spawn (within 5 blocks)
⚡ Legendary Weapons
🗡️ Swords & Melee
| Item | Ability |
|---|---|
| ☁️ Cloud Sword | Walk on clouds; fall immunity; armor shred on hit |
| 💛 Midas Sword | +1 damage per kill (stacks); golden particle on kill |
| 💎 Emerald Blade | Deposit emeralds to upgrade damage; sneak+click GUI |
| 🌑 Shadow Blade | Sneak: teleport behind target; click: invisibility + Speed III |
| 💀 Reaper Scythe | On hit: steal buffs and heal; right-click: AOE sweep |
| 🐉 Dragon Katana | Passive Speed I; right-click: Dragon Fireball dash |
| ✨ Excalibur | 5s true invincibility (all damage cancelled); 50% reflect |
| 🪨 Golem Hammer | Leap + shockwave on landing; passive knockback resistance |
| 🌋 Magma Club | Lava pillar AOE; sneak+click: volcanic eruption |
| ⛏️ Warpick | On-hit 3×3 explosion; on critical hit: armor durability shred |
| 🖤 Obsidian Dagger | Sneak+click: Resistance V shield; click: meteor strike |
| 🔴 Crimson Chainsword | Shred stacks passive; right-click: Chainsaw Rampage burst |
| 👻 Soul Gauntlet | Collect souls on hit; right-click: Soul Blast; sneak: Ground Slam |
| 🪝 Harpoon Launcher | Fires a harpoon that hooks and pulls the target toward you |
🏹 Bows & Ranged
| Item | Ability |
|---|---|
| 🌙 Artemis Bow | 5 homing arrows; lightning proc; Sky Strike sneak ability |
| 👁️ Phantom Longbow | Invisible arrow; caster invisible on fire; target gets Darkness |
| 🌀 Ender Bow | Sneak: Void Dash; arrow hit: swap positions with target |
| 🧪 Toxic Crossbow | High-velocity bolt; poison cloud on impact |
| 🦾 Warden Crossbow | Sonic Boom projectile; converts blocks to Sculk on hit |
| 🌑 Corrupted Crossbow | Tick-based poison cloud bolt; Multishot enchant support |
| 🔱 Guardian Cannon | Passive Dolphins Grace in water; continuous stasis laser |
| 🐡 Pufferfish Cannon | Bouncing pufferfish; explodes with Poison + Nausea |
🔮 Staves & Magic
| Item | Ability |
|---|---|
| 🧊 Lich Staff | 4 ammo ice balls; 30s reload; passive Frost Walker |
| 🌌 Void Staff | Toggle Portal / Shulker Mode; independent cooldowns per mode |
| 🐛 Hypnosis Staff | Hypnotise up to 5 mobs; minions target your enemies |
| 💥 Villager Wand | Aim at block → explosive slime bomb detonates |
| 📓 Death Note | /dn <player>: instant kill (3 uses, per-target cooldown 25s) |
| 🌑 Sculkweaver's Lantern | Sculk bomb: Darkness + Slowness lingering zone |
| ❄️ Horn of Winter | Blizzard: continuous Freeze + Slowness in large radius |
| 🔔 Ghastly Whistle | Rideable Ghast companion; auto-fireball every 2s; 20s duration |
🌊 Tridents, Launchers & Special
| Item | Ability |
|---|---|
| 🔱 Aiglos | Trident: explosive throw + AOE freeze; melee stats via attributes |
| 🌀 Wither Sickles | Auto dual-wield offhand; throw (CD 7s); AOE Wither + lifesteal on hit |
| ⚡ Mjolnir | Throw (returns); melee hit: lightning strike |
| 🦏 Ravager Horn | Mount a Ravager; sneak+click: Stampede wave |
| 🔬 Shrink Ray | Left-click: shrink target; right-click: enlarge target |
| 🔴 Armadillo Detonator | Shoot up to 3 armadillos; left-click: detonate all |
| 🐝 Beehive Blaster | Launches 4 angry bees that auto-target nearest enemy |
| 🌿 Ribbit Reel | Hit block: pull yourself; hit entity: pull them to you |
| 💧 Fountain of Youth | Passive +1 HP/s while held; right-click: 30s healing zone for allies |
🛡️ Armor
| Item | Slot | Ability |
|---|---|---|
| 🪖 Hades' Helm | Head | Night Vision; invisibility while sneaking; fire aura; Wither attacker on hit received |
| 👟 Hermes Boots | Feet | Speed II + Jump Boost; Hermes Dash; full fall immunity |
| 🪂 Reinforced Elytra | Chest | Particle trail while gliding; Sonic Boom boost; landing explosion |
🏰 Dungeons
Dungeons spawn procedurally during a match — no schematic files needed.
dungeons:
mob-hp: 60.0
boss-hp: 100.0
spawn-chance: 0.1
lava-temple:
enabled: true
mob-hp: 40.0
fire-ticks: 100
🎁 Feast Event
High-tier chests spawn at the Cornucopia after a configurable timer.
arena:
feast:
enabled: true
time-minutes: 20
chest-count: 8
announce-minutes: [15, 10, 5, 3, 2, 1]
👥 Teams
| Command | Description |
|---|---|
/team create <name> |
Create a team (you become leader) |
/team invite <player> |
Send an invite (expires 60s) |
/team join <name> |
Join an open team |
/team leave |
Leave your team |
/team open |
Toggle open / invite-only |
/team disband |
Disband team (leader only) |
/tc <message> |
Team chat |
⌨️ Commands
/hg — Main Command
| Sub-command | Permission | Description |
|---|---|---|
join <arena> |
hoplitebr.hg |
Join an arena lobby |
leave |
hoplitebr.hg |
Leave the current arena |
lang <code> |
hoplitebr.hg |
Change your language |
create <name> |
hoplitebr.admin |
Create a new arena |
start <arena> |
hoplitebr.admin |
Force-start a match |
setcornucopia <arena> <theme> |
hoplitebr.admin |
Set and build the Cornucopia |
addspawn <arena> |
hoplitebr.admin |
Add player spawn at your feet |
setuparena <arena> <flag> |
hoplitebr.admin |
Toggle per-arena flags |
setlegendary <arena> <id> |
hoplitebr.admin |
Mark legendary spawn |
setlobby |
hoplitebr.admin |
Set post-match lobby location |
admin give <player> <item> |
hoplitebr.admin |
Give a legendary item |
reload |
hoplitebr.admin |
Reload all config files |
Other Commands
| Command | Permission | Description |
|---|---|---|
/withdraw <amount> |
hoplitebr.withdraw |
Convert lifesteal hearts to items |
/perf <stats|optimize|reload|gc|chunks> |
hoplitebr.admin |
Performance tools |
/structure <reset|stats> |
hoplitebr.admin |
Structure management |
/locate <name> |
hoplitebr.hg |
Locate a placed structure |
/migrate |
hoplitebr.admin |
Migrate legacy legendary PDC tags |
/debug |
hoplitebr.admin |
Dump debug info to console |
/dn <player> |
hoplitebr.dn |
Death Note kill command |
🔐 Permissions
| Permission | Default | Description |
|---|---|---|
hoplitebr.hg |
true |
Basic game commands |
hoplitebr.team |
true |
Team commands |
hoplitebr.tc |
true |
Team chat |
hoplitebr.withdraw |
true |
Lifesteal heart withdrawal |
hoplitebr.dn |
true |
Death Note kill command |
hoplitebr.admin |
op |
All admin / setup commands |
🔧 Configuration
Config files:
config.yml— global settings, arena defaults, database, performanceloot.yml— chest loot tiers (common / uncommon / rare)scoreboard.yml— scoreboard layout and update intervallegend-items/<id>.yml— per-weapon stats, recipe, lore, enchantmentsdungeons.yml— dungeon type toggles and mob HPcornucopia.yml— Cornucopia build presetslanguages/<code>.yml— message translations
🔌 Developer API
// Get the arena a player is in (null if not in any)
Arena arena = HopliteAPI.getPlayerArena(player);
// Access the plugin instance
Main plugin = HopliteAPI.getPlugin();
Custom Events:
| Event | Fired when |
|---|---|
ArenaStartEvent |
Match transitions STARTING → PLAYING |
ArenaEndEvent |
Match ends (winner declared) |
PlayerEliminatedEvent |
A player is eliminated |
DungeonSpawnEvent |
A dungeon is generated |
🔍 Troubleshooting
Legendary abilities not working
- Confirm the player is in an active arena (
PLAYINGorDEATHMATCH) - Check
hoplitebr.adminpermission — OP always bypasses ability checks - Run
/debugand check console for[Ability]log lines withdebug: true
Countdown cancels immediately on start
- Fixed in 4.5.5 (BUG-CD-01)
- Update to latest version
Feast never spawns in second match
- Fixed in 4.5.5 (BUG-FM-01)
- Update to latest version
Stats reset to zero on every join
- Fixed in 4.5.7 (BUG-JOIN-02)
- Update to latest version
Ability listeners fire twice
- Fixed in 4.5.7 (BUG-REG-01)
- Was caused by
registerEvents()in ability constructors
Italic text on legendary item names
- Fixed in 4.5.4 (BUG-FONT-01)
- Paper 1.20.5+ changed the default rendering style for custom item names
Players not teleported to centre at Deathmatch start
- Fixed in 4.6.2 (BUG-1 / GameTask FSM bypass)
GameTaskwas callingarena.setState(DEATHMATCH)directly, bypassingMatchService.transition(). The teleport and border-collapse logic insidescheduleDeathmatchTask()never ran.
World border ignores my config values — always uses 1000 blocks
- Fixed in 4.6.2 (BUG-2 / mismatched config keys)
GameTaskwas readinggame.initial-border-sizeandgame.border.initial-size(wrong keys). The canonical key isgame.border-initial-size. The game-loop always fell back to the hardcoded default of 1000.
Border flickers / jumps every second during PLAYING phase
- Fixed in 4.6.2 (BUG-4 / dual border control)
MatchService.scheduleBorderTask()andGameTask.handleWorldBorder()both wrote to the WorldBorder simultaneously. The per-tick recalculation overrode the smooth periodic animation every second.scheduleBorderTask()has been removed;GameTaskis now the sole border authority.
Legendary item hologram shows material name (e.g. "WOODEN_SWORD") instead of weapon name
- Fixed in 4.6.2 (BUG-5 / legacy display-name API)
Arena.getActiveLegendaryLocations()calledmeta.getDisplayName()(legacy API) which always returns""for items using the Adventure Component API. The fallback ofrecipe.getType().name()was always reached.
Game doesn't end when a full team is the last survivor
- Fixed in 4.6.2 (BUG-6 / checkWin team logic)
checkWin()only ended the game whenaliveCount == 1. If the last 2+ survivors were all on the same team, the game continued and forced teammates to kill each other. Now checks if all alive players share a team.
Arena doesn't reset properly after double `/hg start`
- Fixed in 4.6.2 (BUG-3 / BUG-8 — countdown tasks not tracked)
- Both
startMatch(Arena, int)andstartCountdown()created countdownBukkitRunnables without storing the task reference.cancelAllTasksFor()could not cancel them, leading to two countdown timers running concurrently andstartArena()being called twice.
/reload breaks the plugin
- Always restart the server — never use
/reload
❓ FAQ
Can I run multiple arenas at once?
Yes — each arena is fully isolated in its own world instance.
Do I need FastAsyncWorldEdit?
No. It is only required if you want to paste custom .nbt / .schem structures.
Can players on the same team damage each other?
No — all legendary weapons include friendly-fire checks.
How do I add a custom language?
Create languages/<code>.yml — copy en.yml as a base and translate. Players switch with /hg lang <code>.
Does it support Spigot?
No — Paper 1.21+ is required.
📄 Credits
Author: Duong2012G — Modrinth
Several weapon mechanics were inspired by DemoRaLegendary Weapons by Demora (Apache 2.0 licence). No source code was copied — only gameplay concepts were referenced.
🔗 DemoRaLegendary Weapons: https://modrinth.com/plugin/demoralegendaryweapons
Open source — contributions welcome.
Ченджлог
4.6.4Релиз1.21.9, 1.21.10, 1.21.11 · 4 июня 2026 г.
[4.6.4] — Architecture & Gameplay Bug-Fix Patch (2026-06-04)
11 bugs fixed across MatchService, Main, ArenaManager, Arena, listeners and resources.
No database migrations or config changes required — drop-in replacement for 4.6.3.
One new optional config key:game.reconnect-timeout(default60, seconds).
4.6.3Релиз1.21.9, 1.21.10, 1.21.11 · 3 июня 2026 г.
[4.6.3] — Ability System Bug-Fix Patch (2026-06-03)
Full deep-dive analysis of all 45 ability implementations.
13 confirmed bugs fixed across 12 files (2 framework, 11 ability-level).
No database migrations or config changes required — drop-in replacement for 4.6.2.
4.6.2Релиз1.21.9, 1.21.10, 1.21.11 · 22 мая 2026 г.
[4.6.2] — Gameplay Bug Fix Patch (2026-05-23)
Fixed — 9 bugs across 4 files
These bugs were identified through source-code analysis of v4.6.1. None require a database migration or config change.
🔴 Critical — Broken every match
BUG-1 ·
GameTask— PLAYING→DEATHMATCH transition bypasses FSM (GameTask.java)GameTask.handlePhaseTransitions()calledarena.setState(MatchState.DEATHMATCH)directly instead ofplugin.getMatchService().transition(arena, MatchState.DEATHMATCH). Bypassingtransition()meant:ArenaDeathmatchEventwas never fired — external plugins received no notification.scheduleDeathmatchTask()was never called — players were not teleported to the Cornucopia centre at Deathmatch start.- The rapid border collapse to 10 × 10 over 60 s never happened.
- Scoreboard was not refreshed via
updateScoreboards().
Fix: replaced
arena.setState(MatchState.DEATHMATCH)withplugin.getMatchService().transition(arena, MatchState.DEATHMATCH).BUG-2 ·
GameTask— three different config keys used for the same border size (GameTask.java)Three separate code paths read the "initial border size" value using three different config keys:
config.ymldeclares:game.border-initial-size: 2000(the canonical key)MatchService.javareads:"game.border-initial-size"✅ correctGameTask.javaline 90 read:"game.initial-border-size"❌ key does not exist → fallback1000.0GameTask.javaline 100 read:"game.border.initial-size"❌ key does not exist → fallback1000.0
Effect: the game loop always set the initial border to 1 000 blocks regardless of the admin's configured value. Any server with
border-initial-sizeset above or below 1 000 saw unexpected border behaviour from the very first second of a match.Fix: unified both
GameTaskreads to use"game.border-initial-size".BUG-3 ·
MatchService.startMatch(Arena, int)— countdownBukkitRunnablenot stored (MatchService.java)The overload
startMatch(Arena arena, int countdownSeconds)created a countdownBukkitRunnablebut discarded the returnedBukkitTask. The task was never added tocountdownTasks, socancelAllTasksFor()could not cancel it. CallingstartMatch()twice on the same arena (e.g. two rapid/hg startcommands, or a player join triggering auto-start while a manual start was in progress) launched two concurrent countdowns. Both reached zero and each calledstartArena(), resulting in the match starting twice — players teleported twice, inventory cleared twice, cage system entered an inconsistent state.Fix: stored the returned
BukkitTaskincountdownTasks.put(arena.getName(), task).
🟡 Medium — Visible gameplay defects
BUG-4 ·
MatchService+GameTask— WorldBorder controlled by two systems simultaneously (MatchService.java)MatchService.transition()(called when enteringPLAYING) scheduledscheduleBorderTask(), a periodic timer that shrank the border every N minutes. At the same time,GameTask.handleWorldBorder()ran every server tick duringPLAYINGand recalculated the border size via smooth linear interpolation, then calledWorldBorder.setSize(). The per-tick recalculation fired every second and silently overrode the periodic task'ssetSize(size, duration)smooth animation, causing the border to visibly flicker or jump rather than shrink smoothly.Fix: removed the
scheduleBorderTask(arena)call fromMatchService.transition(PLAYING).GameTask.handleWorldBorder()is now the sole authority for border size — it provides smoother and more precise interpolation than the coarse periodic approach.BUG-5 ·
Arena.getActiveLegendaryLocations()— hologram names always show material ID (Arena.java)The method read
recipe.getItemMeta().getDisplayName()(legacy Bukkit API). All legendary items in this plugin have their display names set via the Adventure Component API (meta.displayName(Component)). The legacygetDisplayName()method returns""for any such item, sohasDisplayName()was alwaysfalse, and every legendary hologram fell back to showingrecipe.getType().name()— raw material names likeWOODEN_SWORDinstead of the intended weapon name (e.g.Excalibur).Fix: replaced
getDisplayName()/hasDisplayName()with Adventure'sPlainTextComponentSerializerto extract the plain-text string from the Component display name.BUG-6 ·
MatchService.checkWin()— game doesn't end when a full team is the last survivor (MatchService.java)checkWin()ended the game whenaliveCount == 1. In team mode, if the final two (or more) surviving players were all on the same team,aliveCountwas ≥ 2, so no win was declared and the game ran indefinitely or until the Deathmatch timer expired — forcing teammates to kill each other.Fix: after counting alive players, if
aliveCount > 1the method now checks whether all surviving players share a single team identifier. If every alive player belongs to the same team (or all are solo/no-team and there is only one unique identity),endGame()is called immediately.
🟢 Minor — Low-severity improvements
BUG-7 ·
TeamManager— deprecatedChatColorusage produces compile warnings (TeamManager.java)TeamManagerandTeamDatauseorg.bukkit.ChatColorfor team color storage, which is deprecated on Paper 1.21 in favour ofnet.kyori.adventure.text.format.NamedTextColor. Migrating the underlying storage type would require a database schema change andDatabaseManagermigration; that is deferred to a future version. Added@SuppressWarnings("deprecation")onloadColors()with aTODOcomment documenting the planned migration path, silencing the compiler warnings without any behaviour change.BUG-8 ·
MatchService.startCountdown()— countdown task not stored, leaks on arena reset (MatchService.java)startCountdown(Arena, List<Player>)(called from the mainstartMatch(Arena, List<Player>)flow) created a countdownBukkitRunnablebut did not add the task tocountdownTasks. If the arena was reset before the countdown finished (e.g. all players left), the orphaned task continued ticking until it reached zero and calledreleasePlayers()on a now-empty arena — potentially transitioning a WAITING arena to PLAYING with no players.Fix: stored the returned task in
countdownTasks.put(arena.getName(), taskRef[0]).BUG-9 ·
MatchService— cage cleanup storesBlockobjects instead ofLocation(MatchService.java)arenaCageswas declaredMap<String, List<Block>>.Blockobjects hold a direct reference to the chunk at the time they were obtained. If chunks unload and reload beforebreakGlassCages()runs, the staleBlockreference may report an incorrectgetType()— the GLASS check would fail and the cage blocks would not be cleared, leaving orphaned glass geometry in the arena world.Fix: changed
arenaCagestoMap<String, List<Location>>.breakGlassCages()now callsl.getBlock()at removal time, always querying the current (guaranteed-loaded) chunk state. BothcreateJoinCage()andteleportPlayersAndCreateCages()updated to storel.clone().
Changed
pom.xml— version4.6.1→4.6.2plugin.yml— version4.6.1→4.6.2; description updatedpaper-plugin.yml— version4.6.1→4.6.2
4.6.1Релиз1.21.9, 1.21.10, 1.21.11 · 15 мая 2026 г.
[4.6.1] — Critical Bug Fix Patch (2026-05-15)
Fixed — 9 bugs across 7 files
Critical — Plugin fails to enable
BUG-PPL-01 ·
paper-plugin.yml—loader:references a class that does not exist (paper-plugin.yml) The file declaredloader: me.duong2012g.hungergamessss.PaperPluginLoader, but this class was never created. Paper attempted to instantiate it during plugin loading, threwClassNotFoundException, and the plugin failed to enable entirely. Fix: removed theloader:line. A customPluginLoaderis not needed for this plugin's feature set; standard Paper loading works correctly.BUG-DB-01 ·
DatabaseManager— SQLite JDBC driver and MySQL connector not shaded into JAR (pom.xml)DatabaseManagercalledconfig.setDriverClassName("org.sqlite.JDBC")and supported MySQL mode, but neitherorg.xerial:sqlite-jdbcnorcom.mysql:mysql-connector-jappeared as Maven dependencies. The built JAR had no driver classes, causingClassNotFoundException: org.sqlite.JDBCon every server startup that uses the default SQLite config. Fix: added both dependencies topom.xmland configured the Maven Shade Plugin to relocate them underme.duong2012g.hungergamessss.libs.{sqlite,mysql}to avoid classpath conflicts with other plugins.
High — Gameplay-breaking
BUG-DB-02 ·
DatabaseManager.updateSchema()—games_playedcolumn added toarenastable instead ofplayers(DatabaseManager.java)updateSchema()iterated a single column array and checked all entries against thearenastable.games_playedis a player stat column that belongs inplayers. On fresh installs theplayerstable lacked the column;savePlayerStats()/loadPlayerStats()threwSQLException: no such column: games_playedwhenever stats were read or written. Fix: split the migration into two separate loops —arenaColumns[]against thearenastable andplayerColumns[]against theplayerstable. Each column is now checked and added against the correct table.BUG-AM-02 ·
AbilityManager.createItem()—unbreakableflag silently overwritten (AbilityManager.java) The unbreakable block retrieved a newItemMetacopy fromitem.getItemMeta(), setunbreakable(true)on it, calleditem.setItemMeta(unbrMeta)— but then the outer code calleditem.setItemMeta(meta)with the original meta object (which did not haveunbreakableset). The secondsetItemMetasilently discarded the flag. Legend items markedunbreakable: truein YAML still lost durability in-game. Fix: callmeta.setUnbreakable(true)directly on the existingmetaobject before the finalitem.setItemMeta(meta)call. The duplicateitem.getItemMeta()call is removed.BUG-AM-03 ·
AbilityManager.createItem()— localization fallback condition never true (AbilityManager.java) The fallback for missing legend name/description keys compared the return value ofgetMessage(key)against the raw key string:if (localizedName.equals("legend_" + key + "_name")).MessageManager.getMessage()never returns the raw key — it returns"§cMessage not found: <key>"— so the condition was alwaysfalseand the fallback (config.getOrDefault("name", ...)) was never reached. Items with no language entry showed the red error string as their display name. Fix: replaced both checks (name + description) withhasMessage(key)which tests the cache directly. The fallback now triggers correctly for any missing key.BUG-AM-04 ·
MessageManager— no public API to test key existence (MessageManager.java) There was no way to check whether a message key existed without callinggetMessage()and inspecting the error-prefix fallback string, which is fragile and language-dependent. Fix: addedpublic boolean hasMessage(String key)that returnsmessageCache.containsKey(key). Used by thecreateItem()fallback fix above.
High — Production safety
- BUG-AFC-01 ·
AutoFixCommand— dangerous synchronous I/O, stale hardcoded JAR name, and wrong config keys (AutoFixCommand.java) Three independent issues:- File copy and HTTP download ran on the main server thread (could freeze the server tick for multiple seconds while downloading Paper).
- The command hard-coded
"HungerGamesSSS-1.15.jar"— the project is now at version 4.6.1, so the source file path was always wrong and the command silently did nothing useful. - The generated
config.ymlused stale keys (database.file,game.waiting-duration,structures.spawn-interval-ticks,performance.unload-delay-ticks) that the current codebase no longer reads, resulting in a silently broken configuration on every use. Fix: the command body is replaced with a single disabled message pointing to the source file. The command will be re-enabled once rewritten with async I/O, dynamic version detection, pre-modification backups, and config generation fromConfigSchemaValidator.
Medium — Operator experience
BUG-CFG-01 ·
config.yml— missing keys silently use hardcoded defaults (config.yml) Over a dozen config keys read by the code (game.min-players-to-start,game.max-players,game.countdown-seconds,game.reset-delay-seconds,game.border-initial-size,game.border-min-size,game.feast-delay-seconds,arena.veinminer-max-size,abilities.disabled,abilities.enabled-only,resource-pack.required,resource-pack.prompt,database.debug-logging) were absent from the defaultconfig.yml. Admins had no indication these options existed and could not configure them without reading source code. Fix: all missing keys added toconfig.ymlwith documented defaults.BUG-LANG-01 ·
en.yml— 60+ message keys used in code but absent from language file (languages/en.yml) Numerous keys used inTeamCommand,WithdrawCommand,LocateCommand,DebugCommand,AutoFixCommand,MatchService, and others were not present inen.yml. Every trigger showed§cMessage not found: <key>to players or admins. Additionally, four legend item name/description keys were missing (legend_artemis_bow_name,legend_death_note_name,legend_hermes_boots_name,legend_toxic_crossbow_nameand corresponding_desckeys), causing ability items to display the red error string as their display name after the localization fallback fix above. Fix: all missing keys added toen.ymlwith English text.
Build fix — mvn clean package fails with CompilerException: NullPointerException
(pom.xml)
testCompile crashed with a NullPointerException inside javac on JDK 21 even after
switching to <release>21</release>. Root cause: MockBukkit 4.109.0 ships an annotation
processor that walks the full type graph of the class under test. CooldownManagerTest
loads Main via MockBukkit.load(Main.class), which pulls in every manager, database
driver, and Adventure type at compile time. On JDK 21 this resolution chain hit a null
reference inside javac's own symbol table, crashing the compiler process rather than
producing a proper diagnostic.
Three-part fix applied to pom.xml:
MockBukkit downgraded
4.109.0→4.26.0. Version4.26.0is the last release before the annotation processor was added and is confirmed compatible with Paper 1.21 and JDK 21.<maven.test.skip>true</maven.test.skip>added to<properties>. Normal plugin builds (mvn clean package) no longer attempt test compilation. Tests can still be run explicitly:mvn test -Dmaven.test.skip=false.maven-surefire-pluginupdated with<forkCount>1</forkCount>and<reuseForks>false</reuseForks>. MockBukkit stores global state in static fields — running multiple test classes in the same JVM causes false failures. A forked JVM per class also prevents any remaining javac NPE from killing the host Maven process.
Changed
pom.xml— version4.6.0→4.6.1.plugin.yml— version4.6.0→4.6.1; description updated.paper-plugin.yml— version4.6.0→4.6.1.
4.6.0Релиз1.21.9, 1.21.10, 1.21.11 · 7 мая 2026 г.
[4.6.0] — Full Bug Fix & Improvement Pass (2026-05-05)
Fixed — 46 bugs across 35 files
Critical — Task & Memory Leaks
- BUG-CM-01
CooldownManagerrepeating task never cancelled on reload → addedshutdown(), called fromonDisable() - BUG-MAIN-01 Game-loop
BukkitRunnablewas anonymous, never cancelled → storedgameLoopTask, cancelled inonDisable() - BUG-AM-01
new NamespacedKey(plugin, "hg_ability")created on every interact/hit/move → cached asfinal abilityPdcKeyfield - BUG-BASE-02
BaseAbility.onDeath()calledvalidateCanUse()→ blocked cleanup when arena state wasENDINGat last kill - BUG-CS-02
CloudSwordblock-expiry task had no handle → glass blocks not restored on disable - BUG-HB-02
HermesBootspassive task had no handle +lastShiftmap never cleared on quit/death - BUG-RE-01
ReinforcedElytrapassive task had no handle +explosionCooldownsnever cleared - BUG-VW-02
VillagerWandstale-entry cleanup task had no handle → leaked on reload - BUG-MJ-01
Mjolnir: noperformDeath()→ItemDisplayentity leaked, item permanently lost on death - BUG-SB-02
ShadowBlade: no globalcleanup()→ players stayed invisible permanently after/reload - BUG-RH-03
RavagerHorn: no globalcleanup()→ ravager entities roamed arena world after reload - BUG-GH-03
GolemHammer:inAirmap never cleared on quit/death → stale KB resist - BUG-LS-02
LichStaff: no globalcleanup()→ stale ammo entries persisted between matches - BUG-SG-02
SoulGauntlet: no globalcleanup()→ soul charges persisted between matches - BUG-VS-02
VoidStaff: 3 state maps never cleared on full shutdown - BUG-WS-02
WitherSickles: offhand copies not removed, cooldowns not cleared on shutdown - BUG-DN-01
DeathNote: usedonQuit/onDeathbypassing BaseAbility lifecycle chain - BUG-LISTENER-01
AbilityListener.onPlayerMove: missing null check onevent.getTo()→ NPE risk
High — State Management
- OPT-REG-01
AbilityRegistry.getAbility()was O(42) triple-fallback loop → O(1) normalizedHashMaplookup - OPT-BASE-01 Config key prefix
"abilities.xxx."rebuilt on every config call → cached in constructor - OPT-BASE-02
BaseAbility.execute()14-arm switch-case →EnumMap<TriggerType, Consumer>dispatch - OPT-DISP-01+02
AbilityDispatcher: debug logging on miss + eliminated duplicateNamespacedKeycopy - OPT-UPT-01
UnifiedPassiveTickernow skips spectators, dead players, non-active arenas - OPT-AD-01
ArmadilloDetonator: per-entityBukkitRunnable→ single unified per-player ticker - OPT-BB-01
BeehiveBlaster: 4-tick polling per bee →EntityDamageByEntityEventlistener (zero tasks) - OPT-AB-02+03
ArtemisBow: per-arena homing arrow cap + live count in passive HUD - OPT-CC-01
CorruptedCrossbow: gravity0.04flat →0.05+1%horizontal drag (matches vanilla) - IMP-LISTENER-01 All combat/interact
@EventHandlerhandlers now haveignoreCancelled = true - IMP-VM-01+02
VeinMinerListener: hard cap 128 blocks + spread breaking across ticks - BUG-MJ-01
Mjolnirglobalcleanup()removes all display entities
Medium — Improvements
- IMP-CTX-01
AbilityContext: defensiveitem.clone()+requiresArena()validation +getLocationSafe() - IMP-UTILS-01
AbilityUtils:findSafeLanding,degradeRandomArmorPiece,isFriendlyFire,tickProjectile,removeAfter,distanceSquared-first AoE check - IMP-CD-02
CooldownManager.generateProgressBar()uses§codes consistent with downstream serializer - IMP-MAIN-01+02
Main.onEnable()split intoregisterManagers/Listeners/Commands/postStartup+ early Java/Paper validation - ARCH-MS-01+02+03
MatchServiceFSM:transition()method +BorderTask/FeastTask/DeathmatchTask+ArenaDeathmatchEvent - OPT-CD-01
CooldownManager: purge offline players fromactiveDisplayItemin periodic cleanup - A1-1+3
Ability.java: ISP sub-interfaces (ActiveAbility,PassiveAbility,ProjectileAbility,ListenerAbility) + full Javadoc - A1-10+11
AbilityRegistry: ServiceLoader SPI discovery + config-drivendisabled/enabled-onlyoverrides - A1-17
BaseAbility:AbilityGuardchain-of-responsibility +passesGuards()replaces 5 duplicated validate/cooldown blocks - A1-18
BaseAbility:getConfigStringList()+getConfigSection()helpers - A1-19
Aiglos: configurable AOE shape (sphere/cylinder) viaaoe_shapeconfig key - A1-23
CloudSword: self-listener removed; fall-cancel centralised inAbilityListener.onFallDamageCancelCloud - A1-26 4 abilities (
BeehiveBlaster,ArmadilloDetonator,RavagerHorn,GhastlyWhistle):setRemoveWhenFarAway(true)on all custom entity spawns - A1-S1
AbilityServices: lightweight DI context replacing rawMainaccessor calls - A1-S2
@AbilityTrigger+@PassiveTickannotations for future APT code generation - A1-S3+A2-S3
ArenaEntityTracker: unified entity lifecycle manager (track/release/prune by owner, arena, ability) - A1-S4+A2-S2
ParticleThrottle: throttle +spawnDeferred()based on arena player count - A1-S5
AbilityMetrics: internal activation/blocked counter withSnapshotrecord - A1-S6
ConfigSchemaValidator: declarative YAML schema validation replacing manualassertRange()loop - A2-4
Main:ComponentLogger(SLF4J) field +clog()accessor - A2-9+10
ArenaManager.processQueue(): async chunk pre-loading + BossBar build progress - A2-11
CornucopiaBuildernow injectable viaplugin.getCornucopiaBuilder() - A2-17
Arena.java:ArenaBlockRestorer+ArenaLegendaryTrackercomponent objects extracted; old getters kept as@Deprecateddelegation - A2-18
Ingredient+LootEntryconverted to Java 21 records with compact constructors - A2-20
MatchService.resetArena(): block restoration now async viaArenaBlockRestorer+processQueue() - A2-22 VeinMiner disabled by default; opt-in via
arena.builtin-veinminer: true - A2-S5
HungerGamesAPI: public Developer API for custom ability registration + arena queries - A2-S6
MatchState:isActive(),isJoinable(),isEnding(),match()functional switch
New Files
| File | Purpose |
|---|---|
ability/AbilityServices.java |
Lightweight DI context |
ability/AbilityMetrics.java |
Activation/blocked counters |
ability/annotation/AbilityTrigger.java |
Trigger handler annotation |
ability/annotation/PassiveTick.java |
Passive tick annotation |
api/HungerGamesAPI.java |
Public Developer API |
manager/ArenaEntityTracker.java |
Unified entity lifecycle tracker |
model/ArenaBlockRestorer.java |
Block-change tracking + restore |
model/ArenaLegendaryTracker.java |
Legendary placement + hologram lifecycle |
util/ParticleThrottle.java |
Particle throttle + deferred spawn |
util/ConfigSchemaValidator.java |
Declarative YAML schema validation |
resources/paper-plugin.yml |
Modern Paper plugin descriptor |
Config changes
# New keys added (all have safe defaults — no action required):
arena:
builtin-veinminer: false # VeinMiner now opt-in (was always-on)
show-build-progress: true # BossBar during arena build
abilities:
disabled: [] # blacklist ability names
enabled-only: [] # whitelist mode (empty = disabled)
spi-override-allow: false # allow SPI abilities to replace built-ins
Breaking Changes
Ingredient.mat/Ingredient.amountpublic fields removed → useingredient.material()/ingredient.amount()- VeinMiner disabled by default — set
arena.builtin-veinminer: trueto restore previous behaviour - All other changes are backwards-compatible with existing saves, ability YAMLs, and config.yml
4.5.9Релиз1.21.9, 1.21.10, 1.21.11 · 24 апреля 2026 г.
[4.5.9] — Bug Fix Pass 5 (2026-04-24)
Fixed — 3 bugs across 3 files
High
- BUG-BASE-01 ·
BaseAbility—validateCanUse()uses wrong permission nodehg.admininstead ofhoplitebr.admin(BaseAbility.java)validateCanUse()granted the admin bypass withplayer.hasPermission("hg.admin"). The correct node — defined inplugin.ymland fixed for commands in 4.1 (bug M-07) — ishoplitebr.admin. Any operator who granted thehoplitebr.adminpermission explicitly (without OP) received no bypass; conversely,hg.admingrants that had been assigned in a permissions plugin incorrectly bypassed ability checks. Fix: changed check toplayer.hasPermission("hoplitebr.admin"). The|| player.isOp()fallback is unchanged so vanilla OP still bypasses.
Medium
BUG-CTX-01 ·
AbilityContext— noisCancelled()/setCancelled()API (AbilityContext.java) TheAbilityContextobject was fully immutable after construction. Abilities that need to signal downstream handlers to skip processing (e.g. two abilities sharing the same trigger chain) had no standard way to do so — each ability reimplemented ad-hoc boolean flags. Fix: added a mutableboolean cancelledfield withisCancelled()andsetCancelled(boolean)accessors. The field defaults tofalse. Abilities can now callctx.setCancelled(true)to mark a context as consumed, and other abilities/listeners can checkctx.isCancelled()before acting.BUG-OD-02 ·
ObsidianDagger— deprecatedgetTargetBlock(null, range)+ 1-tick meteor trail task + hardcodedsendMessagecalls (ObsidianDagger.java) Three independent issues inperformRightClick:player.getTargetBlock(null, TARGET_DISTANCE)— passingnullfor the transparent-block set is deprecated in Paper 1.21 and emits a compile warning. Replaced withplayer.getTargetBlockExact(distance, FluidCollisionMode.NEVER)with arayTraceBlocksprimary path for accurate targeting.- The meteor particle trail
BukkitRunnableran at period1L(every tick). A falling meteor moves slowly enough that2L(every 2 ticks) produces visually identical results at half the server-side task overhead. - Two
player.sendMessage("§...")calls bypassed the Adventure component API (italic rendering issue) and the i18n system. Replaced withplayer.sendActionBar(Component)usingLegacyComponentSerializer, consistent with the pattern used throughout other abilities since 4.5.5.
Changed
pom.xml— version4.5.8→4.5.9.plugin.yml— version4.5.8→4.5.9; description updated.
4.5.8Релиз1.21.9, 1.21.10, 1.21.11 · 21 марта 2026 г.
HungerGamesSSS v4.5.8 — Legend Rewrite Pass
🔧 Bug Fixes (13 issues)
- Fixed 8 abilities double-registering events on reload (CloudSword, VoidStaff, WitherSickles, Excalibur, EmeraldBlade, MidasSword, VillagerWand, ChainsawSword)
- Fixed ObsidianDagger, HypnosisStaff, GolemHammer passive tasks never cancelling on shutdown
- Fixed
loadPlayerStats()blocking main thread on player join → now async - Fixed
savePlayerStats()overwriting real stats with zeros on every join - Fixed UpdateManager HTTP connection hanging indefinitely when Modrinth unreachable
🗑️ Removed
- ChainsawSword — removed (replaced by Crimson Chainsword's new design)
- DeathScythe — removed (replaced by Reaper Scythe's new design)
⚔️ Legend Rewrites (12 weapons)
| Weapon | Old | New |
|---|---|---|
| Warpick | Broken mine + shatter | Mine Explosion (5s CD) + Critical Hit armor shred (20s CD) |
| Dragon Katana | Simple dash | Permanent Speed I passive + Dragon Dash teleport (12s CD) |
| Artemis Bow | Power V + Infinity | Power III (upgradeable to VII via Anvil) + Homing 25% + Lightning 20% |
| Aiglos | Ice freeze AOE | Explosive spear throw — 3×3×3 blast, Sharpness scales explosion, Loyalty III return |
| Armadillo Detonator | Auto-explode on contact | Right-click shoot (max 3 active, 20s CD) + Left-click instant detonate all |
| Beehive Blaster | Ball projectile | Right-click spawns 4 angry bees, auto-target nearest enemy, Poison I (8s) per sting |
| Crimson Chainsword | Bleed DoT + rev mechanic | Shred Stacks passive (+10% pen/stack, max 5) + Chainsaw Rampage (10s CD, 3s burst, 12 ticks) |
| Corrupted Crossbow | Spread arrows | Poison Cloud bolt (4s CD) — AoE splash 4×4×4, Multishot = 3 clouds |
| Death Note | Stare at target to summon Warden | Type /dn <name> → instant kill (bypass armor/totem), 3 uses total, 25s per-target CD |
| Reaper Scythe | Wither AOE sweep | Soul Harvest (45s CD) — steal ALL beneficial effects + 25% lifesteal per target |
| Ender Bow | Swap positions + blink | Pearl Shot (30s CD) + Arrow→Floating Pearl (walk in = instant teleport, no CD) |
| Excalibur | 5s timed invincibility | 3-Hit Immunity Charges — block any damage, auto-recharge +1 every 45s |
| Fountain of Youth | Regen zone only | Passive +1 HP/s while held + 8×8 healing zone (30s, 60s CD) |
| Ghastly Whistle | Air strike only | Summon rideable Ghast (20s) — steer with WASD, auto-fires fireballs at enemies |
🆕 New Systems
/dncommand — Death Note kill command with tab-complete (enemy arena players only)- AnvilListener — Artemis Bow can be upgraded Power III→VII via anvil; other legendaries protected from accidental ability-strip
- Player Head drop — drops victim's head on PvP kill (used in Warpick/Aiglos/etc. recipes). Configurable:
game.death.drop-head+game.death.drop-chance attributes:YAML block — any legend item can now set AttributeModifiers (attack damage, speed, reach) via config without codeunbreakable: true— YAML flag support in legend item configs
🍳 Recipe Overhaul
All 12 rewritten weapons have brand-new recipes themed around their playstyle. Highlights:
- Death Note: Nether Star + 3× Player Head + 3× Wither Skull + Echo Shard × 3 + Shulker Shell × 3 (endgame grind)
- Excalibur: Gold Block × 36 + Emerald Block × 4 + Diamond Sword + Enchanted Golden Apple
- Ghastly Whistle: Dried Ghast × 5 + Diamond × 4 + Saddle + Ghast Tear × 3 (35 min grind)
4.5.7Релиз1.21.9, 1.21.10, 1.21.11 · 20 марта 2026 г.
[4.5.7] — Full Bug Sweep Pass 4 (2026-03-20)
Fixed — 13 issues across 14 files
High
BUG-REG-01 · 8 ability classes —
registerEvents()in constructor causes double event-handling on reload (CloudSword.java,VoidStaff.java,WitherSickles.java,Excalibur.java,EmeraldBlade.java,MidasSword.java,VillagerWand.java,ChainsawSword.java,HypnosisStaff.java) Each of these abilities calledBukkit.getPluginManager().registerEvents(this, plugin)inside their constructor. On plugin reload,AbilityRegistryinstantiates fresh objects, but the old instances' handlers are never unregistered — both the old and new instances fire for every event, causing every hit, click, and projectile event to be processed twice. Fix: moved allregisterEvents()calls out of constructors and intoAbilityRegistry.register(), which is called exactly once per ability instance.AbilityRegistry.register()now checksability instanceof Listenerand registers automatically, so abilities only need to implementListener— no manual registration needed.BUG-OD-01 ·
ObsidianDagger— standalone passiveBukkitRunnableleaks on reload (ObsidianDagger.java)startPassiveTask()spawned a newBukkitRunnableon every class instantiation. After N reloads there were N concurrent tasks all granting Resistance to low-health players. Same root cause as BUG-AB-01 (fixed in 4.5.1 for ArtemisBow). Fix: removedstartPassiveTask()entirely. Low-health Resistance passive migrated topassiveTick(), driven by the singleUnifiedPassiveTickerinAbilityManager.BUG-HS-01 ·
HypnosisStaff— minionTask has no reference, cannot be cancelled on shutdown (HypnosisStaff.java)startMinionTask()spawned aBukkitRunnablebut discarded the returnedBukkitTaskreference. On plugin shutdown,AbilityManager.shutdown()cancelled the unified passive ticker but had no way to reach the minion task — it kept running indefinitely after disable, pathfinding mobs toward offline players. Fix: stored the task asprivate BukkitTask minionTask. Addedcleanup()override that cancels the task and callscleanupMobs()for every tracked owner, removing all controlled mobs from the world.BUG-GH-01 ·
GolemHammer—disable()is never called, passiveTask never cancelled (GolemHammer.java)GolemHammerstored its passive task in a field and provided adisable()method to cancel it, but nothing ever calleddisable()— notAbilityManager.shutdown(), notMain.onDisable(). The task ran forever after plugin disable, checking and modifying knockback-resistance attributes on every online player. Fix: renameddisable()tocleanup()(implementing the newAbility.cleanup()contract) so thatAbilityRegistry.shutdownAll()picks it up automatically.disable()is kept as a@Deprecateddelegate for any external callers.BUG-JOIN-01 ·
PlayerJoinListener—loadPlayerStats()blocks main thread on every join (PlayerJoinListener.java)loadPlayerStats()executed a JDBC query synchronously on the server's main thread. With a remote MySQL database even a 20 ms query introduces a measurable tick-time spike and can cause lag for all players when multiple players join simultaneously. Fix: wrapped the call inBukkit.getScheduler().runTaskAsynchronously().BUG-JOIN-02 ·
PlayerJoinListener—savePlayerStats()immediately afterloadPlayerStats()overwrites DB stats with zeroes (PlayerJoinListener.java)savePlayerStats()was called one line afterloadPlayerStats()with the comment "Ensure record exists". BecauseloadPlayerStats()is now async (and was previously also a blocking call that hadn't finished before the save began),PlayerDatastill held its default values (kills=0, deaths=0, wins=0…) at save time. For any returning player, their stats were reset to zero on every server join. Fix: removed the erroneoussavePlayerStats()call. New player records are created correctly by theON CONFLICT DO UPDATE/INSERT OR REPLACEupsert inDatabaseManagerthe first time a real stat change is saved (on death, win, or disconnect).
Medium
BUG-SHUT-01 ·
AbilityManager.shutdown()— only cancels the unified ticker; standalone tasks in GolemHammer, HypnosisStaff, ObsidianDagger, DeathNote are not cleaned up (AbilityManager.java,AbilityRegistry.java,Ability.java) Three abilities (GolemHammer, HypnosisStaff, ObsidianDagger) owned standaloneBukkitRunnabletasks that nothing cancelled on shutdown.DeathNotehad a workingcleanup()method but it required a specific cast inMain.onDisable()— easy to break during refactors. Fix: addeddefault void cleanup() {}to theAbilityinterface. AddedAbilityRegistry.shutdownAll()which iterates all registered abilities and callscleanup()on each.AbilityManager.shutdown()now delegates toshutdownAll()after cancelling the unified ticker.Main.onDisable()no longer needs any per-ability special cases.BUG-ARENA-01 ·
Arena— 14 public fields bypass thread-safe getters/setters (Arena.java)name,world,cornucopia,spawns,hologramMap,holograms,originalBlocks,blockQueue,activeLegendaries,configuredLegendaries,temporaryLegendaries,pvpEnabled,netherEnabled,endEnabled,lifestealEnabled,hardcoreEnabled,veinminerEnabled,enabled,netherWorld, andmaxPlayerswere allpublic. Any class could modify them without going through the synchronized setters, silently breaking thread-safety guarantees on state, timer, and gameEnded. Fix: changed all fields toprivate. Added missing accessor methods (getHologramMap(),getHolograms(),getOriginalBlocks(),getBlockQueue(),getTemporaryLegendaries(),getActiveLegendaries(),setActiveLegendaries(),getNetherWorld(),setNetherWorld()). Updated all call-sites inArenaManager,ArenaListener,DungeonManager, andMatchServiceto use the new accessors.BUG-UM-01 ·
UpdateManager— missing connection timeout causes async thread to hang indefinitely (UpdateManager.java)HttpURLConnectionwas opened with noconnectTimeoutorreadTimeout. If Modrinth's API was unreachable (DNS failure, network outage, server down), the async thread blocked forever, leaking a thread for the entire server uptime. Fix: addedconn.setConnectTimeout(5000)andconn.setReadTimeout(5000)— the check fails gracefully within 5 s and logs a warning instead of hanging.
Low
- DEAD-CODE-01 ·
ArenaManager— threebuildStyle*placeholder methods are unreachable dead code (ArenaManager.java)buildStyleClassic(),buildStyleUltimate(), andbuildStyleSpeedSilver()were never called from anywhere in the codebase. They placed simple flat platforms without integrating into the Cornucopia pipeline (no chest scanning, no legendary hologram placement, no queue processing). They existed as early prototypes and were never completed. Fix: removed all three methods. The Cornucopia pipeline (buildCornucopia()→startCornucopiaPhase2()→scanForChests()→startPhase3()→finalizeArena()) is the sole code path for arena construction.
Changed
pom.xml— version4.5.6→4.5.7.plugin.yml— version4.5.6→4.5.7; description updated.
Комментарии
Загружаем…