
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
4.5.2
Compatibility
Changes
[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.
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:pSQD27Eo:XPppS2ek"
}
// Legacy Loom dependency
dependencies {
modImplementation "maven.modrinth:pSQD27Eo:XPppS2ek"
}
