Перейти к содержимому
Mineforgian

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 Icon

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 ArenaEndEvent comments, duplicate LootTableManager and CornucopiaBuilder instances causing config-reload drift, startMatch() bypassing the FSM transition() contract, spawn cages placed inside terrain at Y=100, players falling through piston floors due to async physics, dead checkCompatibility() method, empty MatchSession class, stale nested language directory, and disconnected players holding matches open indefinitely (now auto-expired after configurable game.reconnect-timeout seconds, default 60). No database migrations — drop-in replacement for 4.6.3. Upgrade strongly recommended. See CHANGELOG.md for the full list.

Modrinth Paper Java License

45 legendary weapons. Procedural dungeons. Last player standing.

📥 Download🐛 Report a Bug💬 Discord


📖 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

  1. Download the .jar
  2. Drop it into plugins/
  3. Start the server — configs generate automatically
  4. Stand at the centre of your arena → /hg create <name>
  5. /hg setcornucopia <name> classic
  6. Walk to each spawn point → /hg addspawn <name> (repeat 2–24×)
  7. Stand in your lobby → /hg setlobby
  8. /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, performance
  • loot.yml — chest loot tiers (common / uncommon / rare)
  • scoreboard.yml — scoreboard layout and update interval
  • legend-items/<id>.yml — per-weapon stats, recipe, lore, enchantments
  • dungeons.yml — dungeon type toggles and mob HP
  • cornucopia.yml — Cornucopia build presets
  • languages/<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 (PLAYING or DEATHMATCH)
  • Check hoplitebr.admin permission — OP always bypasses ability checks
  • Run /debug and check console for [Ability] log lines with debug: 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)
  • GameTask was calling arena.setState(DEATHMATCH) directly, bypassing MatchService.transition(). The teleport and border-collapse logic inside scheduleDeathmatchTask() never ran.
World border ignores my config values — always uses 1000 blocks
  • Fixed in 4.6.2 (BUG-2 / mismatched config keys)
  • GameTask was reading game.initial-border-size and game.border.initial-size (wrong keys). The canonical key is game.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() and GameTask.handleWorldBorder() both wrote to the WorldBorder simultaneously. The per-tick recalculation overrode the smooth periodic animation every second. scheduleBorderTask() has been removed; GameTask is 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() called meta.getDisplayName() (legacy API) which always returns "" for items using the Adventure Component API. The fallback of recipe.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 when aliveCount == 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) and startCountdown() created countdown BukkitRunnables without storing the task reference. cancelAllTasksFor() could not cancel them, leading to two countdown timers running concurrently and startArena() 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 (default 60, 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() called arena.setState(MatchState.DEATHMATCH) directly instead of plugin.getMatchService().transition(arena, MatchState.DEATHMATCH). Bypassing transition() meant:

    • ArenaDeathmatchEvent was 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) with plugin.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.yml declares: game.border-initial-size: 2000 (the canonical key)
    • MatchService.java reads: "game.border-initial-size" ✅ correct
    • GameTask.java line 90 read: "game.initial-border-size" ❌ key does not exist → fallback 1000.0
    • GameTask.java line 100 read: "game.border.initial-size" ❌ key does not exist → fallback 1000.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-size set above or below 1 000 saw unexpected border behaviour from the very first second of a match.

    Fix: unified both GameTask reads to use "game.border-initial-size".

  • BUG-3 · MatchService.startMatch(Arena, int) — countdown BukkitRunnable not stored (MatchService.java)

    The overload startMatch(Arena arena, int countdownSeconds) created a countdown BukkitRunnable but discarded the returned BukkitTask. The task was never added to countdownTasks, so cancelAllTasksFor() could not cancel it. Calling startMatch() twice on the same arena (e.g. two rapid /hg start commands, or a player join triggering auto-start while a manual start was in progress) launched two concurrent countdowns. Both reached zero and each called startArena(), resulting in the match starting twice — players teleported twice, inventory cleared twice, cage system entered an inconsistent state.

    Fix: stored the returned BukkitTask in countdownTasks.put(arena.getName(), task).


