ServerChunkCache.java

net.minecraft.server.level.ServerChunkCache

信息

  • 全限定名:net.minecraft.server.level.ServerChunkCache
  • 类型:public class
  • 包:net.minecraft.server.level
  • 源码路径:src/main/java/net/minecraft/server/level/ServerChunkCache.java
  • 起始行号:L57
  • 继承:ChunkSource
  • 职责:

    TODO

字段/常量

  • LOGGER

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

      TODO

  • distanceManager

    • 类型: DistanceManager
    • 修饰符: private final
    • 源码定位: L59
    • 说明:

      TODO

  • level

    • 类型: ServerLevel
    • 修饰符: private final
    • 源码定位: L60
    • 说明:

      TODO

  • mainThread

    • 类型: Thread
    • 修饰符: private final
    • 源码定位: L61
    • 说明:

      TODO

  • lightEngine

    • 类型: ThreadedLevelLightEngine
    • 修饰符: private final
    • 源码定位: L62
    • 说明:

      TODO

  • mainThreadProcessor

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

      TODO

  • chunkMap

    • 类型: ChunkMap
    • 修饰符: public final
    • 源码定位: L64
    • 说明:

      TODO

  • savedDataStorage

    • 类型: SavedDataStorage
    • 修饰符: private final
    • 源码定位: L65
    • 说明:

      TODO

  • ticketStorage

    • 类型: TicketStorage
    • 修饰符: private final
    • 源码定位: L66
    • 说明:

      TODO

  • lastInhabitedUpdate

    • 类型: long
    • 修饰符: private
    • 源码定位: L67
    • 说明:

      TODO

  • spawnEnemies

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

      TODO

  • CACHE_SIZE

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

      TODO

  • lastChunkPos

    • 类型: long[]
    • 修饰符: private final
    • 源码定位: L70
    • 说明:

      TODO

  • lastChunkStatus

    • 类型: ChunkStatus[]
    • 修饰符: private final
    • 源码定位: L71
    • 说明:

      TODO

  • lastChunk

    • 类型: ChunkAccess[]
    • 修饰符: private final
    • 源码定位: L72
    • 说明:

      TODO

  • spawningChunks

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

      TODO

  • chunkHoldersToBroadcast

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

      TODO

  • lastSpawnState

    • 类型: NaturalSpawner.SpawnState
    • 修饰符: private
    • 源码定位: L75
    • 说明:

      TODO

内部类/嵌套类型

  • net.minecraft.server.level.ServerChunkCache.MainThreadExecutor
    • 类型: class
    • 修饰符: private final
    • 源码定位: L587
    • 说明:

      TODO

构造器

public ServerChunkCache(ServerLevel level, LevelStorageSource.LevelStorageAccess levelStorage, DataFixer fixerUpper, StructureTemplateManager structureTemplateManager, Executor executor, ChunkGenerator generator, int viewDistance, int simulationDistance, boolean syncWrites, ChunkStatusUpdateListener chunkStatusListener, Supplier<SavedDataStorage> overworldDataStorage) @ L78

  • 构造器名:ServerChunkCache
  • 源码定位:L78
  • 修饰符:public

参数:

  • level: ServerLevel
  • levelStorage: LevelStorageSource.LevelStorageAccess
  • fixerUpper: DataFixer
  • structureTemplateManager: StructureTemplateManager
  • executor: Executor
  • generator: ChunkGenerator
  • viewDistance: int
  • simulationDistance: int
  • syncWrites: boolean
  • chunkStatusListener: ChunkStatusUpdateListener
  • overworldDataStorage: Supplier

说明:

TODO

方法

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

public ThreadedLevelLightEngine getLightEngine() @ L125

  • 方法名:getLightEngine
  • 源码定位:L125
  • 返回类型:ThreadedLevelLightEngine
  • 修饰符:public

参数:

说明:

TODO

private ChunkHolder getVisibleChunkIfPresent(long key) @ L129

  • 方法名:getVisibleChunkIfPresent
  • 源码定位:L129
  • 返回类型:ChunkHolder
  • 修饰符:private

参数:

  • key: long

说明:

TODO

private void storeInCache(long pos, ChunkAccess chunk, ChunkStatus status) @ L133

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

参数:

  • pos: long
  • chunk: ChunkAccess
  • status: ChunkStatus

说明:

TODO

