All versions
๐ด
๐ด
๐ก
๐ก
2.0.6
Release
CombatGunSSS 2.0.65 months ago 86
Compatibility
Minecraft: Java Edition
1.21.x
Platforms
Bukkit
Paper
Purpur
Spigot
Changes
[2.0.6] - 2026-05-14
Fixed
๐ด GunListener.isFriendlyFire() โ Scoreboard team detection using wrong scoreboard (Critical)
- Root cause:
shooter.getScoreboard()andtargetPlayer.getScoreboard()return each player's personal scoreboard view, which may differ between players when a scoreboard plugin assigns individual views. Team membership is defined on the main scoreboard, so using a personal scoreboard could returnnulleven when both players are on the same team โ or vice versa, allowing players to bypass friendly-fire restrictions. - Fix: Replaced both
player.getScoreboard()calls with a singleBukkit.getScoreboardManager().getMainScoreboard()lookup. Team membership is now always evaluated against the authoritative server scoreboard.
๐ด GunListener โ Memory leak in recentDamage map (Critical)
- Root cause:
recentDamageentries were only evicted bypruneRecentDamage(), which runs after each shot. Players who disconnected (crash,/kick, network drop) or died without triggering a subsequent shot by another player left staleDamageRecordentries in the map indefinitely, growing unbounded on long-running servers. - Fix: Added
recentDamage.remove(p.getUniqueId())to bothonQuit()andonDeath()event handlers, ensuring entries are cleaned up immediately when a player leaves or dies โ in addition to the existing TTL-based pruning.
๐ก GunListener โ Shared static Random causes thread contention and predictable spread (Medium)
- Root cause:
private static final Random RANDOM = new Random()is shared across all usages ofGunListener. Java'sRandomis thread-safe via internal synchronization, which causes lock contention when multiple players fire simultaneously, and its LCG algorithm produces correlated sequences when called rapidly โ making bullet spread slightly predictable. - Fix: Replaced the shared field with a private helper
rng()that returnsThreadLocalRandom.current(). Each thread gets its own independent, higher-quality RNG with zero contention. All threeRANDOM.nextDouble()call sites updated torng().nextDouble().
๐ก StatsManager โ Unsynchronized async DB access on shared connection (Medium)
- Root cause:
flushBuffer()issynchronizedon theStatsManagerinstance, butincrementGunKill()andgetGunKills()โ both called from async tasks โ are not. All three methods share the same JDBCConnection. Concurrent execution offlushBuffer()andincrementGunKill()could interleave SQL transactions, causingSQLITE_BUSYerrors, corrupted auto-commit state, or inconsistent read results ingetGunKills(). - Fix: Marked both
incrementGunKill()andgetGunKills()assynchronized. All three DB-touching methods now synchronize on the same monitor (theStatsManagerinstance), serializing access to the sharedconnectionwithout requiring a connection pool.
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:BSt2YZOI:hf6aFK67"
}
// Legacy Loom dependency
dependencies {
modImplementation "maven.modrinth:BSt2YZOI:hf6aFK67"
}

