COWMODSКаталог модов

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

Автор: Duong2012G2,4 тыс.6обновлён 18 июл. 2026 г.
СнаряжениеУправлениеМини-игры

Описание

🔫 CombatGunSSS

Version Platform Java

CombatGunSSS is a high-performance, feature-rich Minecraft gun plugin designed for modern servers (1.21+). It brings a tactical, realistic combat experience with 46 uniquely configured weapons (40 firearms + 6 melee), advanced crafting mechanics, optimized hitscan technology, a full custom events API, and a growing ecosystem of plugin integrations.


🌟 Key Features

  • 🎯 Tactical Combat: Realistic hitscan mechanics with accurate recoil, bullet spread, and headshot multipliers.
  • ⚔️ Melee System: Left-click melee combat with range check, knockback, and separate cooldown system.
  • 🛡️ Advanced Penetration: Projectiles can pass through "soft" blocks and multiple entities based on individual gun stats.
  • 🩸 Progressive Damage: Distance-based Damage Falloff and configurable Knockback intensities.
  • 🛠️ Two-Tier Crafting System:
    • Vanilla Workbench: Craft basic components with official Recipe Book support.
    • Mechanical Crafting Table: A custom 21-slot industrial GUI for assembling advanced firearms.
  • 📂 Interactive Recipe Browser: Built-in visual guide inside the Mechanical Crafting Table — no commands needed! Browse by Gun, Melee, or Ammo category tabs to quickly find what you want to craft.
  • 📦 46 Built-in Weapons: 40 ranged guns + 6 melee weapons with burst-fire, shotgun pellets, and melee logic.
  • 📉 Durability System: Weapons can have limited usage and require repairs (fully configurable).
  • 📊 Persistent HUD: Real-time action bar showing gun name (rarity-colored), visual ammo bar, current/max ammo, and reserve count.
  • 🔄 Auto-Reload: Automatically reloads after firing the last round (toggleable).
  • 🗺️ WorldGuard Integration: Block gun use inside protected regions with a custom gun-shooting flag.
  • 💰 Vault Shop: Players purchase weapons via /gun buy <id> using server economy.
  • 📋 PlaceholderAPI: 8 real-time placeholders for scoreboards, HUDs, and TAB plugins.
  • 🎯 ADS (Aim Down Sights): Per-gun aim-down-sights system. Shift+Right-click to toggle — reduces spread and applies movement penalty.
  • 🩸 Bleeding: Optional damage-over-time on bullet hit. Cured by holding a bandage and pressing [F].
  • 🎒 Ammo Pouch: Compressed ammo bag. Shift+Right-click to unpack into inventory. Give via /gun givepouch.
  • 🌐 Multi-Language (i18n): All messages live in lang/en.yml or lang/vi.yml. Add your own translation file.
  • 🛡️ Anti-Cheat: Automatic exemptions for Vulcan and Matrix to prevent recoil false-positives.
  • 🔌 Developer API: Full custom events — GunShootEvent, GunReloadEvent, GunHitEvent, GunHeadshotEvent.
  • 🤝 Friendly Fire Control: Toggle friendly fire on/off with scoreboard team or permission-group detection.
  • 🎨 Toggleable Effects: Every particle and sound effect can be individually enabled/disabled. Includes a master sound_volume control.
  • 💀 Custom Kill Messages: Death messages show killer name, weapon name (colored by rarity), and headshot indicator.

🎮 Player Controls

ActionControl
ShootRight Click
ReloadSwap Hand Key [F]
Melee AttackLeft Click
Buy a weapon/gun buy <id>
CraftingRight Click on Mechanical Crafting Table

🔫 Weapon Categories

Ranged Weapons (39)

  • Assault Rifles: AK47, M4A1, SCAR, AUG, FAMAS, G36, Groza, AN94, M14, ParaFAL, XM8, Kingfisher
  • SMGs: MP5, P90, Vector, Bizon, UMP, Thompson, MAC-10, MP40, VSS, CG15
  • Snipers: AWM, M24, Kar98k, M82B, M107, VSK94
  • Shotguns: M1014, SPAS-12, MAG-7, M1887, M590, Trogon
  • Pistols: Desert Eagle, G18, USP, M1917, M1873, M500

Melee Weapons (6)

  • Bat: Common, fast swing
  • Knife: Common, highest attack speed
  • Pan: Rare, defensive knockback
  • Parang: Rare, balanced damage/speed
  • Katana: Epic, high damage
  • Scythe: Legendary, maximum damage

🔫 Technical Weapon Stats

Every weapon in CombatGunSSS features a deep set of configurable properties:

  • Damage Falloff: Define exactly at what block distance damage begins to drop (damage_falloff_start) and the minimum damage floor (min_damage_multiplier).
  • Recoil & Spread: Independent Pitch (vertical) and Yaw (horizontal) kick per shot, plus Movement Spread penalties while moving or jumping.
  • Burst Fire: Configure weapons to fire multiple rounds per trigger pull (burst_count) with custom delays.
  • Shotgun Logic: Support for multiple projectiles per shot (projectiles_per_shot) with per-pellet damage calculation.
  • Durability: Set a maximum usage limit (max_durability). Weapons will break and become unusable until repaired.
  • Penetration: Define how many blocks (block_penetration) or entities (entity_penetration) a single bullet can pass through.
  • Melee Range: Maximum attack distance for melee weapons (default: 4 blocks).
  • Knockback: Push strength for both ranged and melee weapons.

📁 Folder Structure

plugins/CombatGunSSS/
├── config.yml              # Main configuration
├── guns/                   # Ranged weapon configs (40 built-in + custom)
│   ├── ak47.yml
│   ├── m4a1.yml
│   └── ...
├── melees/                 # Melee weapon configs (6 built-in + custom)
│   ├── katana.yml
│   ├── knife.yml
│   └── ...
└── README.md

Custom Weapons: Create your own YAML files in guns/ or melees/ folders and run /gun reload.


⚒️ Crafting Mechanics

1. Components (The Basics)

Craft raw materials like Steel Ingots, Gun Barrels, and Springs at a standard Crafting Table. These recipes are automatically unlocked in your Vanilla Recipe Book.

2. Assembly (The Advanced)

Place your components into the Mechanical Crafting Table.

  • Non-grid-based: Just throw the ingredients into the 21-slot input area.
  • Visual Preview: The output slot shows your gun stats before you build it.
  • Recipe Guide: Click the Glowing Book icon in the station to browse all weapon requirements.

📊 Action Bar HUD

While holding any ranged weapon, CombatGunSSS displays a persistent HUD on your action bar:

🔫 AK47  ▐████████████░░░░▌  22 / 30  •  90
  • Gun name is colored by rarity: white (common) → aqua (rare) → light purple (epic) → gold (legendary).
  • Visual bar transitions green → gold (≤25%) → red (≤10%) as ammo depletes.
  • Reserve count turns red when you have no backup ammo left.
  • The HUD automatically yields to the reload progress bar during active reloads.
combatgun:
  hud:
    enabled: true
    update_interval_ticks: 5

🎯 ADS — Aim Down Sights

