mirror of
https://github.com/PaperMC/Paper.git
synced 2025-01-05 02:22:12 +01:00
b67ec825d2
Add commands to get the mobcaps for a world, as well as the mobcaps for each player when per-player mob spawning is enabled. Also has a hover text on each mob category listing what entity types are in said category
212 lines
15 KiB
Diff
212 lines
15 KiB
Diff
--- a/net/minecraft/world/level/NaturalSpawner.java
|
|
+++ b/net/minecraft/world/level/NaturalSpawner.java
|
|
@@ -47,8 +47,13 @@
|
|
import net.minecraft.world.level.levelgen.structure.Structure;
|
|
import net.minecraft.world.level.levelgen.structure.structures.NetherFortressStructure;
|
|
import net.minecraft.world.level.material.FluidState;
|
|
+import net.minecraft.world.level.storage.LevelData;
|
|
import net.minecraft.world.phys.Vec3;
|
|
import org.slf4j.Logger;
|
|
+import org.bukkit.craftbukkit.util.CraftSpawnCategory;
|
|
+import org.bukkit.entity.SpawnCategory;
|
|
+import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason;
|
|
+// CraftBukkit end
|
|
|
|
public final class NaturalSpawner {
|
|
|
|
@@ -82,6 +87,13 @@
|
|
MobCategory enumcreaturetype = entity.getType().getCategory();
|
|
|
|
if (enumcreaturetype != 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 blockposition = entity.blockPosition();
|
|
|
|
chunkSource.query(ChunkPos.asLong(blockposition), (chunk) -> {
|
|
@@ -107,15 +119,31 @@
|
|
return (Biome) chunk.getNoiseBiome(QuartPos.fromBlock(pos.getX()), QuartPos.fromBlock(pos.getY()), QuartPos.fromBlock(pos.getZ())).value();
|
|
}
|
|
|
|
- public static List<MobCategory> getFilteredSpawningCategories(NaturalSpawner.SpawnState info, boolean spawnAnimals, boolean spawnMonsters, boolean rare) {
|
|
+ // CraftBukkit start - add server
|
|
+ public static List<MobCategory> getFilteredSpawningCategories(NaturalSpawner.SpawnState spawnercreature_d, boolean flag, boolean flag1, boolean flag2, ServerLevel worldserver) {
|
|
+ LevelData worlddata = worldserver.getLevelData(); // CraftBukkit - Other mob type spawn tick rate
|
|
+ // CraftBukkit end
|
|
List<MobCategory> list = new ArrayList(NaturalSpawner.SPAWNING_CATEGORIES.length);
|
|
MobCategory[] aenumcreaturetype = NaturalSpawner.SPAWNING_CATEGORIES;
|
|
int i = aenumcreaturetype.length;
|
|
|
|
for (int j = 0; j < i; ++j) {
|
|
MobCategory enumcreaturetype = aenumcreaturetype[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 ((spawnAnimals || !enumcreaturetype.isFriendly()) && (spawnMonsters || enumcreaturetype.isFriendly()) && (rare || !enumcreaturetype.isPersistent()) && info.canSpawnForCategoryGlobal(enumcreaturetype)) {
|
|
+ if (!spawnThisTick || limit == 0) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ if ((flag || !enumcreaturetype.isFriendly()) && (flag1 || enumcreaturetype.isFriendly()) && (flag2 || !enumcreaturetype.isPersistent()) && spawnercreature_d.canSpawnForCategoryGlobal(enumcreaturetype, limit)) {
|
|
+ // CraftBukkit end
|
|
list.add(enumcreaturetype);
|
|
}
|
|
}
|
|
@@ -144,6 +172,16 @@
|
|
gameprofilerfiller.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 group, ServerLevel world, LevelChunk chunk, NaturalSpawner.SpawnPredicate checker, NaturalSpawner.AfterSpawnCallback runner) {
|
|
BlockPos blockposition = NaturalSpawner.getRandomPosWithin(world, chunk);
|
|
|
|
@@ -164,9 +202,9 @@
|
|
StructureManager structuremanager = world.structureManager();
|
|
ChunkGenerator chunkgenerator = world.getChunkSource().getGenerator();
|
|
int i = pos.getY();
|
|
- BlockState iblockdata = chunk.getBlockState(pos);
|
|
+ BlockState iblockdata = world.getBlockStateIfLoadedAndInBounds(pos); // Paper - don't load chunks for mob spawn
|
|
|
|
- if (!iblockdata.isRedstoneConductor(chunk, pos)) {
|
|
+ if (iblockdata != null && !iblockdata.isRedstoneConductor(chunk, pos)) { // Paper - don't load chunks for mob spawn
|
|
BlockPos.MutableBlockPos blockposition_mutableblockposition = new BlockPos.MutableBlockPos();
|
|
int j = 0;
|
|
int k = 0;
|
|
@@ -195,7 +233,7 @@
|
|
if (entityhuman != null) {
|
|
double d2 = entityhuman.distanceToSqr(d0, (double) i, d1);
|
|
|
|
- if (NaturalSpawner.isRightDistanceToPlayerAndSpawnPoint(world, chunk, blockposition_mutableblockposition, d2)) {
|
|
+ if (world.isLoadedAndInBounds(blockposition_mutableblockposition) && NaturalSpawner.isRightDistanceToPlayerAndSpawnPoint(world, chunk, blockposition_mutableblockposition, d2)) { // Paper - don't load chunks for mob spawn
|
|
if (biomesettingsmobs_c == null) {
|
|
Optional<MobSpawnSettings.SpawnerData> optional = NaturalSpawner.getRandomSpawnMobAt(world, structuremanager, chunkgenerator, group, world.random, blockposition_mutableblockposition);
|
|
|
|
@@ -207,7 +245,13 @@
|
|
j1 = biomesettingsmobs_c.minCount + world.random.nextInt(1 + biomesettingsmobs_c.maxCount - biomesettingsmobs_c.minCount);
|
|
}
|
|
|
|
- if (NaturalSpawner.isValidSpawnPostitionForType(world, group, structuremanager, chunkgenerator, biomesettingsmobs_c, blockposition_mutableblockposition, d2) && checker.test(biomesettingsmobs_c.type, blockposition_mutableblockposition, chunk)) {
|
|
+ // Paper start - PreCreatureSpawnEvent
|
|
+ PreSpawnStatus doSpawning = isValidSpawnPostitionForType(world, group, structuremanager, chunkgenerator, biomesettingsmobs_c, blockposition_mutableblockposition, d2);
|
|
+ if (doSpawning == PreSpawnStatus.ABORT) {
|
|
+ return;
|
|
+ }
|
|
+ if (doSpawning == PreSpawnStatus.SUCCESS && checker.test(biomesettingsmobs_c.type, blockposition_mutableblockposition, chunk)) {
|
|
+ // Paper end - PreCreatureSpawnEvent
|
|
Mob entityinsentient = NaturalSpawner.getMobForSpawn(world, biomesettingsmobs_c.type);
|
|
|
|
if (entityinsentient == null) {
|
|
@@ -217,10 +261,15 @@
|
|
entityinsentient.moveTo(d0, (double) i, d1, world.random.nextFloat() * 360.0F, 0.0F);
|
|
if (NaturalSpawner.isValidPositionForMob(world, entityinsentient, d2)) {
|
|
groupdataentity = entityinsentient.finalizeSpawn(world, world.getCurrentDifficultyAt(entityinsentient.blockPosition()), EntitySpawnReason.NATURAL, groupdataentity);
|
|
- ++j;
|
|
- ++k1;
|
|
- world.addFreshEntityWithPassengers(entityinsentient);
|
|
- runner.run(entityinsentient, 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.
|
|
+ world.addFreshEntityWithPassengers(entityinsentient, (entityinsentient instanceof net.minecraft.world.entity.animal.Ocelot && !((org.bukkit.entity.Ageable) entityinsentient.getBukkitEntity()).isAdult()) ? SpawnReason.OCELOT_BABY : SpawnReason.NATURAL);
|
|
+ if (!entityinsentient.isRemoved()) {
|
|
+ ++j;
|
|
+ ++k1;
|
|
+ runner.run(entityinsentient, chunk);
|
|
+ }
|
|
+ // CraftBukkit end
|
|
if (j >= entityinsentient.getMaxSpawnClusterSize()) {
|
|
return;
|
|
}
|
|
@@ -250,10 +299,31 @@
|
|
return squaredDistance <= 576.0D ? false : (world.getSharedSpawnPos().closerToCenterThan(new Vec3((double) pos.getX() + 0.5D, (double) pos.getY(), (double) pos.getZ() + 0.5D), 24.0D) ? false : Objects.equals(new ChunkPos(pos), chunk.getPos()) || world.isNaturalSpawningAllowed((BlockPos) pos));
|
|
}
|
|
|
|
- private static boolean isValidSpawnPostitionForType(ServerLevel world, MobCategory group, StructureManager structureAccessor, ChunkGenerator chunkGenerator, MobSpawnSettings.SpawnerData spawnEntry, BlockPos.MutableBlockPos pos, double squaredDistance) {
|
|
+ // Paper start - PreCreatureSpawnEvent
|
|
+ private enum PreSpawnStatus {
|
|
+ FAIL,
|
|
+ SUCCESS,
|
|
+ CANCELLED,
|
|
+ ABORT
|
|
+ }
|
|
+ private static PreSpawnStatus isValidSpawnPostitionForType(ServerLevel world, MobCategory group, StructureManager structureAccessor, ChunkGenerator chunkGenerator, MobSpawnSettings.SpawnerData spawnEntry, BlockPos.MutableBlockPos pos, double squaredDistance) {
|
|
+ // Paper end - PreCreatureSpawnEvent
|
|
EntityType<?> entitytypes = spawnEntry.type;
|
|
|
|
- return entitytypes.getCategory() == MobCategory.MISC ? false : (!entitytypes.canSpawnFarFromPlayer() && squaredDistance > (double) (entitytypes.getCategory().getDespawnDistance() * entitytypes.getCategory().getDespawnDistance()) ? false : (entitytypes.canSummon() && NaturalSpawner.canSpawnMobAt(world, structureAccessor, chunkGenerator, group, spawnEntry, pos) ? (!SpawnPlacements.isSpawnPositionOk(entitytypes, world, pos) ? false : (!SpawnPlacements.checkSpawnRules(entitytypes, world, EntitySpawnReason.NATURAL, pos, world.random) ? false : world.noCollision(entitytypes.getSpawnAABB((double) pos.getX() + 0.5D, (double) pos.getY(), (double) pos.getZ() + 0.5D)))) : false));
|
|
+ // Paper start - PreCreatureSpawnEvent
|
|
+ com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent event = new com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent(
|
|
+ io.papermc.paper.util.MCUtil.toLocation(world, pos),
|
|
+ org.bukkit.craftbukkit.entity.CraftEntityType.minecraftToBukkit(entitytypes), SpawnReason.NATURAL
|
|
+ );
|
|
+ if (!event.callEvent()) {
|
|
+ if (event.shouldAbortSpawn()) {
|
|
+ return PreSpawnStatus.ABORT;
|
|
+ }
|
|
+ return PreSpawnStatus.CANCELLED;
|
|
+ }
|
|
+ // Paper end - PreCreatureSpawnEvent
|
|
+
|
|
+ return entitytypes.getCategory() == MobCategory.MISC ? PreSpawnStatus.FAIL : (!entitytypes.canSpawnFarFromPlayer() && squaredDistance > (double) (entitytypes.getCategory().getDespawnDistance() * entitytypes.getCategory().getDespawnDistance()) ? PreSpawnStatus.FAIL : (entitytypes.canSummon() && NaturalSpawner.canSpawnMobAt(world, structureAccessor, chunkGenerator, group, spawnEntry, pos) ? (!SpawnPlacements.isSpawnPositionOk(entitytypes, world, pos) ? PreSpawnStatus.FAIL : (!SpawnPlacements.checkSpawnRules(entitytypes, world, EntitySpawnReason.NATURAL, pos, world.random) ? PreSpawnStatus.FAIL : world.noCollision(entitytypes.getSpawnAABB((double) pos.getX() + 0.5D, (double) pos.getY(), (double) pos.getZ() + 0.5D)) ? PreSpawnStatus.SUCCESS : PreSpawnStatus.FAIL)) : PreSpawnStatus.FAIL)); // Paper - PreCreatureSpawnEvent
|
|
}
|
|
|
|
@Nullable
|
|
@@ -268,6 +338,7 @@
|
|
NaturalSpawner.LOGGER.warn("Can't spawn entity of type: {}", BuiltInRegistries.ENTITY_TYPE.getKey(type));
|
|
} catch (Exception exception) {
|
|
NaturalSpawner.LOGGER.warn("Failed to create mob", exception);
|
|
+ com.destroystokyo.paper.exception.ServerInternalException.reportInternalException(exception); // Paper - ServerExceptionEvent
|
|
}
|
|
|
|
return null;
|
|
@@ -356,6 +427,7 @@
|
|
entity = biomesettingsmobs_c.type.create(world.getLevel(), EntitySpawnReason.NATURAL);
|
|
} catch (Exception exception) {
|
|
NaturalSpawner.LOGGER.warn("Failed to create mob", exception);
|
|
+ com.destroystokyo.paper.exception.ServerInternalException.reportInternalException(exception); // Paper - ServerExceptionEvent
|
|
continue;
|
|
}
|
|
|
|
@@ -369,7 +441,7 @@
|
|
|
|
if (entityinsentient.checkSpawnRules(world, EntitySpawnReason.CHUNK_GENERATION) && entityinsentient.checkSpawnObstruction(world)) {
|
|
groupdataentity = entityinsentient.finalizeSpawn(world, world.getCurrentDifficultyAt(entityinsentient.blockPosition()), EntitySpawnReason.CHUNK_GENERATION, groupdataentity);
|
|
- world.addFreshEntityWithPassengers(entityinsentient);
|
|
+ world.addFreshEntityWithPassengers(entityinsentient, SpawnReason.CHUNK_GEN); // CraftBukkit
|
|
flag = true;
|
|
}
|
|
}
|
|
@@ -482,10 +554,12 @@
|
|
return this.unmodifiableMobCategoryCounts;
|
|
}
|
|
|
|
- boolean canSpawnForCategoryGlobal(MobCategory group) {
|
|
- int i = group.getMaxInstancesPerChunk() * this.spawnableChunkCount / NaturalSpawner.MAGIC_NUMBER;
|
|
+ // CraftBukkit start
|
|
+ boolean canSpawnForCategoryGlobal(MobCategory enumcreaturetype, int limit) {
|
|
+ int i = limit * this.spawnableChunkCount / NaturalSpawner.MAGIC_NUMBER;
|
|
+ // CraftBukkit end
|
|
|
|
- return this.mobCategoryCounts.getInt(group) < i;
|
|
+ return this.mobCategoryCounts.getInt(enumcreaturetype) < i;
|
|
}
|
|
|
|
boolean canSpawnForCategoryLocal(MobCategory group, ChunkPos chunkPos) {
|