File size: 4,949 Bytes
d46f4a3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
package net.minecraft.server.commands;

import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.context.CommandContext;
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> p_180438_) {
        p_180438_.register(
            Commands.literal("perf")
                .requires(p_180462_ -> p_180462_.hasPermission(4))
                .then(Commands.literal("start").executes(p_180455_ -> startProfilingDedicatedServer(p_180455_.getSource())))
                .then(Commands.literal("stop").executes(p_180440_ -> stopProfilingDedicatedServer(p_180440_.getSource())))
        );
    }

    private static int startProfilingDedicatedServer(CommandSourceStack p_180442_) throws CommandSyntaxException {
        MinecraftServer minecraftserver = p_180442_.getServer();
        if (minecraftserver.isRecordingMetrics()) {
            throw ERROR_ALREADY_RUNNING.create();
        } else {
            Consumer<ProfileResults> consumer = p_180460_ -> whenStopped(p_180442_, p_180460_);
            Consumer<Path> consumer1 = p_180453_ -> saveResults(p_180442_, p_180453_, minecraftserver);
            minecraftserver.startRecordingMetrics(consumer, consumer1);
            p_180442_.sendSuccess(() -> Component.translatable("commands.perf.started"), false);
            return 0;
        }
    }

    private static int stopProfilingDedicatedServer(CommandSourceStack p_180457_) throws CommandSyntaxException {
        MinecraftServer minecraftserver = p_180457_.getServer();
        if (!minecraftserver.isRecordingMetrics()) {
            throw ERROR_NOT_RUNNING.create();
        } else {
            minecraftserver.finishRecordingMetrics();
            return 0;
        }
    }

    private static void saveResults(CommandSourceStack p_180447_, Path p_180448_, MinecraftServer p_180449_) {
        String s = String.format(Locale.ROOT, "%s-%s-%s", Util.getFilenameFormattedDateTime(), p_180449_.getWorldData().getLevelName(), SharedConstants.getCurrentVersion().getId());

        String s1;
        try {
            s1 = FileUtil.findAvailableName(MetricsPersister.PROFILING_RESULTS_DIR, s, ".zip");
        } catch (IOException ioexception1) {
            p_180447_.sendFailure(Component.translatable("commands.perf.reportFailed"));
            LOGGER.error("Failed to create report name", (Throwable)ioexception1);
            return;
        }

        try (FileZipper filezipper = new FileZipper(MetricsPersister.PROFILING_RESULTS_DIR.resolve(s1))) {
            filezipper.add(Paths.get("system.txt"), p_180449_.fillSystemReport(new SystemReport()).toLineSeparatedString());
            filezipper.add(p_180448_);
        }

        try {
            FileUtils.forceDelete(p_180448_.toFile());
        } catch (IOException ioexception) {
            LOGGER.warn("Failed to delete temporary profiling file {}", p_180448_, ioexception);
        }

        p_180447_.sendSuccess(() -> Component.translatable("commands.perf.reportSaved", s1), false);
    }

    private static void whenStopped(CommandSourceStack p_180444_, ProfileResults p_180445_) {
        if (p_180445_ != EmptyProfileResults.EMPTY) {
            int i = p_180445_.getTickDuration();
            double d0 = (double)p_180445_.getNanoDuration() / (double)TimeUtil.NANOSECONDS_PER_SECOND;
            p_180444_.sendSuccess(
                () -> Component.translatable(
                        "commands.perf.stopped", String.format(Locale.ROOT, "%.2f", d0), i, String.format(Locale.ROOT, "%.2f", (double)i / d0)
                    ),
                false
            );
        }
    }
}