public ChunkAccess getChunk(int x, int z, ChunkStatus targetStatus, boolean loadOrGenerate) @ L145

  • 方法名:getChunk
  • 源码定位:L145
  • 返回类型:ChunkAccess
  • 修饰符:public

参数:

  • x: int
  • z: int
  • targetStatus: ChunkStatus
  • loadOrGenerate: boolean

说明:

TODO

public LevelChunk getChunkNow(int x, int z) @ L177

  • 方法名:getChunkNow
  • 源码定位:L177
  • 返回类型:LevelChunk
  • 修饰符:public

参数:

  • x: int
  • z: int

说明:

TODO

private void clearCache() @ L209

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

参数:

说明:

TODO

public CompletableFuture<ChunkResult<ChunkAccess>> getChunkFuture(int x, int z, ChunkStatus targetStatus, boolean loadOrGenerate) @ L215

  • 方法名:getChunkFuture
  • 源码定位:L215
  • 返回类型:CompletableFuture<ChunkResult>
  • 修饰符:public

参数:

  • x: int
  • z: int
  • targetStatus: ChunkStatus
  • loadOrGenerate: boolean

说明:

TODO

private CompletableFuture<ChunkResult<ChunkAccess>> getChunkFutureMainThread(int x, int z, ChunkStatus targetStatus, boolean loadOrGenerate) @ L231

  • 方法名:getChunkFutureMainThread
  • 源码定位:L231
  • 返回类型:CompletableFuture<ChunkResult>
  • 修饰符:private

参数:

  • x: int
  • z: int
  • targetStatus: ChunkStatus
  • loadOrGenerate: boolean

说明:

TODO

private boolean chunkAbsent(ChunkHolder chunkHolder, int targetTicketLevel) @ L255

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

参数:

  • chunkHolder: ChunkHolder
  • targetTicketLevel: int

说明:

TODO

public boolean hasChunk(int x, int z) @ L259

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

参数:

  • x: int
  • z: int

说明:

TODO

public LightChunk getChunkForLighting(int x, int z) @ L266

  • 方法名:getChunkForLighting
  • 源码定位:L266
  • 返回类型:LightChunk
  • 修饰符:public

参数:

  • x: int
  • z: int

说明:

TODO

public Level getLevel() @ L273

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

参数:

说明:

TODO

public boolean pollTask() @ L277

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

参数:

说明:

TODO

boolean runDistanceManagerUpdates() @ L281

  • 方法名:runDistanceManagerUpdates
  • 源码定位:L281
  • 返回类型:boolean
  • 修饰符:package-private

参数:

说明:

TODO

public boolean isPositionTicking(long chunkKey) @ L293

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

参数:

  • chunkKey: long

说明:

TODO

public void save(boolean flushStorage) @ L302

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

参数:

  • flushStorage: boolean

说明:

TODO

public void close() @ L307

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

参数:

说明:

TODO

public void tick(BooleanSupplier haveTime, boolean tickChunks) @ L315

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

参数:

  • haveTime: BooleanSupplier
  • tickChunks: boolean

说明:

TODO

private void tickChunks() @ L336

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

参数:

说明:

TODO

private void broadcastChangedChunks(ProfilerFiller profiler) @ L354

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

参数:

  • profiler: ProfilerFiller

说明:

TODO

private void tickChunks(ProfilerFiller profiler, long timeDiff) @ L368

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

参数:

  • profiler: ProfilerFiller
  • timeDiff: long

说明:

TODO

private void tickSpawningChunk(LevelChunk chunk, long timeDiff, List<MobCategory> spawningCategories, NaturalSpawner.SpawnState spawnCookie) @ L411

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

参数:

  • chunk: LevelChunk
  • timeDiff: long
  • spawningCategories: List
  • spawnCookie: NaturalSpawner.SpawnState

说明:

TODO

private void getFullChunk(long chunkKey, Consumer<LevelChunk> output) @ L425

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

参数:

  • chunkKey: long
  • output: Consumer

说明:

TODO

public String gatherStats() @ L432

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

参数:

说明:

TODO

public int getPendingTasksCount() @ L437

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

参数:

说明:

TODO

public ChunkGenerator getGenerator() @ L442

  • 方法名:getGenerator
  • 源码定位:L442
  • 返回类型:ChunkGenerator
  • 修饰符:public

参数:

说明:

TODO

public ChunkGeneratorStructureState getGeneratorState() @ L446

  • 方法名:getGeneratorState
  • 源码定位:L446
  • 返回类型:ChunkGeneratorStructureState
  • 修饰符:public

