CombatGunSSS
2.3.0Adds 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 varie
2.3.0latestCombatGunSSS 2.3.0Jul 18, 2026 [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 vanillaMaterials 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 9ar_ammoitems as 9 realGOLD_NUGGETs and happily crafted them into a realGOLD_INGOT(same forsmg_ammo→ iron ingot, andsg_ammo→ brick via furnace smelting), letting players convert unlimited free ammo into real vanilla resources. - Fix 1: AddedCustomItemManager.isCustomItem(ItemStack)— a generic check for ourcustom_item_typePDC tag, true for any ammo/component/station item regardless of which vanillaMaterialit'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: NewCraftingListener.onFurnaceSmelt()handler cancelsFurnaceSmeltEvent(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'sEntityDamageByEntityEventand then calledtarget.damage(gun.getDamage(), player)to apply custom damage manually. That call fires its own freshEntityDamageByEntityEventwith the same damager (the attacking player) and the sameENTITY_ATTACKcause — which re-entersonMeleeAttack()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 listenedEntityDamageByEntityEvent, so it can't recurse the same way). - Fix: Added ameleeInProgressreentrancy guard (Set<UUID>). The player's UUID is added immediately before callingtarget.damage(...)and removed in afinallyblock right after. IfonMeleeAttack()re-enters for the same player while their UUID is already in the set, it now returns immediately without touchingevent.setCancelled()— letting the synthetic inner event proceed uncancelled so the damage actually applies. ---2.2.0CombatGunSSS 2.2.0Jul 18, 2026 [2.2.0] - 2026-07-18
Fixed
####GunCommand— Every player-facing subcommand blocked for non-admins (Critical) - Root cause:onCommand()gated the entire/guncommand tree behindcombatgun.adminbefore dispatching to any subcommand, andplugin.ymladditionally declaredpermission: combatgun.admindirectly on the command — so Bukkit's own command dispatcher rejected non-admins beforeonCommand()ever ran. This silently broke/gun buy(the Vault shop),/gun attach//gun detach//gun attachments,/gun stats,/gun leaderboard, and/gun recipefor every player withoutcombatgun.admin, even though several of these handlers already contained their owncombatgun.usecheck — a check that could never actually execute. - Fix: Removed the command-levelpermission:entry fromplugin.yml.onCommand()now checks a newPLAYER_SUBCOMMANDSset —buy, attach, detach, attachments, stats, leaderboard, recipe, the self-service subcommands that take no target-player argument — againstcombatgun.useORcombatgun.admin. Every other subcommand (give, giveammo, givepart, station, book, givepouch, givethrowable, inspect, reload, statsreset, seasonreset) still requirescombatgun.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 withoutcombatgun.admin. Even after fixingonCommand(), non-admins would still see zero suggestions when typing/gun. - Fix:onTabComplete()now mirrors thePLAYER_SUBCOMMANDSpermission split — non-admins withcombatgun.usesee 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 behindcombatgun.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 anyItemStackthat doesn't fit, so a player with a full inventory was charged the full price but never actually received the gun. - Fix: The leftoverItemStacks returned byaddItem()are now dropped at the player's feet viadropItemNaturally(), 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. - NewCraftingManager.BrowserCategoryenum (GUN,MELEE,AMMO). Guns and melee weapons are both stored underRecipeData.RecipeKind.GUNinternally, so the split is done by checkingGunData.isMelee()rather than changing the existing recipe-kind enum — this avoids touching the exhaustiveswitchonRecipeKindinCustomItemManager. - 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 onPlayerQuitEventto 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 newopenRecipeBrowser(Player, int, BrowserCategory)overload. ---2.1.0CombatGunSSS 2.1.0Jun 6, 2026 [2.1.0] - 2026-06-05
Fixed
####EntityDeathListener—UnsupportedOperationExceptioncrash on every gun kill (Critical) - Root cause: BothonPlayerDeath()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 asyncpruneStale()(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 toConcurrentHashMap. 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: Replacedplayer.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 ableedingShooters: 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: Addedif (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: Addedgun.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 iteratesbuffer, 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.2.0.7CombatGunSSS 2.0.7May 22, 2026 [2.0.7] - 2026-05-22
Fixed
####StatsManager.flushBuffer()— Kill data silently lost under concurrent load (Critical) - Root cause: The old flush implementation callednew 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 theConcurrentHashMapindividually 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 acancelAll()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— Duplicateimportstatement 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 toexisting.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 inReloadManagerwere 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 with
language: vidisplayed English during all reload feedback. - Fix: All four messages now callplugin.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: ReplacedMath.random()withThreadLocalRandom.current().nextDouble(), consistent with the rest of the codebase. ####ThrowableManager.detonateSmoke()— Inconsistent RNG usage (Medium) - Root cause: Same issue asBleedingManager—Math.random()used for particle position offsets inside a hot loop (20 calls per 4-tick interval per active smoke grenade). - Fix: Replaced all threeMath.random()calls withThreadLocalRandom.current().nextDouble(). ####AttachmentManager— Duplicateimportstatement (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: AddedgetDamageRecord(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](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).2.0.6CombatGunSSS 2.0.6May 14, 2026 [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 bothplayer.getScoreboard()calls with a singleBukkit.getScoreboardManager().getMainScoreboard()lookup. Team membership is now always evaluated against the authoritative server scoreboard. ####GunListener— Memory leak inrecentDamagemap (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: AddedrecentDamage.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— Sharedstatic Randomcauses 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 helperrng()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 bothincrementGunKill()andgetGunKills()assynchronized. All three DB-touching methods now synchronize on the same monitor (theStatsManagerinstance), serializing access to the sharedconnectionwithout requiring a connection pool. ---2.0.5CombatGunSSS 2.0.5Apr 26, 2026 [2.0.5] - 2026-04-26
Added
####AttachmentApplyEventandAttachmentRemoveEvent— Developer API - Two new cancellable events fire when a player attaches or removes an attachment from a gun. - `AttachmentApplyEvent` — fired beforefitAttachment()writes the PDC key. ProvidesgetPlayer(),getGunData(),getIncoming()(attachment being fitted), andgetReplacedAttachment()(previously occupied slot, ornull). Cancellable — cancel to block the attachment action. - `AttachmentRemoveEvent` — fired beforeremoveAttachment()clears the PDC key. ProvidesgetPlayer(),getGunData(),getSlot(), andgetRemoved()(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 - Newutil/DamageCalculator.javaextracts all damage-pipeline calculations fromGunListener, 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 fromdamageFalloffStartto min floor. -headshotMultiplier(GunData, boolean headshot)— returnsheadshotMultiplieror1.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 acrossGunListener. -GunListenernow delegates all damage math and spread calculations toDamageCalculator. The privatecomputeFalloffMultiplier()method has been removed (logic moved toDamageCalculator.falloffMultiplier()). ####StatsManager— Per-Kill Detail Log (kills_by_gun_detailtable) - New SQLite tablekills_by_gun_detailadded via schema migration v3. Stores one row per kill withuuid,gun_id,headshotflag, and Unixtimestamp(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 literalconfirmargument as a safeguard against accidental use). Clears the in-memory buffer and truncatesplayer_stats,gun_kills, andkills_by_gun_detailin a single transaction. Prints a gold confirmation message on success. - Both commands requirecombatgun.adminpermission. - Both commands fail gracefully with a clear error ifStatsManageris disabled (SQLite unavailable).Improved
####GunListener— God Class Reduction -performShot()spread logic (previously a three-branchif/else if/elseblock) replaced with a singleDamageCalculator.effectiveSpread()call. -applyEntityDamage()damage calculation (previously inline) replaced withDamageCalculator.finalDamage()andDamageCalculator.baseDamage(). -isHeadshot()private method now delegates entirely toDamageCalculator.isHeadshot()— single source of truth for headshot geometry. - RemovedcomputeFalloffMultiplier()private method (~7 lines) — logic now lives inDamageCalculator. ####StatsManager— Schema Version Bumped to 3 -SCHEMA_VERSIONconstant updated from2to3. -recordKill()now passes theheadshotflag toincrementGunKill()so both the aggregategun_killstable and the newkills_by_gun_detailtable are updated in one async task. -resetStats(UUID)now also deletes fromkills_by_gun_detailin addition toplayer_statsandgun_kills. -resetAllStats()truncates all three stat tables in a single transaction. ---2.0.3CombatGunSSS 2.0.3Apr 18, 2026 [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 calledpouch.setAmount(0)regardless of how many bullets were actually transferred. The entire pouch was destroyed even if bullets remained inside. - Fix: Ifremaining > 0(unpack not completed), update the PDCpouch_quantityto the remaining amount and refresh the lore instead of destroying the pouch. Only callsetAmount(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 torunTaskTimer(plugin, 0L, 2L)and adjustedtotalTicks = reloadTime * 10.0so 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 fixedentity.getHeight() * 0.8without accounting for crouching players (hitbox shrinks to ~1.5 blocks instead of 1.8). - Fix: Detectplayer.isSneaking()and multiplyheight *= 0.85before 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 beforecreateResultItem(). IfcreateResultItem()returnednull(invalid gun ID, config error, etc.), ingredients were consumed but no item was given → permanent item loss. - Fix: Snapshot all input slots before consuming. IfcreateResultItem()returnsnull, restore ingredients from the snapshot. ####GunListener.applyRecoil()— Potential NPE when AntiCheatHook is disabled (Medium) - Root cause:if (hm2 != null) hm2.getAntiCheatHook().exempt(player)— checkedhm2but notgetAntiCheatHook(). If the hook was disabled,getAntiCheatHook()could return an uninitialized instance. - Fix: Added full guard:hm2 != null && hm2.getAntiCheatHook() != null && hm2.getAntiCheatHook().isActive()before callingexempt(). ####BleedingManager— Incorrect damage during server lag (Medium) - Root cause: Task usedelapsed += 20to count ticks, but when the server lagged (>50ms per tick), actual damage was lower than the configureddamage_per_second. - Fix: Switched toSystem.currentTimeMillis()for real-time tracking.lastDamageTimeis 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 checkingitem == null, causing potential NPE with empty inventory slots in some edge cases. - Fix: Movedif (item == null || item.getType().isAir()) continue;before callingidentifyIngredient(). ####GunListener.onItemHeldChange()— Race condition when switching slots rapidly (Medium) - Root cause: Task with 0-tick delay readgetItemInMainHand()— if the player switched slots again before the task ran, it would process the wrong slot's item. - Fix: StorenewSlot = event.getNewSlot()before scheduling the task. Inside the task, checkplayer.getInventory().getHeldItemSlot() != newSlotand abort if the player has switched slots again.2.0.2CombatGunSSS 2.0.2Apr 14, 2026 [2.0.2] - 2026-04-14
Fixed
####NullPointerException: HudManager.sendNow()when shooting any gun - Root cause:GunListenercachedplugin.getHudManager()into afinal HudManager hudManagerfield inside its constructor. However,GunListeneris instantiated inregisterListeners()which runs beforestartServices()whereHudManageris created. The field was alwaysnullat the time of assignment. - Fix: Removed the cachedhudManagerfield entirely. All two call-sites now useplugin.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. ---2.0.1CombatGunSSS 2.0.1Apr 14, 2026 [2.0.1] - 2026-04-14
Fixed
#### Plugin crash on startup (onEnableexception → plugin disabled) - Root cause: Any uncaught exception insideonEnable()caused Bukkit to calldisablePlugin()immediately, leaving the plugin in a broken state where commands responded with _"plugin is disabled"_ instead of a useful error message. - Fix: RefactoredonEnable()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, setsstatsManager = null, and continues loading. All stats-dependent features (/gun stats,/gun leaderboard, PAPI%combatgun_kills%etc.) degrade gracefully with a user-friendly error message. ####HookManagerfailure 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 tonulland the plugin runs without integrations rather than crashing entirely. #### Null pointer exceptions whenhookManagerorstatsManagerare null -GunListener.isAllowed()— WorldGuard check now guards withhm != nullbefore callingcanShoot(). -GunListener.applyRecoil()— AntiCheat exempt call guarded withhm2 != null. -GunCommand.handleBuy()— Vault access guarded; returns a friendly error if hook manager is unavailable. -GunCommand.handleStats()/handleLeaderboard()— Guarded withstatsManager != nullcheck; returns a friendly error if stats are disabled. -EntityDeathListener— Stats recording guarded withstatsManager != null. ####Material.GREY_DYEcompile error -ThrowableManagerused the pre-1.13 material nameGREY_DYEwhich no longer exists. Corrected toGRAY_DYE(American English naming introduced in the 1.13 flattening).Changed
-onEnable()restructured intoinitManagers(),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 fromplugin.ymlat runtime viagetDescription().getVersion()— no hardcoded version strings in Java code. ---2.0.0CombatGunSSS 2.0.0Apr 13, 2026 [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 wherescopeable: 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 undercombatgun.attachments.<id>inconfig.yml. Five built-in definitions included (silencer, extended_mag_ar, extended_mag_smg, tactical_grip, acog_scope). -AttachmentManagerexposes 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 onProjectileHitEventor 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 undercombatgun.throwables.<id>with full control over fuse, radius, damage, duration, and effect ticks. Three built-in throwables included. -ThrowableListenerhandles right-click throw and projectile landing. Registered as a separate listener fromGunListener. - Projectiles are tracked inThrowableManager.liveProjectilesand auto-detonate afterfuse_ticksif they don't land. #### Kill Statistics & Leaderboard - NewStatsManagerbacked 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%. -EntityDeathListenernow callsStatsManager.recordKill()andrecordDeath()on every gun kill. - SQLite JDBC shaded into the jar viamaven-shade-pluginwith relocation todev.duong2012g.combatgun.libs.sqlite— no external jar needed.Changed
-CombatGunSSSPlugin— registersAttachmentManager,ThrowableManager,StatsManager; registersThrowableListener;onDisable()callsstatsManager.shutdown(); startup log prints attachment/throwable counts and stats DB status. -GunCommand— addedattach,detach,attachments,givethrowable,stats,leaderboardtoSUB_COMMANDS, switch, tab completion, and help. -PlaceholderHook— addedkills,deaths,headshots,kdplaceholders (total 12 placeholders now). -EntityDeathListener— records kill/death toStatsManageron every confirmed gun kill. -plugin.yml— version bumped to2.0.0. -pom.xml— version bumped to2.0.0; addedsqlite-jdbc 3.45.1.0as compile dependency; addedmaven-shade-pluginto shade and relocate SQLite. -config.yml— addedcombatgun.attachmentsandcombatgun.throwablessections with default definitions. ---1.1.0CombatGunSSS 1.1.0Apr 10, 2026 [1.1.0] - 2026-04-10
Added
#### PlaceholderAPI Integration - New soft-depend on PlaceholderAPI. When the plugin is present, CombatGunSSS registers a%combatgun_*%expansion with 8 real-time placeholders: | Placeholder | Example | Description | | :--- | :--- | :--- | |%combatgun_gun_name%|AK47| Display name of held gun | |%combatgun_gun_id%|ak47| Internal ID of held gun | |%combatgun_gun_rarity%|epic| Rarity of held gun | |%combatgun_ammo%|24| Current magazine ammo | |%combatgun_ammo_max%|30| Magazine capacity | |%combatgun_ammo_reserve%|90| Reserve ammo in inventory | |%combatgun_is_reloading%|true| Whether player is reloading | |%combatgun_is_gun%|true| Whether held item is a gun | - All placeholders return safe empty-string or0defaults when the player holds no gun. - Compatible with scoreboard plugins (AdvancedHud, TAB, AnimatedScoreboard, etc). #### Vault Economy / Shop - New soft-depend on Vault. When an economy provider is found andcombatgun.shop.enabled: true, players can purchase weapons via/gun buy <gun_id>. - Prices are configured per gun inconfig.ymlundercombatgun.shop.guns. All 45 built-in weapons have default prices included in the generated config. - Admin shortcut:/gun buy <gun_id> freegives the weapon at no cost (requirescombatgun.admin). - Clear failure messages for insufficient funds, missing price entry, and unavailable economy. -/gun buyadded to tab completion and/gunhelp output. -VaultHookis a standalone class —VaultHook.buyGun()can be called from third-party plugins that depend on CombatGunSSS. #### WorldGuard Region Protection - New soft-depend on WorldGuard. When present, a custom flaggun-shootingis registered duringonLoad()(before WorldGuard reads region data). - Shooting is blocked in any region wheregun-shootingis set todeny:
- Falls back to WorldGuard's built-in/rg flag <region> gun-shooting deny # block all gun use /rg flag <region> gun-shooting allow # explicitly allowPVPflag whengun-shootingis not configured on a region. - Applies to all ranged shots and is checked insideisAllowed()— same path as world and permission checks. Players receive an action-bar message:⛔ Guns are disabled in this region.- Fails open on unexpected errors to avoid breaking gameplay. #### Anti-Cheat Exemptions (Vulcan & Matrix) - New soft-depend detection for Vulcan and Matrix anti-cheat plugins. - When either plugin is detected,AntiCheatHook.exempt(player)is called immediately beforeapplyRecoil()— preventing false-positive flags on the camera pitch/yaw changes that recoil applies. - Exemption is automatically removed aftercombatgun.anticheat.exempt_ticksticks (default3). - Both exemption and un-exemption calls are fully wrapped in try-catch — an API version mismatch in either anti-cheat never crashes CombatGunSSS. #### CentralHookManager- NewHookManagerclass owns all hook instances (PlaceholderHook,VaultHook,WorldGuardHook,AntiCheatHook). - Initialized once inonEnable(). Each hook initializes independently — a failure in one never prevents others from loading. - Accessible viaplugin.getHookManager()for third-party plugins that want to query hook state. - Startup log now printsShop: ON (Vault)andWGuard: ONlines based on detected integrations.Changed
-pom.xml— addedprovideddependencies for PlaceholderAPI2.11.6, VaultAPI1.7, and WorldGuard7.0.9. All three are compile-time only (never shaded into the jar). -plugin.yml— version bumped to1.1.0; addedsoftdependlist:[PlaceholderAPI, Vault, WorldGuard, Vulcan, Matrix]; updated command description to includebuy; updated plugin description. -config.yml— addedcombatgun.shopsection withenabled,currency_symbol, and per-gun price list for all 45 built-in weapons; addedcombatgun.anticheat.exempt_tickskey; added inline documentation comments for WorldGuard. -GunCommand— addedbuytoSUB_COMMANDSlist, switch statement, tab completion, and help text; addedhandleBuy()method. -GunListener.isAllowed()— added WorldGuard region check between world check and permission check. -GunListener.applyRecoil()— callsantiCheatHook.exempt(player)before applying camera rotation. -CombatGunSSSPlugin.onLoad()— addedWorldGuardHook.registerFlags()call (required before WorldGuard reads region data). -CombatGunSSSPlugin.onEnable()— initializesHookManagerafter all managers are ready; startup log prints hook status lines. ---1.0.3CombatGunSSS 1.0.3Apr 9, 2026 [1.0.3] - 2026-04-09
Added
#### Action Bar HUD - New persistent ammo HUD displayed on the action bar while holding any ranged weapon. - Format:🔫 <GunName> ▐████████████░░░░▌ 22 / 30 • 90- Gun name is colored by rarity: white (common) → aqua (rare) → light purple (epic) → gold (legendary). - Visual bar transitions from green (full) to gold (≤ 25%) to red (≤ 10%) as ammo depletes. - Reserve count turns red when the player has no reserve ammo left. - HUD yields automatically to the reload progress bar during active reloads — no visual conflict. - Immediate update on every shot (no delay waiting for the next tick). - Configurable via newcombatgun.hudconfig block: -enabled(boolean, defaulttrue) — toggle the HUD entirely. -update_interval_ticks(integer, default5) — refresh rate in ticks. Lower = smoother, minimum 1. #### Auto-Reload When Empty - New config keycombatgun.auto_reload_when_empty(boolean, defaulttrue). - When enabled, CombatGunSSS automatically starts a reload after a gun fires its last round, saving the player from manually pressing[F]. - Auto-reload respects the existingGunReloadEvent— other plugins can still cancel it. - Set tofalsefor hardcore or manual-reload server modes. #### Developer API —GunHitEvent- New informational eventGunHitEvent, fired immediately after a bullet successfully damages a living entity. - Provides the complete damage pipeline in a single event: -getBaseDamage()— damage before falloff and headshot multiplier. -getFinalDamage()— damage actually applied after all multipliers. -isHeadshot()— whether the hit registered as a headshot. -getDistance()— distance in blocks from shooter eye to hit position. -getShooter(),getTarget(),getGun()— full context. - Intended for stat tracking, cosmetic side-effects, or per-hit reward systems. Not cancellable (damage is already done by the time the event fires). #### Developer API —GunShootEvent.getBaseDamage()- New method on the existingGunShootEvent, available pre-shot before any multipliers are applied. - Returnsgun.getDamage() * gun.getProjectileDamageMultiplier(). - Useful for pre-shot checks such as arena minimum-damage caps or logging without recalculating manually.Changed
-CombatGunAPI— addedGunHitEventto theCUSTOM_EVENTSdocumentation array and updated Javadoc to list all four custom events with descriptions. -CombatGunSSSPlugin— registers and stopsHudManager; logsHUD: ON/OFFto console on startup. -GunListener— removed privatesendAmmoBar()helper (replaced byHudManager); callshudManager.sendNow()after each shot for an instant HUD update; appliesauto_reload_when_emptyconfig check before triggering reload on empty magazine.Fixed
- `GunHitEvent` damage transparency — previously, third-party plugins had no way to observe the actual damage applied by a bullet (only the pre-shotGunShootEventexisted). Final damage, falloff, and headshot calculations were completely opaque afterv1.0.2.GunHitEventcloses this gap. ---1.0.2CombatGunSSS 1.0.2Apr 7, 2026 [1.0.2] - 2026-04-07
Added
#### Developer API — Custom Events - `GunShootEvent` (cancellable) — fired before every ranged shot. Other plugins can cancel the shot entirely or modify damage viasetDamageMultiplier(double). Useful for anti-cheat integration, zone-based restrictions, or temporary power-up effects. - `GunReloadEvent` (cancellable) — fired when a player starts a reload. Cancel to block reloading in specific zones or game states (e.g., "no reload during objective capture"). - `GunHeadshotEvent` (informational) — fired on every confirmed headshot. Provides shooter, target, gun data, and final damage for kill-feed plugins, stat trackers, or custom rewards. #### Friendly Fire Control - New config keyscombatgun.friendly_fire(boolean, defaulttrue) andcombatgun.team_provider(string). -scoreboardmode — uses vanilla Minecraft scoreboard teams (/team add …). Players sharing a team cannot shoot each other when friendly fire is off. -permission_groupmode — players sharing anycombatgun.team.<n>permission node are considered teammates. - Both ranged shots and melee attacks respect the friendly-fire setting. #### Toggleable Effects - Newcombatgun.effectsconfig block. Every visual and audio effect can now be enabled or disabled individually without recompiling: -bullet_trail,muzzle_flash,hit_particles,block_impact,headshot_particles-gun_sound,melee_sound,melee_particles-sound_volume— master volume multiplier for all gun sounds (0.0 – 2.0, default 1.5) - Disabling heavy effects (bullet trails, muzzle flash) can meaningfully reduce particle load on high-population servers. #### Kill / Death Messages - Gun kills now override the vanilla death message with a formatted line showing killer name, weapon name (colored by rarity), and a ☠ HEADSHOT prefix when applicable. - Implemented via a short-livedDamageRecordmap (TTL 5 seconds) that links the last gun shot to the subsequent death event — no false attribution on delayed deaths.Fixed
#### Memory Leak inAmmoManager-lastShotandlastMeleeAttackwere plainHashMapentries that were only removed whenclearPlayer()was called on a clean quit or death. Players who disconnected via server crash, network drop, or/kickleft stale entries that accumulated indefinitely. - Fix: AddedAmmoManager.pruneStale(long olderThan)and a periodic async scheduler inonEnable()that calls it every 2 minutes, removing any entry whose timestamp is older than 60 seconds. ####getMeleeCooldownRemainingAlways Returned Wrong Value - The zero-argument overloadgetMeleeCooldownRemaining(UUID)contained a hardcodedcooldownMs = 1000with a comment acknowledging the bug. It would always calculate the remaining cooldown against 1 second regardless of the actual weapon's fire rate. - Fix: Removed the broken zero-argument overload entirely. Only the correctgetMeleeCooldownRemaining(UUID, long weaponCooldownMs)overload is retained. All callers already passed the weapon cooldown. #### Reload Not Cancelled on Teleport / World Change - When a player teleported or changed worlds while a reload was in progress, the reloadBukkitRunnablecontinued executing in the background. If the reload completed, ammo was consumed and applied to the gun even though the player was now in a different (possibly gun-banned) world. - Fix: AddedcancelReload(player, false)calls to bothonTeleportandonChangedWorldevent handlers, matching the existing behaviour ofonDeathandonQuit. ####isMelee()Could ThrowNullPointerException-GunData.isMelee()evaluated"none".equalsIgnoreCase(ammoType). If a gun's YAML omittedammo_typeentirely,ammoTypewasnulland this call threw an NPE silently swallowed by the YAML loader, causing the weapon to not load. - Fix:isMelee()is now a stored boolean field derived at construction time. It istruewhencategory == "melee"ORammoTypeis null, blank, or"none".Refactored
####GunData— Builder Pattern - Replaced the 27-parameter constructor with a fluentGunData.Builder. Every field has a sensible default so callers only set what differs. This prevents silent argument-order bugs (two adjacentdoublefields are indistinguishable to the compiler in a positional constructor). -GunManager.parseGun()updated to use the builder; all other call sites remain source-compatible. #### Bullet Trail — Adaptive Density + Lighter Particle - Trail step count now scales with distance:steps = distance < 20 ? distance × 2 : distance × 0.8, capped at 40. Previously alwaysmin(60, distance × 2)which produced up to 80 particles on sniper shots. - Switched fromParticle.SMOKEtoParticle.DUST(small grey, 0.3f radius) — lighter on client render without a visible quality difference at combat distances. ---1.0.1CombatGunSSS 1.0.1Apr 5, 2026 [1.0.1] - 2026-04-05
Fixed
#### Crafting System - `registerVanillaRecipes()` was never called — component recipes were not registered with Bukkit on startup or after/gun reload, so the vanilla Recipe Book showed no entries. Fixed by callingregisterVanillaRecipes()inonEnable()andhandleReload(). - Best-match recipe resolution —ar_ammo,smg_ammo, andhg_ammoshare the same four ingredient types (brass_casing,gunpowder,steel_scrap,primer_cap). The old first-match algorithm could produce the wrong ammo type when a player placed slightly more ingredients than the minimum required. Replaced with a lowest-surplus algorithm that always selects the recipe whose ingredient amounts are closest to what the player put in. - `break` only exited the inner loop in `registerVanillaRecipes()` — when a component recipe required more than 9 items total (the BukkitShapelessRecipecap), the warning was logged but the outer ingredient loop continued, resulting in a partially-built recipe being registered silently. Changed to a labeledbreak outerso the entire recipe is skipped on overflow. - Double-click (COLLECT) could pull ingredients out of the Mechanical Crafting Table — Bukkit's COLLECT click type sweeps the entire open inventory for matching items, bypassing the station's input-slot guard and corrupting the crafting preview. Added an explicitClickType.DOUBLE_CLICKcancel at the top ofonInventoryClick. - Recipe book identified by display name instead of PDC tag — anywritten_bookwith "Recipe Guide" in its name would hijack the recipe GUI. The book now carries arecipe_book_markerPDC key set at creation time;RecipeBookListenerchecks this key instead. - `RecipeCategory.valueOf` could throw uncaught `IllegalArgumentException` — if PDC data was corrupt or written by a different plugin version, the category click handler would crash with an unhandled exception. Wrapped in try-catch with a warning log. #### Combat - Removed 5 `[DEBUG]` log statements from `GunListener.traceProjectile()` — these were left in from development and spammed the server console with 4–5 lines per bullet fired. ---1.0.0CombatGunSSS 1.0.0Apr 2, 2026 [1.0.0] - 2026-04-02
Added
#### Weapons - 39 Ranged Weapons across 5 categories: - 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 - 6 Melee Weapons: Bat, Knife, Pan, Parang, Katana, Scythe #### Combat System - Hitscan shooting mechanics with ray-tracing - Per-weapon recoil (pitch/yaw), spread, and recovery - Damage falloff based on distance traveled - Headshot detection and multipliers - Block penetration through soft materials - Entity penetration for multi-target shots - Knockback system for both ranged and melee - Movement spread penalty while sprinting/jumping - Burst fire support with configurable delays - Shotgun pellet spread mechanics #### Melee System - Left-click attack mechanics (EntityDamageByEntityEvent) - Range check (4 blocks default) - Separate cooldown system from ranged weapons - Knockback on melee hits - Sound effects at target location - Cooldown feedback in action bar #### Crafting System - Mechanical Crafting Table (21-slot GUI) - Vanilla Crafting Table component recipes - Recipe Book with visual browser - Per-item ingredient requirements - Non-grid-based crafting interface - Output preview with weapon stats #### Ammo System - 5 Ammo Types: AR, SMG, Sniper, Shotgun, Handgun - Magazine system with reload mechanics - Swap-hand [F] key reload - Durability system for weapons - Auto-reload when empty #### Configuration - Per-weapon YAML configuration files - Separated folder structure:guns/andmelees/- Custom weapon support (add your own YAML files) - World whitelist/blacklist support - Mob drop configuration - Custom Model Data support for resource packs #### Developer API - Public API class:CombatGunAPI- Query gun data and recipes - Create gun/ammo/component items programmatically - Check item types and player state - World permission checks #### Commands -/gun give <id> [player]- Give weapons -/gun giveammo <id> [amount] [player]- Give ammo -/gun givepart <id> [amount] [player]- Give components -/gun station [player]- Give crafting table -/gun book [player]- Give recipe book -/gun list [category]- List all weapons -/gun recipe <id>- Show crafting chain -/gun inspect [player]- Inspect held weapon -/gun reload- Reload configurations #### Permissions -combatgun.admin- Access to all admin commands -combatgun.use- Allow using guns (default: true) -combatgun.use.<gun_id>- Per-weapon permissions #### Visual Effects - Bullet trail particles - Muzzle flash effects - Hit particles and sound effects - Headshot particle effects - Block impact particles - Sweep attack particles for melee - Action bar ammo display - Action bar cooldown feedbackTechnical
- Paper/Spigot 1.21+ API support - Java 21 requirement - Persistent data container for item storage - Tab completion for all commands - Optimized ray-tracing with entity filteringNotes
- Initial release after extensive testing - All 45 weapons balanced and configured - Full documentation and examples provided - Ready for production servers ---Future Roadmap
Planned for 1.1.0
- Scope zoom mechanic for sniper rifles - Weapon attachment system - More melee weapons - Explosive weapons (RPG, Grenades)Planned for 1.2.0
- Player statistics tracking - Leaderboard system - Kill/death tracking - Weapon usage statistics ---
Similar projects
Plugin →VeinMiner
Mine the whole vein on mining a single ore/block. Make the tedious mining experience to something satisfying and fun! FTB Ultimine alternative
99.3MSueta
VeinMiner Enchantment
Veinminer Addon - Adds vineminer enchantment to enchanting tables. Only tools with the enchantment can veinmine
18MSueta
WorldEdit
A Minecraft Map Editor... that runs in-game! With selections, schematics, copy and paste, brushes, and scripting. Use it in creative, or use it temporarily in survival.
11.6MSueta
LuckPerms
A permissions plugin/mod for Minecraft servers.
2.8MSueta
ItemSwapper
Inventory update concept for hotswapping items.
2.1MSueta
SkinsRestorer
🎨 Ability to restore/change skins on servers! (Offline and Online Mode)
1.6MSueta