Add support for TPS and tick rate management in 1.21+

This commit is contained in:
2025-07-07 22:43:16 +02:00
parent 3ae9a41b31
commit cccd090357
5 changed files with 320 additions and 5 deletions
@@ -0,0 +1,96 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.bausystem.utils;
import com.comphenix.tinyprotocol.TinyProtocol;
import net.minecraft.network.protocol.game.ClientboundTickingStatePacket;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.ServerTickRateManager;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
public class NativeTickManager21 implements NativeTickManager {
private static final ServerTickRateManager manager = MinecraftServer.getServer().tickRateManager();
private boolean blockTpsPacket = true;
public NativeTickManager21() {
TinyProtocol.instance.addFilter(ClientboundTickingStatePacket.class, this::blockPacket);
}
private Object blockPacket(Player player, Object packet) {
if (blockTpsPacket) {
return new ClientboundTickingStatePacket(20, manager.isFrozen());
} else {
return packet;
}
}
@Override
public void blockTpsPacket(boolean block) {
blockTpsPacket = block;
if (blockTpsPacket) {
ClientboundTickingStatePacket packet = new ClientboundTickingStatePacket(20, manager.isFrozen());
Bukkit.getOnlinePlayers().forEach(player -> TinyProtocol.instance.sendPacket(player, packet));
} else {
ClientboundTickingStatePacket packet = new ClientboundTickingStatePacket(manager.tickrate(), manager.isFrozen());
Bukkit.getOnlinePlayers().forEach(player -> TinyProtocol.instance.sendPacket(player, packet));
}
}
@Override
public void setTickRate(float tickRate) {
if (getFreezeState()) {
setFreeze(false);
}
manager.setTickRate(tickRate);
}
@Override
public boolean getFreezeState() {
return manager.isFrozen();
}
@Override
public void setFreeze(boolean freeze) {
manager.setFrozen(freeze);
manager.tick();
}
@Override
public void stepTick(int ticks) {
manager.stepGameIfPaused(ticks);
}
@Override
public void sprintTicks(int ticks) {
manager.requestGameToSprint(ticks, true);
}
@Override
public boolean isSprinting() {
return manager.isSprinting();
}
@Override
public float tickrate() {
return manager.tickrate();
}
}
@@ -25,6 +25,7 @@ import de.steamwar.bausystem.SWUtils;
import de.steamwar.bausystem.linkage.specific.BauGuiItem;
import de.steamwar.bausystem.region.GlobalRegion;
import de.steamwar.bausystem.region.Region;
import de.steamwar.bausystem.utils.NativeTickManager;
import de.steamwar.bausystem.utils.ScoreboardElement;
import de.steamwar.bausystem.utils.TickEndEvent;
import de.steamwar.bausystem.utils.bossbar.BauSystemBossbar;
@@ -40,6 +41,7 @@ import de.steamwar.linkage.Linked;
import de.steamwar.linkage.LinkedInstance;
import de.steamwar.linkage.MaxVersion;
import lombok.Getter;
import lombok.Setter;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.boss.BarColor;
@@ -52,10 +54,9 @@ import org.bukkit.inventory.ItemStack;
import java.util.Arrays;
@Linked
@MaxVersion(20) // Hotfix for 1.21 tps limit! -> Backport coming later
@MaxVersion(20)
public class TPSSystem implements Listener {
@Getter
private static double currentTPSLimit = 20;
public TPSSystem() {
@@ -66,7 +67,7 @@ public class TPSSystem implements Listener {
}
new TPSLimitCommand();
new TickLimitCommand();
if (Core.getVersion() >= 15 && Core.getVersion() <= 20) { // If 1.21 support is not directly present
if (Core.getVersion() >= 15 && Core.getVersion() <= 20) {
new TPSWarpCommand();
new TickWarpCommand();
if (TPSFreezeUtils.isCanFreeze()) {
@@ -320,7 +321,14 @@ public class TPSSystem implements Listener {
@Override
public String get(Region region, Player p) {
if (tpsSystem != null && tpsSystem.currentlyStepping) {
boolean isWarping = tpsSystem.currentlyStepping;
boolean isFrozen = TPSFreezeUtils.frozen();
if (Core.getVersion() >= 21) {
isWarping = NativeTickManager.impl.isSprinting();
isFrozen = NativeTickManager.impl.getFreezeState();
}
if (tpsSystem != null && isWarping) {
long time = System.currentTimeMillis() % 1000;
if (time < 250) {
return "§e" + BauSystem.MESSAGE.parse("SCOREBOARD_TPS", p) + "§8: §7•••";
@@ -331,7 +339,7 @@ public class TPSSystem implements Listener {
} else {
return "§e" + BauSystem.MESSAGE.parse("SCOREBOARD_TPS", p) + "§8: §7••§e•";
}
} else if (TPSFreezeUtils.frozen()) {
} else if (isFrozen) {
return "§e" + BauSystem.MESSAGE.parse("SCOREBOARD_TPS", p) + "§8: " + BauSystem.MESSAGE.parse("SCOREBOARD_TPS_FROZEN", p);
} else {
return "§e" + BauSystem.MESSAGE.parse("SCOREBOARD_TPS", p) + "§8: " + tpsColor() + TPSWatcher.getTPSUnlimited(TPSWatcher.TPSType.ONE_SECOND) + tpsLimit();
@@ -357,6 +365,14 @@ public class TPSSystem implements Listener {
}
}
public static double getCurrentTPSLimit() {
if (Core.getVersion() >= 21) {
return NativeTickManager.impl.tickrate();
} else {
return currentTPSLimit;
}
}
@Linked
public static class TPSSystemBauGuiItem extends BauGuiItem {
@@ -0,0 +1,68 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.bausystem.features.tpslimit.modern;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.utils.NativeTickManager;
import de.steamwar.command.SWCommand;
import de.steamwar.linkage.Linked;
import de.steamwar.linkage.MinVersion;
import org.bukkit.entity.Player;
import static de.steamwar.bausystem.features.tpslimit.modern.ModernTickCommand.sendTickRateChange;
@Linked
@MinVersion(21)
public class ModernTPSLimitCommand extends SWCommand {
public ModernTPSLimitCommand() {
super("tpslimit");
setMessage(BauSystem.MESSAGE);
addDefaultHelpMessage("TPSLIMIT_HELP");
}
@Register(value = "0", description = "TPSLIMIT_FREEZE_HELP")
public void freeze(@Validator Player player) {
NativeTickManager.impl.setFreeze(true);
sendTickRateChange();
}
@Register(description = "TPSLIMIT_LIMIT_HELP")
public void limit(@Validator Player player, @Min(doubleValue = 0.5) @Max(doubleValue = 20.0) float tpsLimit) {
NativeTickManager.impl.setTickRate(tpsLimit);
sendTickRateChange();
}
@Register(description = "TPSLIMIT_WARP_HELP")
public void warp(@Validator Player player, @Min(doubleValue = 20.0, inclusive = false) float tpsLimit) {
NativeTickManager.impl.setTickRate(tpsLimit);
sendTickRateChange();
}
@Register(description = "TPSLIMIT_HELP")
public void currentLimit(Player player) {
BauSystem.MESSAGE.send("TPSLIMIT_CURRENT", player, NativeTickManager.impl.tickrate());
}
@Register(value = "default", description = "TPSLIMIT_DEFAULT_HELP")
public void reset(@Validator Player player) {
NativeTickManager.impl.setTickRate(20);
sendTickRateChange();
}
}
@@ -0,0 +1,99 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.bausystem.features.tpslimit.modern;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.SWUtils;
import de.steamwar.bausystem.utils.NativeTickManager;
import de.steamwar.command.SWCommand;
import de.steamwar.linkage.Linked;
import de.steamwar.linkage.MinVersion;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
@Linked
@MinVersion(21)
public class ModernTickCommand extends SWCommand implements Listener {
public ModernTickCommand() {
super("tick");
setMessage(BauSystem.MESSAGE);
}
public static void sendTickRateChange() {
Bukkit.getOnlinePlayers().forEach(player -> {
if (NativeTickManager.impl.getFreezeState()) {
SWUtils.sendToActionbar(player, BauSystem.MESSAGE.parse("TPSLIMIT_FROZEN", player));
} else {
SWUtils.sendToActionbar(player, BauSystem.MESSAGE.parse("TPSLIMIT_SET", player, NativeTickManager.impl.tickrate()));
}
});
}
@Register(value = {"rate", "0"}, description = "TICK_FREEZE_HELP")
@Register(value = "freeze", description = "TICK_FREEZE_HELP_2")
public void freeze(@Validator Player player) {
NativeTickManager.impl.setFreeze(true);
sendTickRateChange();
}
@Register(value = "unfreeze", description = "TICK_UNFREEZE_HELP")
public void unfreeze(@Validator Player player) {
NativeTickManager.impl.setFreeze(false);
sendTickRateChange();
}
@Register(value = "step", description = "TICK_STEPPING_HELP")
public void step(@Validator Player player, @Min(intValue = 1) @OptionalValue("1") int steps) {
NativeTickManager.impl.stepTick(steps);
}
@Register(value = "warp", description = "TICK_WARP_HELP")
public void warp(@Validator Player player, @Min(intValue = 1) @OptionalValue("1") int steps) {
NativeTickManager.impl.sprintTicks(steps);
}
@Register(value = "rate", description = "TICK_LIMIT_HELP")
public void limit(@Validator Player player, @Min(doubleValue = 0.5, inclusive = false) float tpsLimit) {
NativeTickManager.impl.setTickRate(tpsLimit);
sendTickRateChange();
}
@Register(value = "rate", description = "TICK_HELP")
public void currentLimit(Player player) {
BauSystem.MESSAGE.send("TPSLIMIT_CURRENT", player, NativeTickManager.impl.tickrate());
}
@Register(value = {"rate", "default"}, description = "TICK_DEFAULT_HELP")
public void reset(@Validator Player player) {
NativeTickManager.impl.setTickRate(20);
sendTickRateChange();
}
@Register(value = "normalclient")
public void smooth(@Validator Player player) {
NativeTickManager.impl.blockTpsPacket(true);
}
@Register(value = "slowclient")
public void unsmooth(@Validator Player player) {
NativeTickManager.impl.blockTpsPacket(false);
}
}
@@ -0,0 +1,36 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.bausystem.utils;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.core.VersionDependent;
public interface NativeTickManager {
NativeTickManager impl = VersionDependent.getVersionImpl(BauSystem.getInstance());
void setTickRate(float tickRate);
boolean getFreezeState();
void setFreeze(boolean freeze);
void stepTick(int ticks);
void sprintTicks(int ticks);
boolean isSprinting();
float tickrate();
void blockTpsPacket(boolean block);
}