diff --git a/BauSystem_15/src/de/steamwar/bausystem/features/tpslimit/TPSLimit_15.java b/BauSystem_15/src/de/steamwar/bausystem/features/tpslimit/TPSLimit_15.java new file mode 100644 index 00000000..db72b3f7 --- /dev/null +++ b/BauSystem_15/src/de/steamwar/bausystem/features/tpslimit/TPSLimit_15.java @@ -0,0 +1,61 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2021 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; + +import lombok.experimental.UtilityClass; +import net.minecraft.server.v1_15_R1.*; +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity; +import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPlayer; +import org.bukkit.entity.Player; +import org.bukkit.entity.TNTPrimed; + +import java.util.ArrayList; +import java.util.List; + +@UtilityClass +public class TPSLimit_15 { + + private static List> packets = new ArrayList<>(); + private static final Vec3D noMotion = new Vec3D(0, 0, 0); + + static void createTickCache(World world) { + packets.clear(); + world.getEntities().stream().filter(entity -> !(entity instanceof Player)).forEach(entity -> { + packets.add(new PacketPlayOutEntityVelocity(entity.getEntityId(), noMotion)); + packets.add(new PacketPlayOutEntityTeleport(((CraftEntity) entity).getHandle())); + + if (entity instanceof TNTPrimed) { + net.minecraft.server.v1_15_R1.Entity serverEntity = ((CraftEntity) entity).getHandle(); + packets.add(new PacketPlayOutEntityMetadata(serverEntity.getId(), serverEntity.getDataWatcher(), true)); + } + }); + } + + static void sendTickPackets() { + Bukkit.getOnlinePlayers().forEach(player -> { + PlayerConnection connection = ((CraftPlayer) player).getHandle().playerConnection; + for (Packet p : packets) { + connection.sendPacket(p); + } + }); + } +} diff --git a/BauSystem_15/src/de/steamwar/bausystem/features/tpslimit/TPSUtils_15.java b/BauSystem_15/src/de/steamwar/bausystem/features/tpslimit/TPSUtils_15.java new file mode 100644 index 00000000..d5a90652 --- /dev/null +++ b/BauSystem_15/src/de/steamwar/bausystem/features/tpslimit/TPSUtils_15.java @@ -0,0 +1,34 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2021 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; + +import lombok.experimental.UtilityClass; +import net.minecraft.server.v1_15_R1.SystemUtils; + +import java.util.function.LongSupplier; + +@UtilityClass +public class TPSUtils_15 { + + public static void init(LongSupplier longSupplier) { + SystemUtils.a = () -> System.nanoTime() + longSupplier.getAsLong(); + } + +} diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/tpslimit/TPSLimitCommand.java b/BauSystem_Main/src/de/steamwar/bausystem/features/tpslimit/TPSLimitCommand.java new file mode 100644 index 00000000..e2baf538 --- /dev/null +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/tpslimit/TPSLimitCommand.java @@ -0,0 +1,113 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2021 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; + +import de.steamwar.bausystem.BauSystem; +import de.steamwar.bausystem.Permission; +import de.steamwar.bausystem.linkage.LinkageType; +import de.steamwar.bausystem.linkage.Linked; +import de.steamwar.command.SWCommand; +import de.steamwar.command.SWCommandUtils; +import de.steamwar.command.TypeMapper; +import lombok.AccessLevel; +import lombok.Getter; +import net.md_5.bungee.api.ChatMessageType; +import net.md_5.bungee.api.chat.TextComponent; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +@Linked(LinkageType.COMMAND) +public class TPSLimitCommand extends SWCommand { + + @Getter(AccessLevel.PACKAGE) + private static TPSLimitCommand instance = null; + + { + instance = this; + } + + private List tabCompletions = new ArrayList<>(Arrays.asList("0,5", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20")); + + public TPSLimitCommand() { + super("tpslimit"); + if (TPSWarpUtils.isWarpAllowed()) { + for (int i = 20; i <= 60; i += 5) { + tabCompletions.add(i + ""); + } + } + } + + @Register(help = true) + public void genericHelp(Player p, String... args) { + p.sendMessage(BauSystem.PREFIX + "Jetziges TPS limit: " + TPSLimitUtils.currentTPSLimit); + p.sendMessage("§8/§etpslimit §8[§7TPS§8|§edefault§8] §8- §7Setzte die TPS auf dem Bau"); + } + + @Register({"default"}) + public void defaultCommand(Player p) { + if (!permissionCheck(p)) return; + TPSLimitUtils.currentTPSLimit = 20; + sendNewTPSLimitMessage(); + TPSLimitUtils.tpsLimiter(); + } + + @Register + public void valueCommand(Player p, double tpsLimitDouble) { + if (!permissionCheck(p)) return; + if (tpsLimitDouble < 0.5 || tpsLimitDouble > (TPSWarpUtils.isWarpAllowed() ? 60 : 20)) { + sendInvalidArgumentMessage(p); + return; + } + TPSLimitUtils.currentTPSLimit = tpsLimitDouble; + sendNewTPSLimitMessage(); + TPSLimitUtils.tpsLimiter(); + } + + @ClassMapper(value = double.class, local = true) + public TypeMapper doubleTypeMapper() { + return SWCommandUtils.createMapper(s -> { + try { + return Double.parseDouble(s.replace(',', '.')); + } catch (NumberFormatException e) { + return 0D; + } + }, s -> tabCompletions); + } + + private boolean permissionCheck(Player player) { + if (!Permission.hasPermission(player, Permission.WORLD)) { + player.sendMessage(BauSystem.PREFIX + "§cDu darfst hier nicht den TPS-Limiter nutzen"); + return false; + } + return true; + } + + private void sendNewTPSLimitMessage() { + Bukkit.getOnlinePlayers().forEach(p -> p.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText("§eTPS limit auf " + TPSLimitUtils.currentTPSLimit + " gesetzt."))); + } + + private void sendInvalidArgumentMessage(Player player) { + player.sendMessage(BauSystem.PREFIX + "§cNur Zahlen zwischen 0,5 und " + (TPSWarpUtils.isWarpAllowed() ? 60 : 20) + ", und 'default' erlaubt."); + } +} diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/tpslimit/TPSLimitUtils.java b/BauSystem_Main/src/de/steamwar/bausystem/features/tpslimit/TPSLimitUtils.java new file mode 100644 index 00000000..f8fd0724 --- /dev/null +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/tpslimit/TPSLimitUtils.java @@ -0,0 +1,93 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2021 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; + +import de.steamwar.bausystem.BauSystem; +import de.steamwar.core.VersionedRunnable; +import lombok.experimental.UtilityClass; +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.scheduler.BukkitTask; + +@UtilityClass +public class TPSLimitUtils { + + private static final World WORLD = Bukkit.getWorlds().get(0); + static double currentTPSLimit = 20; + + private long lastTime = System.nanoTime(); + private long currentTime = System.nanoTime(); + + private double delay = 0; + private int loops = 0; + private long sleepDelay = 0; + + private BukkitTask tpsLimiter = null; + + void tpsLimiter() { + delay = 20 / currentTPSLimit; + loops = (int) Math.ceil(delay); + sleepDelay = (long) (50 * delay) / loops; + + TPSWarpUtils.setTPS(currentTPSLimit); + if (currentTPSLimit >= 20) { + if (tpsLimiter == null) return; + tpsLimiter.cancel(); + tpsLimiter = null; + } else { + if (tpsLimiter != null) return; + tpsLimiter = Bukkit.getScheduler().runTaskTimer(BauSystem.getInstance(), () -> { + VersionedRunnable.call(new VersionedRunnable(() -> TPSLimit_15.createTickCache(WORLD), 15)); + + for (int i = 0; i < loops; i++) { + sleepUntilNextTick(sleepDelay); + VersionedRunnable.call(new VersionedRunnable(TPSLimit_15::sendTickPackets, 15)); + } + }, 0, 1); + } + } + + private void sleepUntilNextTick(long neededDelta) { + lastTime = currentTime; + currentTime = System.nanoTime(); + + long timeDelta = (currentTime - lastTime) / 1000000; + if (neededDelta - timeDelta < 0) return; + + try { + Thread.sleep(neededDelta - timeDelta); + currentTime = System.nanoTime(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + public static double getCurrentTPSLimit() { + return (double) Math.round(currentTPSLimit * 10.0D) / 10.0D; + } + + public static void setTPS(double d) { + if (d < 0.5) d = 0.5; + if (d > (TPSWarpUtils.isWarpAllowed() ? 60 : 20)) d = (TPSWarpUtils.isWarpAllowed() ? 60 : 20); + currentTPSLimit = d; + tpsLimiter(); + } + +} diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/tpslimit/TPSUtils.java b/BauSystem_Main/src/de/steamwar/bausystem/features/tpslimit/TPSUtils.java new file mode 100644 index 00000000..30942bee --- /dev/null +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/tpslimit/TPSUtils.java @@ -0,0 +1,40 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2021 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; + +import de.steamwar.bausystem.BauSystem; +import lombok.experimental.UtilityClass; +import org.bukkit.Bukkit; + +import java.util.function.Supplier; + +@UtilityClass +public class TPSUtils { + + private static long ticksSinceServerStart = 0; + public static final Supplier currentTick = () -> ticksSinceServerStart; + + static { + Bukkit.getScheduler().runTaskTimer(BauSystem.getInstance(), () -> { + ticksSinceServerStart++; + }, 1, 1); + } + +} diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/tpslimit/TPSWarpUtils.java b/BauSystem_Main/src/de/steamwar/bausystem/features/tpslimit/TPSWarpUtils.java new file mode 100644 index 00000000..2e2e465e --- /dev/null +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/tpslimit/TPSWarpUtils.java @@ -0,0 +1,61 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2021 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; + +import de.steamwar.bausystem.BauSystem; +import de.steamwar.core.TPSWatcher; +import de.steamwar.core.VersionedRunnable; +import lombok.experimental.UtilityClass; +import org.bukkit.Bukkit; + +@UtilityClass +public class TPSWarpUtils { + + private static boolean warp = true; + private static long nanoOffset = 0; + private static long nanoDOffset = 0; + + public static void init() { + VersionedRunnable.call(new VersionedRunnable(() -> warp = false, 8), + new VersionedRunnable(() -> { + Bukkit.getScheduler().runTaskTimer(BauSystem.getInstance(), () -> nanoOffset += nanoDOffset, 1, 1); + TPSUtils_15.init(() -> nanoOffset); + }, 15)); + } + + public static void setTPS(double tps) { + double d = 50 - (50 / (tps / 20.0)); + nanoDOffset = Math.max(0, Math.min((long) (d * 1000000), 37500000)); + } + + public static boolean isWarpAllowed() { + return warp; + } + + public static boolean isWarping() { + return nanoDOffset > 0; + } + + public static double getTps(TPSWatcher.TPSType tpsType) { + if (TPSWarpUtils.isWarping()) + return TPSWatcher.getTPS(tpsType, Math.max(TPSLimitUtils.getCurrentTPSLimit(), 20)); + return TPSWatcher.getTPS(tpsType); + } +}