1
|
|
|
package de.pewpewproject.lasertag.mixin; |
2
|
|
|
|
3
|
|
|
import com.google.common.collect.Streams; |
4
|
|
|
import com.mojang.logging.LogUtils; |
5
|
|
|
import de.pewpewproject.lasertag.LasertagMod; |
6
|
|
|
import net.minecraft.Bootstrap; |
7
|
|
|
import net.minecraft.server.dedicated.DedicatedServerWatchdog; |
8
|
|
|
import net.minecraft.util.Util; |
9
|
|
|
import net.minecraft.util.crash.CrashReport; |
10
|
|
|
import net.minecraft.util.crash.CrashReportSection; |
11
|
|
|
import net.minecraft.util.registry.RegistryKey; |
12
|
|
|
import net.minecraft.world.GameRules; |
13
|
|
|
import org.spongepowered.asm.mixin.Mixin; |
14
|
|
|
import org.spongepowered.asm.mixin.injection.At; |
15
|
|
|
import org.spongepowered.asm.mixin.injection.Inject; |
16
|
|
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; |
17
|
|
|
|
18
|
|
|
import java.io.File; |
19
|
|
|
import java.lang.management.ManagementFactory; |
20
|
|
|
import java.lang.management.ThreadInfo; |
21
|
|
|
import java.lang.management.ThreadMXBean; |
22
|
|
|
import java.util.Locale; |
23
|
|
|
import java.util.stream.Collectors; |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* Mixin into the DedicatedServerWatchdog.class to disable the watchdog during map load |
27
|
|
|
* |
28
|
|
|
* @author Étienne Muser |
29
|
|
|
*/ |
30
|
|
|
@Mixin(DedicatedServerWatchdog.class) |
31
|
|
|
public abstract class DedicatedServerWatchdogMixin { |
32
|
|
|
|
33
|
|
|
private boolean isIgnoring = false; |
34
|
|
|
|
35
|
|
|
@Inject(method = "run()V", at = @At("HEAD"), cancellable = true) |
36
|
|
|
private void runOverride(CallbackInfo ci) { |
37
|
|
|
|
38
|
|
|
var thisWD = ((DedicatedServerWatchdog) (Object) this); |
39
|
|
|
|
40
|
|
|
while (thisWD.server.isRunning()) { |
41
|
|
|
|
42
|
|
|
// Get the game managers |
43
|
|
|
var gameManager = thisWD.server.getOverworld().getServerLasertagManager(); |
44
|
|
|
var arenaManager = gameManager.getArenaManager(); |
45
|
|
|
|
46
|
|
|
// If map is loading, ignore |
47
|
|
|
if (arenaManager.isLoading()) { |
48
|
|
|
this.isIgnoring = true; |
49
|
|
|
} |
50
|
|
|
|
51
|
|
|
long lastServerTimeReference = ((DedicatedServerWatchdog) (Object) this).server.getTimeReference(); |
52
|
|
|
long now = Util.getMeasuringTimeMs(); |
53
|
|
|
|
54
|
|
|
|
55
|
|
|
long difference = now - lastServerTimeReference; |
56
|
|
|
if (difference > ((DedicatedServerWatchdog) (Object) this).maxTickTime) { |
57
|
|
|
if (!this.isIgnoring) { |
58
|
|
|
|
59
|
|
|
DedicatedServerWatchdog.LOGGER.error(LogUtils.FATAL_MARKER, "A single server tick took {} seconds (should be max {})", String.format(Locale.ROOT, "%.2f", (float) difference / 1000.0F), String.format(Locale.ROOT, "%.2f", 0.05F)); |
60
|
|
|
DedicatedServerWatchdog.LOGGER.error(LogUtils.FATAL_MARKER, "Considering it to be crashed, server will forcibly shutdown."); |
61
|
|
|
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); |
62
|
|
|
ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(true, true); |
63
|
|
|
StringBuilder stringBuilder = new StringBuilder(); |
64
|
|
|
Error error = new Error("Watchdog"); |
65
|
|
|
ThreadInfo[] var11 = threadInfos; |
66
|
|
|
int var12 = threadInfos.length; |
67
|
|
|
|
68
|
|
|
for (int var13 = 0; var13 < var12; ++var13) { |
69
|
|
|
ThreadInfo threadInfo = var11[var13]; |
70
|
|
|
if (threadInfo.getThreadId() == ((DedicatedServerWatchdog) (Object) this).server.getThread().getId()) { |
71
|
|
|
error.setStackTrace(threadInfo.getStackTrace()); |
72
|
|
|
} |
73
|
|
|
|
74
|
|
|
stringBuilder.append(threadInfo); |
75
|
|
|
stringBuilder.append("\n"); |
76
|
|
|
} |
77
|
|
|
|
78
|
|
|
CrashReport crashReport = new CrashReport("Watching Server", error); |
79
|
|
|
((DedicatedServerWatchdog) (Object) this).server.addSystemDetails(crashReport.getSystemDetailsSection()); |
80
|
|
|
CrashReportSection crashReportSection = crashReport.addElement("Thread Dump"); |
81
|
|
|
crashReportSection.add("Threads", stringBuilder); |
82
|
|
|
CrashReportSection crashReportSection2 = crashReport.addElement("Performance stats"); |
83
|
|
|
crashReportSection2.add("Random tick rate", () -> ((DedicatedServerWatchdog) (Object) this).server.getSaveProperties().getGameRules().get(GameRules.RANDOM_TICK_SPEED).toString()); |
84
|
|
|
crashReportSection2.add("Level stats", () -> Streams.stream(((DedicatedServerWatchdog) (Object) this).server.getWorlds()).map((serverWorld) -> { |
85
|
|
|
RegistryKey var10000 = serverWorld.getRegistryKey(); |
86
|
|
|
return "" + var10000 + ": " + serverWorld.getDebugString(); |
87
|
|
|
}).collect(Collectors.joining(",\n"))); |
88
|
|
|
Bootstrap.println("Crash report:\n" + crashReport.asString()); |
89
|
|
|
File file = new File(new File(((DedicatedServerWatchdog) (Object) this).server.getRunDirectory(), "crash-reports"), "crash-" + Util.getFormattedCurrentTime() + "-server.txt"); |
90
|
|
|
if (crashReport.writeToFile(file)) { |
91
|
|
|
DedicatedServerWatchdog.LOGGER.error("This crash report has been saved to: {}", file.getAbsolutePath()); |
92
|
|
|
} else { |
93
|
|
|
DedicatedServerWatchdog.LOGGER.error("We were unable to save this crash report to disk."); |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
((DedicatedServerWatchdog) (Object) this).shutdown(); |
97
|
|
|
} else { |
98
|
|
|
// Only ignore the first time |
99
|
|
|
this.isIgnoring = false; |
100
|
|
|
LasertagMod.LOGGER.info("Watchdog now not ignoring anymore."); |
101
|
|
|
} |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
try { |
105
|
|
|
Thread.sleep(((DedicatedServerWatchdog) (Object) this).maxTickTime); |
106
|
|
|
} catch (InterruptedException ignored) { |
107
|
|
|
} |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
ci.cancel(); |
111
|
|
|
} |
112
|
|
|
} |
113
|
|
|
|