package net.minecraft.client.renderer.debug; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap.Builder; import com.mojang.blaze3d.vertex.PoseStack; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.CompletableFuture; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.Util; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientChunkCache; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.server.IntegratedServer; import net.minecraft.core.SectionPos; import net.minecraft.resources.ResourceKey; import net.minecraft.server.level.ServerChunkCache; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.Level; import net.minecraft.world.level.chunk.LevelChunk; import org.jetbrains.annotations.Nullable; @Environment(EnvType.CLIENT) public class ChunkDebugRenderer implements DebugRenderer.SimpleDebugRenderer { final Minecraft minecraft; private double lastUpdateTime = Double.MIN_VALUE; private final int radius = 12; @Nullable private ChunkDebugRenderer.ChunkData data; public ChunkDebugRenderer(Minecraft minecraft) { this.minecraft = minecraft; } @Override public void render(PoseStack poseStack, MultiBufferSource bufferSource, double camX, double camY, double camZ) { double d = Util.getNanos(); if (d - this.lastUpdateTime > 3.0E9) { this.lastUpdateTime = d; IntegratedServer integratedServer = this.minecraft.getSingleplayerServer(); if (integratedServer != null) { this.data = new ChunkDebugRenderer.ChunkData(integratedServer, camX, camZ); } else { this.data = null; } } if (this.data != null) { Map map = (Map)this.data.serverData.getNow(null); double e = this.minecraft.gameRenderer.getMainCamera().getPosition().y * 0.85; for (Entry entry : this.data.clientData.entrySet()) { ChunkPos chunkPos = (ChunkPos)entry.getKey(); String string = (String)entry.getValue(); if (map != null) { string = string + (String)map.get(chunkPos); } String[] strings = string.split("\n"); int i = 0; for (String string2 : strings) { DebugRenderer.renderFloatingText( poseStack, bufferSource, string2, SectionPos.sectionToBlockCoord(chunkPos.x, 8), e + i, SectionPos.sectionToBlockCoord(chunkPos.z, 8), -1, 0.15F, true, 0.0F, true ); i -= 2; } } } } @Environment(EnvType.CLIENT) final class ChunkData { final Map clientData; final CompletableFuture> serverData; ChunkData(final IntegratedServer integratedServer, final double x, final double z) { ClientLevel clientLevel = ChunkDebugRenderer.this.minecraft.level; ResourceKey resourceKey = clientLevel.dimension(); int i = SectionPos.posToSectionCoord(x); int j = SectionPos.posToSectionCoord(z); Builder builder = ImmutableMap.builder(); ClientChunkCache clientChunkCache = clientLevel.getChunkSource(); for (int k = i - 12; k <= i + 12; k++) { for (int l = j - 12; l <= j + 12; l++) { ChunkPos chunkPos = new ChunkPos(k, l); String string = ""; LevelChunk levelChunk = clientChunkCache.getChunk(k, l, false); string = string + "Client: "; if (levelChunk == null) { string = string + "0n/a\n"; } else { string = string + (levelChunk.isEmpty() ? " E" : ""); string = string + "\n"; } builder.put(chunkPos, string); } } this.clientData = builder.build(); this.serverData = integratedServer.submit(() -> { ServerLevel serverLevel = integratedServer.getLevel(resourceKey); if (serverLevel == null) { return ImmutableMap.of(); } else { Builder builderx = ImmutableMap.builder(); ServerChunkCache serverChunkCache = serverLevel.getChunkSource(); for (int kx = i - 12; kx <= i + 12; kx++) { for (int lx = j - 12; lx <= j + 12; lx++) { ChunkPos chunkPosx = new ChunkPos(kx, lx); builderx.put(chunkPosx, "Server: " + serverChunkCache.getChunkDebugData(chunkPosx)); } } return builderx.build(); } }); } } }