Enable ADS per gun in its YAML config:

ads:
  enabled: true
  spread_multiplier: 0.35   # 65% tighter accuracy while ADS
  movement_penalty: 0.6     # movement speed while ADS active

Control: Shift+Right-click to toggle ADS on/off. Action bar shows 🎯 ADS [spread ×0.35].

ADS exits automatically when you switch items, teleport, or die. Scopeable sniper rifles use the traditional sneak-to-scope mechanic instead of ADS.


🩸 Bleeding

An optional damage-over-time system. When enabled, bullets hitting players have a configurable chance to cause bleeding.

combatgun:
  bleeding:
    enabled: true
    chance: 0.15
    damage_per_second: 1.0
    duration_seconds: 10
    cure_item: bandage

Hold a bandage (crafting component) and press [F] to cure. Bleeding stops automatically on death.


🎒 Ammo Pouch

A compressed ammo container that stores hundreds of rounds in a single slot.

/gun givepouch ar_ammo 300 PlayerName

Shift+Right-click to unpack all rounds into your inventory. Surplus rounds are left if the inventory is full.


🌐 Multi-Language

Set the active language in config.yml:

combatgun:
  language: vi   # en | vi | (any lang/xx.yml)

Bundled: English (en) and Tiếng Việt (vi). Add a custom translation by placing lang/xx.yml in the plugin data folder and running /gun reload.


🔌 Plugin Integrations

PlaceholderAPI

8 real-time placeholders for use in scoreboards, TAB, AdvancedHud, and similar plugins:

PlaceholderExampleDescription
%combatgun_gun_name%AK47Display name of held gun
%combatgun_gun_id%ak47Internal ID of held gun
%combatgun_gun_rarity%epicRarity of held gun
%combatgun_ammo%24Current magazine ammo
%combatgun_ammo_max%30Magazine capacity
%combatgun_ammo_reserve%90Reserve ammo in inventory
%combatgun_is_reloading%trueWhether player is reloading
%combatgun_is_gun%trueWhether held item is a gun

Vault Shop

Enable the shop in config.yml, set prices per gun, then players can buy weapons in-game:

combatgun:
  shop:
    enabled: true
    currency_symbol: "$"
    guns:
      ak47:  700.0
      awm:  2500.0
      knife: 100.0
/gun buy ak47          # purchase for configured price
/gun buy ak47 free     # admin free-give (requires combatgun.admin)

WorldGuard

The custom flag gun-shooting is automatically registered when WorldGuard is present:

/rg flag <region> gun-shooting deny    # block all guns in region
/rg flag <region> gun-shooting allow   # explicitly allow
/rg flag <region> gun-shooting -g      # remove flag (inherit parent)

Falls back to WorldGuard's built-in PVP flag if gun-shooting is not set on a region.

Anti-Cheat (Vulcan / Matrix)

Auto-detected. When present, players are exempted from motion checks for anticheat.exempt_ticks ticks after each shot to prevent recoil from triggering false positives.

combatgun:
  anticheat:
    exempt_ticks: 3

📜 Commands & Permissions

CommandDescriptionPermission
/gun statsreset <player>Reset a specific player's statscombatgun.admin
/gun seasonreset confirmWipe ALL player stats (season reset)combatgun.admin
/gun give <id> [player]Give a specific weaponcombatgun.admin
/gun giveammo <id> [amt] [player]Give custom ammunitioncombatgun.admin
/gun givepart <id> [amt] [player]Give crafting componentscombatgun.admin
/gun station [player]Give the Mechanical Crafting Tablecombatgun.admin
/gun book [player]Give the Recipe Guide Bookcombatgun.admin
/gun recipe <id>Show the full crafting chain in chatcombatgun.admin
/gun list [category]View all loaded weaponscombatgun.admin
/gun inspect [player]View deep stats of the held weaponcombatgun.admin
/gun reloadReload all configurationscombatgun.admin
/gun buy <id>Purchase a weapon (requires Vault)combatgun.use
/gun givepouch <ammo_id> <amount> [player]Give an ammo pouchcombatgun.admin

Player Permissions

  • combatgun.use — Allows shooting and using guns (default: true)
  • combatgun.use.<gun_id> — Per-gun permission node (e.g. combatgun.use.awm)

🔧 Configuration Examples

Ranged Weapon (AK47)

name: AK47
category: assault_rifles
ammo_type: ar_ammo
damage: 12
fire_rate: 10.0
magazine_size: 30
reload_time: 2.6
headshot_multiplier: 1.8
damage_falloff_start: 35.0
min_damage_multiplier: 0.5
range: 72.0
block_penetration: 0.8
entity_penetration: 0
recoil:
  pitch: 1.45
  yaw: 0.42
  spread: 0.24
  recovery: 0.80
rarity: rare
custom_model_data: 1001
sound: ENTITY_FIREWORK_ROCKET_BLAST
recipe:
  station: mechanical_crafting_table
  ingredients:
    steel_ingot: 6
    gun_barrel: 1
    spring: 2
    hardwood: 3

Melee Weapon (Katana)

name: Katana
category: melee
ammo_type: none
rarity: epic
damage: 10
fire_rate: 1.35
range: 4.0
knockback: 0.3
sound: ENTITY_PLAYER_ATTACK_CRIT
custom_model_data: 26002
recipe:
  station: mechanical_crafting_table
  ingredients:
    steel_ingot: 10
    carbon_fiber: 2
    leather_strip: 3
    blade_core: 1

🔌 Developer API

Custom Events

EventCancellableWhen it fires
GunShootEvent✅Before each shot — cancel or set damage multiplier
GunReloadEvent✅When a player starts reloading
GunHitEvent❌After damage is applied — full damage pipeline
GunHeadshotEvent❌When a shot lands in the head zone
AttachmentApplyEvent✅Before an attachment is fitted to a gun
AttachmentRemoveEvent✅Before an attachment is removed from a gun
@EventHandler
public void onShoot(GunShootEvent event) {
    if (isInsideArena(event.getShooter()))
        event.setDamageMultiplier(2.0);
}

@EventHandler
public void onReload(GunReloadEvent event) {
    if (isCapturingObjective(event.getPlayer()))
        event.setCancelled(true);
}

@EventHandler
public void onHit(GunHitEvent event) {
    // base damage, final damage, headshot flag, distance
    plugin.getStats().record(
        event.getShooter(), event.getGun().getId(),
        event.getFinalDamage(), event.isHeadshot(), event.getDistance());
}

@EventHandler
public void onHeadshot(GunHeadshotEvent event) {
    event.getShooter().giveExp(5);
}

API Instance

CombatGunAPI api = CombatGunAPI.getInstance();

// Query guns
GunData gun              = api.getGun("ak47");
Collection<GunData> all  = api.getAllGuns();

// Create items
ItemStack gunItem         = api.createGunItem("ak47");
ItemStack ammo            = api.createAmmoItem("ar_ammo", 30);

// Check items
boolean isGun             = api.isGun(item);
String  gunId             = api.getGunId(item);

// Player state
boolean isReloading       = api.isReloading(player.getUniqueId());

