diff --git a/BauSystem/BauSystem_Main/src/BauSystem.properties b/BauSystem/BauSystem_Main/src/BauSystem.properties index aea72646..432ac00a 100644 --- a/BauSystem/BauSystem_Main/src/BauSystem.properties +++ b/BauSystem/BauSystem_Main/src/BauSystem.properties @@ -1027,4 +1027,10 @@ 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- §7TNT §e{1} +TLS_MESSAGE_80=§7TLS§8> §7Tick §e{0} §8- §7TNT §e{1} §8(§e{2} §7with Fuse 80§8) +TLS_START_HELP=§8/§etls start §8: §7Start the TNT Listener +TLS_STOP_HELP=§8/§etls stop §8: §7Stop the TNT Listener +TLS_SCOREBOARD_ELEMENT=§eTLS§8: §aon \ No newline at end of file diff --git a/BauSystem/BauSystem_Main/src/BauSystem_de.properties b/BauSystem/BauSystem_Main/src/BauSystem_de.properties index e96c9316..453b4079 100644 --- a/BauSystem/BauSystem_Main/src/BauSystem_de.properties +++ b/BauSystem/BauSystem_Main/src/BauSystem_de.properties @@ -959,4 +959,8 @@ XRAY_OFF=§cXray deaktiviert COLORREPLACE_HELP=§8//§ecolorreplace §8[§7color§8] §8[§7color§8] §8- §7Ersetzt eine Farbe mit einer anderen TYPEREPLACE_HELP=§8//§etyreplace §8[§7type§8] §8[§7type§8] §8- §7Ersetzt einen Blockgruppe mit einer anderen # Schematics -SCHEMATIC_GUI_ITEM=§eSchematics \ No newline at end of file +SCHEMATIC_GUI_ITEM=§eSchematics +TLS_MESSAGE_80=§7TLS§8> §7Tick §e{0} §8- §7TNT §e{1} §8(§e{2} §7mit Fuse 80§8) +TLS_START_HELP=§8/§etls start §8: §7Starte den TNT Listener +TLS_STOP_HELP=§8/§etls stop §8: §7Stope den TNT Listener +TLS_SCOREBOARD_ELEMENT=§eTLS§8: §aan \ 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..355803dc --- /dev/null +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tntlistener/TLSCommand.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.command.SWCommand; +import de.steamwar.linkage.Linked; +import de.steamwar.linkage.LinkedInstance; +import org.bukkit.entity.Player; + +@Linked +public class TLSCommand extends SWCommand { + + @LinkedInstance + private TLSListener listener; + + public TLSCommand() { + super("tntlistener", "tls"); + } + + @Register(value = "start", description = "TLS_START_HELP") + public void start(@Validator Player player) { + listener.startListening(player); + } + + @Register(value = "stop", description = "TLS_STOP_HELP") + public void stop(@Validator Player player) { + listener.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..9b587d8f --- /dev/null +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tntlistener/TLSListener.java @@ -0,0 +1,143 @@ +/* + * 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.BauSystem; +import de.steamwar.bausystem.features.tpslimit.TPSUtils; +import de.steamwar.bausystem.region.Region; +import de.steamwar.linkage.Linked; +import de.steamwar.linkage.LinkedInstance; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.entity.TNTPrimed; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntitySpawnEvent; +import org.bukkit.event.player.PlayerQuitEvent; + +import java.util.*; + +@Linked +public class TLSListener implements Listener { + + @LinkedInstance + private BauSystem bauSystem; + + private final Map> primedTNT = new HashMap<>(); + + private final Set regionSheduled = new HashSet<>(); + + private final Map regionReferenceTick = new HashMap<>(); + + private final Set listeningPlayers = new HashSet<>(); + + private boolean listening = false; + + //TODO: Make adjustable per region + private final long maxDelay = 120; + + @EventHandler(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 + primedTNT.computeIfAbsent(eventRegion, rg -> new ArrayList<>()).add(tnt); + + // Update the relativeTick + 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.add(region)) { + return; + } + + Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> messagePlayer(region, relativeTick), 1); + } + + private void messagePlayer(Region region, long relativeTick) { + + regionSheduled.remove(region); + + int primed80 = 0; + int primed79 = 0; + for (TNTPrimed tnt : primedTNT.getOrDefault(region, Collections.emptyList())) { + if (tnt.getFuseTicks() == 80) { + primed80++; + } else if (tnt.getFuseTicks() == 79) { + primed79++; + } + } + primedTNT.remove(region); + + /* + * 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) { + BauSystem.MESSAGE.sendPrefixless("TLS_MESSAGE_80", p, relativeTick, primed79 + primed80, primed80); + } else { + BauSystem.MESSAGE.sendPrefixless("TLS_MESSAGE_79", p, relativeTick, primed79); + } + } + } + } + + @EventHandler + public void onPlayerQuit(PlayerQuitEvent event) { + stopListening(event.getPlayer()); + } + + public void startListening(Player player) { + listeningPlayers.add(player); + this.listening = true; + } + + public void stopListening(Player player) { + listeningPlayers.remove(player); + if (listeningPlayers.isEmpty()) { + 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..0dec6609 --- /dev/null +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tntlistener/TLSScoreboardElement.java @@ -0,0 +1,55 @@ +/* + * 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.BauSystem; +import de.steamwar.bausystem.Permission; +import de.steamwar.bausystem.region.Region; +import de.steamwar.bausystem.utils.ScoreboardElement; +import de.steamwar.linkage.Linked; +import de.steamwar.linkage.LinkedInstance; +import org.bukkit.entity.Player; + +@Linked +public class TLSScoreboardElement implements ScoreboardElement { + + @LinkedInstance + private TLSListener listener; + + @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 (listener.isActiveFor(p)) { + return BauSystem.MESSAGE.parse("TLS_SCOREBOARD_ELEMENT", p); + } else { + return null; + } + } +} diff --git a/CommonCore/Linkage/src/de/steamwar/linkage/AbstractLinker.java b/CommonCore/Linkage/src/de/steamwar/linkage/AbstractLinker.java index 4f86602d..cf88d488 100644 --- a/CommonCore/Linkage/src/de/steamwar/linkage/AbstractLinker.java +++ b/CommonCore/Linkage/src/de/steamwar/linkage/AbstractLinker.java @@ -99,7 +99,7 @@ public abstract class AbstractLinker { try { instances.forEach((clazz, o) -> { - for (Field field : clazz.getFields()) { + for (Field field : clazz.getDeclaredFields()) { if (field.getAnnotation(LinkedInstance.class) != null) { try { field.setAccessible(true);