Unofficial site, not affiliated with modrinth.com.What is this?
Плагины/CombatGunSSS
Все версииCombatGunSSS 2.1.0

CombatGunSSS 2.1.0

Release1 нед. назад

Список изменений

[2.1.0] - 2026-06-05

Fixed

🔴 EntityDeathListenerUnsupportedOperationException crash on every gun kill (Critical)

  • Root cause: Both onPlayerDeath() and onEntityDeath() called gunListener.getRecentDamage().remove(uuid). Since v2.0.7, getRecentDamage() returns Collections.unmodifiableMap(recentDamage) — calling .remove() on an unmodifiable map throws UnsupportedOperationException immediately. 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 with gunListener.getDamageRecord(uuid), the safe TTL-checked read method that GunListener's own Javadoc already recommended. Entries are now cleaned up by TTL expiry (5 s) and pruneRecentDamage() on the next shot — consistent with the original design intent.

🔴 AmmoManagerConcurrentModificationException / HashMap corruption from async pruneStale() (High)

  • Root cause: lastShot and lastMeleeAttack were plain HashMap instances. pruneStale() is called from runTaskTimerAsynchronously (async thread) while tryShoot(), extendCooldown(), and clearPlayer() modify the same maps on the main thread. A plain HashMap under concurrent access is undefined behaviour in Java — it can throw ConcurrentModificationException or corrupt internal state silently.
  • Fix: Changed both fields to ConcurrentHashMap. All call sites are unchanged; ConcurrentHashMap is 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) bypasses EntityDamageEvent entirely. 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() with player.damage(amount, shooterPlayer) (or player.damage(amount) if the shooter is offline). damage() fires EntityDamageByEntityEvent, correctly consuming absorption hearts and triggering EntityDeathListener with a proper damage cause.
  • Fix 2: Added a bleedingShooters: Map<UUID, UUID> field to track victim → shooter UUID. tryApply() and applyBleeding() now accept a Player shooter parameter (passed through from GunListener.applyEntityDamage()). cancelTask() and cancelAll() 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 nearby LivingEntity instances 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; to isValidTarget() and if (e.hasMetadata("NPC")) continue; to detonateFrag(). No Citizens soft-depend needed — hasMetadata("NPC") is a plain Bukkit API call that returns false when Citizens is absent.

🟠 GunListener — Citizens NPCs pushed by knockback from guns and melee (Medium)

  • Root cause: Both knockback blocks (applyEntityDamage and onMeleeAttack) called target.setVelocity() on any LivingEntity without checking for NPC metadata.
  • Fix: Added !target.hasMetadata("NPC") to both knockback conditions. The NPC metadata check is done before calling setVelocity() — if Citizens is not installed, this check costs a single map lookup that always returns false.

🟡 ReloadManager — Reload progress bar prefix hardcoded in English (Low)

  • Root cause: buildBar() used the literal string "🔄 Reloading " instead of routing through LangManager. All other reload messages already used lang.get(key), making this the only hardcoded English string in the reload system.
  • Fix: Added gun.reload_bar_prefix to both lang/en.yml ("🔄 Reloading") and lang/vi.yml ("🔄 Đang nạp đạn"). buildBar() now calls plugin.getLangManager().get("gun.reload_bar_prefix").

🟡 StatsManager.getTopKillers() — Buffer-only players absent from leaderboard (Low)

  • Root cause: getTopKillers() queried only the SQLite player_stats table. Players who earned kills within the most recent flush window (~5 minutes) existed only in the in-memory buffer and 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 as PlayerStats entries using the online player's display name. The combined list is then re-sorted and trimmed to limit.

Метаданные

Канал релиза

Release

Номер версии

2.1.0

Загрузчики

Bukkit
Paper
Purpur
Spigot

Версии игры

1.21–1.21.11

Загрузок

102

Дата публикации

1 нед. назад

Загрузил

ID версии

Главная