ServerLevel.java

net.minecraft.server.level.ServerLevel

信息

  • 全限定名:net.minecraft.server.level.ServerLevel
  • 类型:public class
  • 包:net.minecraft.server.level
  • 源码路径:src/main/java/net/minecraft/server/level/ServerLevel.java
  • 起始行号:L185
  • 继承:Level
  • 实现:WorldGenLevel, ServerEntityGetter
  • 职责:

    TODO

字段/常量

  • END_SPAWN_POINT

    • 类型: BlockPos
    • 修饰符: public static final
    • 源码定位: L186
    • 说明:

      TODO

  • RAIN_DELAY

    • 类型: IntProvider
    • 修饰符: public static final
    • 源码定位: L187
    • 说明:

      TODO

  • RAIN_DURATION

    • 类型: IntProvider
    • 修饰符: public static final
    • 源码定位: L188
    • 说明:

      TODO

  • THUNDER_DELAY

    • 类型: IntProvider
    • 修饰符: private static final
    • 源码定位: L189
    • 说明:

      TODO

  • THUNDER_DURATION

    • 类型: IntProvider
    • 修饰符: public static final
    • 源码定位: L190
    • 说明:

      TODO

  • LOGGER

    • 类型: Logger
    • 修饰符: private static final
    • 源码定位: L191
    • 说明:

      TODO

  • EMPTY_TIME_NO_TICK

    • 类型: int
    • 修饰符: private static final
    • 源码定位: L192
    • 说明:

      TODO

  • MAX_SCHEDULED_TICKS_PER_TICK

    • 类型: int
    • 修饰符: private static final
    • 源码定位: L193
    • 说明:

      TODO

  • players

    • 类型: List<ServerPlayer>
    • 修饰符: private final
    • 源码定位: L194
    • 说明:

      TODO

  • chunkSource

    • 类型: ServerChunkCache
    • 修饰符: private final
    • 源码定位: L195
    • 说明:

      TODO

  • server

    • 类型: MinecraftServer
    • 修饰符: private final
    • 源码定位: L196
    • 说明:

      TODO

  • serverLevelData

    • 类型: ServerLevelData
    • 修饰符: private final
    • 源码定位: L197
    • 说明:

      TODO

  • entityTickList

    • 类型: EntityTickList
    • 修饰符: private final
    • 源码定位: L198
    • 说明:

      TODO

  • waypointManager

    • 类型: ServerWaypointManager
    • 修饰符: private final
    • 源码定位: L199
    • 说明:

      TODO

  • environmentAttributes

    • 类型: EnvironmentAttributeSystem
    • 修饰符: private
    • 源码定位: L200
    • 说明:

      TODO

  • entityManager

    • 类型: PersistentEntitySectionManager<Entity>
    • 修饰符: private final
    • 源码定位: L201
    • 说明:

      TODO

  • gameEventDispatcher

    • 类型: GameEventDispatcher
    • 修饰符: private final
    • 源码定位: L202
    • 说明:

      TODO

  • noSave

    • 类型: boolean
    • 修饰符: public
    • 源码定位: L203
    • 说明:

      TODO

  • sleepStatus

    • 类型: SleepStatus
    • 修饰符: private final
    • 源码定位: L204
    • 说明:

      TODO

  • emptyTime

    • 类型: int
    • 修饰符: private
    • 源码定位: L205
    • 说明:

      TODO

  • portalForcer

    • 类型: PortalForcer
    • 修饰符: private final
    • 源码定位: L206
    • 说明:

      TODO

  • blockTicks

    • 类型: LevelTicks<Block>
    • 修饰符: private final
    • 源码定位: L207
    • 说明:

      TODO

  • fluidTicks

    • 类型: LevelTicks<Fluid>
    • 修饰符: private final
    • 源码定位: L208
    • 说明:

      TODO

  • pathTypesByPosCache

    • 类型: PathTypeCache
    • 修饰符: private final
    • 源码定位: L209
    • 说明:

      TODO

  • navigatingMobs

    • 类型: Set<Mob>
    • 修饰符: private final
    • 源码定位: L210
    • 说明:

      TODO

  • isUpdatingNavigations

    • 类型: boolean
    • 修饰符: private volatile
    • 源码定位: L211
    • 说明:

      TODO

  • raids

    • 类型: Raids
    • 修饰符: protected final
    • 源码定位: L212
    • 说明:

      TODO

  • blockEvents

    • 类型: ObjectLinkedOpenHashSet<BlockEventData>
    • 修饰符: private final
    • 源码定位: L213
    • 说明:

      TODO

  • blockEventsToReschedule

    • 类型: List<BlockEventData>
    • 修饰符: private final
    • 源码定位: L214
    • 说明:

      TODO

  • handlingTick

    • 类型: boolean
    • 修饰符: private
    • 源码定位: L215
    • 说明:

      TODO

  • customSpawners

    • 类型: List<CustomSpawner>
    • 修饰符: private final
    • 源码定位: L216
    • 说明:

      TODO

  • dragonFight

    • 类型: EnderDragonFight
    • 修饰符: private
    • 源码定位: L217
    • 说明:

      TODO

  • dragonParts

    • 类型: Int2ObjectMap<EnderDragonPart>
    • 修饰符: private final
    • 源码定位: L218
    • 说明:

      TODO

  • structureManager

    • 类型: StructureManager
    • 修饰符: private final
    • 源码定位: L219
    • 说明:

      TODO

  • structureCheck

    • 类型: StructureCheck
    • 修饰符: private final
    • 源码定位: L220
    • 说明:

      TODO

  • tickTime

    • 类型: boolean
    • 修饰符: private final
    • 源码定位: L221
    • 说明:

      TODO

  • debugSynchronizers

    • 类型: LevelDebugSynchronizers
    • 修饰符: private final
    • 源码定位: L222
    • 说明:

      TODO

内部类/嵌套类型

  • net.minecraft.server.level.ServerLevel.=

    • 类型: record
    • 修饰符: package-private
    • 源码定位: L1490
    • 说明:

      TODO

  • net.minecraft.server.level.ServerLevel.=

    • 类型: record
    • 修饰符: package-private
    • 源码定位: L1491
    • 说明:

      TODO

  • net.minecraft.server.level.ServerLevel.=.)

    • 类型: record
    • 修饰符: package-private
    • 源码定位: L1492
    • 说明:

      TODO

  • net.minecraft.server.level.ServerLevel.EntityCallbacks

    • 类型: class
    • 修饰符: private final
    • 源码定位: L1855
    • 说明:

      TODO

构造器

public ServerLevel(MinecraftServer server, Executor executor, LevelStorageSource.LevelStorageAccess levelStorage, ServerLevelData levelData, ResourceKey<Level> dimension, LevelStem levelStem, boolean isDebug, long biomeZoomSeed, List<CustomSpawner> customSpawners, boolean tickTime) @ L224

  • 构造器名:ServerLevel
  • 源码定位:L224
  • 修饰符:public

参数:

  • server: MinecraftServer
  • executor: Executor
  • levelStorage: LevelStorageSource.LevelStorageAccess
  • levelData: ServerLevelData
  • dimension: ResourceKey
  • levelStem: LevelStem
  • isDebug: boolean
  • biomeZoomSeed: long
  • customSpawners: List
  • tickTime: boolean

说明:

TODO

方法

下面的方法块按源码顺序生成。

public void setDragonFight(EnderDragonFight fight) @ L308

  • 方法名:setDragonFight
  • 源码定位:L308
  • 返回类型:void
  • 修饰符:public

参数:

  • fight: EnderDragonFight

说明:

TODO

public Holder<Biome> getUncachedNoiseBiome(int quartX, int quartY, int quartZ) @ L314

  • 方法名:getUncachedNoiseBiome
  • 源码定位:L314
  • 返回类型:Holder
  • 修饰符:public

参数:

  • quartX: int
  • quartY: int
  • quartZ: int

说明:

TODO

public StructureManager structureManager() @ L319

  • 方法名:structureManager
  • 源码定位:L319
  • 返回类型:StructureManager
  • 修饰符:public

参数:

说明:

TODO

public ServerClockManager clockManager() @ L323

  • 方法名:clockManager
  • 源码定位:L323
  • 返回类型:ServerClockManager
  • 修饰符:public

参数:

说明:

TODO

public EnvironmentAttributeSystem environmentAttributes() @ L327

  • 方法名:environmentAttributes
  • 源码定位:L327
  • 返回类型:EnvironmentAttributeSystem
  • 修饰符:public

参数:

说明:

TODO

public EnvironmentAttributeSystem setEnvironmentAttributes(EnvironmentAttributeSystem environmentAttributes) @ L332

  • 方法名:setEnvironmentAttributes
  • 源码定位:L332
  • 返回类型:EnvironmentAttributeSystem
  • 修饰符:public

参数:

  • environmentAttributes: EnvironmentAttributeSystem

说明:

TODO

public void tick(BooleanSupplier haveTime) @ L340

  • 方法名:tick
  • 源码定位:L340
  • 返回类型:void
  • 修饰符:public

参数:

  • haveTime: BooleanSupplier

说明:

TODO

public boolean shouldTickBlocksAt(long chunkPos) @ L460

  • 方法名:shouldTickBlocksAt
  • 源码定位:L460
  • 返回类型:boolean
  • 修饰符:public

参数:

  • chunkPos: long

说明:

TODO

protected void tickTime() @ L465

  • 方法名:tickTime
  • 源码定位:L465
  • 返回类型:void
  • 修饰符:protected

参数:

说明:

TODO

public void tickCustomSpawners(boolean spawnEnemies) @ L475

  • 方法名:tickCustomSpawners
  • 源码定位:L475
  • 返回类型:void
  • 修饰符:public

参数:

  • spawnEnemies: boolean

说明:

TODO

private void wakeUpAllPlayers() @ L481

  • 方法名:wakeUpAllPlayers
  • 源码定位:L481
  • 返回类型:void
  • 修饰符:private

参数:

说明:

TODO

public void tickChunk(LevelChunk chunk, int tickSpeed) @ L486

  • 方法名:tickChunk
  • 源码定位:L486
  • 返回类型:void
  • 修饰符:public

参数:

  • chunk: LevelChunk
  • tickSpeed: int

说明:

TODO

public void tickThunder(LevelChunk chunk) @ L531

  • 方法名:tickThunder
  • 源码定位:L531
  • 返回类型:void
  • 修饰符:public

参数:

  • chunk: LevelChunk

说明:

TODO

public void tickPrecipitation(BlockPos pos) @ L567

  • 方法名:tickPrecipitation
  • 源码定位:L567
  • 返回类型:void
  • 修饰符:public

参数:

  • pos: BlockPos

说明:

TODO

private Optional<BlockPos> findLightningRod(BlockPos center) @ L600

  • 方法名:findLightningRod
  • 源码定位:L600
  • 返回类型:Optional
  • 修饰符:private

参数:

  • center: BlockPos

说明:

TODO

protected BlockPos findLightningTargetAround(BlockPos pos) @ L612

  • 方法名:findLightningTargetAround
  • 源码定位:L612
  • 返回类型:BlockPos
  • 修饰符:protected

参数:

  • pos: BlockPos

说明:

TODO

public boolean isHandlingTick() @ L632

  • 方法名:isHandlingTick
  • 源码定位:L632
  • 返回类型:boolean
  • 修饰符:public

参数:

说明:

TODO

public boolean canSleepThroughNights() @ L636

  • 方法名:canSleepThroughNights
  • 源码定位:L636
  • 返回类型:boolean
  • 修饰符:public

参数:

说明:

TODO

private void announceSleepStatus() @ L640

  • 方法名:announceSleepStatus
  • 源码定位:L640
  • 返回类型:void
  • 修饰符:private

参数:

说明:

TODO

public void updateSleepingPlayerList() @ L658

  • 方法名:updateSleepingPlayerList
  • 源码定位:L658
  • 返回类型:void
  • 修饰符:public

参数:

说明:

TODO

public ServerScoreboard getScoreboard() @ L664

  • 方法名:getScoreboard
  • 源码定位:L664
  • 返回类型:ServerScoreboard
  • 修饰符:public

参数:

说明:

TODO

public ServerWaypointManager getWaypointManager() @ L668

  • 方法名:getWaypointManager
  • 源码定位:L668
  • 返回类型:ServerWaypointManager
  • 修饰符:public

参数:

说明:

TODO

public DifficultyInstance getCurrentDifficultyAt(BlockPos pos) @ L672

  • 方法名:getCurrentDifficultyAt
  • 源码定位:L672
  • 返回类型:DifficultyInstance
  • 修饰符:public

参数:

  • pos: BlockPos

说明:

TODO

public float getMoonBrightness(BlockPos pos) @ L685

  • 方法名:getMoonBrightness
  • 源码定位:L685
  • 返回类型:float
  • 修饰符:public

参数:

  • pos: BlockPos

说明:

TODO

private void prepareWeather(WeatherData weatherData) @ L690

  • 方法名:prepareWeather
  • 源码定位:L690
  • 返回类型:void
  • 修饰符:private

参数:

  • weatherData: WeatherData

说明:

TODO

private void advanceWeatherCycle() @ L699

  • 方法名:advanceWeatherCycle
  • 源码定位:L699
  • 返回类型:void
  • 修饰符:private