🟡 Medium — Visible gameplay defects

  • BUG-4 · MatchService + GameTask — WorldBorder controlled by two systems simultaneously (MatchService.java)

    MatchService.transition() (called when entering PLAYING) scheduled scheduleBorderTask(), a periodic timer that shrank the border every N minutes. At the same time, GameTask.handleWorldBorder() ran every server tick during PLAYING and recalculated the border size via smooth linear interpolation, then called WorldBorder.setSize(). The per-tick recalculation fired every second and silently overrode the periodic task's setSize(size, duration) smooth animation, causing the border to visibly flicker or jump rather than shrink smoothly.

    Fix: removed the scheduleBorderTask(arena) call from MatchService.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 legacy getDisplayName() method returns "" for any such item, so hasDisplayName() was always false, and every legendary hologram fell back to showing recipe.getType().name() — raw material names like WOODEN_SWORD instead of the intended weapon name (e.g. Excalibur).

    Fix: replaced getDisplayName() / hasDisplayName() with Adventure's PlainTextComponentSerializer to 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 when aliveCount == 1. In team mode, if the final two (or more) surviving players were all on the same team, aliveCount was ≥ 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 > 1 the 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 — deprecated ChatColor usage produces compile warnings (TeamManager.java)

    TeamManager and TeamData use org.bukkit.ChatColor for team color storage, which is deprecated on Paper 1.21 in favour of net.kyori.adventure.text.format.NamedTextColor. Migrating the underlying storage type would require a database schema change and DatabaseManager migration; that is deferred to a future version. Added @SuppressWarnings("deprecation") on loadColors() with a TODO comment 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 main startMatch(Arena, List<Player>) flow) created a countdown BukkitRunnable but did not add the task to countdownTasks. If the arena was reset before the countdown finished (e.g. all players left), the orphaned task continued ticking until it reached zero and called releasePlayers() 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 stores Block objects instead of Location (MatchService.java)

    arenaCages was declared Map<String, List<Block>>. Block objects hold a direct reference to the chunk at the time they were obtained. If chunks unload and reload before breakGlassCages() runs, the stale Block reference may report an incorrect getType() — the GLASS check would fail and the cage blocks would not be cleared, leaving orphaned glass geometry in the arena world.

    Fix: changed arenaCages to Map<String, List<Location>>. breakGlassCages() now calls l.getBlock() at removal time, always querying the current (guaranteed-loaded) chunk state. Both createJoinCage() and teleportPlayersAndCreateCages() updated to store l.clone().


Changed

  • pom.xml — version 4.6.14.6.2
  • plugin.yml — version 4.6.14.6.2; description updated
  • paper-plugin.yml — version 4.6.14.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.ymlloader: references a class that does not exist (paper-plugin.yml) The file declared loader: me.duong2012g.hungergamessss.PaperPluginLoader, but this class was never created. Paper attempted to instantiate it during plugin loading, threw ClassNotFoundException, and the plugin failed to enable entirely. Fix: removed the loader: line. A custom PluginLoader is 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) DatabaseManager called config.setDriverClassName("org.sqlite.JDBC") and supported MySQL mode, but neither org.xerial:sqlite-jdbc nor com.mysql:mysql-connector-j appeared as Maven dependencies. The built JAR had no driver classes, causing ClassNotFoundException: org.sqlite.JDBC on every server startup that uses the default SQLite config. Fix: added both dependencies to pom.xml and configured the Maven Shade Plugin to relocate them under me.duong2012g.hungergamessss.libs.{sqlite,mysql} to avoid classpath conflicts with other plugins.

