2025-11-24 22:52:51 +03:00

265 lines
12 KiB
Java

/*
* Decompiled with CFR 0.152.
*
* Could not load the following classes:
* com.mojang.brigadier.Command
* com.mojang.brigadier.CommandDispatcher
* com.mojang.brigadier.Message
* com.mojang.brigadier.builder.LiteralArgumentBuilder
* com.mojang.brigadier.context.CommandContext
* com.mojang.brigadier.context.ContextChain
* com.mojang.brigadier.exceptions.CommandSyntaxException
* com.mojang.brigadier.exceptions.SimpleCommandExceptionType
* com.mojang.logging.LogUtils
* org.apache.commons.io.IOUtils
* org.slf4j.Logger
*/
package net.minecraft.server.commands;
import com.mojang.brigadier.Command;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.Message;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.context.ContextChain;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
import com.mojang.logging.LogUtils;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UncheckedIOException;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.attribute.FileAttribute;
import java.util.Collection;
import java.util.Locale;
import net.minecraft.commands.CommandResultCallback;
import net.minecraft.commands.CommandSource;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.commands.FunctionInstantiationException;
import net.minecraft.commands.arguments.item.FunctionArgument;
import net.minecraft.commands.execution.ChainModifiers;
import net.minecraft.commands.execution.CustomCommandExecutor;
import net.minecraft.commands.execution.ExecutionContext;
import net.minecraft.commands.execution.ExecutionControl;
import net.minecraft.commands.execution.Frame;
import net.minecraft.commands.execution.TraceCallbacks;
import net.minecraft.commands.execution.tasks.CallFunction;
import net.minecraft.commands.functions.CommandFunction;
import net.minecraft.commands.functions.InstantiatedFunction;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.Identifier;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.commands.FunctionCommand;
import net.minecraft.server.permissions.LevelBasedPermissionSet;
import net.minecraft.util.TimeUtil;
import net.minecraft.util.Util;
import net.minecraft.util.profiling.ProfileResults;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
public class DebugCommand {
private static final Logger LOGGER = LogUtils.getLogger();
private static final SimpleCommandExceptionType ERROR_NOT_RUNNING = new SimpleCommandExceptionType((Message)Component.translatable("commands.debug.notRunning"));
private static final SimpleCommandExceptionType ERROR_ALREADY_RUNNING = new SimpleCommandExceptionType((Message)Component.translatable("commands.debug.alreadyRunning"));
private static final SimpleCommandExceptionType NO_RECURSIVE_TRACES = new SimpleCommandExceptionType((Message)Component.translatable("commands.debug.function.noRecursion"));
private static final SimpleCommandExceptionType NO_RETURN_RUN = new SimpleCommandExceptionType((Message)Component.translatable("commands.debug.function.noReturnRun"));
public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
dispatcher.register((LiteralArgumentBuilder)((LiteralArgumentBuilder)((LiteralArgumentBuilder)((LiteralArgumentBuilder)Commands.literal("debug").requires(Commands.hasPermission(Commands.LEVEL_ADMINS))).then(Commands.literal("start").executes(c -> DebugCommand.start((CommandSourceStack)c.getSource())))).then(Commands.literal("stop").executes(c -> DebugCommand.stop((CommandSourceStack)c.getSource())))).then(((LiteralArgumentBuilder)Commands.literal("function").requires(Commands.hasPermission(Commands.LEVEL_ADMINS))).then(Commands.argument("name", FunctionArgument.functions()).suggests(FunctionCommand.SUGGEST_FUNCTION).executes((Command)new TraceCustomExecutor()))));
}
private static int start(CommandSourceStack source) throws CommandSyntaxException {
MinecraftServer server = source.getServer();
if (server.isTimeProfilerRunning()) {
throw ERROR_ALREADY_RUNNING.create();
}
server.startTimeProfiler();
source.sendSuccess(() -> Component.translatable("commands.debug.started"), true);
return 0;
}
private static int stop(CommandSourceStack source) throws CommandSyntaxException {
MinecraftServer server = source.getServer();
if (!server.isTimeProfilerRunning()) {
throw ERROR_NOT_RUNNING.create();
}
ProfileResults results = server.stopTimeProfiler();
double seconds = (double)results.getNanoDuration() / (double)TimeUtil.NANOSECONDS_PER_SECOND;
double tps = (double)results.getTickDuration() / seconds;
source.sendSuccess(() -> Component.translatable("commands.debug.stopped", String.format(Locale.ROOT, "%.2f", seconds), results.getTickDuration(), String.format(Locale.ROOT, "%.2f", tps)), true);
return (int)tps;
}
private static class TraceCustomExecutor
extends CustomCommandExecutor.WithErrorHandling<CommandSourceStack>
implements CustomCommandExecutor.CommandAdapter<CommandSourceStack> {
private TraceCustomExecutor() {
}
@Override
public void runGuarded(CommandSourceStack source, ContextChain<CommandSourceStack> currentStep, ChainModifiers modifiers, ExecutionControl<CommandSourceStack> context) throws CommandSyntaxException {
if (modifiers.isReturn()) {
throw NO_RETURN_RUN.create();
}
if (context.tracer() != null) {
throw NO_RECURSIVE_TRACES.create();
}
CommandContext currentContext = currentStep.getTopContext();
Collection<CommandFunction<CommandSourceStack>> functions = FunctionArgument.getFunctions((CommandContext<CommandSourceStack>)currentContext, "name");
MinecraftServer server = source.getServer();
String outputName = "debug-trace-" + Util.getFilenameFormattedDateTime() + ".txt";
CommandDispatcher<CommandSourceStack> dispatcher = source.getServer().getFunctions().getDispatcher();
int commandCount = 0;
try {
Path dirPath = server.getFile("debug");
Files.createDirectories(dirPath, new FileAttribute[0]);
final PrintWriter output = new PrintWriter(Files.newBufferedWriter(dirPath.resolve(outputName), StandardCharsets.UTF_8, new OpenOption[0]));
Tracer tracer = new Tracer(output);
context.tracer(tracer);
for (final CommandFunction<CommandSourceStack> function : functions) {
try {
CommandSourceStack functionSource = source.withSource(tracer).withMaximumPermission(LevelBasedPermissionSet.GAMEMASTER);
InstantiatedFunction<CommandSourceStack> instantiatedFunction = function.instantiate(null, dispatcher);
context.queueNext(new CallFunction<CommandSourceStack>(this, instantiatedFunction, CommandResultCallback.EMPTY, false){
@Override
public void execute(CommandSourceStack sender, ExecutionContext<CommandSourceStack> context, Frame frame) {
output.println(function.id());
super.execute(sender, context, frame);
}
}.bind(functionSource));
commandCount += instantiatedFunction.entries().size();
}
catch (FunctionInstantiationException exception) {
source.sendFailure(exception.messageComponent());
}
}
}
catch (IOException | UncheckedIOException e) {
LOGGER.warn("Tracing failed", (Throwable)e);
source.sendFailure(Component.translatable("commands.debug.function.traceFailed"));
}
int finalCommandCount = commandCount;
context.queueNext((c, frame) -> {
if (functions.size() == 1) {
source.sendSuccess(() -> Component.translatable("commands.debug.function.success.single", finalCommandCount, Component.translationArg(((CommandFunction)functions.iterator().next()).id()), outputName), true);
} else {
source.sendSuccess(() -> Component.translatable("commands.debug.function.success.multiple", finalCommandCount, functions.size(), outputName), true);
}
});
}
}
private static class Tracer
implements CommandSource,
TraceCallbacks {
public static final int INDENT_OFFSET = 1;
private final PrintWriter output;
private int lastIndent;
private boolean waitingForResult;
private Tracer(PrintWriter output) {
this.output = output;
}
private void indentAndSave(int value) {
this.printIndent(value);
this.lastIndent = value;
}
private void printIndent(int value) {
for (int i = 0; i < value + 1; ++i) {
this.output.write(" ");
}
}
private void newLine() {
if (this.waitingForResult) {
this.output.println();
this.waitingForResult = false;
}
}
@Override
public void onCommand(int depth, String command) {
this.newLine();
this.indentAndSave(depth);
this.output.print("[C] ");
this.output.print(command);
this.waitingForResult = true;
}
@Override
public void onReturn(int depth, String command, int result) {
if (this.waitingForResult) {
this.output.print(" -> ");
this.output.println(result);
this.waitingForResult = false;
} else {
this.indentAndSave(depth);
this.output.print("[R = ");
this.output.print(result);
this.output.print("] ");
this.output.println(command);
}
}
@Override
public void onCall(int depth, Identifier function, int size) {
this.newLine();
this.indentAndSave(depth);
this.output.print("[F] ");
this.output.print(function);
this.output.print(" size=");
this.output.println(size);
}
@Override
public void onError(String message) {
this.newLine();
this.indentAndSave(this.lastIndent + 1);
this.output.print("[E] ");
this.output.print(message);
}
@Override
public void sendSystemMessage(Component message) {
this.newLine();
this.printIndent(this.lastIndent + 1);
this.output.print("[M] ");
this.output.println(message.getString());
}
@Override
public boolean acceptsSuccess() {
return true;
}
@Override
public boolean acceptsFailure() {
return true;
}
@Override
public boolean shouldInformAdmins() {
return false;
}
@Override
public boolean alwaysAccepts() {
return true;
}
@Override
public void close() {
IOUtils.closeQuietly((Writer)this.output);
}
}
}