参数:

说明:

TODO

public void resetWeatherCycle() @ L786

  • 方法名:resetWeatherCycle
  • 源码定位:L786
  • 返回类型:void
  • 修饰符:public

参数:

说明:

TODO

public void resetEmptyTime() @ L795

  • 方法名:resetEmptyTime
  • 源码定位:L795
  • 返回类型:void
  • 修饰符:public

参数:

说明:

TODO

private void tickFluid(BlockPos pos, Fluid type) @ L799

  • 方法名:tickFluid
  • 源码定位:L799
  • 返回类型:void
  • 修饰符:private

参数:

  • pos: BlockPos
  • type: Fluid

说明:

TODO

private void tickBlock(BlockPos pos, Block type) @ L807

  • 方法名:tickBlock
  • 源码定位:L807
  • 返回类型:void
  • 修饰符:private

参数:

  • pos: BlockPos
  • type: Block

说明:

TODO

public void tickNonPassenger(Entity entity) @ L814

  • 方法名:tickNonPassenger
  • 源码定位:L814
  • 返回类型:void
  • 修饰符:public

参数:

  • entity: Entity

说明:

TODO

private void tickPassenger(Entity vehicle, Entity entity) @ L828

  • 方法名:tickPassenger
  • 源码定位:L828
  • 返回类型:void
  • 修饰符:private

参数:

  • vehicle: Entity
  • entity: Entity

说明:

TODO

public void updateNeighboursOnBlockSet(BlockPos pos, BlockState oldState) @ L846

  • 方法名:updateNeighboursOnBlockSet
  • 源码定位:L846
  • 返回类型:void
  • 修饰符:public

参数:

  • pos: BlockPos
  • oldState: BlockState

说明:

TODO

public boolean mayInteract(Entity entity, BlockPos pos) @ L860

  • 方法名:mayInteract
  • 源码定位:L860
  • 返回类型:boolean
  • 修饰符:public

参数:

  • entity: Entity
  • pos: BlockPos

说明:

TODO

public void save(ProgressListener progressListener, boolean flush, boolean noSave) @ L865

  • 方法名:save
  • 源码定位:L865
  • 返回类型:void
  • 修饰符:public

参数:

  • progressListener: ProgressListener
  • flush: boolean
  • noSave: boolean

说明:

TODO

private void saveLevelData(boolean sync) @ L886

  • 方法名:saveLevelData
  • 源码定位:L886
  • 返回类型:void
  • 修饰符:private

参数:

  • sync: boolean

说明:

TODO

public <T extends Entity> List<?extends T> getEntities(EntityTypeTest<Entity,T> type, Predicate<?super T> selector) @ L895

  • 方法名:getEntities
  • 源码定位:L895
  • 返回类型: List<?extends T>
  • 修饰符:public

参数:

  • type: EntityTypeTest<Entity,T>
  • selector: Predicate<?super T>

说明:

TODO

public <T extends Entity> void getEntities(EntityTypeTest<Entity,T> type, Predicate<?super T> selector, List<?super T> result) @ L901

  • 方法名:getEntities
  • 源码定位:L901
  • 返回类型: void
  • 修饰符:public

参数:

  • type: EntityTypeTest<Entity,T>
  • selector: Predicate<?super T>
  • result: List<?super T>

说明:

TODO

public <T extends Entity> void getEntities(EntityTypeTest<Entity,T> type, Predicate<?super T> selector, List<?super T> result, int maxResults) @ L905

  • 方法名:getEntities
  • 源码定位:L905
  • 返回类型: void
  • 修饰符:public

参数:

  • type: EntityTypeTest<Entity,T>
  • selector: Predicate<?super T>
  • result: List<?super T>
  • maxResults: int

说明:

TODO

public List<?extends EnderDragon> getDragons() @ L918

  • 方法名:getDragons
  • 源码定位:L918
  • 返回类型:List<?extends EnderDragon>
  • 修饰符:public

参数:

说明:

TODO

public List<ServerPlayer> getPlayers(Predicate<?super ServerPlayer> selector) @ L922

  • 方法名:getPlayers
  • 源码定位:L922
  • 返回类型:List
  • 修饰符:public

参数:

  • selector: Predicate<?super ServerPlayer>

说明:

TODO

public List<ServerPlayer> getPlayers(Predicate<?super ServerPlayer> selector, int maxResults) @ L926

  • 方法名:getPlayers
  • 源码定位:L926
  • 返回类型:List
  • 修饰符:public

参数:

  • selector: Predicate<?super ServerPlayer>
  • maxResults: int

说明:

TODO

public ServerPlayer getRandomPlayer() @ L941

  • 方法名:getRandomPlayer
  • 源码定位:L941
  • 返回类型:ServerPlayer
  • 修饰符:public

参数:

说明:

TODO

public boolean addFreshEntity(Entity entity) @ L946

  • 方法名:addFreshEntity
  • 源码定位:L946
  • 返回类型:boolean
  • 修饰符:public

参数:

  • entity: Entity

说明:

TODO

public boolean addWithUUID(Entity entity) @ L951

  • 方法名:addWithUUID
  • 源码定位:L951
  • 返回类型:boolean
  • 修饰符:public

参数:

  • entity: Entity

说明:

TODO

public void addDuringTeleport(Entity entity) @ L955

  • 方法名:addDuringTeleport
  • 源码定位:L955
  • 返回类型:void
  • 修饰符:public

参数:

  • entity: Entity

说明:

TODO

public void addNewPlayer(ServerPlayer player) @ L963

  • 方法名:addNewPlayer
  • 源码定位:L963
  • 返回类型:void
  • 修饰符:public

参数:

  • player: ServerPlayer

说明:

TODO

public void addRespawnedPlayer(ServerPlayer player) @ L967

  • 方法名:addRespawnedPlayer
  • 源码定位:L967
  • 返回类型:void
  • 修饰符:public

参数:

  • player: ServerPlayer

说明:

TODO

private void addPlayer(ServerPlayer player) @ L971

  • 方法名:addPlayer
  • 源码定位:L971
  • 返回类型:void
  • 修饰符:private

参数:

  • player: ServerPlayer

说明:

TODO

private boolean addEntity(Entity entity) @ L982

  • 方法名:addEntity
  • 源码定位:L982
  • 返回类型:boolean
  • 修饰符:private

参数:

  • entity: Entity

说明:

TODO

public boolean tryAddFreshEntityWithPassengers(Entity entity) @ L991

  • 方法名:tryAddFreshEntityWithPassengers
  • 源码定位:L991
  • 返回类型:boolean
  • 修饰符:public

参数:

  • entity: Entity

说明:

TODO

public void unload(LevelChunk levelChunk) @ L1000

  • 方法名:unload
  • 源码定位:L1000
  • 返回类型:void
  • 修饰符:public

参数:

  • levelChunk: LevelChunk

说明:

TODO

public void removePlayerImmediately(ServerPlayer player, Entity.RemovalReason reason) @ L1006

  • 方法名:removePlayerImmediately
  • 源码定位:L1006
  • 返回类型:void
  • 修饰符:public

参数:

  • player: ServerPlayer
  • reason: Entity.RemovalReason

说明:

TODO

public void destroyBlockProgress(int id, BlockPos blockPos, int progress) @ L1010

  • 方法名:destroyBlockProgress
  • 源码定位:L1010
  • 返回类型:void
  • 修饰符:public

参数:

  • id: int
  • blockPos: BlockPos
  • progress: int

说明:

TODO

public void playSeededSound(Entity except, double x, double y, double z, Holder<SoundEvent> sound, SoundSource source, float volume, float pitch, long seed) @ L1024

  • 方法名:playSeededSound
  • 源码定位:L1024
  • 返回类型:void
  • 修饰符:public

参数:

  • except: Entity
  • x: double
  • y: double
  • z: double
  • sound: Holder
  • source: SoundSource
  • volume: float
  • pitch: float
  • seed: long

说明:

TODO

public void playSeededSound(Entity except, Entity sourceEntity, Holder<SoundEvent> sound, SoundSource source, float volume, float pitch, long seed) @ L1041

  • 方法名:playSeededSound
  • 源码定位:L1041
  • 返回类型:void
  • 修饰符:public

参数:

  • except: Entity
  • sourceEntity: Entity
  • sound: Holder
  • source: SoundSource
  • volume: float
  • pitch: float
  • seed: long

说明:

TODO

public void globalLevelEvent(int type, BlockPos pos, int data) @ L1058

  • 方法名:globalLevelEvent
  • 源码定位:L1058
  • 返回类型:void
  • 修饰符:public

参数:

  • type: int
  • pos: BlockPos
  • data: int

说明:

TODO

public void levelEvent(Entity source, int type, BlockPos pos, int data) @ L1082

  • 方法名:levelEvent
  • 源码定位:L1082
  • 返回类型:void
  • 修饰符:public

参数:

  • source: Entity
  • type: int
  • pos: BlockPos
  • data: int

说明:

TODO

public int getLogicalHeight() @ L1097

  • 方法名:getLogicalHeight
  • 源码定位:L1097
  • 返回类型:int
  • 修饰符:public

参数:

说明:

TODO

public void gameEvent(Holder<GameEvent> gameEvent, Vec3 position, GameEvent.Context context) @ L1101

  • 方法名:gameEvent
  • 源码定位:L1101
  • 返回类型:void
  • 修饰符:public

参数:

  • gameEvent: Holder
  • position: Vec3
  • context: GameEvent.Context

说明:

TODO

public void sendBlockUpdated(BlockPos pos, BlockState old, BlockState current, int updateFlags) @ L1106

  • 方法名:sendBlockUpdated
  • 源码定位:L1106
  • 返回类型:void
  • 修饰符:public

参数:

  • pos: BlockPos
  • old: BlockState
  • current: BlockState
  • updateFlags: int

说明:

TODO

public void updateNeighborsAt(BlockPos pos, Block sourceBlock) @ L1139

  • 方法名:updateNeighborsAt
  • 源码定位:L1139
  • 返回类型:void
  • 修饰符:public

参数:

  • pos: BlockPos
  • sourceBlock: Block

说明:

TODO

public void updateNeighborsAt(BlockPos pos, Block sourceBlock, Orientation orientation) @ L1144

  • 方法名:updateNeighborsAt
  • 源码定位:L1144
  • 返回类型:void
  • 修饰符:public

参数:

  • pos: BlockPos
  • sourceBlock: Block
  • orientation: Orientation

说明:

TODO

public void updateNeighborsAtExceptFromFacing(BlockPos pos, Block blockObject, Direction skipDirection, Orientation orientation) @ L1149

  • 方法名:updateNeighborsAtExceptFromFacing
  • 源码定位:L1149
  • 返回类型:void
  • 修饰符:public

参数:

  • pos: BlockPos
  • blockObject: Block
  • skipDirection: Direction
  • orientation: Orientation

说明:

TODO

public void neighborChanged(BlockPos pos, Block changedBlock, Orientation orientation) @ L1154

  • 方法名:neighborChanged
  • 源码定位:L1154
  • 返回类型:void
  • 修饰符:public

参数:

  • pos: BlockPos
  • changedBlock: Block
  • orientation: Orientation

说明:

TODO

public void neighborChanged(BlockState state, BlockPos pos, Block changedBlock, Orientation orientation, boolean movedByPiston) @ L1159

  • 方法名:neighborChanged
  • 源码定位:L1159
  • 返回类型:void
  • 修饰符:public

参数:

  • state: BlockState
  • pos: BlockPos
  • changedBlock: Block
  • orientation: Orientation
  • movedByPiston: boolean

说明:

TODO

public void broadcastEntityEvent(Entity entity, byte event) @ L1164

  • 方法名:broadcastEntityEvent
  • 源码定位:L1164
  • 返回类型:void
  • 修饰符:public

参数:

  • entity: Entity
  • event: byte

说明:

TODO

public void broadcastDamageEvent(Entity entity, DamageSource source) @ L1169

  • 方法名:broadcastDamageEvent
  • 源码定位:L1169
  • 返回类型:void
  • 修饰符:public

参数:

  • entity: Entity
  • source: DamageSource

说明:

TODO

public ServerChunkCache getChunkSource() @ L1174

  • 方法名:getChunkSource
  • 源码定位:L1174
  • 返回类型:ServerChunkCache
  • 修饰符:public

参数:

说明:

TODO

public void explode(Entity source, DamageSource damageSource, ExplosionDamageCalculator damageCalculator, double x, double y, double z, float r, boolean fire, Level.ExplosionInteraction interactionType, ParticleOptions smallExplosionParticles, ParticleOptions largeExplosionParticles, WeightedList<ExplosionParticleInfo> blockParticles, Holder<SoundEvent> explosionSound) @ L1178

  • 方法名:explode
  • 源码定位:L1178
  • 返回类型:void
  • 修饰符:public

参数:

  • source: Entity
  • damageSource: DamageSource
  • damageCalculator: ExplosionDamageCalculator
  • x: double
  • y: double
  • z: double
  • r: float
  • fire: boolean
  • interactionType: Level.ExplosionInteraction
  • smallExplosionParticles: ParticleOptions
  • largeExplosionParticles: ParticleOptions
  • blockParticles: WeightedList
  • explosionSound: Holder