// Hook state
boolean shopEnabled       = plugin.getHookManager().getVaultHook().isEnabled();
boolean wgActive          = plugin.getHookManager().getWorldGuardHook().isAvailable();

🚀 Installation

  1. Download the CombatGunSSS-2.3.0.jar.
  2. Drop it into your server's plugins folder.
  3. (Optional) Install any soft-depend plugins you want: PlaceholderAPI, Vault, WorldGuard, Vulcan, Matrix.
  4. Restart the server to generate default configurations.
  5. (Optional) Add a Resource Pack to see 3D gun models.

Requirements

  • Server: Paper/Spigot 1.21+
  • Java: 21 or higher
  • Required dependencies: None (standalone plugin)
  • Optional dependencies: PlaceholderAPI, Vault + economy plugin, WorldGuard, Vulcan, Matrix

📋 Changelog

See CHANGELOG.md for the full version history.

2.3.0 — Critical exploit + melee combat fix

  • Fixed ammo/component items (built on vanilla materials like gold/iron nuggets) being craftable or smeltable into real vanilla resources without a resource pack — closed both the crafting-grid and furnace-smelting paths
  • Fixed melee weapons (katana, knife, pan, etc.) dealing no damage and not affecting the target at all — hit sound played but the attack silently did nothing due to an event-recursion bug

2.2.0 — Permission-gate fix release + Recipe Browser categories

  • Fixed /gun command tree blocking every player-facing subcommand (buy, attach, detach, attachments, stats, leaderboard, recipe) for non-admins — both onCommand() and onTabComplete() were hard-gated behind combatgun.admin
  • Fixed VaultHook.buyGun() silently discarding a purchased weapon when the buyer's inventory was full
  • Added Gun / Melee / Ammo category tabs to the Recipe Browser so players can find craftable items faster

2.1.0 — Bug-fix release: 8 bugs fixed

  • Fixed StatsManager.flushBuffer() race condition — kill data silently lost under concurrent load
  • Fixed ThrowableManager tasks not cancelled on plugin disable — IllegalPluginAccessException on every disable
  • Fixed clearAds() removing scope slowness incorrectly
  • Fixed all ReloadManager messages bypassing i18n (hardcoded English shown to vi servers)
  • Fixed BleedingManager and ThrowableManager using Math.random() instead of ThreadLocalRandom
  • Fixed AttachmentManager and GunListener duplicate import statements
  • Fixed getRecentDamage() exposing mutable map reference; added safe getDamageRecord() lookup

2.0.6 — Bug-fix release: 4 bugs fixed

  • Fixed friendly-fire scoreboard team detection using player scoreboard instead of main scoreboard — could allow friendly-fire bypass
  • Fixed recentDamage memory leak: entries now removed on player quit and death events
  • Fixed shared static Random contention: replaced with ThreadLocalRandom.current() (per-thread, no lock, better entropy)
  • Fixed async SQLite connection safety: incrementGunKill() and getGunKills() now synchronized alongside flushBuffer()

2.0.5 — Developer API & architecture release: 4 new additions, 1 improvement

  • New AttachmentApplyEvent and AttachmentRemoveEvent (cancellable) — fired on /gun attach and /gun detach
  • New DamageCalculator utility class — centralises all damage math extracted from GunListener (headshots, falloff, spread, pellets)
  • New kills_by_gun_detail SQLite table (schema v3) — per-kill log with timestamp, weapon, headshot flag
  • New commands: /gun statsreset <player> and /gun seasonreset confirm
  • GunListener God Class partially refactored — 4 methods delegate to DamageCalculator

2.0.4 — Stability & performance release: 2 bug-fixes, 3 improvements

  • Fixed thread-safety data race in StatsManager stat buffer (ConcurrentHashMap)
  • Fixed silent loading of invalid gun configs (YAML validation with clear per-field errors)
  • StatsManager leaderboard query optimized with a kills DESC index (schema migration v2)
  • Shop price reads now cached in memory; invalidated on /gun reload
  • Config version detection warns admins when config.yml is outdated after an update

2.0.3 — Bug-fix release: 7 bugs fixed

  • Ammo Pouch no longer loses items when the inventory is full
  • Reload task CPU usage reduced by 50% (changed from 1 tick to 2 tick interval)
  • isHeadshot() now works accurately when the player is crouching
  • craftFromStation() properly rolls back ingredients if crafting fails
  • BleedingManager now uses wall-clock time for accurate damage even during server lag
  • applyRecoil() is now fully null-safe
  • Fixed race condition in onItemHeldChange

🔍 Troubleshooting

Friendly fire still occurring despite being disabled
  • Fixed in 2.0.6 — team detection now uses the main scoreboard instead of each player's personal view
  • Ensure teams are set up with /team add <name> and players added with /team join <name> <player>
  • Test with combatgun.team_provider: scoreboard in config.yml
Guns not working / can't shoot
  • Check combatgun.use permission is granted to players
  • Make sure the world is not blacklisted in config.yml
  • If using WorldGuard, check if the region has gun-shooting deny
  • Try /gun give ak47 and test in a clean area
  • Use Paper — Spigot is not supported
Ammo not found / can't reload
  • Make sure you have the correct ammo type in your inventory (e.g. AR ammo for assault rifles)
  • Craft ammo at a Vanilla Crafting Table or Mechanical Crafting Table
  • Check ammo_type in the gun's YAML matches a defined ammo type in config.yml
  • Run /gun reload after editing configs
Recipes not showing in crafting table
  • Run /gun reload to re-register vanilla recipes
  • Give yourself the Recipe Guide Book: /gun book
  • Ensure the ingredient IDs in the gun YAML match those defined in config.yml
Console warning: "config.yml is OUTDATED"
  • This warning appears when your config.yml has a lower config-version than the plugin expects.
  • Back up your current config.yml, delete it, and restart the server to regenerate a fresh one.
  • Then manually copy over your custom settings (world lists, shop prices, effects, etc.) from the backup.
  • The plugin loads normally even with an outdated config — default values are used for missing keys.
Gun config not loading / "config validation failed" warning
  • Added in 2.0.4 — guns with invalid configs now log a clear per-field error instead of loading silently.
  • Check the listed fields: damage, fire_rate, magazine_size, reload_time must all be greater than 0.
  • burst_count must be at least 1. name cannot be blank.
  • Fix the values in the gun's YAML and run /gun reload.
Leaderboard (`/gun leaderboard`) is slow on large servers
  • Fixed in 2.0.4 — a kills DESC index is now created automatically on the SQLite database.
  • The migration runs once on first startup of 2.0.4. If you still experience slowness, ensure the server was fully restarted (not /reload).
Ammo Pouch destroying items when inventory is full
  • Fixed in 2.0.3 — update the plugin
  • Partial unpacks now save remaining ammo back into the pouch instead of destroying it
Reload taking wrong amount of time / inconsistent
  • Fixed in 2.0.3 — reload task now runs every 2 ticks instead of 1 (50% less CPU, same reload duration)
  • If still wrong, check reload_time in the gun's YAML (in seconds)
