SiteZero
Plant. Defend. Defuse. SiteZero is a tactical round-based bomb-defusal game mode for Paper β buy your loadout, manage your economy and outplay the enemy across objective-driven rounds with a deep weapon arsenal, grenades, stats and a clean shop GUI.
1.2.0
Compatibility
Changes
[1.0.0] - 2024-03-29
Added
- Initial release of SiteZero plugin
- Core gameplay mechanics
- Round-based matches with team switching
- Bomb plant and defuse objectives
- Spectator mode for eliminated players
- Economy system
- Dynamic money rewards
- Loss bonus mechanics
- Persistent player economy
- Weapon system
- Multiple weapon categories (Pistols, Rifles, SMGs, Snipers)
- Customizable weapon statistics
- Armor and utility items
- Arena management
- Multi-arena support
- Flexible spawn point configuration
- Bombsite and buy zone definition
- Player statistics
- SQLite database integration
- K/D tracking
- Win/loss statistics
- Objective completion tracking
- Shop system
- GUI-based weapon shop
- Buy phase restrictions
- Team-specific buy zones (optional)
- Commands and permissions
- Comprehensive command system
- Granular permission nodes
- Tab completion support
- Configuration
- Customizable match settings
- Weapon balancing options
- Message customization
Technical Features
- Paper 1.21.x compatibility
- Java 21 support
- Async database operations
- Projectile-based combat system
- Adventure Component API integration
Known Issues
- None at initial release
[1.0.1] - 2024-03-30
Added
- Arena setup checklist command (
/sitezero checklist <arena>) - Visual checklist with status indicators (β/β/β)
- Shop GUI configuration options in config.yml
- Tab completion for checklist command
Fixed
- Message color formatting now properly translates & color codes
- Arena validation logic (removed enabled check from isReady())
- Arena can now be properly enabled after configuration
Changed
- Improved arena setup validation feedback
[1.0.2] - 2024-03-30
Fixed
- NullPointerException when saving arenas with unloaded worlds
- Enable/disable arena commands now properly handle null world references
- Arena configuration save methods now check for null worlds before accessing
Technical
- Added null checks for Location.getWorld() in save methods
- Improved error handling for arena persistence
[1.0.3] - 2024-03-30
Fixed
- Added proper exception handling in command executor
- Added null checks for ConfigurationSection.createSection() returns
- Improved error logging to console for debugging
- Fixed potential NPE when saving nested configuration sections
Technical
- Better defensive programming for configuration saves
- Added try-catch wrapper for all command executions
- Console now logs full stack trace for debugging
[1.0.4] - 2024-03-30
Fixed
- Fixed double message processing causing exceptions
- Removed redundant MiniMessage deserialization for already-processed messages
- Arena enable/disable commands now work properly without errors
Technical
- sendMessage() method now correctly handles pre-processed color codes
- MessageManager already translates color codes, no need for MiniMessage
[1.0.5] - 2024-03-30
Fixed
- Fixed message processing errors in Match class (join, leave, and other announcements)
- Fixed message processing in CombatListener (team damage message)
- Removed incorrect MiniMessage deserialization for pre-processed messages
- Added overloaded broadcastMessage method for String messages
Technical
- MessageManager messages are already color-processed, no MiniMessage needed
- Consistent message handling across all classes
[1.0.6] - 2024-03-31
Fixed
- Fixed message processing in ShopService (removed MiniMessage for pre-processed messages)
- Team-specific items now correctly shown (defuse kits only for defenders)
- Implemented proper default equipment system (only free pistol and knife)
Added
- Default equipment system: Players spawn with free USP pistol and knife
- Team-specific shop items filtering
Changed
- Shop now properly filters defuse kits for defenders only
- Players start with basic equipment instead of empty inventory
[1.1.0] - 2024-03-31
Added
- Arena boundary system to prevent players from leaving the arena
/sitezero setboundary <arena> <pos1|pos2>command- PlayerMoveEvent handler to enforce boundaries
- Improved shop purchase validation
Fixed
- Shop now properly ignores category headers
- Better error messages for invalid shop purchases
Changed
- Shop system now validates items have lore before processing
- Arena class now supports boundary zones
[1.1.1] - 2024-03-31
Added
- Visual bombsite indicators using holograms
/sitezero showholograms <arena>command to manually create holograms- Equipment drop system - purchased items can be dropped, default items cannot
- HologramManager class for managing bombsite visual indicators
Fixed
- Item pickup bug that was blocking ALL items from being picked up
- Bomb pickup now correctly restricted to attackers only during live matches
Changed
- Equipment is now marked as "purchased" or "default" using PersistentDataContainer
- Holograms automatically appear when matches start and disappear when they end
- Default equipment (knife and USP pistol) cannot be dropped
[1.1.2] - 2024-03-31
Fixed
- Bomb can now be properly dropped by attackers to teammates
- Shop purchase validation - free items (price 0) no longer require money
- Shop item name color formatting now properly displays color codes
- Equipment dropping in matches - all items can now be dropped during matches for teamwork
- Default equipment restricted from dropping only outside matches
Changed
- Removed debug logging from item pickup events
- Improved shop purchase logic for free vs paid items
[1.1.3] - 2024-03-31
Fixed
- Bomb dropping now works correctly for attackers (event properly cancelled and handled)
- Weapon shop material conflicts resolved (each weapon has unique material)
- Shop money check now handles free items (price 0) correctly
- Scoreboard updates immediately after purchases
- Shop layout reorganized with better categorization and colored headers
Changed
- Complete weapons.yml overhaul with unique materials for each weapon
- Added USP as free default pistol
- Shop categories now have colored headers and better organization
- Knife excluded from shop display (default equipment)
- Max items per shop row limited to prevent overflow
Added
- More weapons: P250, AUG, MAC-10, P90
- Grenades in weapons.yml (flashbang, smoke) for future implementation
[1.1.4] - 2026-04-26
Added
- Magazine & Reload System: Weapons now have finite ammo based on their magazine size. Automatic reload triggers when the magazine is empty, with a progress bar shown in the action bar. Reload time is weapon-specific.
- Grenade System: Flashbangs (blindness + slowness with line-of-sight check) and smoke grenades (15-second particle cloud) are now fully functional.
- Movement Speed per Weapon: Holding different weapons now affects player movement speed (e.g., knife = 100%, AWP = 80%). Speed updates instantly on weapon switch.
- Auto-Open Shop: New config option
shop.auto-open-on-round-startto automatically open the weapon shop when a new round begins. - MVP System: MVP is now calculated and announced at the end of each round based on kills.
- Headshot Tracking: Headshots are now tracked and stored in the database.
- Assist System: Players who deal at least
combat.assist-damage-thresholddamage to a victim (without landing the kill) earn an assist reward (economy.rewards.assist) and anassistsstat. - HE Grenade: New high-explosive grenade (MAGMA_CREAM) with area damage that falls off with distance, respects friendly fire and supports self-damage. Tunable via
grenades.he-radius/grenades.he-damage.
Fixed
- Critical: Dependencies bundled in JAR - SQLite JDBC and HikariCP are now shaded into the plugin JAR via Shadow plugin. No more external dependency JARs needed.
- Critical: BombService shared state - Bomb state is now per-match instead of global. Multiple arenas can run concurrent matches without interfering with each other.
- Critical: Wins/Losses counted per round - Wins and losses are now only recorded once per match (in
endMatch()), not once per round. - Weapon headshot multiplier ignored - Per-weapon headshot multipliers are now used instead of the global config value.
- Armor penetration ignored - Weapon-specific armor penetration values now affect damage reduction (e.g., AWP with 0.95 penetration nearly bypasses armor).
- Range not enforced - Weapons now have effective range with damage falloff in the last 30% of range and no damage beyond max range.
- Double endRound() calls - Added guard flag to prevent race conditions when multiple events trigger round end simultaneously.
- WorldListener cancelled ALL explosions - Explosions are now only cancelled inside active arena boundaries, not server-wide.
- Chat NullPointerException - Added null checks for player session in team chat handler.
- Economy loss bonus hardcoded - Consecutive loss bonus and max loss bonus now read from config values instead of being hardcoded.
- SQLite connection pool too large - Reduced HikariCP pool size from 10 to 1 (SQLite only supports one writer).
- Bomb timer desync - Removed redundant
shouldExplode()timer. The match timer is now the single source of truth for bomb detonation. - Reload progress task leak - The reload progress-bar task now cancels itself instead of running forever after every reload.
- Grenade/bomb double-trigger - Added a main-hand guard so right-click events no longer fire twice (double grenade throw / double plant/defuse).
- Legacy color codes via MiniMessage - Pre-translated (legacy) messages are sent directly instead of through MiniMessage, which could render literal section signs.
- Lingering movement speed - Weapon movement-speed modifiers are reset on join, leave, round start and match end so they no longer persist after a match.
- Phantom snowball damage - Grenade snowballs are now ignored by the combat damage handler.
- Broken team-assignment message - Fixed the message key (
match.team-assignment) that previously displayed literal text. - money_earned never tracked - The
money_earnedstat is now incremented on every positive payout.
Changed
- AsyncPlayerChatEvent replaced - Migrated from deprecated
AsyncPlayerChatEventto Paper'sAsyncChatEvent. - Shop title - Uses a shared constant between
ShopServiceandPlayerListenerto prevent title mismatch. - Scoreboard - Switched from raw section signs to Unicode escapes for consistency.
- Random - Replaced
new Random()withThreadLocalRandom.current()for bomb carrier selection. - Round tracking - Kills and deaths are now tracked in the
Roundobject for MVP calculation. - Ammo initialized on purchase - Buying a weapon in the shop now sets its ammo to full magazine size.
- Reload safety -
/sitezero reloadno longer reloads arenas while matches are running (config, messages and weapons still reload). - Leave handling - Leaving mid-round drops a carried bomb and re-checks round-end so an emptied team resolves the round immediately.
- Grenade tuning externalized - Flash/smoke/HE radii and durations moved from hardcoded constants into
config.yml. - MatchManager thread-safety - Match lookup maps switched to
ConcurrentHashMap(async chat reads them). /sitezero statsexpanded - Now also shows assists, headshots and money earned.
Removed
LocationUtil.java- Unused utility class.SoundUtil.java- Unused utility class.- Unused per-round kill/death counters in
PlayerSession.
Technical
- Added
com.gradleup.shadowplugin to build configuration with relocation rules for HikariCP and SQLite. BombServicenow uses per-matchBombStateobjects stored in aConcurrentHashMapkeyed by match UUID.- New
GrenadeServiceclass registered as event listener for grenade mechanics. PlayerSessionextended with ammo tracking (Map<String, Integer>) and reload state.PlayerListenernow handlesPlayerItemHeldEventfor movement speed modifiers.Database.incrementHeadshots()method added.ConfigManager.getConsecutiveLossBonus()andgetMaxLossBonus()methods added.- New
assistsdatabase column with automaticALTER TABLEmigration for existing databases. EconomyService.handleAssist(),Database.incrementAssists()andDatabase.incrementMoneyEarned()added.GrenadeServiceextended with HE handling and grenade-kill bookkeeping.MatchManager.hasActivePlayers()added;SiteZero.reload()returns whether arenas were reloaded..gitattributesadded to normalize line endings (LF).- GitHub Actions workflow added for automatic Gradle builds on push/PR.
[1.2.0] - 2026-06-15
Added
- Full CS-style weapon roster: ~30 weapons across pistols, SMGs, rifles, snipers, shotguns (new SHOTGUN type) and machine guns (new MG type), each fully configurable in weapons.yml.
- Full-auto fire: holding left-click keeps firing at the weapon's fire rate for automatic weapons (per-weapon
automatic, default on for rifles/SMGs/MGs). - Weapon zoom: right-click (or F) cycles a scope;
zoom-levelis the number of zoom stages, so the AWP and Scout get a true two-step scope. - CT/T weapon exclusivity: a
teamfield per weapon (attackers/defenders/both) restricts who can buy each gun, CS-style; attackers spawn with a free Glock, defenders with a USP. - Persistent magazine HUD: the action bar always shows the held weapon's magazine/reserve, updated on switch, shot and reload.
- Reload key: press F (the swap-offhand key) to reload, with a progress bar; auto-reload on empty still applies.
- Armor durability: body armor has a point pool (
combat.start-armor) that drains as it absorbs damage (combat.armor-loss-factor) and breaks under fire; shown on the scoreboard and re-buyable to refresh when damaged. - Per-world inventory support:
match.manage-inventory: falsehands inventory ownership to a per-world plugin (e.g. Multiverse-Inventories) - SiteZero only teleports and manages match gear. - Command-driven match start:
match.auto-start(default false) - matches wait in the lobby for/sitezero start <arena>. - Round-end free phase: after a round is decided, survivors get a few seconds to walk, collect loot and plant the bomb for the plant bonus (combat stays off).
- Spectator restricted to teammates: dead players can only watch teammates - the camera is re-attached if they try to free-fly to enemies.
- Hitscan Combat: Weapons fire instant ray-traced shots with black dust tracers and muzzle/impact smoke instead of arrow projectiles. Hit tolerance configurable via
combat.hitscan-ray-size. - Reserve Ammo: Each weapon has a limited reserve pool (
reserve-ammoin weapons.yml). Reloading consumes reserve; an empty reserve blocks the reload. Action bar shows magazine/reserve. - Per-Weapon Sounds:
shoot-sound,shoot-volume,shoot-pitchper weapon. String sound keys, so resource pack custom sounds work. - Custom Model Data: Every weapon supports
custom-model-data(unique defaults 1001-1073); bomb material/model/scale configurable viaitems.*. See RESOURCEPACK.md. - Extended Buy Time: The shop stays usable for
match.extended-buy-timeseconds (default 30) after the round goes live. Buy-phase freezetime: players cannot move while buying. - Spawn Areas:
/sitezero setspawn <arena> <team> [pos1|pos2]and/sitezero setlobby <arena> [pos1|pos2]define regions; players spawn spread out inside them. - Fixed Teams:
/sitezero team attackers|defenders|autochooses a team in the lobby (honored up to balance). - Weapon Drops: Killed players drop their guns and utility; alive match players can pick them up. Drops are cleaned up each round.
- Equipment Persistence: Round survivors keep their full equipment (magazines refilled, knife guaranteed); dead players, round 1 and halftime start fresh.
- Enemy Nametag Hiding: Nametags are only visible to teammates (gold/blue team colors included).
- Planted C4 Display: The planted bomb is a glowing ItemDisplay of the C4 item - resource pack model shows, lies exactly where planted, cannot fall or float. Scale via
items.bomb-display-scale. - Config Merger: New default keys from plugin updates are merged into existing config.yml/messages.yml/weapons.yml without touching configured values.
- Modrinth Update Checker: Async startup check (toggle:
update-checker), admin notice on join. - bStats Metrics: Plugin id 31946, arena-count chart.
- Match End Flow: Winner title with team name and score, 5s free-walk end phase, lobby return, automatic next-match countdown (
match.countdown-time, default 30s). - Flashbang Darkness: Flashbangs apply blindness + darkness; detonations are logged with the affected player count.
Fixed
- Inventory wipe: pre-match inventory backups are stored binary-serialized in SQLite and restored non-destructively (a failed decode never clears the inventory). A YAML round-trip used to hand back maps instead of items and wipe everyone on a post-restart rejoin.
- Inventory shift with Multiverse-Inventories: the join path no longer clears the inventory before the world swap, which previously made per-world plugins store the emptied inventory for the wrong world.
- Armored players nearly unkillable: combat damage was reduced twice (SiteZero armor + the worn item's vanilla armor); damage is now applied directly so only the SiteZero armor model reduces it. Armor broke without the player dying before this.
- Bomb duplication: dropping the bomb with the drop key no longer leaves a copy in the inventory.
- Flashbang crash (Paper 1.21.10):
Particle.FLASHstarted requiring a Color and threw, aborting the flash before anyone was blinded; the effect is applied first and visuals are guarded. - Suffocation on spawn: zone spawns now pick a standable spot instead of the zone's minimum Y, so players don't spawn inside blocks.
- 1.21.x compatibility: Attribute lookups (max health, movement speed) are resolved at runtime, so the plugin runs across the whole 1.21.x line. Previously the unprefixed 1.21.3+ attribute names crashed on 1.21-1.21.2 with a
NoSuchFieldError. - Match freeze at match end: MiniMessage threw on the legacy-colored team name in the win title, aborting endMatch before the lobby return was scheduled.
- Shop delivering wrong weapons: Purchases now resolve by the weapon-id PDC tag instead of the item material.
- Grenades unusable: Recognition no longer depends on exact config ids/materials (
grenade-typefield + material fallback). - Bomb position: The C4 lies where it was planted (ground-snapped), not at the bomb site center; no leftover bomb entities across rounds.
- Defuse: Enemy hits interrupt an active defuse; shots can no longer be absorbed by the bomb entity or items.
- Flashbang no-op: Line-of-sight gate checked a point inside geometry and the minimum duration was below the blindness fade-in.
- Loss bonus: Pays 1400 on first loss, +500 per additional consecutive loss, total capped at 3400 (was 1900 to 4800).
- Natural regen: Full hunger no longer heals players during matches.
- Lobby equipment: No weapons are handed out in the waiting lobby anymore.
- Pickup during round end: Eliminations end the round instantly - drops are now lootable in the ROUND_END phase.
- Raw & codes: Weapon names in purchase messages are color-translated.
Changed
- Controls: left-click fires the weapon (was right-click); right-click is now the scope/zoom. Knife melee, grenades, plant and defuse stay on right-click.
- Defuse: no longer requires sneaking when the main hand is empty or holds the defuse kit (sneak + any item still works).
- Flashbang: a dense white particle wall in front of the camera plus a high, fading tinnitus (no resource pack needed); facing away only shortens the flash.
- Buy-phase freeze: only blocks horizontal movement now - players who spawn on stairs/an edge can still fall and settle.
- Armor model: a weapon's armor-penetration is the fraction of damage passing through armor (
combat.armor-reduction, default 1.0). - Weapon damage rebalanced toward CS per-shot values (AWP stays a lethal body shot).
- Default gunshot sound is
entity.zombie.attack_iron_doorwith per-class pitch variation. - Shop categories sorted by price; knife/defuse-kit special cases match config ids instead of display names.
- Default
match.countdown-timeraised to 30 seconds (lobby wait before the next match).
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:r0c1obLk:MTtB81pu"
}
// Legacy Loom dependency
dependencies {
modImplementation "maven.modrinth:r0c1obLk:MTtB81pu"
}