说明:

TODO

private Explosion.BlockInteraction getDestroyType(GameRule<Boolean> gameRule) @ L1216

  • 方法名:getDestroyType
  • 源码定位:L1216
  • 返回类型:Explosion.BlockInteraction
  • 修饰符:private

参数:

  • gameRule: GameRule

说明:

TODO

public void blockEvent(BlockPos pos, Block block, int b0, int b1) @ L1220

  • 方法名:blockEvent
  • 源码定位:L1220
  • 返回类型:void
  • 修饰符:public

参数:

  • pos: BlockPos
  • block: Block
  • b0: int
  • b1: int

说明:

TODO

private void runBlockEvents() @ L1225

  • 方法名:runBlockEvents
  • 源码定位:L1225
  • 返回类型:void
  • 修饰符:private

参数:

说明:

TODO

private boolean doBlockEvent(BlockEventData eventData) @ L1252

  • 方法名:doBlockEvent
  • 源码定位:L1252
  • 返回类型:boolean
  • 修饰符:private

参数:

  • eventData: BlockEventData

说明:

TODO

public LevelTicks<Block> getBlockTicks() @ L1257

  • 方法名:getBlockTicks
  • 源码定位:L1257
  • 返回类型:LevelTicks
  • 修饰符:public

参数:

说明:

TODO

public LevelTicks<Fluid> getFluidTicks() @ L1261

  • 方法名:getFluidTicks
  • 源码定位:L1261
  • 返回类型:LevelTicks
  • 修饰符:public

参数:

说明:

TODO

public MinecraftServer getServer() @ L1265

  • 方法名:getServer
  • 源码定位:L1265
  • 返回类型:MinecraftServer
  • 修饰符:public

参数:

说明:

TODO

public PortalForcer getPortalForcer() @ L1270

  • 方法名:getPortalForcer
  • 源码定位:L1270
  • 返回类型:PortalForcer
  • 修饰符:public

参数:

说明:

TODO

public StructureTemplateManager getStructureManager() @ L1274

  • 方法名:getStructureManager
  • 源码定位:L1274
  • 返回类型:StructureTemplateManager
  • 修饰符:public

参数:

说明:

TODO

public <T extends ParticleOptions> int sendParticles(T particle, double x, double y, double z, int count, double xDist, double yDist, double zDist, double speed) @ L1278

  • 方法名:sendParticles
  • 源码定位:L1278
  • 返回类型: int
  • 修饰符:public

参数:

  • particle: T
  • x: double
  • y: double
  • z: double
  • count: int
  • xDist: double
  • yDist: double
  • zDist: double
  • speed: double

说明:

TODO

public <T extends ParticleOptions> int sendParticles(T particle, boolean overrideLimiter, boolean alwaysShow, double x, double y, double z, int count, double xDist, double yDist, double zDist, double speed) @ L1284

  • 方法名:sendParticles
  • 源码定位:L1284
  • 返回类型: int
  • 修饰符:public

参数:

  • particle: T
  • overrideLimiter: boolean
  • alwaysShow: boolean
  • x: double
  • y: double
  • z: double
  • count: int
  • xDist: double
  • yDist: double
  • zDist: double
  • speed: double

说明:

TODO

public <T extends ParticleOptions> boolean sendParticles(ServerPlayer player, T particle, boolean overrideLimiter, boolean alwaysShow, double x, double y, double z, int count, double xDist, double yDist, double zDist, double speed) @ L1312

  • 方法名:sendParticles
  • 源码定位:L1312
  • 返回类型: boolean
  • 修饰符:public

参数:

  • player: ServerPlayer
  • particle: T
  • overrideLimiter: boolean
  • alwaysShow: boolean
  • x: double
  • y: double
  • z: double
  • count: int
  • xDist: double
  • yDist: double
  • zDist: double
  • speed: double

说明:

TODO

private boolean sendParticles(ServerPlayer player, boolean overrideLimiter, double x, double y, double z, Packet<?> packet) @ L1332

  • 方法名:sendParticles
  • 源码定位:L1332
  • 返回类型:boolean
  • 修饰符:private

参数:

  • player: ServerPlayer
  • overrideLimiter: boolean
  • x: double
  • y: double
  • z: double
  • packet: Packet<?>

说明:

TODO

public Entity getEntity(int id) @ L1346

  • 方法名:getEntity
  • 源码定位:L1346
  • 返回类型:Entity
  • 修饰符:public

参数:

  • id: int

说明:

TODO

public Entity getEntityInAnyDimension(UUID uuid) @ L1351

  • 方法名:getEntityInAnyDimension
  • 源码定位:L1351
  • 返回类型:Entity
  • 修饰符:public

参数:

  • uuid: UUID

说明:

TODO

public Player getPlayerInAnyDimension(UUID uuid) @ L1370

  • 方法名:getPlayerInAnyDimension
  • 源码定位:L1370
  • 返回类型:Player
  • 修饰符:public

参数:

  • uuid: UUID

说明:

TODO

public Entity getEntityOrPart(int id) @ L1375

  • 方法名:getEntityOrPart
  • 源码定位:L1375
  • 返回类型:Entity
  • 修饰符:public

参数:

  • id: int

说明:

TODO

public Collection<EnderDragonPart> dragonParts() @ L1381

  • 方法名:dragonParts
  • 源码定位:L1381
  • 返回类型:Collection
  • 修饰符:public

参数:

说明:

TODO

public BlockPos findNearestMapStructure(TagKey<Structure> structureTag, BlockPos origin, int maxSearchRadius, boolean createReference) @ L1386

  • 方法名:findNearestMapStructure
  • 源码定位:L1386
  • 返回类型:BlockPos
  • 修饰符:public

参数:

  • structureTag: TagKey
  • origin: BlockPos
  • maxSearchRadius: int
  • createReference: boolean

说明:

TODO

public Pair<BlockPos,Holder<Biome>> findClosestBiome3d(Predicate<Holder<Biome>> biomeTest, BlockPos origin, int maxSearchRadius, int sampleResolutionHorizontal, int sampleResolutionVertical) @ L1402

  • 方法名:findClosestBiome3d
  • 源码定位:L1402
  • 返回类型:Pair<BlockPos,Holder>
  • 修饰符:public

参数:

  • biomeTest: Predicate<Holder>
  • origin: BlockPos
  • maxSearchRadius: int
  • sampleResolutionHorizontal: int
  • sampleResolutionVertical: int

说明:

TODO

public WorldBorder getWorldBorder() @ L1413

  • 方法名:getWorldBorder
  • 源码定位:L1413
  • 返回类型:WorldBorder
  • 修饰符:public

参数:

说明:

TODO

public RecipeManager recipeAccess() @ L1420

  • 方法名:recipeAccess
  • 源码定位:L1420
  • 返回类型:RecipeManager
  • 修饰符:public

参数:

说明:

TODO

public TickRateManager tickRateManager() @ L1424

  • 方法名:tickRateManager
  • 源码定位:L1424
  • 返回类型:TickRateManager
  • 修饰符:public

参数:

说明:

TODO

public boolean noSave() @ L1429

  • 方法名:noSave
  • 源码定位:L1429
  • 返回类型:boolean
  • 修饰符:public

参数:

说明:

TODO

public SavedDataStorage getDataStorage() @ L1434

  • 方法名:getDataStorage
  • 源码定位:L1434
  • 返回类型:SavedDataStorage
  • 修饰符:public

参数:

说明:

TODO

public MapItemSavedData getMapData(MapId id) @ L1438

  • 方法名:getMapData
  • 源码定位:L1438
  • 返回类型:MapItemSavedData
  • 修饰符:public

参数:

  • id: MapId

说明:

TODO

public void setMapData(MapId id, MapItemSavedData data) @ L1443

  • 方法名:setMapData
  • 源码定位:L1443
  • 返回类型:void
  • 修饰符:public

参数:

  • id: MapId
  • data: MapItemSavedData

说明:

TODO

public MapId getFreeMapId() @ L1447

  • 方法名:getFreeMapId
  • 源码定位:L1447
  • 返回类型:MapId
  • 修饰符:public

参数:

说明:

TODO

public void setRespawnData(LevelData.RespawnData respawnData) @ L1451

  • 方法名:setRespawnData
  • 源码定位:L1451
  • 返回类型:void
  • 修饰符:public

参数:

  • respawnData: LevelData.RespawnData

说明:

TODO

public LevelData.RespawnData getRespawnData() @ L1456

  • 方法名:getRespawnData
  • 源码定位:L1456
  • 返回类型:LevelData.RespawnData
  • 修饰符:public

参数:

说明:

TODO

public LongSet getForceLoadedChunks() @ L1461

  • 方法名:getForceLoadedChunks
  • 源码定位:L1461
  • 返回类型:LongSet
  • 修饰符:public

参数:

说明:

TODO

public boolean setChunkForced(int chunkX, int chunkZ, boolean forced) @ L1465

  • 方法名:setChunkForced
  • 源码定位:L1465
  • 返回类型:boolean
  • 修饰符:public

参数:

  • chunkX: int
  • chunkZ: int
  • forced: boolean

说明:

TODO

public List<ServerPlayer> players() @ L1474

  • 方法名:players
  • 源码定位:L1474
  • 返回类型:List
  • 修饰符:public

参数:

说明:

TODO

public void updatePOIOnBlockStateChange(BlockPos pos, BlockState oldState, BlockState newState) @ L1479

  • 方法名:updatePOIOnBlockStateChange
  • 源码定位:L1479
  • 返回类型:void
  • 修饰符:public

参数:

  • pos: BlockPos
  • oldState: BlockState
  • newState: BlockState

说明:

TODO

public PoiManager getPoiManager() @ L1498

  • 方法名:getPoiManager
  • 源码定位:L1498
  • 返回类型:PoiManager
  • 修饰符:public

参数:

说明:

TODO

public boolean isVillage(BlockPos pos) @ L1502

  • 方法名:isVillage
  • 源码定位:L1502
  • 返回类型:boolean
  • 修饰符:public

参数:

  • pos: BlockPos

说明:

TODO

public boolean isVillage(SectionPos sectionPos) @ L1506

  • 方法名:isVillage
  • 源码定位:L1506
  • 返回类型:boolean
  • 修饰符:public

参数:

  • sectionPos: SectionPos

说明:

TODO

public boolean isCloseToVillage(BlockPos pos, int sectionDistance) @ L1510

  • 方法名:isCloseToVillage
  • 源码定位:L1510
  • 返回类型:boolean
  • 修饰符:public

参数:

  • pos: BlockPos
  • sectionDistance: int

说明:

TODO

public int sectionsToVillage(SectionPos pos) @ L1514

  • 方法名:sectionsToVillage
  • 源码定位:L1514
  • 返回类型:int
  • 修饰符:public

参数:

  • pos: SectionPos

说明:

TODO

public Raids getRaids() @ L1518

  • 方法名:getRaids
  • 源码定位:L1518
  • 返回类型:Raids
  • 修饰符:public

参数:

说明:

TODO

public Raid getRaidAt(BlockPos pos) @ L1522

  • 方法名:getRaidAt
  • 源码定位:L1522
  • 返回类型:Raid
  • 修饰符:public

参数:

  • pos: BlockPos

说明:

TODO

public boolean isRaided(BlockPos pos) @ L1526

  • 方法名:isRaided
  • 源码定位:L1526
  • 返回类型:boolean
  • 修饰符:public

参数:

  • pos: BlockPos

说明:

TODO

public void onReputationEvent(ReputationEventType type, Entity source, ReputationEventHandler target) @ L1530

  • 方法名:onReputationEvent
  • 源码定位:L1530
  • 返回类型:void
  • 修饰符:public

参数:

  • type: ReputationEventType
  • source: Entity
  • target: ReputationEventHandler

说明:

TODO

public void saveDebugReport(Path rootDir) @ L1534

  • 方法名:saveDebugReport
  • 源码定位:L1534
  • 返回类型:void
  • 修饰符:public

参数:

  • rootDir: Path

说明:

TODO

private static void dumpEntities(Writer output, Iterable<Entity> entities) @ L1586

  • 方法名:dumpEntities
  • 源码定位:L1586
  • 返回类型:void
  • 修饰符:private static

参数:

  • output: Writer
  • entities: Iterable

说明:

TODO

private void dumpBlockEntityTickers(Writer output) @ L1614

  • 方法名:dumpBlockEntityTickers
  • 源码定位:L1614
  • 返回类型:void
  • 修饰符:private

参数:

  • output: Writer

说明:

TODO

public void clearBlockEvents(BoundingBox bb) @ L1623

  • 方法名:clearBlockEvents
  • 源码定位:L1623
  • 返回类型:void
  • 修饰符:public

参数:

  • bb: BoundingBox

说明:

TODO

public Iterable<Entity> getAllEntities() @ L1628

  • 方法名:getAllEntities
  • 源码定位:L1628
  • 返回类型:Iterable
  • 修饰符:public

参数:

说明:

TODO

public String toString() @ L1632

  • 方法名:toString
  • 源码定位:L1632
  • 返回类型:String
  • 修饰符:public

参数:

说明:

TODO