Headshots not registering correctly
  • Fixed in 2.0.3 — crouching player hitbox now calculated correctly
  • Headshot detection uses the top 20% of the entity hitbox, adjusted for crouch state
Bleeding damage lower than configured
  • Fixed in 2.0.3 — BleedingManager now uses wall-clock time instead of tick counting
  • Damage is now accurate even under server lag
Plugin crashes on startup
  • Fixed in 2.0.1 — startup errors are now isolated per manager
  • Check console for [CombatGunSSS] error lines indicating which manager failed
  • Common cause: corrupted config.yml or invalid gun YAML syntax
  • Fix the config, then restart (do not use /reload)
NullPointerException when shooting
  • Fixed in 2.0.2 — lazy HudManager access prevents NPE at startup
  • If persisting: check that HookManager is not null in the stack trace and update to latest version
Anti-cheat flagging recoil movement
  • Ensure Vulcan or Matrix is listed in softdepend (already done automatically)
  • Increase combatgun.anticheat.exempt_ticks in config.yml (default: 3)
  • The plugin auto-detects and registers exemptions via reflection — no API jar needed
PlaceholderAPI placeholders showing as raw text
  • Install PlaceholderAPI and run /papi reload
  • Ensure PlaceholderAPI is loaded before CombatGunSSS (restart, not /reload)
  • Test with /papi parse me %combatgun_gun_name%
Vault shop not working
  • Make sure an economy plugin (EssentialsX, CMI, etc.) is installed alongside Vault
  • Set combatgun.shop.enabled: true in config.yml
  • Confirm the gun ID exists in combatgun.shop.guns price list
  • Use /gun buy <id> free (admin) to test without balance requirement
/reload breaks the plugin
  • Never use /reload — always do a full server restart
  • Use /gun reload to hot-reload gun configs, language files, and settings without restarting
Custom gun YAML not loading
  • Place the file inside plugins/CombatGunSSS/guns/ (ranged) or melees/ (melee)
  • Run /gun reload — check console for warnings about your file
  • Ensure required fields are present: name, category, ammo_type, damage, magazine_size
  • YAML is whitespace-sensitive — use a validator like yaml.org/start.html

❓ FAQ

Does this work on Spigot?

No — CombatGunSSS requires Paper 1.21+. Spigot lacks several API features used internally.

Can I add my own guns?

Yes. Create a new .yml file in plugins/CombatGunSSS/guns/ following the same format as the built-in weapon files, then run /gun reload.

Can I disable specific guns?

Simply delete or remove the gun's YAML file from the guns/ folder and run /gun reload. Players holding that gun will keep the item but it won't function.

Does it support multiple worlds?

Yes. Configure a world whitelist or blacklist under combatgun.worlds in config.yml.

Is it laggy on large servers?

CombatGunSSS uses hitscan (ray-trace, not projectile entities), so it is significantly lighter than projectile-based gun plugins. Heavy effects like bullet trails can be toggled off in config.yml under combatgun.effects.

Can I disable the crafting system?

Yes — simply don't give players the Mechanical Crafting Table or Recipe Book. Use /gun give for admin distribution. Vanilla workbench recipes can also be disabled per-item.

Do I need a Resource Pack?

No. The plugin works without a resource pack. However, each gun has a custom_model_data value pre-configured for you to link 3D models if you have a pack.

Can I disable bleeding or the Ammo Pouch?

Yes. Set combatgun.bleeding.enabled: false or combatgun.ammo_pouch.enabled: false in config.yml.

Does it support ZombieApocalypseSSS?

Yes — CombatGunSSS is the recommended gun companion for ZombieApocalypseSSS. Guns deal full damage to all zombie types and trigger the noise / aggro system automatically.

Can other plugins listen to gun events?

Yes. Four custom events are exposed via the API: GunShootEvent (cancellable), GunReloadEvent (cancellable), GunHitEvent, and GunHeadshotEvent. See the Developer API section.

How do I give guns without the shop?

Use /gun give <id> [player] (requires combatgun.admin). Example: /gun give awm Steve.


Developed with ❤️ by Duong2012G.


[!NOTE] This plugin is built on the Paper/Spigot 1.21 API and requires Java 21 or higher.

[!TIP] For support and updates, check the repository or contact the developer.

Галерея

Recipe GuideMechanical Crafting TableRecipe Browser in Mechanical Crafting Table

Последние версии

Все версии →
release

CombatGunSSS 2.3.0

2.3.0
18 июл. 2026 г.1,2 тыс.Minecraft: 1.21, 1.21.1, 1.21.2, 1.21.3, 1.21.4, 1.21.5, 1.21.6, 1.21.7, 1.21.8, 1.21.9, 1.21.10, 1.21.11bukkit, paper, purpur, spigot
Скачать
Что нового
## [2.3.0] - 2026-07-18 ### Fixed #### 🔴 `CustomItemManager` — Ammo/component items craftable into real vanilla resources without a resource pack (Critical / Economy exploit) - **Root cause:** Ammo and component items are built on top of vanilla `Material`s purely so a resource pack has something to visually re-skin — e.g. `ar_ammo` = `Material.GOLD_NUGGET`, `smg_ammo` (and the fallback case) = `Material.IRON_NUGGET`, `sg_ammo` = `Material.CLAY_BALL`, and ingot-type components = `Material.IRON_INGOT`. Without a resource pack installed, players see these exactly as plain vanilla nuggets/ingots — and worse, they *are* those vanilla materials underneath the custom name/lore/model data. The vanilla crafting grid recognised 9 `ar_ammo` items as 9 real `GOLD_NUGGET`s and happily crafted them into a real `GOLD_INGOT` (same for `smg_ammo` → iron ingot, and `sg_ammo` → brick via furnace smelting), letting players convert unlimited free ammo into real vanilla resources. - **Fix 1:** Added `CustomItemManager.isCustomItem(ItemStack)` — a generic check for our `custom_item_type` PDC tag, true for any ammo/component/station item regardless of which vanilla `Material` it's built on. - **Fix 2:** `CraftingListener.onPrepareCraft()` now blocks the vanilla crafting-table result (`craftingInventory.setResult(null)`) whenever the 3×3/2×2 grid contains any of our tagged items and the grid doesn't match one of our own recognised workbench recipes. - **Fix 3:** New `CraftingListener.onFurnaceSmelt()` handler cancels `FurnaceSmeltEvent` (covers furnace, blast furnace, and smoker) whenever the item being smelted carries our custom tag — closing the same hole for materials with a vanilla smelting recipe (e.g. clay ball → brick). #### 🔴 `GunListener.onMeleeAttack()` — Melee weapons play the hit sound but deal no damage and don't affect the target (Critical) - **Root cause:** `onMeleeAttack()` cancelled the original swing's `EntityDamageByEntityEvent` and then called `target.damage(gun.getDamage(), player)` to apply custom damage manually. That call fires its *own* fresh `EntityDamageByEntityEvent` with the same damager (the attacking player) and the same `ENTITY_ATTACK` cause — which re-enters `onMeleeAttack()` a second time, synchronously, before the outer call returns. On this inner call, `ammoManager.tryMeleeAttack()` always failed (the cooldown had just been consumed a few lines above in the outer call), so the inner call cancelled *its* event — which was the event actually carrying the damage. The outer call then continued on to play the hit sound and particles regardless, producing the exact symptom reported: audible hit, zero damage, target completely unaffected. This affected melee weapons only (ranged hitscan damage isn't triggered from a listened `EntityDamageByEntityEvent`, so it can't recurse the same way). - **Fix:** Added a `meleeInProgress` reentrancy guard (`Set<UUID>`). The player's UUID is added immediately before calling `target.damage(...)` and removed in a `finally` block right after. If `onMeleeAttack()` re-enters for the same player while their UUID is already in the set, it now returns immediately without touching `event.setCancelled()` — letting the synthetic inner event proceed uncancelled so the damage actually applies. ---
release

