package net.minecraft.world.level; import com.google.common.collect.Lists; import com.mojang.serialization.Codec; import java.io.IOException; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.UUID; import java.util.function.Consumer; import java.util.function.Predicate; import java.util.stream.Collectors; import net.minecraft.CrashReport; import net.minecraft.CrashReportCategory; import net.minecraft.CrashReportDetail; import net.minecraft.ReportedException; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.Holder; import net.minecraft.core.RegistryAccess; import net.minecraft.core.SectionPos; import net.minecraft.core.particles.ParticleOptions; import net.minecraft.core.particles.ParticleTypes; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.Registries; import net.minecraft.network.protocol.Packet; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.FullChunkStatus; import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; import net.minecraft.util.Mth; import net.minecraft.util.RandomSource; import net.minecraft.util.StringRepresentable; import net.minecraft.util.AbortableIterationConsumer.Continuation; import net.minecraft.util.profiling.Profiler; import net.minecraft.util.profiling.ProfilerFiller; import net.minecraft.world.DifficultyInstance; import net.minecraft.world.TickRateManager; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.damagesource.DamageSources; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntitySelector; import net.minecraft.world.entity.boss.EnderDragonPart; import net.minecraft.world.entity.boss.enderdragon.EnderDragon; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.alchemy.PotionBrewing; import net.minecraft.world.item.component.FireworkExplosion; import net.minecraft.world.item.crafting.RecipeAccess; import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.biome.BiomeManager; import net.minecraft.world.level.block.BaseFireBlock; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.FuelValues; import net.minecraft.world.level.block.entity.TickingBlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.border.WorldBorder; import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.chunk.status.ChunkStatus; import net.minecraft.world.level.dimension.DimensionType; import net.minecraft.world.level.entity.EntityTypeTest; import net.minecraft.world.level.entity.LevelEntityGetter; import net.minecraft.world.level.entity.UUIDLookup; import net.minecraft.world.level.gameevent.GameEvent; import net.minecraft.world.level.gameevent.GameEvent.Context; import net.minecraft.world.level.levelgen.Heightmap; import net.minecraft.world.level.lighting.LevelLightEngine; import net.minecraft.world.level.material.FluidState; import net.minecraft.world.level.material.Fluids; import net.minecraft.world.level.redstone.CollectingNeighborUpdater; import net.minecraft.world.level.redstone.NeighborUpdater; import net.minecraft.world.level.redstone.Orientation; import net.minecraft.world.level.saveddata.maps.MapId; import net.minecraft.world.level.saveddata.maps.MapItemSavedData; import net.minecraft.world.level.storage.LevelData; import net.minecraft.world.level.storage.WritableLevelData; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; import net.minecraft.world.scores.Scoreboard; import org.jetbrains.annotations.Nullable; public abstract class Level implements LevelAccessor, UUIDLookup, AutoCloseable { public static final Codec> RESOURCE_KEY_CODEC = ResourceKey.codec(Registries.DIMENSION); public static final ResourceKey OVERWORLD = ResourceKey.create(Registries.DIMENSION, ResourceLocation.withDefaultNamespace("overworld")); public static final ResourceKey NETHER = ResourceKey.create(Registries.DIMENSION, ResourceLocation.withDefaultNamespace("the_nether")); public static final ResourceKey END = ResourceKey.create(Registries.DIMENSION, ResourceLocation.withDefaultNamespace("the_end")); public static final int MAX_LEVEL_SIZE = 30000000; public static final int LONG_PARTICLE_CLIP_RANGE = 512; public static final int SHORT_PARTICLE_CLIP_RANGE = 32; public static final int MAX_BRIGHTNESS = 15; public static final int TICKS_PER_DAY = 24000; public static final int MAX_ENTITY_SPAWN_Y = 20000000; public static final int MIN_ENTITY_SPAWN_Y = -20000000; protected final List blockEntityTickers = Lists.newArrayList(); protected final NeighborUpdater neighborUpdater; private final List pendingBlockEntityTickers = Lists.newArrayList(); private boolean tickingBlockEntities; private final Thread thread; private final boolean isDebug; private int skyDarken; /** * Contains the current Linear Congruential Generator seed for block updates. Used with an A value of 3 and a C value of 0x3c6ef35f, producing a highly planar series of values ill-suited for choosing random blocks in a 16x128x16 field. */ protected int randValue = RandomSource.create().nextInt(); protected final int addend = 1013904223; protected float oRainLevel; protected float rainLevel; protected float oThunderLevel; protected float thunderLevel; public final RandomSource random = RandomSource.create(); @Deprecated private final RandomSource threadSafeRandom = RandomSource.createThreadSafe(); private final Holder dimensionTypeRegistration; protected final WritableLevelData levelData; public final boolean isClientSide; private final WorldBorder worldBorder; private final BiomeManager biomeManager; private final ResourceKey dimension; private final RegistryAccess registryAccess; private final DamageSources damageSources; private long subTickCount; protected Level( WritableLevelData levelData, ResourceKey dimension, RegistryAccess registryAccess, Holder dimensionTypeRegistration, boolean isClientSide, boolean isDebug, long biomeZoomSeed, int maxChainedNeighborUpdates ) { this.levelData = levelData; this.dimensionTypeRegistration = dimensionTypeRegistration; final DimensionType dimensionType = dimensionTypeRegistration.value(); this.dimension = dimension; this.isClientSide = isClientSide; if (dimensionType.coordinateScale() != 1.0) { this.worldBorder = new WorldBorder() { @Override public double getCenterX() { return super.getCenterX() / dimensionType.coordinateScale(); } @Override public double getCenterZ() { return super.getCenterZ() / dimensionType.coordinateScale(); } }; } else { this.worldBorder = new WorldBorder(); } this.thread = Thread.currentThread(); this.biomeManager = new BiomeManager(this, biomeZoomSeed); this.isDebug = isDebug; this.neighborUpdater = new CollectingNeighborUpdater(this, maxChainedNeighborUpdates); this.registryAccess = registryAccess; this.damageSources = new DamageSources(registryAccess); } @Override public boolean isClientSide() { return this.isClientSide; } @Nullable @Override public MinecraftServer getServer() { return null; } /** * Check if the given BlockPos has valid coordinates */ public boolean isInWorldBounds(BlockPos pos) { return !this.isOutsideBuildHeight(pos) && isInWorldBoundsHorizontal(pos); } public static boolean isInSpawnableBounds(BlockPos pos) { return !isOutsideSpawnableHeight(pos.getY()) && isInWorldBoundsHorizontal(pos); } private static boolean isInWorldBoundsHorizontal(BlockPos pos) { return pos.getX() >= -30000000 && pos.getZ() >= -30000000 && pos.getX() < 30000000 && pos.getZ() < 30000000; } private static boolean isOutsideSpawnableHeight(int y) { return y < -20000000 || y >= 20000000; } public LevelChunk getChunkAt(BlockPos pos) { return this.getChunk(SectionPos.blockToSectionCoord(pos.getX()), SectionPos.blockToSectionCoord(pos.getZ())); } public LevelChunk getChunk(int chunkX, int chunkZ) { return (LevelChunk)this.getChunk(chunkX, chunkZ, ChunkStatus.FULL); } @Nullable @Override public ChunkAccess getChunk(int x, int z, ChunkStatus chunkStatus, boolean requireChunk) { ChunkAccess chunkAccess = this.getChunkSource().getChunk(x, z, chunkStatus, requireChunk); if (chunkAccess == null && requireChunk) { throw new IllegalStateException("Should always be able to create a chunk!"); } else { return chunkAccess; } } @Override public boolean setBlock(BlockPos pos, BlockState newState, int flags) { return this.setBlock(pos, newState, flags, 512); } @Override public boolean setBlock(BlockPos pos, BlockState state, int flags, int recursionLeft) { if (this.isOutsideBuildHeight(pos)) { return false; } else if (!this.isClientSide && this.isDebug()) { return false; } else { LevelChunk levelChunk = this.getChunkAt(pos); Block block = state.getBlock(); BlockState blockState = levelChunk.setBlockState(pos, state, flags); if (blockState == null) { return false; } else { BlockState blockState2 = this.getBlockState(pos); if (blockState2 == state) { if (blockState != blockState2) { this.setBlocksDirty(pos, blockState, blockState2); } if ((flags & 2) != 0 && (!this.isClientSide || (flags & 4) == 0) && (this.isClientSide || levelChunk.getFullStatus() != null && levelChunk.getFullStatus().isOrAfter(FullChunkStatus.BLOCK_TICKING))) { this.sendBlockUpdated(pos, blockState, state, flags); } if ((flags & 1) != 0) { this.updateNeighborsAt(pos, blockState.getBlock()); if (!this.isClientSide && state.hasAnalogOutputSignal()) { this.updateNeighbourForOutputSignal(pos, block); } } if ((flags & 16) == 0 && recursionLeft > 0) { int i = flags & -34; blockState.updateIndirectNeighbourShapes(this, pos, i, recursionLeft - 1); state.updateNeighbourShapes(this, pos, i, recursionLeft - 1); state.updateIndirectNeighbourShapes(this, pos, i, recursionLeft - 1); } this.updatePOIOnBlockStateChange(pos, blockState, blockState2); } return true; } } } public void updatePOIOnBlockStateChange(BlockPos pos, BlockState oldState, BlockState newState) { } @Override public boolean removeBlock(BlockPos pos, boolean isMoving) { FluidState fluidState = this.getFluidState(pos); return this.setBlock(pos, fluidState.createLegacyBlock(), 3 | (isMoving ? 64 : 0)); } @Override public boolean destroyBlock(BlockPos pos, boolean dropBlock, @Nullable Entity entity, int recursionLeft) { BlockState blockState = this.getBlockState(pos); if (blockState.isAir()) { return false; } else { FluidState fluidState = this.getFluidState(pos); if (!(blockState.getBlock() instanceof BaseFireBlock)) { this.levelEvent(2001, pos, Block.getId(blockState)); } if (dropBlock) { BlockEntity blockEntity = blockState.hasBlockEntity() ? this.getBlockEntity(pos) : null; Block.dropResources(blockState, this, pos, blockEntity, entity, ItemStack.EMPTY); } boolean bl = this.setBlock(pos, fluidState.createLegacyBlock(), 3, recursionLeft); if (bl) { this.gameEvent(GameEvent.BLOCK_DESTROY, pos, Context.of(entity, blockState)); } return bl; } } public void addDestroyBlockEffect(BlockPos pos, BlockState state) { } /** * Convenience method to update the block on both the client and server */ public boolean setBlockAndUpdate(BlockPos pos, BlockState state) { return this.setBlock(pos, state, 3); } /** * Flags are as in setBlockState */ public abstract void sendBlockUpdated(BlockPos pos, BlockState oldState, BlockState newState, int flags); public void setBlocksDirty(BlockPos blockPos, BlockState oldState, BlockState newState) { } public void updateNeighborsAt(BlockPos pos, Block block, @Nullable Orientation orientation) { } public void updateNeighborsAtExceptFromFacing(BlockPos pos, Block block, Direction facing, @Nullable Orientation orientation) { } public void neighborChanged(BlockPos pos, Block block, @Nullable Orientation orientation) { } public void neighborChanged(BlockState state, BlockPos pos, Block block, @Nullable Orientation orientation, boolean movedByPiston) { } @Override public void neighborShapeChanged(Direction direction, BlockPos pos, BlockPos neighborPos, BlockState neighborState, int flags, int recursionLeft) { this.neighborUpdater.shapeUpdate(direction, neighborState, pos, neighborPos, flags, recursionLeft); } @Override public int getHeight(Heightmap.Types heightmapType, int x, int z) { int i; if (x >= -30000000 && z >= -30000000 && x < 30000000 && z < 30000000) { if (this.hasChunk(SectionPos.blockToSectionCoord(x), SectionPos.blockToSectionCoord(z))) { i = this.getChunk(SectionPos.blockToSectionCoord(x), SectionPos.blockToSectionCoord(z)).getHeight(heightmapType, x & 15, z & 15) + 1; } else { i = this.getMinY(); } } else { i = this.getSeaLevel() + 1; } return i; } @Override public LevelLightEngine getLightEngine() { return this.getChunkSource().getLightEngine(); } @Override public BlockState getBlockState(BlockPos pos) { if (this.isOutsideBuildHeight(pos)) { return Blocks.VOID_AIR.defaultBlockState(); } else { LevelChunk levelChunk = this.getChunk(SectionPos.blockToSectionCoord(pos.getX()), SectionPos.blockToSectionCoord(pos.getZ())); return levelChunk.getBlockState(pos); } } @Override public FluidState getFluidState(BlockPos pos) { if (this.isOutsideBuildHeight(pos)) { return Fluids.EMPTY.defaultFluidState(); } else { LevelChunk levelChunk = this.getChunkAt(pos); return levelChunk.getFluidState(pos); } } public boolean isBrightOutside() { return !this.dimensionType().hasFixedTime() && this.skyDarken < 4; } public boolean isDarkOutside() { return !this.dimensionType().hasFixedTime() && !this.isBrightOutside(); } public boolean isMoonVisible() { if (!this.dimensionType().natural()) { return false; } else { int i = (int)(this.getDayTime() % 24000L); return i >= 12600 && i <= 23400; } } @Override public void playSound(@Nullable Entity entity, BlockPos pos, SoundEvent sound, SoundSource source, float volume, float pitch) { this.playSound(entity, pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5, sound, source, volume, pitch); } public abstract void playSeededSound( @Nullable Entity entity, double x, double y, double z, Holder sound, SoundSource source, float volume, float pitch, long seed ); public void playSeededSound(@Nullable Entity entity, double x, double y, double z, SoundEvent sound, SoundSource source, float volume, float pitch, long seed) { this.playSeededSound(entity, x, y, z, BuiltInRegistries.SOUND_EVENT.wrapAsHolder(sound), source, volume, pitch, seed); } public abstract void playSeededSound( @Nullable Entity entity, Entity sourceEntity, Holder sound, SoundSource source, float volume, float pitch, long seed ); public void playSound(@Nullable Entity entity, double x, double y, double z, SoundEvent sound, SoundSource source) { this.playSound(entity, x, y, z, sound, source, 1.0F, 1.0F); } public void playSound(@Nullable Entity entity, double x, double y, double z, SoundEvent sound, SoundSource source, float volume, float pitch) { this.playSeededSound(entity, x, y, z, sound, source, volume, pitch, this.threadSafeRandom.nextLong()); } public void playSound(@Nullable Entity entity, double x, double y, double z, Holder sound, SoundSource source, float volume, float pitch) { this.playSeededSound(entity, x, y, z, sound, source, volume, pitch, this.threadSafeRandom.nextLong()); } public void playSound(@Nullable Entity entity, Entity sourceEntity, SoundEvent sound, SoundSource source, float volume, float pitch) { this.playSeededSound(entity, sourceEntity, BuiltInRegistries.SOUND_EVENT.wrapAsHolder(sound), source, volume, pitch, this.threadSafeRandom.nextLong()); } public void playLocalSound(BlockPos pos, SoundEvent sound, SoundSource source, float volume, float pitch, boolean distanceDelay) { this.playLocalSound(pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5, sound, source, volume, pitch, distanceDelay); } public void playLocalSound(Entity entity, SoundEvent sound, SoundSource source, float volume, float pitch) { } public void playLocalSound(double x, double y, double z, SoundEvent sound, SoundSource source, float volume, float pitch, boolean distanceDelay) { } public void playPlayerSound(SoundEvent sound, SoundSource source, float volume, float pitch) { } @Override public void addParticle(ParticleOptions particle, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed) { } public void addParticle( ParticleOptions particle, boolean overrideLimiter, boolean alwaysShow, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed ) { } public void addAlwaysVisibleParticle(ParticleOptions particle, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed) { } public void addAlwaysVisibleParticle(ParticleOptions particle, boolean ignoreRange, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed) { } /** * Return getCelestialAngle()*2*PI */ public float getSunAngle(float partialTick) { float f = this.getTimeOfDay(partialTick); return f * (float) (Math.PI * 2); } public void addBlockEntityTicker(TickingBlockEntity ticker) { (this.tickingBlockEntities ? this.pendingBlockEntityTickers : this.blockEntityTickers).add(ticker); } protected void tickBlockEntities() { ProfilerFiller profilerFiller = Profiler.get(); profilerFiller.push("blockEntities"); this.tickingBlockEntities = true; if (!this.pendingBlockEntityTickers.isEmpty()) { this.blockEntityTickers.addAll(this.pendingBlockEntityTickers); this.pendingBlockEntityTickers.clear(); } Iterator iterator = this.blockEntityTickers.iterator(); boolean bl = this.tickRateManager().runsNormally(); while (iterator.hasNext()) { TickingBlockEntity tickingBlockEntity = (TickingBlockEntity)iterator.next(); if (tickingBlockEntity.isRemoved()) { iterator.remove(); } else if (bl && this.shouldTickBlocksAt(tickingBlockEntity.getPos())) { tickingBlockEntity.tick(); } } this.tickingBlockEntities = false; profilerFiller.pop(); } public void guardEntityTick(Consumer consumerEntity, T entity) { try { consumerEntity.accept(entity); } catch (Throwable var6) { CrashReport crashReport = CrashReport.forThrowable(var6, "Ticking entity"); CrashReportCategory crashReportCategory = crashReport.addCategory("Entity being ticked"); entity.fillCrashReportCategory(crashReportCategory); throw new ReportedException(crashReport); } } public boolean shouldTickDeath(Entity entity) { return true; } public boolean shouldTickBlocksAt(long chunkPos) { return true; } public boolean shouldTickBlocksAt(BlockPos pos) { return this.shouldTickBlocksAt(ChunkPos.asLong(pos)); } public void explode(@Nullable Entity source, double x, double y, double z, float radius, Level.ExplosionInteraction explosionInteraction) { this.explode( source, Explosion.getDefaultDamageSource(this, source), null, x, y, z, radius, false, explosionInteraction, ParticleTypes.EXPLOSION, ParticleTypes.EXPLOSION_EMITTER, SoundEvents.GENERIC_EXPLODE ); } public void explode(@Nullable Entity source, double x, double y, double z, float radius, boolean fire, Level.ExplosionInteraction explosionInteraction) { this.explode( source, Explosion.getDefaultDamageSource(this, source), null, x, y, z, radius, fire, explosionInteraction, ParticleTypes.EXPLOSION, ParticleTypes.EXPLOSION_EMITTER, SoundEvents.GENERIC_EXPLODE ); } public void explode( @Nullable Entity source, @Nullable DamageSource damageSource, @Nullable ExplosionDamageCalculator damageCalculator, Vec3 pos, float radius, boolean fire, Level.ExplosionInteraction explosionInteraction ) { this.explode( source, damageSource, damageCalculator, pos.x(), pos.y(), pos.z(), radius, fire, explosionInteraction, ParticleTypes.EXPLOSION, ParticleTypes.EXPLOSION_EMITTER, SoundEvents.GENERIC_EXPLODE ); } public void explode( @Nullable Entity source, @Nullable DamageSource damageSource, @Nullable ExplosionDamageCalculator damageCalculator, double x, double y, double z, float radius, boolean fire, Level.ExplosionInteraction explosionInteraction ) { this.explode( source, damageSource, damageCalculator, x, y, z, radius, fire, explosionInteraction, ParticleTypes.EXPLOSION, ParticleTypes.EXPLOSION_EMITTER, SoundEvents.GENERIC_EXPLODE ); } public abstract void explode( @Nullable Entity source, @Nullable DamageSource damageSource, @Nullable ExplosionDamageCalculator damageCalculator, double x, double y, double z, float radius, boolean fire, Level.ExplosionInteraction explosionInteraction, ParticleOptions smallExplosionParticles, ParticleOptions largeExplosionParticles, Holder explosionSound ); /** * Returns the name of the current chunk provider, by calling chunkprovider.makeString() */ public abstract String gatherChunkSourceStats(); @Nullable @Override public BlockEntity getBlockEntity(BlockPos pos) { if (this.isOutsideBuildHeight(pos)) { return null; } else { return !this.isClientSide && Thread.currentThread() != this.thread ? null : this.getChunkAt(pos).getBlockEntity(pos, LevelChunk.EntityCreationType.IMMEDIATE); } } public void setBlockEntity(BlockEntity blockEntity) { BlockPos blockPos = blockEntity.getBlockPos(); if (!this.isOutsideBuildHeight(blockPos)) { this.getChunkAt(blockPos).addAndRegisterBlockEntity(blockEntity); } } public void removeBlockEntity(BlockPos pos) { if (!this.isOutsideBuildHeight(pos)) { this.getChunkAt(pos).removeBlockEntity(pos); } } public boolean isLoaded(BlockPos pos) { return this.isOutsideBuildHeight(pos) ? false : this.getChunkSource().hasChunk(SectionPos.blockToSectionCoord(pos.getX()), SectionPos.blockToSectionCoord(pos.getZ())); } public boolean loadedAndEntityCanStandOnFace(BlockPos pos, Entity entity, Direction direction) { if (this.isOutsideBuildHeight(pos)) { return false; } else { ChunkAccess chunkAccess = this.getChunk(SectionPos.blockToSectionCoord(pos.getX()), SectionPos.blockToSectionCoord(pos.getZ()), ChunkStatus.FULL, false); return chunkAccess == null ? false : chunkAccess.getBlockState(pos).entityCanStandOnFace(this, pos, entity, direction); } } public boolean loadedAndEntityCanStandOn(BlockPos pos, Entity entity) { return this.loadedAndEntityCanStandOnFace(pos, entity, Direction.UP); } /** * Called on the construction of the {@code Level} class to set up the initial skylight values. */ public void updateSkyBrightness() { double d = 1.0 - this.getRainLevel(1.0F) * 5.0F / 16.0; double e = 1.0 - this.getThunderLevel(1.0F) * 5.0F / 16.0; double f = 0.5 + 2.0 * Mth.clamp((double)Mth.cos(this.getTimeOfDay(1.0F) * (float) (Math.PI * 2)), -0.25, 0.25); this.skyDarken = (int)((1.0 - f * d * e) * 11.0); } public void setSpawnSettings(boolean spawnSettings) { this.getChunkSource().setSpawnSettings(spawnSettings); } public BlockPos getSharedSpawnPos() { BlockPos blockPos = this.levelData.getSpawnPos(); if (!this.getWorldBorder().isWithinBounds(blockPos)) { blockPos = this.getHeightmapPos( Heightmap.Types.MOTION_BLOCKING, BlockPos.containing(this.getWorldBorder().getCenterX(), 0.0, this.getWorldBorder().getCenterZ()) ); } return blockPos; } public float getSharedSpawnAngle() { return this.levelData.getSpawnAngle(); } /** * Called from World constructor to set rainingStrength and thunderingStrength */ protected void prepareWeather() { if (this.levelData.isRaining()) { this.rainLevel = 1.0F; if (this.levelData.isThundering()) { this.thunderLevel = 1.0F; } } } public void close() throws IOException { this.getChunkSource().close(); } @Nullable @Override public BlockGetter getChunkForCollisions(int chunkX, int chunkZ) { return this.getChunk(chunkX, chunkZ, ChunkStatus.FULL, false); } @Override public List getEntities(@Nullable Entity entity, AABB area, Predicate predicate) { Profiler.get().incrementCounter("getEntities"); List list = Lists.newArrayList(); this.getEntities().get(area, entity2 -> { if (entity2 != entity && predicate.test(entity2)) { list.add(entity2); } }); for (EnderDragonPart enderDragonPart : this.dragonParts()) { if (enderDragonPart != entity && enderDragonPart.parentMob != entity && predicate.test(enderDragonPart) && area.intersects(enderDragonPart.getBoundingBox()) ) { list.add(enderDragonPart); } } return list; } @Override public List getEntities(EntityTypeTest entityTypeTest, AABB bounds, Predicate predicate) { List list = Lists.newArrayList(); this.getEntities(entityTypeTest, bounds, predicate, list); return list; } public void getEntities(EntityTypeTest entityTypeTest, AABB bounds, Predicate predicate, List output) { this.getEntities(entityTypeTest, bounds, predicate, output, Integer.MAX_VALUE); } public void getEntities( EntityTypeTest entityTypeTest, AABB bounds, Predicate predicate, List output, int maxResults ) { Profiler.get().incrementCounter("getEntities"); this.getEntities().get(entityTypeTest, bounds, entity -> { if (predicate.test(entity)) { output.add(entity); if (output.size() >= maxResults) { return Continuation.ABORT; } } if (entity instanceof EnderDragon enderDragon) { for (EnderDragonPart enderDragonPart : enderDragon.getSubEntities()) { T entity2 = entityTypeTest.tryCast(enderDragonPart); if (entity2 != null && predicate.test(entity2)) { output.add(entity2); if (output.size() >= maxResults) { return Continuation.ABORT; } } } } return Continuation.CONTINUE; }); } public List getPushableEntities(Entity entity, AABB boundingBox) { return this.getEntities(entity, boundingBox, EntitySelector.pushableBy(entity)); } /** * Returns the Entity with the given ID, or null if it doesn't exist in this Level. */ @Nullable public abstract Entity getEntity(int id); @Nullable public Entity getEntity(UUID uUID) { return this.getEntities().get(uUID); } public abstract Collection dragonParts(); public void blockEntityChanged(BlockPos pos) { if (this.hasChunkAt(pos)) { this.getChunkAt(pos).markUnsaved(); } } /** * If on MP, sends a quitting packet. */ public void disconnect() { } public long getGameTime() { return this.levelData.getGameTime(); } public long getDayTime() { return this.levelData.getDayTime(); } public boolean mayInteract(Entity entity, BlockPos pos) { return true; } /** * Sends a {@link net.minecraft.network.protocol.game.ClientboundEntityEventPacket} to all tracked players of that entity. */ public void broadcastEntityEvent(Entity entity, byte state) { } public void broadcastDamageEvent(Entity entity, DamageSource damageSource) { } public void blockEvent(BlockPos pos, Block block, int eventID, int eventParam) { this.getBlockState(pos).triggerEvent(this, pos, eventID, eventParam); } @Override public LevelData getLevelData() { return this.levelData; } public abstract TickRateManager tickRateManager(); public float getThunderLevel(float partialTick) { return Mth.lerp(partialTick, this.oThunderLevel, this.thunderLevel) * this.getRainLevel(partialTick); } /** * Sets the strength of the thunder. */ public void setThunderLevel(float strength) { float f = Mth.clamp(strength, 0.0F, 1.0F); this.oThunderLevel = f; this.thunderLevel = f; } /** * Returns rain strength. */ public float getRainLevel(float partialTick) { return Mth.lerp(partialTick, this.oRainLevel, this.rainLevel); } /** * Sets the strength of the rain. */ public void setRainLevel(float strength) { float f = Mth.clamp(strength, 0.0F, 1.0F); this.oRainLevel = f; this.rainLevel = f; } private boolean canHaveWeather() { return this.dimensionType().hasSkyLight() && !this.dimensionType().hasCeiling(); } /** * Returns {@code true} if the current thunder strength (weighted with the rain strength) is greater than 0.9 */ public boolean isThundering() { return this.canHaveWeather() && this.getThunderLevel(1.0F) > 0.9; } /** * Returns {@code true} if the current rain strength is greater than 0.2 */ public boolean isRaining() { return this.canHaveWeather() && this.getRainLevel(1.0F) > 0.2; } /** * Check if precipitation is currently happening at a position */ public boolean isRainingAt(BlockPos pos) { if (!this.isRaining()) { return false; } else if (!this.canSeeSky(pos)) { return false; } else if (this.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, pos).getY() > pos.getY()) { return false; } else { Biome biome = this.getBiome(pos).value(); return biome.getPrecipitationAt(pos, this.getSeaLevel()) == Biome.Precipitation.RAIN; } } @Nullable public abstract MapItemSavedData getMapData(MapId mapId); public void globalLevelEvent(int id, BlockPos pos, int data) { } /** * Adds some basic stats of the world to the given crash report. */ public CrashReportCategory fillReportDetails(CrashReport report) { CrashReportCategory crashReportCategory = report.addCategory("Affected level", 1); crashReportCategory.setDetail("All players", (CrashReportDetail)(() -> { List list = this.players(); return list.size() + " total; " + (String)list.stream().map(Player::debugInfo).collect(Collectors.joining(", ")); })); crashReportCategory.setDetail("Chunk stats", this.getChunkSource()::gatherStats); crashReportCategory.setDetail("Level dimension", (CrashReportDetail)(() -> this.dimension().location().toString())); try { this.levelData.fillCrashReportCategory(crashReportCategory, this); } catch (Throwable var4) { crashReportCategory.setDetailError("Level Data Unobtainable", var4); } return crashReportCategory; } public abstract void destroyBlockProgress(int breakerId, BlockPos pos, int progress); public void createFireworks(double x, double y, double z, double xSpeed, double ySpeed, double zSpeed, List explosions) { } public abstract Scoreboard getScoreboard(); public void updateNeighbourForOutputSignal(BlockPos pos, Block block) { for (Direction direction : Direction.Plane.HORIZONTAL) { BlockPos blockPos = pos.relative(direction); if (this.hasChunkAt(blockPos)) { BlockState blockState = this.getBlockState(blockPos); if (blockState.is(Blocks.COMPARATOR)) { this.neighborChanged(blockState, blockPos, block, null, false); } else if (blockState.isRedstoneConductor(this, blockPos)) { blockPos = blockPos.relative(direction); blockState = this.getBlockState(blockPos); if (blockState.is(Blocks.COMPARATOR)) { this.neighborChanged(blockState, blockPos, block, null, false); } } } } } @Override public DifficultyInstance getCurrentDifficultyAt(BlockPos pos) { long l = 0L; float f = 0.0F; if (this.hasChunkAt(pos)) { f = this.getMoonBrightness(); l = this.getChunkAt(pos).getInhabitedTime(); } return new DifficultyInstance(this.getDifficulty(), this.getDayTime(), l, f); } @Override public int getSkyDarken() { return this.skyDarken; } public void setSkyFlashTime(int timeFlash) { } @Override public WorldBorder getWorldBorder() { return this.worldBorder; } public void sendPacketToServer(Packet packet) { throw new UnsupportedOperationException("Can't send packets to server unless you're on the client."); } @Override public DimensionType dimensionType() { return this.dimensionTypeRegistration.value(); } public Holder dimensionTypeRegistration() { return this.dimensionTypeRegistration; } public ResourceKey dimension() { return this.dimension; } @Override public RandomSource getRandom() { return this.random; } @Override public boolean isStateAtPosition(BlockPos pos, Predicate state) { return state.test(this.getBlockState(pos)); } @Override public boolean isFluidAtPosition(BlockPos pos, Predicate predicate) { return predicate.test(this.getFluidState(pos)); } public abstract RecipeAccess recipeAccess(); public BlockPos getBlockRandomPos(int x, int y, int z, int yMask) { this.randValue = this.randValue * 3 + 1013904223; int i = this.randValue >> 2; return new BlockPos(x + (i & 15), y + (i >> 16 & yMask), z + (i >> 8 & 15)); } public boolean noSave() { return false; } @Override public BiomeManager getBiomeManager() { return this.biomeManager; } public final boolean isDebug() { return this.isDebug; } protected abstract LevelEntityGetter getEntities(); @Override public long nextSubTickCount() { return this.subTickCount++; } @Override public RegistryAccess registryAccess() { return this.registryAccess; } public DamageSources damageSources() { return this.damageSources; } public abstract PotionBrewing potionBrewing(); public abstract FuelValues fuelValues(); public int getClientLeafTintColor(BlockPos pos) { return 0; } public static enum ExplosionInteraction implements StringRepresentable { NONE("none"), BLOCK("block"), MOB("mob"), TNT("tnt"), TRIGGER("trigger"); public static final Codec CODEC = StringRepresentable.fromEnum(Level.ExplosionInteraction::values); private final String id; private ExplosionInteraction(final String id) { this.id = id; } @Override public String getSerializedName() { return this.id; } } }