Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
package com.sk89q.worldguard.bukkit.listener;

import com.destroystokyo.paper.event.entity.EntityZapEvent;
import com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldguard.LocalPlayer;
Expand Down Expand Up @@ -657,20 +658,21 @@ public void onCreatureSpawn(CreatureSpawnEvent event) {
return;
}

EntityType entityType = event.getEntityType();
handleCreatureSpawn(event, event.getLocation(), event.getEntityType(), event.getSpawnReason(), cfg, wcfg);
}

private static void handleCreatureSpawn(Cancellable event, Location location, EntityType entityType, SpawnReason spawnReason,
ConfigurationManager cfg, WorldConfiguration wcfg) {
com.sk89q.worldedit.world.entity.EntityType weEntityType = BukkitAdapter.adapt(entityType);

if (weEntityType != null && wcfg.blockCreatureSpawn.contains(weEntityType)) {
event.setCancelled(true);
return;
}

Location eventLoc = event.getLocation();

if (wcfg.useRegions && cfg.useRegionsCreatureSpawnEvent) {
ApplicableRegionSet set =
WorldGuard.getInstance().getPlatform().getRegionContainer().createQuery().getApplicableRegions(BukkitAdapter.adapt(eventLoc));
WorldGuard.getInstance().getPlatform().getRegionContainer().createQuery().getApplicableRegions(BukkitAdapter.adapt(location));

if (!set.testState(null, Flags.MOB_SPAWNING)) {
event.setCancelled(true);
Expand All @@ -685,8 +687,8 @@ public void onCreatureSpawn(CreatureSpawnEvent event) {
}

if (wcfg.blockGroundSlimes && entityType == EntityType.SLIME
&& eventLoc.getY() >= 60
&& event.getSpawnReason() == SpawnReason.NATURAL) {
&& location.getY() >= 60
&& spawnReason == SpawnReason.NATURAL) {
event.setCancelled(true);
return;
}
Expand Down Expand Up @@ -907,6 +909,53 @@ public void onEntityZap(EntityZapEvent event) {
handlePigZap(event.getEntity(), event);
}
}

/**
* Applies the natural spawn checks from {@link WorldGuardEntityListener#onCreatureSpawn} before the
* server constructs the entity. The CreatureSpawnEvent checks fire at the very
* end of the spawn pipeline, after the position was picked, the placement
* checks ran and the mob was constructed and finalized, so a region that denies
* mob spawning pays for a mob to be built and thrown away on every attempt.
* Cancelling here skips the placement checks, the construction and
* finalizeSpawn for a spawn that was going to be refused anyway.
*
* Cancelling does not end the chunk's remaining attempts. Only
* setShouldAbortSpawn(true) makes the spawner return early; a plain cancel
* falls through to the next candidate position exactly like a failed
* placement check.
*
* Note that Paper fires this event for every candidate position, before the
* light, block and collision checks, so the region query below runs
* considerably more often than the CreatureSpawnEvent one did. On Paper with
* per-player-mob-spawns enabled, which is the default, every cancelled pre
* spawn is also charged to a per player mob backoff counter that is added to
* the mob cap of every player within tick view distance and bleeds off one per
* spawn cycle, so a large denied region can suppress spawning in neighbouring
* chunks that allow mobs.
*
* Only NATURAL spawns are handled here; every other spawn reason keeps going
* through the CreatureSpawnEvent checks unchanged.
*/
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
public void onPreCreatureSpawn(PreCreatureSpawnEvent event) {
if (event.getReason() != SpawnReason.NATURAL) {
return;
}

ConfigurationManager cfg = getConfig();

if (!cfg.useRegionsPreCreatureSpawnEvent) {
return;
}

if (cfg.activityHaltToggle) {
event.setCancelled(true);
return;
}

Location spawnLoc = event.getSpawnLocation();
handleCreatureSpawn(event, spawnLoc, event.getType(), event.getReason(), cfg, getWorldConfig(spawnLoc.getWorld()));
}
}

private static class SpigotListener implements Listener {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ public abstract class ConfigurationManager {
"#\r\n";

public boolean useRegionsCreatureSpawnEvent;
public boolean useRegionsPreCreatureSpawnEvent;
public boolean activityHaltToggle = false;
public boolean useGodPermission;
public boolean useGodGroup;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ public void load() {
migrateRegionsToUuid = config.getBoolean("regions.uuid-migration.perform-on-next-start", true);
keepUnresolvedNames = config.getBoolean("regions.uuid-migration.keep-names-that-lack-uuids", true);
useRegionsCreatureSpawnEvent = config.getBoolean("regions.use-creature-spawn-event", true);
useRegionsPreCreatureSpawnEvent = config.getBoolean("regions.use-pre-creature-spawn-event", true);
disableDefaultBypass = config.getBoolean("regions.disable-bypass-by-default", false);
announceBypassStatus = config.getBoolean("regions.announce-bypass-status", false);

Expand Down
Loading