All versions
Memory Leak in
1.0.2
Release
CombatGunSSS 1.0.27 months ago 37
Compatibility
Minecraft: Java Edition
1.21.x
Platforms
Bukkit
Paper
Purpur
Spigot
Changes
[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 keys
combatgun.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
- New
combatgun.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_particlesgun_sound,melee_sound,melee_particlessound_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-lived
DamageRecordmap (TTL 5 seconds) that links the last gun shot to the subsequent death event โ no false attribution on delayed deaths.
Fixed
Memory Leak in AmmoManager
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: Added
AmmoManager.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.
getMeleeCooldownRemaining Always Returned Wrong Value
- The zero-argument overload
getMeleeCooldownRemaining(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 correct
getMeleeCooldownRemaining(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 reload
BukkitRunnablecontinued 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: Added
cancelReload(player, false)calls to bothonTeleportandonChangedWorldevent handlers, matching the existing behaviour ofonDeathandonQuit.
isMelee() Could Throw NullPointerException
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 fluent
GunData.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 from
Particle.SMOKEtoParticle.DUST(small grey, 0.3f radius) โ lighter on client render without a visible quality difference at combat distances.
Projects on Modrinth are automatically available through a Maven repository for use with JVM build tools such as Gradle. To learn more about the Modrinth Maven API, click here.
Note: When available, you should use the creator's maven repo instead as it will have transitive dependency information that the Modrinth Maven API does not. You may also end up with duplicate dependencies if you use a mix of Modrinth and non-Modrinth Maven repositories for your dependencies, because the group identifier will be different when served through the Modrinth Maven API.
Maven coordinates:
Version ID:
build.gradle:
repositories {
exclusiveContent {
forRepository {
maven {
name = "Modrinth"
url = "https://api.modrinth.com/maven"
}
}
// forRepositories(fg.repository) // Uncomment when using ForgeGradle
filter {
includeGroup "maven.modrinth"
}
}
}
// Standard Gradle dependency
dependencies {
implementation "maven.modrinth:BSt2YZOI:khgNe1SM"
}
// Legacy Loom dependency
dependencies {
modImplementation "maven.modrinth:BSt2YZOI:khgNe1SM"
}

