/* * 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 de.steamwar.Reflection; import de.steamwar.bausystem.BauSystem; 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; import org.bukkit.event.Listener; public class TickManager implements Listener { public static final TickManager impl = new TickManager(); private static final ServerTickRateManager manager = MinecraftServer.getServer().tickRateManager(); private static final Reflection.Field remainingSprintTicks = Reflection.getField(ServerTickRateManager.class, long.class, 0); private boolean blockTpsPacket = true; private int totalSteps; private TickManager() { 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; } } public boolean canFreeze() { return true; } public void setBlockTpsPacket(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)); } } public void setTickRate(float tickRate) { if (isFrozen()) { setFreeze(false); } manager.setTickRate(tickRate); } public boolean isFrozen() { return manager.isFrozen(); } public void setFreeze(boolean freeze) { manager.setFrozen(freeze); } public void stepTicks(int ticks) { if (manager.isSprinting()) { manager.stopSprinting(); } else if (manager.isSteppingForward()) { manager.stopStepping(); } this.totalSteps = ticks; manager.setFrozen(true); manager.stepGameIfPaused(ticks); manager.setFrozen(false); Bukkit.getScheduler().runTaskTimer(BauSystem.getInstance(), (bukkitTask) -> { if (manager.isSteppingForward()) return; manager.setFrozen(true); bukkitTask.cancel(); }, 1, 1); } public void sprintTicks(int ticks) { if (manager.isSteppingForward()) { manager.stopStepping(); } else if (manager.isSprinting()) { manager.stopSprinting(); } this.totalSteps = ticks; manager.requestGameToSprint(ticks, true); } public boolean isSprinting() { return manager.isSprinting(); } public boolean isStepping() { return manager.isSteppingForward(); } public float getTickRate() { return manager.tickrate(); } public long getRemainingTicks() { if (isSprinting()) { return remainingSprintTicks.get(manager); } else { return manager.frozenTicksToRun(); } } public long getDoneTicks() { return totalSteps - getRemainingTicks(); } public long getTotalTicks() { return totalSteps; } }