|
1
|
|
|
package de.kleiner3.lasertag.mixin; |
|
2
|
|
|
|
|
3
|
|
|
import com.google.common.collect.Streams; |
|
4
|
|
|
import com.mojang.logging.LogUtils; |
|
5
|
|
|
import de.kleiner3.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
|
|
|
while (((DedicatedServerWatchdog) (Object) this).server.isRunning()) { |
|
39
|
|
|
|
|
40
|
|
|
// If map is loading, ignore |
|
41
|
|
|
if (((DedicatedServerWatchdog) (Object) this).server.getLasertagServerManager().getMapManager().isLoading()) { |
|
42
|
|
|
this.isIgnoring = true; |
|
43
|
|
|
} |
|
44
|
|
|
|
|
45
|
|
|
long lastServerTimeReference = ((DedicatedServerWatchdog) (Object) this).server.getTimeReference(); |
|
46
|
|
|
long now = Util.getMeasuringTimeMs(); |
|
47
|
|
|
|
|
48
|
|
|
|
|
49
|
|
|
long difference = now - lastServerTimeReference; |
|
50
|
|
|
if (difference > ((DedicatedServerWatchdog) (Object) this).maxTickTime) { |
|
51
|
|
|
if (!this.isIgnoring) { |
|
52
|
|
|
|
|
53
|
|
|
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)); |
|
54
|
|
|
DedicatedServerWatchdog.LOGGER.error(LogUtils.FATAL_MARKER, "Considering it to be crashed, server will forcibly shutdown."); |
|
55
|
|
|
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); |
|
56
|
|
|
ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(true, true); |
|
57
|
|
|
StringBuilder stringBuilder = new StringBuilder(); |
|
58
|
|
|
Error error = new Error("Watchdog"); |
|
59
|
|
|
ThreadInfo[] var11 = threadInfos; |
|
60
|
|
|
int var12 = threadInfos.length; |
|
61
|
|
|
|
|
62
|
|
|
for (int var13 = 0; var13 < var12; ++var13) { |
|
63
|
|
|
ThreadInfo threadInfo = var11[var13]; |
|
64
|
|
|
if (threadInfo.getThreadId() == ((DedicatedServerWatchdog) (Object) this).server.getThread().getId()) { |
|
65
|
|
|
error.setStackTrace(threadInfo.getStackTrace()); |
|
66
|
|
|
} |
|
67
|
|
|
|
|
68
|
|
|
stringBuilder.append(threadInfo); |
|
69
|
|
|
stringBuilder.append("\n"); |
|
70
|
|
|
} |
|
71
|
|
|
|
|
72
|
|
|
CrashReport crashReport = new CrashReport("Watching Server", error); |
|
73
|
|
|
((DedicatedServerWatchdog) (Object) this).server.addSystemDetails(crashReport.getSystemDetailsSection()); |
|
74
|
|
|
CrashReportSection crashReportSection = crashReport.addElement("Thread Dump"); |
|
75
|
|
|
crashReportSection.add("Threads", stringBuilder); |
|
76
|
|
|
CrashReportSection crashReportSection2 = crashReport.addElement("Performance stats"); |
|
77
|
|
|
crashReportSection2.add("Random tick rate", () -> ((DedicatedServerWatchdog) (Object) this).server.getSaveProperties().getGameRules().get(GameRules.RANDOM_TICK_SPEED).toString()); |
|
78
|
|
|
crashReportSection2.add("Level stats", () -> Streams.stream(((DedicatedServerWatchdog) (Object) this).server.getWorlds()).map((serverWorld) -> { |
|
79
|
|
|
RegistryKey var10000 = serverWorld.getRegistryKey(); |
|
80
|
|
|
return "" + var10000 + ": " + serverWorld.getDebugString(); |
|
81
|
|
|
}).collect(Collectors.joining(",\n"))); |
|
82
|
|
|
Bootstrap.println("Crash report:\n" + crashReport.asString()); |
|
83
|
|
|
File file = new File(new File(((DedicatedServerWatchdog) (Object) this).server.getRunDirectory(), "crash-reports"), "crash-" + Util.getFormattedCurrentTime() + "-server.txt"); |
|
84
|
|
|
if (crashReport.writeToFile(file)) { |
|
85
|
|
|
DedicatedServerWatchdog.LOGGER.error("This crash report has been saved to: {}", file.getAbsolutePath()); |
|
86
|
|
|
} else { |
|
87
|
|
|
DedicatedServerWatchdog.LOGGER.error("We were unable to save this crash report to disk."); |
|
88
|
|
|
} |
|
89
|
|
|
|
|
90
|
|
|
((DedicatedServerWatchdog) (Object) this).shutdown(); |
|
91
|
|
|
} else { |
|
92
|
|
|
// Only ignore the first time |
|
93
|
|
|
this.isIgnoring = false; |
|
94
|
|
|
LasertagMod.LOGGER.info("Watchdog now not ignoring anymore."); |
|
95
|
|
|
} |
|
96
|
|
|
} |
|
97
|
|
|
|
|
98
|
|
|
try { |
|
99
|
|
|
Thread.sleep(((DedicatedServerWatchdog) (Object) this).maxTickTime); |
|
100
|
|
|
} catch (InterruptedException ignored) { |
|
101
|
|
|
} |
|
102
|
|
|
} |
|
103
|
|
|
|
|
104
|
|
|
ci.cancel(); |
|
105
|
|
|
} |
|
106
|
|
|
} |
|
107
|
|
|
|