
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
672
4
Список изменений
[2.1.0] - 2026-06-05
Fixed
🔴 EntityDeathListener — UnsupportedOperationException crash on every gun kill (Critical)
- Root cause: Both
onPlayerDeath()andonEntityDeath()calledgunListener.getRecentDamage().remove(uuid). Sincev2.0.7,getRecentDamage()returnsCollections.unmodifiableMap(recentDamage)— calling.remove()on an unmodifiable map throwsUnsupportedOperationExceptionimmediately. Every player death after being hit by a gun produced a full stack trace in console and kill attribution was completely broken. - Fix: Replaced both
.remove()calls withgunListener.getDamageRecord(uuid), the safe TTL-checked read method thatGunListener's own Javadoc already recommended. Entries are now cleaned up by TTL expiry (5 s) andpruneRecentDamage()on the next shot — consistent with the original design intent.
🔴 AmmoManager — ConcurrentModificationException / HashMap corruption from async pruneStale() (High)
- Root cause:
lastShotandlastMeleeAttackwere plainHashMapinstances.pruneStale()is called fromrunTaskTimerAsynchronously(async thread) whiletryShoot(),extendCooldown(), andclearPlayer()modify the same maps on the main thread. A plainHashMapunder concurrent access is undefined behaviour in Java — it can throwConcurrentModificationExceptionor corrupt internal state silently. - Fix: Changed both fields to
ConcurrentHashMap. All call sites are unchanged;ConcurrentHashMapis a drop-in replacement that serialises per-key access correctly.
🟠 BleedingManager — Bleeding kills bypass the damage pipeline; no kill attribution (High)
- Root cause:
player.setHealth(newHp)bypassesEntityDamageEvententirely. Deaths from bleeding had no kill feed message, the shooter's kill stat was not recorded, the victim's death stat was not recorded, and absorption hearts were not consumed (damage was applied directly to base HP). - Fix 1: Replaced
player.setHealth()withplayer.damage(amount, shooterPlayer)(orplayer.damage(amount)if the shooter is offline).damage()firesEntityDamageByEntityEvent, correctly consuming absorption hearts and triggeringEntityDeathListenerwith a proper damage cause. - Fix 2: Added a
bleedingShooters: Map<UUID, UUID>field to track victim → shooter UUID.tryApply()andapplyBleeding()now accept aPlayer shooterparameter (passed through fromGunListener.applyEntityDamage()).cancelTask()andcancelAll()clean up the shooter map alongside the task map.
🟠 GunListener + ThrowableManager — Citizens NPCs hit by bullets and grenades (Medium)
- Root cause:
isValidTarget()had no Citizens NPC check, allowing hitscan bullets to hit NPCs. Likewise,detonateFrag()iterated all nearbyLivingEntityinstances without filtering NPCs. When an NPC died from a gun,EntityDeathListener.onEntityDeath()broadcast a kill-feed message to all players. - Fix: Added
if (entity.hasMetadata("NPC")) return false;toisValidTarget()andif (e.hasMetadata("NPC")) continue;todetonateFrag(). No Citizens soft-depend needed —hasMetadata("NPC")is a plain Bukkit API call that returnsfalsewhen Citizens is absent.
🟠 GunListener — Citizens NPCs pushed by knockback from guns and melee (Medium)
- Root cause: Both knockback blocks (
applyEntityDamageandonMeleeAttack) calledtarget.setVelocity()on anyLivingEntitywithout checking for NPC metadata. - Fix: Added
!target.hasMetadata("NPC")to both knockback conditions. The NPC metadata check is done before callingsetVelocity()— if Citizens is not installed, this check costs a single map lookup that always returnsfalse.
🟡 ReloadManager — Reload progress bar prefix hardcoded in English (Low)
- Root cause:
buildBar()used the literal string"🔄 Reloading "instead of routing throughLangManager. All other reload messages already usedlang.get(key), making this the only hardcoded English string in the reload system. - Fix: Added
gun.reload_bar_prefixto bothlang/en.yml("🔄 Reloading") andlang/vi.yml("🔄 Đang nạp đạn").buildBar()now callsplugin.getLangManager().get("gun.reload_bar_prefix").
🟡 StatsManager.getTopKillers() — Buffer-only players absent from leaderboard (Low)
- Root cause:
getTopKillers()queried only the SQLiteplayer_statstable. Players who earned kills within the most recent flush window (~5 minutes) existed only in the in-memorybufferand were invisible on the leaderboard. - Fix: After the DB query, the method now iterates
buffer, identifies UUIDs not already present in the result list, and appends them asPlayerStatsentries using the online player's display name. The combined list is then re-sorted and trimmed tolimit.
Метаданные
Канал релиза
Release
Номер версии
2.1.0
Загрузчики
BukkitPaperPurpurSpigot
Версии игры
1.21–1.21.11
Загрузок
102
Дата публикации
1 нед. назад

