
CombatGunSSS
Adds 45 unique guns to Minecraft, each with custom damage, recoil, fire rate, and reload mechanics, enhancing combat with balanced gameplay, multiple weapon types, and flexible customization for varied playstyles
676
4
Список изменений
[2.0.7] - 2026-05-22
Fixed
🔴 StatsManager.flushBuffer() — Kill data silently lost under concurrent load (Critical)
- Root cause: The old flush implementation called
new HashMap<>(buffer)to snapshot the map, then immediately calledbuffer.clear(). The gap between these two statements allowed the main thread to callrecordKill()and insert a new entry viacomputeIfAbsent. That new entry was then deleted bybuffer.clear()— it was neither in the snapshot (inserted after it was taken) nor survived in the buffer (immediately cleared), so it was never written to SQLite. - Fix: Replaced the snapshot-and-clear pattern with a per-key drain loop: each entry is removed from the
ConcurrentHashMapindividually usingremove(key). SinceConcurrentHashMap.remove()is per-key atomic, any new entry inserted by the main thread after its key has already been drained is guaranteed to remain in the map and be picked up by the next flush cycle. No kill data can be lost.
🔴 ThrowableManager — BukkitRunnable tasks not cancelled on plugin disable (Critical)
- Root cause:
onDisable()stopped the HUD task, bleeding tasks, reload tasks, and the SQLite connection — but never cancelled tasks created byThrowableManager. The fuse-timerBukkitRunnableinhandleThrow()and the smoke particle loop indetonateSmoke()continued to execute after the plugin was disabled, throwingIllegalPluginAccessExceptionfor every remaining tick until the tasks expired naturally. - Fix 1: Added a
cancelAll()method toThrowableManagerthat clearsliveProjectilesand cancels all tracked smoke tasks. - Fix 2:
detonateSmoke()now stores the returnedBukkitTaskreference in aConcurrentHashMap<UUID, BukkitTask>keyed by a random UUID. The task removes its own entry when it completes normally.cancelAll()iterates this map and cancels every outstanding task. - Fix 3:
CombatGunSSSPlugin.onDisable()now callsthrowableManager.cancelAll()alongside the other existing cleanup calls.
🔴 GunListener — Duplicate import statement causes compiler warning (Low / Code Quality)
- Root cause:
GunListener.javacontained two identicalimport java.util.concurrent.ThreadLocalRandom;declarations — one was introduced during a merge and never removed. - Fix: Removed the duplicate import. The class compiles cleanly with a single declaration.
🟡 GunListener.clearAds() — ADS exit incorrectly removes scope slowness (Medium)
- Root cause:
clearAds()used the conditionexisting.getAmplifier() <= 5before removing the Slowness effect. This range is too broad:SCOPE_SLOWNESS = 3falls within it. When a player was simultaneously scoped (Slowness level 3 applied byonSneak) and then exited ADS mode,clearAds()removed the Slowness effect entirely — stripping the scope movement penalty and leaving the player able to move at full speed while scoped. - Fix: Changed the condition to
existing.getAmplifier() != SCOPE_SLOWNESS. ADS exit now only removes the Slowness effect when its amplifier level is not the scope level — leaving scope behaviour intact.
🟡 ReloadManager — Hardcoded English strings bypass i18n system (Medium)
- Root cause: Four player-facing messages in
ReloadManagerwere hardcoded English strings instead of routing throughLangManager: "Magazine is already full!", "No<ammo>in inventory.", "Reload failed: no ammo left.", and the entire "✔ Reloaded! [x/y] -z ammo" completion message. Servers configured withlanguage: vidisplayed English during all reload feedback. - Fix: All four messages now call
plugin.getLangManager().get(key)/.format(key, args)using the existing lang keys (gun.reload_full,gun.reload_no_ammo,gun.reload_failed,gun.reload_done) that were already defined in bothlang/en.ymlandlang/vi.ymlbut never wired up inReloadManager.
🟡 BleedingManager.tryApply() — Inconsistent RNG usage (Medium)
- Root cause:
tryApply()usedMath.random(), which delegates to a single sharedjava.util.Randominstance. This is inconsistent withGunListener, which explicitly replacedstatic RandomwithThreadLocalRandom.current()(with a comment explaining the reasoning). On servers with many simultaneous bleed chance rolls (burst fire hitting multiple targets), the shared lock inMath.random()causes minor contention. - Fix: Replaced
Math.random()withThreadLocalRandom.current().nextDouble(), consistent with the rest of the codebase.
🟡 ThrowableManager.detonateSmoke() — Inconsistent RNG usage (Medium)
- Root cause: Same issue as
BleedingManager—Math.random()used for particle position offsets inside a hot loop (20 calls per 4-tick interval per active smoke grenade). - Fix: Replaced all three
Math.random()calls withThreadLocalRandom.current().nextDouble().
🟡 AttachmentManager — Duplicate import statement (Code Quality)
- Root cause:
AttachmentManager.javacontained two identicalimport org.bukkit.entity.Player;declarations introduced during a refactor. - Fix: Removed the duplicate import.
🟡 GunListener.getRecentDamage() — Mutable map reference exposed via public API (Medium)
- Root cause:
getRecentDamage()returned the internalHashMap<UUID, DamageRecord>reference directly. Any external code (addon plugins accessingGunListenerviaCombatGunAPI) callinggetRecentDamage().clear()or.put(...)could silently corrupt kill-attribution logic without any indication. - Fix 1:
getRecentDamage()now returnsCollections.unmodifiableMap(recentDamage)— external callers can read the map but cannot mutate it. - Fix 2: Added
getDamageRecord(UUID entityId)— a safer lookup method that returns the record only if it is non-null and within the TTL window.EntityDeathListenershould use this instead ofgetRecentDamage().get(uuid)for correct TTL filtering.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Метаданные
Канал релиза
Release
Номер версии
2.0.7
Загрузчики
BukkitPaperPurpurSpigot
Версии игры
1.21–1.21.11
Загрузок
175
Дата публикации
3 нед. назад

