From 33e9b3409f25a7cae5e834024e3df4e2cf7e03dc Mon Sep 17 00:00:00 2001 From: YoyoNow Date: Thu, 31 Jul 2025 16:21:08 +0200 Subject: [PATCH] Add SelectAdjacent for BauSystem and Builder --- .../features/worldedit/SelectAdjacent.java | 157 ++++++++++++++++++ .../de/steamwar/core/WorldEditRenderer.java | 7 + .../src/de/steamwar/teamserver/Builder.java | 6 + .../teamserver/listener/SelectAdjacent.java | 153 +++++++++++++++++ 4 files changed, 323 insertions(+) create mode 100644 BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/SelectAdjacent.java create mode 100644 Teamserver/src/de/steamwar/teamserver/listener/SelectAdjacent.java diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/SelectAdjacent.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/SelectAdjacent.java new file mode 100644 index 00000000..869af4c9 --- /dev/null +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/worldedit/SelectAdjacent.java @@ -0,0 +1,157 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2020 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.worldedit; + +import de.steamwar.bausystem.BauSystem; +import de.steamwar.bausystem.region.Point; +import de.steamwar.bausystem.utils.FlatteningWrapper; +import de.steamwar.core.WorldEditRenderer; +import de.steamwar.linkage.Linked; +import de.steamwar.linkage.MinVersion; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.scheduler.BukkitTask; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +@Linked +@MinVersion(20) +public class SelectAdjacent implements Listener { + + private BlockFace[] FACES = { + BlockFace.NORTH, + BlockFace.EAST, + BlockFace.SOUTH, + BlockFace.WEST, + BlockFace.UP, + BlockFace.DOWN + }; + + private Map selectors = new HashMap<>(); + + @EventHandler + public void onPlayerInteract(PlayerInteractEvent event) { + if (!event.hasItem()) return; + if (event.getItem().getType() != Material.WOODEN_AXE) return; + if (!event.getPlayer().isSneaking()) return; + if (event.getAction() != Action.LEFT_CLICK_BLOCK) return; + Selector selector = selectors.get(event.getPlayer()); + if (selector != null) selector.cancel(); + selector = new Selector(event.getClickedBlock(), event.getPlayer()); + selectors.put(event.getPlayer(), selector); + } + + @EventHandler + public void onPlayerQuit(PlayerQuitEvent event) { + Selector selector = selectors.remove(event.getPlayer()); + if (selector != null) selector.cancel(); + } + + private class Selector { + + private static final int MAX_BLOCKS = 1_000_000; + + private int minX; + private int minY; + private int minZ; + private int maxX; + private int maxY; + private int maxZ; + + private BukkitTask bukkitTask; + private Set seen = new HashSet<>(); + private Set toCalc = new HashSet<>(); + + public Selector(Block block, Player player) { + toCalc.add(block.getLocation()); + minX = block.getX(); + minY = block.getY(); + minZ = block.getZ(); + maxX = block.getX(); + maxY = block.getY(); + maxZ = block.getZ(); + + bukkitTask = Bukkit.getScheduler().runTaskTimer(BauSystem.getInstance(), () -> { + run(); + + long volume = (long)(maxX - minX + 1) * (long)(maxY - minY + 1) * (long)(maxZ - minZ + 1); + player.sendTitle("", "§e" + volume + " §7Blocks", 0, 5, 0); + + Point minPoint = new Point(minX, minY, minZ); + Point maxPoint = new Point(maxX, maxY, maxZ); + + FlatteningWrapper.impl.setSelection(player, minPoint, maxPoint); + WorldEditRenderer.renderPlayer(player); + + boolean finished = toCalc.stream().allMatch(location -> { + return location.getBlockX() >= minX && location.getBlockY() >= minY && location.getBlockZ() >= minZ && + location.getBlockX() <= maxX && location.getBlockY() <= maxY && location.getBlockZ() <= maxZ; + }); + + if (finished || seen.size() > MAX_BLOCKS) { + bukkitTask.cancel(); + player.sendTitle("§aDone", "§e" + volume + " §7Blocks", 0, 20, 5); + } + }, 1, 1); + } + + private void cancel() { + bukkitTask.cancel(); + } + + private void run() { + Set current = toCalc; + toCalc = new HashSet<>(); + + for (Location location : current) { + Block block = location.getBlock(); + if (block.isEmpty() || block.isLiquid()) continue; + seen.add(location); + + minX = Math.min(minX, location.getBlockX()); + maxX = Math.max(maxX, location.getBlockX()); + minY = Math.min(minY, location.getBlockY()); + maxY = Math.max(maxY, location.getBlockY()); + minZ = Math.min(minZ, location.getBlockZ()); + maxZ = Math.max(maxZ, location.getBlockZ()); + + for (BlockFace face : FACES) { + Block next = block.getRelative(face); + if (next.isEmpty() || next.isLiquid()) continue; + Location loc = next.getLocation(); + if (seen.contains(loc)) continue; + toCalc.add(loc); + } + } + } + } +} diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/core/WorldEditRenderer.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/core/WorldEditRenderer.java index 225053a2..79c54986 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/core/WorldEditRenderer.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/core/WorldEditRenderer.java @@ -39,11 +39,14 @@ import org.bukkit.util.Vector; public class WorldEditRenderer implements Listener { + private static WorldEditRenderer INSTANCE; + private static final Material WAND = FlatteningWrapper.impl.getMaterial("WOOD_AXE"); private final WorldEditPlugin we; public WorldEditRenderer() { + INSTANCE = this; we = WorldEditWrapper.getWorldEditPlugin(); Bukkit.getPluginManager().registerEvents(this, Core.getInstance()); @@ -54,6 +57,10 @@ public class WorldEditRenderer implements Listener { }, 20, 20); } + public static void renderPlayer(Player player) { + WorldEditRenderer.INSTANCE.renderPlayer(player, false); + } + private void renderPlayer(Player player, boolean scheduled) { LocalSession session = we.getSession(player); renderClipboard(player, session, scheduled); diff --git a/Teamserver/src/de/steamwar/teamserver/Builder.java b/Teamserver/src/de/steamwar/teamserver/Builder.java index 55aeed2d..94dadf8a 100644 --- a/Teamserver/src/de/steamwar/teamserver/Builder.java +++ b/Teamserver/src/de/steamwar/teamserver/Builder.java @@ -19,12 +19,14 @@ package de.steamwar.teamserver; +import de.steamwar.core.Core; import de.steamwar.core.WorldEditRendererCUIEditor; import de.steamwar.message.Message; import de.steamwar.teamserver.command.*; import de.steamwar.teamserver.listener.AxiomHandshakeListener; import de.steamwar.teamserver.listener.FreezeListener; import de.steamwar.teamserver.listener.PlayerChange; +import de.steamwar.teamserver.listener.SelectAdjacent; import org.bukkit.Bukkit; import org.bukkit.GameRule; import org.bukkit.plugin.java.JavaPlugin; @@ -60,6 +62,10 @@ public final class Builder extends JavaPlugin { Bukkit.getWorlds().get(0).setGameRule(GameRule.REDUCED_DEBUG_INFO, false); new WorldEditRendererCUIEditor(); + + if (Core.getVersion() >= 20) { + Bukkit.getPluginManager().registerEvents(new SelectAdjacent(), this); + } } @Override diff --git a/Teamserver/src/de/steamwar/teamserver/listener/SelectAdjacent.java b/Teamserver/src/de/steamwar/teamserver/listener/SelectAdjacent.java new file mode 100644 index 00000000..06fb8907 --- /dev/null +++ b/Teamserver/src/de/steamwar/teamserver/listener/SelectAdjacent.java @@ -0,0 +1,153 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2020 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.teamserver.listener; + +import com.sk89q.worldedit.bukkit.BukkitWorld; +import com.sk89q.worldedit.bukkit.WorldEditPlugin; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.regions.selector.CuboidRegionSelector; +import com.sk89q.worldedit.world.World; +import de.steamwar.core.WorldEditRenderer; +import de.steamwar.teamserver.Builder; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.scheduler.BukkitTask; + +import java.util.*; + +public class SelectAdjacent implements Listener { + + private BlockFace[] FACES = { + BlockFace.NORTH, + BlockFace.EAST, + BlockFace.SOUTH, + BlockFace.WEST, + BlockFace.UP, + BlockFace.DOWN + }; + + private Map selectors = new HashMap<>(); + + @EventHandler + public void onPlayerInteract(PlayerInteractEvent event) { + if (!event.hasItem()) return; + if (event.getItem().getType() != Material.WOODEN_AXE) return; + if (!event.getPlayer().isSneaking()) return; + if (event.getAction() != Action.LEFT_CLICK_BLOCK) return; + Selector selector = selectors.get(event.getPlayer()); + if (selector != null) selector.cancel(); + selector = new Selector(event.getClickedBlock(), event.getPlayer()); + selectors.put(event.getPlayer(), selector); + } + + @EventHandler + public void onPlayerQuit(PlayerQuitEvent event) { + Selector selector = selectors.remove(event.getPlayer()); + if (selector != null) selector.cancel(); + } + + private static final WorldEditPlugin WORLDEDIT_PLUGIN = Objects.requireNonNull((WorldEditPlugin) Bukkit.getPluginManager().getPlugin("WorldEdit")); + private static final World BUKKITWORLD = new BukkitWorld(Bukkit.getWorlds().get(0)); + + private class Selector { + + private static final int MAX_BLOCKS = 1_000_000; + + private int minX; + private int minY; + private int minZ; + private int maxX; + private int maxY; + private int maxZ; + + private BukkitTask bukkitTask; + private Set seen = new HashSet<>(); + private Set toCalc = new HashSet<>(); + + public Selector(Block block, Player player) { + toCalc.add(block.getLocation()); + minX = block.getX(); + minY = block.getY(); + minZ = block.getZ(); + maxX = block.getX(); + maxY = block.getY(); + maxZ = block.getZ(); + + bukkitTask = Bukkit.getScheduler().runTaskTimer(Builder.getInstance(), () -> { + run(); + + long volume = (long)(maxX - minX + 1) * (long)(maxY - minY + 1) * (long)(maxZ - minZ + 1); + player.sendTitle("", "§e" + volume + " §7Blocks", 0, 5, 0); + + WORLDEDIT_PLUGIN.getSession(player).setRegionSelector(BUKKITWORLD, new CuboidRegionSelector(BUKKITWORLD, BlockVector3.at(minX, minY, minZ), BlockVector3.at(maxX, maxY, maxZ))); + WorldEditRenderer.renderPlayer(player); + + boolean finished = toCalc.stream().allMatch(location -> { + return location.getBlockX() >= minX && location.getBlockY() >= minY && location.getBlockZ() >= minZ && + location.getBlockX() <= maxX && location.getBlockY() <= maxY && location.getBlockZ() <= maxZ; + }); + + if (finished || seen.size() > MAX_BLOCKS) { + bukkitTask.cancel(); + player.sendTitle("§aDone", "§e" + volume + " §7Blocks", 0, 20, 5); + } + }, 1, 1); + } + + private void cancel() { + bukkitTask.cancel(); + } + + private void run() { + Set current = toCalc; + toCalc = new HashSet<>(); + + for (Location location : current) { + Block block = location.getBlock(); + if (block.isEmpty() || block.isLiquid()) continue; + seen.add(location); + + minX = Math.min(minX, location.getBlockX()); + maxX = Math.max(maxX, location.getBlockX()); + minY = Math.min(minY, location.getBlockY()); + maxY = Math.max(maxY, location.getBlockY()); + minZ = Math.min(minZ, location.getBlockZ()); + maxZ = Math.max(maxZ, location.getBlockZ()); + + for (BlockFace face : FACES) { + Block next = block.getRelative(face); + if (next.isEmpty() || next.isLiquid()) continue; + Location loc = next.getLocation(); + if (seen.contains(loc)) continue; + toCalc.add(loc); + } + } + } + } +}