public boolean isFlat() @ L1637

  • 方法名:isFlat
  • 源码定位:L1637
  • 返回类型:boolean
  • 修饰符:public

参数:

说明:

TODO

public long getSeed() @ L1641

  • 方法名:getSeed
  • 源码定位:L1641
  • 返回类型:long
  • 修饰符:public

参数:

说明:

TODO

public EnderDragonFight getDragonFight() @ L1646

  • 方法名:getDragonFight
  • 源码定位:L1646
  • 返回类型:EnderDragonFight
  • 修饰符:public

参数:

说明:

TODO

public WeatherData getWeatherData() @ L1650

  • 方法名:getWeatherData
  • 源码定位:L1650
  • 返回类型:WeatherData
  • 修饰符:public

参数:

说明:

TODO

public ServerLevel getLevel() @ L1654

  • 方法名:getLevel
  • 源码定位:L1654
  • 返回类型:ServerLevel
  • 修饰符:public

参数:

说明:

TODO

public String getWatchdogStats() @ L1659

  • 方法名:getWatchdogStats
  • 源码定位:L1659
  • 返回类型:String
  • 修饰符:public

参数:

说明:

TODO

private static <T> String getTypeCount(Iterable<T> values, Function<T,String> typeGetter) @ L1675

  • 方法名:getTypeCount
  • 源码定位:L1675
  • 返回类型: String
  • 修饰符:private static

参数:

  • values: Iterable
  • typeGetter: Function<T,String>

说明:

TODO

protected LevelEntityGetter<Entity> getEntities() @ L1696

  • 方法名:getEntities
  • 源码定位:L1696
  • 返回类型:LevelEntityGetter
  • 修饰符:protected

参数:

说明:

TODO

public void addLegacyChunkEntities(Stream<Entity> loaded) @ L1701

  • 方法名:addLegacyChunkEntities
  • 源码定位:L1701
  • 返回类型:void
  • 修饰符:public

参数:

  • loaded: Stream

说明:

TODO

public void addWorldGenChunkEntities(Stream<Entity> loaded) @ L1705

  • 方法名:addWorldGenChunkEntities
  • 源码定位:L1705
  • 返回类型:void
  • 修饰符:public

参数:

  • loaded: Stream

说明:

TODO

public void startTickingChunk(LevelChunk levelChunk) @ L1709

  • 方法名:startTickingChunk
  • 源码定位:L1709
  • 返回类型:void
  • 修饰符:public

参数:

  • levelChunk: LevelChunk

说明:

TODO

public void onStructureStartsAvailable(ChunkAccess chunk) @ L1713

  • 方法名:onStructureStartsAvailable
  • 源码定位:L1713
  • 返回类型:void
  • 修饰符:public

参数:

  • chunk: ChunkAccess

说明:

TODO

public PathTypeCache getPathTypeCache() @ L1717

  • 方法名:getPathTypeCache
  • 源码定位:L1717
  • 返回类型:PathTypeCache
  • 修饰符:public

参数:

说明:

TODO

public void waitForEntities(ChunkPos centerChunk, int radius) @ L1721

  • 方法名:waitForEntities
  • 源码定位:L1721
  • 返回类型:void
  • 修饰符:public

参数:

  • centerChunk: ChunkPos
  • radius: int

说明:

TODO

public boolean isSpawningMonsters() @ L1736

  • 方法名:isSpawningMonsters
  • 源码定位:L1736
  • 返回类型:boolean
  • 修饰符:public

参数:

说明:

TODO

public void close() @ L1742

  • 方法名:close
  • 源码定位:L1742
  • 返回类型:void
  • 修饰符:public

参数:

说明:

TODO

public String gatherChunkSourceStats() @ L1748

  • 方法名:gatherChunkSourceStats
  • 源码定位:L1748
  • 返回类型:String
  • 修饰符:public

参数:

说明:

TODO

public boolean areEntitiesLoaded(long chunkKey) @ L1753

  • 方法名:areEntitiesLoaded
  • 源码定位:L1753
  • 返回类型:boolean
  • 修饰符:public

参数:

  • chunkKey: long

说明:

TODO

public boolean isPositionTickingWithEntitiesLoaded(long key) @ L1757

  • 方法名:isPositionTickingWithEntitiesLoaded
  • 源码定位:L1757
  • 返回类型:boolean
  • 修饰符:public

参数:

  • key: long

说明:

TODO

public boolean isPositionEntityTicking(BlockPos pos) @ L1761

  • 方法名:isPositionEntityTicking
  • 源码定位:L1761
  • 返回类型:boolean
  • 修饰符:public

参数:

  • pos: BlockPos

说明:

TODO

public boolean areEntitiesActuallyLoadedAndTicking(ChunkPos pos) @ L1765

  • 方法名:areEntitiesActuallyLoadedAndTicking
  • 源码定位:L1765
  • 返回类型:boolean
  • 修饰符:public

参数:

  • pos: ChunkPos

说明:

TODO

public boolean anyPlayerCloseEnoughForSpawning(BlockPos pos) @ L1769

  • 方法名:anyPlayerCloseEnoughForSpawning
  • 源码定位:L1769
  • 返回类型:boolean
  • 修饰符:public

参数:

  • pos: BlockPos

说明:

TODO

public boolean anyPlayerCloseEnoughForSpawning(ChunkPos pos) @ L1773

  • 方法名:anyPlayerCloseEnoughForSpawning
  • 源码定位:L1773
  • 返回类型:boolean
  • 修饰符:public

参数:

  • pos: ChunkPos

说明:

TODO

public boolean canSpreadFireAround(BlockPos pos) @ L1777

  • 方法名:canSpreadFireAround
  • 源码定位:L1777
  • 返回类型:boolean
  • 修饰符:public

参数:

  • pos: BlockPos

说明:

TODO

public boolean canSpawnEntitiesInChunk(ChunkPos pos) @ L1782

  • 方法名:canSpawnEntitiesInChunk
  • 源码定位:L1782
  • 返回类型:boolean
  • 修饰符:public

参数:

  • pos: ChunkPos

说明:

TODO

public FeatureFlagSet enabledFeatures() @ L1786

  • 方法名:enabledFeatures
  • 源码定位:L1786
  • 返回类型:FeatureFlagSet
  • 修饰符:public

参数:

说明:

TODO

public PotionBrewing potionBrewing() @ L1791

  • 方法名:potionBrewing
  • 源码定位:L1791
  • 返回类型:PotionBrewing
  • 修饰符:public

参数:

说明:

TODO

public FuelValues fuelValues() @ L1796

  • 方法名:fuelValues
  • 源码定位:L1796
  • 返回类型:FuelValues
  • 修饰符:public

参数:

说明:

TODO

public GameRules getGameRules() @ L1801

  • 方法名:getGameRules
  • 源码定位:L1801
  • 返回类型:GameRules
  • 修饰符:public

参数:

说明:

TODO

public CrashReportCategory fillReportDetails(CrashReport report) @ L1805

  • 方法名:fillReportDetails
  • 源码定位:L1805
  • 返回类型:CrashReportCategory
  • 修饰符:public

参数:

  • report: CrashReport

说明:

TODO

public int getSeaLevel() @ L1824

  • 方法名:getSeaLevel
  • 源码定位:L1824
  • 返回类型:int
  • 修饰符:public

参数:

说明:

TODO

public void onBlockEntityAdded(BlockEntity blockEntity) @ L1829

  • 方法名:onBlockEntityAdded
  • 源码定位:L1829
  • 返回类型:void
  • 修饰符:public

参数:

  • blockEntity: BlockEntity

说明:

TODO

public LevelDebugSynchronizers debugSynchronizers() @ L1835

  • 方法名:debugSynchronizers
  • 源码定位:L1835
  • 返回类型:LevelDebugSynchronizers
  • 修饰符:public

参数:

说明:

TODO

public boolean isAllowedToEnterPortal(Level toLevel) @ L1839

  • 方法名:isAllowedToEnterPortal
  • 源码定位:L1839
  • 返回类型:boolean
  • 修饰符:public

参数:

  • toLevel: Level

说明:

TODO

public boolean isPvpAllowed() @ L1843

  • 方法名:isPvpAllowed
  • 源码定位:L1843
  • 返回类型:boolean
  • 修饰符:public

参数:

说明:

TODO

public boolean isCommandBlockEnabled() @ L1847

  • 方法名:isCommandBlockEnabled
  • 源码定位:L1847
  • 返回类型:boolean
  • 修饰符:public

参数:

说明:

TODO

public boolean isSpawnerBlockEnabled() @ L1851

  • 方法名:isSpawnerBlockEnabled
  • 源码定位:L1851
  • 返回类型:boolean
  • 修饰符:public

参数:

说明:

TODO

代码

public class ServerLevel extends Level implements WorldGenLevel, ServerEntityGetter {
    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;
    private final List<ServerPlayer> players = Lists.newArrayList();
    private final ServerChunkCache chunkSource;
    private final MinecraftServer server;
    private final ServerLevelData serverLevelData;
    private final EntityTickList entityTickList = new EntityTickList();
    private final ServerWaypointManager waypointManager;
    private EnvironmentAttributeSystem environmentAttributes;
    private final PersistentEntitySectionManager<Entity> entityManager;
    private final GameEventDispatcher gameEventDispatcher;
    public boolean noSave;
    private final SleepStatus sleepStatus;
    private int emptyTime;
    private final PortalForcer portalForcer;
    private final LevelTicks<Block> blockTicks = new LevelTicks<>(this::isPositionTickingWithEntitiesLoaded);
    private final LevelTicks<Fluid> fluidTicks = new LevelTicks<>(this::isPositionTickingWithEntitiesLoaded);
    private final PathTypeCache pathTypesByPosCache = new PathTypeCache();
    private final Set<Mob> navigatingMobs = new ObjectOpenHashSet<>();
    private volatile boolean isUpdatingNavigations;
    protected final Raids raids;
    private final ObjectLinkedOpenHashSet<BlockEventData> blockEvents = new ObjectLinkedOpenHashSet<>();
    private final List<BlockEventData> blockEventsToReschedule = new ArrayList<>(64);
    private boolean handlingTick;
    private final List<CustomSpawner> customSpawners;
    private @Nullable EnderDragonFight dragonFight;
    private final Int2ObjectMap<EnderDragonPart> dragonParts = new Int2ObjectOpenHashMap<>();
    private final StructureManager structureManager;
    private final StructureCheck structureCheck;
    private final boolean tickTime;
    private final LevelDebugSynchronizers debugSynchronizers = new LevelDebugSynchronizers(this);
 
    public ServerLevel(
        MinecraftServer server,
        Executor executor,
        LevelStorageSource.LevelStorageAccess levelStorage,
        ServerLevelData levelData,
        ResourceKey<Level> dimension,
        LevelStem levelStem,
        boolean isDebug,
        long biomeZoomSeed,
        List<CustomSpawner> customSpawners,
        boolean tickTime
    ) {
        super(levelData, dimension, server.registryAccess(), levelStem.type(), false, isDebug, biomeZoomSeed, server.getMaxChainedNeighborUpdates());
        this.tickTime = tickTime;
        this.server = server;
        this.customSpawners = customSpawners;
        this.serverLevelData = levelData;
        ChunkGenerator generator = levelStem.generator();
        boolean syncWrites = server.forceSynchronousWrites();
        DataFixer fixerUpper = server.getFixerUpper();
        EntityPersistentStorage<Entity> entityStorage = new EntityStorage(
            new SimpleRegionStorage(
                new RegionStorageInfo(levelStorage.getLevelId(), dimension, "entities"),
                levelStorage.getDimensionPath(dimension).resolve("entities"),
                fixerUpper,
                syncWrites,
                DataFixTypes.ENTITY_CHUNK
            ),
            this,
            server
        );
        this.entityManager = new PersistentEntitySectionManager<>(Entity.class, new ServerLevel.EntityCallbacks(), entityStorage);
        this.chunkSource = new ServerChunkCache(
            this,
            levelStorage,
            fixerUpper,
            server.getStructureManager(),
            executor,
            generator,
            server.getPlayerList().getViewDistance(),
            server.getPlayerList().getSimulationDistance(),
            syncWrites,
            this.entityManager::updateChunkStatus,
            () -> server.overworld().getDataStorage()
        );
        this.chunkSource.getGeneratorState().ensureStructuresGenerated();
        this.portalForcer = new PortalForcer(this);
        if (this.canHaveWeather()) {
            this.prepareWeather(server.getWeatherData());
        }
 
        this.raids = this.getDataStorage().computeIfAbsent(Raids.TYPE);
        if (!server.isSingleplayer()) {
            levelData.setGameType(server.getDefaultGameType());
        }
 
        WorldGenSettings worldGenSettings = server.getWorldGenSettings();
        WorldOptions options = worldGenSettings.options();
        long seed = options.seed();
        this.structureCheck = new StructureCheck(
            this.chunkSource.chunkScanner(),
            this.registryAccess(),
            server.getStructureManager(),
            dimension,
            generator,
            this.chunkSource.randomState(),
            this,
            generator.getBiomeSource(),
            seed,
            fixerUpper
        );
        this.structureManager = new StructureManager(this, options, this.structureCheck);
        if (this.dimensionType().hasEnderDragonFight()) {
            this.dragonFight = this.getDataStorage().computeIfAbsent(EnderDragonFight.TYPE);
            this.dragonFight.init(this, seed, BlockPos.ZERO);
        }
 
        this.sleepStatus = new SleepStatus();
        this.gameEventDispatcher = new GameEventDispatcher(this);
        this.waypointManager = new ServerWaypointManager();
        this.environmentAttributes = EnvironmentAttributeSystem.builder().addDefaultLayers(this).build();
        this.updateSkyBrightness();
    }
 
