Add debug chunks command
This feature dumps the entire chunk system state to disk, useful for debugging the chunk system state without a debugger.
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1349,10 +1349,6 @@ public final class ChunkHolderManager {
|
||||
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();
|
||||
|
||||
@@ -6,12 +6,14 @@ import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedThreadedTaskQu
|
||||
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;
|
||||
@@ -21,24 +23,34 @@ import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.task.ChunkUpgrade
|
||||
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;
|
||||
@@ -867,6 +879,16 @@ public final class ChunkTaskScheduler {
|
||||
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) + "']";
|
||||
@@ -890,4 +912,113 @@ public final class ChunkTaskScheduler {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -24,6 +27,7 @@ import net.minecraft.world.level.chunk.ImposterProtoChunk;
|
||||
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;
|
||||
@@ -32,6 +36,8 @@ import java.util.List;
|
||||
|
||||
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) -> {
|
||||
@@ -62,6 +68,14 @@ public final class MoonriseCommand {
|
||||
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);
|
||||
})
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -238,4 +252,36 @@ public final class MoonriseCommand {
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user