ClientChunkCache.java
net.minecraft.client.multiplayer.ClientChunkCache
信息
- 全限定名:net.minecraft.client.multiplayer.ClientChunkCache
- 类型:public class
- 包:net.minecraft.client.multiplayer
- 源码路径:src/main/java/net/minecraft/client/multiplayer/ClientChunkCache.java
- 起始行号:L35
- 继承:ChunkSource
- 职责:
TODO
字段/常量
-
LOGGER- 类型:
Logger - 修饰符:
private static final - 源码定位:
L36 - 说明:
TODO
- 类型:
-
emptyChunk- 类型:
LevelChunk - 修饰符:
private final - 源码定位:
L37 - 说明:
TODO
- 类型:
-
lightEngine- 类型:
LevelLightEngine - 修饰符:
private final - 源码定位:
L38 - 说明:
TODO
- 类型:
-
storage- 类型:
ClientChunkCache.Storage - 修饰符:
private volatile - 源码定位:
L39 - 说明:
TODO
- 类型:
-
level- 类型:
ClientLevel - 修饰符:
private final - 源码定位:
L40 - 说明:
TODO
- 类型:
内部类/嵌套类型
net.minecraft.client.multiplayer.ClientChunkCache.Storage- 类型:
class - 修饰符:
private final - 源码定位:
L191 - 说明:
TODO
- 类型:
构造器
public ClientChunkCache(ClientLevel level, int serverChunkRadius) @ L42
- 构造器名:ClientChunkCache
- 源码定位:L42
- 修饰符:public
参数:
- level: ClientLevel
- serverChunkRadius: int
说明:
TODO
方法
下面的方法块按源码顺序生成。
public LevelLightEngine getLightEngine() @ L49
- 方法名:getLightEngine
- 源码定位:L49
- 返回类型:LevelLightEngine
- 修饰符:public
参数:
- 无
说明:
TODO
private static boolean isValidChunk(LevelChunk chunk, int x, int z) @ L54
- 方法名:isValidChunk
- 源码定位:L54
- 返回类型:boolean
- 修饰符:private static
参数:
- chunk: LevelChunk
- x: int
- z: int
说明:
TODO
public void drop(ChunkPos pos) @ L63
- 方法名:drop
- 源码定位:L63
- 返回类型:void
- 修饰符:public
参数:
- pos: ChunkPos
说明:
TODO
public LevelChunk getChunk(int x, int z, ChunkStatus targetStatus, boolean loadOrGenerate) @ L73
- 方法名:getChunk
- 源码定位:L73
- 返回类型:LevelChunk
- 修饰符:public
参数:
- x: int
- z: int
- targetStatus: ChunkStatus
- loadOrGenerate: boolean
说明:
TODO
public BlockGetter getLevel() @ L84
- 方法名:getLevel
- 源码定位:L84
- 返回类型:BlockGetter
- 修饰符:public
参数:
- 无
说明:
TODO
public void replaceBiomes(int chunkX, int chunkZ, FriendlyByteBuf readBuffer) @ L89
- 方法名:replaceBiomes
- 源码定位:L89
- 返回类型:void
- 修饰符:public
参数:
- chunkX: int
- chunkZ: int
- readBuffer: FriendlyByteBuf
说明:
TODO
public LevelChunk replaceWithPacketData(int chunkX, int chunkZ, FriendlyByteBuf readBuffer, Map<Heightmap.Types,long[]> heightmaps, Consumer<ClientboundLevelChunkPacketData.BlockEntityTagOutput> blockEntities) @ L103
- 方法名:replaceWithPacketData
- 源码定位:L103
- 返回类型:LevelChunk
- 修饰符:public
参数:
- chunkX: int
- chunkZ: int
- readBuffer: FriendlyByteBuf
- heightmaps: Map<Heightmap.Types,long[]>
- blockEntities: Consumer<ClientboundLevelChunkPacketData.BlockEntityTagOutput>
说明:
TODO
public void tick(BooleanSupplier haveTime, boolean tickChunks) @ L131
- 方法名:tick
- 源码定位:L131
- 返回类型:void
- 修饰符:public
参数:
- haveTime: BooleanSupplier
- tickChunks: boolean
说明:
TODO
public void updateViewCenter(int x, int z) @ L135
- 方法名:updateViewCenter
- 源码定位:L135
- 返回类型:void
- 修饰符:public
参数:
- x: int
- z: int
说明:
TODO
public void updateViewRadius(int viewRange) @ L140
- 方法名:updateViewRadius
- 源码定位:L140
- 返回类型:void
- 修饰符:public
参数:
- viewRange: int
说明:
TODO
private static int calculateStorageRange(int viewRange) @ L162
- 方法名:calculateStorageRange
- 源码定位:L162
- 返回类型:int
- 修饰符:private static
参数:
- viewRange: int
说明:
TODO
public String gatherStats() @ L166
- 方法名:gatherStats
- 源码定位:L166
- 返回类型:String
- 修饰符:public
参数:
- 无
说明:
TODO
public int getLoadedChunksCount() @ L171
- 方法名:getLoadedChunksCount
- 源码定位:L171
- 返回类型:int
- 修饰符:public
参数:
- 无
说明:
TODO
public void onLightUpdate(LightLayer layer, SectionPos pos) @ L176
- 方法名:onLightUpdate
- 源码定位:L176
- 返回类型:void
- 修饰符:public
参数:
- layer: LightLayer
- pos: SectionPos
说明:
TODO
public LongOpenHashSet getLoadedEmptySections() @ L181
- 方法名:getLoadedEmptySections
- 源码定位:L181
- 返回类型:LongOpenHashSet
- 修饰符:public
参数:
- 无
说明:
TODO
public void onSectionEmptinessChanged(int sectionX, int sectionY, int sectionZ, boolean empty) @ L185
- 方法名:onSectionEmptinessChanged
- 源码定位:L185
- 返回类型:void
- 修饰符:public
参数:
- sectionX: int
- sectionY: int
- sectionZ: int
- empty: boolean
说明:
TODO
代码
@OnlyIn(Dist.CLIENT)
public class ClientChunkCache extends ChunkSource {
private static final Logger LOGGER = LogUtils.getLogger();
private final LevelChunk emptyChunk;
private final LevelLightEngine lightEngine;
private volatile ClientChunkCache.Storage storage;
private final ClientLevel level;
public ClientChunkCache(ClientLevel level, int serverChunkRadius) {
this.level = level;
this.emptyChunk = new EmptyLevelChunk(level, new ChunkPos(0, 0), level.registryAccess().lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.PLAINS));
this.lightEngine = new LevelLightEngine(this, true, level.dimensionType().hasSkyLight());
this.storage = new ClientChunkCache.Storage(calculateStorageRange(serverChunkRadius));
}
@Override
public LevelLightEngine getLightEngine() {
return this.lightEngine;
}
private static boolean isValidChunk(@Nullable LevelChunk chunk, int x, int z) {
if (chunk == null) {
return false;
} else {
ChunkPos pos = chunk.getPos();
return pos.x() == x && pos.z() == z;
}
}
public void drop(ChunkPos pos) {
if (this.storage.inRange(pos.x(), pos.z())) {
int index = this.storage.getIndex(pos.x(), pos.z());
LevelChunk currentChunk = this.storage.getChunk(index);
if (isValidChunk(currentChunk, pos.x(), pos.z())) {
this.storage.drop(index, currentChunk);
}
}
}
public @Nullable LevelChunk getChunk(int x, int z, ChunkStatus targetStatus, boolean loadOrGenerate) {
if (this.storage.inRange(x, z)) {
LevelChunk chunk = this.storage.getChunk(this.storage.getIndex(x, z));
if (isValidChunk(chunk, x, z)) {
return chunk;
}
}
return loadOrGenerate ? this.emptyChunk : null;
}
@Override
public BlockGetter getLevel() {
return this.level;
}
public void replaceBiomes(int chunkX, int chunkZ, FriendlyByteBuf readBuffer) {
if (!this.storage.inRange(chunkX, chunkZ)) {
LOGGER.warn("Ignoring chunk since it's not in the view range: {}, {}", chunkX, chunkZ);
} else {
int index = this.storage.getIndex(chunkX, chunkZ);
LevelChunk chunk = this.storage.chunks.get(index);
if (!isValidChunk(chunk, chunkX, chunkZ)) {
LOGGER.warn("Ignoring chunk since it's not present: {}, {}", chunkX, chunkZ);
} else {
chunk.replaceBiomes(readBuffer);
}
}
}
public @Nullable LevelChunk replaceWithPacketData(
int chunkX,
int chunkZ,
FriendlyByteBuf readBuffer,
Map<Heightmap.Types, long[]> heightmaps,
Consumer<ClientboundLevelChunkPacketData.BlockEntityTagOutput> blockEntities
) {
if (!this.storage.inRange(chunkX, chunkZ)) {
LOGGER.warn("Ignoring chunk since it's not in the view range: {}, {}", chunkX, chunkZ);
return null;
} else {
int index = this.storage.getIndex(chunkX, chunkZ);
LevelChunk chunk = this.storage.chunks.get(index);
ChunkPos pos = new ChunkPos(chunkX, chunkZ);
if (!isValidChunk(chunk, chunkX, chunkZ)) {
chunk = new LevelChunk(this.level, pos);
chunk.replaceWithPacketData(readBuffer, heightmaps, blockEntities);
this.storage.replace(index, chunk);
} else {
chunk.replaceWithPacketData(readBuffer, heightmaps, blockEntities);
this.storage.refreshEmptySections(chunk);
}
this.level.onChunkLoaded(pos);
return chunk;
}
}
@Override
public void tick(BooleanSupplier haveTime, boolean tickChunks) {
}
public void updateViewCenter(int x, int z) {
this.storage.viewCenterX = x;
this.storage.viewCenterZ = z;
}
public void updateViewRadius(int viewRange) {
int chunkRadius = this.storage.chunkRadius;
int newChunkRadius = calculateStorageRange(viewRange);
if (chunkRadius != newChunkRadius) {
ClientChunkCache.Storage newStorage = new ClientChunkCache.Storage(newChunkRadius);
newStorage.viewCenterX = this.storage.viewCenterX;
newStorage.viewCenterZ = this.storage.viewCenterZ;
for (int i = 0; i < this.storage.chunks.length(); i++) {
LevelChunk chunk = this.storage.chunks.get(i);
if (chunk != null) {
ChunkPos pos = chunk.getPos();
if (newStorage.inRange(pos.x(), pos.z())) {
newStorage.replace(newStorage.getIndex(pos.x(), pos.z()), chunk);
}
}
}
this.storage = newStorage;
}
}
private static int calculateStorageRange(int viewRange) {
return Math.max(2, viewRange) + 3;
}
@Override
public String gatherStats() {
return this.storage.chunks.length() + ", " + this.getLoadedChunksCount();
}
@Override
public int getLoadedChunksCount() {
return this.storage.chunkCount;
}
@Override
public void onLightUpdate(LightLayer layer, SectionPos pos) {
Minecraft.getInstance().levelRenderer.setSectionDirty(pos.x(), pos.y(), pos.z());
}
public LongOpenHashSet getLoadedEmptySections() {
return this.storage.loadedEmptySections;
}
@Override
public void onSectionEmptinessChanged(int sectionX, int sectionY, int sectionZ, boolean empty) {
this.storage.onSectionEmptinessChanged(sectionX, sectionY, sectionZ, empty);
}
@OnlyIn(Dist.CLIENT)
private final class Storage {
private final AtomicReferenceArray<@Nullable LevelChunk> chunks;
private final LongOpenHashSet loadedEmptySections;
private final int chunkRadius;
private final int viewRange;
private volatile int viewCenterX;
private volatile int viewCenterZ;
private int chunkCount;
private Storage(int chunkRadius) {
Objects.requireNonNull(ClientChunkCache.this);
super();
this.loadedEmptySections = new LongOpenHashSet();
this.chunkRadius = chunkRadius;
this.viewRange = chunkRadius * 2 + 1;
this.chunks = new AtomicReferenceArray<>(this.viewRange * this.viewRange);
}
private int getIndex(int chunkX, int chunkZ) {
return Math.floorMod(chunkZ, this.viewRange) * this.viewRange + Math.floorMod(chunkX, this.viewRange);
}
private void replace(int index, @Nullable LevelChunk newChunk) {
LevelChunk removedChunk = this.chunks.getAndSet(index, newChunk);
if (removedChunk != null) {
this.chunkCount--;
this.dropEmptySections(removedChunk);
ClientChunkCache.this.level.unload(removedChunk);
}
if (newChunk != null) {
this.chunkCount++;
this.addEmptySections(newChunk);
}
}
private void drop(int index, LevelChunk oldChunk) {
if (this.chunks.compareAndSet(index, oldChunk, null)) {
this.chunkCount--;
this.dropEmptySections(oldChunk);
}
ClientChunkCache.this.level.unload(oldChunk);
}
public void onSectionEmptinessChanged(int sectionX, int sectionY, int sectionZ, boolean empty) {
if (this.inRange(sectionX, sectionZ)) {
long sectionNode = SectionPos.asLong(sectionX, sectionY, sectionZ);
if (empty) {
this.loadedEmptySections.add(sectionNode);
} else if (this.loadedEmptySections.remove(sectionNode)) {
ClientChunkCache.this.level.onSectionBecomingNonEmpty(sectionNode);
}
}
}
private void dropEmptySections(LevelChunk chunk) {
LevelChunkSection[] sections = chunk.getSections();
for (int sectionIndex = 0; sectionIndex < sections.length; sectionIndex++) {
ChunkPos chunkPos = chunk.getPos();
this.loadedEmptySections.remove(SectionPos.asLong(chunkPos.x(), chunk.getSectionYFromSectionIndex(sectionIndex), chunkPos.z()));
}
}
private void addEmptySections(LevelChunk chunk) {
LevelChunkSection[] sections = chunk.getSections();
for (int sectionIndex = 0; sectionIndex < sections.length; sectionIndex++) {
LevelChunkSection section = sections[sectionIndex];
if (section.hasOnlyAir()) {
ChunkPos chunkPos = chunk.getPos();
this.loadedEmptySections.add(SectionPos.asLong(chunkPos.x(), chunk.getSectionYFromSectionIndex(sectionIndex), chunkPos.z()));
}
}
}
private void refreshEmptySections(LevelChunk chunk) {
ChunkPos chunkPos = chunk.getPos();
LevelChunkSection[] sections = chunk.getSections();
for (int sectionIndex = 0; sectionIndex < sections.length; sectionIndex++) {
LevelChunkSection section = sections[sectionIndex];
long sectionNode = SectionPos.asLong(chunkPos.x(), chunk.getSectionYFromSectionIndex(sectionIndex), chunkPos.z());
if (section.hasOnlyAir()) {
this.loadedEmptySections.add(sectionNode);
} else if (this.loadedEmptySections.remove(sectionNode)) {
ClientChunkCache.this.level.onSectionBecomingNonEmpty(sectionNode);
}
}
}
private boolean inRange(int chunkX, int chunkZ) {
return Math.abs(chunkX - this.viewCenterX) <= this.chunkRadius && Math.abs(chunkZ - this.viewCenterZ) <= this.chunkRadius;
}
protected @Nullable LevelChunk getChunk(int index) {
return this.chunks.get(index);
}
private void dumpChunks(String file) {
try (FileOutputStream stream = new FileOutputStream(file)) {
int chunkRadius = ClientChunkCache.this.storage.chunkRadius;
for (int z = this.viewCenterZ - chunkRadius; z <= this.viewCenterZ + chunkRadius; z++) {
for (int x = this.viewCenterX - chunkRadius; x <= this.viewCenterX + chunkRadius; x++) {
LevelChunk chunk = ClientChunkCache.this.storage.chunks.get(ClientChunkCache.this.storage.getIndex(x, z));
if (chunk != null) {
ChunkPos pos = chunk.getPos();
stream.write((pos.x() + "\t" + pos.z() + "\t" + chunk.isEmpty() + "\n").getBytes(StandardCharsets.UTF_8));
}
}
}
} catch (IOException var10) {
ClientChunkCache.LOGGER.error("Failed to dump chunks to file {}", file, var10);
}
}
}
}引用的其他类
-
- 引用位置:
方法调用 - 关联成员:
Minecraft.getInstance()
- 引用位置:
-
- 引用位置:
参数/字段
- 引用位置:
-
- 引用位置:
参数/方法调用 - 关联成员:
SectionPos.asLong()
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
ClientboundLevelChunkPacketData
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
返回值
- 引用位置:
-
- 引用位置:
参数/构造调用 - 关联成员:
ChunkPos()
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
继承
- 引用位置:
-
- 引用位置:
构造调用 - 关联成员:
EmptyLevelChunk()
- 引用位置:
-
- 引用位置:
参数/字段/构造调用/返回值 - 关联成员:
LevelChunk()
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
字段/构造调用/返回值 - 关联成员:
LevelLightEngine()
- 引用位置: