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();
}
}
}
}引用的其他类
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
参数/方法调用 - 关联成员:
SectionPos.blockToSectionCoord()
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
参数/字段/返回值
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
ChunkLevel.byStatus()
- 引用位置:
-
- 引用位置:
字段/构造调用 - 关联成员:
ChunkMap()
- 引用位置:
-
- 引用位置:
返回值
- 引用位置:
-
- 引用位置:
字段
- 引用位置:
-
- 引用位置:
参数/字段
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
字段/返回值
- 引用位置:
-
- 引用位置:
参数/构造调用 - 关联成员:
Ticket()
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
FileUtil.createDirectoriesSafe()
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
Util.pauseInIde(), Util.shuffle()
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
Profiler.get()
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
返回值
- 引用位置:
-
- 引用位置:
参数/方法调用/构造调用 - 关联成员:
ChunkPos(), ChunkPos.pack()
- 引用位置:
-
- 引用位置:
返回值
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
构造调用 - 关联成员:
LocalMobCapCalculator()
- 引用位置:
-
- 引用位置:
参数/字段/方法调用/返回值 - 关联成员:
NaturalSpawner.createState(), NaturalSpawner.getFilteredSpawningCategories(), NaturalSpawner.spawnForChunk()
- 引用位置:
-
- 引用位置:
字段
- 引用位置:
-
- 引用位置:
参数/字段/返回值
- 引用位置:
-
- 引用位置:
参数/返回值
- 引用位置:
-
- 引用位置:
返回值
- 引用位置:
-
- 引用位置:
继承
- 引用位置:
-
- 引用位置:
参数/字段/返回值
- 引用位置:
-
- 引用位置:
返回值
- 引用位置:
-
- 引用位置:
参数/字段
- 引用位置:
-
- 引用位置:
返回值
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
返回值
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
参数/字段/构造调用/返回值 - 关联成员:
SavedDataStorage()
- 引用位置: