package net.minecraft.world.level; import org.apache.logging.log4j.LogManager; import net.minecraft.world.level.pathfinder.PathComputationType; import net.minecraft.tags.FluidTags; import net.minecraft.tags.BlockTags; import net.minecraft.world.level.material.FluidState; import net.minecraft.world.level.levelgen.Heightmap; import javax.annotation.Nullable; import java.util.List; import net.minecraft.util.WeighedRandom; import java.util.Random; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.SpawnGroupData; import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.ChunkGenerator; import net.minecraft.nbt.CompoundTag; import net.minecraft.world.entity.Entity; import net.minecraft.core.Registry; import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.MobSpawnType; import net.minecraft.world.entity.SpawnPlacements; import java.util.Objects; import net.minecraft.core.Position; import net.minecraft.world.phys.Vec3; import net.minecraft.util.Mth; import net.minecraft.core.BlockPos; import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.MobCategory; import org.apache.logging.log4j.Logger; public final class NaturalSpawner { private static final Logger LOGGER; public static void spawnCategoryForChunk(final MobCategory aky, final ServerLevel xd, final LevelChunk cai, final BlockPos fk) { final ChunkGenerator bzx5 = xd.getChunkSource().getGenerator(); int integer6 = 0; final BlockPos fk2 = getRandomPosWithin(xd, cai); final int integer7 = fk2.getX(); final int integer8 = fk2.getY(); final int integer9 = fk2.getZ(); if (integer8 < 1) { return; } final BlockState byg11 = cai.getBlockState(fk2); if (byg11.isRedstoneConductor(cai, fk2)) { return; } final BlockPos.MutableBlockPos a12 = new BlockPos.MutableBlockPos(); for (int integer10 = 0; integer10 < 3; ++integer10) { int integer11 = integer7; int integer12 = integer9; final int integer13 = 6; Biome.SpawnerData e17 = null; SpawnGroupData alj18 = null; int integer14 = Mth.ceil(Math.random() * 4.0); int integer15 = 0; for (int integer16 = 0; integer16 < integer14; ++integer16) { integer11 += xd.random.nextInt(6) - xd.random.nextInt(6); integer12 += xd.random.nextInt(6) - xd.random.nextInt(6); a12.set(integer11, integer8, integer12); final float float22 = integer11 + 0.5f; final float float23 = integer12 + 0.5f; final Player ayg24 = xd.getNearestPlayerIgnoreY(float22, float23, -1.0); if (ayg24 != null) { final double double25 = ayg24.distanceToSqr(float22, integer8, float23); if (double25 > 576.0) { if (!fk.closerThan(new Vec3(float22, integer8, float23), 24.0)) { final ChunkPos bje27 = new ChunkPos(a12); if (Objects.equals(bje27, cai.getPos()) || xd.getChunkSource().isEntityTickingChunk(bje27)) { if (e17 == null) { e17 = getRandomSpawnMobAt(bzx5, aky, xd.random, a12); if (e17 == null) { break; } integer14 = e17.minCount + xd.random.nextInt(1 + e17.maxCount - e17.minCount); } if (e17.type.getCategory() != MobCategory.MISC) { if (e17.type.canSpawnFarFromPlayer() || double25 <= 16384.0) { final EntityType akr28 = e17.type; if (akr28.canSummon()) { if (canSpawnMobAt(bzx5, aky, e17, a12)) { final SpawnPlacements.Type c29 = SpawnPlacements.getPlacementType(akr28); if (isSpawnPositionOk(c29, xd, a12, akr28)) { if (SpawnPlacements.checkSpawnRules(akr28, xd, MobSpawnType.NATURAL, a12, xd.random)) { if (xd.noCollision(akr28.getAABB(float22, integer8, float23))) { Mob akx30; try { final Entity akn31 = (Entity)akr28.create(xd); if (!(akn31 instanceof Mob)) { throw new IllegalStateException("Trying to spawn a non-mob: " + Registry.ENTITY_TYPE.getKey(akr28)); } akx30 = (Mob)akn31; } catch (Exception exception31) { NaturalSpawner.LOGGER.warn("Failed to create mob", (Throwable)exception31); return; } akx30.moveTo(float22, integer8, float23, xd.random.nextFloat() * 360.0f, 0.0f); if (double25 <= 16384.0 || !akx30.removeWhenFarAway(double25)) { if (akx30.checkSpawnRules(xd, MobSpawnType.NATURAL)) { if (akx30.checkSpawnObstruction(xd)) { alj18 = akx30.finalizeSpawn(xd, xd.getCurrentDifficultyAt(new BlockPos(akx30)), MobSpawnType.NATURAL, alj18, null); ++integer6; ++integer15; xd.addFreshEntity(akx30); if (integer6 >= akx30.getMaxSpawnClusterSize()) { return; } if (akx30.isMaxGroupSizeReached(integer15)) { break; } } } } } } } } } } } } } } } } } } @Nullable private static Biome.SpawnerData getRandomSpawnMobAt(final ChunkGenerator bzx, final MobCategory aky, final Random random, final BlockPos fk) { final List list5 = bzx.getMobsAt(aky, fk); if (list5.isEmpty()) { return null; } return WeighedRandom.getRandomItem(random, list5); } private static boolean canSpawnMobAt(final ChunkGenerator bzx, final MobCategory aky, final Biome.SpawnerData e, final BlockPos fk) { final List list5 = bzx.getMobsAt(aky, fk); return !list5.isEmpty() && list5.contains(e); } private static BlockPos getRandomPosWithin(final Level bjt, final LevelChunk cai) { final ChunkPos bje3 = cai.getPos(); final int integer4 = bje3.getMinBlockX() + bjt.random.nextInt(16); final int integer5 = bje3.getMinBlockZ() + bjt.random.nextInt(16); final int integer6 = cai.getHeight(Heightmap.Types.WORLD_SURFACE, integer4, integer5) + 1; final int integer7 = bjt.random.nextInt(integer6 + 1); return new BlockPos(integer4, integer7, integer5); } public static boolean isValidEmptySpawnBlock(final BlockGetter bjd, final BlockPos fk, final BlockState byg, final FluidState cog) { return !byg.isCollisionShapeFullBlock(bjd, fk) && !byg.isSignalSource() && cog.isEmpty() && !byg.is(BlockTags.RAILS); } public static boolean isSpawnPositionOk(final SpawnPlacements.Type c, final LevelReader bjw, final BlockPos fk, @Nullable final EntityType akr) { if (c == SpawnPlacements.Type.NO_RESTRICTIONS) { return true; } if (akr == null || !bjw.getWorldBorder().isWithinBounds(fk)) { return false; } final BlockState byg5 = bjw.getBlockState(fk); final FluidState cog6 = bjw.getFluidState(fk); final BlockPos fk2 = fk.above(); final BlockPos fk3 = fk.below(); switch (c) { case IN_WATER: { return cog6.is(FluidTags.WATER) && bjw.getFluidState(fk3).is(FluidTags.WATER) && !bjw.getBlockState(fk2).isRedstoneConductor(bjw, fk2); } default: { final BlockState byg6 = bjw.getBlockState(fk3); return byg6.isValidSpawn(bjw, fk3, akr) && isValidEmptySpawnBlock(bjw, fk, byg5, cog6) && isValidEmptySpawnBlock(bjw, fk2, bjw.getBlockState(fk2), bjw.getFluidState(fk2)); } } } public static void spawnMobsForChunkGeneration(final LevelAccessor bju, final Biome bkq, final int integer3, final int integer4, final Random random) { final List list6 = bkq.getMobs(MobCategory.CREATURE); if (list6.isEmpty()) { return; } final int integer5 = integer3 << 4; final int integer6 = integer4 << 4; while (random.nextFloat() < bkq.getCreatureProbability()) { final Biome.SpawnerData e9 = WeighedRandom.getRandomItem(random, list6); final int integer7 = e9.minCount + random.nextInt(1 + e9.maxCount - e9.minCount); SpawnGroupData alj11 = null; int integer8 = integer5 + random.nextInt(16); int integer9 = integer6 + random.nextInt(16); final int integer10 = integer8; final int integer11 = integer9; for (int integer12 = 0; integer12 < integer7; ++integer12) { boolean boolean17 = false; for (int integer13 = 0; !boolean17 && integer13 < 4; ++integer13) { final BlockPos fk19 = getTopNonCollidingPos(bju, e9.type, integer8, integer9); if (e9.type.canSummon() && isSpawnPositionOk(SpawnPlacements.Type.ON_GROUND, bju, fk19, e9.type)) { final float float20 = e9.type.getWidth(); final double double21 = Mth.clamp(integer8, integer5 + (double)float20, integer5 + 16.0 - float20); final double double22 = Mth.clamp(integer9, integer6 + (double)float20, integer6 + 16.0 - float20); if (!bju.noCollision(e9.type.getAABB(double21, fk19.getY(), double22))) { continue; } if (!SpawnPlacements.checkSpawnRules(e9.type, bju, MobSpawnType.CHUNK_GENERATION, new BlockPos(double21, fk19.getY(), double22), bju.getRandom())) { continue; } Entity akn25; try { akn25 = (Entity)e9.type.create(bju.getLevel()); } catch (Exception exception26) { NaturalSpawner.LOGGER.warn("Failed to create mob", (Throwable)exception26); continue; } akn25.moveTo(double21, fk19.getY(), double22, random.nextFloat() * 360.0f, 0.0f); if (akn25 instanceof Mob) { final Mob akx26 = (Mob)akn25; if (akx26.checkSpawnRules(bju, MobSpawnType.CHUNK_GENERATION) && akx26.checkSpawnObstruction(bju)) { alj11 = akx26.finalizeSpawn(bju, bju.getCurrentDifficultyAt(new BlockPos(akx26)), MobSpawnType.CHUNK_GENERATION, alj11, null); bju.addFreshEntity(akx26); boolean17 = true; } } } for (integer8 += random.nextInt(5) - random.nextInt(5), integer9 += random.nextInt(5) - random.nextInt(5); integer8 < integer5 || integer8 >= integer5 + 16 || integer9 < integer6 || integer9 >= integer6 + 16; integer8 = integer10 + random.nextInt(5) - random.nextInt(5), integer9 = integer11 + random.nextInt(5) - random.nextInt(5)) {} } } } } private static BlockPos getTopNonCollidingPos(final LevelReader bjw, @Nullable final EntityType akr, final int integer3, final int integer4) { final BlockPos fk5 = new BlockPos(integer3, bjw.getHeight(SpawnPlacements.getHeightmapType(akr), integer3, integer4), integer4); final BlockPos fk6 = fk5.below(); if (bjw.getBlockState(fk6).isPathfindable(bjw, fk6, PathComputationType.LAND)) { return fk6; } return fk5; } static { LOGGER = LogManager.getLogger(); } }