Calculate tps

This commit is contained in:
IzzelAliz 2020-05-17 09:47:06 +08:00
parent 37cdbc1107
commit a58b89b652
2 changed files with 150 additions and 4 deletions

View File

@ -9,14 +9,22 @@ import com.mojang.datafixers.DataFixer;
import io.izzel.arclight.bridge.command.ICommandSourceBridge;
import io.izzel.arclight.bridge.server.MinecraftServerBridge;
import io.izzel.arclight.bridge.world.WorldBridge;
import io.izzel.arclight.mod.ArclightConstants;
import io.izzel.arclight.mod.server.BukkitRegistry;
import io.izzel.arclight.mod.util.BukkitOptionParser;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import net.minecraft.command.CommandSource;
import net.minecraft.command.Commands;
import net.minecraft.crash.CrashReport;
import net.minecraft.crash.ReportedException;
import net.minecraft.network.ServerStatusResponse;
import net.minecraft.profiler.DebugProfiler;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.management.PlayerProfileCache;
import net.minecraft.util.SharedConstants;
import net.minecraft.util.Util;
import net.minecraft.util.text.StringTextComponent;
import net.minecraft.world.WorldSettings;
import net.minecraft.world.WorldType;
import net.minecraft.world.chunk.listener.IChunkStatusListener;
@ -24,6 +32,9 @@ import net.minecraft.world.chunk.listener.IChunkStatusListenerFactory;
import net.minecraft.world.server.ServerWorld;
import net.minecraft.world.storage.SaveHandler;
import net.minecraft.world.storage.WorldInfo;
import net.minecraftforge.fml.StartupQuery;
import net.minecraftforge.fml.server.ServerLifecycleHooks;
import org.apache.logging.log4j.Logger;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
@ -33,27 +44,53 @@ import org.bukkit.craftbukkit.v1_14_R1.scoreboard.CraftScoreboardManager;
import org.bukkit.event.server.ServerLoadEvent;
import org.bukkit.event.world.WorldInitEvent;
import org.bukkit.plugin.PluginLoadOrder;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
import io.izzel.arclight.mod.ArclightConstants;
import io.izzel.arclight.mod.util.BukkitOptionParser;
import javax.annotation.Nullable;
import java.io.File;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.net.Proxy;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.function.BooleanSupplier;
@Mixin(MinecraftServer.class)
public class MinecraftServerMixin implements MinecraftServerBridge, ICommandSourceBridge {
public abstract class MinecraftServerMixin implements MinecraftServerBridge, ICommandSourceBridge {
// @formatter:off
@Shadow private int tickCounter;
@Shadow protected abstract boolean init() throws IOException;
@Shadow protected long serverTime;
@Shadow @Final private ServerStatusResponse statusResponse;
@Shadow @Nullable private String motd;
@Shadow public abstract void applyServerIconToResponse(ServerStatusResponse response);
@Shadow private volatile boolean serverRunning;
@Shadow private long timeOfLastWarning;
@Shadow @Final private static Logger LOGGER;
@Shadow private boolean startProfiling;
@Shadow @Final private DebugProfiler profiler;
@Shadow protected abstract void tick(BooleanSupplier hasTimeLeft);
@Shadow protected abstract boolean isAheadOfTime();
@Shadow private boolean isRunningScheduledTasks;
@Shadow private long runTasksUntil;
@Shadow protected abstract void runScheduledTasks();
@Shadow private volatile boolean serverIsRunning;
@Shadow protected abstract void finalTick(CrashReport report);
@Shadow public abstract CrashReport addServerInfoToCrashReport(CrashReport report);
@Shadow public abstract File getDataDirectory();
@Shadow private boolean serverStopped;
@Shadow protected abstract void stopServer();
@Shadow protected abstract void systemExitNow();
// @formatter:on
public CraftServer server;
@ -66,6 +103,12 @@ public class MinecraftServerMixin implements MinecraftServerBridge, ICommandSour
private boolean hasStopped = false;
private final Object stopLock = new Object();
private static final int TPS = 20;
private static final int TICK_TIME = 1000000000 / TPS;
private static final int SAMPLE_INTERVAL = 100;
private static int currentTick = (int) (System.currentTimeMillis() / 50);
public final double[] recentTps = new double[3];
public boolean hasStopped() {
synchronized (stopLock) {
return hasStopped;
@ -83,6 +126,107 @@ public class MinecraftServerMixin implements MinecraftServerBridge, ICommandSour
}
}
/**
* @author IzzelAliz
* @reason
*/
@Overwrite
public void run() {
try {
if (this.init()) {
ServerLifecycleHooks.handleServerStarted((MinecraftServer) (Object) this);
this.serverTime = Util.milliTime();
this.statusResponse.setServerDescription(new StringTextComponent(this.motd));
this.statusResponse.setVersion(new ServerStatusResponse.Version(SharedConstants.getVersion().getName(), SharedConstants.getVersion().getProtocolVersion()));
this.applyServerIconToResponse(this.statusResponse);
Arrays.fill(recentTps, 0);
long curTime, tickSection = Util.milliTime(), tickCount = 1;
while (this.serverRunning) {
long i = (curTime = Util.milliTime()) - this.serverTime;
if (i > 5000L && this.serverTime - this.timeOfLastWarning >= 30000L) {
long j = i / 50L;
if (server.getWarnOnOverload()) {
LOGGER.warn("Can't keep up! Is the server overloaded? Running {}ms or {} ticks behind", i, j);
}
this.serverTime += j * 50L;
this.timeOfLastWarning = this.serverTime;
}
if (tickCount++ % SAMPLE_INTERVAL == 0) {
double currentTps = 1E3 / (curTime - tickSection) * SAMPLE_INTERVAL;
recentTps[0] = calcTps(recentTps[0], 0.92, currentTps); // 1/exp(5sec/1min)
recentTps[1] = calcTps(recentTps[1], 0.9835, currentTps); // 1/exp(5sec/5min)
recentTps[2] = calcTps(recentTps[2], 0.9945, currentTps); // 1/exp(5sec/15min)
tickSection = curTime;
}
currentTick = (int) (System.currentTimeMillis() / 50);
this.serverTime += 50L;
if (this.startProfiling) {
this.startProfiling = false;
this.profiler.func_219899_d().func_219939_d();
}
this.profiler.startTick();
this.profiler.startSection("tick");
this.tick(this::isAheadOfTime);
this.profiler.endStartSection("nextTickWait");
this.isRunningScheduledTasks = true;
this.runTasksUntil = Math.max(Util.milliTime() + 50L, this.serverTime);
this.runScheduledTasks();
this.profiler.endSection();
this.profiler.endTick();
this.serverIsRunning = true;
}
ServerLifecycleHooks.handleServerStopping((MinecraftServer) (Object) this);
ServerLifecycleHooks.expectServerStopped(); // has to come before finalTick to avoid race conditions
} else {
ServerLifecycleHooks.expectServerStopped(); // has to come before finalTick to avoid race conditions
this.finalTick((CrashReport) null);
}
} catch (StartupQuery.AbortedException e) {
// ignore silently
ServerLifecycleHooks.expectServerStopped(); // has to come before finalTick to avoid race conditions
} catch (Throwable throwable1) {
LOGGER.error("Encountered an unexpected exception", throwable1);
CrashReport crashreport;
if (throwable1 instanceof ReportedException) {
crashreport = this.addServerInfoToCrashReport(((ReportedException) throwable1).getCrashReport());
} else {
crashreport = this.addServerInfoToCrashReport(new CrashReport("Exception in server tick loop", throwable1));
}
File file1 = new File(new File(this.getDataDirectory(), "crash-reports"), "crash-" + (new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss")).format(new Date()) + "-server.txt");
if (crashreport.saveToFile(file1)) {
LOGGER.error("This crash report has been saved to: {}", (Object) file1.getAbsolutePath());
} else {
LOGGER.error("We were unable to save this crash report to disk.");
}
ServerLifecycleHooks.expectServerStopped(); // has to come before finalTick to avoid race conditions
this.finalTick(crashreport);
} finally {
try {
this.serverStopped = true;
this.stopServer();
} catch (Throwable throwable) {
LOGGER.error("Exception stopping the server", throwable);
} finally {
ServerLifecycleHooks.handleServerStopped((MinecraftServer) (Object) this);
this.systemExitNow();
}
}
}
private static double calcTps(double avg, double exp, double tps) {
return (avg * exp) + (tps * (1 - exp));
}
@Inject(method = "stopServer", cancellable = true, at = @At("HEAD"))
public void arclight$setStopped(CallbackInfo ci) {
synchronized (stopLock) {

View File

@ -29,7 +29,9 @@ public class ArclightMixinPlugin implements IMixinConfigPlugin {
ImmutableMap.<String, Map.Entry<List<FieldNode>, List<MethodNode>>>builder()
.put("net.minecraft.server.MinecraftServer",
Maps.immutableEntry(
ImmutableList.of(),
ImmutableList.of(
new FieldNode(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "currentTick", "I", null, null)
),
ImmutableList.of(
new MethodNode(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "getServer", "()Lnet/minecraft/server/MinecraftServer;", null, null)
)