    @Deprecated
    @VisibleForTesting
    public void setDragonFight(@Nullable EnderDragonFight fight) {
        this.dragonFight = fight;
    }
 
    @Override
    public Holder<Biome> getUncachedNoiseBiome(int quartX, int quartY, int quartZ) {
        return this.getChunkSource().getGenerator().getBiomeSource().getNoiseBiome(quartX, quartY, quartZ, this.getChunkSource().randomState().sampler());
    }
 
    public StructureManager structureManager() {
        return this.structureManager;
    }
 
    public ServerClockManager clockManager() {
        return this.server.clockManager();
    }
 
    @Override
    public EnvironmentAttributeSystem environmentAttributes() {
        return this.environmentAttributes;
    }
 
    @Deprecated
    @VisibleForTesting
    public EnvironmentAttributeSystem setEnvironmentAttributes(EnvironmentAttributeSystem environmentAttributes) {
        EnvironmentAttributeSystem previous = this.environmentAttributes;
        this.environmentAttributes = environmentAttributes;
        return previous;
    }
 
    public void tick(BooleanSupplier haveTime) {
        ProfilerFiller profiler = Profiler.get();
        this.handlingTick = true;
        TickRateManager tickRateManager = this.tickRateManager();
        boolean runs = tickRateManager.runsNormally();
        if (runs) {
            profiler.push("world border");
            this.getWorldBorder().tick();
            profiler.popPush("weather");
            this.advanceWeatherCycle();
            profiler.pop();
        }
 
        int percentage = this.getGameRules().get(GameRules.PLAYERS_SLEEPING_PERCENTAGE);
        if (this.sleepStatus.areEnoughSleeping(percentage) && this.sleepStatus.areEnoughDeepSleeping(percentage, this.players)) {
            Optional<Holder<WorldClock>> defaultClock = this.dimensionType().defaultClock();
            if (this.getGameRules().get(GameRules.ADVANCE_TIME) && defaultClock.isPresent()) {
                this.server.clockManager().moveToTimeMarker(defaultClock.get(), ClockTimeMarkers.WAKE_UP_FROM_SLEEP);
            }
 
            this.wakeUpAllPlayers();
            if (this.getGameRules().get(GameRules.ADVANCE_WEATHER) && this.isRaining()) {
                this.resetWeatherCycle();
            }
        }
 
        this.updateSkyBrightness();
        if (runs) {
            this.tickTime();
        }
 
        profiler.push("tickPending");
        if (!this.isDebug() && runs) {
            long tick = this.getGameTime();
            profiler.push("blockTicks");
            this.blockTicks.tick(tick, 65536, this::tickBlock);
            profiler.popPush("fluidTicks");
            this.fluidTicks.tick(tick, 65536, this::tickFluid);
            profiler.pop();
        }
 
        profiler.popPush("raid");
        if (runs) {
            this.raids.tick(this);
        }
 
        profiler.popPush("chunkSource");
        this.getChunkSource().tick(haveTime, true);
        profiler.popPush("blockEvents");
        if (runs) {
            this.runBlockEvents();
        }
 
        this.handlingTick = false;
        profiler.pop();
        boolean isActive = this.chunkSource.hasActiveTickets();
        if (isActive) {
            this.resetEmptyTime();
        }
 
        if (runs) {
            this.emptyTime++;
        }
 
        if (this.emptyTime < 300) {
            profiler.push("entities");
            if (this.dragonFight != null && runs) {
                profiler.push("dragonFight");
                this.dragonFight.tick();
                profiler.pop();
            }
 
            this.entityTickList
                .forEach(
                    entity -> {
                        if (!entity.isRemoved()) {
                            if (!tickRateManager.isEntityFrozen(entity)) {
                                profiler.push("checkDespawn");
                                entity.checkDespawn();
                                profiler.pop();
                                if (entity instanceof ServerPlayer
                                    || this.chunkSource.chunkMap.getDistanceManager().inEntityTickingRange(entity.chunkPosition().pack())) {
                                    Entity vehicle = entity.getVehicle();
                                    if (vehicle != null) {
                                        if (!vehicle.isRemoved() && vehicle.hasPassenger(entity)) {
                                            return;
                                        }
 
                                        entity.stopRiding();
                                    }
 
                                    profiler.push("tick");
                                    this.guardEntityTick(this::tickNonPassenger, entity);
                                    profiler.pop();
                                }
                            }
                        }
                    }
                );
            profiler.popPush("blockEntities");
            this.tickBlockEntities();
            profiler.pop();
        }
 
        profiler.push("entityManagement");
        this.entityManager.tick();
        profiler.pop();
        profiler.push("debugSynchronizers");
        if (this.debugSynchronizers.hasAnySubscriberFor(DebugSubscriptions.NEIGHBOR_UPDATES)) {
            this.neighborUpdater
                .setDebugListener(blockPos -> this.debugSynchronizers.broadcastEventToTracking(blockPos, DebugSubscriptions.NEIGHBOR_UPDATES, blockPos));
        } else {
            this.neighborUpdater.setDebugListener(null);
        }
 
        this.debugSynchronizers.tick(this.server.debugSubscribers());
        profiler.pop();
        this.environmentAttributes().invalidateTickCache();
    }
 
    @Override
    public boolean shouldTickBlocksAt(long chunkPos) {
        return this.chunkSource.chunkMap.getDistanceManager().inBlockTickingRange(chunkPos);
    }
 
    protected void tickTime() {
        if (this.tickTime) {
            long time = this.levelData.getGameTime() + 1L;
            this.serverLevelData.setGameTime(time);
            Profiler.get().push("scheduledFunctions");
            this.server.getScheduledEvents().tick(this.server, time);
            Profiler.get().pop();
        }
    }
 
    public void tickCustomSpawners(boolean spawnEnemies) {
        for (CustomSpawner spawner : this.customSpawners) {
            spawner.tick(this, spawnEnemies);
        }
    }
 
    private void wakeUpAllPlayers() {
        this.sleepStatus.removeAllSleepers();
        this.players.stream().filter(LivingEntity::isSleeping).collect(Collectors.toList()).forEach(player -> player.stopSleepInBed(false, false));
    }
 
    public void tickChunk(LevelChunk chunk, int tickSpeed) {
        ChunkPos chunkPos = chunk.getPos();
        int minX = chunkPos.getMinBlockX();
        int minZ = chunkPos.getMinBlockZ();
        ProfilerFiller profiler = Profiler.get();
        profiler.push("iceandsnow");
 
        for (int i = 0; i < tickSpeed; i++) {
            if (this.random.nextInt(48) == 0) {
                this.tickPrecipitation(this.getBlockRandomPos(minX, 0, minZ, 15));
            }
        }
 
        profiler.popPush("tickBlocks");
        if (tickSpeed > 0) {
            LevelChunkSection[] sections = chunk.getSections();
 
            for (int sectionIndex = 0; sectionIndex < sections.length; sectionIndex++) {
                LevelChunkSection section = sections[sectionIndex];
                if (section.isRandomlyTicking()) {
                    int sectionY = chunk.getSectionYFromSectionIndex(sectionIndex);
                    int minYInSection = SectionPos.sectionToBlockCoord(sectionY);
 
                    for (int ix = 0; ix < tickSpeed; ix++) {
                        BlockPos pos = this.getBlockRandomPos(minX, minYInSection, minZ, 15);
                        profiler.push("randomTick");
                        BlockState blockState = section.getBlockState(pos.getX() - minX, pos.getY() - minYInSection, pos.getZ() - minZ);
                        if (blockState.isRandomlyTicking()) {
                            blockState.randomTick(this, pos, this.random);
                        }
 
                        FluidState fluidState = blockState.getFluidState();
                        if (fluidState.isRandomlyTicking()) {
                            fluidState.randomTick(this, pos, this.random);
                        }
 
                        profiler.pop();
                    }
                }
            }
        }
 
        profiler.pop();
    }
 
    public void tickThunder(LevelChunk chunk) {
        ChunkPos chunkPos = chunk.getPos();
        boolean raining = this.isRaining();
        int minX = chunkPos.getMinBlockX();
        int minZ = chunkPos.getMinBlockZ();
        ProfilerFiller profiler = Profiler.get();
        profiler.push("thunder");
        if (raining && this.isThundering() && this.random.nextInt(100000) == 0) {
            BlockPos pos = this.findLightningTargetAround(this.getBlockRandomPos(minX, 0, minZ, 15));
            if (this.isRainingAt(pos)) {
                DifficultyInstance difficulty = this.getCurrentDifficultyAt(pos);
                boolean isTrap = this.getGameRules().get(GameRules.SPAWN_MOBS)
                    && this.random.nextDouble() < difficulty.getEffectiveDifficulty() * 0.01
                    && !this.getBlockState(pos.below()).is(BlockTags.LIGHTNING_RODS);
                if (isTrap) {
                    SkeletonHorse horse = EntityType.SKELETON_HORSE.create(this, EntitySpawnReason.EVENT);
                    if (horse != null) {
                        horse.setTrap(true);
                        horse.setAge(0);
                        horse.setPos(pos.getX(), pos.getY(), pos.getZ());
                        this.addFreshEntity(horse);
                    }
                }
 
                LightningBolt bolt = EntityType.LIGHTNING_BOLT.create(this, EntitySpawnReason.EVENT);
                if (bolt != null) {
                    bolt.snapTo(Vec3.atBottomCenterOf(pos));
                    bolt.setVisualOnly(isTrap);
                    this.addFreshEntity(bolt);
                }
            }
        }
 
        profiler.pop();
    }
 
    @VisibleForTesting
    public void tickPrecipitation(BlockPos pos) {
        BlockPos topPos = this.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, pos);
        BlockPos belowPos = topPos.below();
        Biome biome = this.getBiome(topPos).value();
        if (biome.shouldFreeze(this, belowPos)) {
            this.setBlockAndUpdate(belowPos, Blocks.ICE.defaultBlockState());
        }
 
