ChunkMap.java
net.minecraft.server.level.ChunkMap
信息
- 全限定名:net.minecraft.server.level.ChunkMap
- 类型:public class
- 包:net.minecraft.server.level
- 源码路径:src/main/java/net/minecraft/server/level/ChunkMap.java
- 起始行号:L111
- 继承:SimpleRegionStorage
- 实现:ChunkHolder.PlayerProvider, GeneratingChunkMap
- 职责:
TODO
字段/常量
-
UNLOADED_CHUNK_LIST_RESULT- 类型:
ChunkResult<List<ChunkAccess>> - 修饰符:
private static final - 源码定位:
L112 - 说明:
TODO
- 类型:
-
UNLOADED_CHUNK_LIST_FUTURE- 类型:
CompletableFuture<ChunkResult<List<ChunkAccess>>> - 修饰符:
private static final - 源码定位:
L113 - 说明:
TODO
- 类型:
-
CHUNK_TYPE_REPLACEABLE- 类型:
byte - 修饰符:
private static final - 源码定位:
L116 - 说明:
TODO
- 类型:
-
CHUNK_TYPE_UNKNOWN- 类型:
byte - 修饰符:
private static final - 源码定位:
L117 - 说明:
TODO
- 类型:
-
CHUNK_TYPE_FULL- 类型:
byte - 修饰符:
private static final - 源码定位:
L118 - 说明:
TODO
- 类型:
-
LOGGER- 类型:
Logger - 修饰符:
private static final - 源码定位:
L119 - 说明:
TODO
- 类型:
-
CHUNK_SAVED_PER_TICK- 类型:
int - 修饰符:
private static final - 源码定位:
L120 - 说明:
TODO
- 类型:
-
CHUNK_SAVED_EAGERLY_PER_TICK- 类型:
int - 修饰符:
private static final - 源码定位:
L121 - 说明:
TODO
- 类型:
-
EAGER_CHUNK_SAVE_COOLDOWN_IN_MILLIS- 类型:
int - 修饰符:
private static final - 源码定位:
L122 - 说明:
TODO
- 类型:
-
MAX_ACTIVE_CHUNK_WRITES- 类型:
int - 修饰符:
private static final - 源码定位:
L123 - 说明:
TODO
- 类型:
-
MIN_VIEW_DISTANCE- 类型:
int - 修饰符:
public static final - 源码定位:
L124 - 说明:
TODO
- 类型:
-
MAX_VIEW_DISTANCE- 类型:
int - 修饰符:
public static final - 源码定位:
L125 - 说明:
TODO
- 类型:
-
FORCED_TICKET_LEVEL- 类型:
int - 修饰符:
public static final - 源码定位:
L126 - 说明:
TODO
- 类型:
-
updatingChunkMap- 类型:
Long2ObjectLinkedOpenHashMap<ChunkHolder> - 修饰符:
private final - 源码定位:
L127 - 说明:
TODO
- 类型:
-
visibleChunkMap- 类型:
Long2ObjectLinkedOpenHashMap<ChunkHolder> - 修饰符:
private volatile - 源码定位:
L128 - 说明:
TODO
- 类型:
-
pendingUnloads- 类型:
Long2ObjectLinkedOpenHashMap<ChunkHolder> - 修饰符:
private final - 源码定位:
L129 - 说明:
TODO
- 类型:
-
pendingGenerationTasks- 类型:
List<ChunkGenerationTask> - 修饰符:
private final - 源码定位:
L130 - 说明:
TODO
- 类型:
-
level- 类型:
ServerLevel - 修饰符:
private final - 源码定位:
L131 - 说明:
TODO
- 类型:
-
lightEngine- 类型:
ThreadedLevelLightEngine - 修饰符:
private final - 源码定位:
L132 - 说明:
TODO
- 类型:
-
mainThreadExecutor- 类型:
BlockableEventLoop<Runnable> - 修饰符:
private final - 源码定位:
L133 - 说明:
TODO
- 类型:
-
randomState- 类型:
RandomState - 修饰符:
private final - 源码定位:
L134 - 说明:
TODO
- 类型:
-
chunkGeneratorState- 类型:
ChunkGeneratorStructureState - 修饰符:
private final - 源码定位:
L135 - 说明:
TODO
- 类型:
-
ticketStorage- 类型:
TicketStorage - 修饰符:
private final - 源码定位:
L136 - 说明:
TODO
- 类型:
-
poiManager- 类型:
PoiManager - 修饰符:
private final - 源码定位:
L137 - 说明:
TODO
- 类型:
-
toDrop- 类型:
LongSet - 修饰符:
private final - 源码定位:
L138 - 说明:
TODO
- 类型:
-
modified- 类型:
boolean - 修饰符:
private - 源码定位:
L139 - 说明:
TODO
- 类型:
-
worldgenTaskDispatcher- 类型:
ChunkTaskDispatcher - 修饰符:
private final - 源码定位:
L140 - 说明:
TODO
- 类型:
-
lightTaskDispatcher- 类型:
ChunkTaskDispatcher - 修饰符:
private final - 源码定位:
L141 - 说明:
TODO
- 类型:
-
chunkStatusListener- 类型:
ChunkStatusUpdateListener - 修饰符:
private final - 源码定位:
L142 - 说明:
TODO
- 类型:
-
distanceManager- 类型:
ChunkMap.DistanceManager - 修饰符:
private final - 源码定位:
L143 - 说明:
TODO
- 类型:
-
storageName- 类型:
String - 修饰符:
private final - 源码定位:
L144 - 说明:
TODO
- 类型:
-
playerMap- 类型:
PlayerMap - 修饰符:
private final - 源码定位:
L145 - 说明:
TODO
- 类型:
-
entityMap- 类型:
Int2ObjectMap<ChunkMap.TrackedEntity> - 修饰符:
private final - 源码定位:
L146 - 说明:
TODO
- 类型:
-
chunkTypeCache- 类型:
Long2ByteMap - 修饰符:
private final - 源码定位:
L147 - 说明:
TODO
- 类型:
-
nextChunkSaveTime- 类型:
Long2LongMap - 修饰符:
private final - 源码定位:
L148 - 说明:
TODO
- 类型:
-
chunksToEagerlySave- 类型:
LongSet - 修饰符:
private final - 源码定位:
L149 - 说明:
TODO
- 类型:
-
unloadQueue- 类型:
Queue<Runnable> - 修饰符:
private final - 源码定位:
L150 - 说明:
TODO
- 类型:
-
activeChunkWrites- 类型:
AtomicInteger - 修饰符:
private final - 源码定位:
L151 - 说明:
TODO
- 类型:
-
serverViewDistance- 类型:
int - 修饰符:
private - 源码定位:
L152 - 说明:
TODO
- 类型:
-
worldGenContext- 类型:
WorldGenContext - 修饰符:
private final - 源码定位:
L153 - 说明:
TODO
- 类型:
内部类/嵌套类型
-
net.minecraft.server.level.ChunkMap.DistanceManager- 类型:
class - 修饰符:
private - 源码定位:
L1294 - 说明:
TODO
- 类型:
-
net.minecraft.server.level.ChunkMap.TrackedEntity- 类型:
class - 修饰符:
private - 源码定位:
L1316 - 说明:
TODO
- 类型:
构造器
public ChunkMap(ServerLevel level, LevelStorageSource.LevelStorageAccess levelStorage, DataFixer dataFixer, StructureTemplateManager structureManager, Executor executor, BlockableEventLoop<Runnable> mainThreadExecutor, LightChunkGetter chunkGetter, ChunkGenerator generator, ChunkStatusUpdateListener chunkStatusListener, Supplier<SavedDataStorage> overworldDataStorage, TicketStorage ticketStorage, int serverViewDistance, boolean syncWrites) @ L155
- 构造器名:ChunkMap
- 源码定位:L155
- 修饰符:public
参数:
- level: ServerLevel
- levelStorage: LevelStorageSource.LevelStorageAccess
- dataFixer: DataFixer
- structureManager: StructureTemplateManager
- executor: Executor
- mainThreadExecutor: BlockableEventLoop
- chunkGetter: LightChunkGetter
- generator: ChunkGenerator
- chunkStatusListener: ChunkStatusUpdateListener
- overworldDataStorage: Supplier
- ticketStorage: TicketStorage
- serverViewDistance: int
- syncWrites: boolean
说明:
TODO
方法
下面的方法块按源码顺序生成。
private void setChunkUnsaved(ChunkPos chunkPos) @ L211
- 方法名:setChunkUnsaved
- 源码定位:L211
- 返回类型:void
- 修饰符:private
参数:
- chunkPos: ChunkPos
说明:
TODO
protected ChunkGenerator generator() @ L215
- 方法名:generator
- 源码定位:L215
- 返回类型:ChunkGenerator
- 修饰符:protected
参数:
- 无
说明:
TODO
protected ChunkGeneratorStructureState generatorState() @ L219
- 方法名:generatorState
- 源码定位:L219
- 返回类型:ChunkGeneratorStructureState
- 修饰符:protected
参数:
- 无
说明:
TODO
protected RandomState randomState() @ L223
- 方法名:randomState
- 源码定位:L223
- 返回类型:RandomState
- 修饰符:protected
参数:
- 无
说明:
TODO
public boolean isChunkTracked(ServerPlayer player, int chunkX, int chunkZ) @ L227
- 方法名:isChunkTracked
- 源码定位:L227
- 返回类型:boolean
- 修饰符:public
参数:
- player: ServerPlayer
- chunkX: int
- chunkZ: int
说明:
TODO
private boolean isChunkOnTrackedBorder(ServerPlayer player, int chunkX, int chunkZ) @ L231
- 方法名:isChunkOnTrackedBorder
- 源码定位:L231
- 返回类型:boolean
- 修饰符:private
参数:
- player: ServerPlayer
- chunkX: int
- chunkZ: int
说明:
TODO
protected ThreadedLevelLightEngine getLightEngine() @ L247
- 方法名:getLightEngine
- 源码定位:L247
- 返回类型:ThreadedLevelLightEngine
- 修饰符:protected
参数:
- 无
说明:
TODO
public ChunkHolder getUpdatingChunkIfPresent(long key) @ L251
- 方法名:getUpdatingChunkIfPresent
- 源码定位:L251
- 返回类型:ChunkHolder
- 修饰符:public
参数:
- key: long
说明:
TODO
protected ChunkHolder getVisibleChunkIfPresent(long key) @ L255
- 方法名:getVisibleChunkIfPresent
- 源码定位:L255
- 返回类型:ChunkHolder
- 修饰符:protected
参数:
- key: long
说明:
TODO
public ChunkStatus getLatestStatus(long key) @ L259
- 方法名:getLatestStatus
- 源码定位:L259
- 返回类型:ChunkStatus
- 修饰符:public
参数:
- key: long
说明:
TODO
protected IntSupplier getChunkQueueLevel(long pos) @ L264
- 方法名:getChunkQueueLevel
- 源码定位:L264
- 返回类型:IntSupplier
- 修饰符:protected
参数:
- pos: long
说明:
TODO
public String getChunkDebugData(ChunkPos pos) @ L273
- 方法名:getChunkDebugData
- 源码定位:L273
- 返回类型:String
- 修饰符:public
参数:
- pos: ChunkPos
说明:
TODO
CompletableFuture<ChunkResult<List<ChunkAccess>>> getChunkRangeFuture(ChunkHolder centerChunk, int range, IntFunction<ChunkStatus> distanceToStatus) @ L295
- 方法名:getChunkRangeFuture
- 源码定位:L295
- 返回类型:CompletableFuture<ChunkResult<List
>> - 修饰符:package-private
参数:
- centerChunk: ChunkHolder
- range: int
- distanceToStatus: IntFunction
说明:
TODO
public ReportedException debugFuturesAndCreateReportedException(IllegalStateException exception, String details) @ L339
- 方法名:debugFuturesAndCreateReportedException
- 源码定位:L339
- 返回类型:ReportedException
- 修饰符:public
参数:
- exception: IllegalStateException
- details: String
说明:
TODO
public CompletableFuture<ChunkResult<LevelChunk>> prepareEntityTickingChunk(ChunkHolder chunk) @ L359
- 方法名:prepareEntityTickingChunk
- 源码定位:L359
- 返回类型:CompletableFuture<ChunkResult
> - 修饰符:public
参数:
- chunk: ChunkHolder
说明:
TODO
private ChunkHolder updateChunkScheduling(long node, int level, ChunkHolder chunk, int oldLevel) @ L364
- 方法名:updateChunkScheduling
- 源码定位:L364
- 返回类型:ChunkHolder
- 修饰符:private
参数:
- node: long
- level: int
- chunk: ChunkHolder
- oldLevel: int
说明:
TODO
private void onLevelChange(ChunkPos pos, IntSupplier oldLevel, int newLevel, IntConsumer setQueueLevel) @ L396
- 方法名:onLevelChange
- 源码定位:L396
- 返回类型:void
- 修饰符:private
参数:
- pos: ChunkPos
- oldLevel: IntSupplier
- newLevel: int
- setQueueLevel: IntConsumer
说明:
TODO
public void close() @ L401
- 方法名:close
- 源码定位:L401
- 返回类型:void
- 修饰符:public
参数:
- 无
说明:
TODO
protected void saveAllChunks(boolean flushStorage) @ L412
- 方法名:saveAllChunks
- 源码定位:L412
- 返回类型:void
- 修饰符:protected
参数:
- flushStorage: boolean
说明:
TODO
protected void tick(BooleanSupplier haveTime) @ L447
- 方法名:tick
- 源码定位:L447
- 返回类型:void
- 修饰符:protected
参数:
- haveTime: BooleanSupplier
说明:
TODO
public boolean hasWork() @ L459
- 方法名:hasWork
- 源码定位:L459
- 返回类型:boolean
- 修饰符:public
参数:
- 无
说明:
TODO
private void processUnloads(BooleanSupplier haveTime) @ L471
- 方法名:processUnloads
- 源码定位:L471
- 返回类型:void
- 修饰符:private
参数:
- haveTime: BooleanSupplier
说明:
TODO
private void saveChunksEagerly(BooleanSupplier haveTime) @ L494
- 方法名:saveChunksEagerly
- 源码定位:L494
- 返回类型:void
- 修饰符:private
参数:
- haveTime: BooleanSupplier
说明:
TODO
private void scheduleUnload(long pos, ChunkHolder chunkHolder) @ L512
- 方法名:scheduleUnload
- 源码定位:L512
- 返回类型:void
- 修饰符:private
参数:
- pos: long
- chunkHolder: ChunkHolder
说明:
TODO
protected boolean promoteChunkMap() @ L542
- 方法名:promoteChunkMap
- 源码定位:L542
- 返回类型:boolean
- 修饰符:protected
参数:
- 无
说明:
TODO
private CompletableFuture<ChunkAccess> scheduleChunkLoad(ChunkPos pos) @ L552
- 方法名:scheduleChunkLoad
- 源码定位:L552
- 返回类型:CompletableFuture
- 修饰符:private
参数:
- pos: ChunkPos
说明:
TODO
private ChunkAccess handleChunkLoadFailure(Throwable throwable, ChunkPos pos) @ L578
- 方法名:handleChunkLoadFailure
- 源码定位:L578
- 返回类型:ChunkAccess
- 修饰符:private
参数:
- throwable: Throwable
- pos: ChunkPos
说明:
TODO
private ChunkAccess createEmptyChunk(ChunkPos pos) @ L598
- 方法名:createEmptyChunk
- 源码定位:L598
- 返回类型:ChunkAccess
- 修饰符:private
参数:
- pos: ChunkPos
说明:
TODO
private void markPositionReplaceable(ChunkPos pos) @ L603
- 方法名:markPositionReplaceable
- 源码定位:L603
- 返回类型:void
- 修饰符:private
参数:
- pos: ChunkPos
说明:
TODO
private byte markPosition(ChunkPos pos, ChunkType type) @ L607
- 方法名:markPosition
- 源码定位:L607
- 返回类型:byte
- 修饰符:private
参数:
- pos: ChunkPos
- type: ChunkType
说明:
TODO
public GenerationChunkHolder acquireGeneration(long chunkNode) @ L611
- 方法名:acquireGeneration
- 源码定位:L611
- 返回类型:GenerationChunkHolder
- 修饰符:public
参数:
- chunkNode: long
说明:
TODO
public void releaseGeneration(GenerationChunkHolder chunkHolder) @ L618
- 方法名:releaseGeneration
- 源码定位:L618
- 返回类型:void
- 修饰符:public
参数:
- chunkHolder: GenerationChunkHolder
说明:
TODO
public CompletableFuture<ChunkAccess> applyStep(GenerationChunkHolder chunkHolder, ChunkStep step, StaticCache2D<GenerationChunkHolder> cache) @ L623
- 方法名:applyStep
- 源码定位:L623
- 返回类型:CompletableFuture
- 修饰符:public
参数:
- chunkHolder: GenerationChunkHolder
- step: ChunkStep
- cache: StaticCache2D
说明:
TODO
public ChunkGenerationTask scheduleGenerationTask(ChunkStatus targetStatus, ChunkPos pos) @ L650
- 方法名:scheduleGenerationTask
- 源码定位:L650
- 返回类型:ChunkGenerationTask
- 修饰符:public
参数:
- targetStatus: ChunkStatus
- pos: ChunkPos
说明:
TODO
private void runGenerationTask(ChunkGenerationTask task) @ L657
- 方法名:runGenerationTask
- 源码定位:L657
- 返回类型:void
- 修饰符:private
参数:
- task: ChunkGenerationTask
说明:
TODO
public void runGenerationTasks() @ L667
- 方法名:runGenerationTasks
- 源码定位:L667
- 返回类型:void
- 修饰符:public
参数:
- 无
说明:
TODO
public CompletableFuture<ChunkResult<LevelChunk>> prepareTickingChunk(ChunkHolder chunk) @ L673
- 方法名:prepareTickingChunk
- 源码定位:L673
- 返回类型:CompletableFuture<ChunkResult
> - 修饰符:public
参数:
- chunk: ChunkHolder
说明:
TODO
private void onChunkReadyToSend(ChunkHolder chunkHolder, LevelChunk chunk) @ L690
- 方法名:onChunkReadyToSend
- 源码定位:L690
- 返回类型:void
- 修饰符:private
参数:
- chunkHolder: ChunkHolder
- chunk: LevelChunk
说明:
TODO
public CompletableFuture<ChunkResult<LevelChunk>> prepareAccessibleChunk(ChunkHolder chunk) @ L703
- 方法名:prepareAccessibleChunk
- 源码定位:L703
- 返回类型:CompletableFuture<ChunkResult
> - 修饰符:public
参数:
- chunk: ChunkHolder
说明:
TODO
Stream<ChunkHolder> allChunksWithAtLeastStatus(ChunkStatus status) @ L708
- 方法名:allChunksWithAtLeastStatus
- 源码定位:L708
- 返回类型:Stream
- 修饰符:package-private
参数:
- status: ChunkStatus
说明:
TODO
private boolean saveChunkIfNeeded(ChunkHolder chunk, long now) @ L713
- 方法名:saveChunkIfNeeded
- 源码定位:L713
- 返回类型:boolean
- 修饰符:private
参数:
- chunk: ChunkHolder
- now: long
说明:
TODO
private boolean save(ChunkAccess chunk) @ L740
- 方法名:save
- 源码定位:L740
- 返回类型:boolean
- 修饰符:private
参数:
- chunk: ChunkAccess
说明:
TODO
private boolean isExistingChunkFull(ChunkPos pos) @ L780
- 方法名:isExistingChunkFull
- 源码定位:L780
- 返回类型:boolean
- 修饰符:private
参数:
- pos: ChunkPos
说明:
TODO
protected void setServerViewDistance(int newViewDistance) @ L803
- 方法名:setServerViewDistance
- 源码定位:L803
- 返回类型:void
- 修饰符:protected
参数:
- newViewDistance: int
说明:
TODO
private int getPlayerViewDistance(ServerPlayer player) @ L815
- 方法名:getPlayerViewDistance
- 源码定位:L815
- 返回类型:int
- 修饰符:private
参数:
- player: ServerPlayer
说明:
TODO
private void markChunkPendingToSend(ServerPlayer player, ChunkPos pos) @ L819
- 方法名:markChunkPendingToSend
- 源码定位:L819
- 返回类型:void
- 修饰符:private
参数:
- player: ServerPlayer
- pos: ChunkPos
说明:
TODO
private static void markChunkPendingToSend(ServerPlayer player, LevelChunk chunk) @ L826
- 方法名:markChunkPendingToSend
- 源码定位:L826
- 返回类型:void
- 修饰符:private static
参数:
- player: ServerPlayer
- chunk: LevelChunk
说明:
TODO
private static void dropChunk(ServerPlayer player, ChunkPos pos) @ L830
- 方法名:dropChunk
- 源码定位:L830
- 返回类型:void
- 修饰符:private static
参数:
- player: ServerPlayer
- pos: ChunkPos
说明:
TODO
public LevelChunk getChunkToSend(long key) @ L834
- 方法名:getChunkToSend
- 源码定位:L834
- 返回类型:LevelChunk
- 修饰符:public
参数:
- key: long
说明:
TODO
public int size() @ L839
- 方法名:size
- 源码定位:L839
- 返回类型:int
- 修饰符:public
参数:
- 无
说明:
TODO
public net.minecraft.server.level.DistanceManager getDistanceManager() @ L843
- 方法名:getDistanceManager
- 源码定位:L843
- 返回类型:net.minecraft.server.level.DistanceManager
- 修饰符:public
参数:
- 无
说明:
TODO
void dumpChunks(Writer output) @ L847
- 方法名:dumpChunks
- 源码定位:L847
- 返回类型:void
- 修饰符:package-private
参数:
- output: Writer
说明:
TODO
private static String printFuture(CompletableFuture<ChunkResult<LevelChunk>> future) @ L896
- 方法名:printFuture
- 源码定位:L896
- 返回类型:String
- 修饰符:private static
参数:
- future: CompletableFuture<ChunkResult
>
说明:
TODO
private CompletableFuture<Optional<CompoundTag>> readChunk(ChunkPos pos) @ L911
- 方法名:readChunk
- 源码定位:L911
- 返回类型:CompletableFuture<Optional
> - 修饰符:private
参数:
- pos: ChunkPos
说明:
TODO
private CompoundTag upgradeChunkTag(CompoundTag tag) @ L915
- 方法名:upgradeChunkTag
- 源码定位:L915
- 返回类型:CompoundTag
- 修饰符:private
参数:
- tag: CompoundTag
说明:
TODO
public static CompoundTag getChunkDataFixContextTag(ResourceKey<Level> dimension, Optional<Identifier> generatorIdentifier) @ L924
- 方法名:getChunkDataFixContextTag
- 源码定位:L924
- 返回类型:CompoundTag
- 修饰符:public static
参数:
- dimension: ResourceKey
- generatorIdentifier: Optional
说明:
TODO
void collectSpawningChunks(List<LevelChunk> output) @ L931
- 方法名:collectSpawningChunks
- 源码定位:L931
- 返回类型:void
- 修饰符:package-private
参数:
- output: List
说明:
TODO
void forEachBlockTickingChunk(Consumer<LevelChunk> tickingChunkConsumer) @ L945
- 方法名:forEachBlockTickingChunk
- 源码定位:L945
- 返回类型:void
- 修饰符:package-private
参数:
- tickingChunkConsumer: Consumer
说明:
TODO
boolean anyPlayerCloseEnoughForSpawning(ChunkPos pos) @ L957
- 方法名:anyPlayerCloseEnoughForSpawning
- 源码定位:L957
- 返回类型:boolean
- 修饰符:package-private
参数:
- pos: ChunkPos
说明:
TODO
boolean anyPlayerCloseEnoughTo(BlockPos pos, int maxDistance) @ L962
- 方法名:anyPlayerCloseEnoughTo
- 源码定位:L962
- 返回类型:boolean
- 修饰符:package-private
参数:
- pos: BlockPos
- maxDistance: int
说明:
TODO
private boolean anyPlayerCloseEnoughForSpawningInternal(ChunkPos pos) @ L974
- 方法名:anyPlayerCloseEnoughForSpawningInternal
- 源码定位:L974
- 返回类型:boolean
- 修饰符:private
参数:
- pos: ChunkPos
说明:
TODO
public List<ServerPlayer> getPlayersCloseForSpawning(ChunkPos pos) @ L984
- 方法名:getPlayersCloseForSpawning
- 源码定位:L984
- 返回类型:List
- 修饰符:public
参数:
- pos: ChunkPos
说明:
TODO
private boolean playerIsCloseEnoughForSpawning(ServerPlayer player, ChunkPos pos) @ L1001
- 方法名:playerIsCloseEnoughForSpawning
- 源码定位:L1001
- 返回类型:boolean
- 修饰符:private
参数:
- player: ServerPlayer
- pos: ChunkPos
说明:
TODO
private boolean playerIsCloseEnoughTo(ServerPlayer player, Vec3 pos, int maxDistance) @ L1010
- 方法名:playerIsCloseEnoughTo
- 源码定位:L1010
- 返回类型:boolean
- 修饰符:private
参数:
- player: ServerPlayer
- pos: Vec3
- maxDistance: int
说明:
TODO
private static double euclideanDistanceSquared(ChunkPos chunkPos, Vec3 pos) @ L1019
- 方法名:euclideanDistanceSquared
- 源码定位:L1019
- 返回类型:double
- 修饰符:private static
参数:
- chunkPos: ChunkPos
- pos: Vec3
说明:
TODO
private boolean skipPlayer(ServerPlayer player) @ L1027
- 方法名:skipPlayer
- 源码定位:L1027
- 返回类型:boolean
- 修饰符:private
参数:
- player: ServerPlayer
说明:
TODO
void updatePlayerStatus(ServerPlayer player, boolean added) @ L1031
- 方法名:updatePlayerStatus
- 源码定位:L1031
- 返回类型:void
- 修饰符:package-private
参数:
- player: ServerPlayer
- added: boolean
说明:
TODO
private void updatePlayerPos(ServerPlayer player) @ L1054
- 方法名:updatePlayerPos
- 源码定位:L1054
- 返回类型:void
- 修饰符:private
参数:
- player: ServerPlayer
说明:
TODO
public void move(ServerPlayer player) @ L1059
- 方法名:move
- 源码定位:L1059
- 返回类型:void
- 修饰符:public
参数:
- player: ServerPlayer
说明:
TODO
private void updateChunkTracking(ServerPlayer player) @ L1095
- 方法名:updateChunkTracking
- 源码定位:L1095
- 返回类型:void
- 修饰符:private
参数:
- player: ServerPlayer
说明:
TODO
private void applyChunkTrackingView(ServerPlayer player, ChunkTrackingView next) @ L1107
- 方法名:applyChunkTrackingView
- 源码定位:L1107
- 返回类型:void
- 修饰符:private
参数:
- player: ServerPlayer
- next: ChunkTrackingView
说明:
TODO
public List<ServerPlayer> getPlayers(ChunkPos pos, boolean borderOnly) @ L1120
- 方法名:getPlayers
- 源码定位:L1120
- 返回类型:List
- 修饰符:public
参数:
- pos: ChunkPos
- borderOnly: boolean
说明:
TODO
protected void addEntity(Entity entity) @ L1134
- 方法名:addEntity
- 源码定位:L1134
- 返回类型:void
- 修饰符:protected
参数:
- entity: Entity
说明:
TODO
protected void removeEntity(Entity entity) @ L1160
- 方法名:removeEntity
- 源码定位:L1160
- 返回类型:void
- 修饰符:protected
参数:
- entity: Entity
说明:
TODO
protected void tick() @ L1175
- 方法名:tick
- 源码定位:L1175
- 返回类型:void
- 修饰符:protected
参数:
- 无
说明:
TODO
public void sendToTrackingPlayers(Entity entity, Packet<?super ClientGamePacketListener> packet) @ L1209
- 方法名:sendToTrackingPlayers
- 源码定位:L1209
- 返回类型:void
- 修饰符:public
参数:
- entity: Entity
- packet: Packet<?super ClientGamePacketListener>
说明:
TODO
public void sendToTrackingPlayersFiltered(Entity entity, Packet<?super ClientGamePacketListener> packet, Predicate<ServerPlayer> targetPredicate) @ L1216
- 方法名:sendToTrackingPlayersFiltered
- 源码定位:L1216
- 返回类型:void
- 修饰符:public
参数:
- entity: Entity
- packet: Packet<?super ClientGamePacketListener>
- targetPredicate: Predicate
说明:
TODO
protected void sendToTrackingPlayersAndSelf(Entity entity, Packet<?super ClientGamePacketListener> packet) @ L1223
- 方法名:sendToTrackingPlayersAndSelf
- 源码定位:L1223
- 返回类型:void
- 修饰符:protected
参数:
- entity: Entity
- packet: Packet<?super ClientGamePacketListener>
说明:
TODO
public boolean isTrackedByAnyPlayer(Entity entity) @ L1230
- 方法名:isTrackedByAnyPlayer
- 源码定位:L1230
- 返回类型:boolean
- 修饰符:public
参数:
- entity: Entity
说明:
TODO
public void forEachEntityTrackedBy(ServerPlayer player, Consumer<Entity> consumer) @ L1235
- 方法名:forEachEntityTrackedBy
- 源码定位:L1235
- 返回类型:void
- 修饰符:public
参数:
- player: ServerPlayer
- consumer: Consumer
说明:
TODO
public void resendBiomesForChunks(List<ChunkAccess> chunks) @ L1243
- 方法名:resendBiomesForChunks
- 源码定位:L1243
- 返回类型:void
- 修饰符:public
参数:
- chunks: List
说明:
TODO
protected PoiManager getPoiManager() @ L1263
- 方法名:getPoiManager
- 源码定位:L1263
- 返回类型:PoiManager
- 修饰符:protected
参数:
- 无
说明:
TODO
public String getStorageName() @ L1267
- 方法名:getStorageName
- 源码定位:L1267
- 返回类型:String
- 修饰符:public
参数:
- 无
说明:
TODO
void onFullChunkStatusChange(ChunkPos pos, FullChunkStatus status) @ L1271
- 方法名:onFullChunkStatusChange
- 源码定位:L1271
- 返回类型:void
- 修饰符:package-private
参数:
- pos: ChunkPos
- status: FullChunkStatus
说明:
TODO
public void waitForLightBeforeSending(ChunkPos centerChunk, int chunkRadius) @ L1275
- 方法名:waitForLightBeforeSending
- 源码定位:L1275
- 返回类型:void
- 修饰符:public
参数:
- centerChunk: ChunkPos
- chunkRadius: int
说明:
TODO
public void forEachReadyToSendChunk(Consumer<LevelChunk> consumer) @ L1285
- 方法名:forEachReadyToSendChunk
- 源码定位:L1285
- 返回类型:void
- 修饰符:public
参数:
- consumer: Consumer
说明:
TODO
代码
public class ChunkMap extends SimpleRegionStorage implements ChunkHolder.PlayerProvider, GeneratingChunkMap {
private static final ChunkResult<List<ChunkAccess>> UNLOADED_CHUNK_LIST_RESULT = ChunkResult.error("Unloaded chunks found in range");
private static final CompletableFuture<ChunkResult<List<ChunkAccess>>> UNLOADED_CHUNK_LIST_FUTURE = CompletableFuture.completedFuture(
UNLOADED_CHUNK_LIST_RESULT
);
private static final byte CHUNK_TYPE_REPLACEABLE = -1;
private static final byte CHUNK_TYPE_UNKNOWN = 0;
private static final byte CHUNK_TYPE_FULL = 1;
private static final Logger LOGGER = LogUtils.getLogger();
private static final int CHUNK_SAVED_PER_TICK = 200;
private static final int CHUNK_SAVED_EAGERLY_PER_TICK = 20;
private static final int EAGER_CHUNK_SAVE_COOLDOWN_IN_MILLIS = 10000;
private static final int MAX_ACTIVE_CHUNK_WRITES = 128;
public static final int MIN_VIEW_DISTANCE = 2;
public static final int MAX_VIEW_DISTANCE = 32;
public static final int FORCED_TICKET_LEVEL = ChunkLevel.byStatus(FullChunkStatus.ENTITY_TICKING);
private final Long2ObjectLinkedOpenHashMap<ChunkHolder> updatingChunkMap = new Long2ObjectLinkedOpenHashMap<>();
private volatile Long2ObjectLinkedOpenHashMap<ChunkHolder> visibleChunkMap = this.updatingChunkMap.clone();
private final Long2ObjectLinkedOpenHashMap<ChunkHolder> pendingUnloads = new Long2ObjectLinkedOpenHashMap<>();
private final List<ChunkGenerationTask> pendingGenerationTasks = new ArrayList<>();
private final ServerLevel level;
private final ThreadedLevelLightEngine lightEngine;
private final BlockableEventLoop<Runnable> mainThreadExecutor;
private final RandomState randomState;
private final ChunkGeneratorStructureState chunkGeneratorState;
private final TicketStorage ticketStorage;
private final PoiManager poiManager;
private final LongSet toDrop = new LongOpenHashSet();
private boolean modified;
private final ChunkTaskDispatcher worldgenTaskDispatcher;
private final ChunkTaskDispatcher lightTaskDispatcher;
private final ChunkStatusUpdateListener chunkStatusListener;
private final ChunkMap.DistanceManager distanceManager;
private final String storageName;
private final PlayerMap playerMap = new PlayerMap();
private final Int2ObjectMap<ChunkMap.TrackedEntity> entityMap = new Int2ObjectOpenHashMap<>();
private final Long2ByteMap chunkTypeCache = new Long2ByteOpenHashMap();
private final Long2LongMap nextChunkSaveTime = new Long2LongOpenHashMap();
private final LongSet chunksToEagerlySave = new LongLinkedOpenHashSet();
private final Queue<Runnable> unloadQueue = Queues.newConcurrentLinkedQueue();
private final AtomicInteger activeChunkWrites = new AtomicInteger();
private int serverViewDistance;
private final WorldGenContext worldGenContext;
public ChunkMap(
ServerLevel level,
LevelStorageSource.LevelStorageAccess levelStorage,
DataFixer dataFixer,
StructureTemplateManager structureManager,
Executor executor,
BlockableEventLoop<Runnable> mainThreadExecutor,
LightChunkGetter chunkGetter,
ChunkGenerator generator,
ChunkStatusUpdateListener chunkStatusListener,
Supplier<SavedDataStorage> overworldDataStorage,
TicketStorage ticketStorage,
int serverViewDistance,
boolean syncWrites
) {
super(
new RegionStorageInfo(levelStorage.getLevelId(), level.dimension(), "chunk"),
levelStorage.getDimensionPath(level.dimension()).resolve("region"),
dataFixer,
syncWrites,
DataFixTypes.CHUNK
);
Path storageFolder = levelStorage.getDimensionPath(level.dimension());
this.storageName = storageFolder.getFileName().toString();
this.level = level;
RegistryAccess registryAccess = level.registryAccess();
long levelSeed = level.getSeed();
if (generator instanceof NoiseBasedChunkGenerator noiseGenerator) {
this.randomState = RandomState.create(noiseGenerator.generatorSettings().value(), registryAccess.lookupOrThrow(Registries.NOISE), levelSeed);
} else {
this.randomState = RandomState.create(NoiseGeneratorSettings.dummy(), registryAccess.lookupOrThrow(Registries.NOISE), levelSeed);
}
this.chunkGeneratorState = generator.createState(registryAccess.lookupOrThrow(Registries.STRUCTURE_SET), this.randomState, levelSeed);
this.mainThreadExecutor = mainThreadExecutor;
ConsecutiveExecutor worldgen = new ConsecutiveExecutor(executor, "worldgen");
this.chunkStatusListener = chunkStatusListener;
ConsecutiveExecutor light = new ConsecutiveExecutor(executor, "light");
this.worldgenTaskDispatcher = new ChunkTaskDispatcher(worldgen, executor);
this.lightTaskDispatcher = new ChunkTaskDispatcher(light, executor);
this.lightEngine = new ThreadedLevelLightEngine(chunkGetter, this, this.level.dimensionType().hasSkyLight(), light, this.lightTaskDispatcher);
this.distanceManager = new ChunkMap.DistanceManager(ticketStorage, executor, mainThreadExecutor);
this.ticketStorage = ticketStorage;
this.poiManager = new PoiManager(
new RegionStorageInfo(levelStorage.getLevelId(), level.dimension(), "poi"),
storageFolder.resolve("poi"),
dataFixer,
syncWrites,
registryAccess,
level.getServer(),
level
);
this.setServerViewDistance(serverViewDistance);
this.worldGenContext = new WorldGenContext(level, generator, structureManager, this.lightEngine, mainThreadExecutor, this::setChunkUnsaved);
}
private void setChunkUnsaved(ChunkPos chunkPos) {
this.chunksToEagerlySave.add(chunkPos.pack());
}
protected ChunkGenerator generator() {
return this.worldGenContext.generator();
}
protected ChunkGeneratorStructureState generatorState() {
return this.chunkGeneratorState;
}
protected RandomState randomState() {
return this.randomState;
}
public boolean isChunkTracked(ServerPlayer player, int chunkX, int chunkZ) {
return player.getChunkTrackingView().contains(chunkX, chunkZ) && !player.connection.chunkSender.isPending(ChunkPos.pack(chunkX, chunkZ));
}
private boolean isChunkOnTrackedBorder(ServerPlayer player, int chunkX, int chunkZ) {
if (!this.isChunkTracked(player, chunkX, chunkZ)) {
return false;
} else {
for (int dx = -1; dx <= 1; dx++) {
for (int dz = -1; dz <= 1; dz++) {
if ((dx != 0 || dz != 0) && !this.isChunkTracked(player, chunkX + dx, chunkZ + dz)) {
return true;
}
}
}
return false;
}
}
protected ThreadedLevelLightEngine getLightEngine() {
return this.lightEngine;
}
public @Nullable ChunkHolder getUpdatingChunkIfPresent(long key) {
return this.updatingChunkMap.get(key);
}
protected @Nullable ChunkHolder getVisibleChunkIfPresent(long key) {
return this.visibleChunkMap.get(key);
}
public @Nullable ChunkStatus getLatestStatus(long key) {
ChunkHolder chunkHolder = this.getVisibleChunkIfPresent(key);
return chunkHolder != null ? chunkHolder.getLatestStatus() : null;
}
protected IntSupplier getChunkQueueLevel(long pos) {
return () -> {
ChunkHolder chunk = this.getVisibleChunkIfPresent(pos);
return chunk == null
? ChunkTaskPriorityQueue.PRIORITY_LEVEL_COUNT - 1
: Math.min(chunk.getQueueLevel(), ChunkTaskPriorityQueue.PRIORITY_LEVEL_COUNT - 1);
};
}
public String getChunkDebugData(ChunkPos pos) {
ChunkHolder chunkHolder = this.getVisibleChunkIfPresent(pos.pack());
if (chunkHolder == null) {
return "null";
} else {
String result = chunkHolder.getTicketLevel() + "\n";
ChunkStatus status = chunkHolder.getLatestStatus();
ChunkAccess chunk = chunkHolder.getLatestChunk();
if (status != null) {
result = result + "St: \u00a7" + status.getIndex() + status + "\u00a7r\n";
}
if (chunk != null) {
result = result + "Ch: \u00a7" + chunk.getPersistedStatus().getIndex() + chunk.getPersistedStatus() + "\u00a7r\n";
}
FullChunkStatus fullStatus = chunkHolder.getFullStatus();
result = result + '\u00a7' + fullStatus.ordinal() + fullStatus;
return result + "\u00a7r";
}
}
CompletableFuture<ChunkResult<List<ChunkAccess>>> getChunkRangeFuture(ChunkHolder centerChunk, int range, IntFunction<ChunkStatus> distanceToStatus) {
if (range == 0) {
ChunkStatus status = distanceToStatus.apply(0);
return centerChunk.scheduleChunkGenerationTask(status, this).thenApply(r -> r.map(List::of));
} else {
int chunkCount = Mth.square(range * 2 + 1);
List<CompletableFuture<ChunkResult<ChunkAccess>>> deps = new ArrayList<>(chunkCount);
ChunkPos centerPos = centerChunk.getPos();
for (int z = -range; z <= range; z++) {
for (int x = -range; x <= range; x++) {
int distance = Math.max(Math.abs(x), Math.abs(z));
long chunkNode = ChunkPos.pack(centerPos.x() + x, centerPos.z() + z);
ChunkHolder chunk = this.getUpdatingChunkIfPresent(chunkNode);
if (chunk == null) {
return UNLOADED_CHUNK_LIST_FUTURE;
}
ChunkStatus depStatus = distanceToStatus.apply(distance);
deps.add(chunk.scheduleChunkGenerationTask(depStatus, this));
}
}
return Util.sequence(deps).thenApply(chunkResults -> {
List<ChunkAccess> chunks = new ArrayList<>(chunkResults.size());
for (ChunkResult<ChunkAccess> chunkResult : chunkResults) {
if (chunkResult == null) {
throw this.debugFuturesAndCreateReportedException(new IllegalStateException("At least one of the chunk futures were null"), "n/a");
}
ChunkAccess chunkx = chunkResult.orElse(null);
if (chunkx == null) {
return UNLOADED_CHUNK_LIST_RESULT;
}
chunks.add(chunkx);
}
return ChunkResult.of(chunks);
});
}
}
public ReportedException debugFuturesAndCreateReportedException(IllegalStateException exception, String details) {
StringBuilder sb = new StringBuilder();
Consumer<ChunkHolder> addToDebug = holder -> holder.getAllFutures().forEach(pair -> {
ChunkStatus status = pair.getFirst();
CompletableFuture<ChunkResult<ChunkAccess>> future = pair.getSecond();
if (future != null && future.isDone() && future.join() == null) {
sb.append(holder.getPos()).append(" - status: ").append(status).append(" future: ").append(future).append(System.lineSeparator());
}
});
sb.append("Updating:").append(System.lineSeparator());
this.updatingChunkMap.values().forEach(addToDebug);
sb.append("Visible:").append(System.lineSeparator());
this.visibleChunkMap.values().forEach(addToDebug);
CrashReport report = CrashReport.forThrowable(exception, "Chunk loading");
CrashReportCategory category = report.addCategory("Chunk loading");
category.setDetail("Details", details);
category.setDetail("Futures", sb);
return new ReportedException(report);
}
public CompletableFuture<ChunkResult<LevelChunk>> prepareEntityTickingChunk(ChunkHolder chunk) {
return this.getChunkRangeFuture(chunk, 2, distance -> ChunkStatus.FULL)
.thenApply(chunkResult -> chunkResult.map(list -> (LevelChunk)list.get(list.size() / 2)));
}
private @Nullable ChunkHolder updateChunkScheduling(long node, int level, @Nullable ChunkHolder chunk, int oldLevel) {
if (!ChunkLevel.isLoaded(oldLevel) && !ChunkLevel.isLoaded(level)) {
return chunk;
} else {
if (chunk != null) {
chunk.setTicketLevel(level);
}
if (chunk != null) {
if (!ChunkLevel.isLoaded(level)) {
this.toDrop.add(node);
} else {
this.toDrop.remove(node);
}
}
if (ChunkLevel.isLoaded(level) && chunk == null) {
chunk = this.pendingUnloads.remove(node);
if (chunk != null) {
chunk.setTicketLevel(level);
} else {
chunk = new ChunkHolder(ChunkPos.unpack(node), level, this.level, this.lightEngine, this::onLevelChange, this);
}
this.updatingChunkMap.put(node, chunk);
this.modified = true;
}
return chunk;
}
}
private void onLevelChange(ChunkPos pos, IntSupplier oldLevel, int newLevel, IntConsumer setQueueLevel) {
this.worldgenTaskDispatcher.onLevelChange(pos, oldLevel, newLevel, setQueueLevel);
this.lightTaskDispatcher.onLevelChange(pos, oldLevel, newLevel, setQueueLevel);
}
@Override
public void close() throws IOException {
try {
this.worldgenTaskDispatcher.close();
this.lightTaskDispatcher.close();
this.poiManager.close();
} finally {
super.close();
}
}
protected void saveAllChunks(boolean flushStorage) {
if (flushStorage) {
List<ChunkHolder> chunksToSave = this.visibleChunkMap
.values()
.stream()
.filter(ChunkHolder::wasAccessibleSinceLastSave)
.peek(ChunkHolder::refreshAccessibility)
.toList();
MutableBoolean didWork = new MutableBoolean();
do {
didWork.setFalse();
chunksToSave.stream()
.map(chunkx -> {
this.mainThreadExecutor.managedBlock(chunkx::isReadyForSaving);
return chunkx.getLatestChunk();
})
.filter(chunkAccess -> chunkAccess instanceof ImposterProtoChunk || chunkAccess instanceof LevelChunk)
.filter(this::save)
.forEach(c -> didWork.setTrue());
} while (didWork.isTrue());
this.poiManager.flushAll();
this.processUnloads(() -> true);
this.synchronize(true).join();
} else {
this.nextChunkSaveTime.clear();
long now = Util.getMillis();
for (ChunkHolder chunk : this.visibleChunkMap.values()) {
this.saveChunkIfNeeded(chunk, now);
}
}
}
protected void tick(BooleanSupplier haveTime) {
ProfilerFiller profiler = Profiler.get();
profiler.push("poi");
this.poiManager.tick(haveTime);
profiler.popPush("chunk_unload");
if (!this.level.noSave()) {
this.processUnloads(haveTime);
}
profiler.pop();
}
public boolean hasWork() {
return this.lightEngine.hasLightWork()
|| !this.pendingUnloads.isEmpty()
|| !this.updatingChunkMap.isEmpty()
|| this.poiManager.hasWork()
|| !this.toDrop.isEmpty()
|| !this.unloadQueue.isEmpty()
|| this.worldgenTaskDispatcher.hasWork()
|| this.lightTaskDispatcher.hasWork()
|| this.distanceManager.hasTickets();
}
private void processUnloads(BooleanSupplier haveTime) {
for (LongIterator iterator = this.toDrop.iterator(); iterator.hasNext(); iterator.remove()) {
long pos = iterator.nextLong();
ChunkHolder chunkHolder = this.updatingChunkMap.get(pos);
if (chunkHolder != null) {
this.updatingChunkMap.remove(pos);
this.pendingUnloads.put(pos, chunkHolder);
this.modified = true;
this.scheduleUnload(pos, chunkHolder);
}
}
int minimalNumberOfChunksToProcess = Math.max(0, this.unloadQueue.size() - 2000);
Runnable unloadTask;
while ((minimalNumberOfChunksToProcess > 0 || haveTime.getAsBoolean()) && (unloadTask = this.unloadQueue.poll()) != null) {
minimalNumberOfChunksToProcess--;
unloadTask.run();
}
this.saveChunksEagerly(haveTime);
}
private void saveChunksEagerly(BooleanSupplier haveTime) {
long now = Util.getMillis();
int eagerlySavedCount = 0;
LongIterator iterator = this.chunksToEagerlySave.iterator();
while (eagerlySavedCount < 20 && this.activeChunkWrites.get() < 128 && haveTime.getAsBoolean() && iterator.hasNext()) {
long chunkPos = iterator.nextLong();
ChunkHolder chunkHolder = this.visibleChunkMap.get(chunkPos);
ChunkAccess latestChunk = chunkHolder != null ? chunkHolder.getLatestChunk() : null;
if (latestChunk == null || !latestChunk.isUnsaved()) {
iterator.remove();
} else if (this.saveChunkIfNeeded(chunkHolder, now)) {
eagerlySavedCount++;
iterator.remove();
}
}
}
private void scheduleUnload(long pos, ChunkHolder chunkHolder) {
CompletableFuture<?> saveSyncFuture = chunkHolder.getSaveSyncFuture();
saveSyncFuture.thenRunAsync(() -> {
CompletableFuture<?> currentFuture = chunkHolder.getSaveSyncFuture();
if (currentFuture != saveSyncFuture) {
this.scheduleUnload(pos, chunkHolder);
} else {
ChunkAccess chunk = chunkHolder.getLatestChunk();
if (this.pendingUnloads.remove(pos, chunkHolder) && chunk != null) {
if (chunk instanceof LevelChunk levelChunk) {
levelChunk.setLoaded(false);
}
this.save(chunk);
if (chunk instanceof LevelChunk levelChunk) {
this.level.unload(levelChunk);
}
this.lightEngine.updateChunkStatus(chunk.getPos());
this.lightEngine.tryScheduleUpdate();
this.nextChunkSaveTime.remove(chunk.getPos().pack());
}
}
}, this.unloadQueue::add).whenComplete((ignored, throwable) -> {
if (throwable != null) {
LOGGER.error("Failed to save chunk {}", chunkHolder.getPos(), throwable);
}
});
}
protected boolean promoteChunkMap() {
if (!this.modified) {
return false;
} else {
this.visibleChunkMap = this.updatingChunkMap.clone();
this.modified = false;
return true;
}
}
private CompletableFuture<ChunkAccess> scheduleChunkLoad(ChunkPos pos) {
CompletableFuture<Optional<SerializableChunkData>> chunkDataFuture = this.readChunk(pos).thenApplyAsync(chunkData -> chunkData.map(tag -> {
SerializableChunkData parsedData = SerializableChunkData.parse(this.level, this.level.palettedContainerFactory(), tag);
if (parsedData == null) {
LOGGER.error("Chunk file at {} is missing level data, skipping", pos);
}
return parsedData;
}), Util.backgroundExecutor().forName("parseChunk"));
CompletableFuture<?> poiFuture = this.poiManager.prefetch(pos);
return chunkDataFuture.<Object, Optional<SerializableChunkData>>thenCombine(
(CompletionStage<? extends Object>)poiFuture, (chunkData, ignored) -> chunkData
)
.thenApplyAsync(chunkData -> {
Profiler.get().incrementCounter("chunkLoad");
if (chunkData.isPresent()) {
ChunkAccess chunk = chunkData.get().read(this.level, this.poiManager, this.storageInfo(), pos);
this.markPosition(pos, chunk.getPersistedStatus().getChunkType());
return chunk;
} else {
return this.createEmptyChunk(pos);
}
}, this.mainThreadExecutor)
.exceptionallyAsync(throwable -> this.handleChunkLoadFailure(throwable, pos), this.mainThreadExecutor);
}
private ChunkAccess handleChunkLoadFailure(Throwable throwable, ChunkPos pos) {
Throwable unwrapped = throwable instanceof CompletionException e ? e.getCause() : throwable;
Throwable cause = unwrapped instanceof ReportedException ex ? ex.getCause() : unwrapped;
boolean alwaysThrow = cause instanceof Error;
boolean ioException = cause instanceof IOException || cause instanceof NbtException;
if (!alwaysThrow) {
if (!ioException) {
}
this.level.getServer().reportChunkLoadFailure(cause, this.storageInfo(), pos);
return this.createEmptyChunk(pos);
} else {
CrashReport report = CrashReport.forThrowable(throwable, "Exception loading chunk");
CrashReportCategory chunkBeingLoaded = report.addCategory("Chunk being loaded");
chunkBeingLoaded.setDetail("pos", pos);
this.markPositionReplaceable(pos);
throw new ReportedException(report);
}
}
private ChunkAccess createEmptyChunk(ChunkPos pos) {
this.markPositionReplaceable(pos);
return new ProtoChunk(pos, UpgradeData.EMPTY, this.level, this.level.palettedContainerFactory(), null);
}
private void markPositionReplaceable(ChunkPos pos) {
this.chunkTypeCache.put(pos.pack(), (byte)-1);
}
private byte markPosition(ChunkPos pos, ChunkType type) {
return this.chunkTypeCache.put(pos.pack(), (byte)(type == ChunkType.PROTOCHUNK ? -1 : 1));
}
@Override
public GenerationChunkHolder acquireGeneration(long chunkNode) {
ChunkHolder chunkHolder = this.updatingChunkMap.get(chunkNode);
chunkHolder.increaseGenerationRefCount();
return chunkHolder;
}
@Override
public void releaseGeneration(GenerationChunkHolder chunkHolder) {
chunkHolder.decreaseGenerationRefCount();
}
@Override
public CompletableFuture<ChunkAccess> applyStep(GenerationChunkHolder chunkHolder, ChunkStep step, StaticCache2D<GenerationChunkHolder> cache) {
ChunkPos pos = chunkHolder.getPos();
if (step.targetStatus() == ChunkStatus.EMPTY) {
return this.scheduleChunkLoad(pos);
} else {
try {
GenerationChunkHolder holder = cache.get(pos.x(), pos.z());
ChunkAccess centerChunk = holder.getChunkIfPresentUnchecked(step.targetStatus().getParent());
if (centerChunk == null) {
throw new IllegalStateException("Parent chunk missing");
} else {
return step.apply(this.worldGenContext, cache, centerChunk);
}
} catch (Exception var8) {
var8.getStackTrace();
CrashReport report = CrashReport.forThrowable(var8, "Exception generating new chunk");
CrashReportCategory category = report.addCategory("Chunk to be generated");
category.setDetail("Status being generated", () -> step.targetStatus().getName());
category.setDetail("Location", String.format(Locale.ROOT, "%d,%d", pos.x(), pos.z()));
category.setDetail("Position hash", ChunkPos.pack(pos.x(), pos.z()));
category.setDetail("Generator", this.generator());
throw new ReportedException(report);
}
}
}
@Override
public ChunkGenerationTask scheduleGenerationTask(ChunkStatus targetStatus, ChunkPos pos) {
ChunkGenerationTask task = ChunkGenerationTask.create(this, targetStatus, pos);
this.pendingGenerationTasks.add(task);
return task;
}
private void runGenerationTask(ChunkGenerationTask task) {
GenerationChunkHolder chunk = task.getCenter();
this.worldgenTaskDispatcher.submit(() -> {
CompletableFuture<?> future = task.runUntilWait();
if (future != null) {
future.thenRun(() -> this.runGenerationTask(task));
}
}, chunk.getPos().pack(), chunk::getQueueLevel);
}
@Override
public void runGenerationTasks() {
this.pendingGenerationTasks.forEach(this::runGenerationTask);
this.pendingGenerationTasks.clear();
}
public CompletableFuture<ChunkResult<LevelChunk>> prepareTickingChunk(ChunkHolder chunk) {
CompletableFuture<ChunkResult<List<ChunkAccess>>> future = this.getChunkRangeFuture(chunk, 1, distance -> ChunkStatus.FULL);
return future.thenApplyAsync(listResult -> listResult.map(list -> {
LevelChunk levelChunk = (LevelChunk)list.get(list.size() / 2);
levelChunk.postProcessGeneration(this.level);
this.level.startTickingChunk(levelChunk);
CompletableFuture<?> sendSyncFuture = chunk.getSendSyncFuture();
if (sendSyncFuture.isDone()) {
this.onChunkReadyToSend(chunk, levelChunk);
} else {
sendSyncFuture.thenAcceptAsync(ignored -> this.onChunkReadyToSend(chunk, levelChunk), this.mainThreadExecutor);
}
return levelChunk;
}), this.mainThreadExecutor);
}
private void onChunkReadyToSend(ChunkHolder chunkHolder, LevelChunk chunk) {
ChunkPos chunkPos = chunk.getPos();
for (ServerPlayer player : this.playerMap.getAllPlayers()) {
if (player.getChunkTrackingView().contains(chunkPos)) {
markChunkPendingToSend(player, chunk);
}
}
this.level.getChunkSource().onChunkReadyToSend(chunkHolder);
this.level.debugSynchronizers().registerChunk(chunk);
}
public CompletableFuture<ChunkResult<LevelChunk>> prepareAccessibleChunk(ChunkHolder chunk) {
return this.getChunkRangeFuture(chunk, 1, ChunkLevel::getStatusAroundFullChunk)
.thenApply(chunkResult -> chunkResult.map(list -> (LevelChunk)list.get(list.size() / 2)));
}
Stream<ChunkHolder> allChunksWithAtLeastStatus(ChunkStatus status) {
int level = ChunkLevel.byStatus(status);
return this.visibleChunkMap.values().stream().filter(chunk -> chunk.getTicketLevel() <= level);
}
private boolean saveChunkIfNeeded(ChunkHolder chunk, long now) {
if (chunk.wasAccessibleSinceLastSave() && chunk.isReadyForSaving()) {
ChunkAccess chunkAccess = chunk.getLatestChunk();
if (!(chunkAccess instanceof ImposterProtoChunk) && !(chunkAccess instanceof LevelChunk)) {
return false;
} else if (!chunkAccess.isUnsaved()) {
return false;
} else {
long chunkPos = chunkAccess.getPos().pack();
long nextSaveTime = this.nextChunkSaveTime.getOrDefault(chunkPos, -1L);
if (now < nextSaveTime) {
return false;
} else {
boolean saved = this.save(chunkAccess);
chunk.refreshAccessibility();
if (saved) {
this.nextChunkSaveTime.put(chunkPos, now + 10000L);
}
return saved;
}
}
} else {
return false;
}
}
private boolean save(ChunkAccess chunk) {
this.poiManager.flush(chunk.getPos());
if (!chunk.tryMarkSaved()) {
return false;
} else {
ChunkPos pos = chunk.getPos();
try {
ChunkStatus status = chunk.getPersistedStatus();
if (status.getChunkType() != ChunkType.LEVELCHUNK) {
if (this.isExistingChunkFull(pos)) {
return false;
}
if (status == ChunkStatus.EMPTY && chunk.getAllStarts().values().stream().noneMatch(StructureStart::isValid)) {
return false;
}
}
Profiler.get().incrementCounter("chunkSave");
this.activeChunkWrites.incrementAndGet();
SerializableChunkData data = SerializableChunkData.copyOf(this.level, chunk);
CompletableFuture<CompoundTag> encodedData = CompletableFuture.supplyAsync(data::write, Util.backgroundExecutor());
this.write(pos, encodedData::join).handle((ignored, throwable) -> {
if (throwable != null) {
this.level.getServer().reportChunkSaveFailure(throwable, this.storageInfo(), pos);
}
this.activeChunkWrites.decrementAndGet();
return null;
});
this.markPosition(pos, status.getChunkType());
return true;
} catch (Exception var6) {
this.level.getServer().reportChunkSaveFailure(var6, this.storageInfo(), pos);
return false;
}
}
}
private boolean isExistingChunkFull(ChunkPos pos) {
byte cachedChunkType = this.chunkTypeCache.get(pos.pack());
if (cachedChunkType != 0) {
return cachedChunkType == 1;
} else {
CompoundTag currentTag;
try {
currentTag = this.readChunk(pos).join().orElse(null);
if (currentTag == null) {
this.markPositionReplaceable(pos);
return false;
}
} catch (Exception var5) {
LOGGER.error("Failed to read chunk {}", pos, var5);
this.markPositionReplaceable(pos);
return false;
}
ChunkType chunkType = SerializableChunkData.getChunkStatusFromTag(currentTag).getChunkType();
return this.markPosition(pos, chunkType) == 1;
}
}
protected void setServerViewDistance(int newViewDistance) {
int actualNewDistance = Mth.clamp(newViewDistance, 2, 32);
if (actualNewDistance != this.serverViewDistance) {
this.serverViewDistance = actualNewDistance;
this.distanceManager.updatePlayerTickets(this.serverViewDistance);
for (ServerPlayer player : this.playerMap.getAllPlayers()) {
this.updateChunkTracking(player);
}
}
}
private int getPlayerViewDistance(ServerPlayer player) {
return Mth.clamp(player.requestedViewDistance(), 2, this.serverViewDistance);
}
private void markChunkPendingToSend(ServerPlayer player, ChunkPos pos) {
LevelChunk chunk = this.getChunkToSend(pos.pack());
if (chunk != null) {
markChunkPendingToSend(player, chunk);
}
}
private static void markChunkPendingToSend(ServerPlayer player, LevelChunk chunk) {
player.connection.chunkSender.markChunkPendingToSend(chunk);
}
private static void dropChunk(ServerPlayer player, ChunkPos pos) {
player.connection.chunkSender.dropChunk(player, pos);
}
public @Nullable LevelChunk getChunkToSend(long key) {
ChunkHolder chunkHolder = this.getVisibleChunkIfPresent(key);
return chunkHolder == null ? null : chunkHolder.getChunkToSend();
}
public int size() {
return this.visibleChunkMap.size();
}
public net.minecraft.server.level.DistanceManager getDistanceManager() {
return this.distanceManager;
}
void dumpChunks(Writer output) throws IOException {
CsvOutput csvOutput = CsvOutput.builder()
.addColumn("x")
.addColumn("z")
.addColumn("level")
.addColumn("in_memory")
.addColumn("status")
.addColumn("full_status")
.addColumn("accessible_ready")
.addColumn("ticking_ready")
.addColumn("entity_ticking_ready")
.addColumn("ticket")
.addColumn("spawning")
.addColumn("block_entity_count")
.addColumn("ticking_ticket")
.addColumn("ticking_level")
.addColumn("block_ticks")
.addColumn("fluid_ticks")
.build(output);
for (Entry<ChunkHolder> entry : this.visibleChunkMap.long2ObjectEntrySet()) {
long posKey = entry.getLongKey();
ChunkPos pos = ChunkPos.unpack(posKey);
ChunkHolder holder = entry.getValue();
Optional<ChunkAccess> chunk = Optional.ofNullable(holder.getLatestChunk());
Optional<LevelChunk> fullChunk = chunk.flatMap(
chunkAccess -> chunkAccess instanceof LevelChunk ? Optional.of((LevelChunk)chunkAccess) : Optional.empty()
);
csvOutput.writeRow(
pos.x(),
pos.z(),
holder.getTicketLevel(),
chunk.isPresent(),
chunk.map(ChunkAccess::getPersistedStatus).orElse(null),
fullChunk.map(LevelChunk::getFullStatus).orElse(null),
printFuture(holder.getFullChunkFuture()),
printFuture(holder.getTickingChunkFuture()),
printFuture(holder.getEntityTickingChunkFuture()),
this.ticketStorage.getTicketDebugString(posKey, false),
this.anyPlayerCloseEnoughForSpawning(pos),
fullChunk.<Integer>map(c -> c.getBlockEntities().size()).orElse(0),
this.ticketStorage.getTicketDebugString(posKey, true),
this.distanceManager.getChunkLevel(posKey, true),
fullChunk.<Integer>map(levelChunk -> levelChunk.getBlockTicks().count()).orElse(0),
fullChunk.<Integer>map(levelChunk -> levelChunk.getFluidTicks().count()).orElse(0)
);
}
}
private static String printFuture(CompletableFuture<ChunkResult<LevelChunk>> future) {
try {
ChunkResult<LevelChunk> result = future.getNow(null);
if (result != null) {
return result.isSuccess() ? "done" : "unloaded";
} else {
return "not completed";
}
} catch (CompletionException var2) {
return "failed " + var2.getCause().getMessage();
} catch (CancellationException var3) {
return "cancelled";
}
}
private CompletableFuture<Optional<CompoundTag>> readChunk(ChunkPos pos) {
return this.read(pos).thenApplyAsync(chunkTag -> chunkTag.map(this::upgradeChunkTag), Util.backgroundExecutor().forName("upgradeChunk"));
}
private CompoundTag upgradeChunkTag(CompoundTag tag) {
return this.upgradeChunkTag(
tag,
-1,
getChunkDataFixContextTag(this.level.dimension(), this.generator().getTypeNameForDataFixer()),
SharedConstants.getCurrentVersion().dataVersion().version()
);
}
public static CompoundTag getChunkDataFixContextTag(ResourceKey<Level> dimension, Optional<Identifier> generatorIdentifier) {
CompoundTag contextTag = new CompoundTag();
contextTag.putString("dimension", dimension.identifier().toString());
generatorIdentifier.ifPresent(identifier -> contextTag.putString("generator", identifier.toString()));
return contextTag;
}
void collectSpawningChunks(List<LevelChunk> output) {
LongIterator spawnCandidateChunks = this.distanceManager.getSpawnCandidateChunks();
while (spawnCandidateChunks.hasNext()) {
ChunkHolder holder = this.visibleChunkMap.get(spawnCandidateChunks.nextLong());
if (holder != null) {
LevelChunk chunk = holder.getTickingChunk();
if (chunk != null && this.anyPlayerCloseEnoughForSpawningInternal(holder.getPos())) {
output.add(chunk);
}
}
}
}
void forEachBlockTickingChunk(Consumer<LevelChunk> tickingChunkConsumer) {
this.distanceManager.forEachEntityTickingChunk(chunkPos -> {
ChunkHolder holder = this.visibleChunkMap.get(chunkPos);
if (holder != null) {
LevelChunk chunk = holder.getTickingChunk();
if (chunk != null) {
tickingChunkConsumer.accept(chunk);
}
}
});
}
boolean anyPlayerCloseEnoughForSpawning(ChunkPos pos) {
TriState triState = this.distanceManager.hasPlayersNearby(pos.pack());
return triState == TriState.DEFAULT ? this.anyPlayerCloseEnoughForSpawningInternal(pos) : triState.toBoolean(true);
}
boolean anyPlayerCloseEnoughTo(BlockPos pos, int maxDistance) {
Vec3 target = new Vec3(pos);
for (ServerPlayer player : this.playerMap.getAllPlayers()) {
if (this.playerIsCloseEnoughTo(player, target, maxDistance)) {
return true;
}
}
return false;
}
private boolean anyPlayerCloseEnoughForSpawningInternal(ChunkPos pos) {
for (ServerPlayer player : this.playerMap.getAllPlayers()) {
if (this.playerIsCloseEnoughForSpawning(player, pos)) {
return true;
}
}
return false;
}
public List<ServerPlayer> getPlayersCloseForSpawning(ChunkPos pos) {
long key = pos.pack();
if (!this.distanceManager.hasPlayersNearby(key).toBoolean(true)) {
return List.of();
} else {
Builder<ServerPlayer> builder = ImmutableList.builder();
for (ServerPlayer player : this.playerMap.getAllPlayers()) {
if (this.playerIsCloseEnoughForSpawning(player, pos)) {
builder.add(player);
}
}
return builder.build();
}
}
private boolean playerIsCloseEnoughForSpawning(ServerPlayer player, ChunkPos pos) {
if (player.isSpectator()) {
return false;
} else {
double distanceToChunk = euclideanDistanceSquared(pos, player.position());
return distanceToChunk < 16384.0;
}
}
private boolean playerIsCloseEnoughTo(ServerPlayer player, Vec3 pos, int maxDistance) {
if (player.isSpectator()) {
return false;
} else {
double distanceToPos = player.position().distanceTo(pos);
return distanceToPos < maxDistance;
}
}
private static double euclideanDistanceSquared(ChunkPos chunkPos, Vec3 pos) {
double xPos = SectionPos.sectionToBlockCoord(chunkPos.x(), 8);
double zPos = SectionPos.sectionToBlockCoord(chunkPos.z(), 8);
double xd = xPos - pos.x;
double zd = zPos - pos.z;
return xd * xd + zd * zd;
}
private boolean skipPlayer(ServerPlayer player) {
return player.isSpectator() && !this.level.getGameRules().get(GameRules.SPECTATORS_GENERATE_CHUNKS);
}
void updatePlayerStatus(ServerPlayer player, boolean added) {
boolean ignored = this.skipPlayer(player);
boolean wasIgnored = this.playerMap.ignoredOrUnknown(player);
if (added) {
this.playerMap.addPlayer(player, ignored);
this.updatePlayerPos(player);
if (!ignored) {
this.distanceManager.addPlayer(SectionPos.of(player), player);
}
player.setChunkTrackingView(ChunkTrackingView.EMPTY);
this.updateChunkTracking(player);
} else {
SectionPos lastPos = player.getLastSectionPos();
this.playerMap.removePlayer(player);
if (!wasIgnored) {
this.distanceManager.removePlayer(lastPos, player);
}
this.applyChunkTrackingView(player, ChunkTrackingView.EMPTY);
}
}
private void updatePlayerPos(ServerPlayer player) {
SectionPos pos = SectionPos.of(player);
player.setLastSectionPos(pos);
}
public void move(ServerPlayer player) {
for (ChunkMap.TrackedEntity trackedEntity : this.entityMap.values()) {
if (trackedEntity.entity == player) {
trackedEntity.updatePlayers(this.level.players());
} else {
trackedEntity.updatePlayer(player);
}
}
SectionPos oldSection = player.getLastSectionPos();
SectionPos newSection = SectionPos.of(player);
boolean wasIgnored = this.playerMap.ignored(player);
boolean ignored = this.skipPlayer(player);
boolean positionChanged = oldSection.asLong() != newSection.asLong();
if (positionChanged || wasIgnored != ignored) {
this.updatePlayerPos(player);
if (!wasIgnored) {
this.distanceManager.removePlayer(oldSection, player);
}
if (!ignored) {
this.distanceManager.addPlayer(newSection, player);
}
if (!wasIgnored && ignored) {
this.playerMap.ignorePlayer(player);
}
if (wasIgnored && !ignored) {
this.playerMap.unIgnorePlayer(player);
}
this.updateChunkTracking(player);
}
}
private void updateChunkTracking(ServerPlayer player) {
ChunkPos chunkPos = player.chunkPosition();
int playerViewDistance = this.getPlayerViewDistance(player);
if (!(
player.getChunkTrackingView() instanceof ChunkTrackingView.Positioned view
&& view.center().equals(chunkPos)
&& view.viewDistance() == playerViewDistance
)) {
this.applyChunkTrackingView(player, ChunkTrackingView.of(chunkPos, playerViewDistance));
}
}
private void applyChunkTrackingView(ServerPlayer player, ChunkTrackingView next) {
if (player.level() == this.level) {
ChunkTrackingView previous = player.getChunkTrackingView();
if (next instanceof ChunkTrackingView.Positioned to
&& !(previous instanceof ChunkTrackingView.Positioned from && from.center().equals(to.center()))) {
player.connection.send(new ClientboundSetChunkCacheCenterPacket(to.center().x(), to.center().z()));
}
ChunkTrackingView.difference(previous, next, pos -> this.markChunkPendingToSend(player, pos), pos -> dropChunk(player, pos));
player.setChunkTrackingView(next);
}
}
@Override
public List<ServerPlayer> getPlayers(ChunkPos pos, boolean borderOnly) {
Set<ServerPlayer> allPlayers = this.playerMap.getAllPlayers();
Builder<ServerPlayer> result = ImmutableList.builder();
for (ServerPlayer player : allPlayers) {
if (borderOnly && this.isChunkOnTrackedBorder(player, pos.x(), pos.z()) || !borderOnly && this.isChunkTracked(player, pos.x(), pos.z())) {
result.add(player);
}
}
return result.build();
}
protected void addEntity(Entity entity) {
if (!(entity instanceof EnderDragonPart)) {
EntityType<?> type = entity.getType();
int range = type.clientTrackingRange() * 16;
if (range != 0) {
int updateInterval = type.updateInterval();
if (this.entityMap.containsKey(entity.getId())) {
throw (IllegalStateException)Util.pauseInIde(new IllegalStateException("Entity is already tracked!"));
} else {
ChunkMap.TrackedEntity trackedEntity = new ChunkMap.TrackedEntity(entity, range, updateInterval, type.trackDeltas());
this.entityMap.put(entity.getId(), trackedEntity);
trackedEntity.updatePlayers(this.level.players());
if (entity instanceof ServerPlayer player) {
this.updatePlayerStatus(player, true);
for (ChunkMap.TrackedEntity e : this.entityMap.values()) {
if (e.entity != player) {
e.updatePlayer(player);
}
}
}
}
}
}
}
protected void removeEntity(Entity entity) {
if (entity instanceof ServerPlayer player) {
this.updatePlayerStatus(player, false);
for (ChunkMap.TrackedEntity trackedEntity : this.entityMap.values()) {
trackedEntity.removePlayer(player);
}
}
ChunkMap.TrackedEntity trackedEntity = this.entityMap.remove(entity.getId());
if (trackedEntity != null) {
trackedEntity.broadcastRemoved();
}
}
protected void tick() {
for (ServerPlayer player : this.playerMap.getAllPlayers()) {
this.updateChunkTracking(player);
}
List<ServerPlayer> movedPlayers = Lists.newArrayList();
List<ServerPlayer> players = this.level.players();
for (ChunkMap.TrackedEntity trackedEntity : this.entityMap.values()) {
SectionPos oldPos = trackedEntity.lastSectionPos;
SectionPos newPos = SectionPos.of(trackedEntity.entity);
boolean sectionPosChanged = !Objects.equals(oldPos, newPos);
if (sectionPosChanged) {
trackedEntity.updatePlayers(players);
Entity entity = trackedEntity.entity;
if (entity instanceof ServerPlayer) {
movedPlayers.add((ServerPlayer)entity);
}
trackedEntity.lastSectionPos = newPos;
}
if (sectionPosChanged || trackedEntity.entity.needsSync || this.distanceManager.inEntityTickingRange(newPos.chunk().pack())) {
trackedEntity.serverEntity.sendChanges();
}
}
if (!movedPlayers.isEmpty()) {
for (ChunkMap.TrackedEntity trackedEntity : this.entityMap.values()) {
trackedEntity.updatePlayers(movedPlayers);
}
}
}
public void sendToTrackingPlayers(Entity entity, Packet<? super ClientGamePacketListener> packet) {
ChunkMap.TrackedEntity trackedEntity = this.entityMap.get(entity.getId());
if (trackedEntity != null) {
trackedEntity.sendToTrackingPlayers(packet);
}
}
public void sendToTrackingPlayersFiltered(Entity entity, Packet<? super ClientGamePacketListener> packet, Predicate<ServerPlayer> targetPredicate) {
ChunkMap.TrackedEntity trackedEntity = this.entityMap.get(entity.getId());
if (trackedEntity != null) {
trackedEntity.sendToTrackingPlayersFiltered(packet, targetPredicate);
}
}
protected void sendToTrackingPlayersAndSelf(Entity entity, Packet<? super ClientGamePacketListener> packet) {
ChunkMap.TrackedEntity trackedEntity = this.entityMap.get(entity.getId());
if (trackedEntity != null) {
trackedEntity.sendToTrackingPlayersAndSelf(packet);
}
}
public boolean isTrackedByAnyPlayer(Entity entity) {
ChunkMap.TrackedEntity trackedEntity = this.entityMap.get(entity.getId());
return trackedEntity != null ? !trackedEntity.seenBy.isEmpty() : false;
}
public void forEachEntityTrackedBy(ServerPlayer player, Consumer<Entity> consumer) {
for (ChunkMap.TrackedEntity entity : this.entityMap.values()) {
if (entity.seenBy.contains(player.connection)) {
consumer.accept(entity.entity);
}
}
}
public void resendBiomesForChunks(List<ChunkAccess> chunks) {
Map<ServerPlayer, List<LevelChunk>> chunksForPlayers = new HashMap<>();
for (ChunkAccess chunkAccess : chunks) {
ChunkPos pos = chunkAccess.getPos();
LevelChunk chunk;
if (chunkAccess instanceof LevelChunk levelChunk) {
chunk = levelChunk;
} else {
chunk = this.level.getChunk(pos.x(), pos.z());
}
for (ServerPlayer player : this.getPlayers(pos, false)) {
chunksForPlayers.computeIfAbsent(player, p -> new ArrayList<>()).add(chunk);
}
}
chunksForPlayers.forEach((playerx, chunkList) -> playerx.connection.send(ClientboundChunksBiomesPacket.forChunks((List<LevelChunk>)chunkList)));
}
protected PoiManager getPoiManager() {
return this.poiManager;
}
public String getStorageName() {
return this.storageName;
}
void onFullChunkStatusChange(ChunkPos pos, FullChunkStatus status) {
this.chunkStatusListener.onChunkStatusChange(pos, status);
}
public void waitForLightBeforeSending(ChunkPos centerChunk, int chunkRadius) {
int affectedLightChunkRadius = chunkRadius + 1;
ChunkPos.rangeClosed(centerChunk, affectedLightChunkRadius).forEach(chunkPos -> {
ChunkHolder chunkHolder = this.getVisibleChunkIfPresent(chunkPos.pack());
if (chunkHolder != null) {
chunkHolder.addSendDependency(this.lightEngine.waitForPendingTasks(chunkPos.x(), chunkPos.z()));
}
});
}
public void forEachReadyToSendChunk(Consumer<LevelChunk> consumer) {
for (ChunkHolder chunkHolder : this.visibleChunkMap.values()) {
LevelChunk chunk = chunkHolder.getChunkToSend();
if (chunk != null) {
consumer.accept(chunk);
}
}
}
private class DistanceManager extends net.minecraft.server.level.DistanceManager {
protected DistanceManager(TicketStorage ticketStorage, Executor executor, Executor mainThreadExecutor) {
Objects.requireNonNull(ChunkMap.this);
super(ticketStorage, executor, mainThreadExecutor);
}
@Override
protected boolean isChunkToRemove(long node) {
return ChunkMap.this.toDrop.contains(node);
}
@Override
protected @Nullable ChunkHolder getChunk(long node) {
return ChunkMap.this.getUpdatingChunkIfPresent(node);
}
@Override
protected @Nullable ChunkHolder updateChunkScheduling(long node, int level, @Nullable ChunkHolder chunk, int oldLevel) {
return ChunkMap.this.updateChunkScheduling(node, level, chunk, oldLevel);
}
}
private class TrackedEntity implements ServerEntity.Synchronizer {
private final ServerEntity serverEntity;
private final Entity entity;
private final int range;
private SectionPos lastSectionPos;
private final Set<ServerPlayerConnection> seenBy;
public TrackedEntity(Entity entity, int range, int updateInterval, boolean trackDelta) {
Objects.requireNonNull(ChunkMap.this);
super();
this.seenBy = Sets.newIdentityHashSet();
this.serverEntity = new ServerEntity(ChunkMap.this.level, entity, updateInterval, trackDelta, this);
this.entity = entity;
this.range = range;
this.lastSectionPos = SectionPos.of(entity);
}
@Override
public boolean equals(Object obj) {
return obj instanceof ChunkMap.TrackedEntity ? ((ChunkMap.TrackedEntity)obj).entity.getId() == this.entity.getId() : false;
}
@Override
public int hashCode() {
return this.entity.getId();
}
@Override
public void sendToTrackingPlayers(Packet<? super ClientGamePacketListener> packet) {
for (ServerPlayerConnection connection : this.seenBy) {
connection.send(packet);
}
}
@Override
public void sendToTrackingPlayersAndSelf(Packet<? super ClientGamePacketListener> packet) {
this.sendToTrackingPlayers(packet);
if (this.entity instanceof ServerPlayer player) {
player.connection.send(packet);
}
}
@Override
public void sendToTrackingPlayersFiltered(Packet<? super ClientGamePacketListener> packet, Predicate<ServerPlayer> targetPredicate) {
for (ServerPlayerConnection connection : this.seenBy) {
if (targetPredicate.test(connection.getPlayer())) {
connection.send(packet);
}
}
}
public void broadcastRemoved() {
for (ServerPlayerConnection connection : this.seenBy) {
this.serverEntity.removePairing(connection.getPlayer());
}
}
public void removePlayer(ServerPlayer player) {
if (this.seenBy.remove(player.connection)) {
this.serverEntity.removePairing(player);
if (this.seenBy.isEmpty()) {
ChunkMap.this.level.debugSynchronizers().dropEntity(this.entity);
}
}
}
public void updatePlayer(ServerPlayer player) {
if (player != this.entity) {
Vec3 deltaToPlayer = player.position().subtract(this.entity.position());
int playerViewDistance = ChunkMap.this.getPlayerViewDistance(player);
double visibleRange = Math.min(this.getEffectiveRange(), playerViewDistance * 16);
double distanceSquared = deltaToPlayer.x * deltaToPlayer.x + deltaToPlayer.z * deltaToPlayer.z;
double rangeSquared = visibleRange * visibleRange;
boolean visibleToPlayer = distanceSquared <= rangeSquared
&& this.entity.broadcastToPlayer(player)
&& ChunkMap.this.isChunkTracked(player, this.entity.chunkPosition().x(), this.entity.chunkPosition().z());
if (visibleToPlayer) {
if (this.seenBy.add(player.connection)) {
this.serverEntity.addPairing(player);
if (this.seenBy.size() == 1) {
ChunkMap.this.level.debugSynchronizers().registerEntity(this.entity);
}
ChunkMap.this.level.debugSynchronizers().startTrackingEntity(player, this.entity);
}
} else {
this.removePlayer(player);
}
}
}
private int scaledRange(int range) {
return ChunkMap.this.level.getServer().getScaledTrackingDistance(range);
}
private int getEffectiveRange() {
int effectiveRange = this.range;
for (Entity passenger : this.entity.getIndirectPassengers()) {
int passengerRange = passenger.getType().clientTrackingRange() * 16;
if (passengerRange > effectiveRange) {
effectiveRange = passengerRange;
}
}
return this.scaledRange(effectiveRange);
}
public void updatePlayers(List<ServerPlayer> players) {
for (ServerPlayer player : players) {
this.updatePlayer(player);
}
}
}
}引用的其他类
-
- 引用位置:
方法调用 - 关联成员:
CrashReport.forThrowable()
- 引用位置:
-
- 引用位置:
构造调用/返回值 - 关联成员:
ReportedException()
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
SharedConstants.getCurrentVersion()
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
System.lineSeparator()
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
SectionPos.of(), SectionPos.sectionToBlockCoord()
- 引用位置:
-
- 引用位置:
参数/构造调用/返回值 - 关联成员:
CompoundTag()
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
ClientboundChunksBiomesPacket.forChunks()
- 引用位置:
-
ClientboundPlayerInfoUpdatePacket
- 引用位置:
参数
- 引用位置:
-
ClientboundSetChunkCacheCenterPacket
- 引用位置:
构造调用 - 关联成员:
ClientboundSetChunkCacheCenterPacket()
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
参数/字段/方法调用/返回值 - 关联成员:
ChunkGenerationTask.create()
- 引用位置:
-
- 引用位置:
参数/字段/实现/构造调用/返回值 - 关联成员:
ChunkHolder()
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
ChunkLevel.byStatus(), ChunkLevel.isLoaded()
- 引用位置:
-
- 引用位置:
参数/字段/方法调用/返回值 - 关联成员:
ChunkResult.error(), ChunkResult.of()
- 引用位置:
-
- 引用位置:
字段/构造调用 - 关联成员:
ChunkTaskDispatcher()
- 引用位置:
-
- 引用位置:
参数/方法调用 - 关联成员:
ChunkTrackingView.difference(), ChunkTrackingView.of()
- 引用位置:
-
- 引用位置:
字段/返回值
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
实现
- 引用位置:
-
- 引用位置:
参数/返回值
- 引用位置:
-
- 引用位置:
字段/构造调用 - 关联成员:
PlayerMap()
- 引用位置:
-
- 引用位置:
构造调用 - 关联成员:
ServerEntity()
- 引用位置:
-
- 引用位置:
参数/字段
- 引用位置:
-
- 引用位置:
参数/返回值
- 引用位置:
-
- 引用位置:
字段/构造调用/返回值 - 关联成员:
ThreadedLevelLightEngine()
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
CsvOutput.builder()
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
Mth.clamp(), Mth.square()
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
Util.backgroundExecutor(), Util.getMillis(), Util.pauseInIde(), Util.sequence()
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
Profiler.get()
- 引用位置:
-
- 引用位置:
参数/字段
- 引用位置:
-
- 引用位置:
构造调用 - 关联成员:
ConsecutiveExecutor()
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
字段/构造调用/返回值 - 关联成员:
PoiManager()
- 引用位置:
-
- 引用位置:
参数/方法调用 - 关联成员:
ChunkPos.pack(), ChunkPos.rangeClosed(), ChunkPos.unpack()
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
参数/字段
- 引用位置:
-
- 引用位置:
参数/字段/返回值
- 引用位置:
-
- 引用位置:
参数/返回值
- 引用位置:
-
- 引用位置:
字段/返回值
- 引用位置:
-
- 引用位置:
参数/返回值
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
构造调用 - 关联成员:
ProtoChunk()
- 引用位置:
-
- 引用位置:
参数/返回值
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
字段/构造调用 - 关联成员:
WorldGenContext()
- 引用位置:
-
- 引用位置:
构造调用 - 关联成员:
RegionStorageInfo()
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
SerializableChunkData.copyOf(), SerializableChunkData.getChunkStatusFromTag(), SerializableChunkData.parse()
- 引用位置:
-
- 引用位置:
继承
- 引用位置:
-
- 引用位置:
参数/字段
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
NoiseGeneratorSettings.dummy()
- 引用位置:
-
- 引用位置:
字段/方法调用/返回值 - 关联成员:
RandomState.create()
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
参数/构造调用 - 关联成员:
Vec3()
- 引用位置: