
ZombieApocalypseSSS
ZombieApocalypseSSS is a comprehensive Minecraft plugin that transforms your server into a thrilling zombie survival apocalypse. Featuring advanced zombie AI, evolving variants, player infection mechanics, psychological horror elements, and dynamic events
- Загрузки
- 2K
- Подписчики
- 4
- Обновлён
- 31 мая 2026 г.
- Лицензия
- Apache-2.0
Опубликован 29 января 2026 г.
ZombieApocalypseSSS
A full-featured zombie apocalypse survival plugin for Paper 1.21+
Turn your Minecraft server into a post-apocalyptic nightmare — one corpse at a time.
🆕 What's new in 4.5.1 — Critical Bug Purge III
This patch fixes 11 bugs found during deep code review, including two that rendered entire gameplay systems non-functional and one that could cause permanent zombie immortality after a server restart.
| # | Severity | Summary |
|---|---|---|
| BF-16 | 🔴 Critical | Turret system was completely broken — crafted turrets could never be placed as actual turrets due to an Adventure vs. Legacy API mismatch in isTurretItem() |
| BF-17 | 🔴 Critical | Turret data loss on server restart — onDisable() never called turretManager.cleanup(), skipping the final save |
| BF-19 | 🔴 Critical | All zombie custom AI lost after /zapoc reload — trackedZombies was cleared but never rescanned; behavior task saw an empty collection |
| BF-18 | 🔴 Critical | Turret config changes had no effect after reload — TurretManager was never recreated; range/damage/fire-rate kept stale values |
| BF-20 | 🔴 Critical | Feign-Death zombie permanently invulnerable after restart — invulnerable=true + AI=false persisted in NBT but the revive task was lost on shutdown |
| BF-21 | 🟡 Medium | Campfire I/O spam on main thread — saveCampfires() was called on every zombie hit; replaced with a dirty-flag + 5-second flush cycle |
| BF-23 | 🟡 Medium | Screamer: 6 YAML reads per zombie per 0.5 s — all screamer config values now cached with the sun-effect values |
| BF-24 | 🟡 Medium | Breaker: success-chance YAML read per zombie per 2 s — now cached |
| BF-25 | 🟡 Medium | Hallucination map used reusable int entity ID as key — changed to UUID |
| BF-22 | 🟢 Minor | Campfire heal loop used 3×3 chunk scan — expanded to 5×5 to match tier-3 radius |
| BF-26 | 🟢 Minor | handleBreaker() repeated the enabled check already performed by the caller |
No config changes required. All existing
data.yml,campfires.yml, andturrets_data.ymlare fully compatible.
📖 Table of Contents
- ✨ Features
- 📦 Requirements
- 🚀 Installation
- ⌨️ Commands & Permissions
- 🔧 Configuration
- 🔫 CombatGunSSS Integration
- 🧩 Developer API
- 🔍 Troubleshooting
- ❓ FAQ
- 📄 Credits
✨ Features
🧠 Advanced Zombie AI
- 15+ configurable zombie types (Tank, Screamer, Assassin, Boss, etc.)
- Hive Mind — group aggro behavior
- Noise system — blocks, gunshots, explosions attract zombies
- Scent detection — sneak < walk < sprint
- Feign Death zombies revive after a delay
- Breaker AI — destroy doors and blocks
- Sun weakness (burn or slow)
🎯 Tier Evolution System
| Tier | Multiplier | Speed | Knockback |
|---|---|---|---|
| Primal | ×1.0 | — | 0 |
| Adapted | ×1.2 | +0.1 | 0 |
| Mutated | ×1.5 | +0.2 | 0.2 |
| Aberrant | ×2.5 | +0.3 | 0.5 |
📈 Progressive Difficulty
| Phase | Days | Spawn Rate | Unlock |
|---|---|---|---|
| 1 | 0–5 | 40% | Basic zombies |
| 2 | 6–15 | 70% | Status zombies |
| 3 | 16–30 | 90% | Special zombies |
| 4 | 31+ | 100% | All types |
⚠️ Fixed in 4.4.0: Phase-based spawning now works correctly.
🧬 Infection System
- 0–100 infection points
- Effects scale with level
- +5 per hit (30% chance)
- Natural decay (~1 point / 30s, faster in safe zones)
🧠 Psychological Horror (Sanity)
- BossBar display (green → purple)
- Hallucinations, fake sounds, fake messages
- Drains in darkness, near zombies, when alone
⚠️ Fixed in 4.4.0: All sanity multipliers now apply correctly.
🔥 Campfire Safe Zones
- Prevent zombie spawning
- Heal players and reduce infection
- 3 upgrade tiers
- Zombies can destroy them
🩹 Survival Items
- Bandage — stop bleeding
- Antivirus — reset infection
- Adrenaline — temporary boost with crash
- Zombie Camo — ignored by zombies
- Radio — locate supply drops
🌙 Blood Moon
- Random night event (default 5%)
- Increased spawn rate and stronger zombies
- API events available
📦 Supply Drops
- Random drops near players
- Fully configurable loot table
- Optional coordinate broadcast
⚠️ Fixed in 4.5.0: Tier 3 campfire radius now correctly blocks zombie spawns.
🔫 Sentry Turrets
- Auto-target zombies
- Multiple ammo types
- Persistent across restarts
⚠️ Fixed in 4.4.0: Data loss issue resolved.
🌍 World Structures
- Military Checkpoint
- Crashed Plane
- Hospital
- Survivor Camp
- Outpost
✔️ Scale with difficulty ✔️ No ocean spawning
📖 Survival Guide GUI
- Interactive in-game guide
- Covers all systems and mechanics
📦 Requirements
| Component | Version |
|---|---|
| Java | 21+ |
| Paper | 1.21+ |
| WorldGuard | Optional |
| ProtocolLib | Optional |
🚀 Installation
- Download the
.jar - Place it in
plugins/ - Start the server
- Edit configs
- Use
/zapoc reload
⌨️ Commands
| Command | Description |
|---|---|
/zapoc reload |
Reload config |
/zapoc summon |
Spawn zombie |
/zapoc infect |
Infect player |
/zapoc cure |
Cure |
/zapoc panic |
Spawn horde |
/zapoc bloodmoon |
Toggle event |
/zapoc supplydrop |
Force drop |
/zapoc stats |
View stats |
🔐 Permissions
| Permission | Description |
|---|---|
zapoc.admin |
Full access |
zapoc.use |
Use features |
zapoc.immune |
Immune to infection |
🔧 Configuration
Config files:
config.ymlzombies.ymlitems.ymlevents.ymlmechanics.ymlturrets.ymlsun-effect.ymlspawn-control.ymlmessages_en.ymlmessages_vi.yml
🔫 CombatGunSSS Integration
👉 https://modrinth.com/plugin/combatgunsss
- 30+ guns
- Full compatibility
- Optional but recommended
🧩 Developer API
ZombieApocAPI api = ZombieApocAPI.getInstance();
api.isInfected(player);
api.getInfectionPoints(player); // raw 0–100
api.getSanity(player); // new in 4.5.0
api.isBleeding(player); // new in 4.5.0
api.spawnZombie(world, location, "tank");
api.isInSafeZone(location);
Events:
- BloodMoonStartEvent
- BloodMoonEndEvent
- SupplyDropEvent
- ZombieInfectEvent
🔍 Troubleshooting
Zombies not spawning
- Check
spawn-control.enabled - Ensure world is enabled
- Use Paper (not Spigot)
- Test with
/zapoc summon
Zombies spawning inside Tier 3 campfire zones
- Fixed in 4.5.0
- The spawn-block check now uses a 5×5 chunk search to cover the full 40-block radius
API zombie has wrong / mixed type after spawnZombie()
- Fixed in 4.5.0
- Previous builds applied a random type first then the requested type, leaving stale PDC keys
Spawn phases not working
- Fixed in 4.4.0
- Update plugin
Sanity system not working
- Fixed in 4.4.0
- Check
mechanics.yml
Infection not disabling
- Fixed in 4.4.0
Turrets disappearing
- Fixed in 4.4.0
Recipes not showing
- Fixed in 4.3.3
Zombie names broken
- Fixed in 4.3.3
Structures spawning in ocean
- Fixed in 4.3.1+
/reload breaks plugin
- Always restart server
❓ FAQ
Does this replace vanilla zombies?
Optional — configurable.
Does it support multiple worlds?
Yes.
Is it laggy?
Optimized, but depends on config.
Can I disable infection?
Yes (config.yml).
Do I need CombatGunSSS?
No, but recommended.
Does it support Spigot?
No — Paper only.
📄 Credits
Author: Duong2012G License: Apache 2.0
Open source — contributions welcome.
Ченджлог
4.5.2Релиз1.21.9, 1.21.10, 1.21.11 · 31 мая 2026 г.
[4.5.2] — Bug-Fix Release
🔴 Critical Fixes
BUG-1 · Boss Zombie drops Diamond Armour on death
- File:
ZombieSpawnService.java—spawnBossZombie() - Root cause:
setArmorContents()was called without setting drop chances to 0. Bukkit's default drop chance is ~8.5%, so players could farm Diamond Helmet / Chestplate by killing the Boss. - Fix: Added explicit
setHelmetDropChance(0f),setChestplateDropChance(0f),setLeggingsDropChance(0f), andsetBootsDropChance(0f)after every armour assignment, including the custom-model-data head path.
BUG-2 · Boss HP inconsistent — evolveZombie() multiplier overridden
- File:
ZombieSpawnService.java—spawnBossZombie() - Root cause:
spawnCustomZombie()internally calledevolveZombie()which applied a tier health multiplier (up to ×2.5 for ABERRANT), butspawnBossZombie()then hard-reset the attribute toboss.healthconfig value, discarding the tier scaling entirely. - Fix:
spawnBossZombie()now setsbossHealthfirst, then callsevolveZombie()so the tier multiplier compounds on top of the intended base. Boss HP in late-game now scales correctly with the threat level.
🟡 Medium Fixes
BUG-3 · ABERRANT zombies appear ~35 days too early
- File:
ZombieEvolutionService.java—determineTier() - Root cause: The
elsebranch coveredthreat >= 30, but the class-level comment stated "Threat > 50: Aberrant possible". Players hit ABERRANT-tier enemies during Phase 3 (Day ~15) instead of Phase 5 (Day ~25+). - Fix: Inserted an explicit
threat < 50bracket (no ABERRANT, Mutated max) and moved ABERRANT tothreat >= 50only, matching the original design intent.
BUG-4 · /zapoc infect shows hardcoded "10m" regardless of config
- File:
ZombieApoc.java—onCommand()case"infect" - Root cause:
incubationTickswas read from config but immediately discarded; the player message used the literal string"10m". - Fix:
timeDisplayis now derived fromincubationTicks / 20seconds so the displayed time always reflects the actual configured value.
BUG-5 · ZombieBehaviorTask batch-skipping uses hashCode() % 2 (uneven distribution)
- File:
ZombieBehaviorTask.java—run() - Root cause:
UUID.hashCode()is a 32-bit hash with no uniformity guarantee. In practice this can result in 70 % of zombies processed on one tick and 30 % on the other, defeating the purpose of two-bucket batching. - Fix: Changed the modulus operand to
UUID.getLeastSignificantBits()which is a raw 64-bit random value from the UUID v4 spec, giving near-perfect 50/50 split.
BUG-6 · HallucinationListener double-registration on /zapoc reload
- File:
ZombieApoc.java—startTasks() - Root cause:
startTasks()checkedif (hallucinationListener == null)before creating a new instance, butcancelAllTasks()already set it tonull. In specific edge cases (e.g.psychological-horror.enabledtoggled via config between reloads) the old instance could survive unregistered yet the new instance would be registered on top. - Fix:
startTasks()now unconditionally callsHandlerList.unregisterAll()and nulls the field before constructing a freshHallucinationListener, making the lifecycle explicit and immune to ordering issues.
BUG-7 · WorldEventTask blood-moon maps leak UUID entries for unloaded worlds
- File:
WorldEventTask.java - Root cause:
bloodMoonStatesandbloodMoonRolledDayused world UUIDs as keys but never removed entries when a world was unloaded (e.g. on map rotation or/mv unload). Over time this created an unbounded set of stale entries. - Fix:
WorldEventTasknow implementsListenerand handlesWorldUnloadEventto remove both maps' entries for the unloaded world. The task is registered and unregistered as a listener instartTasks()/cancelAllTasks().
🟢 Minor Improvements
PERF-1 · ZombieBehaviorTask reads 7 YAML values every 10-tick cycle
- File:
ZombieBehaviorTask.java - Root cause:
moanEnabled,moanChance,moanVol,moanPitch,mutationEnabled,mutationInterval, andbreakingEnabledwere fetched fromFileConfigurationon every call torun(). - Fix: All seven values are now cached as class-level fields alongside the
existing sun / screamer caches and refreshed once every 100 ticks inside
reloadSunConfigCache().
PERF-2 · handleExplosive() reads explosive.power from YAML at explosion time
- File:
ZombieBehaviorTask.java - Fix: Added
cachedExplosivePowerfield, populated inreloadSunConfigCache().
PERF-3 · CampfireManager.isInsideSafeZone() reads check-radius from YAML per call
- File:
CampfireManager.java - Root cause:
plugin.getMechanicsConfig().getInt("campfires.check-radius", 100)was called on every invocation.isInsideSafeZone()is called once per second per online player fromPlayerStatusTask, making this a hot path. - Fix: Added
cachedCheckRadiusfield, initialised in the constructor and refreshed inreload().
DEPRECATION-1 · TurretListener used legacy ChatColor string API
- File:
TurretListener.java - Fix: All player messages converted to
net.kyori.adventure.text.ComponentusingNamedTextColorandTextDecoration, the standard approach on Paper 1.16+.
DEPRECATION-2 · ZombieApoc.sendActionBar() used BungeeCord spigot bridge
- File:
ZombieApoc.java - Fix: Replaced
player.spigot().sendMessage(ChatMessageType.ACTION_BAR, ...)with the Paper-nativeplayer.sendActionBar(Component)call. Removed unusednet.md_5.bungee.api.*imports.
4.5.1Релиз1.21.9, 1.21.10, 1.21.11 · 21 мая 2026 г.
[4.5.1] - 2026-05-20 - Critical Bug Purge III
Fixed
Turret system completely non-functional — Adventure vs. Legacy API mismatch (BF-16) — The crafting recipe assigned the "Sentry Turret" display name via the Adventure API (
meta.displayName(Component.text(...))).TurretListener.isTurretItem()read the name with the deprecated Legacy API (meta.getDisplayName()), which always returns an empty string when the name was set via Adventure — so the check never matched andTurretManager.createTurret()was never called. Placing a crafted turret had no effect.isTurretItem()now reads the AdventuredisplayName()component first, serialises it to plain text, and falls back to the legacy accessor for pre-migration items.onDisable()never calledturretManager.cleanup()— turret data loss on restart (BF-17) —CampfireManager.shutdown()was called inonDisable()but the analogousTurretManager.cleanup()call was absent.cleanup()is responsible for cancelling the internal firing task and flushingturrets_data.yml. Turrets placed during a session without a prior auto-save were silently lost on server restart.turretManager.cleanup()is now called fromonDisable()./zapoc reloadwiped all zombie custom AI — missingscanExistingZombies()call (BF-19) —loadConfig()(called byloadAllConfigs()at the start of every reload) calledtrackedZombies.clear().ZombieBehaviorTaskiterates that collection each tick; after a reload it was empty, so every living custom zombie lost its AI (no moans, no screaming, no sun effect, no mutation) until the zombie died or new ones spawned.scanExistingZombies()is now called at the end of the reload handler to repopulate the map immediately./zapoc reloaddid not rebuildTurretManager— config changes had no effect (BF-18) —TurretManagerreadsrange,damage, andfire-rate-ticksonly in its constructor. After a reload the old instance kept running with stale values. The reload handler now callsturretManager.cleanup()and recreates a freshTurretManager, matching the pattern already applied toStructureManager.Feign-Death zombie permanently invulnerable and AI-less after server restart (BF-20) — When a Feign-Death zombie "died", the code set
invulnerable=trueandAI=false, then scheduled aBukkitRunnableto revive it after 3–5 s. If the server restarted during that window, the task was discarded but the entity flags persisted in NBT — leaving an immortal, motionless zombie that could never be killed. A new PDC keyfeign_activeis set at the start of feign-death and cleared on revive.ZombieCleanupListener.onChunkLoadnow detects this key and immediately invokes the sharedSpecialZombieListener.reviveZombie()method, which also handles the scheduled revive path.CampfireManager.damageCampfire()triggered a full YAML write on every zombie hit (BF-21) —saveCampfires()(atomic file I/O on the main thread) was called each time a zombie attacked a campfire. With multiple zombies attacking multiple campfires simultaneously during a Blood Moon, this caused significant main-thread stalls. Replaced with a dirty flag: damage marks the campfire data as dirty, and the periodic task flushes to disk at most once every ~5 seconds. Immediate saves are still performed when a campfire is destroyed.ZombieBehaviorTask.handleScreamer()read 6 YAML values per zombie per 0.5 s (BF-23) — Every invocation ofhandleScreamer()calledgetZombiesConfig().getDouble/getString/getInt()six times. With 20 Screamer zombies that is 120 YAML map lookups per 500 ms. All six values (scream-chance,particle,particle-count,scream-range,darkness-duration,message) are now cached in the same 5-second refresh cycle used by sun-effect config.ZombieBehaviorTask.handleBreaker()readsuccess-chancefrom YAML per zombie per 2 s (BF-24) —zombie-breaking.success-chancewas fetched fromFileConfigurationinside the per-zombie loop every 40 ticks. Now cached alongside the other task-level values.PsychologicalHorrorTask.activeHallucinationsused entity ID (int) as map key (BF-25) — Entity IDs are 32-bit integers reused by the server after an entity despawns. Usingentity.getEntityId()as the map key could cause staleHallucinationDataentries to collide with or shadow new entries sharing the same recycled ID. The key type is nowUUID(entity.getUniqueId()), which is guaranteed unique per entity.Campfire heal task used 3×3 chunk scan instead of 5×5 (BF-22) —
isInsideSafeZone()andonSpawn()were already corrected to 5×5 in a prior release, but the heal loop insidestartTask()still searched only 3×3 chunks (~32 block reach). Tier-3 campfires have a 40-block radius, so players near the edge of the safe zone were not receiving the Regeneration effect. The heal loop now uses 5×5, consistent with all other campfire range checks.
Notes
- No config file changes are required. All existing
data.yml,campfires.yml, andturrets_data.ymlfiles are fully compatible with 4.5.1. - Any turrets placed before this update that were saved in
turrets_data.ymlwill load and function normally. Turrets placed during a session that ended with a 4.5.0 server stop without an intermediate save may have been lost — this is the bug being fixed.
4.4.0Релиз1.21.9, 1.21.10, 1.21.11 · 17 апреля 2026 г.
[4.4.0] - 2026-04-17 - Bug Purge & Correctness Update
Fixed
SurvivalGuideListener.PAGE_KEYNPE on startup —static final NamespacedKey PAGE_KEYwas initialised at class-load time before the plugin instance existed, causing aNullPointerExceptionon every server start.PAGE_KEYis now an instance field initialised in the constructor. (BF-01)InfectionListenerignoredinfection.enabledconfig — infection spread on every zombie hit regardless ofinfection.enabled: falseinconfig.yml.ZombieBehaviorServicealready checked the flag butInfectionListenerdid not, making the config option partially ineffective. (BF-02a)InfectionListenerignoredzapoc.immunepermission — players withzapoc.immunecould still receive infection points. The permission is now checked in bothInfectionListenerandZombieBehaviorService. (BF-02b)Day-based spawn-rate (
shouldSpawn()) was completely bypassed —ZombieBehaviorServiceruns atEventPriority.HIGHand cancelled natural spawns beforeSpawnListenerran, so the phase-based rates (40% / 70% / 90% / 100%) never took effect — zombies always spawned at 100% regardless of server age.shouldSpawn()is now called directly insideZombieBehaviorService.onCreatureSpawn. (BF-03)TurretManagercould permanently deleteturrets_data.yml— the save code calleddataFile.delete()beforerenameTo(). If the rename failed the data file was gone permanently. Replaced withFiles.move(ATOMIC_MOVE)and a non-atomic fallback. (BF-04)Sanity config multipliers loaded but never applied —
sanityDrainBase,nightMultiplier,infectedMultiplier,lowHealthMultiplier, andnearbyZombiesMultiplierwere read frommechanics.ymlbutupdateSanity()used hardcoded constants. Editing these values in config had no visible effect. All five multipliers are now applied correctly. (BF-05)whisperPhrasesconfig key loaded but never used —handleWhispers()used an internal hardcoded array instead of the phrases frommechanics.yml. Player-configured phrases includingphrases-vi/phrases-enare now displayed correctly. (BF-06)hg.no-netherite-dropbypass via corpse zombie — the death handler stripped netherite fromevent.getDrops()but then re-equipped the corpse zombie with raw armour including netherite. A player could kill the corpse to recover the items. Netherite is now stripped from the corpse's equipment when the option is enabled. (BF-07)Tank zombie
ENTITY_RAVAGER_STEPsound spam —handleTank()fired a full-volume step sound every 0.5 s with no throttle, flooding clients when multiple Tank zombies were active. A 10% per-call chance now limits the frequency. (BF-08)panicCooldownsHashMap memory leak — UUID entries were added on/zapoc panicbut never removed on disconnect. Entries are now removed inonPlayerQuit. (BF-09)Stalker spawn used zombie's world for player's Y lookup —
getHighestBlockYAt()was called on the zombie's world; if the target player had recently changed worlds the Y could be wrong. Player's own world is now used for the lookup. (BF-10)
Performance
handleSunEffect()YAML reads inside per-zombie loop eliminated — sevenFileConfiguration.get*()calls per zombie per tick removed. All sun-effect config values are now cached at task level and refreshed every 5 s. Eliminates ~1,400 redundant map lookups per second at 200 tracked zombies. (BF-11)Math.random()replaced withThreadLocalRandomthroughout — 20+ usages replaced acrossCampfireManager,ZombieApoc,ZombieBehaviorService,ZombieBehaviorTask,PlayerStatusTask,WorldEventTask,ZombieEvolutionService, andSpecialZombieListener. (BF-12)
Notes
- No config changes required. All existing
data.yml,campfires.yml, andturrets_data.ymlare fully compatible.
4.3.3Релиз1.21.9, 1.21.10, 1.21.11 · 14 апреля 2026 г.
ZombieApocalypseSSS – Bug Fix Report (v4.3.2 → v4.3.3)
Custom Items Retained
- Bandage – stops bleeding
- Antivirus – cures infection
- Adrenaline – speed/strength boost (60s cooldown, crash effect after)
- ZombieCamo – zombies ignore you for 60s
- Radio – detect next supply drop location (5min cooldown)
Bug Fixes
| # | Severity | File | Description |
|---|---|---|---|
| 33 | 🔴 | ZombieSpawnService | Zombie names encoded as §c§lSCREAMER → fixed to §c§lSCREAMER. Affected: SCREAMER, Stalker, Crawler, Feign Death, Boss. Names were rendering as garbage text on all servers. |
| 34 | 🔴 | ZombieApoc (onEnable) | registerRecipes() was never called — comment RECIPES REMOVED was accidentally left after feature re-enable. All 6 vanilla crafting recipes (bandage, antivirus, adrenaline, zombie camo, radio, turret) were silently unavailable. |
| 35 | 🔴 | BandageItem | create() read custom-model-data from getPluginConfig() (config.yml) instead of getItemsConfig() (items.yml). Bandages would always spawn without CustomModelData, breaking resource packs. |
| 36 | 🔴 | SurvivalGuideListener | SurvivalGuideHolder.getInventory() returned null — violates InventoryHolder contract. Fixed: holder stores and returns its own Inventory reference. |
| 37 | 🔴 | MilitaryCheckpoint | spawnTowerSniper() re-rolled random offset/height instead of using the cached towerBase/towerHeight computed in cacheTowerPosition(). Tower snipers could spawn anywhere except the actual tower. |
| 38 | 🟠 | AdrenalineItem | onInteract() lacked RIGHT_CLICK_AIR / RIGHT_CLICK_BLOCK guard. Item could trigger on LEFT_CLICK_BLOCK in edge cases. |
| 39 | 🟠 | ZombieApoc (TabComplete) | /zapoc give camo was listed in tab-complete but the switch-case expected "zombie-camo". Fixed to "zombie-camo" in both places. |
| 40 | 🟠 | CrashedSupplyPlane | setMaxHealth() deprecated since Bukkit 1.16. All mob health now uses Attribute.GENERIC_MAX_HEALTH.setBaseValue(). Affected: pilot, supply guard, medic, soldier zombies. |
| 41 | 🟠 | MilitaryCheckpoint | Same setMaxHealth() deprecation fix for heavy soldiers, commanders, and tower snipers. |
| 42 | 🟠 | AbandonedHospital | WHITE_BED placement ignored required BlockData facing direction — bed failed silently. Replaced with WHITE_WOOL as visual equivalent. |
| 43 | 🟠 | RadioItem | Lore cooldown update only scanned main hand + off hand. If player moved radio to any other slot the cooldown label stayed stale forever. Fixed to scan full inventory. |
| 44 | 🟡 | Structure types (×5) | isReplaceable() and isValidMobSpawn() copy-pasted in 5 classes. Extracted to new StructureHelper utility class. |
| 45 | 🟡 | AbandonedHospital, AbandonedOutpostPro, SurvivorCamp | Structure zombies used vanilla world.spawnEntity() — bypassing custom AI, tier evolution, sun effects, and tracking. Now use ZombieSpawnService.spawnCustomZombie(). |
New Utility Class
StructureHelper (structure/StructureHelper.java)
isReplaceable(Material)— shared replaceable-block checkisValidMobSpawn(World, Location)— 3-block column spawn validationsetBlockSafe(World/Location, Material)— convenience block-place wrapper
[4.3.3] - 2026-04-13 - Bug Purge & Structure Overhaul
Fixed
- Zombie name encoding — SCREAMER, Stalker, Crawler, Feign Death, and Boss zombies displayed garbage text (
§c§lSCREAMER) on all servers due to source-file encoding corruption. All names now render correctly. - Recipes never registered — all 6 crafting recipes (Bandage, Antivirus, Adrenaline, Zombie Camo, Radio, Sentry Turret) were silently missing.
registerRecipes()is now correctly called inonEnable. - Bandage CustomModelData —
BandageItem.create()read CMD from the wrong config file (config.ymlinstead ofitems.yml). Bandages now respect resource-pack model overrides. - SurvivalGuide GUI crash —
SurvivalGuideHolder.getInventory()returnednull, violating theInventoryHoldercontract. The holder now stores and returns a properInventoryreference. - Tower sniper spawn position —
MilitaryCheckpointsnipers re-randomised their spawn offset on every call instead of using the cached tower position fromcacheTowerPosition(). Snipers now always spawn on the tower platform they are supposed to guard. - AdrenalineItem left-click trigger — added missing
RIGHT_CLICK_AIR / RIGHT_CLICK_BLOCKguard; item could fire on left-click in edge cases. /zapoc give zombie-camo— tab-completion listed"camo"but execution expected"zombie-camo". Both are now consistent.setMaxHealth()deprecation — replaced all deprecatedLivingEntity.setMaxHealth()calls inCrashedSupplyPlaneandMilitaryCheckpointwithAttribute.GENERIC_MAX_HEALTH.setBaseValue().- AbandonedHospital beds —
WHITE_BEDplacement requires directionalBlockDatanot set during procedural generation, causing silent failures. Replaced withWHITE_WOOLas a visual equivalent. - RadioItem stale cooldown lore — cooldown label in item lore only updated when the radio was in the main hand or off hand. Now scans the full inventory so the label always reflects the real cooldown.
Changed
- Structure zombies now use plugin AI —
AbandonedHospital,AbandonedOutpostPro, andSurvivorCampspawned plain vanilla zombies that bypassed custom AI, tier evolution, sun effects, and the tracking map. They now callZombieSpawnService.spawnCustomZombie(), so all structure zombies scale with server difficulty like any other custom zombie. Thematic equipment (hospital gown, outpost guard gear, survivor leather) is applied on top of the custom zombie.
Added
StructureHelperutility class —isReplaceable(Material),isValidMobSpawn(World, Location), andsetBlockSafe()were copy-pasted identically across 5 structure classes. This shared utility eliminates the duplication. All structure implementations now delegate toStructureHelper.
Notes
- No config file changes required for this update. Existing
data.yml,campfires.yml, andturrets_data.ymlare fully compatible. - This release is recommended for all servers running 4.3.x.
4.3.1Релиз1.21.9, 1.21.10, 1.21.11 · 9 апреля 2026 г.
ZombieApocalypseSSS – Bug Fix Report (v4.3 → v4.3.1)
Custom Items Retained
- Bandage – stops bleeding
- Antivirus – cures infection
- Adrenaline – speed/strength boost (now with 60s cooldown)
- ZombieCamo – zombies ignore you for 60s
- Radio – detect next supply drop location
Removed Items
Gun system (commented out), Ammo, Landmine, Grenade, Flashbang, MedKit — stubs removed from ZombieUtils.
Bug Fixes
| # | Severity | File | Description |
|---|---|---|---|
| 1 | 🔴 | WorldEventTask | Blood Moon now rolls once per night, not every 5 s (was ~99% effective chance) |
| 2 | 🔴 | ZombieApoc | /zapoc infect & /zapoc cure now use infectionPointsKey (new system) |
| 3 | 🟠 | PlayerStatusTask | Removed legacy infectKey migration block that caused same-tick inconsistency |
| 4 | 🟠 | WorldEventTask | Supply drop location uses getHighestBlockYAt on main thread safely |
| 5 | 🟠 | WorldEventTask | Loot chance uses nextDouble()*100 (was nextInt(100) >= double) |
| 6 | 🟡 | PlayerStatusTask | tickCount changed to long (no overflow) |
| 7 | 🟡 | ZombieApoc | isPlayerInfected() now checks infectionPointsKey |
| 8 | 🟡 | WorldEventTask | Blood Moon messages routed through i18n system |
| 9 | 🔴 | ZombieCleanupListener | Zombie tracking restored on chunk reload (ChunkLoadEvent) |
| 10 | 🔴 | ZombieApoc | /zapoc reload unregisters old StructureManager listener (was multiplying on each reload) |
| 11 | 🟠 | StructureManager | Structures skip ocean/river biomes |
| 12 | 🟠 | ZombieBehaviorTask | Screamer config now reads from correct file (zombies.yml) and correct path |
| 13 | 🟡 | WorldEventTask | Supply drop rotates among random online player (not always first in list) |
| 14 | 🟡 | PlayerStatusTask | Bleeding uses System.currentTimeMillis() for stable 2 s interval under lag |
| 15 | 🔴 | AntivirusItem | Now clears infectionPointsKey — previously never worked |
| 16 | 🔴 | ZombieUtils | All getItemByKey calls are null-safe with log warnings |
| 17 | 🟠 | AdrenalineItem | 60-second cooldown added (was infinite-spam) |
| 18 | 🟠 | ItemListener | EquipmentSlot.HAND guard prevents items firing twice per click |
| 19 | 🟡 | PsychologicalHorrorTask | Hallucination zombie spawned as PLUGIN reason, not intercepted by SpawnListener |
| 20 | 🟡 | RadioItem | Cooldown lore update checks both main hand and offhand |
| 21 | 🟡 | SpecialZombieListener | NamespacedKey for ability cooldown cached (not created per-hit) |
| 22 | 🔴 | ZombieCamoItem | Now writes to plugin.getCamoKey() ("apoc-camo") — previously never worked |
| 23 | 🔴 | BandageItem | Checks PersistentDataType.INTEGER (was BYTE) — previously never worked |
| 24 | 🔴 | ZombieBehaviorService | onPlayerDeath uses correct key names — bleeding/infect now cleared on death |
| 25 | 🟠 | ZombieUtils | Removed dead stubs for unregistered item keys (gun, ammo, landmine, etc.) |
| 26 | 🟠 | ZombieBehaviorService | Bleeding config reads from zombiesConfig (was pluginConfig) |
| 27 | 🔴 | ZombieApoc | /zapoc give <gun> no longer NPE-crashes; shows friendly message |
| 28 | 🟠 | ZombieBehaviorTask | moans, mutation, zombie-breaking now read from zombiesConfig |
| 29 | 🟠 | ItemProtectionListener | CMD validation uses getItemsConfig() (was getConfig()) |
| 30 | 🟠 | ZombieApoc | /zapoc stats shows actual infection points (was always "NO") |
| 31 | 🟠 | ZombieBehaviorTask | Screamer config path fixed: zombies.types.screamer.* |
| 32 | 🟡 | SurvivalGuideListener | GuidePage.valueOf() wrapped in try/catch |
4.3Релиз1.21.9, 1.21.10, 1.21.11 · 5 апреля 2026 г.
[4.3] — 2026-04-05 🏗️ Structure System Refactor
Complete overhaul of all structure generation code for maintainability and safety.
🏗️ Structure System Overhaul
MilitaryCheckpoint.java
- Refactored rotation system: Unified
transform()andapplyRotation()methods - Variant enum system: Converted from integers to proper
Variantenum (ABANDONED, OVERRUN, ACTIVE) - Constants extraction: Added 50+ named constants:
SPAWN_RADIUS,MAX_TERRAIN_HEIGHT_DIFFROAD_LENGTH,ROAD_HALF_WIDTHBARRIER_LENGTH_BASE,TOWER_HEIGHT_BASESHULKER_CHANCE,CHEST_SEARCH_ATTEMPTSHEAVY_SPAWN_CHANCE,BOSS_HEALTH_BASE, etc.
- Improved setBlockSafe(): Added comprehensive
isReplaceable()helper - Cached tower position: Pre-calculate for sniper spawning
CrashedSupplyPlane.java
- Fixed setBlockSafe(): Properly handles replaceable blocks
- Mob spawn safety: Added
isValidMobSpawn() - Location-based random: Unique randomness per structure
- Performance optimization: Reduced
Location.clone()calls - Improved canSpawn(): Height variation checks
AbandonedHospital.java
- Added constants:
SPAWN_CHECK_RADIUS,MAX_HEIGHT_VARIATION,ZOMBIE_COUNT - Safe block placement:
setBlockSafe()withisReplaceable() - Mob spawn safety:
isValidMobSpawn() - Safe chest placement:
placeChestSafely()
SurvivorCamp.java
- Complete refactor: Extracted all magic numbers to constants
- Tent validation:
buildTent()checks space before building - Safe methods:
setBlockSafe(),placeBlockSafely(),placeChestSafely()
AbandonedOutpostPro.java
- Added constants:
FENCE_BROKEN_CHANCE,STRUCTURE_RADIUS - Improved canSpawn(): Height check + replaceable block validation
- Mob spawn safety:
spawnOutpostZombies()with validation
🧹 Code Cleanup
Removed Unused Files (11 files)
- EngineeringTableManager.java — No longer used
- items/impl/*.java (10 files) — Unused items:
- ChainsawItem.java, FlashbangItem.java, GrenadeItem.java
- KatanaItem.java, LandmineItem.java, MedKitItem.java
- MeleeWeaponItem.java, MolotovItem.java, SimpleItem.java
- ZGunWeapon.java
Updated Files
- StructureManager.java — Changed
AbandonedOutpost→AbandonedOutpostPro
📊 Statistics
- Files refactored: 5 structure files
- Files deleted: 11 unused files
- Constants added: 75+
- Helper methods added: 15+
🔒 Safety Improvements
All structures now have:
- ✅
setBlockSafe()withisReplaceable() - ✅
isValidMobSpawn()for safe spawning - ✅ Height-aware
canSpawn()validation - ✅ Location-based random seed
- ✅ Constants replacing magic numbers
4.2Релиз1.21.9, 1.21.10, 1.21.11 · 5 апреля 2026 г.
[4.2] — 2026-04-05 🧹 The Streamlining Update
Plugin refactored to focus on core survival mechanics. Guns and weapons moved to separate plugin.
🔥 Major Changes
🗑️ Removed Custom Items (except 5 core items)
All custom items have been removed except:
- Bandage — Paper ×2 + String
- Antivirus — Golden Apple + Potion + Fermented Spider Eye
- Adrenaline — Sugar + Glowstone Dust + Spider Eye
- Zombie Camo — 8× Rotten Flesh + Slime Ball
- Radio — 9× Compass
Removed: medkit, molotov, grenade, flashbang, landmine, goggles, all guns (pistol, smg, ak47, sniper, shotgun), all melee weapons (bat, katana, chainsaw, axes), and ammo.
🔨 Removed Engineering Table
The EngineeringTableManager has been completely removed. All remaining items now use vanilla crafting table recipes.
🔫 CombatGunSSS Integration
For guns, ammo, and advanced weaponry, install the companion plugin:
- CombatGunSSS — 30+ guns with ballistics engine
- Full API compatibility between plugins
- Seamless infection and zombie damage integration
4.1Релиз1.21.9, 1.21.10, 1.21.11 · 14 марта 2026 г.
[4.1] — 2026-03-14 🔧 The Bug Purge Update
23 bugs fixed across all severity levels. This release focuses entirely on correctness, stability, and developer experience — no new gameplay features, just a rock-solid foundation.
🔴 Critical Fixes
🏰 Turrets lost on every server restart (BUG-02)
loadTurrets() and saveTurrets() were both empty // TODO stubs.
Every turret placed by players vanished after a restart.
Fix: Full persistence implemented via turrets_data.yml using atomic
write (.tmp → rename) to prevent data corruption on crashes.
📦 Supply Drop loot table was never loaded (BUG-03)
spawnSupplyDrop() read supply-drop.loot from config.yml, but this section
is defined in events.yml. The config section was always null, so every
Supply Drop fell back to a hardcoded 5-item list, ignoring all admin configuration.
Fix: spawnSupplyDrop() and all related methods now read exclusively from
eventsConfig (events.yml). Admin-configured loot tables are now respected.
🌕 blood-moon.chance config had no effect (BUG-04)
The bloodMoonChance field was loaded from config (default 5%) but never
referenced in checkBloodMoon(). Blood Moon triggered at 100% probability
every N days instead of the configured 5% chance.
Fix: Math.random() < bloodMoonChance check added in checkBloodMoon().
Blood Moon now correctly triggers by chance each eligible night.
💥 NullPointerException when Molotov reached max range (BUG-05)
ProjectileHitEvent fires when a projectile expires at max range, at which point
both getHitBlock() and getHitEntity() return null. The handler assumed at
least one was non-null, causing a server-crashing NPE.
Fix: Three-way null check: hit block → hit entity → projectile location (fallback).
🟠 High Severity Fixes
🩸 Bleeding damage triggered every 40 seconds instead of 2 (BUG-06)
PlayerStatusTask runs every 20 ticks (1 second). The bleeding check used
tickCount % 40 == 0, which fires every 40 runs = every 40 seconds.
Players with bleeding could regenerate faster than they took damage.
Fix: Changed to tickCount % 2 == 0 → bleeds every 2 seconds as intended.
⚠️ No combined cap on zombie difficulty multipliers (BUG-07)
Three independent scaling systems stacked without a total ceiling:
- Global difficulty multiplier (capped at ×3.0 ✓)
- Evolution tier multiplier (Aberrant ×2.5 ✓)
- Per-player
daysSurvivedscaling (no cap ✗)
On a 100-day server, the combined multiplier could reach ×26×, making zombies practically unkillable.
Fix: daysSurvived scaling is now capped — health ≤ ×3.0, speed ≤ ×2.0,
spawn count ≤ 5 per spawn event.
🔫 Gun reload lock persisted through player relog (BUG-08)
RELOAD_COOLDOWN_KEY and FIRE_COOLDOWN_KEY were stored in
player.getPersistentDataContainer() as Unix timestamps. If a player disconnected
mid-reload and reconnected, the stale future timestamp would lock them out of
firing/reloading until the original timer expired.
Fix: Both cooldowns moved to ConcurrentHashMap<UUID, Long> in-memory maps.
Cleared automatically when the player disconnects.
👻 Hallucination Villager stood completely still (BUG-09)
updateHallucinations() called mob.setTarget(player) to make hallucinations
chase their target. While Zombie entities respond to setTarget() by pathfinding
toward the player, Villager ignores it (Villagers don't attack).
Fix: Villager hallucinations now use getPathfinder().moveTo(player, 1.15)
with periodic re-pathfind if the current path becomes null.
👁️ Players who joined late could see hallucination entities (BUG-10)
spawnHallucination() called hideEntity() only for players online at spawn time.
Any player joining after a hallucination spawned would see a phantom zombie/villager
wandering around with no explanation.
Fix: The existing onPlayerJoin() handler correctly hides all active
hallucinations for new joiners. Spawn-time hiding loop now also reliably covers
all current online players.
🟡 Medium Severity Fixes
🪓 Weapon upgrade chain used hardcoded CustomModelData values (BUG-11)
FeaturesListener hardcoded CMDs like 3004, 3011, 3005 directly in Java.
Changing any custom-model-data in items.yml would silently break upgrade
chains with no error or warning.
Fix: CMDs are now read from items.yml and mechanics.yml at runtime.
Upgrade chains survive config customization.
🔥 Campfire data corrupted if world name contained _ (BUG-12)
Save keys used worldName_x_y_z format. Loading split on _ and took parts[0]
as the world name. A world named my_world would produce 5 parts instead of 4,
causing parts[0] = "my" → Bukkit.getWorld("my") = null → all campfires lost.
Fix: Keys now use worldUUID_x_y_z (UUID with hyphens stripped = always 32
hex chars, no ambiguous underscores). Legacy world-name keys are still parsed as
a migration fallback.
🌙 blood-moon.enabled / supply-drop.enabled read from wrong config (BUG-14)
Both keys are defined in events.yml, but WorldEventTask.run() read them from
config.yml. Since config.yml doesn't contain these keys, the fallback true
was always used — neither event could be disabled.
Fix: All WorldEventTask config reads now use eventsConfig consistently.
☠️ Corpse reanimation could not be disabled (BUG-15)
When a player died, items were cleared and a zombie "corpse" was spawned wearing their gear. There was no config toggle — servers running minigames or PvP arenas had no way to opt out of this mechanic.
Fix: Gated behind corpse-reanimation.enabled in mechanics.yml (default: true).
💬 Misleading comment in ZombieBehaviorTask (BUG-16)
Comment said // Every 20 ticks = 1s next to tickCounter % 2 == 0. The actual
math (task runs every 10 ticks; %2 = every 2 runs = every 20 ticks = 1s) was
correct but the comment was confusing enough to invite accidental breakage.
Fix: Comment updated to explain the full calculation chain.
🎆 Default Screamer particle "PORTAL" is invalid in Paper 1.21 (BUG-17)
Particle.valueOf("PORTAL") throws IllegalArgumentException in Paper 1.21+.
The exception was caught and logged, but the screamer ability silently failed every
time on unmodified configs.
Fix: Default changed to "ENTITY_EFFECT" in both code and zombies.yml.
🟢 Code Quality
| # | Change |
|---|---|
| 🗑️ BUG-18 | Deleted manager/ZombieManager.java — 532 lines of dead code never instantiated. Contained a severe anti-pattern (one BukkitTask per zombie). |
| 🗑️ BUG-19 | Deleted manager/EventManager.java — 739 lines of boilerplate never registered as a Listener in onEnable(). |
| 📐 BUG-20 | Added missing constants to ZombieConstants: AI ranges, noise radii, bleed values, day-scaling caps. Magic numbers reduced across codebase. |
| 🏷️ BUG-21 | Renamed trapKey / is_trap PDC key to engineeringItemKey / is_engineering_item in EngineeringTableManager. Ammo is not a trap. |
| 🏷️ BUG-22 | Renamed ZombieUtils.applyAI() → applyFollowRange(). The method only sets follow range to 40 blocks — the old name implied far more than it did. |
| 🧹 BUG-23 | ZombieCleanupListener now removes zombies from the tracking map on chunk unload, freeing strong references to de-activated entities and reducing memory pressure on long-running servers. |
⚙️ Config Changes
mechanics.yml — new keys:
corpse-reanimation:
enabled: true # Set false to disable zombie corpse on player death
infection:
natural-decay:
enabled: true
chance-per-second: 0.033 # ~1 point per 30s; 3× faster in Campfire safe zones
events.yml — new key:
blood-moon:
cycle-days: 7 # (moved from config.yml) eligible cycle in days
zombies.yml — changed default:
screamer:
particle: ENTITY_EFFECT # was: PORTAL (invalid in Paper 1.21)
🧩 API Changes
// ✅ Fixed
api.isInfected(player) // now correctly returns true when infected
// ✅ New
api.getInfectionLevel(player) // int 0–5
api.getInfectionPoints(player) // int 0–100
api.setInfectionPoints(player, n)
api.getThreatLevel() // double
api.getZombieTier(zombie) // ZombieTier enum
api.isInSafeZone(location) // boolean
api.spawnZombie(world, loc, type)
// ⚠️ Deprecated
api.infectPlayer(player, ticks) // use setInfectionPoints() instead
Комментарии
Загружаем…