Links
Tags
Creators
Details
4.5.7
Compatibility
Changes
[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.
Projects on Modrinth are automatically available through a Maven repository for use with JVM build tools such as Gradle. To learn more about the Modrinth Maven API, click here.
Note: When available, you should use the creator's maven repo instead as it will have transitive dependency information that the Modrinth Maven API does not. You may also end up with duplicate dependencies if you use a mix of Modrinth and non-Modrinth Maven repositories for your dependencies, because the group identifier will be different when served through the Modrinth Maven API.
Maven coordinates:
Version ID:
build.gradle:
repositories {
exclusiveContent {
forRepository {
maven {
name = "Modrinth"
url = "https://api.modrinth.com/maven"
}
}
// forRepositories(fg.repository) // Uncomment when using ForgeGradle
filter {
includeGroup "maven.modrinth"
}
}
}
// Standard Gradle dependency
dependencies {
implementation "maven.modrinth:VxdGAFxk:1u80eHH7"
}
// Legacy Loom dependency
dependencies {
modImplementation "maven.modrinth:VxdGAFxk:1u80eHH7"
}

