PaperMC/paper-server/patches/sources/net/minecraft/world/level/NaturalSpawner.java.patch

236 lines
14 KiB
Diff
Raw Normal View History

2024-12-15 00:42:27 +01:00
--- a/net/minecraft/world/level/NaturalSpawner.java
+++ b/net/minecraft/world/level/NaturalSpawner.java
@@ -49,6 +_,13 @@
import net.minecraft.world.phys.Vec3;
import org.slf4j.Logger;
+// CraftBukkit start
+import net.minecraft.world.level.storage.LevelData;
+import org.bukkit.craftbukkit.util.CraftSpawnCategory;
+import org.bukkit.entity.SpawnCategory;
+import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason;
+// CraftBukkit end
+
public final class NaturalSpawner {
private static final Logger LOGGER = LogUtils.getLogger();
private static final int MIN_SPAWN_DISTANCE = 24;
@@ -72,6 +_,13 @@
if (!(entity instanceof Mob mob && (mob.isPersistenceRequired() || mob.requiresCustomPersistence()))) {
MobCategory category = entity.getType().getCategory();
if (category != MobCategory.MISC) {
+ // Paper start - Only count natural spawns
+ if (!entity.level().paperConfig().entities.spawning.countAllMobsForSpawning &&
+ !(entity.spawnReason == org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.NATURAL ||
+ entity.spawnReason == org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.CHUNK_GEN)) {
+ continue;
+ }
+ // Paper end - Only count natural spawns
BlockPos blockPos = entity.blockPosition();
chunkGetter.query(ChunkPos.asLong(blockPos), chunk -> {
MobSpawnSettings.MobSpawnCost mobSpawnCost = getRoughBiome(blockPos, chunk).getMobSettings().getMobSpawnCost(entity.getType());
2024-12-15 07:43:34 +01:00
@@ -96,17 +_,37 @@
2024-12-15 00:42:27 +01:00
return chunk.getNoiseBiome(QuartPos.fromBlock(pos.getX()), QuartPos.fromBlock(pos.getY()), QuartPos.fromBlock(pos.getZ())).value();
}
+ // CraftBukkit start - add server
public static List<MobCategory> getFilteredSpawningCategories(
- NaturalSpawner.SpawnState spawnState, boolean spawnFriendlies, boolean spawnEnemies, boolean spawnPassives
+ NaturalSpawner.SpawnState spawnState, boolean spawnFriendlies, boolean spawnEnemies, boolean spawnPassives, ServerLevel worldserver
) {
+ LevelData worlddata = worldserver.getLevelData(); // CraftBukkit - Other mob type spawn tick rate
+ // CraftBukkit end
List<MobCategory> list = new ArrayList<>(SPAWNING_CATEGORIES.length);
-
- for (MobCategory mobCategory : SPAWNING_CATEGORIES) {
- if ((spawnFriendlies || !mobCategory.isFriendly())
- && (spawnEnemies || mobCategory.isFriendly())
- && (spawnPassives || !mobCategory.isPersistent())
- && spawnState.canSpawnForCategoryGlobal(mobCategory)) {
- list.add(mobCategory);
+ MobCategory[] aenumcreaturetype = NaturalSpawner.SPAWNING_CATEGORIES;
+ int i = aenumcreaturetype.length;
+
+ for (int j = 0; j < i; ++j) {
+ MobCategory enumcreaturetype = SPAWNING_CATEGORIES[j];
+ // CraftBukkit start - Use per-world spawn limits
+ boolean spawnThisTick = true;
+ int limit = enumcreaturetype.getMaxInstancesPerChunk();
+ SpawnCategory spawnCategory = CraftSpawnCategory.toBukkit(enumcreaturetype);
+ if (CraftSpawnCategory.isValidForLimits(spawnCategory)) {
+ spawnThisTick = worldserver.ticksPerSpawnCategory.getLong(spawnCategory) != 0 && worlddata.getGameTime() % worldserver.ticksPerSpawnCategory.getLong(spawnCategory) == 0;
+ limit = worldserver.getWorld().getSpawnLimit(spawnCategory);
+ }
+
+ if (!spawnThisTick || limit == 0) {
+ continue;
+ }
+
+ if ((spawnFriendlies || !enumcreaturetype.isFriendly())
+ && (spawnEnemies || enumcreaturetype.isFriendly())
+ && (spawnPassives || !enumcreaturetype.isPersistent())
2024-12-15 07:43:34 +01:00
+ && spawnState.canSpawnForCategoryGlobal(enumcreaturetype, limit)) { // Paper - Optional per player mob spawns; remove global check, check later during the local one
2024-12-15 00:42:27 +01:00
+ list.add(enumcreaturetype);
2024-12-15 07:43:34 +01:00
+ // CraftBukkit end
2024-12-15 00:42:27 +01:00
}
}
@@ -126,6 +_,16 @@
profilerFiller.pop();
}
+ // Paper start - Add mobcaps commands
+ public static int globalLimitForCategory(final ServerLevel level, final MobCategory category, final int spawnableChunkCount) {
+ final int categoryLimit = level.getWorld().getSpawnLimitUnsafe(CraftSpawnCategory.toBukkit(category));
+ if (categoryLimit < 1) {
+ return categoryLimit;
+ }
+ return categoryLimit * spawnableChunkCount / NaturalSpawner.MAGIC_NUMBER;
+ }
+ // Paper end - Add mobcaps commands
+
public static void spawnCategoryForChunk(
MobCategory category, ServerLevel level, LevelChunk chunk, NaturalSpawner.SpawnPredicate filter, NaturalSpawner.AfterSpawnCallback callback
) {
@@ -151,8 +_,8 @@
StructureManager structureManager = level.structureManager();
ChunkGenerator generator = level.getChunkSource().getGenerator();
int y = pos.getY();
- BlockState blockState = chunk.getBlockState(pos);
- if (!blockState.isRedstoneConductor(chunk, pos)) {
2024-12-15 07:43:34 +01:00
+ BlockState blockState = level.getBlockStateIfLoadedAndInBounds(pos); // Paper - don't load chunks for mob spawn
2024-12-15 00:42:27 +01:00
+ if (blockState != null && !blockState.isRedstoneConductor(chunk, pos)) { // Paper - don't load chunks for mob spawn
BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
int i = 0;
@@ -174,7 +_,7 @@
Player nearestPlayer = level.getNearestPlayer(d, y, d1, -1.0, false);
if (nearestPlayer != null) {
double d2 = nearestPlayer.distanceToSqr(d, y, d1);
- if (isRightDistanceToPlayerAndSpawnPoint(level, chunk, mutableBlockPos, d2)) {
2024-12-18 19:09:46 +01:00
+ if (level.isLoadedAndInBounds(mutableBlockPos) && isRightDistanceToPlayerAndSpawnPoint(level, chunk, mutableBlockPos, d2)) { // Paper - don't load chunks for mob spawn
2024-12-15 00:42:27 +01:00
if (spawnerData == null) {
Optional<MobSpawnSettings.SpawnerData> randomSpawnMobAt = getRandomSpawnMobAt(
level, structureManager, generator, category, level.random, mutableBlockPos
@@ -187,8 +_,13 @@
ceil = spawnerData.minCount + level.random.nextInt(1 + spawnerData.maxCount - spawnerData.minCount);
}
- if (isValidSpawnPostitionForType(level, category, structureManager, generator, spawnerData, mutableBlockPos, d2)
- && filter.test(spawnerData.type, mutableBlockPos, chunk)) {
+ // Paper start - PreCreatureSpawnEvent
+ PreSpawnStatus doSpawning = isValidSpawnPostitionForType(level, category, structureManager, generator, spawnerData, mutableBlockPos, d2);
+ if (doSpawning == PreSpawnStatus.ABORT) {
+ return;
+ }
+ if (doSpawning == PreSpawnStatus.SUCCESS && filter.test(spawnerData.type, mutableBlockPos, chunk)) {
+ // Paper end - PreCreatureSpawnEvent
Mob mobForSpawn = getMobForSpawn(level, spawnerData.type);
if (mobForSpawn == null) {
return;
@@ -199,10 +_,15 @@
spawnGroupData = mobForSpawn.finalizeSpawn(
level, level.getCurrentDifficultyAt(mobForSpawn.blockPosition()), EntitySpawnReason.NATURAL, spawnGroupData
);
- i++;
- i3++;
- level.addFreshEntityWithPassengers(mobForSpawn);
- callback.run(mobForSpawn, chunk);
+ // CraftBukkit start
+ // SPIGOT-7045: Give ocelot babies back their special spawn reason. Note: This is the only modification required as ocelots count as monsters which means they only spawn during normal chunk ticking and do not spawn during chunk generation as starter mobs.
+ level.addFreshEntityWithPassengers(mobForSpawn, (mobForSpawn instanceof net.minecraft.world.entity.animal.Ocelot && !((org.bukkit.entity.Ageable) mobForSpawn.getBukkitEntity()).isAdult()) ? SpawnReason.OCELOT_BABY : SpawnReason.NATURAL);
+ if (!mobForSpawn.isRemoved()) {
+ ++i;
+ ++i3;
+ callback.run(mobForSpawn, chunk);
+ }
+ // CraftBukkit end
if (i >= mobForSpawn.getMaxSpawnClusterSize()) {
return;
}
@@ -225,7 +_,15 @@
&& (Objects.equals(new ChunkPos(pos), chunk.getPos()) || level.isNaturalSpawningAllowed(pos));
}
- private static boolean isValidSpawnPostitionForType(
+ // Paper start - PreCreatureSpawnEvent
+ private enum PreSpawnStatus {
+ FAIL,
+ SUCCESS,
+ CANCELLED,
+ ABORT
+ }
+ private static PreSpawnStatus isValidSpawnPostitionForType(
+ // Paper end - PreCreatureSpawnEvent
ServerLevel level,
MobCategory category,
StructureManager structureManager,
@@ -235,16 +_,20 @@
double distance
) {
EntityType<?> entityType = data.type;
- return entityType.getCategory() != MobCategory.MISC
- && (
- entityType.canSpawnFarFromPlayer()
- || !(distance > entityType.getCategory().getDespawnDistance() * entityType.getCategory().getDespawnDistance())
- )
- && entityType.canSummon()
- && canSpawnMobAt(level, structureManager, generator, category, data, pos)
- && SpawnPlacements.isSpawnPositionOk(entityType, level, pos)
- && SpawnPlacements.checkSpawnRules(entityType, level, EntitySpawnReason.NATURAL, pos, level.random)
- && level.noCollision(entityType.getSpawnAABB(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5));
+
+ // Paper start - PreCreatureSpawnEvent
+ com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent event = new com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent(
+ io.papermc.paper.util.MCUtil.toLocation(level, pos),
+ org.bukkit.craftbukkit.entity.CraftEntityType.minecraftToBukkit(entityType), SpawnReason.NATURAL
+ );
+ if (!event.callEvent()) {
+ if (event.shouldAbortSpawn()) {
+ return PreSpawnStatus.ABORT;
+ }
+ return PreSpawnStatus.CANCELLED;
+ }
+ // Paper end - PreCreatureSpawnEvent
+ return entityType.getCategory() == MobCategory.MISC ? PreSpawnStatus.FAIL : (!entityType.canSpawnFarFromPlayer() && distance > (double) (entityType.getCategory().getDespawnDistance() * entityType.getCategory().getDespawnDistance()) ? PreSpawnStatus.FAIL : (entityType.canSummon() && NaturalSpawner.canSpawnMobAt(level, structureManager, generator, category, data, pos) ? (!SpawnPlacements.isSpawnPositionOk(entityType, level, pos) ? PreSpawnStatus.FAIL : (!SpawnPlacements.checkSpawnRules(entityType, level, EntitySpawnReason.NATURAL, pos, level.random) ? PreSpawnStatus.FAIL : level.noCollision(entityType.getSpawnAABB((double) pos.getX() + 0.5D, (double) pos.getY(), (double) pos.getZ() + 0.5D)) ? PreSpawnStatus.SUCCESS : PreSpawnStatus.FAIL)) : PreSpawnStatus.FAIL)); // Paper - PreCreatureSpawnEvent
}
@Nullable
@@ -258,6 +_,7 @@
LOGGER.warn("Can't spawn entity of type: {}", BuiltInRegistries.ENTITY_TYPE.getKey(entityType));
} catch (Exception var4) {
LOGGER.warn("Failed to create mob", (Throwable)var4);
+ com.destroystokyo.paper.exception.ServerInternalException.reportInternalException(var4); // Paper - ServerExceptionEvent
}
return null;
@@ -364,6 +_,7 @@
entity = spawnerData.type.create(levelAccessor.getLevel(), EntitySpawnReason.NATURAL);
} catch (Exception var27) {
LOGGER.warn("Failed to create mob", (Throwable)var27);
+ com.destroystokyo.paper.exception.ServerInternalException.reportInternalException(var27); // Paper - ServerExceptionEvent
continue;
}
@@ -381,7 +_,7 @@
EntitySpawnReason.CHUNK_GENERATION,
spawnGroupData
);
- levelAccessor.addFreshEntityWithPassengers(mob);
+ levelAccessor.addFreshEntityWithPassengers(mob, SpawnReason.CHUNK_GEN); // CraftBukkit
flag = true;
}
}
@@ -501,8 +_,10 @@
return this.unmodifiableMobCategoryCounts;
}
- boolean canSpawnForCategoryGlobal(MobCategory category) {
- int i = category.getMaxInstancesPerChunk() * this.spawnableChunkCount / NaturalSpawner.MAGIC_NUMBER;
+ // CraftBukkit start
+ boolean canSpawnForCategoryGlobal(MobCategory category, int limit) {
+ int i = limit * this.spawnableChunkCount / NaturalSpawner.MAGIC_NUMBER;
+ // CraftBukkit end
return this.mobCategoryCounts.getInt(category) < i;
}