High — Gameplay-breaking

  • BUG-DB-02 · DatabaseManager.updateSchema()games_played column added to arenas table instead of players (DatabaseManager.java) updateSchema() iterated a single column array and checked all entries against the arenas table. games_played is a player stat column that belongs in players. On fresh installs the players table lacked the column; savePlayerStats() / loadPlayerStats() threw SQLException: no such column: games_played whenever stats were read or written. Fix: split the migration into two separate loops — arenaColumns[] against the arenas table and playerColumns[] against the players table. Each column is now checked and added against the correct table.

  • BUG-AM-02 · AbilityManager.createItem()unbreakable flag silently overwritten (AbilityManager.java) The unbreakable block retrieved a new ItemMeta copy from item.getItemMeta(), set unbreakable(true) on it, called item.setItemMeta(unbrMeta) — but then the outer code called item.setItemMeta(meta) with the original meta object (which did not have unbreakable set). The second setItemMeta silently discarded the flag. Legend items marked unbreakable: true in YAML still lost durability in-game. Fix: call meta.setUnbreakable(true) directly on the existing meta object before the final item.setItemMeta(meta) call. The duplicate item.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 of getMessage(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 always false and 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) with hasMessage(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 calling getMessage() and inspecting the error-prefix fallback string, which is fragile and language-dependent. Fix: added public boolean hasMessage(String key) that returns messageCache.containsKey(key). Used by the createItem() 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:
    1. File copy and HTTP download ran on the main server thread (could freeze the server tick for multiple seconds while downloading Paper).
    2. 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.
    3. The generated config.yml used 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 from ConfigSchemaValidator.

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 default config.yml. Admins had no indication these options existed and could not configure them without reading source code. Fix: all missing keys added to config.yml with documented defaults.

  • BUG-LANG-01 · en.yml — 60+ message keys used in code but absent from language file (languages/en.yml) Numerous keys used in TeamCommand, WithdrawCommand, LocateCommand, DebugCommand, AutoFixCommand, MatchService, and others were not present in en.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_name and corresponding _desc keys), causing ability items to display the red error string as their display name after the localization fallback fix above. Fix: all missing keys added to en.yml with 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:

  1. MockBukkit downgraded 4.109.04.26.0. Version 4.26.0 is the last release before the annotation processor was added and is confirmed compatible with Paper 1.21 and JDK 21.

  2. <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.

  3. maven-surefire-plugin updated 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 — version 4.6.04.6.1.
  • plugin.yml — version 4.6.04.6.1; description updated.
  • paper-plugin.yml — version 4.6.04.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 CooldownManager repeating task never cancelled on reload → added shutdown(), called from onDisable()
  • BUG-MAIN-01 Game-loop BukkitRunnable was anonymous, never cancelled → stored gameLoopTask, cancelled in onDisable()
  • BUG-AM-01 new NamespacedKey(plugin, "hg_ability") created on every interact/hit/move → cached as final abilityPdcKey field
  • BUG-BASE-02 BaseAbility.onDeath() called validateCanUse() → blocked cleanup when arena state was ENDING at last kill
  • BUG-CS-02 CloudSword block-expiry task had no handle → glass blocks not restored on disable
  • BUG-HB-02 HermesBoots passive task had no handle + lastShift map never cleared on quit/death
  • BUG-RE-01 ReinforcedElytra passive task had no handle + explosionCooldowns never cleared
  • BUG-VW-02 VillagerWand stale-entry cleanup task had no handle → leaked on reload
  • BUG-MJ-01 Mjolnir: no performDeath()ItemDisplay entity leaked, item permanently lost on death
  • BUG-SB-02 ShadowBlade: no global cleanup() → players stayed invisible permanently after /reload
  • BUG-RH-03 RavagerHorn: no global cleanup() → ravager entities roamed arena world after reload
  • BUG-GH-03 GolemHammer: inAir map never cleared on quit/death → stale KB resist
  • BUG-LS-02 LichStaff: no global cleanup() → stale ammo entries persisted between matches
  • BUG-SG-02 SoulGauntlet: no global cleanup() → 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: used onQuit/onDeath bypassing BaseAbility lifecycle chain
  • BUG-LISTENER-01 AbilityListener.onPlayerMove: missing null check on event.getTo() → NPE risk

High — State Management

  • OPT-REG-01 AbilityRegistry.getAbility() was O(42) triple-fallback loop → O(1) normalized HashMap lookup
  • 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 duplicate NamespacedKey copy
  • OPT-UPT-01 UnifiedPassiveTicker now skips spectators, dead players, non-active arenas
  • OPT-AD-01 ArmadilloDetonator: per-entity BukkitRunnable → single unified per-player ticker
  • OPT-BB-01 BeehiveBlaster: 4-tick polling per bee → EntityDamageByEntityEvent listener (zero tasks)
  • OPT-AB-02+03 ArtemisBow: per-arena homing arrow cap + live count in passive HUD
  • OPT-CC-01 CorruptedCrossbow: gravity 0.04 flat → 0.05 + 1% horizontal drag (matches vanilla)
  • IMP-LISTENER-01 All combat/interact @EventHandler handlers now have ignoreCancelled = true
  • IMP-VM-01+02 VeinMinerListener: hard cap 128 blocks + spread breaking across ticks
  • BUG-MJ-01 Mjolnir global cleanup() removes all display entities

