diff --git a/BauSystem_19/src/de/steamwar/bausystem/utils/NMSWrapper19.java b/BauSystem_19/src/de/steamwar/bausystem/utils/NMSWrapper19.java index 24edeb9a..d6c7df29 100644 --- a/BauSystem_19/src/de/steamwar/bausystem/utils/NMSWrapper19.java +++ b/BauSystem_19/src/de/steamwar/bausystem/utils/NMSWrapper19.java @@ -28,6 +28,7 @@ import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.game.*; +import net.minecraft.network.syncher.DataWatcher; import net.minecraft.server.level.PlayerInteractManager; import net.minecraft.world.level.EnumGamemode; import net.minecraft.world.phys.Vec3D; @@ -44,7 +45,6 @@ import org.bukkit.inventory.ItemStack; import java.util.ArrayList; import java.util.List; -import java.util.Objects; import java.util.function.LongSupplier; public class NMSWrapper19 implements NMSWrapper { @@ -91,7 +91,9 @@ public class NMSWrapper19 implements NMSWrapper { if (entity instanceof TNTPrimed) { net.minecraft.world.entity.Entity serverEntity = ((CraftEntity) entity).getHandle(); - packets.add(new PacketPlayOutEntityMetadata(serverEntity.ah(), Objects.requireNonNull(serverEntity.al().c()))); + List> list = serverEntity.al().c(); + if(list != null) + packets.add(new PacketPlayOutEntityMetadata(serverEntity.ah(), list)); } }); } diff --git a/BauSystem_19/src/de/steamwar/bausystem/utils/PlayerMovementWrapper19.java b/BauSystem_19/src/de/steamwar/bausystem/utils/PlayerMovementWrapper19.java index fd9a892e..afc51ded 100644 --- a/BauSystem_19/src/de/steamwar/bausystem/utils/PlayerMovementWrapper19.java +++ b/BauSystem_19/src/de/steamwar/bausystem/utils/PlayerMovementWrapper19.java @@ -21,19 +21,60 @@ package de.steamwar.bausystem.utils; import net.minecraft.network.protocol.game.PacketPlayInFlying; import net.minecraft.server.level.EntityPlayer; +import org.bukkit.Location; import org.bukkit.craftbukkit.v1_19_R2.entity.CraftPlayer; import org.bukkit.entity.Player; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + public class PlayerMovementWrapper19 implements PlayerMovementWrapper { + private static class Position { + private double x; + private double y; + private double z; + private float yaw; + private float pitch; + } + + private Map playerLocationMap = new HashMap<>(); + @Override public void setPosition(Player player, Object object) { + Position position = playerLocationMap.computeIfAbsent(player.getUniqueId(), uuid -> { + Position pos = new Position(); + Location location = player.getLocation(); + pos.x = location.getX(); + pos.y = location.getY(); + pos.z = location.getZ(); + pos.yaw = location.getYaw(); + pos.pitch = location.getPitch(); + return pos; + }); PacketPlayInFlying packetPlayInFlying = ((PacketPlayInFlying) object); EntityPlayer entityPlayer = ((CraftPlayer) player).getHandle(); if (packetPlayInFlying.h) { entityPlayer.b(packetPlayInFlying.a, packetPlayInFlying.b, packetPlayInFlying.c, packetPlayInFlying.d, packetPlayInFlying.e); + position.x = packetPlayInFlying.a; + position.y = packetPlayInFlying.b; + position.z = packetPlayInFlying.c; + position.yaw = packetPlayInFlying.d; + position.pitch = packetPlayInFlying.e; } else { entityPlayer.e(packetPlayInFlying.a, packetPlayInFlying.b, packetPlayInFlying.c); + position.x = packetPlayInFlying.a; + position.y = packetPlayInFlying.b; + position.z = packetPlayInFlying.c; + } + } + + @Override + public void disable(Player player) { + Position position = playerLocationMap.remove(player.getUniqueId()); + if (position != null) { + player.teleport(new Location(player.getWorld(), position.x, position.y, position.z, position.yaw, position.pitch)); } } } diff --git a/BauSystem_Main/src/BauSystem.properties b/BauSystem_Main/src/BauSystem.properties index 8d03d0ca..a3860e34 100644 --- a/BauSystem_Main/src/BauSystem.properties +++ b/BauSystem_Main/src/BauSystem.properties @@ -509,6 +509,18 @@ SCRIPT_GUI_CONSTANT_TPS_LORE = §etps§7 of the server SCRIPT_GUI_CONSTANT_TPS_LIMIT_NAME = §7Constant §etps_limit SCRIPT_GUI_CONSTANT_TPS_LIMIT_LORE = §etps_limit§7 of the server +# Shield Printing +SHIELD_PRINTING_NO_REGION = §cYou are not in a region. +SHIELD_PRINTING_NOT_RUNNING = §cThe shield printing is not running. +SHIELD_PRINTING_DISALLOWED = §cYou are not allowed to use shield printing here. +SHIELD_PRINTING_BOSSBAR = §fMovements: {0} +SHIELD_PRINTING_BOSSBAR_COPIED = §fMovements: {0} Copied: {1} + +SHIELD_PRINTING_START = §aThe shield printing has been started. +SHIELD_PRINTING_COPY = §aThe shield has been copied. +SHIELD_PRINTING_APPLY = §aThe shield has been applied. +SHIELD_PRINTING_STOP = §aThe shield printing has been stopped. + # Unsign Book UNSIGN_HELP=§8/§eunsign §8- §7Make a signed book writable again @@ -655,8 +667,10 @@ INVENTORY_FILL_DISABLE = §cInventoryFiller deactivated KILLCHECKER_HELP_ENABLE = §8/§ekillchecker enable §8- §7Enables Killchecker / Recalculates kills KILLCHECKER_HELP_DISABLE = §8/§ekillchecker disable §8- §7Disables Killchecker KILLCHECKER_INFO = §7Shows the overlaps of cannon kills in your build area. +KILLCHECKER_INFO2 = §7Only colorable blocks like Wool, Terractotta, Stained Glass and Concrete are counted. KILLCHECKER_ENABLE = §aKillchecker activated KILLCHECKER_DISABLE = §cKillchecker deactivated +KILLCHECKER_BOSSBAR = §e§l{0} §7(§e{1}%§7) §e§l{2}§7 cannons # BlockCounter BLOCK_COUNTER_HELP_TOGGLE = §8/§eblockcounter §8- §7Toggle on/off @@ -918,6 +932,8 @@ SKULL_GUI_ITEM_NAME = §ePlayer Heads ANVIL_INV_NAME=Player name # StructureVoid STRUCTURE_VOID_COMMAND_HELP=§8/§estructureVoid §8-§7 Receive a StructureVoid +# Dragon Egg +DRAGON_EGG_COMMAND_HELP=§8/§edragonegg §8-§7 Receive a Dragon Egg # NightVision NIGHT_VISION_HELP=§8/§enightvision §8-§7 Toggel nightvision. NIGHT_VISION_OFF=§eNightvision deactivated diff --git a/BauSystem_Main/src/BauSystem_de.properties b/BauSystem_Main/src/BauSystem_de.properties index f49f1fc6..9689be29 100644 --- a/BauSystem_Main/src/BauSystem_de.properties +++ b/BauSystem_Main/src/BauSystem_de.properties @@ -502,6 +502,18 @@ SCRIPT_GUI_CONSTANT_TPS_LORE = §etps§7 vom Server SCRIPT_GUI_CONSTANT_TPS_LIMIT_NAME = §7Constant §etps_limit SCRIPT_GUI_CONSTANT_TPS_LIMIT_LORE = §etps_limit§7 vom Server +# Shield Printing +SHIELD_PRINTING_NO_REGION = §cDu bist in keiner Region. +SHIELD_PRINTING_NOT_RUNNING = §cShield printing ist nicht aktiv. +SHIELD_PRINTING_DISALLOWED = §cDu darfst Shield printing nicht benutzen. +SHIELD_PRINTING_BOSSBAR = §fBewegungen: {0} +SHIELD_PRINTING_BOSSBAR_COPIED = §fBewegungen: {0} Kopiert: {1} + +SHIELD_PRINTING_START = §aShield printing wurde gestartet. +SHIELD_PRINTING_COPY = §aSchilde wurden kopiert. +SHIELD_PRINTING_APPLY = §aSchilde wurden angewendet. +SHIELD_PRINTING_STOP = §aShield printing wurde gestoppt. + # Unsign Book UNSIGN_HELP=§8/§eunsign §8- §7Mache ein Buch beschreibbar @@ -628,8 +640,10 @@ INVENTORY_FILL_DISABLE = §cInventoryFiller deactivated KILLCHECKER_HELP_ENABLE = §8/§ekillchecker enable §8- §7Aktiviert Killchecker / Berechnet kills neu KILLCHECKER_HELP_DISABLE = §8/§ekillchecker disable §8- §7Deaktiviert Killchecker KILLCHECKER_INFO = §7Zeigt Überlappungen der Kanonen Kills im Baubereich an. +KILLCHECKER_INFO2 = §7Nur farbige Blöcke wie Wolle, Terracotta, Stained Glass und Concrete wird gezählt. KILLCHECKER_ENABLE = §aKillchecker aktiviert KILLCHECKER_DISABLE = §cKillchecker deaktiviert +KILLCHECKER_BOSSBAR = §e§l{0} §7(§e{1}%§7) §e§l{2}§7 Kanonnen # BlockCounter BLOCK_COUNTER_HELP_TOGGLE = §8/§eblockcounter §8- §7Wechsel zwischen an und aus @@ -889,6 +903,8 @@ SKULL_GUI_ITEM_NAME = §eSpieler Köpfe ANVIL_INV_NAME=Spieler name # StructureVoid STRUCTURE_VOID_COMMAND_HELP=§8/§estructureVoid §8-§7 Erhalte ein StructureVoid +# Dragon Egg +DRAGON_EGG_COMMAND_HELP=§8/§edragonegg §8-§7 Erhalte ein Drachenei # NightVision NIGHT_VISION_HELP=§8/§enightvision §8-§7 Schalte Nightvision an oder aus. NIGHT_VISION_OFF=§eNightvision deaktiviert diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/killchecker/Cuboid.java b/BauSystem_Main/src/de/steamwar/bausystem/features/killchecker/Cuboid.java new file mode 100644 index 00000000..08632a9c --- /dev/null +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/killchecker/Cuboid.java @@ -0,0 +1,34 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 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.killchecker; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class Cuboid { + private double x; + private double y; + private double z; + private double dx; + private double dy; + private double dz; +} \ No newline at end of file diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/killchecker/KillcheckerCommand.java b/BauSystem_Main/src/de/steamwar/bausystem/features/killchecker/KillcheckerCommand.java index fee15416..1340f1c1 100644 --- a/BauSystem_Main/src/de/steamwar/bausystem/features/killchecker/KillcheckerCommand.java +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/killchecker/KillcheckerCommand.java @@ -21,8 +21,10 @@ package de.steamwar.bausystem.features.killchecker; import de.steamwar.bausystem.BauSystem; import de.steamwar.bausystem.region.Region; +import de.steamwar.bausystem.utils.bossbar.BossBarService; import de.steamwar.command.SWCommand; import de.steamwar.linkage.Linked; +import de.steamwar.linkage.LinkedInstance; import org.bukkit.Bukkit; import org.bukkit.block.Block; import org.bukkit.entity.Player; @@ -42,17 +44,21 @@ public class KillcheckerCommand extends SWCommand implements Listener { private Map visualizers = new HashMap<>(); + @LinkedInstance + public BossBarService bossBarService; + public KillcheckerCommand() { super("killchecker"); addDefaultHelpMessage("KILLCHECKER_INFO"); + addDefaultHelpMessage("KILLCHECKER_INFO2"); } @Register(value = "enable", description = "KILLCHECKER_HELP_ENABLE") - public void genericCommand(Player player) { + public void genericCommand(Player player, @OptionalValue("-outline") @StaticValue(value = {"-area", "-outline"}, allowISE = true) boolean onlyOutline) { Region region = Region.getRegion(player.getLocation()); - KillcheckerVisualizer killcheckerVisualizer = visualizers.computeIfAbsent(region, KillcheckerVisualizer::new); + KillcheckerVisualizer killcheckerVisualizer = visualizers.computeIfAbsent(region, region1 -> new KillcheckerVisualizer(region1, bossBarService)); killcheckerVisualizer.recalc(); - killcheckerVisualizer.show(player); + killcheckerVisualizer.show(player, onlyOutline); BauSystem.MESSAGE.send("KILLCHECKER_ENABLE", player); } @@ -73,7 +79,7 @@ public class KillcheckerCommand extends SWCommand implements Listener { Player player = event.getPlayer(); Set regions = new HashSet<>(); visualizers.forEach((region, visualizer) -> { - if (visualizer.hide(player)) { + if (visualizer.disconnect(player)) { regions.add(region); } }); diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/killchecker/KillcheckerVisualizer.java b/BauSystem_Main/src/de/steamwar/bausystem/features/killchecker/KillcheckerVisualizer.java index e97f4a30..ff21d13b 100644 --- a/BauSystem_Main/src/de/steamwar/bausystem/features/killchecker/KillcheckerVisualizer.java +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/killchecker/KillcheckerVisualizer.java @@ -19,11 +19,13 @@ package de.steamwar.bausystem.features.killchecker; -import de.steamwar.bausystem.features.slaves.laufbau.Cuboid; +import de.steamwar.bausystem.BauSystem; import de.steamwar.bausystem.region.Point; import de.steamwar.bausystem.region.Region; import de.steamwar.bausystem.region.utils.RegionExtensionType; import de.steamwar.bausystem.region.utils.RegionType; +import de.steamwar.bausystem.utils.bossbar.BauSystemBossbar; +import de.steamwar.bausystem.utils.bossbar.BossBarService; import de.steamwar.entity.REntity; import de.steamwar.entity.REntityServer; import de.steamwar.entity.RFallingBlockEntity; @@ -31,6 +33,9 @@ import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.World; import org.bukkit.block.Block; +import org.bukkit.boss.BarColor; +import org.bukkit.boss.BarStyle; +import org.bukkit.boss.BossBar; import org.bukkit.entity.Player; import java.util.HashMap; @@ -40,33 +45,59 @@ import java.util.Set; public class KillcheckerVisualizer { - private static final Material[] materials = new Material[] {Material.YELLOW_STAINED_GLASS, Material.ORANGE_STAINED_GLASS, Material.RED_STAINED_GLASS, Material.PURPLE_STAINED_GLASS, Material.BLACK_STAINED_GLASS}; + private static final Material[] MATERIALS = new Material[] {Material.YELLOW_STAINED_GLASS, Material.ORANGE_STAINED_GLASS, Material.RED_STAINED_GLASS, Material.PURPLE_STAINED_GLASS, Material.BLACK_STAINED_GLASS}; private static final World WORLD = Bukkit.getWorlds().get(0); - private Point minPoint; - private Point maxPoint; + private static final double SURROUND = 4; + + private final Point minPoint; + private final Point maxPoint; - private Set players = new HashSet<>(); + private final int yArea; + private final int zArea; + private final int xArea; - public KillcheckerVisualizer(Region region) { + private final Set players = new HashSet<>(); + private final Set areaPlayers = new HashSet<>(); + + private final Region region; + private final BossBarService bossBarService; + + public KillcheckerVisualizer(Region region, BossBarService bossBarService) { + this.region = region; this.minPoint = region.getMinPoint(RegionType.BUILD, RegionExtensionType.NORMAL); this.maxPoint = region.getMaxPoint(RegionType.BUILD, RegionExtensionType.NORMAL); + + yArea = (maxPoint.getX() - minPoint.getX()) * (maxPoint.getZ() - minPoint.getZ()); + zArea = (maxPoint.getX() - minPoint.getX()) * (maxPoint.getY() - minPoint.getY()); + xArea = (maxPoint.getY() - minPoint.getY()) * (maxPoint.getZ() - minPoint.getZ()); + + this.bossBarService = bossBarService; } - private REntityServer rEntityServer = new REntityServer(); + private final REntityServer outline = new REntityServer(); + private final REntityServer inner = new REntityServer(); - private Map killCount = new HashMap<>(); - private Map rEntities = new HashMap<>(); + private final Map killCount = new HashMap<>(); + private final Set outlinePointsCache = new HashSet<>(); + private final Map rEntities = new HashMap<>(); + + private double percent = 0; + private int kills = 0; + private int cannonCount = 0; public void recalc() { Set cuboids = new HashSet<>(); Set points = new HashSet<>(); - for (int x = minPoint.getX() + 1; x < maxPoint.getX() - 1; x++) { + for (int x = minPoint.getX() + 1; x < maxPoint.getX(); x++) { for (int y = minPoint.getY(); y < maxPoint.getY(); y++) { - for (int z = minPoint.getZ() + 1; z < maxPoint.getZ() - 1; z++) { + for (int z = minPoint.getZ() + 1; z < maxPoint.getZ(); z++) { if (points.contains(new Point(x, y, z))) continue; Block block = WORLD.getBlockAt(x, y, z); if (block.getType().isAir()) continue; + String name = block.getType().name(); + if (!name.endsWith("_WOOL") && !name.endsWith("_STAINED_GLASS") && !name.endsWith("_CONCRETE") && !name.endsWith("_TERRACOTTA")) continue; + if (name.equals("_GLAZED_TERRACOTTA")) continue; Cuboid cuboid = create(block.getType(), x, y, z); cuboids.add(cuboid); for (int dx = (int) cuboid.getX(); dx <= cuboid.getDx(); dx++) { @@ -79,60 +110,162 @@ public class KillcheckerVisualizer { } } } + cannonCount = cuboids.size(); Map kill = new HashMap<>(); - for (int x = minPoint.getX(); x < maxPoint.getX(); x++) { - for (int z = minPoint.getZ(); z < maxPoint.getZ(); z++) { + int yKills = 0; + int yCount = 0; + Set yPoints = new HashSet<>(); + for (int x = minPoint.getX(); x <= maxPoint.getX(); x++) { + for (int z = minPoint.getZ(); z <= maxPoint.getZ(); z++) { Set cuboidSet = new HashSet<>(); for (Cuboid cuboid : cuboids) { - if (x >= cuboid.getX() - 3.5 && x <= cuboid.getDx() + 3.5 && z >= cuboid.getZ() - 3.5 && z <= cuboid.getDz() + 3.5) { + if (x >= cuboid.getX() - SURROUND && x < cuboid.getDx() + SURROUND && z >= cuboid.getZ() - SURROUND && z < cuboid.getDz() + SURROUND) { cuboidSet.add(cuboid); } } if (cuboidSet.size() > 1) { - Point p1 = new Point(x, minPoint.getY(), z); - kill.put(p1, Math.max(kill.getOrDefault(p1, 0), cuboidSet.size())); - Point p2 = new Point(x, maxPoint.getY(), z); + yCount++; + yKills += splitIntoDoubleKills(cuboidSet.size()); + Point p2 = new Point(x, maxPoint.getY() + 1, z); + yPoints.add(p2); kill.put(p2, Math.max(kill.getOrDefault(p2, 0), cuboidSet.size())); } } } - for (int y = minPoint.getY(); y < maxPoint.getY(); y++) { - for (int z = minPoint.getZ(); z < maxPoint.getZ(); z++) { + int xKills = 0; + int xCount = 0; + Set xPoints = new HashSet<>(); + for (int y = minPoint.getY(); y <= maxPoint.getY(); y++) { + for (int z = minPoint.getZ(); z <= maxPoint.getZ(); z++) { Set cuboidSet = new HashSet<>(); for (Cuboid cuboid : cuboids) { - if (y >= cuboid.getY() - 3.5 && y <= cuboid.getDy() + 3.5 && z >= cuboid.getZ() - 3.5 && z <= cuboid.getDz() + 3.5) { + if (y >= cuboid.getY() - SURROUND && y < cuboid.getDy() + SURROUND && z >= cuboid.getZ() - SURROUND && z < cuboid.getDz() + SURROUND) { cuboidSet.add(cuboid); } } if (cuboidSet.size() > 1) { - Point p1 = new Point(minPoint.getX(), y, z); + xCount++; + xKills += splitIntoDoubleKills(cuboidSet.size()); + Point p1 = new Point(minPoint.getX() - 1, y, z); + xPoints.add(p1); kill.put(p1, Math.max(kill.getOrDefault(p1, 0), cuboidSet.size())); - Point p2 = new Point(maxPoint.getX(), y, z); + Point p2 = new Point(maxPoint.getX() + 1, y, z); + xPoints.add(p2); kill.put(p2, Math.max(kill.getOrDefault(p2, 0), cuboidSet.size())); } } } - for (int x = minPoint.getX(); x < maxPoint.getX(); x++) { - for (int y = minPoint.getY(); y < maxPoint.getY(); y++) { + int zKills = 0; + int zCount = 0; + Set zPoints = new HashSet<>(); + for (int x = minPoint.getX(); x <= maxPoint.getX(); x++) { + for (int y = minPoint.getY(); y <= maxPoint.getY(); y++) { Set cuboidSet = new HashSet<>(); for (Cuboid cuboid : cuboids) { - if (x >= cuboid.getX() - 3.5 && x <= cuboid.getDx() + 3.5 && y >= cuboid.getY() - 3.5 && y <= cuboid.getDy() + 3.5) { + if (x >= cuboid.getX() - SURROUND && x < cuboid.getDx() + SURROUND && y >= cuboid.getY() - SURROUND && y < cuboid.getDy() + SURROUND) { cuboidSet.add(cuboid); } } if (cuboidSet.size() > 1) { - Point p1 = new Point(x, y, minPoint.getZ()); + zCount++; + zKills += splitIntoDoubleKills(cuboidSet.size()); + Point p1 = new Point(x, y, minPoint.getZ() - 1); + zPoints.add(p1); kill.put(p1, Math.max(kill.getOrDefault(p1, 0), cuboidSet.size())); - Point p2 = new Point(x, y, maxPoint.getZ()); + Point p2 = new Point(x, y, maxPoint.getZ() + 1); + zPoints.add(p2); kill.put(p2, Math.max(kill.getOrDefault(p2, 0), cuboidSet.size())); } } } + Set outlinePoints = new HashSet<>(); + yPoints.forEach(point -> { + Point p1 = new Point(point.getX() - 1, point.getY(), point.getZ()); + Point p2 = new Point(point.getX() + 1, point.getY(), point.getZ()); + Point p3 = new Point(point.getX(), point.getY(), point.getZ() - 1); + Point p4 = new Point(point.getX(), point.getY(), point.getZ() + 1); + + Point p5 = new Point(point.getX() - 1, point.getY(), point.getZ() - 1); + Point p6 = new Point(point.getX() - 1, point.getY(), point.getZ() + 1); + Point p7 = new Point(point.getX() + 1, point.getY(), point.getZ() - 1); + Point p8 = new Point(point.getX() + 1, point.getY(), point.getZ() + 1); + + int count = kill.get(point); + + int surrounded = 0; + if (kill.getOrDefault(p1, 0) == count) surrounded++; + if (kill.getOrDefault(p2, 0) == count) surrounded++; + if (kill.getOrDefault(p3, 0) == count) surrounded++; + if (kill.getOrDefault(p4, 0) == count) surrounded++; + if (surrounded != 4) outlinePoints.add(point); + if (kill.getOrDefault(p5, 0) != count) outlinePoints.add(point); + if (kill.getOrDefault(p6, 0) != count) outlinePoints.add(point); + if (kill.getOrDefault(p7, 0) != count) outlinePoints.add(point); + if (kill.getOrDefault(p8, 0) != count) outlinePoints.add(point); + }); + xPoints.forEach(point -> { + Point p1 = new Point(point.getX(), point.getY() - 1, point.getZ()); + Point p2 = new Point(point.getX(), point.getY() + 1, point.getZ()); + Point p3 = new Point(point.getX(), point.getY(), point.getZ() - 1); + Point p4 = new Point(point.getX(), point.getY(), point.getZ() + 1); + + Point p5 = new Point(point.getX(), point.getY() - 1, point.getZ() - 1); + Point p6 = new Point(point.getX(), point.getY() - 1, point.getZ() + 1); + Point p7 = new Point(point.getX(), point.getY() + 1, point.getZ() - 1); + Point p8 = new Point(point.getX(), point.getY() + 1, point.getZ() + 1); + + int count = kill.get(point); + + int surrounded = 0; + if (kill.getOrDefault(p1, 0) == count) surrounded++; + if (kill.getOrDefault(p2, 0) == count) surrounded++; + if (kill.getOrDefault(p3, 0) == count) surrounded++; + if (kill.getOrDefault(p4, 0) == count) surrounded++; + if (surrounded != 4) outlinePoints.add(point); + if (kill.getOrDefault(p5, 0) != count) outlinePoints.add(point); + if (kill.getOrDefault(p6, 0) != count) outlinePoints.add(point); + if (kill.getOrDefault(p7, 0) != count) outlinePoints.add(point); + if (kill.getOrDefault(p8, 0) != count) outlinePoints.add(point); + }); + zPoints.forEach(point -> { + Point p1 = new Point(point.getX() - 1, point.getY(), point.getZ()); + Point p2 = new Point(point.getX() + 1, point.getY(), point.getZ()); + Point p3 = new Point(point.getX(), point.getY() - 1, point.getZ()); + Point p4 = new Point(point.getX(), point.getY() + 1, point.getZ()); + + Point p5 = new Point(point.getX() - 1, point.getY() - 1, point.getZ()); + Point p6 = new Point(point.getX() - 1, point.getY() + 1, point.getZ()); + Point p7 = new Point(point.getX() + 1, point.getY() - 1, point.getZ()); + Point p8 = new Point(point.getX() + 1, point.getY() + 1, point.getZ()); + + int count = kill.get(point); + + int surrounded = 0; + if (kill.getOrDefault(p1, 0) == count) surrounded++; + if (kill.getOrDefault(p2, 0) == count) surrounded++; + if (kill.getOrDefault(p3, 0) == count) surrounded++; + if (kill.getOrDefault(p4, 0) == count) surrounded++; + if (surrounded != 4) outlinePoints.add(point); + if (kill.getOrDefault(p5, 0) != count) outlinePoints.add(point); + if (kill.getOrDefault(p6, 0) != count) outlinePoints.add(point); + if (kill.getOrDefault(p7, 0) != count) outlinePoints.add(point); + if (kill.getOrDefault(p8, 0) != count) outlinePoints.add(point); + }); + + double xPercent = zCount / (double) xArea; + double yPercent = yCount / (double) yArea; + double zPercent = xCount / (double) zArea; + percent = (xPercent + yPercent + zPercent) / 3; + kills = zKills + yKills + xKills; + players.forEach(this::updateBossBar); + Set pointSet = new HashSet<>(killCount.keySet()); + Set outlinePointsCacheLast = new HashSet<>(outlinePointsCache); + outlinePointsCache.clear(); for (Point point : pointSet) { if (!kill.containsKey(point)) { rEntities.get(point).die(); @@ -142,16 +275,37 @@ public class KillcheckerVisualizer { } kill.forEach((point, count) -> { if (rEntities.containsKey(point)) { - if (killCount.get(point) == count) return; + if (killCount.get(point) == count && outlinePoints.contains(point) == outlinePointsCacheLast.contains(point)) return; rEntities.get(point).die(); } - RFallingBlockEntity entity = new RFallingBlockEntity(rEntityServer, point.toLocation(WORLD, 0.5, 0, 0.5), materials[Math.min(count - 1, materials.length) - 1]); + RFallingBlockEntity entity = new RFallingBlockEntity(outlinePoints.contains(point) ? outline : inner, point.toLocation(WORLD, 0.5, 0, 0.5), MATERIALS[Math.min(count - 1, MATERIALS.length) - 1]); entity.setNoGravity(true); rEntities.put(point, entity); + if (outlinePoints.contains(point)) outlinePointsCache.add(point); killCount.put(point, count); }); } + private int splitIntoDoubleKills(int kills) { + return kills * (kills - 1) / 2; + } + + private void updateBossBar(Player player) { + BauSystemBossbar bossbar = bossBarService.get(player, region, "killchecker"); + bossbar.setTitle(BauSystem.MESSAGE.parse("KILLCHECKER_BOSSBAR", player, kills, ((int) (percent * 1000) / 10.0), cannonCount)); + bossbar.setProgress(percent); + + if (percent >= 0.35) { + bossbar.setColor(BarColor.RED); + } else if (percent >= 0.25) { + bossbar.setColor(BarColor.PURPLE); + } else if (percent >= 0.15) { + bossbar.setColor(BarColor.YELLOW); + } else { + bossbar.setColor(BarColor.GREEN); + } + } + private Cuboid create(Material type, int x, int y, int z) { Set checked = new HashSet<>(); Set points = new HashSet<>(); @@ -202,16 +356,42 @@ public class KillcheckerVisualizer { return new Cuboid(minX, minY, minZ, maxX, maxY, maxZ); } - public boolean show(Player player) { - rEntityServer.addPlayer(player); + public boolean show(Player player, boolean onlyOutline) { + outline.addPlayer(player); + if (!onlyOutline) { + inner.addPlayer(player); + areaPlayers.add(player); + } else if (areaPlayers.contains(player)) { + inner.removePlayer(player); + areaPlayers.remove(player); + } + updateBossBar(player); return players.add(player); } public boolean hide(Player player) { - rEntityServer.removePlayer(player); + outline.removePlayer(player); + if (areaPlayers.contains(player)) { + inner.removePlayer(player); + } players.remove(player); + areaPlayers.remove(player); + bossBarService.remove(player, region, "killchecker"); if (players.isEmpty()) { - rEntityServer.close(); + outline.close(); + inner.close(); + return true; + } + return false; + } + + public boolean disconnect(Player player) { + players.remove(player); + areaPlayers.remove(player); + bossBarService.remove(player, region, "killchecker"); + if (players.isEmpty()) { + outline.close(); + inner.close(); return true; } return false; diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/loader/LoaderButton.java b/BauSystem_Main/src/de/steamwar/bausystem/features/loader/LoaderButton.java index e5e74171..4a1d5159 100644 --- a/BauSystem_Main/src/de/steamwar/bausystem/features/loader/LoaderButton.java +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/loader/LoaderButton.java @@ -24,6 +24,7 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; import org.bukkit.block.Block; + @AllArgsConstructor @RequiredArgsConstructor @Getter @@ -46,34 +47,25 @@ public enum LoaderButton { switch (block.getType()) { case LEVER: return LoaderButton.SWITCH; - case ACACIA_BUTTON: - case BIRCH_BUTTON: - case DARK_OAK_BUTTON: - case JUNGLE_BUTTON: - case OAK_BUTTON: - case SPRUCE_BUTTON: - return LoaderButton.WOOD_BUTTON; - case STONE_BUTTON: - return LoaderButton.STONE_BUTTON; - case ACACIA_PRESSURE_PLATE: - case BIRCH_PRESSURE_PLATE: - case DARK_OAK_PRESSURE_PLATE: - case JUNGLE_PRESSURE_PLATE: - case OAK_PRESSURE_PLATE: - case SPRUCE_PRESSURE_PLATE: - case STONE_PRESSURE_PLATE: - return LoaderButton.PRESSURE_PLATE; - case HEAVY_WEIGHTED_PRESSURE_PLATE: - case LIGHT_WEIGHTED_PRESSURE_PLATE: - return LoaderButton.WEIGHTED_PRESSURE_PLATE; case TRIPWIRE: return LoaderButton.TRIPWIRE; case NOTE_BLOCK: return LoaderButton.NOTEBLOCK; case DAYLIGHT_DETECTOR: return LoaderButton.DAYLIGHTSENSOR; + case HEAVY_WEIGHTED_PRESSURE_PLATE: + case LIGHT_WEIGHTED_PRESSURE_PLATE: + return LoaderButton.WEIGHTED_PRESSURE_PLATE; default: - return LoaderButton.INVALID; + if (block.getType().name().contains("STONE_BUTTON")) { + return LoaderButton.STONE_BUTTON; + } else if (block.getType().name().contains("BUTTON")) { + return LoaderButton.WOOD_BUTTON; + } else if (block.getType().name().contains("PRESSURE_PLATE")) { + return LoaderButton.PRESSURE_PLATE; + } else { + return LoaderButton.INVALID; + } } } } diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/region/TNTListener.java b/BauSystem_Main/src/de/steamwar/bausystem/features/region/TNTListener.java index 6bb5b15b..958471e2 100644 --- a/BauSystem_Main/src/de/steamwar/bausystem/features/region/TNTListener.java +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/region/TNTListener.java @@ -35,6 +35,7 @@ import org.bukkit.block.Block; import org.bukkit.entity.Player; import org.bukkit.event.Event; import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.block.BlockExplodeEvent; import org.bukkit.event.entity.EntityExplodeEvent; @@ -83,7 +84,7 @@ public class TNTListener implements Listener { explode(event.blockList(), event.getBlock().getLocation(), null, null); } - @EventHandler + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) public void onExplode(EntityExplodeEvent event) { explode(event.blockList(), event.getLocation(), EventType.TNTExplodeInBuild, event); } diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/script/ScriptExecutor.java b/BauSystem_Main/src/de/steamwar/bausystem/features/script/ScriptExecutor.java index 785c042b..25b29147 100644 --- a/BauSystem_Main/src/de/steamwar/bausystem/features/script/ScriptExecutor.java +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/script/ScriptExecutor.java @@ -191,30 +191,6 @@ public final class ScriptExecutor { private String[] replaceExpressions(String s) { s = s.replaceAll(" +", " "); - // TODO: Remove this code as the Expression System below will replace it! - Set variables = new HashSet<>(localVariables.allVariables()); - variables.addAll(Constants.allVariables()); - variables.addAll(globalVariables.allVariables()); - - for (int i = 0; i < 3; i++) { - for (String variable : variables) { - s = s.replace("<" + variable + ">", getValue(variable)); - s = s.replace("<" + variable + ".type>", getOrItselfValue(variable).type()); - } - for (String constVariable : Constants.allVariables()) { - s = s.replace("", getConstant(constVariable)); - s = s.replace("", Constants.getConstant(constVariable, player).type()); - } - for (String localVariable : localVariables.allVariables()) { - s = s.replace("", getLocal(localVariable)); - s = s.replace("", getLocalVariables().getValue(localVariable).type()); - } - for (String globalVariable : globalVariables.allVariables()) { - s = s.replace("", getGlobal(globalVariable)); - s = s.replace("", globalVariables.getValue(globalVariable).type()); - } - } - StringBuilder result = new StringBuilder(); int depth = 0; StringBuilder st = new StringBuilder(); diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/script/ScriptListener.java b/BauSystem_Main/src/de/steamwar/bausystem/features/script/ScriptListener.java index fbdbf0f2..ef9abe06 100644 --- a/BauSystem_Main/src/de/steamwar/bausystem/features/script/ScriptListener.java +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/script/ScriptListener.java @@ -47,7 +47,7 @@ public class ScriptListener implements Listener { new ScriptExecutor((BookMeta) item.getItemMeta(), event.getPlayer(), null); } - @EventHandler + @EventHandler(priority = EventPriority.LOWEST) public void onPlayerJoin(PlayerJoinEvent event) { GLOBAL_CONTEXT.put(event.getPlayer(), new Context()); } diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/script/command/If.java b/BauSystem_Main/src/de/steamwar/bausystem/features/script/command/If.java index e2a2ac44..47d92f25 100644 --- a/BauSystem_Main/src/de/steamwar/bausystem/features/script/command/If.java +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/script/command/If.java @@ -46,7 +46,7 @@ public class If implements SpecialCommand { BauSystem.MESSAGE.send("SCRIPT_COMMAND_ERROR_NO_BOOLEAN", scriptExecutor.getPlayer()); return false; } - if (v.asBoolean()) { + if (v.asBoolean() && command.length > 2) { jumpToIndex(scriptExecutor, command[2]); } else if (command.length > 3) { jumpToIndex(scriptExecutor, command[3]); diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/script/command/string/Substring.java b/BauSystem_Main/src/de/steamwar/bausystem/features/script/command/string/Substring.java index 74202855..8624cae1 100644 --- a/BauSystem_Main/src/de/steamwar/bausystem/features/script/command/string/Substring.java +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/script/command/string/Substring.java @@ -80,7 +80,7 @@ public class Substring implements SpecialCommand { } else { result = new Value.StringValue(v1.asString().substring((int) v2.asLong())); } - } catch (ArrayIndexOutOfBoundsException e) { + } catch (ArrayIndexOutOfBoundsException | StringIndexOutOfBoundsException e) { result = new Value.StringValue(""); } scriptExecutor.getLocalVariables().putValue(resultName, result); diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/script/custom/event/EventListener.java b/BauSystem_Main/src/de/steamwar/bausystem/features/script/custom/event/EventListener.java index f8913e28..4ee1a97d 100644 --- a/BauSystem_Main/src/de/steamwar/bausystem/features/script/custom/event/EventListener.java +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/script/custom/event/EventListener.java @@ -30,6 +30,7 @@ import org.bukkit.Bukkit; import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.block.Action; import org.bukkit.event.block.BlockBreakEvent; @@ -58,7 +59,7 @@ public class EventListener implements Listener { }, 2, 2); } - @EventHandler + @EventHandler(priority = EventPriority.MONITOR) public void onPlayerJoin(PlayerJoinEvent event) { manager.callEvent(EventType.SelfJoin, event.getPlayer(), event); } diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/script/expression/operator/math/DivideOperator.java b/BauSystem_Main/src/de/steamwar/bausystem/features/script/expression/operator/math/DivideOperator.java index 679c69de..a4ab1c8c 100644 --- a/BauSystem_Main/src/de/steamwar/bausystem/features/script/expression/operator/math/DivideOperator.java +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/script/expression/operator/math/DivideOperator.java @@ -41,6 +41,9 @@ public class DivideOperator implements Operator { if (is(value, Value.StringValue.class, Value.BooleanValue.class) || is(value2, Value.StringValue.class, Value.BooleanValue.class)) { throw new IllegalArgumentException("SCRIPT_COMMAND_ARITHMETIC_DIV_ERROR"); } + if (value2.asDouble() == 0) { + return new Value.DoubleValue(Double.NaN); + } return new Value.DoubleValue(value.asDouble() / value2.asDouble()); } } diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/shieldprinting/ShieldPrinting.java b/BauSystem_Main/src/de/steamwar/bausystem/features/shieldprinting/ShieldPrinting.java new file mode 100644 index 00000000..ea3b0fbc --- /dev/null +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/shieldprinting/ShieldPrinting.java @@ -0,0 +1,195 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 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.shieldprinting; + +import de.steamwar.bausystem.BauSystem; +import de.steamwar.bausystem.region.Region; +import de.steamwar.bausystem.utils.bossbar.BauSystemBossbar; +import de.steamwar.bausystem.utils.bossbar.BossBarService; +import de.steamwar.bausystem.utils.bossbar.RegionedBossbar; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.data.BlockData; +import org.bukkit.boss.BarColor; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockFormEvent; +import org.bukkit.event.block.BlockPistonExtendEvent; +import org.bukkit.event.block.BlockPistonRetractEvent; +import org.bukkit.event.block.EntityBlockFormEvent; +import org.bukkit.event.entity.EntityAirChangeEvent; +import org.bukkit.event.entity.EntityChangeBlockEvent; +import org.bukkit.event.entity.EntityDeathEvent; +import org.bukkit.event.entity.EntitySpawnEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.util.Vector; + +import java.util.*; + +public class ShieldPrinting implements Listener { + + private static final World WORLD = Bukkit.getWorlds().get(0); + + static { + Bukkit.getScheduler().runTaskTimer(BauSystem.getInstance(), () -> { + ShieldPrintingCommand.SHIELD_PRINTING_MAP.values().forEach(shieldPrinting -> { + shieldPrinting.fallingBlocks.replaceAll((entity, location) -> { + if (entity.isDead()) return null; + return location; + }); + }); + }, 1, 1); + } + + /** + * Vector of current position, Vector of origin + */ + private Map shieldMap = new HashMap<>(); + private Map shieldData = new HashMap<>(); + + private final Region region; + + public ShieldPrinting(Region region) { + this.region = region; + Bukkit.getPluginManager().registerEvents(this, BauSystem.getInstance()); + updateBossbars(); + } + + public void copy() { + for (Map.Entry entry : shieldMap.entrySet()) { + shieldData.put(entry.getValue(), entry.getKey().toLocation(WORLD).getBlock().getBlockData()); + } + updateBossbars(); + } + + public void apply() { + for (Map.Entry entry : shieldData.entrySet()) { + entry.getKey().toLocation(WORLD).getBlock().setBlockData(entry.getValue(), false); + } + } + + public void disable() { + HandlerList.unregisterAll(this); + shieldMap.clear(); + shieldData.clear(); + + for (Player player : Bukkit.getOnlinePlayers()) { + BossBarService.instance.remove(player, region, "shieldprinting"); + } + } + + @EventHandler + public void onBlockPistonExtend(BlockPistonExtendEvent event) { + update(event.getDirection(), event.getBlocks()); + } + + @EventHandler + public void onBlockPistonRetract(BlockPistonRetractEvent event) { + update(event.getDirection(), event.getBlocks()); + } + + private void update(BlockFace direction, List blockList) { + Set toRemove = new HashSet<>(); + Map temp = new HashMap<>(); + for (Block block : blockList) { + if (Region.getRegion(block.getLocation()) != region) continue; + Vector vector = block.getLocation().toVector(); + Vector origin = vector.clone(); + vector = vector.add(direction.getDirection()); + if (shieldMap.containsKey(origin)) { + toRemove.add(origin); + temp.put(vector, shieldMap.get(origin)); + } else { + temp.put(vector, origin); + } + } + shieldMap.keySet().removeAll(toRemove); + shieldMap.putAll(temp); + + toRemove.clear(); + for (Map.Entry entry : shieldMap.entrySet()) { + if (entry.getKey().equals(entry.getValue())) { + toRemove.add(entry.getKey()); + } + } + shieldMap.keySet().removeAll(toRemove); + + updateBossbars(); + } + + private Map fallingBlocks = new HashMap<>(); + + @EventHandler + public void onEntitySpawn(EntitySpawnEvent event) { + if (event.getEntityType() != EntityType.FALLING_BLOCK) return; + if (Region.getRegion(event.getLocation()) != region) return; + fallingBlocks.put(event.getEntity(), event.getLocation().getBlock().getLocation()); + } + + @EventHandler + public void onEntityChangeBlock(EntityChangeBlockEvent event) { + if (!event.getBlock().getType().isAir()) return; + if (event.getEntityType() != EntityType.FALLING_BLOCK) return; + if (Region.getRegion(event.getBlock().getLocation()) != region) return; + Location origin = fallingBlocks.remove(event.getEntity()); + if (origin == null) return; + Location destination = event.getBlock().getLocation(); + Vector originOrigin = shieldMap.remove(origin.toVector()); + shieldMap.put(destination.toVector(), originOrigin != null ? originOrigin : origin.toVector()); + + updateBossbars(); + } + + @EventHandler + public void onPlayerInteract(PlayerInteractEvent event) { + if (event.getClickedBlock() == null) return; + if (event.getItem() == null) return; + if (Region.getRegion(event.getClickedBlock().getLocation()) != region) return; + Vector vector = event.getClickedBlock().getLocation().toVector(); + if (!shieldMap.containsKey(vector)) return; + event.getClickedBlock().setType(Material.AIR); + } + + @EventHandler + public void onPlayerJoin(PlayerJoinEvent event) { + updateBossbar(event.getPlayer()); + } + + private void updateBossbars() { + for (Player player : Bukkit.getOnlinePlayers()) { + updateBossbar(player); + } + } + + private void updateBossbar(Player player) { + BauSystemBossbar bossbar = BossBarService.instance.get(player, region, "shieldprinting"); + bossbar.setColor(BarColor.YELLOW); + bossbar.setTitle(BauSystem.MESSAGE.parse(shieldData.isEmpty() ? "SHIELD_PRINTING_BOSSBAR" : "SHIELD_PRINTING_BOSSBAR_COPIED", player, shieldMap.size(), shieldData.size())); + } +} diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/shieldprinting/ShieldPrintingCommand.java b/BauSystem_Main/src/de/steamwar/bausystem/features/shieldprinting/ShieldPrintingCommand.java new file mode 100644 index 00000000..a72443f5 --- /dev/null +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/shieldprinting/ShieldPrintingCommand.java @@ -0,0 +1,129 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 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.shieldprinting; + +import de.steamwar.bausystem.BauSystem; +import de.steamwar.bausystem.Permission; +import de.steamwar.bausystem.region.Region; +import de.steamwar.command.SWCommand; +import de.steamwar.command.TypeValidator; +import de.steamwar.linkage.Linked; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerInteractEvent; + +import java.util.HashMap; +import java.util.Map; + +@Linked +public class ShieldPrintingCommand extends SWCommand implements Listener { + + public ShieldPrintingCommand() { + super("shieldprinting"); + } + + static final Map SHIELD_PRINTING_MAP = new HashMap<>(); + + @Register + public void genericCommand(@Validator Player player, ShieldPrintingState shieldPrintingState) { + Region region = Region.getRegion(player.getLocation()); + if (region.isGlobal()) { + BauSystem.MESSAGE.send("SHIELD_PRINTING_NO_REGION", player); + return; + } + ShieldPrinting shieldPrinting; + switch (shieldPrintingState) { + case START: + shieldPrinting = SHIELD_PRINTING_MAP.put(region, new ShieldPrinting(region)); + if (shieldPrinting != null) { + shieldPrinting.disable(); + } + BauSystem.MESSAGE.send("SHIELD_PRINTING_START", player); + break; + case COPY: + shieldPrinting = SHIELD_PRINTING_MAP.get(region); + if (shieldPrinting == null) { + BauSystem.MESSAGE.send("SHIELD_PRINTING_NOT_RUNNING", player); + return; + } + shieldPrinting.copy(); + BauSystem.MESSAGE.send("SHIELD_PRINTING_COPY", player); + break; + case APPLY: + shieldPrinting = SHIELD_PRINTING_MAP.get(region); + if (shieldPrinting == null) { + BauSystem.MESSAGE.send("SHIELD_PRINTING_NOT_RUNNING", player); + return; + } + shieldPrinting.apply(); + BauSystem.MESSAGE.send("SHIELD_PRINTING_APPLY", player); + break; + } + } + + @Register("stop") + public void stopCommand(@Validator Player player) { + Region region = Region.getRegion(player.getLocation()); + if (region.isGlobal()) { + BauSystem.MESSAGE.send("SHIELD_PRINTING_NO_REGION", player); + return; + } + ShieldPrinting shieldPrinting = SHIELD_PRINTING_MAP.remove(region); + if (shieldPrinting == null) { + BauSystem.MESSAGE.send("SHIELD_PRINTING_NOT_RUNNING", player); + return; + } + shieldPrinting.disable(); + BauSystem.MESSAGE.send("SHIELD_PRINTING_STOP", player); + } + + @ClassValidator(value = Player.class, local = true) + public TypeValidator validator() { + return (commandSender, player, messageSender) -> { + if (!Permission.hasPermission(player, Permission.WORLD)) { + messageSender.send("SHIELD_PRINTING_DISALLOWED", player); + return false; + } + Region region = Region.getRegion(player.getLocation()); + if (region.isGlobal()) { + messageSender.send("SHIELD_PRINTING_NO_REGION", player); + return false; + } + return true; + }; + } + + @EventHandler + public void onPlayerInteract(PlayerInteractEvent event) { + if (event.getClickedBlock() == null) { + return; + } + Region region = Region.getRegion(event.getClickedBlock().getLocation()); + if (region.isGlobal()) { + return; + } + ShieldPrinting shieldPrinting = SHIELD_PRINTING_MAP.get(region); + if (shieldPrinting == null) { + return; + } + shieldPrinting.onPlayerInteract(event); + } +} diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/shieldprinting/ShieldPrintingState.java b/BauSystem_Main/src/de/steamwar/bausystem/features/shieldprinting/ShieldPrintingState.java new file mode 100644 index 00000000..0f782f67 --- /dev/null +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/shieldprinting/ShieldPrintingState.java @@ -0,0 +1,27 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 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.shieldprinting; + +public enum ShieldPrintingState { + + START, + COPY, + APPLY +} diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/gui/TNTElementGUI.java b/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/gui/TNTElementGUI.java index e87640d0..93181f90 100644 --- a/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/gui/TNTElementGUI.java +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/gui/TNTElementGUI.java @@ -136,22 +136,22 @@ public class TNTElementGUI { // Alignment inv.setItem(23, new SWItem(Material.OAK_BUTTON, BauSystem.MESSAGE.parse("SIMULATOR_ALIGNMENT_NEGATIVE_Z", player), clickType -> { // Z negative if (clickType == ClickType.DOUBLE_CLICK) return; - tntElement.align(new Vector(0.5, 0, 0.49)); + tntElement.align(new Vector(0, 0, 0.49)); tntElement.change(); })); inv.setItem(25, new SWItem(Material.OAK_BUTTON, BauSystem.MESSAGE.parse("SIMULATOR_ALIGNMENT_POSITIVE_Z", player), clickType -> { // Z positive if (clickType == ClickType.DOUBLE_CLICK) return; - tntElement.align(new Vector(0.5, 0, 0.51)); + tntElement.align(new Vector(0, 0, 0.51)); tntElement.change(); })); inv.setItem(15, new SWItem(Material.OAK_BUTTON, BauSystem.MESSAGE.parse("SIMULATOR_ALIGNMENT_POSITIVE_X", player), clickType -> { // X positive if (clickType == ClickType.DOUBLE_CLICK) return; - tntElement.align(new Vector(0.51, 0, 0.5)); + tntElement.align(new Vector(0.51, 0, 0)); tntElement.change(); })); inv.setItem(33, new SWItem(Material.OAK_BUTTON, BauSystem.MESSAGE.parse("SIMULATOR_ALIGNMENT_NEGATIVE_X", player), clickType -> { // X negative if (clickType == ClickType.DOUBLE_CLICK) return; - tntElement.align(new Vector(0.49, 0, 0.5)); + tntElement.align(new Vector(0.49, 0, 0)); tntElement.change(); })); inv.setItem(24, new SWItem(Material.SUNFLOWER, BauSystem.MESSAGE.parse("SIMULATOR_ALIGNMENT_CENTER", player), clickType -> { // CENTER diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/tnt/TNTElement.java b/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/tnt/TNTElement.java index af6e3045..666c3359 100644 --- a/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/tnt/TNTElement.java +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/tnt/TNTElement.java @@ -293,22 +293,25 @@ public class TNTElement implements SimulatorElement { parentVector = tntGroup.getPosition(); } - if (vector.getX() - (int) vector.getX() == 0.49) { - vector.setX(vector.getX() + 0.02); - } - if (vector.getX() - (int) vector.getX() == -0.49) { - vector.setX(vector.getX() - 0.02); - } - if (vector.getZ() - (int) vector.getZ() == 0.49) { - vector.setZ(vector.getZ() + 0.02); - } - if (vector.getZ() - (int) vector.getZ() == -0.49) { - vector.setZ(vector.getZ() - 0.02); + if (offset.getX() != 0) { + if (vector.getX() - (int) vector.getX() == 0.49) { + vector.setX(vector.getX() + 0.02); + } + if (vector.getX() - (int) vector.getX() == -0.49) { + vector.setX(vector.getX() - 0.02); + } + vector.setX(vector.getBlockX() + offset.getX()); } - vector.setX(vector.getBlockX() + offset.getX()); - vector.setY(vector.getBlockY() + offset.getY()); - vector.setZ(vector.getBlockZ() + offset.getZ()); + if (offset.getZ() != 0) { + if (vector.getZ() - (int) vector.getZ() == 0.49) { + vector.setZ(vector.getZ() + 0.02); + } + if (vector.getZ() - (int) vector.getZ() == -0.49) { + vector.setZ(vector.getZ() - 0.02); + } + vector.setZ(vector.getBlockZ() + offset.getZ()); + } setPosition(vector.subtract(parentVector)); } diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/tpslimit/FreezeUtils.java b/BauSystem_Main/src/de/steamwar/bausystem/features/tpslimit/FreezeUtils.java index b9a24ad1..294cffd0 100644 --- a/BauSystem_Main/src/de/steamwar/bausystem/features/tpslimit/FreezeUtils.java +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/tpslimit/FreezeUtils.java @@ -20,13 +20,27 @@ package de.steamwar.bausystem.features.tpslimit; import com.comphenix.tinyprotocol.Reflection; +import com.comphenix.tinyprotocol.TinyProtocol; +import de.steamwar.bausystem.BauSystem; +import de.steamwar.core.BountifulWrapper; +import de.steamwar.core.ChatWrapper; import lombok.Getter; import lombok.experimental.UtilityClass; import org.bukkit.Bukkit; import org.bukkit.World; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.entity.TNTPrimed; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.scheduler.BukkitTask; import yapion.utils.ReflectionsUtils; import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; @UtilityClass public class FreezeUtils { @@ -64,10 +78,89 @@ public class FreezeUtils { if (freezeEnabled) { try { field.set(getWorldHandle.invoke(world), state); + cacheEntityPackets(state); frozen = state; } catch (IllegalAccessException e) { // Ignored; } } } + + private List packets = new ArrayList<>(); + private Set entities = new HashSet<>(); + private BukkitTask task = null; + + private Class vec3dClass = Reflection.getClass("{nms.world.phys}.Vec3D"); + private Reflection.FieldAccessor zeroVec3d = (Reflection.FieldAccessor) Reflection.getField(vec3dClass, vec3dClass, 0); + private Object ZERO_VEC3D = zeroVec3d.get(null); + private Class velocityPacketClass = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutEntityVelocity"); + private Reflection.ConstructorInvoker velocityPacketConstructor = Reflection.getConstructor(velocityPacketClass, int.class, vec3dClass); + + private Class teleportPacketClass = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutEntityTeleport"); + private Class entityClass = Reflection.getClass("{nms.world.entity}.Entity"); + private Reflection.ConstructorInvoker teleportPacketConstructor = Reflection.getConstructor(teleportPacketClass, entityClass); + + private Class craftEntityClass = Reflection.getClass("{obc}.entity.CraftEntity"); + private Reflection.MethodInvoker getHandle = Reflection.getMethod(craftEntityClass, "getHandle"); + + private Object noGravityDataWatcher = BountifulWrapper.impl.getDataWatcherObject(5, Boolean.class); + private Object fuseDataWatcher = BountifulWrapper.impl.getDataWatcherObject(8, Integer.class); + + private void cacheEntityPackets(boolean state) { + if (state) { + createPackets(); + + if (task == null) { + task = new BukkitRunnable() { + @Override + public void run() { + createPackets(); + + for (Player player : Bukkit.getOnlinePlayers()) { + for (Object packet : packets) { + TinyProtocol.instance.sendPacket(player, packet); + } + } + } + }.runTaskTimer(BauSystem.getInstance(), 1, 1); + } + } else { + packets.clear(); + entities.clear(); + + if (task != null) { + task.cancel(); + task = null; + } + } + } + + private void createPackets() { + if (FreezeUtils.entities.stream().anyMatch(Entity::isDead)) { + entities.clear(); + packets.clear(); + } + List entities = Bukkit.getWorlds().get(0).getEntities().stream() + .filter(e -> !(e instanceof Player)) + .filter(e -> !FreezeUtils.entities.contains(e)) + .collect(Collectors.toList()); + + for (Entity entity : entities) { + packets.add(teleportPacketConstructor.invoke(getHandle.invoke(entity))); + } + for (Entity entity : entities) { + packets.add(velocityPacketConstructor.invoke(entity.getEntityId(), ZERO_VEC3D)); + } + for (Entity entity : entities) { + packets.add(ChatWrapper.impl.getDataWatcherPacket(entity.getEntityId(), noGravityDataWatcher, true)); + } + for (Entity entity : entities) { + if (!(entity instanceof TNTPrimed)) continue; + TNTPrimed tnt = (TNTPrimed) entity; + int fuse = tnt.getFuseTicks(); + packets.add(ChatWrapper.impl.getDataWatcherPacket(entity.getEntityId(), fuseDataWatcher, fuse - (fuse % 5) + 1)); + } + + FreezeUtils.entities.addAll(entities); + } } diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/tracer/record/Recorder.java b/BauSystem_Main/src/de/steamwar/bausystem/features/tracer/record/Recorder.java index 2d74492d..8b3fcea0 100644 --- a/BauSystem_Main/src/de/steamwar/bausystem/features/tracer/record/Recorder.java +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/tracer/record/Recorder.java @@ -32,6 +32,7 @@ import org.bukkit.entity.Entity; import org.bukkit.entity.Player; 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.EntityExplodeEvent; import org.bukkit.event.entity.EntitySpawnEvent; @@ -180,7 +181,7 @@ public class Recorder implements Listener { }); } - @EventHandler + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) public void onEntityExplode(EntityExplodeEvent event) { Entity entity = event.getEntity(); if (!(entity instanceof TNTPrimed)) { @@ -188,7 +189,8 @@ public class Recorder implements Listener { } TraceRecorder traceRecorder = get((TNTPrimed) entity); Region region = tntTraceRecorderMap.get((TNTPrimed) entity); - traceRecorder.explode((TNTPrimed) entity, !event.blockList().isEmpty() && region.inRegion(event.getLocation(), RegionType.BUILD, RegionExtensionType.EXTENSION)); + boolean inBuildRegion = event.blockList().stream().anyMatch(block -> region.inRegion(block.getLocation(), RegionType.BUILD, RegionExtensionType.EXTENSION)); + traceRecorder.explode((TNTPrimed) entity, inBuildRegion); tntTraceRecorderMap.remove(entity); tick(); } diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/tracer/show/EntityShowMode.java b/BauSystem_Main/src/de/steamwar/bausystem/features/tracer/show/EntityShowMode.java index bfcd15e5..6f61a08e 100644 --- a/BauSystem_Main/src/de/steamwar/bausystem/features/tracer/show/EntityShowMode.java +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/tracer/show/EntityShowMode.java @@ -202,6 +202,8 @@ public class EntityShowMode implements ShowMode { entity.setDisplayName(fuseTicks + ""); } else if (showModeParameter.isCount()) { entity.setDisplayName(new HashSet<>(records).size() + ""); + } else if (showModeParameter.isTicksSinceStart()) { + entity.setDisplayName((80 - fuseTicks) + tntPosition.getRecord().getOffset() + ""); } } diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/tracer/show/ShowModeParameter.java b/BauSystem_Main/src/de/steamwar/bausystem/features/tracer/show/ShowModeParameter.java index d8e3a75f..4babe506 100644 --- a/BauSystem_Main/src/de/steamwar/bausystem/features/tracer/show/ShowModeParameter.java +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/tracer/show/ShowModeParameter.java @@ -31,6 +31,7 @@ public class ShowModeParameter { private boolean ticks = false; private boolean count = false; private boolean buildDestroyOnly = false; + private boolean ticksSinceStart = false; public void enableWater() { this.water = true; @@ -63,4 +64,8 @@ public class ShowModeParameter { public void enableBuildDestroyOnly() { this.buildDestroyOnly = true; } + + public void enableTicksSinceStart() { + this.ticksSinceStart = true; + } } diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/tracer/show/ShowModeParameterType.java b/BauSystem_Main/src/de/steamwar/bausystem/features/tracer/show/ShowModeParameterType.java index 3d28237d..a80a3633 100644 --- a/BauSystem_Main/src/de/steamwar/bausystem/features/tracer/show/ShowModeParameterType.java +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/tracer/show/ShowModeParameterType.java @@ -38,9 +38,10 @@ public enum ShowModeParameterType { }, Arrays.asList("-advanced", "-a"), "INTERPOLATE_Y", "INTERPOLATE_XZ"), SOURCE(ShowModeParameter::enableSourceOnly, Arrays.asList("-source", "-sourceonly", "-ignite"), "TICKS", "ADVANCED", "INTERPOLATE_Y", "INTERPOLATE_XZ", "WATER"), EXPLODE(ShowModeParameter::enableExplodeOnly, Arrays.asList("-explode", "-explodeonly"), "TICKS", "ADVANCED", "INTERPOLATE_Y", "INTERPOLATE_XZ", "WATER"), - TICKS(ShowModeParameter::enableTicks, Arrays.asList("-ticks", "-t"), "EXPLODE", "SOURCE", "COUNT"), - COUNT(ShowModeParameter::enableCount, Arrays.asList("-count", "-c"), "COUNT"), - BUILD_DESTROY_ONLY(ShowModeParameter::enableBuildDestroyOnly, Arrays.asList("-builddestroy", "-builddestoryonly"), "WATER"); + TICKS(ShowModeParameter::enableTicks, Arrays.asList("-ticks", "-t"), "EXPLODE", "SOURCE", "COUNT", "TICKS_SINCE_START"), + COUNT(ShowModeParameter::enableCount, Arrays.asList("-count", "-c"), "TICKS", "TICKS_SINCE_START"), + BUILD_DESTROY_ONLY(ShowModeParameter::enableBuildDestroyOnly, Arrays.asList("-builddestroy", "-builddestoryonly"), "WATER"), + TICKS_SINCE_START(ShowModeParameter::enableTicksSinceStart, Arrays.asList("-tickssincestart", "-tss"), "TICKS", "COUNT"); @Getter private final Consumer showModeParameterConsumer; diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/util/DragonEggCommand.java b/BauSystem_Main/src/de/steamwar/bausystem/features/util/DragonEggCommand.java new file mode 100644 index 00000000..b9cc7442 --- /dev/null +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/util/DragonEggCommand.java @@ -0,0 +1,40 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 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.util; + +import de.steamwar.bausystem.SWUtils; +import de.steamwar.command.SWCommand; +import de.steamwar.linkage.Linked; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +@Linked +public class DragonEggCommand extends SWCommand { + + public DragonEggCommand() { + super("dragonegg", "enderdragon", "dragon", "egg", "enderei", "ender"); + } + + @Register(description = "DRAGON_EGG_COMMAND_HELP") + public void genericCommand(Player p) { + SWUtils.giveItemToPlayer(p, new ItemStack(Material.DRAGON_EGG, 1)); + } +} diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/xray/XrayCommand.java b/BauSystem_Main/src/de/steamwar/bausystem/features/xray/XrayCommand.java index 22c19b67..f1232760 100644 --- a/BauSystem_Main/src/de/steamwar/bausystem/features/xray/XrayCommand.java +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/xray/XrayCommand.java @@ -97,6 +97,7 @@ public class XrayCommand extends SWCommand implements Listener { techHiderCommand.disable(region, player); if (hidden.get(region).contains(player)) { hidden.get(region).remove(player); + PlayerMovementWrapper.impl.disable(player); BauSystem.MESSAGE.sendPrefixless("XRAY_OFF", player, ChatMessageType.ACTION_BAR); } else { hidden.get(region).add(player); diff --git a/BauSystem_Main/src/de/steamwar/bausystem/utils/PlayerMovementWrapper.java b/BauSystem_Main/src/de/steamwar/bausystem/utils/PlayerMovementWrapper.java index f8aa209d..4a6a6447 100644 --- a/BauSystem_Main/src/de/steamwar/bausystem/utils/PlayerMovementWrapper.java +++ b/BauSystem_Main/src/de/steamwar/bausystem/utils/PlayerMovementWrapper.java @@ -27,4 +27,6 @@ public interface PlayerMovementWrapper { PlayerMovementWrapper impl = VersionDependent.getVersionImpl(BauSystem.getInstance()); void setPosition(Player player, Object object); + default void disable(Player player) { + } } diff --git a/BauSystem_Main/src/de/steamwar/bausystem/utils/bossbar/BauSystemBossbar.java b/BauSystem_Main/src/de/steamwar/bausystem/utils/bossbar/BauSystemBossbar.java new file mode 100644 index 00000000..e72e0b53 --- /dev/null +++ b/BauSystem_Main/src/de/steamwar/bausystem/utils/bossbar/BauSystemBossbar.java @@ -0,0 +1,51 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 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.bossbar; + +import de.steamwar.bausystem.region.Region; +import org.bukkit.boss.BarColor; +import org.bukkit.boss.BarFlag; +import org.bukkit.boss.BarStyle; + +public interface BauSystemBossbar { + + String getTitle(); + void setTitle(String title); + + double getProgress(); + void setProgress(double progress); + + BarColor getColor(); + void setColor(BarColor color); + + BarStyle getStyle(); + void setStyle(BarStyle style); + + boolean hasFlag(BarFlag flag); + void addFlag(BarFlag flag); + void removeFlag(BarFlag flag); + + boolean isVisible(); + void setVisible(boolean visible); + + Region getRegion(); + + void cleanup(); +} diff --git a/BauSystem_Main/src/de/steamwar/bausystem/utils/bossbar/BossBarService.java b/BauSystem_Main/src/de/steamwar/bausystem/utils/bossbar/BossBarService.java new file mode 100644 index 00000000..e6ea2c16 --- /dev/null +++ b/BauSystem_Main/src/de/steamwar/bausystem/utils/bossbar/BossBarService.java @@ -0,0 +1,96 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 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.bossbar; + +import de.steamwar.bausystem.region.Region; +import de.steamwar.linkage.Linked; +import org.bukkit.Bukkit; +import org.bukkit.boss.BarColor; +import org.bukkit.boss.BarStyle; +import org.bukkit.boss.BossBar; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerQuitEvent; + +import java.util.HashMap; +import java.util.Map; + +@Linked +public class BossBarService implements Listener { + + public static BossBarService instance; + + public BossBarService() { + instance = this; + } + + private final Map>> playerBossBars = new HashMap<>(); + + public synchronized BauSystemBossbar get(Player player, Region region, String key) { + return playerBossBars.computeIfAbsent(player, p -> new HashMap<>()) + .computeIfAbsent(region, r -> new HashMap<>()) + .computeIfAbsent(key, k -> { + BossBar bossBar = Bukkit.createBossBar("", BarColor.WHITE, BarStyle.SOLID); + bossBar.addPlayer(player); + if (region.isGlobal()) { + return new GlobalBossbar(bossBar); + } else { + return new RegionedBossbar(bossBar, region, player); + } + }); + } + + public synchronized void removeAll(Player player, String key) { + Map> regionMap = playerBossBars.get(player); + if (regionMap == null) return; + for (Map bossBarMap : regionMap.values()) { + BauSystemBossbar bossBar = bossBarMap.remove(key); + if (bossBar == null) continue; + bossBar.cleanup(); + } + } + + public synchronized void removeAll(Player player) { + Map> regionMap = playerBossBars.remove(player); + if (regionMap == null) return; + for (Map bossBarMap : regionMap.values()) { + for (BauSystemBossbar bossBar : bossBarMap.values()) { + bossBar.cleanup(); + } + } + } + + public synchronized void remove(Player player, Region region, String key) { + Map> regionMap = playerBossBars.get(player); + if (regionMap == null) return; + Map bossBarMap = regionMap.get(region); + if (bossBarMap == null) return; + BauSystemBossbar bossBar = bossBarMap.remove(key); + if (bossBar == null) return; + bossBar.cleanup(); + } + + @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) + public void onPlayerQuit(PlayerQuitEvent event) { + removeAll(event.getPlayer()); + } +} diff --git a/BauSystem_Main/src/de/steamwar/bausystem/utils/bossbar/GlobalBossbar.java b/BauSystem_Main/src/de/steamwar/bausystem/utils/bossbar/GlobalBossbar.java new file mode 100644 index 00000000..a5aaf9fe --- /dev/null +++ b/BauSystem_Main/src/de/steamwar/bausystem/utils/bossbar/GlobalBossbar.java @@ -0,0 +1,112 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 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.bossbar; + +import de.steamwar.bausystem.region.GlobalRegion; +import de.steamwar.bausystem.region.Region; +import org.bukkit.boss.BarColor; +import org.bukkit.boss.BarFlag; +import org.bukkit.boss.BarStyle; +import org.bukkit.boss.BossBar; + +public class GlobalBossbar implements BauSystemBossbar { + + private BossBar bossBar; + + public GlobalBossbar(BossBar bossBar) { + this.bossBar = bossBar; + } + + @Override + public String getTitle() { + return bossBar.getTitle(); + } + + @Override + public void setTitle(String title) { + bossBar.setTitle(title); + } + + @Override + public double getProgress() { + return bossBar.getProgress(); + } + + @Override + public void setProgress(double progress) { + bossBar.setProgress(progress); + } + + @Override + public BarColor getColor() { + return bossBar.getColor(); + } + + @Override + public void setColor(BarColor color) { + bossBar.setColor(color); + } + + @Override + public BarStyle getStyle() { + return bossBar.getStyle(); + } + + @Override + public void setStyle(BarStyle style) { + bossBar.setStyle(style); + } + + @Override + public boolean hasFlag(BarFlag flag) { + return bossBar.hasFlag(flag); + } + + @Override + public void addFlag(BarFlag flag) { + bossBar.addFlag(flag); + } + + @Override + public void removeFlag(BarFlag flag) { + bossBar.removeFlag(flag); + } + + @Override + public boolean isVisible() { + return bossBar.isVisible(); + } + + @Override + public void setVisible(boolean visible) { + bossBar.setVisible(visible); + } + + @Override + public Region getRegion() { + return GlobalRegion.getInstance(); + } + + @Override + public void cleanup() { + bossBar.removeAll(); + bossBar = null; + } +} diff --git a/BauSystem_Main/src/de/steamwar/bausystem/utils/bossbar/RegionedBossbar.java b/BauSystem_Main/src/de/steamwar/bausystem/utils/bossbar/RegionedBossbar.java new file mode 100644 index 00000000..5fb1cc99 --- /dev/null +++ b/BauSystem_Main/src/de/steamwar/bausystem/utils/bossbar/RegionedBossbar.java @@ -0,0 +1,148 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 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.bossbar; + +import de.steamwar.bausystem.BauSystem; +import de.steamwar.bausystem.region.Region; +import de.steamwar.bausystem.region.utils.RegionExtensionType; +import de.steamwar.bausystem.region.utils.RegionType; +import org.bukkit.Bukkit; +import org.bukkit.boss.BarColor; +import org.bukkit.boss.BarFlag; +import org.bukkit.boss.BarStyle; +import org.bukkit.boss.BossBar; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.event.player.PlayerQuitEvent; + +public class RegionedBossbar implements BauSystemBossbar, Listener { + + private BossBar bossBar; + private Region region; + private Player player; + + public RegionedBossbar(BossBar bossBar, Region region, Player player) { + this.bossBar = bossBar; + this.region = region; + this.player = player; + Bukkit.getPluginManager().registerEvents(this, BauSystem.getInstance()); + } + + @Override + public String getTitle() { + return bossBar.getTitle(); + } + + @Override + public void setTitle(String title) { + bossBar.setTitle(title); + } + + @Override + public double getProgress() { + return bossBar.getProgress(); + } + + @Override + public void setProgress(double progress) { + bossBar.setProgress(progress); + } + + @Override + public BarColor getColor() { + return bossBar.getColor(); + } + + @Override + public void setColor(BarColor color) { + bossBar.setColor(color); + } + + @Override + public BarStyle getStyle() { + return bossBar.getStyle(); + } + + @Override + public void setStyle(BarStyle style) { + bossBar.setStyle(style); + } + + @Override + public boolean hasFlag(BarFlag flag) { + return bossBar.hasFlag(flag); + } + + @Override + public void addFlag(BarFlag flag) { + bossBar.addFlag(flag); + } + + @Override + public void removeFlag(BarFlag flag) { + bossBar.removeFlag(flag); + } + + @Override + public boolean isVisible() { + return bossBar.isVisible(); + } + + @Override + public void setVisible(boolean visible) { + bossBar.setVisible(visible); + } + + @Override + public Region getRegion() { + return region; + } + + @EventHandler + public void onPlayerMove(PlayerMoveEvent event) { + if (event.getPlayer() != player) return; + if (region.inRegion(event.getTo(), RegionType.NORMAL, RegionExtensionType.NORMAL)) { + bossBar.addPlayer(player); + } else { + bossBar.removePlayer(player); + } + } + + @EventHandler + public void onPlayerQuit(PlayerQuitEvent event) { + if (event.getPlayer() != player) return; + cleanup(); + } + + @Override + public void cleanup() { + if (bossBar != null) { + bossBar.removeAll(); + bossBar = null; + } + region = null; + player = null; + + PlayerMoveEvent.getHandlerList().unregister(this); + PlayerQuitEvent.getHandlerList().unregister(this); + } +}