        if (this.isRaining()) {
            int maxHeight = this.getGameRules().get(GameRules.MAX_SNOW_ACCUMULATION_HEIGHT);
            if (maxHeight > 0 && biome.shouldSnow(this, topPos)) {
                BlockState state = this.getBlockState(topPos);
                if (state.is(Blocks.SNOW)) {
                    int currentLayers = state.getValue(SnowLayerBlock.LAYERS);
                    if (currentLayers < Math.min(maxHeight, 8)) {
                        BlockState newState = state.setValue(SnowLayerBlock.LAYERS, currentLayers + 1);
                        Block.pushEntitiesUp(state, newState, this, topPos);
                        this.setBlockAndUpdate(topPos, newState);
                    }
                } else {
                    this.setBlockAndUpdate(topPos, Blocks.SNOW.defaultBlockState());
                }
            }
 
            Biome.Precipitation precipitation = biome.getPrecipitationAt(belowPos, this.getSeaLevel());
            if (precipitation != Biome.Precipitation.NONE) {
                BlockState belowState = this.getBlockState(belowPos);
                belowState.getBlock().handlePrecipitation(belowState, this, belowPos, precipitation);
            }
        }
    }
 
    private Optional<BlockPos> findLightningRod(BlockPos center) {
        Optional<BlockPos> nearbyLightningRod = this.getPoiManager()
            .findClosest(
                p -> p.is(PoiTypes.LIGHTNING_ROD),
                lightningRodPos -> lightningRodPos.getY() == this.getHeight(Heightmap.Types.WORLD_SURFACE, lightningRodPos.getX(), lightningRodPos.getZ()) - 1,
                center,
                128,
                PoiManager.Occupancy.ANY
            );
        return nearbyLightningRod.map(blockPos -> blockPos.above(1));
    }
 
    protected BlockPos findLightningTargetAround(BlockPos pos) {
        BlockPos center = this.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, pos);
        Optional<BlockPos> lightningRodTarget = this.findLightningRod(center);
        if (lightningRodTarget.isPresent()) {
            return lightningRodTarget.get();
        } else {
            AABB search = AABB.encapsulatingFullBlocks(center, center.atY(this.getMaxY() + 1)).inflate(3.0);
            List<LivingEntity> entities = this.getEntitiesOfClass(LivingEntity.class, search, input -> input.isAlive() && this.canSeeSky(input.blockPosition()));
            if (!entities.isEmpty()) {
                return entities.get(this.random.nextInt(entities.size())).blockPosition();
            } else {
                if (center.getY() == this.getMinY() - 1) {
                    center = center.above(2);
                }
 
                return center;
            }
        }
    }
 
    public boolean isHandlingTick() {
        return this.handlingTick;
    }
 
    public boolean canSleepThroughNights() {
        return this.getGameRules().get(GameRules.PLAYERS_SLEEPING_PERCENTAGE) <= 100;
    }
 
    private void announceSleepStatus() {
        if (this.canSleepThroughNights()) {
            if (!this.getServer().isSingleplayer() || this.getServer().isPublished()) {
                int percentage = this.getGameRules().get(GameRules.PLAYERS_SLEEPING_PERCENTAGE);
                Component message;
                if (this.sleepStatus.areEnoughSleeping(percentage)) {
                    message = Component.translatable("sleep.skipping_night");
                } else {
                    message = Component.translatable("sleep.players_sleeping", this.sleepStatus.amountSleeping(), this.sleepStatus.sleepersNeeded(percentage));
                }
 
                for (ServerPlayer player : this.players) {
                    player.sendOverlayMessage(message);
                }
            }
        }
    }
 
    public void updateSleepingPlayerList() {
        if (!this.players.isEmpty() && this.sleepStatus.update(this.players)) {
            this.announceSleepStatus();
        }
    }
 
    public ServerScoreboard getScoreboard() {
        return this.server.getScoreboard();
    }
 
    public ServerWaypointManager getWaypointManager() {
        return this.waypointManager;
    }
 
    @Override
    public DifficultyInstance getCurrentDifficultyAt(BlockPos pos) {
        long localTime = 0L;
        float moonBrightness = 0.0F;
        ChunkAccess chunk = this.getChunk(SectionPos.blockToSectionCoord(pos.getX()), SectionPos.blockToSectionCoord(pos.getZ()), ChunkStatus.FULL, false);
        if (chunk != null) {
            localTime = chunk.getInhabitedTime();
            moonBrightness = this.getMoonBrightness(pos);
        }
 
        return new DifficultyInstance(this.getDifficulty(), this.getOverworldClockTime(), localTime, moonBrightness);
    }
 
    public float getMoonBrightness(BlockPos pos) {
        MoonPhase moonPhase = this.environmentAttributes.getValue(EnvironmentAttributes.MOON_PHASE, pos);
        return DimensionType.MOON_BRIGHTNESS_PER_PHASE[moonPhase.index()];
    }
 
    private void prepareWeather(WeatherData weatherData) {
        if (weatherData.isRaining()) {
            this.rainLevel = 1.0F;
            if (weatherData.isThundering()) {
                this.thunderLevel = 1.0F;
            }
        }
    }
 
    private void advanceWeatherCycle() {
        boolean wasRaining = this.isRaining();
        if (this.canHaveWeather()) {
            WeatherData weatherData = this.getWeatherData();
            if (this.getGameRules().get(GameRules.ADVANCE_WEATHER)) {
                int clearWeatherTime = weatherData.getClearWeatherTime();
                int thunderTime = weatherData.getThunderTime();
                int rainTime = weatherData.getRainTime();
                boolean thundering = weatherData.isThundering();
                boolean raining = weatherData.isRaining();
                if (clearWeatherTime > 0) {
                    clearWeatherTime--;
                    thunderTime = thundering ? 0 : 1;
                    rainTime = raining ? 0 : 1;
                    thundering = false;
                    raining = false;
                } else {
                    if (thunderTime > 0) {
                        if (--thunderTime == 0) {
                            thundering = !thundering;
                        }
                    } else if (thundering) {
                        thunderTime = THUNDER_DURATION.sample(this.random);
                    } else {
                        thunderTime = THUNDER_DELAY.sample(this.random);
                    }
 
                    if (rainTime > 0) {
                        if (--rainTime == 0) {
                            raining = !raining;
                        }
                    } else if (raining) {
                        rainTime = RAIN_DURATION.sample(this.random);
                    } else {
                        rainTime = RAIN_DELAY.sample(this.random);
                    }
                }
 
                weatherData.setThunderTime(thunderTime);
                weatherData.setRainTime(rainTime);
                weatherData.setClearWeatherTime(clearWeatherTime);
                weatherData.setThundering(thundering);
                weatherData.setRaining(raining);
            }
 
            this.oThunderLevel = this.thunderLevel;
            if (weatherData.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 (weatherData.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 (wasRaining != this.isRaining()) {
            if (wasRaining) {
                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() {
        WeatherData weatherData = this.getWeatherData();
        weatherData.setRainTime(0);
        weatherData.setRaining(false);
        weatherData.setThunderTime(0);
        weatherData.setThundering(false);
    }
 
    public void resetEmptyTime() {
        this.emptyTime = 0;
    }
 
    private void tickFluid(BlockPos pos, Fluid type) {
        BlockState blockState = this.getBlockState(pos);
        FluidState fluidState = blockState.getFluidState();
        if (fluidState.is(type)) {
            fluidState.tick(this, pos, blockState);
        }
    }
 
    private void tickBlock(BlockPos pos, Block type) {
        BlockState state = this.getBlockState(pos);
        if (state.is(type)) {
            state.tick(this, pos, this.random);
        }
    }
 
    public void tickNonPassenger(Entity entity) {
        entity.setOldPosAndRot();
        ProfilerFiller profiler = Profiler.get();
        entity.tickCount++;
        profiler.push(entity.typeHolder()::getRegisteredName);
        profiler.incrementCounter("tickNonPassenger");
        entity.tick();
        profiler.pop();
 
        for (Entity passenger : entity.getPassengers()) {
            this.tickPassenger(entity, passenger);
        }
    }
 
    private void tickPassenger(Entity vehicle, Entity entity) {
        if (entity.isRemoved() || entity.getVehicle() != vehicle) {
            entity.stopRiding();
        } else if (entity instanceof Player || this.entityTickList.contains(entity)) {
            entity.setOldPosAndRot();
            entity.tickCount++;
            ProfilerFiller profiler = Profiler.get();
            profiler.push(entity.typeHolder()::getRegisteredName);
            profiler.incrementCounter("tickPassenger");
            entity.rideTick();
            profiler.pop();
 
            for (Entity passenger : entity.getPassengers()) {
                this.tickPassenger(entity, passenger);
            }
        }
    }
 
    public void updateNeighboursOnBlockSet(BlockPos pos, BlockState oldState) {
        BlockState blockState = this.getBlockState(pos);
        Block newBlock = blockState.getBlock();
        boolean blockChanged = !oldState.is(newBlock);
        if (blockChanged) {
            oldState.affectNeighborsAfterRemoval(this, pos, false);
        }
 
        this.updateNeighborsAt(pos, blockState.getBlock());
        if (blockState.hasAnalogOutputSignal()) {
            this.updateNeighbourForOutputSignal(pos, newBlock);
        }
    }
 
    @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 progressListener, boolean flush, boolean noSave) {
        ServerChunkCache chunkSource = this.getChunkSource();
        if (!noSave) {
            if (progressListener != null) {
                progressListener.progressStartNoAbort(Component.translatable("menu.savingLevel"));
            }
 
            this.saveLevelData(flush);
            if (progressListener != null) {
                progressListener.progressStage(Component.translatable("menu.savingChunks"));
            }
 
            chunkSource.save(flush);
            if (flush) {
                this.entityManager.saveAll();
            } else {
                this.entityManager.autoSave();
            }
        }
    }
 
    private void saveLevelData(boolean sync) {
        SavedDataStorage savedDataStorage = this.getChunkSource().getDataStorage();
        if (sync) {
            savedDataStorage.saveAndJoin();
        } else {
            savedDataStorage.scheduleSave();
        }
    }
 
    public <T extends Entity> List<? extends T> getEntities(EntityTypeTest<Entity, T> type, Predicate<? super T> selector) {
        List<T> result = Lists.newArrayList();
        this.getEntities(type, selector, result);
        return result;
    }
 
    public <T extends Entity> void getEntities(EntityTypeTest<Entity, T> type, Predicate<? super T> selector, List<? super T> result) {
        this.getEntities(type, selector, result, Integer.MAX_VALUE);
    }
 
    public <T extends Entity> void getEntities(EntityTypeTest<Entity, T> type, Predicate<? super T> selector, List<? super T> result, int maxResults) {
        this.getEntities().get(type, entity -> {
            if (selector.test(entity)) {
                result.add(entity);
                if (result.size() >= maxResults) {
                    return AbortableIterationConsumer.Continuation.ABORT;
                }
            }
 
            return AbortableIterationConsumer.Continuation.CONTINUE;
        });
    }
 
    public List<? extends EnderDragon> getDragons() {
        return this.getEntities(EntityType.ENDER_DRAGON, LivingEntity::isAlive);
    }
 
    public List<ServerPlayer> getPlayers(Predicate<? super ServerPlayer> selector) {
        return this.getPlayers(selector, Integer.MAX_VALUE);
    }
 
    public List<ServerPlayer> getPlayers(Predicate<? super ServerPlayer> selector, int maxResults) {
        List<ServerPlayer> result = Lists.newArrayList();
 
        for (ServerPlayer player : this.players) {
            if (selector.test(player)) {
                result.add(player);
                if (result.size() >= maxResults) {
                    return result;
                }
            }
        }
 
        return result;
    }
 
    public @Nullable ServerPlayer getRandomPlayer() {
        List<ServerPlayer> players = this.getPlayers(LivingEntity::isAlive);
        return players.isEmpty() ? null : players.get(this.random.nextInt(players.size()));
    }
 
    @Override
    public boolean addFreshEntity(Entity entity) {
        return this.addEntity(entity);
    }
 
    public boolean addWithUUID(Entity entity) {
        return this.addEntity(entity);
    }
 
    public void addDuringTeleport(Entity entity) {
        if (entity instanceof ServerPlayer player) {
            this.addPlayer(player);
        } 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 existing = this.getEntity(player.getUUID());
        if (existing != null) {
            LOGGER.warn("Force-added player with duplicate UUID {}", player.getUUID());
            existing.unRide();
            this.removePlayerImmediately((ServerPlayer)existing, Entity.RemovalReason.DISCARDED);
        }
 
        this.entityManager.addNewEntity(player);
    }
 
    private boolean addEntity(Entity entity) {
        if (entity.isRemoved()) {
            LOGGER.warn("Tried to add entity {} but it was marked as removed already", entity.typeHolder().getRegisteredName());
            return false;
        } else {
            return this.entityManager.addNewEntity(entity);
        }
    }
 
    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 levelChunk) {
        levelChunk.clearAllBlockEntities();
        levelChunk.unregisterTickContainerFromLevel(this);
        this.debugSynchronizers.dropChunk(levelChunk.getPos());
    }
 
    public void removePlayerImmediately(ServerPlayer player, Entity.RemovalReason reason) {
        player.remove(reason);
    }
 
    @Override
    public void destroyBlockProgress(int id, BlockPos blockPos, int progress) {
        for (ServerPlayer player : this.server.getPlayerList().getPlayers()) {
            if (player.level() == this && player.getId() != id) {
                double xd = blockPos.getX() - player.getX();
                double yd = blockPos.getY() - player.getY();
                double zd = blockPos.getZ() - player.getZ();
                if (xd * xd + yd * yd + zd * zd < 1024.0) {
                    player.connection.send(new ClientboundBlockDestructionPacket(id, blockPos, progress));
                }
            }
        }
    }
 
    @Override
    public void playSeededSound(
        @Nullable Entity except, double x, double y, double z, Holder<SoundEvent> sound, SoundSource source, float volume, float pitch, long seed
    ) {
        this.server
            .getPlayerList()
            .broadcast(
                except 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 except, Entity sourceEntity, Holder<SoundEvent> sound, SoundSource source, float volume, float pitch, long seed
    ) {
        this.server
            .getPlayerList()
            .broadcast(
                except 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 type, BlockPos pos, int data) {
        if (this.getGameRules().get(GameRules.GLOBAL_SOUND_EVENTS)) {
            this.server.getPlayerList().getPlayers().forEach(player -> {
                Vec3 soundPos;
                if (player.level() == this) {
                    Vec3 centerOfBlock = Vec3.atCenterOf(pos);
                    if (player.distanceToSqr(centerOfBlock) < Mth.square(32)) {
                        soundPos = centerOfBlock;
                    } else {
                        Vec3 directionToEvent = centerOfBlock.subtract(player.position()).normalize();
                        soundPos = player.position().add(directionToEvent.scale(32.0));
                    }
                } else {
                    soundPos = player.position();
                }
 
                player.connection.send(new ClientboundLevelEventPacket(type, BlockPos.containing(soundPos), data, true));
            });
        } else {
            this.levelEvent(null, type, pos, data);
        }
    }
 
    @Override
    public void levelEvent(@Nullable Entity source, int type, BlockPos pos, int data) {
        this.server
            .getPlayerList()
            .broadcast(
                source 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> gameEvent, Vec3 position, GameEvent.Context context) {
        this.gameEventDispatcher.post(gameEvent, position, context);
    }
 
    @Override
    public void sendBlockUpdated(BlockPos pos, BlockState old, BlockState current, int updateFlags) {
        if (this.isUpdatingNavigations) {
            String message = "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 oldShape = old.getCollisionShape(this, pos);
        VoxelShape newShape = current.getCollisionShape(this, pos);
        if (Shapes.joinIsNotEmpty(oldShape, newShape, BooleanOp.NOT_SAME)) {
            List<PathNavigation> navigationsToUpdate = new ObjectArrayList<>();
 
            for (Mob navigatingMob : this.navigatingMobs) {
                PathNavigation pathNavigation = navigatingMob.getNavigation();
                if (pathNavigation.shouldRecomputePath(pos)) {
                    navigationsToUpdate.add(pathNavigation);
                }
            }
 
            try {
                this.isUpdatingNavigations = true;
 
                for (PathNavigation navigation : navigationsToUpdate) {
                    navigation.recomputePath();
                }
            } finally {
                this.isUpdatingNavigations = false;
            }
        }
    }
 
    @Override
    public void updateNeighborsAt(BlockPos pos, Block sourceBlock) {
        this.updateNeighborsAt(pos, sourceBlock, ExperimentalRedstoneUtils.initialOrientation(this, null, null));
    }
 
    @Override
    public void updateNeighborsAt(BlockPos pos, Block sourceBlock, @Nullable Orientation orientation) {
        this.neighborUpdater.updateNeighborsAtExceptFromFacing(pos, sourceBlock, null, orientation);
    }
 
    @Override
    public void updateNeighborsAtExceptFromFacing(BlockPos pos, Block blockObject, Direction skipDirection, @Nullable Orientation orientation) {
        this.neighborUpdater.updateNeighborsAtExceptFromFacing(pos, blockObject, skipDirection, orientation);
    }
 
    @Override
    public void neighborChanged(BlockPos pos, Block changedBlock, @Nullable Orientation orientation) {
        this.neighborUpdater.neighborChanged(pos, changedBlock, orientation);
    }
 
    @Override
    public void neighborChanged(BlockState state, BlockPos pos, Block changedBlock, @Nullable Orientation orientation, boolean movedByPiston) {
        this.neighborUpdater.neighborChanged(state, pos, changedBlock, orientation, movedByPiston);
    }
 
    @Override
    public void broadcastEntityEvent(Entity entity, byte event) {
        this.getChunkSource().sendToTrackingPlayersAndSelf(entity, new ClientboundEntityEventPacket(entity, event));
    }
 
    @Override
    public void broadcastDamageEvent(Entity entity, DamageSource source) {
        this.getChunkSource().sendToTrackingPlayersAndSelf(entity, new ClientboundDamageEventPacket(entity, source));
    }
 
    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 r,
        boolean fire,
        Level.ExplosionInteraction interactionType,
        ParticleOptions smallExplosionParticles,
        ParticleOptions largeExplosionParticles,
        WeightedList<ExplosionParticleInfo> blockParticles,
        Holder<SoundEvent> explosionSound
    ) {
        Explosion.BlockInteraction blockInteraction = switch (interactionType) {
            case NONE -> Explosion.BlockInteraction.KEEP;
            case BLOCK -> this.getDestroyType(GameRules.BLOCK_EXPLOSION_DROP_DECAY);
            case MOB -> this.getGameRules().get(GameRules.MOB_GRIEFING)
                ? this.getDestroyType(GameRules.MOB_EXPLOSION_DROP_DECAY)
                : Explosion.BlockInteraction.KEEP;
            case TNT -> this.getDestroyType(GameRules.TNT_EXPLOSION_DROP_DECAY);
            case TRIGGER -> Explosion.BlockInteraction.TRIGGER_BLOCK;
        };
        Vec3 center = new Vec3(x, y, z);
        ServerExplosion explosion = new ServerExplosion(this, source, damageSource, damageCalculator, center, r, fire, blockInteraction);
        int blockCount = explosion.explode();
        ParticleOptions explosionParticle = explosion.isSmall() ? smallExplosionParticles : largeExplosionParticles;
 
        for (ServerPlayer player : this.players) {
            if (player.distanceToSqr(center) < 4096.0) {
                Optional<Vec3> playerKnockback = Optional.ofNullable(explosion.getHitPlayers().get(player));
                player.connection.send(new ClientboundExplodePacket(center, r, blockCount, playerKnockback, explosionParticle, explosionSound, blockParticles));
            }
        }
    }
 
    private Explosion.BlockInteraction getDestroyType(GameRule<Boolean> gameRule) {
        return this.getGameRules().get(gameRule) ? Explosion.BlockInteraction.DESTROY_WITH_DECAY : Explosion.BlockInteraction.DESTROY;
    }
 
    @Override
    public void blockEvent(BlockPos pos, Block block, int b0, int b1) {
        this.blockEvents.add(new BlockEventData(pos, block, b0, b1));
    }
 
    private void runBlockEvents() {
        this.blockEventsToReschedule.clear();
 
        while (!this.blockEvents.isEmpty()) {
            BlockEventData eventData = this.blockEvents.removeFirst();
            if (this.shouldTickBlocksAt(eventData.pos())) {
                if (this.doBlockEvent(eventData)) {
                    this.server
                        .getPlayerList()
                        .broadcast(
                            null,
                            eventData.pos().getX(),
                            eventData.pos().getY(),
                            eventData.pos().getZ(),
                            64.0,
                            this.dimension(),
                            new ClientboundBlockEventPacket(eventData.pos(), eventData.block(), eventData.paramA(), eventData.paramB())
                        );
                }
            } else {
                this.blockEventsToReschedule.add(eventData);
            }
        }
 
        this.blockEvents.addAll(this.blockEventsToReschedule);
    }
 
    private boolean doBlockEvent(BlockEventData eventData) {
        BlockState state = this.getBlockState(eventData.pos());
        return state.is(eventData.block()) ? state.triggerEvent(this, eventData.pos(), eventData.paramA(), eventData.paramB()) : false;
    }
 
    public LevelTicks<Block> getBlockTicks() {
        return this.blockTicks;
    }
 
    public LevelTicks<Fluid> getFluidTicks() {
        return this.fluidTicks;
    }
 
    @Override
    public MinecraftServer getServer() {
        return this.server;
    }
 
    public PortalForcer getPortalForcer() {
        return this.portalForcer;
    }
 
    public StructureTemplateManager getStructureManager() {
        return this.server.getStructureManager();
    }
 
    public <T extends ParticleOptions> int sendParticles(
        T particle, double x, double y, double z, int count, double xDist, double yDist, double zDist, double speed
    ) {
        return this.sendParticles(particle, false, false, x, y, z, count, xDist, yDist, zDist, speed);
    }
 
    public <T extends ParticleOptions> int sendParticles(
        T particle,
        boolean overrideLimiter,
        boolean alwaysShow,
        double x,
        double y,
        double z,
        int count,
        double xDist,
        double yDist,
        double zDist,
        double speed
    ) {
        ClientboundLevelParticlesPacket packet = new ClientboundLevelParticlesPacket(
            particle, overrideLimiter, alwaysShow, x, y, z, (float)xDist, (float)yDist, (float)zDist, (float)speed, count
        );
        int result = 0;
 
        for (int i = 0; i < this.players.size(); i++) {
            ServerPlayer player = this.players.get(i);
            if (this.sendParticles(player, overrideLimiter, x, y, z, packet)) {
                result++;
            }
        }
 
        return result;
    }
 
    public <T extends ParticleOptions> boolean sendParticles(
        ServerPlayer player,
        T particle,
        boolean overrideLimiter,
        boolean alwaysShow,
        double x,
        double y,
        double z,
        int count,
        double xDist,
        double yDist,
        double zDist,
        double speed
    ) {
        Packet<?> packet = new ClientboundLevelParticlesPacket(
            particle, overrideLimiter, alwaysShow, x, y, z, (float)xDist, (float)yDist, (float)zDist, (float)speed, count
        );
        return this.sendParticles(player, overrideLimiter, x, y, z, packet);
    }
 
    private boolean sendParticles(ServerPlayer player, boolean overrideLimiter, double x, double y, double z, Packet<?> packet) {
        if (player.level() != this) {
            return false;
        } else {
            BlockPos pos = player.blockPosition();
            if (pos.closerToCenterThan(new Vec3(x, y, z), overrideLimiter ? 512.0 : 32.0)) {
                player.connection.send(packet);
                return true;
            } else {
                return false;
            }
        }
    }
 
    @Override
    public @Nullable Entity getEntity(int id) {
        return this.getEntities().get(id);
    }
 
    @Override
    public @Nullable Entity getEntityInAnyDimension(UUID uuid) {
        Entity entity = this.getEntity(uuid);
        if (entity != null) {
            return entity;
        } else {
            for (ServerLevel otherLevel : this.getServer().getAllLevels()) {
                if (otherLevel != this) {
                    Entity otherEntity = otherLevel.getEntity(uuid);
                    if (otherEntity != null) {
                        return otherEntity;
                    }
                }
            }
 
            return null;
        }
    }
 
    @Override
    public @Nullable Player getPlayerInAnyDimension(UUID uuid) {
        return this.getServer().getPlayerList().getPlayer(uuid);
    }
 
    @Deprecated
    public @Nullable Entity getEntityOrPart(int id) {
        Entity entity = this.getEntities().get(id);
        return entity != null ? entity : this.dragonParts.get(id);
    }
 
    @Override
    public Collection<EnderDragonPart> dragonParts() {
        return this.dragonParts.values();
    }
 
    public @Nullable BlockPos findNearestMapStructure(TagKey<Structure> structureTag, BlockPos origin, int maxSearchRadius, boolean createReference) {
        if (!this.server.getWorldGenSettings().options().generateStructures()) {
            return null;
        } else {
            Optional<HolderSet.Named<Structure>> tag = this.registryAccess().lookupOrThrow(Registries.STRUCTURE).get(structureTag);
            if (tag.isEmpty()) {
                return null;
            } else {
                Pair<BlockPos, Holder<Structure>> result = this.getChunkSource()
                    .getGenerator()
                    .findNearestMapStructure(this, tag.get(), origin, maxSearchRadius, createReference);
                return result != null ? result.getFirst() : null;
            }
        }
    }
 
    public @Nullable Pair<BlockPos, Holder<Biome>> findClosestBiome3d(
        Predicate<Holder<Biome>> biomeTest, BlockPos origin, int maxSearchRadius, int sampleResolutionHorizontal, int sampleResolutionVertical
    ) {
        return this.getChunkSource()
            .getGenerator()
            .getBiomeSource()
            .findClosestBiome3d(
                origin, maxSearchRadius, sampleResolutionHorizontal, sampleResolutionVertical, biomeTest, this.getChunkSource().randomState().sampler(), this
            );
    }
 
    @Override
    public WorldBorder getWorldBorder() {
        WorldBorder worldBorder = this.getDataStorage().computeIfAbsent(WorldBorder.TYPE);
        worldBorder.applyInitialSettings(this.levelData.getGameTime());
        return worldBorder;
    }
 
    public RecipeManager recipeAccess() {
        return this.server.getRecipeManager();
    }
 
    @Override
    public TickRateManager tickRateManager() {
        return this.server.tickRateManager();
    }
 
    @Override
    public boolean noSave() {
        return this.noSave;
    }
 
    public SavedDataStorage getDataStorage() {
        return this.getChunkSource().getDataStorage();
    }
 
    @Override
    public @Nullable MapItemSavedData getMapData(MapId id) {
        return this.getServer().getDataStorage().get(MapItemSavedData.type(id));
    }
 
    public void setMapData(MapId id, MapItemSavedData data) {
        this.getServer().getDataStorage().set(MapItemSavedData.type(id), data);
    }
 
    public MapId getFreeMapId() {
        return this.getServer().getDataStorage().computeIfAbsent(MapIndex.TYPE).getNextMapId();
    }
 
    @Override
    public void setRespawnData(LevelData.RespawnData respawnData) {
        this.getServer().setRespawnData(respawnData);
    }
 
    @Override
    public LevelData.RespawnData getRespawnData() {
        return this.getServer().getRespawnData();
    }
 
    public LongSet getForceLoadedChunks() {
        return this.chunkSource.getForceLoadedChunks();
    }
 
    public boolean setChunkForced(int chunkX, int chunkZ, boolean forced) {
        boolean updated = this.chunkSource.updateChunkForced(new ChunkPos(chunkX, chunkZ), forced);
        if (forced && updated) {
            this.getChunk(chunkX, chunkZ);
        }
 
        return updated;
    }
 
    @Override
    public List<ServerPlayer> players() {
        return this.players;
    }
 
    @Override
    public void updatePOIOnBlockStateChange(BlockPos pos, BlockState oldState, BlockState newState) {
        Optional<Holder<PoiType>> oldType = PoiTypes.forState(oldState);
        Optional<Holder<PoiType>> newType = PoiTypes.forState(newState);
        if (!Objects.equals(oldType, newType)) {
            BlockPos immutable = pos.immutable();
            oldType.ifPresent(poiType -> this.getServer().execute(() -> {
                this.getPoiManager().remove(immutable);
                this.debugSynchronizers.dropPoi(immutable);
            }));
            newType.ifPresent(poiType -> this.getServer().execute(() -> {
                PoiRecord record = this.getPoiManager().add(immutable, (Holder<PoiType>)poiType);
                if (record != null) {
                    this.debugSynchronizers.registerPoi(record);
                }
            }));
        }
    }
 
    public PoiManager getPoiManager() {
        return this.getChunkSource().getPoiManager();
    }
 
    public boolean isVillage(BlockPos pos) {
        return this.isCloseToVillage(pos, 1);
    }
 
    public boolean isVillage(SectionPos sectionPos) {
        return this.isVillage(sectionPos.center());
    }
 
    public boolean isCloseToVillage(BlockPos pos, int sectionDistance) {
        return sectionDistance > 6 ? false : this.sectionsToVillage(SectionPos.of(pos)) <= sectionDistance;
    }
 
    public int sectionsToVillage(SectionPos pos) {
        return this.getPoiManager().sectionsToVillage(pos);
    }
 
    public Raids getRaids() {
        return this.raids;
    }
 
    public @Nullable 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 source, ReputationEventHandler target) {
        target.onReputationEventFrom(type, source);
    }
 
    public void saveDebugReport(Path rootDir) throws IOException {
        ChunkMap chunkMap = this.getChunkSource().chunkMap;
 
        try (Writer output = Files.newBufferedWriter(rootDir.resolve("stats.txt"))) {
            output.write(String.format(Locale.ROOT, "spawning_chunks: %d\n", chunkMap.getDistanceManager().getNaturalSpawnChunkCount()));
            NaturalSpawner.SpawnState lastSpawnState = this.getChunkSource().getLastSpawnState();
            if (lastSpawnState != null) {
                for (Entry<MobCategory> entry : lastSpawnState.getMobCategoryCounts().object2IntEntrySet()) {
                    output.write(String.format(Locale.ROOT, "spawn_count.%s: %d\n", entry.getKey().getName(), entry.getIntValue()));
                }
            }
 
            output.write(String.format(Locale.ROOT, "entities: %s\n", this.entityManager.gatherStats()));
            output.write(String.format(Locale.ROOT, "block_entity_tickers: %d\n", this.blockEntityTickers.size()));
            output.write(String.format(Locale.ROOT, "block_ticks: %d\n", this.getBlockTicks().count()));
            output.write(String.format(Locale.ROOT, "fluid_ticks: %d\n", this.getFluidTicks().count()));
            output.write("distance_manager: " + chunkMap.getDistanceManager().getDebugStatus() + "\n");
            output.write(String.format(Locale.ROOT, "pending_tasks: %d\n", this.getChunkSource().getPendingTasksCount()));
        }
 
        CrashReport test = new CrashReport("Level dump", new Exception("dummy"));
        this.fillReportDetails(test);
 
        try (Writer output = Files.newBufferedWriter(rootDir.resolve("example_crash.txt"))) {
            output.write(test.getFriendlyReport(ReportType.TEST));
        }
 
        Path chunks = rootDir.resolve("chunks.csv");
 
        try (Writer output = Files.newBufferedWriter(chunks)) {
            chunkMap.dumpChunks(output);
        }
 
        Path entityChunks = rootDir.resolve("entity_chunks.csv");
 
        try (Writer output = Files.newBufferedWriter(entityChunks)) {
            this.entityManager.dumpSections(output);
        }
 
        Path entities = rootDir.resolve("entities.csv");
 
        try (Writer output = Files.newBufferedWriter(entities)) {
            dumpEntities(output, this.getEntities().getAll());
        }
 
        Path blockEntities = rootDir.resolve("block_entities.csv");
 
        try (Writer output = Files.newBufferedWriter(blockEntities)) {
            this.dumpBlockEntityTickers(output);
        }
    }
 
    private static void dumpEntities(Writer output, Iterable<Entity> 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(output);
 
        for (Entity entity : entities) {
            Component customName = entity.getCustomName();
            Component displayName = entity.getDisplayName();
            csvOutput.writeRow(
                entity.getX(),
                entity.getY(),
                entity.getZ(),
                entity.getUUID(),
                entity.typeHolder().getRegisteredName(),
                entity.isAlive(),
                displayName.getString(),
                customName != null ? customName.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 ticker : this.blockEntityTickers) {
            BlockPos blockPos = ticker.getPos();
            csvOutput.writeRow(blockPos.getX(), blockPos.getY(), blockPos.getZ(), ticker.getType());
        }
    }
 
    @VisibleForTesting
    public void clearBlockEvents(BoundingBox bb) {
        this.blockEvents.removeIf(e -> bb.isInside(e.pos()));
    }
 
    public Iterable<Entity> getAllEntities() {
        return this.getEntities().getAll();
    }
 
    @Override
    public String toString() {
        return "ServerLevel[" + this.serverLevelData.getLevelName() + "]";
    }
 
    public boolean isFlat() {
        return this.server.getWorldData().isFlatWorld();
    }
 
    @Override
    public long getSeed() {
        return this.server.getWorldGenSettings().options().seed();
    }
 
    public @Nullable EnderDragonFight getDragonFight() {
        return this.dragonFight;
    }
 
    public WeatherData getWeatherData() {
        return this.server.getWeatherData();
    }
 
    @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(), e -> e.typeHolder().getRegisteredName()),
            this.blockEntityTickers.size(),
            getTypeCount(this.blockEntityTickers, TickingBlockEntity::getType),
            this.getBlockTicks().count(),
            this.getFluidTicks().count(),
            this.gatherChunkSourceStats()
        );
    }
 
    private static <T> String getTypeCount(Iterable<T> values, Function<T, String> typeGetter) {
        try {
            Object2IntOpenHashMap<String> countByType = new Object2IntOpenHashMap<>();
 
            for (T e : values) {
                String type = typeGetter.apply(e);
                countByType.addTo(type, 1);
            }
 
            Comparator<Entry<String>> compareByCount = Comparator.comparingInt(Entry::getIntValue);
            return countByType.object2IntEntrySet()
                .stream()
                .sorted(compareByCount.reversed())
                .limit(5L)
                .map(ex -> (String)ex.getKey() + ":" + ex.getIntValue())
                .collect(Collectors.joining(","));
        } catch (Exception var6) {
            return "";
        }
    }
 
    @Override
    protected LevelEntityGetter<Entity> getEntities() {
        return this.entityManager.getEntityGetter();
    }
 
    public void addLegacyChunkEntities(Stream<Entity> loaded) {
        this.entityManager.addLegacyChunkEntities(loaded);
    }
 
    public void addWorldGenChunkEntities(Stream<Entity> loaded) {
        this.entityManager.addWorldGenChunkEntities(loaded);
    }
 
    public void startTickingChunk(LevelChunk levelChunk) {
        levelChunk.unpackTicks(this.getGameTime());
    }
 
    public void onStructureStartsAvailable(ChunkAccess chunk) {
        this.server.execute(() -> this.structureCheck.onStructureLoad(chunk.getPos(), chunk.getAllStarts()));
    }
 
    public PathTypeCache getPathTypeCache() {
        return this.pathTypesByPosCache;
    }
 
    public void waitForEntities(ChunkPos centerChunk, int radius) {
        List<ChunkPos> chunks = ChunkPos.rangeClosed(centerChunk, radius).toList();
        this.server.managedBlock(() -> {
            this.entityManager.processPendingLoads();
 
            for (ChunkPos chunk : chunks) {
                if (!this.areEntitiesLoaded(chunk.pack())) {
                    return false;
                }
            }
 
            return true;
        });
    }
 
    public boolean isSpawningMonsters() {
        return this.getLevelData().getDifficulty() != Difficulty.PEACEFUL
            && this.getGameRules().get(GameRules.SPAWN_MOBS)
            && this.getGameRules().get(GameRules.SPAWN_MONSTERS);
    }
 
    @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 chunkKey) {
        return this.entityManager.areEntitiesLoaded(chunkKey);
    }
 
    public boolean isPositionTickingWithEntitiesLoaded(long key) {
        return this.areEntitiesLoaded(key) && this.chunkSource.isPositionTicking(key);
    }
 
    public boolean isPositionEntityTicking(BlockPos pos) {
        return this.entityManager.canPositionTick(pos) && this.chunkSource.chunkMap.getDistanceManager().inEntityTickingRange(ChunkPos.pack(pos));
    }
 
    public boolean areEntitiesActuallyLoadedAndTicking(ChunkPos pos) {
        return this.entityManager.isTicking(pos) && this.entityManager.areEntitiesLoaded(pos.pack());
    }
 
    public boolean anyPlayerCloseEnoughForSpawning(BlockPos pos) {
        return this.anyPlayerCloseEnoughForSpawning(ChunkPos.containing(pos));
    }
 
    public boolean anyPlayerCloseEnoughForSpawning(ChunkPos pos) {
        return this.chunkSource.chunkMap.anyPlayerCloseEnoughForSpawning(pos);
    }
 
    public boolean canSpreadFireAround(BlockPos pos) {
        int spreadRadius = this.getGameRules().get(GameRules.FIRE_SPREAD_RADIUS_AROUND_PLAYER);
        return spreadRadius == -1 || this.chunkSource.chunkMap.anyPlayerCloseEnoughTo(pos, spreadRadius);
    }
 
    public boolean canSpawnEntitiesInChunk(ChunkPos pos) {
        return this.entityManager.canPositionTick(pos) && this.getWorldBorder().isWithinBounds(pos);
    }
 
    @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 GameRules getGameRules() {
        return this.server.getGameRules();
    }
 
    @Override
    public CrashReportCategory fillReportDetails(CrashReport report) {
        CrashReportCategory category = super.fillReportDetails(report);
        WeatherData weatherData = this.getWeatherData();
        category.setDetail("Loaded entity count", () -> String.valueOf(this.entityManager.count()));
        category.setDetail(
            "Server weather",
            () -> String.format(
                Locale.ROOT,
                "Rain time: %d (now: %b), thunder time: %d (now: %b)",
                weatherData.getRainTime(),
                this.isRaining(),
                weatherData.getThunderTime(),
                this.isThundering()
            )
        );
        return category;
    }
 
    @Override
    public int getSeaLevel() {
        return this.chunkSource.getGenerator().getSeaLevel();
    }
 
    @Override
    public void onBlockEntityAdded(BlockEntity blockEntity) {
        super.onBlockEntityAdded(blockEntity);
        this.debugSynchronizers.registerBlockEntity(blockEntity);
    }
 
    public LevelDebugSynchronizers debugSynchronizers() {
        return this.debugSynchronizers;
    }
 
    public boolean isAllowedToEnterPortal(Level toLevel) {
        return toLevel.dimension() == Level.NETHER ? this.getGameRules().get(GameRules.ALLOW_ENTERING_NETHER_USING_PORTALS) : true;
    }
 
    public boolean isPvpAllowed() {
        return this.getGameRules().get(GameRules.PVP);
    }
 
    public boolean isCommandBlockEnabled() {
        return this.getGameRules().get(GameRules.COMMAND_BLOCKS_WORK);
    }
 
    public boolean isSpawnerBlockEnabled() {
        return this.getGameRules().get(GameRules.SPAWNER_BLOCKS_WORK);
    }
 
    private final class EntityCallbacks implements LevelCallback<Entity> {
        private EntityCallbacks() {
            Objects.requireNonNull(ServerLevel.this);
            super();
        }
 
        public void onCreated(Entity entity) {
            if (entity instanceof WaypointTransmitter waypoint && waypoint.isTransmittingWaypoint()) {
                ServerLevel.this.getWaypointManager().trackWaypoint(waypoint);
            }
        }
 
        public void onDestroyed(Entity entity) {
            if (entity instanceof WaypointTransmitter waypoint) {
                ServerLevel.this.getWaypointManager().untrackWaypoint(waypoint);
            }
 
            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 player) {
                ServerLevel.this.players.add(player);
                if (player.isReceivingWaypoints()) {
                    ServerLevel.this.getWaypointManager().addPlayer(player);
                }
 
                ServerLevel.this.updateSleepingPlayerList();
            }
 
            if (entity instanceof WaypointTransmitter waypoint && waypoint.isTransmittingWaypoint()) {
                ServerLevel.this.getWaypointManager().trackWaypoint(waypoint);
            }
 
            if (entity instanceof Mob mob) {
                if (ServerLevel.this.isUpdatingNavigations) {
                    String message = "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 dragon) {
                for (EnderDragonPart subEntity : dragon.getSubEntities()) {
                    ServerLevel.this.dragonParts.put(subEntity.getId(), subEntity);
                }
            }
 
            entity.updateDynamicGameEventListener(DynamicGameEventListener::add);
        }
 
        public void onTrackingEnd(Entity entity) {
            ServerLevel.this.getChunkSource().removeEntity(entity);
            if (entity instanceof ServerPlayer player) {
                ServerLevel.this.players.remove(player);
                ServerLevel.this.getWaypointManager().removePlayer(player);
                ServerLevel.this.updateSleepingPlayerList();
            }
 
            if (entity instanceof Mob mob) {
                if (ServerLevel.this.isUpdatingNavigations) {
                    String message = "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 dragon) {
                for (EnderDragonPart subEntity : dragon.getSubEntities()) {
                    ServerLevel.this.dragonParts.remove(subEntity.getId());
                }
            }
 
            entity.updateDynamicGameEventListener(DynamicGameEventListener::remove);
            ServerLevel.this.debugSynchronizers.dropEntity(entity);
        }
 
        public void onSectionChange(Entity entity) {
            entity.updateDynamicGameEventListener(DynamicGameEventListener::move);
        }
    }
}

引用的其他类