Medium — Improvements

  • IMP-CTX-01 AbilityContext: defensive item.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 into registerManagers/Listeners/Commands/postStartup + early Java/Paper validation
  • ARCH-MS-01+02+03 MatchService FSM: transition() method + BorderTask/FeastTask/DeathmatchTask + ArenaDeathmatchEvent
  • OPT-CD-01 CooldownManager: purge offline players from activeDisplayItem in periodic cleanup
  • A1-1+3 Ability.java: ISP sub-interfaces (ActiveAbility, PassiveAbility, ProjectileAbility, ListenerAbility) + full Javadoc
  • A1-10+11 AbilityRegistry: ServiceLoader SPI discovery + config-driven disabled/enabled-only overrides
  • A1-17 BaseAbility: AbilityGuard chain-of-responsibility + passesGuards() replaces 5 duplicated validate/cooldown blocks
  • A1-18 BaseAbility: getConfigStringList() + getConfigSection() helpers
  • A1-19 Aiglos: configurable AOE shape (sphere / cylinder) via aoe_shape config key
  • A1-23 CloudSword: self-listener removed; fall-cancel centralised in AbilityListener.onFallDamageCancelCloud
  • A1-26 4 abilities (BeehiveBlaster, ArmadilloDetonator, RavagerHorn, GhastlyWhistle): setRemoveWhenFarAway(true) on all custom entity spawns
  • A1-S1 AbilityServices: lightweight DI context replacing raw Main accessor calls
  • A1-S2 @AbilityTrigger + @PassiveTick annotations 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 with Snapshot record
  • A1-S6 ConfigSchemaValidator: declarative YAML schema validation replacing manual assertRange() loop
  • A2-4 Main: ComponentLogger (SLF4J) field + clog() accessor
  • A2-9+10 ArenaManager.processQueue(): async chunk pre-loading + BossBar build progress
  • A2-11 CornucopiaBuilder now injectable via plugin.getCornucopiaBuilder()
  • A2-17 Arena.java: ArenaBlockRestorer + ArenaLegendaryTracker component objects extracted; old getters kept as @Deprecated delegation
  • A2-18 Ingredient + LootEntry converted to Java 21 records with compact constructors
  • A2-20 MatchService.resetArena(): block restoration now async via ArenaBlockRestorer + 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.amount public fields removed → use ingredient.material() / ingredient.amount()
  • VeinMiner disabled by default — set arena.builtin-veinminer: true to 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 · BaseAbilityvalidateCanUse() uses wrong permission node hg.admin instead of hoplitebr.admin (BaseAbility.java) validateCanUse() granted the admin bypass with player.hasPermission("hg.admin"). The correct node — defined in plugin.yml and fixed for commands in 4.1 (bug M-07) — is hoplitebr.admin. Any operator who granted the hoplitebr.admin permission explicitly (without OP) received no bypass; conversely, hg.admin grants that had been assigned in a permissions plugin incorrectly bypassed ability checks. Fix: changed check to player.hasPermission("hoplitebr.admin"). The || player.isOp() fallback is unchanged so vanilla OP still bypasses.

Medium

  • BUG-CTX-01 · AbilityContext — no isCancelled() / setCancelled() API (AbilityContext.java) The AbilityContext object 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 mutable boolean cancelled field with isCancelled() and setCancelled(boolean) accessors. The field defaults to false. Abilities can now call ctx.setCancelled(true) to mark a context as consumed, and other abilities/listeners can check ctx.isCancelled() before acting.

  • BUG-OD-02 · ObsidianDagger — deprecated getTargetBlock(null, range) + 1-tick meteor trail task + hardcoded sendMessage calls (ObsidianDagger.java) Three independent issues in performRightClick:

    1. player.getTargetBlock(null, TARGET_DISTANCE) — passing null for the transparent-block set is deprecated in Paper 1.21 and emits a compile warning. Replaced with player.getTargetBlockExact(distance, FluidCollisionMode.NEVER) with a rayTraceBlocks primary path for accurate targeting.
    2. The meteor particle trail BukkitRunnable ran at period 1L (every tick). A falling meteor moves slowly enough that 2L (every 2 ticks) produces visually identical results at half the server-side task overhead.
    3. Two player.sendMessage("§...") calls bypassed the Adventure component API (italic rendering issue) and the i18n system. Replaced with player.sendActionBar(Component) using LegacyComponentSerializer, consistent with the pattern used throughout other abilities since 4.5.5.