CombatGunSSS 2.2.0

2.2.0
18 июл. 2026 г.27Minecraft: 1.21, 1.21.1, 1.21.2, 1.21.3, 1.21.4, 1.21.5, 1.21.6, 1.21.7, 1.21.8, 1.21.9, 1.21.10, 1.21.11bukkit, paper, purpur, spigot
Скачать
Что нового
## [2.2.0] - 2026-07-18 ### Fixed #### 🔴 `GunCommand` — Every player-facing subcommand blocked for non-admins (Critical) - **Root cause:** `onCommand()` gated the entire `/gun` command tree behind `combatgun.admin` before dispatching to any subcommand, and `plugin.yml` additionally declared `permission: combatgun.admin` directly on the command — so Bukkit's own command dispatcher rejected non-admins before `onCommand()` ever ran. This silently broke `/gun buy` (the Vault shop), `/gun attach` / `/gun detach` / `/gun attachments`, `/gun stats`, `/gun leaderboard`, and `/gun recipe` for every player without `combatgun.admin`, even though several of these handlers already contained their own `combatgun.use` check — a check that could never actually execute. - **Fix:** Removed the command-level `permission:` entry from `plugin.yml`. `onCommand()` now checks a new `PLAYER_SUBCOMMANDS` set — `buy, attach, detach, attachments, stats, leaderboard, recipe`, the self-service subcommands that take no target-player argument — against `combatgun.use` OR `combatgun.admin`. Every other subcommand (`give, giveammo, givepart, station, book, givepouch, givethrowable, inspect, reload, statsreset, seasonreset`) still requires `combatgun.admin`, since each of those can act on an arbitrary target player and is not meant to be self-service. #### 🔴 `GunCommand.onTabComplete()` — Same admin-only gate hid tab-completion for regular players (High) - **Root cause:** Identical bug to the one above — `onTabComplete()` returned an empty list for any sender without `combatgun.admin`. Even after fixing `onCommand()`, non-admins would still see zero suggestions when typing `/gun `. - **Fix:** `onTabComplete()` now mirrors the `PLAYER_SUBCOMMANDS` permission split — non-admins with `combatgun.use` see the subcommand names they're allowed to run, and id-completion for `/gun recipe <id>` (a read-only lookup) is available to them as well. Argument completion for admin-only give/inspect/config subcommands remains gated behind `combatgun.admin`. #### 🟠 `VaultHook.buyGun()` — Purchased weapon silently lost when inventory is full (Medium) - **Root cause:** `player.getInventory().addItem(gunItem)` was called without checking its return value. `addItem()` silently drops any `ItemStack` that doesn't fit, so a player with a full inventory was charged the full price but never actually received the gun. - **Fix:** The leftover `ItemStack`s returned by `addItem()` are now dropped at the player's feet via `dropItemNaturally()`, and the player receives an additional chat message letting them know the purchase was dropped rather than lost. ### Added #### `CraftingManager` — Recipe Browser split into Gun / Melee / Ammo tabs - The recipe browser (opened from the Mechanical Crafting Table's book icon) now shows three clickable category tabs in its bottom row instead of one long mixed list, so players can find what they want to craft without scrolling past unrelated items. - New `CraftingManager.BrowserCategory` enum (`GUN`, `MELEE`, `AMMO`). Guns and melee weapons are both stored under `RecipeData.RecipeKind.GUN` internally, so the split is done by checking `GunData.isMelee()` rather than changing the existing recipe-kind enum — this avoids touching the exhaustive `switch` on `RecipeKind` in `CustomItemManager`. - Each player's last-viewed tab is remembered in-memory (keyed by UUID) so paging with prev/next stays on the same category instead of resetting to Gun; the cache entry is cleared on `PlayerQuitEvent` to avoid unbounded growth. - A category with no recipes configured shows an explicit "No recipes here" placeholder instead of an empty page. - `openRecipeBrowser(Player, int)` is preserved for existing call sites and now delegates to a new `openRecipeBrowser(Player, int, BrowserCategory)` overload. ---
release

CombatGunSSS 2.1.0

2.1.0
6 июн. 2026 г.479Minecraft: 1.21, 1.21.1, 1.21.2, 1.21.3, 1.21.4, 1.21.5, 1.21.6, 1.21.7, 1.21.8, 1.21.9, 1.21.10, 1.21.11bukkit, paper, purpur, spigot
Скачать
Что нового
## [2.1.0] - 2026-06-05 ### Fixed #### 🔴 `EntityDeathListener` — `UnsupportedOperationException` 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. #### 🔴 `AmmoManager` — `ConcurrentModificationException` / 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

CombatGunSSS 2.0.7

2.0.7
22 мая 2026 г.203Minecraft: 1.21, 1.21.1, 1.21.2, 1.21.3, 1.21.4, 1.21.5, 1.21.6, 1.21.7, 1.21.8, 1.21.9, 1.21.10, 1.21.11bukkit, paper, purpur, spigot
Скачать
Что нового
## [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 called `buffer.clear()`. The gap between these two statements allowed the main thread to call `recordKill()` and insert a new entry via `computeIfAbsent`. That new entry was then deleted by `buffer.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 `ConcurrentHashMap` individually using `remove(key)`. Since `ConcurrentHashMap.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 by `ThrowableManager`. The fuse-timer `BukkitRunnable` in `handleThrow()` and the smoke particle loop in `detonateSmoke()` continued to execute after the plugin was disabled, throwing `IllegalPluginAccessException` for every remaining tick until the tasks expired naturally. - **Fix 1:** Added a `cancelAll()` method to `ThrowableManager` that clears `liveProjectiles` and cancels all tracked smoke tasks. - **Fix 2:** `detonateSmoke()` now stores the returned `BukkitTask` reference in a `ConcurrentHashMap<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 calls `throwableManager.cancelAll()` alongside the other existing cleanup calls. #### 🔴 `GunListener` — Duplicate `import` statement causes compiler warning (Low / Code Quality) - **Root cause:** `GunListener.java` contained two identical `import 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 condition `existing.getAmplifier() <= 5` before removing the Slowness effect. This range is too broad: `SCOPE_SLOWNESS = 3` falls within it. When a player was simultaneously scoped (Slowness level 3 applied by `onSneak`) 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 `ReloadManager` were hardcoded English strings instead of routing through `LangManager`: "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 with `language: vi` displayed 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 both `lang/en.yml` and `lang/vi.yml` but never wired up in `ReloadManager`. #### 🟡 `BleedingManager.tryApply()` — Inconsistent RNG usage (Medium) - **Root cause:** `tryApply()` used `Math.random()`, which delegates to a single shared `java.util.Random` instance. This is inconsistent with `GunListener`, which explicitly replaced `static Random` with `ThreadLocalRandom.current()` (with a comment explaining the reasoning). On servers with many simultaneous bleed chance rolls (burst fire hitting multiple targets), the shared lock in `Math.random()` causes minor contention. - **Fix:** Replaced `Math.random()` with `ThreadLocalRandom.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 with `ThreadLocalRandom.current().nextDouble()`. #### 🟡 `AttachmentManager` — Duplicate `import` statement (Code Quality) - **Root cause:** `AttachmentManager.java` contained two identical `import 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 internal `HashMap<UUID, DamageRecord>` reference directly. Any external code (addon plugins accessing `GunListener` via `CombatGunAPI`) calling `getRecentDamage().clear()` or `.put(...)` could silently corrupt kill-attribution logic without any indication. - **Fix 1:** `getRecentDamage()` now returns `Collections.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. `EntityDeathListener` should use this instead of `getRecentDamage().get(uuid)` for correct TTL filtering. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
release

