Skip to content

Commit

Permalink
Add debug chunks command
Browse files Browse the repository at this point in the history
This feature dumps the entire chunk system state to disk, useful
for debugging the chunk system state without a debugger.
  • Loading branch information
Spottedleaf committed Jun 19, 2024
1 parent 4911c50 commit 044a23f
Show file tree
Hide file tree
Showing 4 changed files with 211 additions and 4 deletions.
34 changes: 34 additions & 0 deletions src/main/java/ca/spottedleaf/moonrise/common/util/JsonUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package ca.spottedleaf.moonrise.common.util;

import com.google.gson.JsonElement;
import com.google.gson.internal.Streams;
import com.google.gson.stream.JsonWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;

public final class JsonUtil {

public static void writeJson(final JsonElement element, final File file) throws IOException {
final StringWriter stringWriter = new StringWriter();
final JsonWriter jsonWriter = new JsonWriter(stringWriter);
jsonWriter.setIndent(" ");
jsonWriter.setLenient(false);
Streams.write(element, jsonWriter);

final String jsonString = stringWriter.toString();

final File parent = file.getParentFile();
if (parent != null) {
parent.mkdirs();
}
file.createNewFile();
try (final PrintStream out = new PrintStream(new FileOutputStream(file), false, StandardCharsets.UTF_8)) {
out.print(jsonString);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -1349,10 +1349,6 @@ private boolean processPendingFullUpdate() {
public JsonObject getDebugJson() {
final JsonObject ret = new JsonObject();

ret.addProperty("lock_shift", Integer.valueOf(this.taskScheduler.getChunkSystemLockShift()));
ret.addProperty("ticket_shift", Integer.valueOf(ThreadedTicketLevelPropagator.SECTION_SHIFT));
ret.addProperty("region_shift", Integer.valueOf(((ChunkSystemServerLevel)this.world).moonrise$getRegionChunkShift()));

ret.add("unload_queue", this.unloadQueue.toDebugJson());

final JsonArray holders = new JsonArray();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@
import ca.spottedleaf.concurrentutil.lock.ReentrantAreaLock;
import ca.spottedleaf.concurrentutil.util.ConcurrentUtil;
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
import ca.spottedleaf.moonrise.common.util.JsonUtil;
import ca.spottedleaf.moonrise.common.util.MoonriseCommon;
import ca.spottedleaf.moonrise.common.util.TickThread;
import ca.spottedleaf.moonrise.common.util.WorldUtil;
import ca.spottedleaf.moonrise.patches.chunk_system.io.RegionFileIOThread;
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemChunkStatus;
import ca.spottedleaf.moonrise.patches.chunk_system.player.ChunkSystemServerPlayer;
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.executor.RadiusAwarePrioritisedExecutor;
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.task.ChunkFullTask;
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.task.ChunkLightTask;
Expand All @@ -21,24 +23,34 @@
import ca.spottedleaf.moonrise.patches.chunk_system.server.ChunkSystemMinecraftServer;
import ca.spottedleaf.moonrise.patches.chunk_system.status.ChunkSystemChunkStep;
import ca.spottedleaf.moonrise.patches.chunk_system.util.ParallelSearchRadiusIteration;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import net.minecraft.CrashReport;
import net.minecraft.CrashReportCategory;
import net.minecraft.ReportedException;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ChunkLevel;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.FullChunkStatus;
import net.minecraft.server.level.GenerationChunkHolder;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.level.TicketType;
import net.minecraft.util.StaticCache2D;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.status.ChunkPyramid;
import net.minecraft.world.level.chunk.status.ChunkStatus;
import net.minecraft.world.level.chunk.status.ChunkStep;
import net.minecraft.world.phys.Vec3;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
Expand Down Expand Up @@ -867,6 +879,16 @@ public ChunkInfo(final int chunkX, final int chunkZ, final ServerLevel world) {
this.world = world;
}

public JsonObject toJson() {
final JsonObject ret = new JsonObject();

ret.addProperty("chunk-x", Integer.valueOf(this.chunkX));
ret.addProperty("chunk-z", Integer.valueOf(this.chunkZ));
ret.addProperty("world-name", WorldUtil.getWorldName(this.world));

return ret;
}

@Override
public String toString() {
return "[( " + this.chunkX + "," + this.chunkZ + ") in '" + WorldUtil.getWorldName(this.world) + "']";
Expand All @@ -890,4 +912,113 @@ public static ChunkInfo[] getChunkInfos() {
return WAITING_CHUNKS.toArray(new ChunkInfo[0]);
}
}

private static JsonObject debugPlayer(final ServerPlayer player) {
final Level world = player.level();

final JsonObject ret = new JsonObject();

ret.addProperty("name", player.getScoreboardName());
ret.addProperty("uuid", player.getUUID().toString());
ret.addProperty("real", ((ChunkSystemServerPlayer)player).moonrise$isRealPlayer());

ret.addProperty("world-name", WorldUtil.getWorldName(world));

final Vec3 pos = player.position();

ret.addProperty("x", pos.x);
ret.addProperty("y", pos.y);
ret.addProperty("z", pos.z);

final Entity.RemovalReason removalReason = player.getRemovalReason();

ret.addProperty("removal-reason", removalReason == null ? "null" : removalReason.name());

ret.add("view-distances", ((ChunkSystemServerPlayer)player).moonrise$getViewDistanceHolder().toJson());

return ret;
}

public JsonObject getDebugJson() {
final JsonObject ret = new JsonObject();

ret.addProperty("lock_shift", Integer.valueOf(this.getChunkSystemLockShift()));
ret.addProperty("ticket_shift", Integer.valueOf(ThreadedTicketLevelPropagator.SECTION_SHIFT));
ret.addProperty("region_shift", Integer.valueOf(((ChunkSystemServerLevel)this.world).moonrise$getRegionChunkShift()));

ret.addProperty("name", WorldUtil.getWorldName(this.world));
ret.addProperty("view-distance", ((ChunkSystemServerLevel)this.world).moonrise$getPlayerChunkLoader().getAPIViewDistance());
ret.addProperty("tick-distance", ((ChunkSystemServerLevel)this.world).moonrise$getPlayerChunkLoader().getAPITickDistance());
ret.addProperty("send-distance", ((ChunkSystemServerLevel)this.world).moonrise$getPlayerChunkLoader().getAPISendViewDistance());

final JsonArray players = new JsonArray();
ret.add("players", players);

for (final ServerPlayer player : this.world.players()) {
players.add(debugPlayer(player));
}

ret.add("chunk-holder-manager", this.chunkHolderManager.getDebugJson());

return ret;
}

public static JsonObject debugAllWorlds(final MinecraftServer server) {
final JsonObject ret = new JsonObject();

ret.addProperty("data-version", 2);

final JsonArray allPlayers = new JsonArray();
ret.add("all-players", allPlayers);

for (final ServerPlayer player : server.getPlayerList().getPlayers()) {
allPlayers.add(debugPlayer(player));
}

final JsonArray chunkWaitInfos = new JsonArray();
ret.add("chunk-wait-infos", chunkWaitInfos);

for (final ChunkTaskScheduler.ChunkInfo info : getChunkInfos()) {
chunkWaitInfos.add(info.toJson());
}

final JsonArray worlds = new JsonArray();
ret.add("worlds", worlds);

for (final ServerLevel world : server.getAllLevels()) {
worlds.add(((ChunkSystemServerLevel)world).moonrise$getChunkTaskScheduler().getDebugJson());
}

return ret;
}

public static File getChunkDebugFile() {
return new File(
new File(new File("."), "debug"),
"chunks-" + DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss").format(LocalDateTime.now()) + ".txt"
);
}

public static void dumpAllChunkLoadInfo(final MinecraftServer server, final boolean writeDebugInfo) {
final ChunkInfo[] chunkInfos = getChunkInfos();
if (chunkInfos.length > 0) {
LOGGER.error("Chunk wait task info below: ");
for (final ChunkInfo chunkInfo : chunkInfos) {
final NewChunkHolder holder = ((ChunkSystemServerLevel)chunkInfo.world).moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(chunkInfo.chunkX, chunkInfo.chunkZ);
LOGGER.error("Chunk wait: " + chunkInfo);
LOGGER.error("Chunk holder: " + holder);
}

if (writeDebugInfo) {
final File file = getChunkDebugFile();
LOGGER.error("Writing chunk information dump to " + file);
try {
JsonUtil.writeJson(ChunkTaskScheduler.debugAllWorlds(server), file);
LOGGER.error("Successfully written chunk information!");
} catch (final Throwable thr) {
LOGGER.error("Failed to dump chunk information to file " + file.toString(), thr);
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
package ca.spottedleaf.moonrise.patches.command;

import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
import ca.spottedleaf.moonrise.common.util.JsonUtil;
import ca.spottedleaf.moonrise.common.util.MoonriseCommon;
import ca.spottedleaf.moonrise.common.util.MoonriseConstants;
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
import ca.spottedleaf.moonrise.patches.chunk_system.player.ChunkSystemServerPlayer;
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler;
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder;
import ca.spottedleaf.moonrise.patches.starlight.light.StarLightLightingProvider;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.logging.LogUtils;
import it.unimi.dsi.fastutil.longs.LongArrayFIFOQueue;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import net.minecraft.commands.CommandSourceStack;
Expand All @@ -24,6 +27,7 @@
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.phys.Vec3;
import org.slf4j.Logger;
import java.io.File;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
Expand All @@ -32,6 +36,8 @@

public final class MoonriseCommand {

private static final Logger LOGGER = LogUtils.getLogger();

public static void register(final CommandDispatcher<CommandSourceStack> dispatcher) {
dispatcher.register(
Commands.literal("moonrise").requires((final CommandSourceStack src) -> {
Expand Down Expand Up @@ -62,6 +68,14 @@ public static void register(final CommandDispatcher<CommandSourceStack> dispatch
return MoonriseCommand.relight(ctx, IntegerArgumentType.getInteger(ctx, "radius"));
})
)
).then(
Commands.literal("debug")
.then(
Commands.literal("chunks")
.executes((final CommandContext<CommandSourceStack> ctx) -> {
return MoonriseCommand.debugChunks(ctx);
})
)
)
);
}
Expand Down Expand Up @@ -238,4 +252,36 @@ public static int relight(final CommandContext<CommandSourceStack> ctx, final in

return ret;
}

public static int debugChunks(final CommandContext<CommandSourceStack> ctx) {
final File file = ChunkTaskScheduler.getChunkDebugFile();

ctx.getSource().sendSuccess(() -> {
return MutableComponent.create(
new PlainTextContents.LiteralContents(
"Writing chunk information dump to '" + file + "'"
)
);
}, true);
try {
JsonUtil.writeJson(ChunkTaskScheduler.debugAllWorlds(ctx.getSource().getServer()), file);

ctx.getSource().sendSuccess(() -> {
return MutableComponent.create(
new PlainTextContents.LiteralContents(
"Wrote chunk information dump to '" + file + "'"
)
);
}, true);
} catch (final Throwable throwable) {
LOGGER.error("Failed to dump chunk information to file '" + file.getAbsolutePath() + "'", throwable);
ctx.getSource().sendFailure(MutableComponent.create(
new PlainTextContents.LiteralContents(
"Failed to dump chunk information, see console"
)
));
}

return 0;
}
}

0 comments on commit 044a23f

Please sign in to comment.