参数:

说明:

TODO

public RandomState randomState() @ L450

  • 方法名:randomState
  • 源码定位:L450
  • 返回类型:RandomState
  • 修饰符:public

参数:

说明:

TODO

public int getLoadedChunksCount() @ L454

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

参数:

说明:

TODO

public void blockChanged(BlockPos pos) @ L459

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

参数:

  • pos: BlockPos

说明:

TODO

public void onLightUpdate(LightLayer layer, SectionPos pos) @ L468

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

参数:

  • layer: LightLayer
  • pos: SectionPos

说明:

TODO

public boolean hasActiveTickets() @ L478

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

参数:

说明:

TODO

public void addTicket(Ticket ticket, ChunkPos pos) @ L482

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

参数:

  • ticket: Ticket
  • pos: ChunkPos

说明:

TODO

public CompletableFuture<?> addTicketAndLoadWithRadius(TicketType type, ChunkPos pos, int radius) @ L486

  • 方法名:addTicketAndLoadWithRadius
  • 源码定位:L486
  • 返回类型:CompletableFuture<?>
  • 修饰符:public

参数:

  • type: TicketType
  • pos: ChunkPos
  • radius: int

说明:

TODO

public void addTicketWithRadius(TicketType type, ChunkPos pos, int radius) @ L500

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

参数:

  • type: TicketType
  • pos: ChunkPos
  • radius: int

说明:

TODO

public void removeTicketWithRadius(TicketType type, ChunkPos pos, int radius) @ L504

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

参数:

  • type: TicketType
  • pos: ChunkPos
  • radius: int

说明:

TODO

public boolean updateChunkForced(ChunkPos pos, boolean forced) @ L508

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

参数:

  • pos: ChunkPos
  • forced: boolean

说明:

TODO

public LongSet getForceLoadedChunks() @ L513

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

参数:

说明:

TODO

public void move(ServerPlayer player) @ L518

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

参数:

  • player: ServerPlayer

说明:

TODO

public void removeEntity(Entity entity) @ L527

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

参数:

  • entity: Entity

说明:

TODO

public void addEntity(Entity entity) @ L531

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

参数:

  • entity: Entity

说明:

TODO

public void sendToTrackingPlayersAndSelf(Entity entity, Packet<?super ClientGamePacketListener> packet) @ L535

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

参数:

  • entity: Entity
  • packet: Packet<?super ClientGamePacketListener>

说明:

TODO

public void sendToTrackingPlayers(Entity entity, Packet<?super ClientGamePacketListener> packet) @ L539

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

参数:

  • entity: Entity
  • packet: Packet<?super ClientGamePacketListener>

说明:

TODO

public void setViewDistance(int newDistance) @ L543

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

参数:

  • newDistance: int

说明:

TODO

public void setSimulationDistance(int simulationDistance) @ L547

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

参数:

  • simulationDistance: int

说明:

TODO

public void setSpawnSettings(boolean spawnEnemies) @ L551

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

参数:

  • spawnEnemies: boolean

说明:

TODO

public String getChunkDebugData(ChunkPos pos) @ L556

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

参数:

  • pos: ChunkPos

说明:

TODO

public SavedDataStorage getDataStorage() @ L560

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

参数:

说明:

TODO

public PoiManager getPoiManager() @ L564

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

参数:

说明:

TODO

public ChunkScanAccess chunkScanner() @ L568

  • 方法名:chunkScanner
  • 源码定位:L568
  • 返回类型:ChunkScanAccess
  • 修饰符:public

参数:

说明:

TODO

public NaturalSpawner.SpawnState getLastSpawnState() @ L572

  • 方法名:getLastSpawnState
  • 源码定位:L572
  • 返回类型:NaturalSpawner.SpawnState
  • 修饰符:public

参数:

说明:

TODO

public void deactivateTicketsOnClosing() @ L577

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

参数:

说明:

TODO

public void onChunkReadyToSend(ChunkHolder chunk) @ L581

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

参数:

  • chunk: ChunkHolder

说明:

TODO

代码

public class ServerChunkCache extends ChunkSource {
    private static final Logger LOGGER = LogUtils.getLogger();
    private final DistanceManager distanceManager;
    private final ServerLevel level;
    private final Thread mainThread;
    private final ThreadedLevelLightEngine lightEngine;
    private final ServerChunkCache.MainThreadExecutor mainThreadProcessor;
    public final ChunkMap chunkMap;
    private final SavedDataStorage savedDataStorage;
    private final TicketStorage ticketStorage;
    private long lastInhabitedUpdate;
    private boolean spawnEnemies = true;
    private static final int CACHE_SIZE = 4;
    private final long[] lastChunkPos = new long[4];
    private final @Nullable ChunkStatus[] lastChunkStatus = new ChunkStatus[4];
    private final @Nullable ChunkAccess[] lastChunk = new ChunkAccess[4];
    private final List<LevelChunk> spawningChunks = new ObjectArrayList<>();
    private final Set<ChunkHolder> chunkHoldersToBroadcast = new ReferenceOpenHashSet<>();
    @VisibleForDebug
    private NaturalSpawner.@Nullable SpawnState lastSpawnState;
 
    public ServerChunkCache(
        ServerLevel level,
        LevelStorageSource.LevelStorageAccess levelStorage,
        DataFixer fixerUpper,
        StructureTemplateManager structureTemplateManager,
        Executor executor,
        ChunkGenerator generator,
        int viewDistance,
        int simulationDistance,
        boolean syncWrites,
        ChunkStatusUpdateListener chunkStatusListener,
        Supplier<SavedDataStorage> overworldDataStorage
    ) {
        this.level = level;
        this.mainThreadProcessor = new ServerChunkCache.MainThreadExecutor(level);
        this.mainThread = Thread.currentThread();
        Path dataFolder = levelStorage.getDimensionPath(level.dimension()).resolve("data");
 
        try {
            FileUtil.createDirectoriesSafe(dataFolder);
        } catch (IOException var14) {
            LOGGER.error("Failed to create dimension data storage directory", (Throwable)var14);
        }
 
        this.savedDataStorage = new SavedDataStorage(dataFolder, fixerUpper, level.registryAccess());
        this.ticketStorage = this.savedDataStorage.computeIfAbsent(TicketStorage.TYPE);
        this.chunkMap = new ChunkMap(
            level,
            levelStorage,
            fixerUpper,
            structureTemplateManager,
            executor,
            this.mainThreadProcessor,
            this,
            generator,
            chunkStatusListener,
            overworldDataStorage,
            this.ticketStorage,
            viewDistance,
            syncWrites
        );
        this.lightEngine = this.chunkMap.getLightEngine();
        this.distanceManager = this.chunkMap.getDistanceManager();
        this.distanceManager.updateSimulationDistance(simulationDistance);
        this.clearCache();
    }
 
    public ThreadedLevelLightEngine getLightEngine() {
        return this.lightEngine;
    }
 
    private @Nullable ChunkHolder getVisibleChunkIfPresent(long key) {
        return this.chunkMap.getVisibleChunkIfPresent(key);
    }
 
    private void storeInCache(long pos, @Nullable ChunkAccess chunk, ChunkStatus status) {
        for (int i = 3; i > 0; i--) {
            this.lastChunkPos[i] = this.lastChunkPos[i - 1];
            this.lastChunkStatus[i] = this.lastChunkStatus[i - 1];
            this.lastChunk[i] = this.lastChunk[i - 1];
        }
 
        this.lastChunkPos[0] = pos;
        this.lastChunkStatus[0] = status;
        this.lastChunk[0] = chunk;
    }
 
    @Override
    public @Nullable ChunkAccess getChunk(int x, int z, ChunkStatus targetStatus, boolean loadOrGenerate) {
        if (Thread.currentThread() != this.mainThread) {
            return CompletableFuture.<ChunkAccess>supplyAsync(() -> this.getChunk(x, z, targetStatus, loadOrGenerate), this.mainThreadProcessor).join();
        } else {
            ProfilerFiller profiler = Profiler.get();
            profiler.incrementCounter("getChunk");
            long pos = ChunkPos.pack(x, z);
 
            for (int i = 0; i < 4; i++) {
                if (pos == this.lastChunkPos[i] && targetStatus == this.lastChunkStatus[i]) {
                    ChunkAccess chunkAccess = this.lastChunk[i];
                    if (chunkAccess != null || !loadOrGenerate) {
                        return chunkAccess;
                    }
                }
            }
 
            profiler.incrementCounter("getChunkCacheMiss");
            CompletableFuture<ChunkResult<ChunkAccess>> serverFuture = this.getChunkFutureMainThread(x, z, targetStatus, loadOrGenerate);
            this.mainThreadProcessor.managedBlock(serverFuture::isDone);
            ChunkResult<ChunkAccess> chunkResult = serverFuture.join();
            ChunkAccess chunk = chunkResult.orElse(null);
            if (chunk == null && loadOrGenerate) {
                throw (IllegalStateException)Util.pauseInIde(new IllegalStateException("Chunk not there when requested: " + chunkResult.getError()));
            } else {
                this.storeInCache(pos, chunk, targetStatus);
                return chunk;
            }
        }
    }
 
    @Override
    public @Nullable LevelChunk getChunkNow(int x, int z) {
        if (Thread.currentThread() != this.mainThread) {
            return null;
        } else {
            Profiler.get().incrementCounter("getChunkNow");
            long pos = ChunkPos.pack(x, z);
 
            for (int i = 0; i < 4; i++) {
                if (pos == this.lastChunkPos[i] && this.lastChunkStatus[i] == ChunkStatus.FULL) {
                    ChunkAccess chunkAccess = this.lastChunk[i];
                    return chunkAccess instanceof LevelChunk ? (LevelChunk)chunkAccess : null;
                }
            }
 
            ChunkHolder chunkHolder = this.getVisibleChunkIfPresent(pos);
            if (chunkHolder == null) {
                return null;
            } else {
                ChunkAccess chunk = chunkHolder.getChunkIfPresent(ChunkStatus.FULL);
                if (chunk != null) {
                    this.storeInCache(pos, chunk, ChunkStatus.FULL);
                    if (chunk instanceof LevelChunk) {
                        return (LevelChunk)chunk;
                    }
                }
 
                return null;
            }
        }
    }
 
    private void clearCache() {
        Arrays.fill(this.lastChunkPos, ChunkPos.INVALID_CHUNK_POS);
        Arrays.fill(this.lastChunkStatus, null);
        Arrays.fill(this.lastChunk, null);
    }
 
    public CompletableFuture<ChunkResult<ChunkAccess>> getChunkFuture(int x, int z, ChunkStatus targetStatus, boolean loadOrGenerate) {
        boolean isMainThread = Thread.currentThread() == this.mainThread;
        CompletableFuture<ChunkResult<ChunkAccess>> serverFuture;
        if (isMainThread) {
            serverFuture = this.getChunkFutureMainThread(x, z, targetStatus, loadOrGenerate);
            this.mainThreadProcessor.managedBlock(serverFuture::isDone);
        } else {
            serverFuture = CompletableFuture.<CompletableFuture<ChunkResult<ChunkAccess>>>supplyAsync(
                    () -> this.getChunkFutureMainThread(x, z, targetStatus, loadOrGenerate), this.mainThreadProcessor
                )
                .thenCompose(chunk -> (CompletionStage<ChunkResult<ChunkAccess>>)chunk);
        }
 
        return serverFuture;
    }
 
    private CompletableFuture<ChunkResult<ChunkAccess>> getChunkFutureMainThread(int x, int z, ChunkStatus targetStatus, boolean loadOrGenerate) {
        ChunkPos pos = new ChunkPos(x, z);
        long key = pos.pack();
        int targetTicketLevel = ChunkLevel.byStatus(targetStatus);
        ChunkHolder chunkHolder = this.getVisibleChunkIfPresent(key);
        if (loadOrGenerate) {
            this.addTicket(new Ticket(TicketType.UNKNOWN, targetTicketLevel), pos);
            if (this.chunkAbsent(chunkHolder, targetTicketLevel)) {
                ProfilerFiller profiler = Profiler.get();
                profiler.push("chunkLoad");
                this.runDistanceManagerUpdates();
                chunkHolder = this.getVisibleChunkIfPresent(key);
                profiler.pop();
                if (this.chunkAbsent(chunkHolder, targetTicketLevel)) {
                    throw (IllegalStateException)Util.pauseInIde(new IllegalStateException("No chunk holder after ticket has been added"));
                }
            }
        }
 
        return this.chunkAbsent(chunkHolder, targetTicketLevel)
            ? GenerationChunkHolder.UNLOADED_CHUNK_FUTURE
            : chunkHolder.scheduleChunkGenerationTask(targetStatus, this.chunkMap);
    }
 
    private boolean chunkAbsent(@Nullable ChunkHolder chunkHolder, int targetTicketLevel) {
        return chunkHolder == null || chunkHolder.getTicketLevel() > targetTicketLevel;
    }
 