CombatGunSSS 2.0.6

2.0.6
14 мая 2026 г.86Minecraft: 1.21, 1.21.1, 1.21.2, 1.21.3, 1.21.4, 1.21.5, 1.21.6, 1.21.7, 1.21.8, 1.21.9, 1.21.10, 1.21.11bukkit, paper, purpur, spigot
Скачать
Что нового
## [2.0.6] - 2026-05-14 ### Fixed #### 🔴 `GunListener.isFriendlyFire()` — Scoreboard team detection using wrong scoreboard (Critical) - **Root cause:** `shooter.getScoreboard()` and `targetPlayer.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 return `null` even 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 single `Bukkit.getScoreboardManager().getMainScoreboard()` lookup. Team membership is now always evaluated against the authoritative server scoreboard. #### 🔴 `GunListener` — Memory leak in `recentDamage` map (Critical) - **Root cause:** `recentDamage` entries were only evicted by `pruneRecentDamage()`, which runs *after each shot*. Players who disconnected (crash, `/kick`, network drop) or died without triggering a subsequent shot by another player left stale `DamageRecord` entries in the map indefinitely, growing unbounded on long-running servers. - **Fix:** Added `recentDamage.remove(p.getUniqueId())` to both `onQuit()` and `onDeath()` 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 of `GunListener`. Java's `Random` is 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 returns `ThreadLocalRandom.current()`. Each thread gets its own independent, higher-quality RNG with zero contention. All three `RANDOM.nextDouble()` call sites updated to `rng().nextDouble()`. #### 🟡 `StatsManager` — Unsynchronized async DB access on shared connection (Medium) - **Root cause:** `flushBuffer()` is `synchronized` on the `StatsManager` instance, but `incrementGunKill()` and `getGunKills()` — both called from async tasks — are not. All three methods share the same JDBC `Connection`. Concurrent execution of `flushBuffer()` and `incrementGunKill()` could interleave SQL transactions, causing `SQLITE_BUSY` errors, corrupted auto-commit state, or inconsistent read results in `getGunKills()`. - **Fix:** Marked both `incrementGunKill()` and `getGunKills()` as `synchronized`. All three DB-touching methods now synchronize on the same monitor (the `StatsManager` instance), serializing access to the shared `connection` without requiring a connection pool. ---
release

CombatGunSSS 2.0.5

2.0.5
26 апр. 2026 г.109Minecraft: 1.21, 1.21.1, 1.21.2, 1.21.3, 1.21.4, 1.21.5, 1.21.6, 1.21.7, 1.21.8, 1.21.9, 1.21.10, 1.21.11bukkit, paper, purpur, spigot
Скачать
Что нового
## [2.0.5] - 2026-04-26 ### Added #### `AttachmentApplyEvent` and `AttachmentRemoveEvent` — Developer API - Two new cancellable events fire when a player attaches or removes an attachment from a gun. - **`AttachmentApplyEvent`** — fired before `fitAttachment()` writes the PDC key. Provides `getPlayer()`, `getGunData()`, `getIncoming()` (attachment being fitted), and `getReplacedAttachment()` (previously occupied slot, or `null`). Cancellable — cancel to block the attachment action. - **`AttachmentRemoveEvent`** — fired before `removeAttachment()` clears the PDC key. Provides `getPlayer()`, `getGunData()`, `getSlot()`, and `getRemoved()` (the attachment being stripped). Cancellable — cancel to block the removal. - `AttachmentManager.fitAttachment(Player, ItemStack, GunData, String)` — new overload that fires the apply event. The no-player overload is retained for internal use. - `AttachmentManager.removeAttachment(Player, ItemStack, GunData, AttachmentType)` — new overload that fires the remove event. - Example use-cases: locking legendary weapons from modification, auto-returning detached attachments to the player's inventory, logging equipment changes, restricting attachments in specific regions. #### `DamageCalculator` — Centralised Damage Math Utility - New `util/DamageCalculator.java` extracts all damage-pipeline calculations from `GunListener`, making them independently testable and accessible to add-on plugins. - **Static methods**: - `baseDamage(GunData, double eventMult)` — gun damage × projectile multiplier × event multiplier. - `falloffMultiplier(GunData, double dist)` — linear range-based damage reduction from `damageFalloffStart` to min floor. - `headshotMultiplier(GunData, boolean headshot)` — returns `headshotMultiplier` or `1.0`. - `finalDamage(GunData, double dist, boolean headshot, double eventMult)` — full pipeline in one call. - `isHeadshot(double hitY, double baseY, double height, boolean crouching)` — single source of truth for headshot detection, accounting for crouch state. - `effectiveSpread(GunData, double scopeSpread, boolean scoped, boolean ads, double moveMult)` — spread value for all fire modes. - `pelletSpread(double baseSpread, boolean multiPellet)` — per-pellet spread for shotguns. - **Named constants**: `HEAD_ZONE_START` (0.80), `CROUCH_FACTOR` (0.85), `SHOTGUN_SPREAD_FACTOR` (1.45) — previously magic numbers scattered across `GunListener`. - `GunListener` now delegates all damage math and spread calculations to `DamageCalculator`. The private `computeFalloffMultiplier()` method has been removed (logic moved to `DamageCalculator.falloffMultiplier()`). #### `StatsManager` — Per-Kill Detail Log (`kills_by_gun_detail` table) - New SQLite table `kills_by_gun_detail` added via schema migration v3. Stores one row per kill with `uuid`, `gun_id`, `headshot` flag, and Unix `timestamp` (ms). - Enables future features: per-weapon leaderboards, session-based kill analytics, and season resets that preserve historical totals in the detail log while zeroing the aggregate counters. - Two covering indexes (`idx_kbgd_uuid`, `idx_kbgd_gun`) ensure per-player and per-weapon queries stay fast as the table grows. - Schema migration runs automatically on first startup of 2.0.5 — no manual SQL required. #### `GunCommand` — Season Reset and Per-Player Stats Reset - **`/gun statsreset <player>`** — resets kills, deaths, headshots, gun kills, and detail records for a single online player. Wipes the in-memory buffer and schedules async DB deletes across all three stat tables. - **`/gun seasonreset confirm`** — resets ALL players' stats (requires the literal `confirm` argument as a safeguard against accidental use). Clears the in-memory buffer and truncates `player_stats`, `gun_kills`, and `kills_by_gun_detail` in a single transaction. Prints a gold confirmation message on success. - Both commands require `combatgun.admin` permission. - Both commands fail gracefully with a clear error if `StatsManager` is disabled (SQLite unavailable). ### Improved #### `GunListener` — God Class Reduction - `performShot()` spread logic (previously a three-branch `if`/`else if`/`else` block) replaced with a single `DamageCalculator.effectiveSpread()` call. - `applyEntityDamage()` damage calculation (previously inline) replaced with `DamageCalculator.finalDamage()` and `DamageCalculator.baseDamage()`. - `isHeadshot()` private method now delegates entirely to `DamageCalculator.isHeadshot()` — single source of truth for headshot geometry. - Removed `computeFalloffMultiplier()` private method (~7 lines) — logic now lives in `DamageCalculator`. #### `StatsManager` — Schema Version Bumped to 3 - `SCHEMA_VERSION` constant updated from `2` to `3`. - `recordKill()` now passes the `headshot` flag to `incrementGunKill()` so both the aggregate `gun_kills` table and the new `kills_by_gun_detail` table are updated in one async task. - `resetStats(UUID)` now also deletes from `kills_by_gun_detail` in addition to `player_stats` and `gun_kills`. - `resetAllStats()` truncates all three stat tables in a single transaction. ---
release

