diff options
Diffstat (limited to 'patch-remap/mache-vineflower/net/minecraft/world/level/NaturalSpawner.java.patch')
-rw-r--r-- | patch-remap/mache-vineflower/net/minecraft/world/level/NaturalSpawner.java.patch | 826 |
1 files changed, 826 insertions, 0 deletions
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/level/NaturalSpawner.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/level/NaturalSpawner.java.patch new file mode 100644 index 0000000000..ef2620e36c --- /dev/null +++ b/patch-remap/mache-vineflower/net/minecraft/world/level/NaturalSpawner.java.patch @@ -0,0 +1,826 @@ +--- a/net/minecraft/world/level/NaturalSpawner.java ++++ b/net/minecraft/world/level/NaturalSpawner.java +@@ -4,6 +4,7 @@ + import it.unimi.dsi.fastutil.objects.Object2IntMap; + import it.unimi.dsi.fastutil.objects.Object2IntMaps; + import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; ++import java.util.Iterator; + import java.util.Objects; + import java.util.Optional; + import java.util.function.Consumer; +@@ -15,6 +16,7 @@ + import net.minecraft.core.QuartPos; + import net.minecraft.core.registries.BuiltInRegistries; + import net.minecraft.core.registries.Registries; ++import net.minecraft.nbt.CompoundTag; + import net.minecraft.server.level.ServerLevel; + import net.minecraft.tags.BiomeTags; + import net.minecraft.tags.BlockTags; +@@ -25,16 +27,16 @@ + import net.minecraft.util.random.WeightedRandomList; + import net.minecraft.world.entity.Entity; + import net.minecraft.world.entity.EntityType; ++import net.minecraft.world.entity.EnumMobSpawn; ++import net.minecraft.world.entity.GroupDataEntity; + import net.minecraft.world.entity.Mob; + import net.minecraft.world.entity.MobCategory; +-import net.minecraft.world.entity.MobSpawnType; +-import net.minecraft.world.entity.SpawnGroupData; + import net.minecraft.world.entity.SpawnPlacements; + import net.minecraft.world.entity.player.Player; + import net.minecraft.world.level.biome.Biome; + import net.minecraft.world.level.biome.MobSpawnSettings; + import net.minecraft.world.level.block.Blocks; +-import net.minecraft.world.level.block.state.BlockState; ++import net.minecraft.world.level.block.state.IBlockData; + import net.minecraft.world.level.chunk.ChunkAccess; + import net.minecraft.world.level.chunk.ChunkGenerator; + import net.minecraft.world.level.chunk.LevelChunk; +@@ -43,196 +45,231 @@ + 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.pathfinder.PathComputationType; ++import net.minecraft.world.level.pathfinder.PathMode; ++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 { ++ + private static final Logger LOGGER = LogUtils.getLogger(); + private static final int MIN_SPAWN_DISTANCE = 24; + public static final int SPAWN_DISTANCE_CHUNK = 8; + public static final int SPAWN_DISTANCE_BLOCK = 128; +- static final int MAGIC_NUMBER = (int)Math.pow(17.0, 2.0); +- private static final MobCategory[] SPAWNING_CATEGORIES = Stream.of(MobCategory.values()) +- .filter(category -> category != MobCategory.MISC) +- .toArray(MobCategory[]::new); ++ static final int MAGIC_NUMBER = (int) Math.pow(17.0D, 2.0D); ++ private static final MobCategory[] SPAWNING_CATEGORIES = (MobCategory[]) Stream.of(MobCategory.values()).filter((enumcreaturetype) -> { ++ return enumcreaturetype != MobCategory.MISC; ++ }).toArray((i) -> { ++ return new MobCategory[i]; ++ }); + +- private NaturalSpawner() { +- } ++ private NaturalSpawner() {} + +- public static NaturalSpawner.SpawnState createState( +- int spawnableChunkCount, Iterable<Entity> entities, NaturalSpawner.ChunkGetter chunkGetter, LocalMobCapCalculator calculator +- ) { +- PotentialCalculator potentialCalculator = new PotentialCalculator(); +- Object2IntOpenHashMap<MobCategory> map = new Object2IntOpenHashMap<>(); ++ public static NaturalSpawner.SpawnState createState(int spawnableChunkCount, Iterable<Entity> entities, NaturalSpawner.ChunkGetter chunkGetter, LocalMobCapCalculator calculator) { ++ PotentialCalculator spawnercreatureprobabilities = new PotentialCalculator(); ++ Object2IntOpenHashMap<MobCategory> object2intopenhashmap = new Object2IntOpenHashMap(); ++ Iterator iterator = entities.iterator(); + +- for (Entity entity : entities) { +- if (entity instanceof Mob mob && (mob.isPersistenceRequired() || mob.requiresCustomPersistence())) { +- continue; ++ while (iterator.hasNext()) { ++ Entity entity = (Entity) iterator.next(); ++ ++ if (entity instanceof Mob) { ++ Mob entityinsentient = (Mob) entity; ++ ++ if (entityinsentient.isPersistenceRequired() || entityinsentient.requiresCustomPersistence()) { ++ continue; ++ } + } + +- MobCategory category = entity.getType().getCategory(); +- if (category != MobCategory.MISC) { +- BlockPos blockPos = entity.blockPosition(); +- chunkGetter.query(ChunkPos.asLong(blockPos), chunk -> { +- MobSpawnSettings.MobSpawnCost mobSpawnCost = getRoughBiome(blockPos, chunk).getMobSettings().getMobSpawnCost(entity.getType()); +- if (mobSpawnCost != null) { +- potentialCalculator.addCharge(entity.blockPosition(), mobSpawnCost.charge()); ++ MobCategory enumcreaturetype = entity.getType().getCategory(); ++ ++ if (enumcreaturetype != MobCategory.MISC) { ++ BlockPos blockposition = entity.blockPosition(); ++ ++ chunkGetter.query(ChunkPos.asLong(blockposition), (chunk) -> { ++ MobSpawnSettings.MobSpawnCost biomesettingsmobs_b = getRoughBiome(blockposition, chunk).getMobSettings().getMobSpawnCost(entity.getType()); ++ ++ if (biomesettingsmobs_b != null) { ++ spawnercreatureprobabilities.addCharge(entity.blockPosition(), biomesettingsmobs_b.charge()); + } + + if (entity instanceof Mob) { +- calculator.addMob(chunk.getPos(), category); ++ calculator.addMob(chunk.getPos(), enumcreaturetype); + } + +- map.addTo(category, 1); ++ object2intopenhashmap.addTo(enumcreaturetype, 1); + }); + } + } + +- return new NaturalSpawner.SpawnState(spawnableChunkCount, map, potentialCalculator, calculator); ++ return new NaturalSpawner.SpawnState(spawnableChunkCount, object2intopenhashmap, spawnercreatureprobabilities, calculator); + } + + static Biome getRoughBiome(BlockPos pos, ChunkAccess chunk) { +- return chunk.getNoiseBiome(QuartPos.fromBlock(pos.getX()), QuartPos.fromBlock(pos.getY()), QuartPos.fromBlock(pos.getZ())).value(); ++ return (Biome) chunk.getNoiseBiome(QuartPos.fromBlock(pos.getX()), QuartPos.fromBlock(pos.getY()), QuartPos.fromBlock(pos.getZ())).value(); + } + +- public static void spawnForChunk( +- ServerLevel level, LevelChunk chunk, NaturalSpawner.SpawnState spawnState, boolean spawnFriendlies, boolean spawnMonsters, boolean forcedDespawn +- ) { ++ public static void spawnForChunk(ServerLevel level, LevelChunk chunk, NaturalSpawner.SpawnState spawnState, boolean spawnFriendlies, boolean spawnMonsters, boolean forcedDespawn) { + level.getProfiler().push("spawner"); ++ MobCategory[] aenumcreaturetype = NaturalSpawner.SPAWNING_CATEGORIES; ++ int i = aenumcreaturetype.length; + +- for (MobCategory mobCategory : SPAWNING_CATEGORIES) { +- if ((spawnFriendlies || !mobCategory.isFriendly()) +- && (spawnMonsters || mobCategory.isFriendly()) +- && (forcedDespawn || !mobCategory.isPersistent()) +- && spawnState.canSpawnForCategory(mobCategory, chunk.getPos())) { +- spawnCategoryForChunk(mobCategory, level, chunk, spawnState::canSpawn, spawnState::afterSpawn); ++ LevelData worlddata = level.getLevelData(); // CraftBukkit - Other mob type spawn tick rate ++ ++ 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 = level.ticksPerSpawnCategory.getLong(spawnCategory) != 0 && worlddata.getGameTime() % level.ticksPerSpawnCategory.getLong(spawnCategory) == 0; ++ limit = level.getWorld().getSpawnLimit(spawnCategory); + } ++ ++ if (!spawnThisTick || limit == 0) { ++ continue; ++ } ++ ++ if ((spawnFriendlies || !enumcreaturetype.isFriendly()) && (spawnMonsters || enumcreaturetype.isFriendly()) && (forcedDespawn || !enumcreaturetype.isPersistent()) && spawnState.canSpawnForCategory(enumcreaturetype, chunk.getPos(), limit)) { ++ // CraftBukkit end ++ Objects.requireNonNull(spawnState); ++ NaturalSpawner.SpawnPredicate spawnercreature_c = spawnState::canSpawn; ++ ++ Objects.requireNonNull(spawnState); ++ spawnCategoryForChunk(enumcreaturetype, level, chunk, spawnercreature_c, spawnState::afterSpawn); ++ } + } + + level.getProfiler().pop(); + } + +- public static void spawnCategoryForChunk( +- MobCategory category, ServerLevel level, LevelChunk chunk, NaturalSpawner.SpawnPredicate filter, NaturalSpawner.AfterSpawnCallback callback +- ) { +- BlockPos randomPosWithin = getRandomPosWithin(level, chunk); +- if (randomPosWithin.getY() >= level.getMinBuildHeight() + 1) { +- spawnCategoryForPosition(category, level, chunk, randomPosWithin, filter, callback); ++ public static void spawnCategoryForChunk(MobCategory category, ServerLevel level, LevelChunk chunk, NaturalSpawner.SpawnPredicate filter, NaturalSpawner.AfterSpawnCallback callback) { ++ BlockPos blockposition = getRandomPosWithin(level, chunk); ++ ++ if (blockposition.getY() >= level.getMinBuildHeight() + 1) { ++ spawnCategoryForPosition(category, level, chunk, blockposition, filter, callback); + } + } + + @VisibleForDebug + public static void spawnCategoryForPosition(MobCategory category, ServerLevel level, BlockPos pos) { +- spawnCategoryForPosition(category, level, level.getChunk(pos), pos, (entityType, spawnPos, chunk) -> true, (mob, chunk) -> { ++ spawnCategoryForPosition(category, level, level.getChunk(pos), pos, (entitytypes, blockposition1, ichunkaccess) -> { ++ return true; ++ }, (entityinsentient, ichunkaccess) -> { + }); + } + +- public static void spawnCategoryForPosition( +- MobCategory category, +- ServerLevel level, +- ChunkAccess chunk, +- BlockPos pos, +- NaturalSpawner.SpawnPredicate filter, +- NaturalSpawner.AfterSpawnCallback callback +- ) { +- StructureManager structureManager = level.structureManager(); +- ChunkGenerator generator = level.getChunkSource().getGenerator(); +- int y = pos.getY(); +- BlockState blockState = chunk.getBlockState(pos); +- if (!blockState.isRedstoneConductor(chunk, pos)) { +- BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(); +- int i = 0; ++ public static void spawnCategoryForPosition(MobCategory category, ServerLevel level, ChunkAccess chunk, BlockPos pos, NaturalSpawner.SpawnPredicate filter, NaturalSpawner.AfterSpawnCallback callback) { ++ StructureManager structuremanager = level.structureManager(); ++ ChunkGenerator chunkgenerator = level.getChunkSource().getGenerator(); ++ int i = pos.getY(); ++ IBlockData iblockdata = chunk.getBlockState(pos); + +- for (int i1 = 0; i1 < 3; i1++) { +- int x = pos.getX(); +- int z = pos.getZ(); +- int i2 = 6; +- MobSpawnSettings.SpawnerData spawnerData = null; +- SpawnGroupData spawnGroupData = null; +- int ceil = Mth.ceil(level.random.nextFloat() * 4.0F); +- int i3 = 0; ++ if (!iblockdata.isRedstoneConductor(chunk, pos)) { ++ BlockPos.MutableBlockPos blockposition_mutableblockposition = new BlockPos.MutableBlockPos(); ++ int j = 0; ++ int k = 0; + +- for (int i4 = 0; i4 < ceil; i4++) { +- x += level.random.nextInt(6) - level.random.nextInt(6); +- z += level.random.nextInt(6) - level.random.nextInt(6); +- mutableBlockPos.set(x, y, z); +- double d = (double)x + 0.5; +- double d1 = (double)z + 0.5; +- Player nearestPlayer = level.getNearestPlayer(d, (double)y, d1, -1.0, false); +- if (nearestPlayer != null) { +- double d2 = nearestPlayer.distanceToSqr(d, (double)y, d1); +- if (isRightDistanceToPlayerAndSpawnPoint(level, chunk, mutableBlockPos, d2)) { +- if (spawnerData == null) { +- Optional<MobSpawnSettings.SpawnerData> randomSpawnMobAt = getRandomSpawnMobAt( +- level, structureManager, generator, category, level.random, mutableBlockPos +- ); +- if (randomSpawnMobAt.isEmpty()) { +- break; +- } ++ while (k < 3) { ++ int l = pos.getX(); ++ int i1 = pos.getZ(); ++ boolean flag = true; ++ MobSpawnSettings.SpawnerData biomesettingsmobs_c = null; ++ GroupDataEntity groupdataentity = null; ++ int j1 = Mth.ceil(level.random.nextFloat() * 4.0F); ++ int k1 = 0; ++ int l1 = 0; + +- spawnerData = randomSpawnMobAt.get(); +- ceil = spawnerData.minCount + level.random.nextInt(1 + spawnerData.maxCount - spawnerData.minCount); +- } ++ while (true) { ++ if (l1 < j1) { ++ label53: ++ { ++ l += level.random.nextInt(6) - level.random.nextInt(6); ++ i1 += level.random.nextInt(6) - level.random.nextInt(6); ++ blockposition_mutableblockposition.set(l, i, i1); ++ double d0 = (double) l + 0.5D; ++ double d1 = (double) i1 + 0.5D; ++ Player entityhuman = level.getNearestPlayer(d0, (double) i, d1, -1.0D, false); + +- if (isValidSpawnPostitionForType(level, category, structureManager, generator, spawnerData, mutableBlockPos, d2) +- && filter.test(spawnerData.type, mutableBlockPos, chunk)) { +- Mob mobForSpawn = getMobForSpawn(level, spawnerData.type); +- if (mobForSpawn == null) { +- return; +- } ++ if (entityhuman != null) { ++ double d2 = entityhuman.distanceToSqr(d0, (double) i, d1); + +- mobForSpawn.moveTo(d, (double)y, d1, level.random.nextFloat() * 360.0F, 0.0F); +- if (isValidPositionForMob(level, mobForSpawn, d2)) { +- spawnGroupData = mobForSpawn.finalizeSpawn( +- level, level.getCurrentDifficultyAt(mobForSpawn.blockPosition()), MobSpawnType.NATURAL, spawnGroupData, null +- ); +- i++; +- i3++; +- level.addFreshEntityWithPassengers(mobForSpawn); +- callback.run(mobForSpawn, chunk); +- if (i >= mobForSpawn.getMaxSpawnClusterSize()) { +- return; ++ if (isRightDistanceToPlayerAndSpawnPoint(level, chunk, blockposition_mutableblockposition, d2)) { ++ if (biomesettingsmobs_c == null) { ++ Optional<MobSpawnSettings.SpawnerData> optional = getRandomSpawnMobAt(level, structuremanager, chunkgenerator, category, level.random, blockposition_mutableblockposition); ++ ++ if (optional.isEmpty()) { ++ break label53; ++ } ++ ++ biomesettingsmobs_c = (MobSpawnSettings.SpawnerData) optional.get(); ++ j1 = biomesettingsmobs_c.minCount + level.random.nextInt(1 + biomesettingsmobs_c.maxCount - biomesettingsmobs_c.minCount); + } + +- if (mobForSpawn.isMaxGroupSizeReached(i3)) { +- break; ++ if (isValidSpawnPostitionForType(level, category, structuremanager, chunkgenerator, biomesettingsmobs_c, blockposition_mutableblockposition, d2) && filter.test(biomesettingsmobs_c.type, blockposition_mutableblockposition, chunk)) { ++ Mob entityinsentient = getMobForSpawn(level, biomesettingsmobs_c.type); ++ ++ if (entityinsentient == null) { ++ return; ++ } ++ ++ entityinsentient.moveTo(d0, (double) i, d1, level.random.nextFloat() * 360.0F, 0.0F); ++ if (isValidPositionForMob(level, entityinsentient, d2)) { ++ groupdataentity = entityinsentient.finalizeSpawn(level, level.getCurrentDifficultyAt(entityinsentient.blockPosition()), EnumMobSpawn.NATURAL, groupdataentity, (CompoundTag) null); ++ // 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(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; ++ callback.run(entityinsentient, chunk); ++ } ++ // CraftBukkit end ++ if (j >= entityinsentient.getMaxSpawnClusterSize()) { ++ return; ++ } ++ ++ if (entityinsentient.isMaxGroupSizeReached(k1)) { ++ break label53; ++ } ++ } + } + } + } ++ ++ ++l1; ++ continue; + } + } ++ ++ ++k; ++ break; + } + } ++ + } + } + + private static boolean isRightDistanceToPlayerAndSpawnPoint(ServerLevel level, ChunkAccess chunk, BlockPos.MutableBlockPos pos, double distance) { +- return !(distance <= 576.0) +- && !level.getSharedSpawnPos().closerToCenterThan(new Vec3((double)pos.getX() + 0.5, (double)pos.getY(), (double)pos.getZ() + 0.5), 24.0) +- && (Objects.equals(new ChunkPos(pos), chunk.getPos()) || level.isNaturalSpawningAllowed(pos)); ++ return distance <= 576.0D ? false : (level.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()) || level.isNaturalSpawningAllowed((BlockPos) pos)); + } + +- private static boolean isValidSpawnPostitionForType( +- ServerLevel level, +- MobCategory category, +- StructureManager structureManager, +- ChunkGenerator generator, +- MobSpawnSettings.SpawnerData data, +- BlockPos.MutableBlockPos pos, +- double distance +- ) { +- EntityType<?> entityType = data.type; +- if (entityType.getCategory() == MobCategory.MISC) { ++ private static boolean isValidSpawnPostitionForType(ServerLevel level, MobCategory category, StructureManager structureManager, ChunkGenerator generator, MobSpawnSettings.SpawnerData data, BlockPos.MutableBlockPos pos, double distance) { ++ EntityType<?> entitytypes = data.type; ++ ++ if (entitytypes.getCategory() == MobCategory.MISC) { + return false; +- } else if (!entityType.canSpawnFarFromPlayer() +- && distance > (double)(entityType.getCategory().getDespawnDistance() * entityType.getCategory().getDespawnDistance())) { ++ } else if (!entitytypes.canSpawnFarFromPlayer() && distance > (double) (entitytypes.getCategory().getDespawnDistance() * entitytypes.getCategory().getDespawnDistance())) { + return false; +- } else if (entityType.canSummon() && canSpawnMobAt(level, structureManager, generator, category, data, pos)) { +- SpawnPlacements.Type placementType = SpawnPlacements.getPlacementType(entityType); +- return isSpawnPositionOk(placementType, level, pos, entityType) +- && SpawnPlacements.checkSpawnRules(entityType, level, MobSpawnType.NATURAL, pos, level.random) +- && level.noCollision(entityType.getAABB((double)pos.getX() + 0.5, (double)pos.getY(), (double)pos.getZ() + 0.5)); ++ } else if (entitytypes.canSummon() && canSpawnMobAt(level, structureManager, generator, category, data, pos)) { ++ SpawnPlacements.Surface entitypositiontypes_surface = SpawnPlacements.getPlacementType(entitytypes); ++ ++ return !isSpawnPositionOk(entitypositiontypes_surface, level, pos, entitytypes) ? false : (!SpawnPlacements.checkSpawnRules(entitytypes, level, EnumMobSpawn.NATURAL, pos, level.random) ? false : level.noCollision(entitytypes.getAABB((double) pos.getX() + 0.5D, (double) pos.getY(), (double) pos.getZ() + 0.5D))); + } else { + return false; + } +@@ -241,96 +278,83 @@ + @Nullable + private static Mob getMobForSpawn(ServerLevel level, EntityType<?> entityType) { + try { +- Entity var3 = entityType.create(level); +- if (var3 instanceof Mob) { +- return (Mob)var3; ++ Entity entity = entityType.create(level); ++ ++ if (entity instanceof Mob) { ++ Mob entityinsentient = (Mob) entity; ++ ++ return entityinsentient; + } + +- LOGGER.warn("Can't spawn entity of type: {}", BuiltInRegistries.ENTITY_TYPE.getKey(entityType)); +- } catch (Exception var4) { +- LOGGER.warn("Failed to create mob", (Throwable)var4); ++ NaturalSpawner.LOGGER.warn("Can't spawn entity of type: {}", BuiltInRegistries.ENTITY_TYPE.getKey(entityType)); ++ } catch (Exception exception) { ++ NaturalSpawner.LOGGER.warn("Failed to create mob", exception); + } + + return null; + } + + private static boolean isValidPositionForMob(ServerLevel level, Mob mob, double distance) { +- return ( +- !(distance > (double)(mob.getType().getCategory().getDespawnDistance() * mob.getType().getCategory().getDespawnDistance())) +- || !mob.removeWhenFarAway(distance) +- ) +- && mob.checkSpawnRules(level, MobSpawnType.NATURAL) +- && mob.checkSpawnObstruction(level); ++ return distance > (double) (mob.getType().getCategory().getDespawnDistance() * mob.getType().getCategory().getDespawnDistance()) && mob.removeWhenFarAway(distance) ? false : mob.checkSpawnRules(level, EnumMobSpawn.NATURAL) && mob.checkSpawnObstruction(level); + } + +- private static Optional<MobSpawnSettings.SpawnerData> getRandomSpawnMobAt( +- ServerLevel level, StructureManager structureManager, ChunkGenerator generator, MobCategory category, RandomSource random, BlockPos pos +- ) { +- Holder<Biome> biome = level.getBiome(pos); +- return category == MobCategory.WATER_AMBIENT && biome.is(BiomeTags.REDUCED_WATER_AMBIENT_SPAWNS) && random.nextFloat() < 0.98F +- ? Optional.empty() +- : mobsAt(level, structureManager, generator, category, pos, biome).getRandom(random); ++ private static Optional<MobSpawnSettings.SpawnerData> getRandomSpawnMobAt(ServerLevel level, StructureManager structureManager, ChunkGenerator generator, MobCategory category, RandomSource random, BlockPos pos) { ++ Holder<Biome> holder = level.getBiome(pos); ++ ++ return category == MobCategory.WATER_AMBIENT && holder.is(BiomeTags.REDUCED_WATER_AMBIENT_SPAWNS) && random.nextFloat() < 0.98F ? Optional.empty() : mobsAt(level, structureManager, generator, category, pos, holder).getRandom(random); + } + +- private static boolean canSpawnMobAt( +- ServerLevel level, StructureManager structureManager, ChunkGenerator generator, MobCategory category, MobSpawnSettings.SpawnerData data, BlockPos pos +- ) { +- return mobsAt(level, structureManager, generator, category, pos, null).unwrap().contains(data); ++ private static boolean canSpawnMobAt(ServerLevel level, StructureManager structureManager, ChunkGenerator generator, MobCategory category, MobSpawnSettings.SpawnerData data, BlockPos pos) { ++ return mobsAt(level, structureManager, generator, category, pos, (Holder) null).unwrap().contains(data); + } + +- private static WeightedRandomList<MobSpawnSettings.SpawnerData> mobsAt( +- ServerLevel level, StructureManager structureManager, ChunkGenerator generator, MobCategory category, BlockPos pos, @Nullable Holder<Biome> biome +- ) { +- return isInNetherFortressBounds(pos, level, category, structureManager) +- ? NetherFortressStructure.FORTRESS_ENEMIES +- : generator.getMobsAt(biome != null ? biome : level.getBiome(pos), structureManager, category, pos); ++ private static WeightedRandomList<MobSpawnSettings.SpawnerData> mobsAt(ServerLevel level, StructureManager structureManager, ChunkGenerator generator, MobCategory category, BlockPos pos, @Nullable Holder<Biome> biome) { ++ return isInNetherFortressBounds(pos, level, category, structureManager) ? NetherFortressStructure.FORTRESS_ENEMIES : generator.getMobsAt(biome != null ? biome : level.getBiome(pos), structureManager, category, pos); + } + + public static boolean isInNetherFortressBounds(BlockPos pos, ServerLevel level, MobCategory category, StructureManager structureManager) { + if (category == MobCategory.MONSTER && level.getBlockState(pos.below()).is(Blocks.NETHER_BRICKS)) { +- Structure structure = structureManager.registryAccess().registryOrThrow(Registries.STRUCTURE).get(BuiltinStructures.FORTRESS); +- return structure != null && structureManager.getStructureAt(pos, structure).isValid(); ++ Structure structure = (Structure) structureManager.registryAccess().registryOrThrow(Registries.STRUCTURE).get(BuiltinStructures.FORTRESS); ++ ++ return structure == null ? false : structureManager.getStructureAt(pos, structure).isValid(); + } else { + return false; + } + } + + private static BlockPos getRandomPosWithin(Level level, LevelChunk chunk) { +- ChunkPos pos = chunk.getPos(); +- int i = pos.getMinBlockX() + level.random.nextInt(16); +- int i1 = pos.getMinBlockZ() + level.random.nextInt(16); +- int i2 = chunk.getHeight(Heightmap.Types.WORLD_SURFACE, i, i1) + 1; +- int i3 = Mth.randomBetweenInclusive(level.random, level.getMinBuildHeight(), i2); +- return new BlockPos(i, i3, i1); ++ ChunkPos chunkcoordintpair = chunk.getPos(); ++ int i = chunkcoordintpair.getMinBlockX() + level.random.nextInt(16); ++ int j = chunkcoordintpair.getMinBlockZ() + level.random.nextInt(16); ++ int k = chunk.getHeight(Heightmap.Types.WORLD_SURFACE, i, j) + 1; ++ int l = Mth.randomBetweenInclusive(level.random, level.getMinBuildHeight(), k); ++ ++ return new BlockPos(i, l, j); + } + +- public static boolean isValidEmptySpawnBlock(BlockGetter block, BlockPos pos, BlockState blockState, FluidState fluidState, EntityType<?> entityType) { +- return !blockState.isCollisionShapeFullBlock(block, pos) +- && !blockState.isSignalSource() +- && fluidState.isEmpty() +- && !blockState.is(BlockTags.PREVENT_MOB_SPAWNING_INSIDE) +- && !entityType.isBlockDangerous(blockState); ++ public static boolean isValidEmptySpawnBlock(BlockGetter block, BlockPos pos, IBlockData blockState, FluidState fluidState, EntityType<?> entityType) { ++ return blockState.isCollisionShapeFullBlock(block, pos) ? false : (blockState.isSignalSource() ? false : (!fluidState.isEmpty() ? false : (blockState.is(BlockTags.PREVENT_MOB_SPAWNING_INSIDE) ? false : !entityType.isBlockDangerous(blockState)))); + } + +- public static boolean isSpawnPositionOk(SpawnPlacements.Type placeType, LevelReader level, BlockPos pos, @Nullable EntityType<?> entityType) { +- if (placeType == SpawnPlacements.Type.NO_RESTRICTIONS) { ++ public static boolean isSpawnPositionOk(SpawnPlacements.Surface placeType, LevelReader level, BlockPos pos, @Nullable EntityType<?> entityType) { ++ if (placeType == SpawnPlacements.Surface.NO_RESTRICTIONS) { + return true; + } else if (entityType != null && level.getWorldBorder().isWithinBounds(pos)) { +- BlockState blockState = level.getBlockState(pos); +- FluidState fluidState = level.getFluidState(pos); +- BlockPos blockPos = pos.above(); +- BlockPos blockPos1 = pos.below(); ++ IBlockData iblockdata = level.getBlockState(pos); ++ FluidState fluid = level.getFluidState(pos); ++ BlockPos blockposition1 = pos.above(); ++ BlockPos blockposition2 = pos.below(); ++ + switch (placeType) { + case IN_WATER: +- return fluidState.is(FluidTags.WATER) && !level.getBlockState(blockPos).isRedstoneConductor(level, blockPos); ++ return fluid.is(FluidTags.WATER) && !level.getBlockState(blockposition1).isRedstoneConductor(level, blockposition1); + case IN_LAVA: +- return fluidState.is(FluidTags.LAVA); ++ return fluid.is(FluidTags.LAVA); + case ON_GROUND: + default: +- BlockState blockState1 = level.getBlockState(blockPos1); +- return blockState1.isValidSpawn(level, blockPos1, entityType) +- && isValidEmptySpawnBlock(level, pos, blockState, fluidState, entityType) +- && isValidEmptySpawnBlock(level, blockPos, level.getBlockState(blockPos), level.getFluidState(blockPos), entityType); ++ IBlockData iblockdata1 = level.getBlockState(blockposition2); ++ ++ return !iblockdata1.isValidSpawn(level, blockposition2, entityType) ? false : isValidEmptySpawnBlock(level, pos, iblockdata, fluid, entityType) && isValidEmptySpawnBlock(level, blockposition1, level.getBlockState(blockposition1), level.getFluidState(blockposition1), entityType); + } + } else { + return false; +@@ -338,49 +362,46 @@ + } + + public static void spawnMobsForChunkGeneration(ServerLevelAccessor levelAccessor, Holder<Biome> biome, ChunkPos chunkPos, RandomSource random) { +- MobSpawnSettings mobSettings = biome.value().getMobSettings(); +- WeightedRandomList<MobSpawnSettings.SpawnerData> mobs = mobSettings.getMobs(MobCategory.CREATURE); +- if (!mobs.isEmpty()) { +- int minBlockX = chunkPos.getMinBlockX(); +- int minBlockZ = chunkPos.getMinBlockZ(); ++ MobSpawnSettings biomesettingsmobs = ((Biome) biome.value()).getMobSettings(); ++ WeightedRandomList<MobSpawnSettings.SpawnerData> weightedrandomlist = biomesettingsmobs.getMobs(MobCategory.CREATURE); + +- while (random.nextFloat() < mobSettings.getCreatureProbability()) { +- Optional<MobSpawnSettings.SpawnerData> random1 = mobs.getRandom(random); +- if (!random1.isEmpty()) { +- MobSpawnSettings.SpawnerData spawnerData = random1.get(); +- int i = spawnerData.minCount + random.nextInt(1 + spawnerData.maxCount - spawnerData.minCount); +- SpawnGroupData spawnGroupData = null; +- int i1 = minBlockX + random.nextInt(16); +- int i2 = minBlockZ + random.nextInt(16); +- int i3 = i1; +- int i4 = i2; ++ if (!weightedrandomlist.isEmpty()) { ++ int i = chunkPos.getMinBlockX(); ++ int j = chunkPos.getMinBlockZ(); + +- for (int i5 = 0; i5 < i; i5++) { ++ while (random.nextFloat() < biomesettingsmobs.getCreatureProbability()) { ++ Optional<MobSpawnSettings.SpawnerData> optional = weightedrandomlist.getRandom(random); ++ ++ if (!optional.isEmpty()) { ++ MobSpawnSettings.SpawnerData biomesettingsmobs_c = (MobSpawnSettings.SpawnerData) optional.get(); ++ int k = biomesettingsmobs_c.minCount + random.nextInt(1 + biomesettingsmobs_c.maxCount - biomesettingsmobs_c.minCount); ++ GroupDataEntity groupdataentity = null; ++ int l = i + random.nextInt(16); ++ int i1 = j + random.nextInt(16); ++ int j1 = l; ++ int k1 = i1; ++ ++ for (int l1 = 0; l1 < k; ++l1) { + boolean flag = false; + +- for (int i6 = 0; !flag && i6 < 4; i6++) { +- BlockPos topNonCollidingPos = getTopNonCollidingPos(levelAccessor, spawnerData.type, i1, i2); +- if (spawnerData.type.canSummon() +- && isSpawnPositionOk(SpawnPlacements.getPlacementType(spawnerData.type), levelAccessor, topNonCollidingPos, spawnerData.type)) { +- float width = spawnerData.type.getWidth(); +- double d = Mth.clamp((double)i1, (double)minBlockX + (double)width, (double)minBlockX + 16.0 - (double)width); +- double d1 = Mth.clamp((double)i2, (double)minBlockZ + (double)width, (double)minBlockZ + 16.0 - (double)width); +- if (!levelAccessor.noCollision(spawnerData.type.getAABB(d, (double)topNonCollidingPos.getY(), d1)) +- || !SpawnPlacements.checkSpawnRules( +- spawnerData.type, +- levelAccessor, +- MobSpawnType.CHUNK_GENERATION, +- BlockPos.containing(d, (double)topNonCollidingPos.getY(), d1), +- levelAccessor.getRandom() +- )) { ++ for (int i2 = 0; !flag && i2 < 4; ++i2) { ++ BlockPos blockposition = getTopNonCollidingPos(levelAccessor, biomesettingsmobs_c.type, l, i1); ++ ++ if (biomesettingsmobs_c.type.canSummon() && isSpawnPositionOk(SpawnPlacements.getPlacementType(biomesettingsmobs_c.type), levelAccessor, blockposition, biomesettingsmobs_c.type)) { ++ float f = biomesettingsmobs_c.type.getWidth(); ++ double d0 = Mth.clamp((double) l, (double) i + (double) f, (double) i + 16.0D - (double) f); ++ double d1 = Mth.clamp((double) i1, (double) j + (double) f, (double) j + 16.0D - (double) f); ++ ++ if (!levelAccessor.noCollision(biomesettingsmobs_c.type.getAABB(d0, (double) blockposition.getY(), d1)) || !SpawnPlacements.checkSpawnRules(biomesettingsmobs_c.type, levelAccessor, EnumMobSpawn.CHUNK_GENERATION, BlockPos.containing(d0, (double) blockposition.getY(), d1), levelAccessor.getRandom())) { + continue; + } + + Entity entity; ++ + try { +- entity = spawnerData.type.create(levelAccessor.getLevel()); +- } catch (Exception var27) { +- LOGGER.warn("Failed to create mob", (Throwable)var27); ++ entity = biomesettingsmobs_c.type.create(levelAccessor.getLevel()); ++ } catch (Exception exception) { ++ NaturalSpawner.LOGGER.warn("Failed to create mob", exception); + continue; + } + +@@ -388,76 +409,64 @@ + continue; + } + +- entity.moveTo(d, (double)topNonCollidingPos.getY(), d1, random.nextFloat() * 360.0F, 0.0F); +- if (entity instanceof Mob mob +- && mob.checkSpawnRules(levelAccessor, MobSpawnType.CHUNK_GENERATION) +- && mob.checkSpawnObstruction(levelAccessor)) { +- spawnGroupData = mob.finalizeSpawn( +- levelAccessor, +- levelAccessor.getCurrentDifficultyAt(mob.blockPosition()), +- MobSpawnType.CHUNK_GENERATION, +- spawnGroupData, +- null +- ); +- levelAccessor.addFreshEntityWithPassengers(mob); +- flag = true; ++ entity.moveTo(d0, (double) blockposition.getY(), d1, random.nextFloat() * 360.0F, 0.0F); ++ if (entity instanceof Mob) { ++ Mob entityinsentient = (Mob) entity; ++ ++ if (entityinsentient.checkSpawnRules(levelAccessor, EnumMobSpawn.CHUNK_GENERATION) && entityinsentient.checkSpawnObstruction(levelAccessor)) { ++ groupdataentity = entityinsentient.finalizeSpawn(levelAccessor, levelAccessor.getCurrentDifficultyAt(entityinsentient.blockPosition()), EnumMobSpawn.CHUNK_GENERATION, groupdataentity, (CompoundTag) null); ++ levelAccessor.addFreshEntityWithPassengers(entityinsentient, SpawnReason.CHUNK_GEN); // CraftBukkit ++ flag = true; ++ } + } + } + +- i1 += random.nextInt(5) - random.nextInt(5); ++ l += random.nextInt(5) - random.nextInt(5); + +- for (i2 += random.nextInt(5) - random.nextInt(5); +- i1 < minBlockX || i1 >= minBlockX + 16 || i2 < minBlockZ || i2 >= minBlockZ + 16; +- i2 = i4 + random.nextInt(5) - random.nextInt(5) +- ) { +- i1 = i3 + random.nextInt(5) - random.nextInt(5); ++ for (i1 += random.nextInt(5) - random.nextInt(5); l < i || l >= i + 16 || i1 < j || i1 >= j + 16; i1 = k1 + random.nextInt(5) - random.nextInt(5)) { ++ l = j1 + random.nextInt(5) - random.nextInt(5); + } + } + } + } + } ++ + } + } + + private static BlockPos getTopNonCollidingPos(LevelReader level, EntityType<?> entityType, int x, int z) { +- int height = level.getHeight(SpawnPlacements.getHeightmapType(entityType), x, z); +- BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(x, height, z); ++ int k = level.getHeight(SpawnPlacements.getHeightmapType(entityType), x, z); ++ BlockPos.MutableBlockPos blockposition_mutableblockposition = new BlockPos.MutableBlockPos(x, k, z); ++ + if (level.dimensionType().hasCeiling()) { + do { +- mutableBlockPos.move(Direction.DOWN); +- } while (!level.getBlockState(mutableBlockPos).isAir()); ++ blockposition_mutableblockposition.move(Direction.DOWN); ++ } while (!level.getBlockState(blockposition_mutableblockposition).isAir()); + + do { +- mutableBlockPos.move(Direction.DOWN); +- } while (level.getBlockState(mutableBlockPos).isAir() && mutableBlockPos.getY() > level.getMinBuildHeight()); ++ blockposition_mutableblockposition.move(Direction.DOWN); ++ } while (level.getBlockState(blockposition_mutableblockposition).isAir() && blockposition_mutableblockposition.getY() > level.getMinBuildHeight()); + } + +- if (SpawnPlacements.getPlacementType(entityType) == SpawnPlacements.Type.ON_GROUND) { +- BlockPos blockPos = mutableBlockPos.below(); +- if (level.getBlockState(blockPos).isPathfindable(level, blockPos, PathComputationType.LAND)) { +- return blockPos; ++ if (SpawnPlacements.getPlacementType(entityType) == SpawnPlacements.Surface.ON_GROUND) { ++ BlockPos blockposition = blockposition_mutableblockposition.below(); ++ ++ if (level.getBlockState(blockposition).isPathfindable(level, blockposition, PathMode.LAND)) { ++ return blockposition; + } + } + +- return mutableBlockPos.immutable(); ++ return blockposition_mutableblockposition.immutable(); + } + + @FunctionalInterface +- public interface AfterSpawnCallback { +- void run(Mob mob, ChunkAccess chunk); +- } +- +- @FunctionalInterface + public interface ChunkGetter { ++ + void query(long chunkPos, Consumer<LevelChunk> consumer); + } + +- @FunctionalInterface +- public interface SpawnPredicate { +- boolean test(EntityType<?> entityType, BlockPos pos, ChunkAccess chunk); +- } +- + public static class SpawnState { ++ + private final int spawnableChunkCount; + private final Object2IntOpenHashMap<MobCategory> mobCategoryCounts; + private final PotentialCalculator spawnPotential; +@@ -469,12 +478,7 @@ + private EntityType<?> lastCheckedType; + private double lastCharge; + +- SpawnState( +- int spawnableChunkCount, +- Object2IntOpenHashMap<MobCategory> mobCategoryCounts, +- PotentialCalculator spawnPotential, +- LocalMobCapCalculator localMobCapCalculator +- ) { ++ SpawnState(int spawnableChunkCount, Object2IntOpenHashMap<MobCategory> mobCategoryCounts, PotentialCalculator spawnPotential, LocalMobCapCalculator localMobCapCalculator) { + this.spawnableChunkCount = spawnableChunkCount; + this.mobCategoryCounts = mobCategoryCounts; + this.spawnPotential = spawnPotential; +@@ -485,37 +489,43 @@ + private boolean canSpawn(EntityType<?> entityType, BlockPos pos, ChunkAccess chunk) { + this.lastCheckedPos = pos; + this.lastCheckedType = entityType; +- MobSpawnSettings.MobSpawnCost mobSpawnCost = NaturalSpawner.getRoughBiome(pos, chunk).getMobSettings().getMobSpawnCost(entityType); +- if (mobSpawnCost == null) { +- this.lastCharge = 0.0; ++ MobSpawnSettings.MobSpawnCost biomesettingsmobs_b = NaturalSpawner.getRoughBiome(pos, chunk).getMobSettings().getMobSpawnCost(entityType); ++ ++ if (biomesettingsmobs_b == null) { ++ this.lastCharge = 0.0D; + return true; + } else { +- double d = mobSpawnCost.charge(); +- this.lastCharge = d; +- double potentialEnergyChange = this.spawnPotential.getPotentialEnergyChange(pos, d); +- return potentialEnergyChange <= mobSpawnCost.energyBudget(); ++ double d0 = biomesettingsmobs_b.charge(); ++ ++ this.lastCharge = d0; ++ double d1 = this.spawnPotential.getPotentialEnergyChange(pos, d0); ++ ++ return d1 <= biomesettingsmobs_b.energyBudget(); + } + } + + private void afterSpawn(Mob mob, ChunkAccess chunk) { +- EntityType<?> type = mob.getType(); +- BlockPos blockPos = mob.blockPosition(); +- double d; +- if (blockPos.equals(this.lastCheckedPos) && type == this.lastCheckedType) { +- d = this.lastCharge; ++ EntityType<?> entitytypes = mob.getType(); ++ BlockPos blockposition = mob.blockPosition(); ++ double d0; ++ ++ if (blockposition.equals(this.lastCheckedPos) && entitytypes == this.lastCheckedType) { ++ d0 = this.lastCharge; + } else { +- MobSpawnSettings.MobSpawnCost mobSpawnCost = NaturalSpawner.getRoughBiome(blockPos, chunk).getMobSettings().getMobSpawnCost(type); +- if (mobSpawnCost != null) { +- d = mobSpawnCost.charge(); ++ MobSpawnSettings.MobSpawnCost biomesettingsmobs_b = NaturalSpawner.getRoughBiome(blockposition, chunk).getMobSettings().getMobSpawnCost(entitytypes); ++ ++ if (biomesettingsmobs_b != null) { ++ d0 = biomesettingsmobs_b.charge(); + } else { +- d = 0.0; ++ d0 = 0.0D; + } + } + +- this.spawnPotential.addCharge(blockPos, d); +- MobCategory category = type.getCategory(); +- this.mobCategoryCounts.addTo(category, 1); +- this.localMobCapCalculator.addMob(new ChunkPos(blockPos), category); ++ this.spawnPotential.addCharge(blockposition, d0); ++ MobCategory enumcreaturetype = entitytypes.getCategory(); ++ ++ this.mobCategoryCounts.addTo(enumcreaturetype, 1); ++ this.localMobCapCalculator.addMob(new ChunkPos(blockposition), enumcreaturetype); + } + + public int getSpawnableChunkCount() { +@@ -526,9 +536,24 @@ + return this.unmodifiableMobCategoryCounts; + } + +- boolean canSpawnForCategory(MobCategory category, ChunkPos pos) { +- int i = category.getMaxInstancesPerChunk() * this.spawnableChunkCount / NaturalSpawner.MAGIC_NUMBER; +- return this.mobCategoryCounts.getInt(category) < i && this.localMobCapCalculator.canSpawn(category, pos); ++ // CraftBukkit start ++ boolean canSpawnForCategory(MobCategory enumcreaturetype, ChunkPos chunkcoordintpair, int limit) { ++ int i = limit * this.spawnableChunkCount / NaturalSpawner.MAGIC_NUMBER; ++ // CraftBukkit end ++ ++ return this.mobCategoryCounts.getInt(enumcreaturetype) >= i ? false : this.localMobCapCalculator.canSpawn(enumcreaturetype, chunkcoordintpair); + } + } ++ ++ @FunctionalInterface ++ public interface SpawnPredicate { ++ ++ boolean test(EntityType<?> entityType, BlockPos pos, ChunkAccess chunk); ++ } ++ ++ @FunctionalInterface ++ public interface AfterSpawnCallback { ++ ++ void run(Mob mob, ChunkAccess chunk); ++ } + } |