minecraft-src/net/minecraft/server/commands/PerfCommand.java
2025-07-04 01:41:11 +03:00

114 lines
4.4 KiB
Java

package net.minecraft.server.commands;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
import com.mojang.logging.LogUtils;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Locale;
import java.util.function.Consumer;
import net.minecraft.FileUtil;
import net.minecraft.SharedConstants;
import net.minecraft.SystemReport;
import net.minecraft.Util;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.network.chat.Component;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.FileZipper;
import net.minecraft.util.TimeUtil;
import net.minecraft.util.profiling.EmptyProfileResults;
import net.minecraft.util.profiling.ProfileResults;
import net.minecraft.util.profiling.metrics.storage.MetricsPersister;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
public class PerfCommand {
private static final Logger LOGGER = LogUtils.getLogger();
private static final SimpleCommandExceptionType ERROR_NOT_RUNNING = new SimpleCommandExceptionType(Component.translatable("commands.perf.notRunning"));
private static final SimpleCommandExceptionType ERROR_ALREADY_RUNNING = new SimpleCommandExceptionType(Component.translatable("commands.perf.alreadyRunning"));
public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
dispatcher.register(
Commands.literal("perf")
.requires(commandSourceStack -> commandSourceStack.hasPermission(4))
.then(Commands.literal("start").executes(commandContext -> startProfilingDedicatedServer(commandContext.getSource())))
.then(Commands.literal("stop").executes(commandContext -> stopProfilingDedicatedServer(commandContext.getSource())))
);
}
private static int startProfilingDedicatedServer(CommandSourceStack source) throws CommandSyntaxException {
MinecraftServer minecraftServer = source.getServer();
if (minecraftServer.isRecordingMetrics()) {
throw ERROR_ALREADY_RUNNING.create();
} else {
Consumer<ProfileResults> consumer = profileResults -> whenStopped(source, profileResults);
Consumer<Path> consumer2 = path -> saveResults(source, path, minecraftServer);
minecraftServer.startRecordingMetrics(consumer, consumer2);
source.sendSuccess(() -> Component.translatable("commands.perf.started"), false);
return 0;
}
}
private static int stopProfilingDedicatedServer(CommandSourceStack source) throws CommandSyntaxException {
MinecraftServer minecraftServer = source.getServer();
if (!minecraftServer.isRecordingMetrics()) {
throw ERROR_NOT_RUNNING.create();
} else {
minecraftServer.finishRecordingMetrics();
return 0;
}
}
private static void saveResults(CommandSourceStack source, Path path, MinecraftServer server) {
String string = String.format(
Locale.ROOT, "%s-%s-%s", Util.getFilenameFormattedDateTime(), server.getWorldData().getLevelName(), SharedConstants.getCurrentVersion().getId()
);
String string2;
try {
string2 = FileUtil.findAvailableName(MetricsPersister.PROFILING_RESULTS_DIR, string, ".zip");
} catch (IOException var11) {
source.sendFailure(Component.translatable("commands.perf.reportFailed"));
LOGGER.error("Failed to create report name", (Throwable)var11);
return;
}
FileZipper fileZipper = new FileZipper(MetricsPersister.PROFILING_RESULTS_DIR.resolve(string2));
try {
fileZipper.add(Paths.get("system.txt"), server.fillSystemReport(new SystemReport()).toLineSeparatedString());
fileZipper.add(path);
} catch (Throwable var10) {
try {
fileZipper.close();
} catch (Throwable var8) {
var10.addSuppressed(var8);
}
throw var10;
}
fileZipper.close();
try {
FileUtils.forceDelete(path.toFile());
} catch (IOException var9) {
LOGGER.warn("Failed to delete temporary profiling file {}", path, var9);
}
source.sendSuccess(() -> Component.translatable("commands.perf.reportSaved", string2), false);
}
private static void whenStopped(CommandSourceStack source, ProfileResults results) {
if (results != EmptyProfileResults.EMPTY) {
int i = results.getTickDuration();
double d = (double)results.getNanoDuration() / TimeUtil.NANOSECONDS_PER_SECOND;
source.sendSuccess(
() -> Component.translatable("commands.perf.stopped", String.format(Locale.ROOT, "%.2f", d), i, String.format(Locale.ROOT, "%.2f", i / d)), false
);
}
}
}