CombatGunSSS 2.0.3

2.0.3
18 апр. 2026 г.65Minecraft: 1.21, 1.21.1, 1.21.2, 1.21.3, 1.21.4, 1.21.5, 1.21.6, 1.21.7, 1.21.8, 1.21.9, 1.21.10, 1.21.11paper, purpur
Скачать
Что нового
## [2.0.3] - 2026-04-18 ### Fixed #### 🔴 `AmmoPouchManager.unpackPouch()` — Pouch lost when inventory is full (Critical) - **Root cause:** When partially unpacking (only a few slots left in inventory), the code always called `pouch.setAmount(0)` regardless of how many bullets were actually transferred. The entire pouch was destroyed even if bullets remained inside. - **Fix:** If `remaining > 0` (unpack not completed), update the PDC `pouch_quantity` to the remaining amount and refresh the lore instead of destroying the pouch. Only call `setAmount(0)` when all bullets have been successfully transferred. #### 🔴 `ReloadManager` — Reload task running every 1 tick causing high CPU usage (Critical) - **Root cause:** `runTaskTimer(plugin, 0L, 1L)` scheduled 20 times per second for every player reloading. With 10 players reloading simultaneously, this resulted in 200 task calls per second just for the progress bar. - **Fix:** Changed to `runTaskTimer(plugin, 0L, 2L)` and adjusted `totalTicks = reloadTime * 10.0` so reload duration remains unchanged. This reduces reload system CPU load by approximately 50%. #### 🔴 `GunListener.isHeadshot()` — Inaccurate headshots while player is crouching (Critical) - **Root cause:** Used fixed `entity.getHeight() * 0.8` without accounting for crouching players (hitbox shrinks to ~1.5 blocks instead of 1.8). - **Fix:** Detect `player.isSneaking()` and multiply `height *= 0.85` before calculating headY threshold. Headshot detection is now accurate for both standing and crouching states. #### 🔴 `CraftingManager.craftFromStation()` — No rollback when crafting fails (Critical) - **Root cause:** `consumeIngredients()` ran before `createResultItem()`. If `createResultItem()` returned `null` (invalid gun ID, config error, etc.), ingredients were consumed but no item was given → permanent item loss. - **Fix:** Snapshot all input slots before consuming. If `createResultItem()` returns `null`, restore ingredients from the snapshot. #### 🟡 `GunListener.applyRecoil()` — Potential NPE when AntiCheatHook is disabled (Medium) - **Root cause:** `if (hm2 != null) hm2.getAntiCheatHook().exempt(player)` — checked `hm2` but not `getAntiCheatHook()`. If the hook was disabled, `getAntiCheatHook()` could return an uninitialized instance. - **Fix:** Added full guard: `hm2 != null && hm2.getAntiCheatHook() != null && hm2.getAntiCheatHook().isActive()` before calling `exempt()`. #### 🟡 `BleedingManager` — Incorrect damage during server lag (Medium) - **Root cause:** Task used `elapsed += 20` to count ticks, but when the server lagged (>50ms per tick), actual damage was lower than the configured `damage_per_second`. - **Fix:** Switched to `System.currentTimeMillis()` for real-time tracking. `lastDamageTime` is now updated only when damage is actually applied, ensuring exactly 1 second between damage ticks regardless of server lag. #### 🟡 `CraftingManager.consumeIngredients()` — Incorrect null-check order (Medium) - **Root cause:** `identifyIngredient(item)` was called before checking `item == null`, causing potential NPE with empty inventory slots in some edge cases. - **Fix:** Moved `if (item == null || item.getType().isAir()) continue;` before calling `identifyIngredient()`. #### 🟡 `GunListener.onItemHeldChange()` — Race condition when switching slots rapidly (Medium) - **Root cause:** Task with 0-tick delay read `getItemInMainHand()` — if the player switched slots again before the task ran, it would process the wrong slot's item. - **Fix:** Store `newSlot = event.getNewSlot()` before scheduling the task. Inside the task, check `player.getInventory().getHeldItemSlot() != newSlot` and abort if the player has switched slots again.
release

CombatGunSSS 2.0.2

2.0.2
14 апр. 2026 г.41Minecraft: 1.21, 1.21.1, 1.21.2, 1.21.3, 1.21.4, 1.21.5, 1.21.6, 1.21.7, 1.21.8, 1.21.9, 1.21.10, 1.21.11paper, purpur
Скачать
Что нового
## [2.0.2] - 2026-04-14 ### Fixed #### `NullPointerException: HudManager.sendNow()` when shooting any gun - **Root cause**: `GunListener` cached `plugin.getHudManager()` into a `final HudManager hudManager` field inside its constructor. However, `GunListener` is instantiated in `registerListeners()` which runs **before** `startServices()` where `HudManager` is created. The field was always `null` at the time of assignment. - **Fix**: Removed the cached `hudManager` field entirely. All two call-sites now use `plugin.getHudManager()` inline with a null-guard: `if (plugin.getHudManager() != null) plugin.getHudManager().sendNow(player)`. This makes the call lazy — it resolves the reference at the moment of use rather than at construction time, so startup order no longer matters. ---
release

CombatGunSSS 2.0.1