    @Override
    public boolean hasChunk(int x, int z) {
        ChunkHolder chunkHolder = this.getVisibleChunkIfPresent(new ChunkPos(x, z).pack());
        int targetTicketLevel = ChunkLevel.byStatus(ChunkStatus.FULL);
        return !this.chunkAbsent(chunkHolder, targetTicketLevel);
    }
 
    @Override
    public @Nullable LightChunk getChunkForLighting(int x, int z) {
        long key = ChunkPos.pack(x, z);
        ChunkHolder chunkHolder = this.getVisibleChunkIfPresent(key);
        return chunkHolder == null ? null : chunkHolder.getChunkIfPresentUnchecked(ChunkStatus.INITIALIZE_LIGHT.getParent());
    }
 
    public Level getLevel() {
        return this.level;
    }
 
    public boolean pollTask() {
        return this.mainThreadProcessor.pollTask();
    }
 
    boolean runDistanceManagerUpdates() {
        boolean updated = this.distanceManager.runAllUpdates(this.chunkMap);
        boolean promoted = this.chunkMap.promoteChunkMap();
        this.chunkMap.runGenerationTasks();
        if (!updated && !promoted) {
            return false;
        } else {
            this.clearCache();
            return true;
        }
    }
 
    public boolean isPositionTicking(long chunkKey) {
        if (!this.level.shouldTickBlocksAt(chunkKey)) {
            return false;
        } else {
            ChunkHolder holder = this.getVisibleChunkIfPresent(chunkKey);
            return holder == null ? false : holder.getTickingChunkFuture().getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK).isSuccess();
        }
    }
 
    public void save(boolean flushStorage) {
        this.runDistanceManagerUpdates();
        this.chunkMap.saveAllChunks(flushStorage);
    }
 
    @Override
    public void close() throws IOException {
        this.save(true);
        this.savedDataStorage.close();
        this.lightEngine.close();
        this.chunkMap.close();
    }
 
    @Override
    public void tick(BooleanSupplier haveTime, boolean tickChunks) {
        ProfilerFiller profiler = Profiler.get();
        profiler.push("purge");
        if (this.level.tickRateManager().runsNormally() || !tickChunks) {
            this.ticketStorage.purgeStaleTickets(this.chunkMap);
        }
 
        this.runDistanceManagerUpdates();
        profiler.popPush("chunks");
        if (tickChunks) {
            this.tickChunks();
            this.chunkMap.tick();
        }
 
        profiler.popPush("unload");
        this.chunkMap.tick(haveTime);
        profiler.pop();
        this.clearCache();
    }
 
    private void tickChunks() {
        long time = this.level.getGameTime();
        long timeDiff = time - this.lastInhabitedUpdate;
        this.lastInhabitedUpdate = time;
        if (!this.level.isDebug()) {
            ProfilerFiller profiler = Profiler.get();
            profiler.push("pollingChunks");
            if (this.level.tickRateManager().runsNormally()) {
                profiler.push("tickingChunks");
                this.tickChunks(profiler, timeDiff);
                profiler.pop();
            }
 
            this.broadcastChangedChunks(profiler);
            profiler.pop();
        }
    }
 
    private void broadcastChangedChunks(ProfilerFiller profiler) {
        profiler.push("broadcast");
 
        for (ChunkHolder chunkHolder : this.chunkHoldersToBroadcast) {
            LevelChunk chunk = chunkHolder.getTickingChunk();
            if (chunk != null) {
                chunkHolder.broadcastChanges(chunk);
            }
        }
 
        this.chunkHoldersToBroadcast.clear();
        profiler.pop();
    }
 
    private void tickChunks(ProfilerFiller profiler, long timeDiff) {
        profiler.push("naturalSpawnCount");
        int chunkCount = this.distanceManager.getNaturalSpawnChunkCount();
        NaturalSpawner.SpawnState spawnCookie = NaturalSpawner.createState(
            chunkCount, this.level.getAllEntities(), this::getFullChunk, new LocalMobCapCalculator(this.chunkMap)
        );
        this.lastSpawnState = spawnCookie;
        boolean doMobSpawning = this.level.getGameRules().get(GameRules.SPAWN_MOBS);
        int tickSpeed = this.level.getGameRules().get(GameRules.RANDOM_TICK_SPEED);
        List<MobCategory> spawningCategories;
        if (doMobSpawning) {
            boolean spawnPersistent = this.level.getGameTime() % 400L == 0L;
            spawningCategories = NaturalSpawner.getFilteredSpawningCategories(spawnCookie, true, this.spawnEnemies, spawnPersistent);
        } else {
            spawningCategories = List.of();
        }
 
        List<LevelChunk> spawningChunks = this.spawningChunks;
 
        try {
            profiler.popPush("filteringSpawningChunks");
            this.chunkMap.collectSpawningChunks(spawningChunks);
            profiler.popPush("shuffleSpawningChunks");
            Util.shuffle(spawningChunks, this.level.getRandom());
            profiler.popPush("tickSpawningChunks");
 
            for (LevelChunk chunk : spawningChunks) {
                this.tickSpawningChunk(chunk, timeDiff, spawningCategories, spawnCookie);
            }
        } finally {
            spawningChunks.clear();
        }
 
        profiler.popPush("tickTickingChunks");
        this.chunkMap.forEachBlockTickingChunk(chunkx -> this.level.tickChunk(chunkx, tickSpeed));
        if (doMobSpawning) {
            profiler.popPush("customSpawners");
            this.level.tickCustomSpawners(this.spawnEnemies);
        }
 
        profiler.pop();
    }
 
    private void tickSpawningChunk(LevelChunk chunk, long timeDiff, List<MobCategory> spawningCategories, NaturalSpawner.SpawnState spawnCookie) {
        ChunkPos chunkPos = chunk.getPos();
        chunk.incrementInhabitedTime(timeDiff);
        if (this.distanceManager.inEntityTickingRange(chunkPos.pack())) {
            this.level.tickThunder(chunk);
        }
 
        if (!spawningCategories.isEmpty()) {
            if (this.level.canSpawnEntitiesInChunk(chunkPos)) {
                NaturalSpawner.spawnForChunk(this.level, chunk, spawnCookie, spawningCategories);
            }
        }
    }
 
    private void getFullChunk(long chunkKey, Consumer<LevelChunk> output) {
        ChunkHolder chunkHolder = this.getVisibleChunkIfPresent(chunkKey);
        if (chunkHolder != null) {
            chunkHolder.getFullChunkFuture().getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK).ifSuccess(output);
        }
    }
 
    @Override
    public String gatherStats() {
        return Integer.toString(this.getLoadedChunksCount());
    }
 
    @VisibleForTesting
    public int getPendingTasksCount() {
        return this.mainThreadProcessor.getPendingTasksCount();
    }
 
    public ChunkGenerator getGenerator() {
        return this.chunkMap.generator();
    }
 
    public ChunkGeneratorStructureState getGeneratorState() {
        return this.chunkMap.generatorState();
    }
 
    public RandomState randomState() {
        return this.chunkMap.randomState();
    }
 
    @Override
    public int getLoadedChunksCount() {
        return this.chunkMap.size();
    }
 
    public void blockChanged(BlockPos pos) {
        int xc = SectionPos.blockToSectionCoord(pos.getX());
        int zc = SectionPos.blockToSectionCoord(pos.getZ());
        ChunkHolder chunk = this.getVisibleChunkIfPresent(ChunkPos.pack(xc, zc));
        if (chunk != null && chunk.blockChanged(pos)) {
            this.chunkHoldersToBroadcast.add(chunk);
        }
    }
 
    @Override
    public void onLightUpdate(LightLayer layer, SectionPos pos) {
        this.mainThreadProcessor.execute(() -> {
            ChunkHolder chunk = this.getVisibleChunkIfPresent(pos.chunk().pack());
            if (chunk != null && chunk.sectionLightChanged(layer, pos.y())) {
                this.chunkHoldersToBroadcast.add(chunk);
            }
        });
    }
 
    public boolean hasActiveTickets() {
        return this.ticketStorage.shouldKeepDimensionActive();
    }
 
    public void addTicket(Ticket ticket, ChunkPos pos) {
        this.ticketStorage.addTicket(ticket, pos);
    }
 
    public CompletableFuture<?> addTicketAndLoadWithRadius(TicketType type, ChunkPos pos, int radius) {
        if (!type.doesLoad()) {
            throw new IllegalStateException("Ticket type " + type + " does not trigger chunk loading");
        } else if (type.canExpireIfUnloaded()) {
            throw new IllegalStateException("Ticket type " + type + " can expire before it loads, cannot fetch asynchronously");
        } else {
            this.addTicketWithRadius(type, pos, radius);
            this.runDistanceManagerUpdates();
            ChunkHolder chunkHolder = this.getVisibleChunkIfPresent(pos.pack());
            Objects.requireNonNull(chunkHolder, "No chunk was scheduled for loading");
            return this.chunkMap.getChunkRangeFuture(chunkHolder, radius, distance -> ChunkStatus.FULL);
        }
    }
 
    public void addTicketWithRadius(TicketType type, ChunkPos pos, int radius) {
        this.ticketStorage.addTicketWithRadius(type, pos, radius);
    }
 
    public void removeTicketWithRadius(TicketType type, ChunkPos pos, int radius) {
        this.ticketStorage.removeTicketWithRadius(type, pos, radius);
    }
 
    @Override
    public boolean updateChunkForced(ChunkPos pos, boolean forced) {
        return this.ticketStorage.updateChunkForced(pos, forced);
    }
 
    @Override
    public LongSet getForceLoadedChunks() {
        return this.ticketStorage.getForceLoadedChunks();
    }
 
    public void move(ServerPlayer player) {
        if (!player.isRemoved()) {
            this.chunkMap.move(player);
            if (player.isReceivingWaypoints()) {
                this.level.getWaypointManager().updatePlayer(player);
            }
        }
    }
 
    public void removeEntity(Entity entity) {
        this.chunkMap.removeEntity(entity);
    }
 
    public void addEntity(Entity entity) {
        this.chunkMap.addEntity(entity);
    }
 
    public void sendToTrackingPlayersAndSelf(Entity entity, Packet<? super ClientGamePacketListener> packet) {
        this.chunkMap.sendToTrackingPlayersAndSelf(entity, packet);
    }
 
    public void sendToTrackingPlayers(Entity entity, Packet<? super ClientGamePacketListener> packet) {
        this.chunkMap.sendToTrackingPlayers(entity, packet);
    }
 
    public void setViewDistance(int newDistance) {
        this.chunkMap.setServerViewDistance(newDistance);
    }
 
    public void setSimulationDistance(int simulationDistance) {
        this.distanceManager.updateSimulationDistance(simulationDistance);
    }
 
    @Override
    public void setSpawnSettings(boolean spawnEnemies) {
        this.spawnEnemies = spawnEnemies;
    }
 
    public String getChunkDebugData(ChunkPos pos) {
        return this.chunkMap.getChunkDebugData(pos);
    }
 
    public SavedDataStorage getDataStorage() {
        return this.savedDataStorage;
    }
 
    public PoiManager getPoiManager() {
        return this.chunkMap.getPoiManager();
    }
 
    public ChunkScanAccess chunkScanner() {
        return this.chunkMap.chunkScanner();
    }
 
    @VisibleForDebug
    public NaturalSpawner.@Nullable SpawnState getLastSpawnState() {
        return this.lastSpawnState;
    }
 
    public void deactivateTicketsOnClosing() {
        this.ticketStorage.deactivateTicketsOnClosing();
    }
 
    public void onChunkReadyToSend(ChunkHolder chunk) {
        if (chunk.hasChangesToBroadcast()) {
            this.chunkHoldersToBroadcast.add(chunk);
        }
    }
 
    private final class MainThreadExecutor extends BlockableEventLoop<Runnable> {
        private MainThreadExecutor(Level level) {
            Objects.requireNonNull(ServerChunkCache.this);
            super("Chunk source main thread executor for " + level.dimension().identifier(), false);
        }
 
        @Override
        public Runnable wrapRunnable(Runnable runnable) {
            return runnable;
        }
 
        @Override
        protected boolean shouldRun(Runnable task) {
            return true;
        }
 
        @Override
        protected boolean scheduleExecutables() {
            return true;
        }
 
        @Override
        protected Thread getRunningThread() {
            return ServerChunkCache.this.mainThread;
        }
 
        @Override
        protected void doRunTask(Runnable task) {
            Profiler.get().incrementCounter("runTask");
            super.doRunTask(task);
        }
 
        @Override
        protected boolean pollTask() {
            if (ServerChunkCache.this.runDistanceManagerUpdates()) {
                return true;
            } else {
                ServerChunkCache.this.lightEngine.tryScheduleUpdate();
                return super.pollTask();
            }
        }
    }
}

引用的其他类