package net.minecraft.server.level; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Lists; import com.mojang.datafixers.DataFixer; import com.mojang.datafixers.util.Pair; import com.mojang.logging.LogUtils; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.longs.LongSet; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet; import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import it.unimi.dsi.fastutil.objects.Object2IntMap.Entry; import java.io.IOException; import java.io.Writer; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.List; import java.util.Locale; import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.Executor; import java.util.function.BooleanSupplier; import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; import net.minecraft.CrashReport; import net.minecraft.CrashReportCategory; import net.minecraft.CrashReportDetail; import net.minecraft.ReportType; import net.minecraft.Util; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.Holder; import net.minecraft.core.HolderSet; import net.minecraft.core.SectionPos; import net.minecraft.core.HolderSet.Named; import net.minecraft.core.particles.ParticleOptions; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.Registries; import net.minecraft.network.chat.Component; import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.game.ClientboundBlockDestructionPacket; import net.minecraft.network.protocol.game.ClientboundBlockEventPacket; import net.minecraft.network.protocol.game.ClientboundDamageEventPacket; import net.minecraft.network.protocol.game.ClientboundEntityEventPacket; import net.minecraft.network.protocol.game.ClientboundExplodePacket; import net.minecraft.network.protocol.game.ClientboundGameEventPacket; import net.minecraft.network.protocol.game.ClientboundLevelEventPacket; import net.minecraft.network.protocol.game.ClientboundLevelParticlesPacket; import net.minecraft.network.protocol.game.ClientboundSetDefaultSpawnPositionPacket; import net.minecraft.network.protocol.game.ClientboundSoundEntityPacket; import net.minecraft.network.protocol.game.ClientboundSoundPacket; import net.minecraft.network.protocol.game.DebugPackets; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; import net.minecraft.server.ServerScoreboard; import net.minecraft.server.level.progress.ChunkProgressListener; import net.minecraft.server.players.SleepStatus; import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundSource; import net.minecraft.tags.TagKey; import net.minecraft.util.CsvOutput; import net.minecraft.util.Mth; import net.minecraft.util.ProgressListener; import net.minecraft.util.RandomSource; import net.minecraft.util.AbortableIterationConsumer.Continuation; import net.minecraft.util.datafix.DataFixTypes; import net.minecraft.util.profiling.Profiler; import net.minecraft.util.profiling.ProfilerFiller; import net.minecraft.util.valueproviders.IntProvider; import net.minecraft.util.valueproviders.UniformInt; import net.minecraft.world.DifficultyInstance; import net.minecraft.world.RandomSequences; import net.minecraft.world.TickRateManager; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntitySpawnReason; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.LightningBolt; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.MobCategory; import net.minecraft.world.entity.ReputationEventHandler; import net.minecraft.world.entity.ai.navigation.PathNavigation; import net.minecraft.world.entity.ai.village.ReputationEventType; import net.minecraft.world.entity.ai.village.poi.PoiManager; import net.minecraft.world.entity.ai.village.poi.PoiType; import net.minecraft.world.entity.ai.village.poi.PoiTypes; import net.minecraft.world.entity.ai.village.poi.PoiManager.Occupancy; import net.minecraft.world.entity.animal.horse.SkeletonHorse; 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.entity.raid.Raid; import net.minecraft.world.entity.raid.Raids; import net.minecraft.world.flag.FeatureFlagSet; import net.minecraft.world.item.alchemy.PotionBrewing; import net.minecraft.world.item.crafting.RecipeManager; import net.minecraft.world.level.BlockEventData; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.CustomSpawner; import net.minecraft.world.level.ExplosionDamageCalculator; import net.minecraft.world.level.GameRules; import net.minecraft.world.level.Level; import net.minecraft.world.level.NaturalSpawner; import net.minecraft.world.level.ServerExplosion; import net.minecraft.world.level.StructureManager; import net.minecraft.world.level.WorldGenLevel; import net.minecraft.world.level.Explosion.BlockInteraction; import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.SnowLayerBlock; 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.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkGenerator; import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.chunk.LevelChunkSection; import net.minecraft.world.level.chunk.storage.EntityStorage; import net.minecraft.world.level.chunk.storage.RegionStorageInfo; import net.minecraft.world.level.chunk.storage.SimpleRegionStorage; import net.minecraft.world.level.dimension.BuiltinDimensionTypes; import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.dimension.end.EndDragonFight; import net.minecraft.world.level.entity.EntityPersistentStorage; import net.minecraft.world.level.entity.EntityTickList; import net.minecraft.world.level.entity.EntityTypeTest; import net.minecraft.world.level.entity.LevelCallback; import net.minecraft.world.level.entity.LevelEntityGetter; import net.minecraft.world.level.entity.PersistentEntitySectionManager; import net.minecraft.world.level.gameevent.DynamicGameEventListener; import net.minecraft.world.level.gameevent.GameEvent; import net.minecraft.world.level.gameevent.GameEventDispatcher; import net.minecraft.world.level.gameevent.GameEvent.Context; import net.minecraft.world.level.levelgen.Heightmap; import net.minecraft.world.level.levelgen.structure.BoundingBox; import net.minecraft.world.level.levelgen.structure.Structure; import net.minecraft.world.level.levelgen.structure.StructureCheck; import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; import net.minecraft.world.level.material.Fluid; import net.minecraft.world.level.material.FluidState; import net.minecraft.world.level.pathfinder.PathTypeCache; import net.minecraft.world.level.portal.PortalForcer; import net.minecraft.world.level.redstone.ExperimentalRedstoneUtils; import net.minecraft.world.level.redstone.Orientation; import net.minecraft.world.level.saveddata.maps.MapId; import net.minecraft.world.level.saveddata.maps.MapIndex; import net.minecraft.world.level.saveddata.maps.MapItemSavedData; import net.minecraft.world.level.storage.DimensionDataStorage; import net.minecraft.world.level.storage.LevelStorageSource; import net.minecraft.world.level.storage.ServerLevelData; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.shapes.BooleanOp; import net.minecraft.world.phys.shapes.Shapes; import net.minecraft.world.phys.shapes.VoxelShape; import net.minecraft.world.ticks.LevelTicks; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLevel { public static final BlockPos END_SPAWN_POINT = new BlockPos(100, 50, 0); public static final IntProvider RAIN_DELAY = UniformInt.of(12000, 180000); public static final IntProvider RAIN_DURATION = UniformInt.of(12000, 24000); private static final IntProvider THUNDER_DELAY = UniformInt.of(12000, 180000); public static final IntProvider THUNDER_DURATION = UniformInt.of(3600, 15600); private static final Logger LOGGER = LogUtils.getLogger(); private static final int EMPTY_TIME_NO_TICK = 300; private static final int MAX_SCHEDULED_TICKS_PER_TICK = 65536; final List players = Lists.newArrayList(); private final ServerChunkCache chunkSource; private final MinecraftServer server; private final ServerLevelData serverLevelData; private int lastSpawnChunkRadius; final EntityTickList entityTickList = new EntityTickList(); private final PersistentEntitySectionManager entityManager; private final GameEventDispatcher gameEventDispatcher; public boolean noSave; private final SleepStatus sleepStatus; private int emptyTime; private final PortalForcer portalForcer; private final LevelTicks blockTicks = new LevelTicks<>(this::isPositionTickingWithEntitiesLoaded); private final LevelTicks fluidTicks = new LevelTicks<>(this::isPositionTickingWithEntitiesLoaded); private final PathTypeCache pathTypesByPosCache = new PathTypeCache(); final Set navigatingMobs = new ObjectOpenHashSet<>(); volatile boolean isUpdatingNavigations; protected final Raids raids; private final ObjectLinkedOpenHashSet blockEvents = new ObjectLinkedOpenHashSet<>(); private final List blockEventsToReschedule = new ArrayList(64); private boolean handlingTick; private final List customSpawners; @Nullable private EndDragonFight dragonFight; final Int2ObjectMap dragonParts = new Int2ObjectOpenHashMap<>(); private final StructureManager structureManager; private final StructureCheck structureCheck; private final boolean tickTime; private final RandomSequences randomSequences; public ServerLevel( MinecraftServer server, Executor dispatcher, LevelStorageSource.LevelStorageAccess levelStorageAccess, ServerLevelData serverLevelData, ResourceKey dimension, LevelStem levelStem, ChunkProgressListener progressListener, boolean isDebug, long biomeZoomSeed, List customSpawners, boolean tickTime, @Nullable RandomSequences randomSequences ) { super(serverLevelData, dimension, server.registryAccess(), levelStem.type(), false, isDebug, biomeZoomSeed, server.getMaxChainedNeighborUpdates()); this.tickTime = tickTime; this.server = server; this.customSpawners = customSpawners; this.serverLevelData = serverLevelData; ChunkGenerator chunkGenerator = levelStem.generator(); boolean bl = server.forceSynchronousWrites(); DataFixer dataFixer = server.getFixerUpper(); EntityPersistentStorage entityPersistentStorage = new EntityStorage( new SimpleRegionStorage( new RegionStorageInfo(levelStorageAccess.getLevelId(), dimension, "entities"), levelStorageAccess.getDimensionPath(dimension).resolve("entities"), dataFixer, bl, DataFixTypes.ENTITY_CHUNK ), this, server ); this.entityManager = new PersistentEntitySectionManager<>(Entity.class, new ServerLevel.EntityCallbacks(), entityPersistentStorage); this.chunkSource = new ServerChunkCache( this, levelStorageAccess, dataFixer, server.getStructureManager(), dispatcher, chunkGenerator, server.getPlayerList().getViewDistance(), server.getPlayerList().getSimulationDistance(), bl, progressListener, this.entityManager::updateChunkStatus, () -> server.overworld().getDataStorage() ); this.chunkSource.getGeneratorState().ensureStructuresGenerated(); this.portalForcer = new PortalForcer(this); this.updateSkyBrightness(); this.prepareWeather(); this.getWorldBorder().setAbsoluteMaxSize(server.getAbsoluteMaxWorldSize()); this.raids = this.getDataStorage().computeIfAbsent(Raids.getType(this.dimensionTypeRegistration())); if (!server.isSingleplayer()) { serverLevelData.setGameType(server.getDefaultGameType()); } long l = server.getWorldData().worldGenOptions().seed(); this.structureCheck = new StructureCheck( this.chunkSource.chunkScanner(), this.registryAccess(), server.getStructureManager(), dimension, chunkGenerator, this.chunkSource.randomState(), this, chunkGenerator.getBiomeSource(), l, dataFixer ); this.structureManager = new StructureManager(this, server.getWorldData().worldGenOptions(), this.structureCheck); if (this.dimension() == Level.END && this.dimensionTypeRegistration().is(BuiltinDimensionTypes.END)) { this.dragonFight = new EndDragonFight(this, l, server.getWorldData().endDragonFightData()); } else { this.dragonFight = null; } this.sleepStatus = new SleepStatus(); this.gameEventDispatcher = new GameEventDispatcher(this); this.randomSequences = (RandomSequences)Objects.requireNonNullElseGet(randomSequences, () -> this.getDataStorage().computeIfAbsent(RandomSequences.TYPE)); } @Deprecated @VisibleForTesting public void setDragonFight(@Nullable EndDragonFight dragonFight) { this.dragonFight = dragonFight; } public void setWeatherParameters(int clearTime, int weatherTime, boolean isRaining, boolean isThundering) { this.serverLevelData.setClearWeatherTime(clearTime); this.serverLevelData.setRainTime(weatherTime); this.serverLevelData.setThunderTime(weatherTime); this.serverLevelData.setRaining(isRaining); this.serverLevelData.setThundering(isThundering); } @Override public Holder getUncachedNoiseBiome(int x, int y, int z) { return this.getChunkSource().getGenerator().getBiomeSource().getNoiseBiome(x, y, z, this.getChunkSource().randomState().sampler()); } public StructureManager structureManager() { return this.structureManager; } /** * Runs a single tick for the world */ public void tick(BooleanSupplier hasTimeLeft) { ProfilerFiller profilerFiller = Profiler.get(); this.handlingTick = true; TickRateManager tickRateManager = this.tickRateManager(); boolean bl = tickRateManager.runsNormally(); if (bl) { profilerFiller.push("world border"); this.getWorldBorder().tick(); profilerFiller.popPush("weather"); this.advanceWeatherCycle(); profilerFiller.pop(); } int i = this.getGameRules().getInt(GameRules.RULE_PLAYERS_SLEEPING_PERCENTAGE); if (this.sleepStatus.areEnoughSleeping(i) && this.sleepStatus.areEnoughDeepSleeping(i, this.players)) { if (this.getGameRules().getBoolean(GameRules.RULE_DAYLIGHT)) { long l = this.levelData.getDayTime() + 24000L; this.setDayTime(l - l % 24000L); } this.wakeUpAllPlayers(); if (this.getGameRules().getBoolean(GameRules.RULE_WEATHER_CYCLE) && this.isRaining()) { this.resetWeatherCycle(); } } this.updateSkyBrightness(); if (bl) { this.tickTime(); } profilerFiller.push("tickPending"); if (!this.isDebug() && bl) { long l = this.getGameTime(); profilerFiller.push("blockTicks"); this.blockTicks.tick(l, 65536, this::tickBlock); profilerFiller.popPush("fluidTicks"); this.fluidTicks.tick(l, 65536, this::tickFluid); profilerFiller.pop(); } profilerFiller.popPush("raid"); if (bl) { this.raids.tick(this); } profilerFiller.popPush("chunkSource"); this.getChunkSource().tick(hasTimeLeft, true); profilerFiller.popPush("blockEvents"); if (bl) { this.runBlockEvents(); } this.handlingTick = false; profilerFiller.pop(); boolean bl2 = !this.players.isEmpty() || !this.getForceLoadedChunks().isEmpty(); if (bl2) { this.resetEmptyTime(); } if (bl2 || this.emptyTime++ < 300) { profilerFiller.push("entities"); if (this.dragonFight != null && bl) { profilerFiller.push("dragonFight"); this.dragonFight.tick(); profilerFiller.pop(); } this.entityTickList.forEach(entity -> { if (!entity.isRemoved()) { if (!tickRateManager.isEntityFrozen(entity)) { profilerFiller.push("checkDespawn"); entity.checkDespawn(); profilerFiller.pop(); if (entity instanceof ServerPlayer || this.chunkSource.chunkMap.getDistanceManager().inEntityTickingRange(entity.chunkPosition().toLong())) { Entity entity2 = entity.getVehicle(); if (entity2 != null) { if (!entity2.isRemoved() && entity2.hasPassenger(entity)) { return; } entity.stopRiding(); } profilerFiller.push("tick"); this.guardEntityTick(this::tickNonPassenger, entity); profilerFiller.pop(); } } } }); profilerFiller.pop(); this.tickBlockEntities(); } profilerFiller.push("entityManagement"); this.entityManager.tick(); profilerFiller.pop(); } @Override public boolean shouldTickBlocksAt(long chunkPos) { return this.chunkSource.chunkMap.getDistanceManager().inBlockTickingRange(chunkPos); } protected void tickTime() { if (this.tickTime) { long l = this.levelData.getGameTime() + 1L; this.serverLevelData.setGameTime(l); Profiler.get().push("scheduledFunctions"); this.serverLevelData.getScheduledEvents().tick(this.server, l); Profiler.get().pop(); if (this.serverLevelData.getGameRules().getBoolean(GameRules.RULE_DAYLIGHT)) { this.setDayTime(this.levelData.getDayTime() + 1L); } } } public void setDayTime(long time) { this.serverLevelData.setDayTime(time); } public void tickCustomSpawners(boolean spawnEnemies, boolean spawnFriendlies) { for (CustomSpawner customSpawner : this.customSpawners) { customSpawner.tick(this, spawnEnemies, spawnFriendlies); } } private void wakeUpAllPlayers() { this.sleepStatus.removeAllSleepers(); ((List)this.players.stream().filter(LivingEntity::isSleeping).collect(Collectors.toList())) .forEach(serverPlayer -> serverPlayer.stopSleepInBed(false, false)); } public void tickChunk(LevelChunk chunk, int randomTickSpeed) { ChunkPos chunkPos = chunk.getPos(); int i = chunkPos.getMinBlockX(); int j = chunkPos.getMinBlockZ(); ProfilerFiller profilerFiller = Profiler.get(); profilerFiller.push("iceandsnow"); for (int k = 0; k < randomTickSpeed; k++) { if (this.random.nextInt(48) == 0) { this.tickPrecipitation(this.getBlockRandomPos(i, 0, j, 15)); } } profilerFiller.popPush("tickBlocks"); if (randomTickSpeed > 0) { LevelChunkSection[] levelChunkSections = chunk.getSections(); for (int l = 0; l < levelChunkSections.length; l++) { LevelChunkSection levelChunkSection = levelChunkSections[l]; if (levelChunkSection.isRandomlyTicking()) { int m = chunk.getSectionYFromSectionIndex(l); int n = SectionPos.sectionToBlockCoord(m); for (int o = 0; o < randomTickSpeed; o++) { BlockPos blockPos = this.getBlockRandomPos(i, n, j, 15); profilerFiller.push("randomTick"); BlockState blockState = levelChunkSection.getBlockState(blockPos.getX() - i, blockPos.getY() - n, blockPos.getZ() - j); if (blockState.isRandomlyTicking()) { blockState.randomTick(this, blockPos, this.random); } FluidState fluidState = blockState.getFluidState(); if (fluidState.isRandomlyTicking()) { fluidState.randomTick(this, blockPos, this.random); } profilerFiller.pop(); } } } } profilerFiller.pop(); } public void tickThunder(LevelChunk chunk) { ChunkPos chunkPos = chunk.getPos(); boolean bl = this.isRaining(); int i = chunkPos.getMinBlockX(); int j = chunkPos.getMinBlockZ(); ProfilerFiller profilerFiller = Profiler.get(); profilerFiller.push("thunder"); if (bl && this.isThundering() && this.random.nextInt(100000) == 0) { BlockPos blockPos = this.findLightningTargetAround(this.getBlockRandomPos(i, 0, j, 15)); if (this.isRainingAt(blockPos)) { DifficultyInstance difficultyInstance = this.getCurrentDifficultyAt(blockPos); boolean bl2 = this.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && this.random.nextDouble() < difficultyInstance.getEffectiveDifficulty() * 0.01 && !this.getBlockState(blockPos.below()).is(Blocks.LIGHTNING_ROD); if (bl2) { SkeletonHorse skeletonHorse = EntityType.SKELETON_HORSE.create(this, EntitySpawnReason.EVENT); if (skeletonHorse != null) { skeletonHorse.setTrap(true); skeletonHorse.setAge(0); skeletonHorse.setPos(blockPos.getX(), blockPos.getY(), blockPos.getZ()); this.addFreshEntity(skeletonHorse); } } LightningBolt lightningBolt = EntityType.LIGHTNING_BOLT.create(this, EntitySpawnReason.EVENT); if (lightningBolt != null) { lightningBolt.snapTo(Vec3.atBottomCenterOf(blockPos)); lightningBolt.setVisualOnly(bl2); this.addFreshEntity(lightningBolt); } } } profilerFiller.pop(); } @VisibleForTesting public void tickPrecipitation(BlockPos blockPos) { BlockPos blockPos2 = this.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, blockPos); BlockPos blockPos3 = blockPos2.below(); Biome biome = this.getBiome(blockPos2).value(); if (biome.shouldFreeze(this, blockPos3)) { this.setBlockAndUpdate(blockPos3, Blocks.ICE.defaultBlockState()); } if (this.isRaining()) { int i = this.getGameRules().getInt(GameRules.RULE_SNOW_ACCUMULATION_HEIGHT); if (i > 0 && biome.shouldSnow(this, blockPos2)) { BlockState blockState = this.getBlockState(blockPos2); if (blockState.is(Blocks.SNOW)) { int j = (Integer)blockState.getValue(SnowLayerBlock.LAYERS); if (j < Math.min(i, 8)) { BlockState blockState2 = blockState.setValue(SnowLayerBlock.LAYERS, j + 1); Block.pushEntitiesUp(blockState, blockState2, this, blockPos2); this.setBlockAndUpdate(blockPos2, blockState2); } } else { this.setBlockAndUpdate(blockPos2, Blocks.SNOW.defaultBlockState()); } } Biome.Precipitation precipitation = biome.getPrecipitationAt(blockPos3, this.getSeaLevel()); if (precipitation != Biome.Precipitation.NONE) { BlockState blockState3 = this.getBlockState(blockPos3); blockState3.getBlock().handlePrecipitation(blockState3, this, blockPos3, precipitation); } } } private Optional findLightningRod(BlockPos pos) { Optional optional = this.getPoiManager() .findClosest( holder -> holder.is(PoiTypes.LIGHTNING_ROD), blockPos -> blockPos.getY() == this.getHeight(Heightmap.Types.WORLD_SURFACE, blockPos.getX(), blockPos.getZ()) - 1, pos, 128, Occupancy.ANY ); return optional.map(blockPos -> blockPos.above(1)); } protected BlockPos findLightningTargetAround(BlockPos pos) { BlockPos blockPos = this.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, pos); Optional optional = this.findLightningRod(blockPos); if (optional.isPresent()) { return (BlockPos)optional.get(); } else { AABB aABB = AABB.encapsulatingFullBlocks(blockPos, blockPos.atY(this.getMaxY() + 1)).inflate(3.0); List list = this.getEntitiesOfClass( LivingEntity.class, aABB, livingEntity -> livingEntity != null && livingEntity.isAlive() && this.canSeeSky(livingEntity.blockPosition()) ); if (!list.isEmpty()) { return ((LivingEntity)list.get(this.random.nextInt(list.size()))).blockPosition(); } else { if (blockPos.getY() == this.getMinY() - 1) { blockPos = blockPos.above(2); } return blockPos; } } } public boolean isHandlingTick() { return this.handlingTick; } public boolean canSleepThroughNights() { return this.getGameRules().getInt(GameRules.RULE_PLAYERS_SLEEPING_PERCENTAGE) <= 100; } private void announceSleepStatus() { if (this.canSleepThroughNights()) { if (!this.getServer().isSingleplayer() || this.getServer().isPublished()) { int i = this.getGameRules().getInt(GameRules.RULE_PLAYERS_SLEEPING_PERCENTAGE); Component component; if (this.sleepStatus.areEnoughSleeping(i)) { component = Component.translatable("sleep.skipping_night"); } else { component = Component.translatable("sleep.players_sleeping", this.sleepStatus.amountSleeping(), this.sleepStatus.sleepersNeeded(i)); } for (ServerPlayer serverPlayer : this.players) { serverPlayer.displayClientMessage(component, true); } } } } /** * Updates the flag that indicates whether all players in the world are sleeping. */ public void updateSleepingPlayerList() { if (!this.players.isEmpty() && this.sleepStatus.update(this.players)) { this.announceSleepStatus(); } } public ServerScoreboard getScoreboard() { return this.server.getScoreboard(); } private void advanceWeatherCycle() { boolean bl = this.isRaining(); if (this.dimensionType().hasSkyLight()) { if (this.getGameRules().getBoolean(GameRules.RULE_WEATHER_CYCLE)) { int i = this.serverLevelData.getClearWeatherTime(); int j = this.serverLevelData.getThunderTime(); int k = this.serverLevelData.getRainTime(); boolean bl2 = this.levelData.isThundering(); boolean bl3 = this.levelData.isRaining(); if (i > 0) { i--; j = bl2 ? 0 : 1; k = bl3 ? 0 : 1; bl2 = false; bl3 = false; } else { if (j > 0) { if (--j == 0) { bl2 = !bl2; } } else if (bl2) { j = THUNDER_DURATION.sample(this.random); } else { j = THUNDER_DELAY.sample(this.random); } if (k > 0) { if (--k == 0) { bl3 = !bl3; } } else if (bl3) { k = RAIN_DURATION.sample(this.random); } else { k = RAIN_DELAY.sample(this.random); } } this.serverLevelData.setThunderTime(j); this.serverLevelData.setRainTime(k); this.serverLevelData.setClearWeatherTime(i); this.serverLevelData.setThundering(bl2); this.serverLevelData.setRaining(bl3); } this.oThunderLevel = this.thunderLevel; if (this.levelData.isThundering()) { this.thunderLevel += 0.01F; } else { this.thunderLevel -= 0.01F; } this.thunderLevel = Mth.clamp(this.thunderLevel, 0.0F, 1.0F); this.oRainLevel = this.rainLevel; if (this.levelData.isRaining()) { this.rainLevel += 0.01F; } else { this.rainLevel -= 0.01F; } this.rainLevel = Mth.clamp(this.rainLevel, 0.0F, 1.0F); } if (this.oRainLevel != this.rainLevel) { this.server.getPlayerList().broadcastAll(new ClientboundGameEventPacket(ClientboundGameEventPacket.RAIN_LEVEL_CHANGE, this.rainLevel), this.dimension()); } if (this.oThunderLevel != this.thunderLevel) { this.server .getPlayerList() .broadcastAll(new ClientboundGameEventPacket(ClientboundGameEventPacket.THUNDER_LEVEL_CHANGE, this.thunderLevel), this.dimension()); } if (bl != this.isRaining()) { if (bl) { this.server.getPlayerList().broadcastAll(new ClientboundGameEventPacket(ClientboundGameEventPacket.STOP_RAINING, 0.0F)); } else { this.server.getPlayerList().broadcastAll(new ClientboundGameEventPacket(ClientboundGameEventPacket.START_RAINING, 0.0F)); } this.server.getPlayerList().broadcastAll(new ClientboundGameEventPacket(ClientboundGameEventPacket.RAIN_LEVEL_CHANGE, this.rainLevel)); this.server.getPlayerList().broadcastAll(new ClientboundGameEventPacket(ClientboundGameEventPacket.THUNDER_LEVEL_CHANGE, this.thunderLevel)); } } @VisibleForTesting public void resetWeatherCycle() { this.serverLevelData.setRainTime(0); this.serverLevelData.setRaining(false); this.serverLevelData.setThunderTime(0); this.serverLevelData.setThundering(false); } /** * Resets the updateEntityTick field to 0 */ public void resetEmptyTime() { this.emptyTime = 0; } private void tickFluid(BlockPos pos, Fluid fluid) { BlockState blockState = this.getBlockState(pos); FluidState fluidState = blockState.getFluidState(); if (fluidState.is(fluid)) { fluidState.tick(this, pos, blockState); } } private void tickBlock(BlockPos pos, Block block) { BlockState blockState = this.getBlockState(pos); if (blockState.is(block)) { blockState.tick(this, pos, this.random); } } public void tickNonPassenger(Entity entity) { entity.setOldPosAndRot(); ProfilerFiller profilerFiller = Profiler.get(); entity.tickCount++; profilerFiller.push((Supplier)(() -> BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType()).toString())); profilerFiller.incrementCounter("tickNonPassenger"); entity.tick(); profilerFiller.pop(); for (Entity entity2 : entity.getPassengers()) { this.tickPassenger(entity, entity2); } } private void tickPassenger(Entity ridingEntity, Entity passengerEntity) { if (passengerEntity.isRemoved() || passengerEntity.getVehicle() != ridingEntity) { passengerEntity.stopRiding(); } else if (passengerEntity instanceof Player || this.entityTickList.contains(passengerEntity)) { passengerEntity.setOldPosAndRot(); passengerEntity.tickCount++; ProfilerFiller profilerFiller = Profiler.get(); profilerFiller.push((Supplier)(() -> BuiltInRegistries.ENTITY_TYPE.getKey(passengerEntity.getType()).toString())); profilerFiller.incrementCounter("tickPassenger"); passengerEntity.rideTick(); profilerFiller.pop(); for (Entity entity : passengerEntity.getPassengers()) { this.tickPassenger(passengerEntity, entity); } } } @Override public boolean mayInteract(Entity entity, BlockPos pos) { return !(entity instanceof Player player && (this.server.isUnderSpawnProtection(this, pos, player) || !this.getWorldBorder().isWithinBounds(pos))); } public void save(@Nullable ProgressListener progress, boolean flush, boolean skipSave) { ServerChunkCache serverChunkCache = this.getChunkSource(); if (!skipSave) { if (progress != null) { progress.progressStartNoAbort(Component.translatable("menu.savingLevel")); } this.saveLevelData(flush); if (progress != null) { progress.progressStage(Component.translatable("menu.savingChunks")); } serverChunkCache.save(flush); if (flush) { this.entityManager.saveAll(); } else { this.entityManager.autoSave(); } } } private void saveLevelData(boolean join) { if (this.dragonFight != null) { this.server.getWorldData().setEndDragonFightData(this.dragonFight.saveData()); } DimensionDataStorage dimensionDataStorage = this.getChunkSource().getDataStorage(); if (join) { dimensionDataStorage.saveAndJoin(); } else { dimensionDataStorage.scheduleSave(); } } public List getEntities(EntityTypeTest typeTest, Predicate predicate) { List list = Lists.newArrayList(); this.getEntities(typeTest, predicate, list); return list; } public void getEntities(EntityTypeTest typeTest, Predicate predicate, List output) { this.getEntities(typeTest, predicate, output, Integer.MAX_VALUE); } public void getEntities(EntityTypeTest typeTest, Predicate predicate, List output, int maxResults) { this.getEntities().get(typeTest, entity -> { if (predicate.test(entity)) { output.add(entity); if (output.size() >= maxResults) { return Continuation.ABORT; } } return Continuation.CONTINUE; }); } public List getDragons() { return this.getEntities(EntityType.ENDER_DRAGON, LivingEntity::isAlive); } public List getPlayers(Predicate predicate) { return this.getPlayers(predicate, Integer.MAX_VALUE); } public List getPlayers(Predicate predicate, int maxResults) { List list = Lists.newArrayList(); for (ServerPlayer serverPlayer : this.players) { if (predicate.test(serverPlayer)) { list.add(serverPlayer); if (list.size() >= maxResults) { return list; } } } return list; } @Nullable public ServerPlayer getRandomPlayer() { List list = this.getPlayers(LivingEntity::isAlive); return list.isEmpty() ? null : (ServerPlayer)list.get(this.random.nextInt(list.size())); } @Override public boolean addFreshEntity(Entity entity) { return this.addEntity(entity); } /** * Used for "unnatural" ways of entities appearing in the world, e.g. summon command, interdimensional teleports */ public boolean addWithUUID(Entity entity) { return this.addEntity(entity); } public void addDuringTeleport(Entity entity) { if (entity instanceof ServerPlayer serverPlayer) { this.addPlayer(serverPlayer); } else { this.addEntity(entity); } } public void addNewPlayer(ServerPlayer player) { this.addPlayer(player); } public void addRespawnedPlayer(ServerPlayer player) { this.addPlayer(player); } private void addPlayer(ServerPlayer player) { Entity entity = this.getEntity(player.getUUID()); if (entity != null) { LOGGER.warn("Force-added player with duplicate UUID {}", player.getUUID()); entity.unRide(); this.removePlayerImmediately((ServerPlayer)entity, Entity.RemovalReason.DISCARDED); } this.entityManager.addNewEntity(player); } /** * Called when an entity is spawned in the world. This includes players. */ private boolean addEntity(Entity entity) { if (entity.isRemoved()) { LOGGER.warn("Tried to add entity {} but it was marked as removed already", EntityType.getKey(entity.getType())); return false; } else { return this.entityManager.addNewEntity(entity); } } /** * Attempts to summon an entity and it's passangers. They will only be summoned if all entities are unique and not already in queue to be summoned. */ public boolean tryAddFreshEntityWithPassengers(Entity entity) { if (entity.getSelfAndPassengers().map(Entity::getUUID).anyMatch(this.entityManager::isLoaded)) { return false; } else { this.addFreshEntityWithPassengers(entity); return true; } } public void unload(LevelChunk chunk) { chunk.clearAllBlockEntities(); chunk.unregisterTickContainerFromLevel(this); } public void removePlayerImmediately(ServerPlayer player, Entity.RemovalReason reason) { player.remove(reason); } @Override public void destroyBlockProgress(int breakerId, BlockPos pos, int progress) { for (ServerPlayer serverPlayer : this.server.getPlayerList().getPlayers()) { if (serverPlayer != null && serverPlayer.level() == this && serverPlayer.getId() != breakerId) { double d = pos.getX() - serverPlayer.getX(); double e = pos.getY() - serverPlayer.getY(); double f = pos.getZ() - serverPlayer.getZ(); if (d * d + e * e + f * f < 1024.0) { serverPlayer.connection.send(new ClientboundBlockDestructionPacket(breakerId, pos, progress)); } } } } @Override public void playSeededSound( @Nullable Entity entity, double x, double y, double z, Holder sound, SoundSource source, float volume, float pitch, long seed ) { this.server .getPlayerList() .broadcast( entity instanceof Player player ? player : null, x, y, z, sound.value().getRange(volume), this.dimension(), new ClientboundSoundPacket(sound, source, x, y, z, volume, pitch, seed) ); } @Override public void playSeededSound(@Nullable Entity entity, Entity sourceEntity, Holder sound, SoundSource source, float volume, float pitch, long seed) { this.server .getPlayerList() .broadcast( entity instanceof Player player ? player : null, sourceEntity.getX(), sourceEntity.getY(), sourceEntity.getZ(), sound.value().getRange(volume), this.dimension(), new ClientboundSoundEntityPacket(sound, source, sourceEntity, volume, pitch, seed) ); } @Override public void globalLevelEvent(int id, BlockPos pos, int data) { if (this.getGameRules().getBoolean(GameRules.RULE_GLOBAL_SOUND_EVENTS)) { this.server.getPlayerList().getPlayers().forEach(serverPlayer -> { Vec3 vec32; if (serverPlayer.level() == this) { Vec3 vec3 = Vec3.atCenterOf(pos); if (serverPlayer.distanceToSqr(vec3) < Mth.square(32)) { vec32 = vec3; } else { Vec3 vec33 = vec3.subtract(serverPlayer.position()).normalize(); vec32 = serverPlayer.position().add(vec33.scale(32.0)); } } else { vec32 = serverPlayer.position(); } serverPlayer.connection.send(new ClientboundLevelEventPacket(id, BlockPos.containing(vec32), data, true)); }); } else { this.levelEvent(null, id, pos, data); } } @Override public void levelEvent(@Nullable Entity entity, int type, BlockPos pos, int data) { this.server .getPlayerList() .broadcast( entity instanceof Player player ? player : null, pos.getX(), pos.getY(), pos.getZ(), 64.0, this.dimension(), new ClientboundLevelEventPacket(type, pos, data, false) ); } public int getLogicalHeight() { return this.dimensionType().logicalHeight(); } @Override public void gameEvent(Holder gameEvent, Vec3 pos, Context context) { this.gameEventDispatcher.post(gameEvent, pos, context); } @Override public void sendBlockUpdated(BlockPos pos, BlockState oldState, BlockState newState, int flags) { if (this.isUpdatingNavigations) { String string = "recursive call to sendBlockUpdated"; Util.logAndPauseIfInIde("recursive call to sendBlockUpdated", new IllegalStateException("recursive call to sendBlockUpdated")); } this.getChunkSource().blockChanged(pos); this.pathTypesByPosCache.invalidate(pos); VoxelShape voxelShape = oldState.getCollisionShape(this, pos); VoxelShape voxelShape2 = newState.getCollisionShape(this, pos); if (Shapes.joinIsNotEmpty(voxelShape, voxelShape2, BooleanOp.NOT_SAME)) { List list = new ObjectArrayList<>(); for (Mob mob : this.navigatingMobs) { PathNavigation pathNavigation = mob.getNavigation(); if (pathNavigation.shouldRecomputePath(pos)) { list.add(pathNavigation); } } try { this.isUpdatingNavigations = true; for (PathNavigation pathNavigation2 : list) { pathNavigation2.recomputePath(); } } finally { this.isUpdatingNavigations = false; } } } @Override public void updateNeighborsAt(BlockPos pos, Block block) { this.updateNeighborsAt(pos, block, ExperimentalRedstoneUtils.initialOrientation(this, null, null)); } @Override public void updateNeighborsAt(BlockPos pos, Block block, @Nullable Orientation orientation) { this.neighborUpdater.updateNeighborsAtExceptFromFacing(pos, block, null, orientation); } @Override public void updateNeighborsAtExceptFromFacing(BlockPos pos, Block block, Direction facing, @Nullable Orientation orientation) { this.neighborUpdater.updateNeighborsAtExceptFromFacing(pos, block, facing, orientation); } @Override public void neighborChanged(BlockPos pos, Block block, @Nullable Orientation orientation) { this.neighborUpdater.neighborChanged(pos, block, orientation); } @Override public void neighborChanged(BlockState state, BlockPos pos, Block block, @Nullable Orientation orientation, boolean movedByPiston) { this.neighborUpdater.neighborChanged(state, pos, block, orientation, movedByPiston); } @Override public void broadcastEntityEvent(Entity entity, byte state) { this.getChunkSource().broadcastAndSend(entity, new ClientboundEntityEventPacket(entity, state)); } @Override public void broadcastDamageEvent(Entity entity, DamageSource damageSource) { this.getChunkSource().broadcastAndSend(entity, new ClientboundDamageEventPacket(entity, damageSource)); } /** * Gets the world's chunk provider */ public ServerChunkCache getChunkSource() { return this.chunkSource; } @Override 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, ParticleOptions smallExplosionParticles, ParticleOptions largeExplosionParticles, Holder explosionSound ) { BlockInteraction blockInteraction = switch (explosionInteraction) { case NONE -> BlockInteraction.KEEP; case BLOCK -> this.getDestroyType(GameRules.RULE_BLOCK_EXPLOSION_DROP_DECAY); case MOB -> this.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) ? this.getDestroyType(GameRules.RULE_MOB_EXPLOSION_DROP_DECAY) : BlockInteraction.KEEP; case TNT -> this.getDestroyType(GameRules.RULE_TNT_EXPLOSION_DROP_DECAY); case TRIGGER -> BlockInteraction.TRIGGER_BLOCK; }; Vec3 vec3 = new Vec3(x, y, z); ServerExplosion serverExplosion = new ServerExplosion(this, source, damageSource, damageCalculator, vec3, radius, fire, blockInteraction); serverExplosion.explode(); ParticleOptions particleOptions = serverExplosion.isSmall() ? smallExplosionParticles : largeExplosionParticles; for (ServerPlayer serverPlayer : this.players) { if (serverPlayer.distanceToSqr(vec3) < 4096.0) { Optional optional = Optional.ofNullable((Vec3)serverExplosion.getHitPlayers().get(serverPlayer)); serverPlayer.connection.send(new ClientboundExplodePacket(vec3, optional, particleOptions, explosionSound)); } } } private BlockInteraction getDestroyType(GameRules.Key decayGameRule) { return this.getGameRules().getBoolean(decayGameRule) ? BlockInteraction.DESTROY_WITH_DECAY : BlockInteraction.DESTROY; } @Override public void blockEvent(BlockPos pos, Block block, int eventID, int eventParam) { this.blockEvents.add(new BlockEventData(pos, block, eventID, eventParam)); } private void runBlockEvents() { this.blockEventsToReschedule.clear(); while (!this.blockEvents.isEmpty()) { BlockEventData blockEventData = this.blockEvents.removeFirst(); if (this.shouldTickBlocksAt(blockEventData.pos())) { if (this.doBlockEvent(blockEventData)) { this.server .getPlayerList() .broadcast( null, blockEventData.pos().getX(), blockEventData.pos().getY(), blockEventData.pos().getZ(), 64.0, this.dimension(), new ClientboundBlockEventPacket(blockEventData.pos(), blockEventData.block(), blockEventData.paramA(), blockEventData.paramB()) ); } } else { this.blockEventsToReschedule.add(blockEventData); } } this.blockEvents.addAll(this.blockEventsToReschedule); } private boolean doBlockEvent(BlockEventData event) { BlockState blockState = this.getBlockState(event.pos()); return blockState.is(event.block()) ? blockState.triggerEvent(this, event.pos(), event.paramA(), event.paramB()) : false; } public LevelTicks getBlockTicks() { return this.blockTicks; } public LevelTicks getFluidTicks() { return this.fluidTicks; } @NotNull @Override public MinecraftServer getServer() { return this.server; } public PortalForcer getPortalForcer() { return this.portalForcer; } public StructureTemplateManager getStructureManager() { return this.server.getStructureManager(); } public int sendParticles( T type, double posX, double posY, double posZ, int particleCount, double xOffset, double yOffset, double zOffset, double speed ) { return this.sendParticles(type, false, false, posX, posY, posZ, particleCount, xOffset, yOffset, zOffset, speed); } public int sendParticles( T type, boolean overrideLimiter, boolean alwaysShow, double posX, double posY, double posZ, int particleCount, double xOffset, double yOffset, double zOffset, double speed ) { ClientboundLevelParticlesPacket clientboundLevelParticlesPacket = new ClientboundLevelParticlesPacket( type, overrideLimiter, alwaysShow, posX, posY, posZ, (float)xOffset, (float)yOffset, (float)zOffset, (float)speed, particleCount ); int i = 0; for (int j = 0; j < this.players.size(); j++) { ServerPlayer serverPlayer = (ServerPlayer)this.players.get(j); if (this.sendParticles(serverPlayer, overrideLimiter, posX, posY, posZ, clientboundLevelParticlesPacket)) { i++; } } return i; } public boolean sendParticles( ServerPlayer player, T particle, boolean overrideLimiter, boolean alwaysShow, double posX, double posY, double posZ, int count, double xDist, double yDist, double zDist, double maxSpeed ) { Packet packet = new ClientboundLevelParticlesPacket( particle, overrideLimiter, alwaysShow, posX, posY, posZ, (float)xDist, (float)yDist, (float)zDist, (float)maxSpeed, count ); return this.sendParticles(player, overrideLimiter, posX, posY, posZ, packet); } private boolean sendParticles(ServerPlayer player, boolean longDistance, double posX, double posY, double posZ, Packet packet) { if (player.level() != this) { return false; } else { BlockPos blockPos = player.blockPosition(); if (blockPos.closerToCenterThan(new Vec3(posX, posY, posZ), longDistance ? 512.0 : 32.0)) { player.connection.send(packet); return true; } else { return false; } } } @Nullable @Override public Entity getEntity(int id) { return this.getEntities().get(id); } @Deprecated @Nullable public Entity getEntityOrPart(int id) { Entity entity = this.getEntities().get(id); return entity != null ? entity : this.dragonParts.get(id); } @Override public Collection dragonParts() { return this.dragonParts.values(); } @Nullable public BlockPos findNearestMapStructure(TagKey structureTag, BlockPos pos, int radius, boolean skipExistingChunks) { if (!this.server.getWorldData().worldGenOptions().generateStructures()) { return null; } else { Optional> optional = this.registryAccess().lookupOrThrow(Registries.STRUCTURE).get(structureTag); if (optional.isEmpty()) { return null; } else { Pair> pair = this.getChunkSource() .getGenerator() .findNearestMapStructure(this, (HolderSet)optional.get(), pos, radius, skipExistingChunks); return pair != null ? pair.getFirst() : null; } } } @Nullable public Pair> findClosestBiome3d( Predicate> biomePredicate, BlockPos pos, int radius, int horizontalStep, int verticalStep ) { return this.getChunkSource() .getGenerator() .getBiomeSource() .findClosestBiome3d(pos, radius, horizontalStep, verticalStep, biomePredicate, this.getChunkSource().randomState().sampler(), this); } public RecipeManager recipeAccess() { return this.server.getRecipeManager(); } @Override public TickRateManager tickRateManager() { return this.server.tickRateManager(); } @Override public boolean noSave() { return this.noSave; } public DimensionDataStorage getDataStorage() { return this.getChunkSource().getDataStorage(); } @Nullable @Override public MapItemSavedData getMapData(MapId mapId) { return this.getServer().overworld().getDataStorage().get(MapItemSavedData.type(mapId)); } public void setMapData(MapId mapId, MapItemSavedData data) { this.getServer().overworld().getDataStorage().set(MapItemSavedData.type(mapId), data); } public MapId getFreeMapId() { return this.getServer().overworld().getDataStorage().computeIfAbsent(MapIndex.TYPE).getNextMapId(); } public void setDefaultSpawnPos(BlockPos pos, float angle) { BlockPos blockPos = this.levelData.getSpawnPos(); float f = this.levelData.getSpawnAngle(); if (!blockPos.equals(pos) || f != angle) { this.levelData.setSpawn(pos, angle); this.getServer().getPlayerList().broadcastAll(new ClientboundSetDefaultSpawnPositionPacket(pos, angle)); } if (this.lastSpawnChunkRadius > 1) { this.getChunkSource().removeTicketWithRadius(TicketType.START, new ChunkPos(blockPos), this.lastSpawnChunkRadius); } int i = this.getGameRules().getInt(GameRules.RULE_SPAWN_CHUNK_RADIUS) + 1; if (i > 1) { this.getChunkSource().addTicketWithRadius(TicketType.START, new ChunkPos(pos), i); } this.lastSpawnChunkRadius = i; } public LongSet getForceLoadedChunks() { return this.chunkSource.getForceLoadedChunks(); } public boolean setChunkForced(int chunkX, int chunkZ, boolean add) { boolean bl = this.chunkSource.updateChunkForced(new ChunkPos(chunkX, chunkZ), add); if (add && bl) { this.getChunk(chunkX, chunkZ); } return bl; } @Override public List players() { return this.players; } @Override public void updatePOIOnBlockStateChange(BlockPos pos, BlockState oldState, BlockState newState) { Optional> optional = PoiTypes.forState(oldState); Optional> optional2 = PoiTypes.forState(newState); if (!Objects.equals(optional, optional2)) { BlockPos blockPos = pos.immutable(); optional.ifPresent(holder -> this.getServer().execute(() -> { this.getPoiManager().remove(blockPos); DebugPackets.sendPoiRemovedPacket(this, blockPos); })); optional2.ifPresent(holder -> this.getServer().execute(() -> { this.getPoiManager().add(blockPos, holder); DebugPackets.sendPoiAddedPacket(this, blockPos); })); } } public PoiManager getPoiManager() { return this.getChunkSource().getPoiManager(); } public boolean isVillage(BlockPos pos) { return this.isCloseToVillage(pos, 1); } public boolean isVillage(SectionPos pos) { return this.isVillage(pos.center()); } public boolean isCloseToVillage(BlockPos pos, int sections) { return sections > 6 ? false : this.sectionsToVillage(SectionPos.of(pos)) <= sections; } public int sectionsToVillage(SectionPos pos) { return this.getPoiManager().sectionsToVillage(pos); } public Raids getRaids() { return this.raids; } @Nullable public Raid getRaidAt(BlockPos pos) { return this.raids.getNearbyRaid(pos, 9216); } public boolean isRaided(BlockPos pos) { return this.getRaidAt(pos) != null; } public void onReputationEvent(ReputationEventType type, Entity target, ReputationEventHandler host) { host.onReputationEventFrom(type, target); } public void saveDebugReport(Path path) throws IOException { ChunkMap chunkMap = this.getChunkSource().chunkMap; Writer writer = Files.newBufferedWriter(path.resolve("stats.txt")); try { writer.write(String.format(Locale.ROOT, "spawning_chunks: %d\n", chunkMap.getDistanceManager().getNaturalSpawnChunkCount())); NaturalSpawner.SpawnState spawnState = this.getChunkSource().getLastSpawnState(); if (spawnState != null) { for (Entry entry : spawnState.getMobCategoryCounts().object2IntEntrySet()) { writer.write(String.format(Locale.ROOT, "spawn_count.%s: %d\n", ((MobCategory)entry.getKey()).getName(), entry.getIntValue())); } } writer.write(String.format(Locale.ROOT, "entities: %s\n", this.entityManager.gatherStats())); writer.write(String.format(Locale.ROOT, "block_entity_tickers: %d\n", this.blockEntityTickers.size())); writer.write(String.format(Locale.ROOT, "block_ticks: %d\n", this.getBlockTicks().count())); writer.write(String.format(Locale.ROOT, "fluid_ticks: %d\n", this.getFluidTicks().count())); writer.write("distance_manager: " + chunkMap.getDistanceManager().getDebugStatus() + "\n"); writer.write(String.format(Locale.ROOT, "pending_tasks: %d\n", this.getChunkSource().getPendingTasksCount())); } catch (Throwable var22) { if (writer != null) { try { writer.close(); } catch (Throwable var16) { var22.addSuppressed(var16); } } throw var22; } if (writer != null) { writer.close(); } CrashReport crashReport = new CrashReport("Level dump", new Exception("dummy")); this.fillReportDetails(crashReport); Writer writer2 = Files.newBufferedWriter(path.resolve("example_crash.txt")); try { writer2.write(crashReport.getFriendlyReport(ReportType.TEST)); } catch (Throwable var21) { if (writer2 != null) { try { writer2.close(); } catch (Throwable var15) { var21.addSuppressed(var15); } } throw var21; } if (writer2 != null) { writer2.close(); } Path path2 = path.resolve("chunks.csv"); Writer writer3 = Files.newBufferedWriter(path2); try { chunkMap.dumpChunks(writer3); } catch (Throwable var20) { if (writer3 != null) { try { writer3.close(); } catch (Throwable var14) { var20.addSuppressed(var14); } } throw var20; } if (writer3 != null) { writer3.close(); } Path path3 = path.resolve("entity_chunks.csv"); Writer writer4 = Files.newBufferedWriter(path3); try { this.entityManager.dumpSections(writer4); } catch (Throwable var19) { if (writer4 != null) { try { writer4.close(); } catch (Throwable var13) { var19.addSuppressed(var13); } } throw var19; } if (writer4 != null) { writer4.close(); } Path path4 = path.resolve("entities.csv"); Writer writer5 = Files.newBufferedWriter(path4); try { dumpEntities(writer5, this.getEntities().getAll()); } catch (Throwable var18) { if (writer5 != null) { try { writer5.close(); } catch (Throwable var12) { var18.addSuppressed(var12); } } throw var18; } if (writer5 != null) { writer5.close(); } Path path5 = path.resolve("block_entities.csv"); Writer writer6 = Files.newBufferedWriter(path5); try { this.dumpBlockEntityTickers(writer6); } catch (Throwable var17) { if (writer6 != null) { try { writer6.close(); } catch (Throwable var11) { var17.addSuppressed(var11); } } throw var17; } if (writer6 != null) { writer6.close(); } } private static void dumpEntities(Writer writer, Iterable entities) throws IOException { CsvOutput csvOutput = CsvOutput.builder() .addColumn("x") .addColumn("y") .addColumn("z") .addColumn("uuid") .addColumn("type") .addColumn("alive") .addColumn("display_name") .addColumn("custom_name") .build(writer); for (Entity entity : entities) { Component component = entity.getCustomName(); Component component2 = entity.getDisplayName(); csvOutput.writeRow( entity.getX(), entity.getY(), entity.getZ(), entity.getUUID(), BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType()), entity.isAlive(), component2.getString(), component != null ? component.getString() : null ); } } private void dumpBlockEntityTickers(Writer output) throws IOException { CsvOutput csvOutput = CsvOutput.builder().addColumn("x").addColumn("y").addColumn("z").addColumn("type").build(output); for (TickingBlockEntity tickingBlockEntity : this.blockEntityTickers) { BlockPos blockPos = tickingBlockEntity.getPos(); csvOutput.writeRow(blockPos.getX(), blockPos.getY(), blockPos.getZ(), tickingBlockEntity.getType()); } } @VisibleForTesting public void clearBlockEvents(BoundingBox boundingBox) { this.blockEvents.removeIf(blockEventData -> boundingBox.isInside(blockEventData.pos())); } @Override public float getShade(Direction direction, boolean shade) { return 1.0F; } /** * Gets an unmodifiable iterator of all loaded entities in the world. */ public Iterable getAllEntities() { return this.getEntities().getAll(); } public String toString() { return "ServerLevel[" + this.serverLevelData.getLevelName() + "]"; } public boolean isFlat() { return this.server.getWorldData().isFlatWorld(); } @Override public long getSeed() { return this.server.getWorldData().worldGenOptions().seed(); } @Nullable public EndDragonFight getDragonFight() { return this.dragonFight; } @Override public ServerLevel getLevel() { return this; } @VisibleForTesting public String getWatchdogStats() { return String.format( Locale.ROOT, "players: %s, entities: %s [%s], block_entities: %d [%s], block_ticks: %d, fluid_ticks: %d, chunk_source: %s", this.players.size(), this.entityManager.gatherStats(), getTypeCount(this.entityManager.getEntityGetter().getAll(), entity -> BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType()).toString()), this.blockEntityTickers.size(), getTypeCount(this.blockEntityTickers, TickingBlockEntity::getType), this.getBlockTicks().count(), this.getFluidTicks().count(), this.gatherChunkSourceStats() ); } private static String getTypeCount(Iterable objects, Function typeGetter) { try { Object2IntOpenHashMap object2IntOpenHashMap = new Object2IntOpenHashMap<>(); for (T object : objects) { String string = (String)typeGetter.apply(object); object2IntOpenHashMap.addTo(string, 1); } return (String)object2IntOpenHashMap.object2IntEntrySet() .stream() .sorted(Comparator.comparing(Entry::getIntValue).reversed()) .limit(5L) .map(entry -> (String)entry.getKey() + ":" + entry.getIntValue()) .collect(Collectors.joining(",")); } catch (Exception var6) { return ""; } } @Override protected LevelEntityGetter getEntities() { return this.entityManager.getEntityGetter(); } public void addLegacyChunkEntities(Stream entities) { this.entityManager.addLegacyChunkEntities(entities); } public void addWorldGenChunkEntities(Stream entities) { this.entityManager.addWorldGenChunkEntities(entities); } public void startTickingChunk(LevelChunk chunk) { chunk.unpackTicks(this.getLevelData().getGameTime()); } public void onStructureStartsAvailable(ChunkAccess chunk) { this.server.execute(() -> this.structureCheck.onStructureLoad(chunk.getPos(), chunk.getAllStarts())); } public PathTypeCache getPathTypeCache() { return this.pathTypesByPosCache; } @Override public void close() throws IOException { super.close(); this.entityManager.close(); } @Override public String gatherChunkSourceStats() { return "Chunks[S] W: " + this.chunkSource.gatherStats() + " E: " + this.entityManager.gatherStats(); } public boolean areEntitiesLoaded(long chunkPos) { return this.entityManager.areEntitiesLoaded(chunkPos); } public boolean isPositionTickingWithEntitiesLoaded(long chunkPos) { return this.areEntitiesLoaded(chunkPos) && this.chunkSource.isPositionTicking(chunkPos); } public boolean isPositionEntityTicking(BlockPos pos) { return this.entityManager.canPositionTick(pos) && this.chunkSource.chunkMap.getDistanceManager().inEntityTickingRange(ChunkPos.asLong(pos)); } public boolean areEntitiesActuallyLoadedAndTicking(ChunkPos chunkPos) { return this.entityManager.isTicking(chunkPos) && this.entityManager.areEntitiesLoaded(chunkPos.toLong()); } public boolean anyPlayerCloseEnoughForSpawning(BlockPos pos) { return this.anyPlayerCloseEnoughForSpawning(new ChunkPos(pos)); } public boolean anyPlayerCloseEnoughForSpawning(ChunkPos chunkPos) { return this.chunkSource.chunkMap.anyPlayerCloseEnoughForSpawning(chunkPos); } public boolean canSpawnEntitiesInChunk(ChunkPos chunkPos) { return this.entityManager.canPositionTick(chunkPos) && this.getWorldBorder().isWithinBounds(chunkPos); } @Override public FeatureFlagSet enabledFeatures() { return this.server.getWorldData().enabledFeatures(); } @Override public PotionBrewing potionBrewing() { return this.server.potionBrewing(); } @Override public FuelValues fuelValues() { return this.server.fuelValues(); } public RandomSource getRandomSequence(ResourceLocation location) { return this.randomSequences.get(location); } public RandomSequences getRandomSequences() { return this.randomSequences; } public GameRules getGameRules() { return this.serverLevelData.getGameRules(); } @Override public CrashReportCategory fillReportDetails(CrashReport report) { CrashReportCategory crashReportCategory = super.fillReportDetails(report); crashReportCategory.setDetail("Loaded entity count", (CrashReportDetail)(() -> String.valueOf(this.entityManager.count()))); return crashReportCategory; } @Override public int getSeaLevel() { return this.chunkSource.getGenerator().getSeaLevel(); } final class EntityCallbacks implements LevelCallback { public void onCreated(Entity entity) { } public void onDestroyed(Entity entity) { ServerLevel.this.getScoreboard().entityRemoved(entity); } public void onTickingStart(Entity entity) { ServerLevel.this.entityTickList.add(entity); } public void onTickingEnd(Entity entity) { ServerLevel.this.entityTickList.remove(entity); } public void onTrackingStart(Entity entity) { ServerLevel.this.getChunkSource().addEntity(entity); if (entity instanceof ServerPlayer serverPlayer) { ServerLevel.this.players.add(serverPlayer); ServerLevel.this.updateSleepingPlayerList(); } if (entity instanceof Mob mob) { if (ServerLevel.this.isUpdatingNavigations) { String string = "onTrackingStart called during navigation iteration"; Util.logAndPauseIfInIde( "onTrackingStart called during navigation iteration", new IllegalStateException("onTrackingStart called during navigation iteration") ); } ServerLevel.this.navigatingMobs.add(mob); } if (entity instanceof EnderDragon enderDragon) { for (EnderDragonPart enderDragonPart : enderDragon.getSubEntities()) { ServerLevel.this.dragonParts.put(enderDragonPart.getId(), enderDragonPart); } } entity.updateDynamicGameEventListener(DynamicGameEventListener::add); } public void onTrackingEnd(Entity entity) { ServerLevel.this.getChunkSource().removeEntity(entity); if (entity instanceof ServerPlayer serverPlayer) { ServerLevel.this.players.remove(serverPlayer); ServerLevel.this.updateSleepingPlayerList(); } if (entity instanceof Mob mob) { if (ServerLevel.this.isUpdatingNavigations) { String string = "onTrackingStart called during navigation iteration"; Util.logAndPauseIfInIde( "onTrackingStart called during navigation iteration", new IllegalStateException("onTrackingStart called during navigation iteration") ); } ServerLevel.this.navigatingMobs.remove(mob); } if (entity instanceof EnderDragon enderDragon) { for (EnderDragonPart enderDragonPart : enderDragon.getSubEntities()) { ServerLevel.this.dragonParts.remove(enderDragonPart.getId()); } } entity.updateDynamicGameEventListener(DynamicGameEventListener::remove); } public void onSectionChange(Entity entity) { entity.updateDynamicGameEventListener(DynamicGameEventListener::move); } } }