Changed

  • pom.xml — version 4.5.84.5.9.
  • plugin.yml — version 4.5.84.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

  • /dn command — 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 code
  • unbreakable: 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 called Bukkit.getPluginManager().registerEvents(this, plugin) inside their constructor. On plugin reload, AbilityRegistry instantiates 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 all registerEvents() calls out of constructors and into AbilityRegistry.register(), which is called exactly once per ability instance. AbilityRegistry.register() now checks ability instanceof Listener and registers automatically, so abilities only need to implement Listener — no manual registration needed.

  • BUG-OD-01 · ObsidianDagger — standalone passive BukkitRunnable leaks on reload (ObsidianDagger.java) startPassiveTask() spawned a new BukkitRunnable on 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: removed startPassiveTask() entirely. Low-health Resistance passive migrated to passiveTick(), driven by the single UnifiedPassiveTicker in AbilityManager.

  • BUG-HS-01 · HypnosisStaff — minionTask has no reference, cannot be cancelled on shutdown (HypnosisStaff.java) startMinionTask() spawned a BukkitRunnable but discarded the returned BukkitTask reference. 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 as private BukkitTask minionTask. Added cleanup() override that cancels the task and calls cleanupMobs() for every tracked owner, removing all controlled mobs from the world.

  • BUG-GH-01 · GolemHammerdisable() is never called, passiveTask never cancelled (GolemHammer.java) GolemHammer stored its passive task in a field and provided a disable() method to cancel it, but nothing ever called disable() — not AbilityManager.shutdown(), not Main.onDisable(). The task ran forever after plugin disable, checking and modifying knockback-resistance attributes on every online player. Fix: renamed disable() to cleanup() (implementing the new Ability.cleanup() contract) so that AbilityRegistry.shutdownAll() picks it up automatically. disable() is kept as a @Deprecated delegate for any external callers.

  • BUG-JOIN-01 · PlayerJoinListenerloadPlayerStats() 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 in Bukkit.getScheduler().runTaskAsynchronously().

  • BUG-JOIN-02 · PlayerJoinListenersavePlayerStats() immediately after loadPlayerStats() overwrites DB stats with zeroes (PlayerJoinListener.java) savePlayerStats() was called one line after loadPlayerStats() with the comment "Ensure record exists". Because loadPlayerStats() is now async (and was previously also a blocking call that hadn't finished before the save began), PlayerData still 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 erroneous savePlayerStats() call. New player records are created correctly by the ON CONFLICT DO UPDATE / INSERT OR REPLACE upsert in DatabaseManager the 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 standalone BukkitRunnable tasks that nothing cancelled on shutdown. DeathNote had a working cleanup() method but it required a specific cast in Main.onDisable() — easy to break during refactors. Fix: added default void cleanup() {} to the Ability interface. Added AbilityRegistry.shutdownAll() which iterates all registered abilities and calls cleanup() on each. AbilityManager.shutdown() now delegates to shutdownAll() 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, and maxPlayers were all public. 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 to private. Added missing accessor methods (getHologramMap(), getHolograms(), getOriginalBlocks(), getBlockQueue(), getTemporaryLegendaries(), getActiveLegendaries(), setActiveLegendaries(), getNetherWorld(), setNetherWorld()). Updated all call-sites in ArenaManager, ArenaListener, DungeonManager, and MatchService to use the new accessors.

  • BUG-UM-01 · UpdateManager — missing connection timeout causes async thread to hang indefinitely (UpdateManager.java) HttpURLConnection was opened with no connectTimeout or readTimeout. 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: added conn.setConnectTimeout(5000) and conn.setReadTimeout(5000) — the check fails gracefully within 5 s and logs a warning instead of hanging.

Low

  • DEAD-CODE-01 · ArenaManager — three buildStyle* placeholder methods are unreachable dead code (ArenaManager.java) buildStyleClassic(), buildStyleUltimate(), and buildStyleSpeedSilver() 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 — version 4.5.64.5.7.
  • plugin.yml — version 4.5.64.5.7; description updated.

Комментарии

Загружаем…