diff --git a/BauSystem/BauSystem_Main/src/BauSystem.properties b/BauSystem/BauSystem_Main/src/BauSystem.properties index aea72646..e8b74647 100644 --- a/BauSystem/BauSystem_Main/src/BauSystem.properties +++ b/BauSystem/BauSystem_Main/src/BauSystem.properties @@ -1027,4 +1027,9 @@ XRAY_OFF=§cXray deactivated COLORREPLACE_HELP=§8//§ecolorreplace §8[§7color§8] §8[§7color§8] §8- §7Replace all blocks of one color with another TYPEREPLACE_HELP=§8//§etypereplace §8[§7type§8] §8[§7type§8] §8- §7Replace all blocks of one type with another # Schematic -SCHEMATIC_GUI_ITEM=§eSchematics \ No newline at end of file +SCHEMATIC_GUI_ITEM=§eSchematics +# TNTListener +TLS_MESSAGE_79=§7TLS§8> §7Tick §e{0}§8: §e{1} §7TNT +TLS_MESSAGE_80=§7TLS§8> §7Tick §e{0}§8: §e{1} §7TNT §8(§e{2} §7with Fuse 80§8) +TLS_START_HELP=§7Start the TNT Listener +TLS_STOP_HELP=§7Stop the TNT Listener \ No newline at end of file diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tntlistener/TLSCommand.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tntlistener/TLSCommand.java new file mode 100644 index 00000000..9336ea39 --- /dev/null +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tntlistener/TLSCommand.java @@ -0,0 +1,39 @@ +/* + * 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.tntlistener; + +import de.steamwar.command.SWCommand; +import de.steamwar.linkage.Linked; +import org.bukkit.entity.Player; + +@Linked +public class TLSCommand extends SWCommand { + public TLSCommand() { super("tntlistener", "tls"); } + + @Register(value = "start", description = "TLS_START_HELP") + public void start(@Validator Player player) { + TLSListener.instance.startListening(player); + } + + @Register(value = "stop", description = "TLS_STOP_HELP") + public void stop(@Validator Player player) { + TLSListener.instance.stopListening(player); + } +} diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tntlistener/TLSListener.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tntlistener/TLSListener.java new file mode 100644 index 00000000..806fec7f --- /dev/null +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tntlistener/TLSListener.java @@ -0,0 +1,162 @@ +/* + * 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.tntlistener; + +import de.steamwar.bausystem.features.tpslimit.TPSUtils; +import de.steamwar.bausystem.BauSystem; +import de.steamwar.bausystem.region.Region; +import de.steamwar.linkage.Linked; +import de.steamwar.linkage.LinkedInstance; +import org.bukkit.entity.TNTPrimed; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntitySpawnEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.entity.Player; + +import java.util.*; + +@Linked +public class TLSListener implements Listener { + + @LinkedInstance + BauSystem bauSystem; + + /** + * Expose an instance to the TLS Command so it can access it + */ + @LinkedInstance + public static TLSListener instance; + + private final Map> primedTNT = new HashMap<>(); + + private final Map regionSheduled = new HashMap<>(); + + private final Map regionReferenceTick = new HashMap<>(); + + private final List listeningPlayers = new ArrayList<>(); + + private boolean listening = false; + + private final long maxDelay = 120; + + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) + public void onTNTSpawn(EntitySpawnEvent event) { + if (!(event.getEntity() instanceof TNTPrimed tnt)) return; + if (!(listening)) return; + + Region eventRegion = Region.getRegion(event.getLocation()); + + // Add tnt to the list + if (!primedTNT.containsKey(eventRegion)) { + primedTNT.put(eventRegion, new ArrayList<>()); + } + primedTNT.get(eventRegion).add(tnt); + + // Update the realtiveTick + long currentTick = TPSUtils.currentRealTick.get(); + + long referenceTick = regionReferenceTick.getOrDefault(eventRegion, 0L); + if (currentTick - referenceTick > maxDelay) { + referenceTick = currentTick; + regionReferenceTick.put(eventRegion, referenceTick); + } + long relativeTick = currentTick - referenceTick; + + this.messageNextTick(eventRegion, relativeTick); + } + + public void messageNextTick(Region region, long relativeTick) { + /* + * Some TNT can be spawned so late in the gt, that they do not get processed, + * essentially lengthening their fuse by 1gt. To detect this, shedule the + * message at the start of the next (server side) gt. If the tnt got processed it has a + * Fuse of 79, else 80. + */ + + if (regionSheduled.getOrDefault(region, false)) { + return; + } + regionSheduled.put(region, true); + + new BukkitRunnable() { + @Override + public void run() { + + regionSheduled.remove(region); + + int primed80 = 0; + int primed79 = 0; + //tntFuse79.put(region, 0); + //tntFuse80.put(region, 0); + for (TNTPrimed tnt : primedTNT.get(region)) { + if (tnt.getFuseTicks() == 80) { + primed80++; + //tntFuse80.put(region, tntFuse80.getOrDefault(region, 0) + 1); + } else if (tnt.getFuseTicks() == 79) { + primed79++; + //tntFuse79.getOrDefault(region, tntFuse79.getOrDefault(region, 0) + 1); + } + } + //primedTNT.get(region).removeIf(t -> t.getFuseTicks() <= 78 || t.isDead()); + primedTNT.get(region).clear(); + + /* + * Message the player, if there are tnt with Fuse 80 use special message + */ + for (Player p : listeningPlayers) { + if (region.getArea().inRegion(p.getLocation(), false)) { + if (primed80 > 0) { + // Foreach throws IDE errors: variables in lambda expressions should be final + //p.sendMessage(String.format("§7TLS§8> §7Tick §e%d§8: §e%d §7TNT §8(§e%d §7with Fuse 80§8)", relativeTick, primed79 + primed80, primed80)); + BauSystem.MESSAGE.sendPrefixless("TLS_MESSAGE_80", p, relativeTick, primed79 + primed80, primed80); + } else { + BauSystem.MESSAGE.sendPrefixless("TLS_MESSAGE_79", p, relativeTick, primed79); + //p.sendMessage(String.format("§7TLS§8> §7Tick §e%d§8: §e%d §7TNT", relativeTick, primed79)); + } + } + } + } + }.runTaskLater(bauSystem.getInstance(), 1); + } + + @EventHandler + public void onPlayerQuit(PlayerQuitEvent event) { + listeningPlayers.remove(event.getPlayer()); + } + + public void startListening(Player player) { + this.listening = true; + listeningPlayers.add(player); + } + + public void stopListening(Player player) { + listeningPlayers.remove(player); + if (listeningPlayers.toArray().length == 0) { + this.listening = false; + } + } + + public boolean isActiveFor(Player player) { + return listeningPlayers.contains(player); + } +} diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tntlistener/TLSScoreboardElement.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tntlistener/TLSScoreboardElement.java new file mode 100644 index 00000000..61f842e5 --- /dev/null +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tntlistener/TLSScoreboardElement.java @@ -0,0 +1,46 @@ +/* + * 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.tntlistener; + +import de.steamwar.linkage.Linked; +import de.steamwar.bausystem.Permission; +import de.steamwar.bausystem.region.Region; +import de.steamwar.bausystem.utils.ScoreboardElement; +import org.bukkit.entity.Player; + +@Linked +public class TLSScoreboardElement implements ScoreboardElement { + + @Override + public ScoreboardGroup getGroup() { return ScoreboardGroup.OTHER; } + + @Override + public int order() { return 2; } + + @Override + public String get(Region region, Player p) { + if (!Permission.BUILD.hasPermission(p)) return null; + if (TLSListener.instance.isActiveFor(p)) { + return "§e" + "TLS" + "§8: " + "§aon"; + } else { + return null; + } + } +}