From cccd090357b54bc07c2bbcc5076d3926b06d15a9 Mon Sep 17 00:00:00 2001 From: Chaoscaot Date: Mon, 7 Jul 2025 22:43:16 +0200 Subject: [PATCH] Add support for TPS and tick rate management in 1.21+ --- .../bausystem/utils/NativeTickManager21.java | 96 ++++++++++++++++++ .../features/tpslimit/TPSSystem.java | 26 ++++- .../modern/ModernTPSLimitCommand.java | 68 +++++++++++++ .../tpslimit/modern/ModernTickCommand.java | 99 +++++++++++++++++++ .../bausystem/utils/NativeTickManager.java | 36 +++++++ 5 files changed, 320 insertions(+), 5 deletions(-) create mode 100644 BauSystem/BauSystem_21/src/de/steamwar/bausystem/utils/NativeTickManager21.java create mode 100644 BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tpslimit/modern/ModernTPSLimitCommand.java create mode 100644 BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tpslimit/modern/ModernTickCommand.java create mode 100644 BauSystem/BauSystem_Main/src/de/steamwar/bausystem/utils/NativeTickManager.java diff --git a/BauSystem/BauSystem_21/src/de/steamwar/bausystem/utils/NativeTickManager21.java b/BauSystem/BauSystem_21/src/de/steamwar/bausystem/utils/NativeTickManager21.java new file mode 100644 index 00000000..80d9ce17 --- /dev/null +++ b/BauSystem/BauSystem_21/src/de/steamwar/bausystem/utils/NativeTickManager21.java @@ -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 . + */ + +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(); + } +} diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tpslimit/TPSSystem.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tpslimit/TPSSystem.java index ac393e57..46dc7257 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tpslimit/TPSSystem.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tpslimit/TPSSystem.java @@ -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 { diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tpslimit/modern/ModernTPSLimitCommand.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tpslimit/modern/ModernTPSLimitCommand.java new file mode 100644 index 00000000..4d2295c0 --- /dev/null +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tpslimit/modern/ModernTPSLimitCommand.java @@ -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 . + */ + +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(); + } +} diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tpslimit/modern/ModernTickCommand.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tpslimit/modern/ModernTickCommand.java new file mode 100644 index 00000000..057658b5 --- /dev/null +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tpslimit/modern/ModernTickCommand.java @@ -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 . + */ + +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); + } +} diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/utils/NativeTickManager.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/utils/NativeTickManager.java new file mode 100644 index 00000000..7dadf98f --- /dev/null +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/utils/NativeTickManager.java @@ -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 . + */ + +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); +}