2.0.1
14 апр. 2026 г.23Minecraft: 1.21, 1.21.1, 1.21.2, 1.21.3, 1.21.4, 1.21.5, 1.21.6, 1.21.7, 1.21.8, 1.21.9, 1.21.10, 1.21.11paper, purpur
Скачать
Что нового
## [2.0.1] - 2026-04-14 ### Fixed #### Plugin crash on startup (`onEnable` exception → plugin disabled) - **Root cause**: Any uncaught exception inside `onEnable()` caused Bukkit to call `disablePlugin()` immediately, leaving the plugin in a broken state where commands responded with _"plugin is disabled"_ instead of a useful error message. - **Fix**: Refactored `onEnable()` into isolated private methods (`initManagers()`, `registerListeners()`, `startServices()`, `registerCommands()`). Each step is now wrapped in a top-level try-catch. A detailed stack trace is printed to console before the plugin gracefully self-disables — making the actual root cause visible instead of a cryptic command error. #### `StatsManager` (SQLite) failure no longer kills the plugin - `statsManager.init()` is now wrapped in its own try-catch. If SQLite initialisation fails (missing driver, file permission error, corrupted DB), the plugin logs a warning, sets `statsManager = null`, and continues loading. All stats-dependent features (`/gun stats`, `/gun leaderboard`, PAPI `%combatgun_kills%` etc.) degrade gracefully with a user-friendly error message. #### `HookManager` failure no longer kills the plugin - `hookManager.initAll()` is wrapped in a try-catch. If any soft-depend integration throws an unexpected exception, the hook manager is set to `null` and the plugin runs without integrations rather than crashing entirely. #### Null pointer exceptions when `hookManager` or `statsManager` are null - `GunListener.isAllowed()` — WorldGuard check now guards with `hm != null` before calling `canShoot()`. - `GunListener.applyRecoil()` — AntiCheat exempt call guarded with `hm2 != null`. - `GunCommand.handleBuy()` — Vault access guarded; returns a friendly error if hook manager is unavailable. - `GunCommand.handleStats()` / `handleLeaderboard()` — Guarded with `statsManager != null` check; returns a friendly error if stats are disabled. - `EntityDeathListener` — Stats recording guarded with `statsManager != null`. #### `Material.GREY_DYE` compile error - `ThrowableManager` used the pre-1.13 material name `GREY_DYE` which no longer exists. Corrected to `GRAY_DYE` (American English naming introduced in the 1.13 flattening). ### Changed - `onEnable()` restructured into `initManagers()`, `registerListeners()`, `startServices()`, `registerCommands()`, `scheduleCleanup()`, `printStartupBanner()` for clarity and fault isolation. - `onDisable()` wraps each cleanup call in its own try-catch so a failure in one manager never prevents the others from shutting down cleanly. - Startup banner now correctly reads version from `plugin.yml` at runtime via `getDescription().getVersion()` — no hardcoded version strings in Java code. ---
release

CombatGunSSS 2.0.0

2.0.0
13 апр. 2026 г.29Minecraft: 1.21, 1.21.1, 1.21.2, 1.21.3, 1.21.4, 1.21.5, 1.21.6, 1.21.7, 1.21.8, 1.21.9, 1.21.10, 1.21.11paper, purpur
Скачать
Что нового
## [2.0.0] - 2026-04-12 ### Added #### Weapon Attachment System - Four attachment types with stackable stat modifiers: - `SILENCER` — reduces sound radius (config: `sound_radius_multiplier`) with optional damage penalty. - `EXTENDED_MAG` — adds flat magazine capacity (config: `magazine_bonus`). - `GRIP` — reduces pitch recoil and bullet spread (config: `recoil_pitch_multiplier`, `spread_multiplier`). - `SCOPE` — enables precision scope spread at runtime, even on guns where `scopeable: false`. - Each gun holds at most one attachment per slot. Attaching a second of the same type replaces the first. - Attachments persist on the gun item via PersistentDataContainer keys (`combatgun:attach_silencer`, etc.). - **Commands**: `/gun attach <id>`, `/gun detach <TYPE>`, `/gun attachments` (inspect held gun). - **Config**: Defined under `combatgun.attachments.<id>` in `config.yml`. Five built-in definitions included (silencer, extended_mag_ar, extended_mag_smg, tactical_grip, acog_scope). - `AttachmentManager` exposes stat modifier helpers used at shoot time: `soundMultiplier()`, `damageMultiplier()`, `magBonus()`, `spreadMultiplier()`, `recoilPitchMultiplier()`, `hasScopeAttachment()`. #### Throwable Items (Grenades) - Three throwable types, all thrown as Snowball projectiles and detonated on `ProjectileHitEvent` or after fuse: - `FRAG` — Creates a visual explosion and deals falloff damage to nearby entities. - `SMOKE` — Spawns a timed smoke particle cloud (campfire smoke particles for 10 seconds by default). - `FLASHBANG` — Applies Blindness and Slowness (simulated deafness) to nearby players, scaled by distance. - **Command**: `/gun givethrowable <id> [player]`. - **Config**: Defined under `combatgun.throwables.<id>` with full control over fuse, radius, damage, duration, and effect ticks. Three built-in throwables included. - `ThrowableListener` handles right-click throw and projectile landing. Registered as a separate listener from `GunListener`. - Projectiles are tracked in `ThrowableManager.liveProjectiles` and auto-detonate after `fuse_ticks` if they don't land. #### Kill Statistics & Leaderboard - New `StatsManager` backed by SQLite (`plugins/CombatGunSSS/stats.db`). No external database required. - Tracks per-player: kills, deaths, headshots, and per-gun kill counts. - Write buffer flushes to DB every 5 minutes and on plugin disable — minimises disk writes while keeping data safe. - **Commands**: `/gun stats [player]` (view stats), `/gun leaderboard` (top 10 by kills). - **New PlaceholderAPI placeholders**: `%combatgun_kills%`, `%combatgun_deaths%`, `%combatgun_headshots%`, `%combatgun_kd%`. - `EntityDeathListener` now calls `StatsManager.recordKill()` and `recordDeath()` on every gun kill. - SQLite JDBC shaded into the jar via `maven-shade-plugin` with relocation to `dev.duong2012g.combatgun.libs.sqlite` — no external jar needed. ### Changed - `CombatGunSSSPlugin` — registers `AttachmentManager`, `ThrowableManager`, `StatsManager`; registers `ThrowableListener`; `onDisable()` calls `statsManager.shutdown()`; startup log prints attachment/throwable counts and stats DB status. - `GunCommand` — added `attach`, `detach`, `attachments`, `givethrowable`, `stats`, `leaderboard` to `SUB_COMMANDS`, switch, tab completion, and help. - `PlaceholderHook` — added `kills`, `deaths`, `headshots`, `kd` placeholders (total 12 placeholders now). - `EntityDeathListener` — records kill/death to `StatsManager` on every confirmed gun kill. - `plugin.yml` — version bumped to `2.0.0`. - `pom.xml` — version bumped to `2.0.0`; added `sqlite-jdbc 3.45.1.0` as compile dependency; added `maven-shade-plugin` to shade and relocate SQLite. - `config.yml` — added `combatgun.attachments` and `combatgun.throwables` sections with default definitions. ---