diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/hullhider/HullCalc.java b/BauSystem_Main/src/de/steamwar/bausystem/features/hullhider/HullCalc.java new file mode 100644 index 00000000..6510df16 --- /dev/null +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/hullhider/HullCalc.java @@ -0,0 +1,310 @@ +/* + * 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.hullhider; + +import de.steamwar.bausystem.region.Point; +import de.steamwar.bausystem.region.Region; +import de.steamwar.entity.REntity; +import de.steamwar.entity.REntityServer; +import de.steamwar.entity.RFallingBlockEntity; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.data.type.Slab; +import org.bukkit.entity.Player; + +import java.util.HashSet; +import java.util.Set; + +public class HullCalc { + + + + private static final World WORLD = Bukkit.getWorlds().get(0); + + private static final int[] c1 = new int[]{1, -1, 0, 0}; + private static final int[] c2 = new int[]{0, 0, 1, -1}; + + private int minX, minY, minZ, maxX, maxY, maxZ; + private REntityServer entityServer = new REntityServer(); + + private Set nonTechHideBlock = new HashSet<>(); + private Set hullBlocks = new HashSet<>(); + + public HullCalc(Region region) { + this.minX = region.getMinPointBuild().getX(); + this.minY = region.getMinPointBuild().getY(); + this.minZ = region.getMinPointBuild().getZ(); + this.maxX = region.getMaxPointBuild().getX(); + this.maxY = region.getMaxPointBuild().getY(); + this.maxZ = region.getMaxPointBuild().getZ(); + } + + public void calc() { + entityServer.getEntities().forEach(REntity::die); + long time = System.currentTimeMillis(); + hullBlocks.clear(); + + calc(minX, minY, minZ, maxX, maxY, minZ, 0, 0, 1, maxZ - minZ); + calc(minX, minY, maxZ, maxX, maxY, maxZ, 0, 0, -1, maxZ - minZ); + calc(minX, minY, minZ, minX, maxY, maxZ, 1, 0, 0, maxX - minX); + calc(maxX, minY, minZ, maxX, maxY, maxZ, -1, 0, 0, maxX - minX); + calc(minX, minY, minZ, maxX, minY, maxZ, 0, 1, 0, maxY - minY); + calc(minX, maxY, minZ, maxX, maxY, maxZ, 0, -1, 0, maxY - minY); + + hullBlocks.removeIf(point -> { + return point.getX() < minX || point.getX() > maxX || point.getY() < minY || point.getY() > maxY || point.getZ() < minZ || point.getZ() > maxZ; + }); + nonTechHideBlock.addAll(hullBlocks); + + System.out.println(System.currentTimeMillis() - time + "ms " + hullBlocks.size()); + + hullBlocks.forEach(point -> { + RFallingBlockEntity rFallingBlockEntity = new RFallingBlockEntity(entityServer, point.toLocation(WORLD).add(0.5, 0, 0.5), Material.WHITE_STAINED_GLASS); + rFallingBlockEntity.setNoGravity(true); + }); + } + + private void calc(int minX, int minY, int minZ, int maxX, int maxY, int maxZ, int dirX, int dirY, int dirZ, int steps) { + boolean sideWays = dirY == 0; + Set points = new HashSet<>(); + for (int x = minX; x <= maxX; x++) { + for (int y = minY; y <= maxY; y++) { + for (int z = minZ; z <= maxZ; z++) { + points.add(new Point(dirX == 0 ? x : minX - dirX, dirY == 0 ? y : minY - dirY, dirZ == 0 ? z : minZ - dirZ)); + } + } + } + for (int i = 0; i <= steps; i++) { + Set nextLayer = new HashSet<>(); + + for (Point point : points) { + Point next = point.add(dirX, dirY, dirZ); + if (!empty(next, sideWays)) { + hullBlocks.add(next); + + if (dirX != 0) { + for (int c = 0; c < 4; c++) { + if (empty(point.add(0, c1[c], c2[c]), sideWays) && empty(point.add(dirX, c1[c], c2[c]), sideWays)) { + nextLayer.add(point.add(dirX, c1[c], c2[c])); + } + } + } else if (dirY != 0) { + for (int c = 0; c < 4; c++) { + if (empty(point.add(c1[c], 0, c2[c]), sideWays) && empty(point.add(c1[c], dirY, c2[c]), sideWays)) { + nextLayer.add(point.add(c1[c], dirY, c2[c])); + } + } + } else { + for (int c = 0; c < 4; c++) { + if (empty(point.add(c1[c], c2[c], 0), sideWays) && empty(point.add(c1[c], c2[c], dirZ), sideWays)) { + nextLayer.add(point.add(c1[c], c2[c], dirZ)); + } + } + } + } else { + nextLayer.add(next); + nonTechHideBlock.add(next); + + if (dirX != 0) { + for (int c = 0; c < 4; c++) { + if (!empty(point.add(0, c1[c], c2[c]), sideWays)) { + hullBlocks.add(point.add(0, c1[c], c2[c])); + } + if (empty(point.add(dirX, c1[c], c2[c]), sideWays)) { + nextLayer.add(point.add(dirX, c1[c], c2[c])); + } else { + hullBlocks.add(point.add(dirX, c1[c], c2[c])); + } + } + } else if (dirY != 0) { + for (int c = 0; c < 4; c++) { + if (!empty(point.add(c1[c], 0, c2[c]), sideWays)) { + hullBlocks.add(point.add(c1[c], 0, c2[c])); + } + if (empty(point.add(c1[c], dirY, c2[c]), sideWays)) { + nextLayer.add(point.add(c1[c], dirY, c2[c])); + } else { + hullBlocks.add(point.add(c1[c], dirY, c2[c])); + } + } + } else { + for (int c = 0; c < 4; c++) { + if (!empty(point.add(c1[c], c2[c], 0), sideWays)) { + hullBlocks.add(point.add(c1[c], c2[c], 0)); + } + if (empty(point.add(c1[c], c2[c], dirZ), sideWays)) { + nextLayer.add(point.add(c1[c], c2[c], dirZ)); + } else { + hullBlocks.add(point.add(c1[c], c2[c], dirZ)); + } + } + } + } + } + points.clear(); + for (Point point : nextLayer) { + if (point.getX() < this.minX || point.getX() > this.maxX) { + continue; + } + if (point.getY() < this.minY || point.getY() > this.maxY) { + continue; + } + if (point.getZ() < this.minZ || point.getZ() > this.maxZ) { + continue; + } + points.add(point); + } + if (points.isEmpty()) { + break; + } + } + } + + private boolean empty(Point point, boolean side) { + Block block = point.toLocation(WORLD).getBlock(); + if (block.getBlockData() instanceof Slab slab && slab.getType() == Slab.Type.DOUBLE) { + return false; + } + Material material = block.getType(); + if (material.isAir()) { + return true; + } + if (material == Material.WATER) { + return true; + } + if (material == Material.SCAFFOLDING) { + return true; + } + if (material == Material.LADDER) { + return true; + } + if (material == Material.COBWEB) { + return true; + } + if (material == Material.IRON_BARS) { + return true; + } + if (material == Material.PLAYER_HEAD || material == Material.PLAYER_WALL_HEAD) { + return true; + } + if (material == Material.END_ROD) { + return true; + } + if (material == Material.CHAIN) { + return true; + } + if (material == Material.LANTERN) { + return true; + } + if (material == Material.LIGHTNING_ROD) { + return true; + } + if (material == Material.SOUL_LANTERN) { + return true; + } + if (material == Material.VINE) { + return true; + } + if (material == Material.SCULK_VEIN) { + return true; + } + if (material == Material.LEVER) { + return true; + } + if (material == Material.END_STONE_BRICK_WALL) { + return false; + } + if (material == Material.END_STONE_BRICK_SLAB) { + return false; + } + if (material == Material.END_STONE_BRICK_STAIRS) { + return false; + } + String name = material.name(); + if (name.contains("GLASS")) { + return true; + } + if (name.contains("BANNER")) { + return true; + } + if (side && name.contains("CARPET")) { + return true; + } + if (name.contains("DOOR")) { + return true; + } + if (name.contains("SIGN")) { + return true; + } + if (name.contains("FENCE_GATE")) { + return true; + } + if (name.endsWith("_FENCE")) { + return true; + } + if (name.endsWith("_SKULL")) { + return true; + } + if (name.endsWith("_WALL")) { + return true; + } + if (side && name.endsWith("_SLAB")) { + return true; + } + if (name.endsWith("_STAIRS")) { + return true; + } + if (name.endsWith("_CANDLE")) { + return true; + } + if (name.endsWith("_BUTTON")) { + return true; + } + return false; + } + + public void show(Player player) { + entityServer.addPlayer(player); + } + + public void set() { + hullBlocks.forEach(point -> { + point.toLocation(WORLD).getBlock().setType(Material.END_STONE); + }); + entityServer.getPlayers().forEach(player -> { + entityServer.removePlayer(player); + }); + } + + public void hide() { + for (int x = minX; x <= maxX; x++) { + for (int y = minY; y <= maxY; y++) { + for (int z = minZ; z <= maxZ; z++) { + if (nonTechHideBlock.contains(new Point(x, y, z))) { + continue; + } + WORLD.getBlockAt(x, y, z).setType(Material.END_STONE); + } + } + } + } +} diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/hullhider/HullCommand.java b/BauSystem_Main/src/de/steamwar/bausystem/features/hullhider/HullCommand.java new file mode 100644 index 00000000..3ff4cddf --- /dev/null +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/hullhider/HullCommand.java @@ -0,0 +1,67 @@ +/* + * 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.hullhider; + +import de.steamwar.bausystem.region.Region; +import de.steamwar.command.SWCommand; +import de.steamwar.linkage.Linked; +import org.bukkit.entity.Player; + +import java.util.HashMap; +import java.util.Map; + +@Linked +public class HullCommand extends SWCommand { + + private Map hullhiderMap = new HashMap<>(); + + public HullCommand() { + super("hullhider"); + } + + @Register + public void toggle(Player p) { + Region region = Region.getRegion(p.getLocation()); + if (hullhiderMap.containsKey(region)) { + Hullhider hullhider = hullhiderMap.get(region); + if (hullhider.togglePlayer(p)) { + hullhider.close(); + hullhiderMap.remove(region); + } + } else { + Hullhider hullhider = new Hullhider(region.getMinPointTestblock().getX(), region.getMaxPointTestblock().getX(), + region.getMinPointTestblock().getY(), region.getMaxPointTestblock().getY(), + region.getMinPointTestblock().getZ(), region.getMaxPointTestblock().getZ()); + hullhider.init(region.getFloorLevel() == 0); + hullhider.togglePlayer(p); + hullhiderMap.put(region, hullhider); + } + } + + @Register("recalc") + public void recalc(Player p) { + Region region = Region.getRegion(p.getLocation()); + if (hullhiderMap.containsKey(region)) { + Hullhider hullhider = hullhiderMap.get(region); + hullhider.close(); + hullhider.init(region.getFloorLevel() == 0); + } + } +} diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/hullhider/HullDir.java b/BauSystem_Main/src/de/steamwar/bausystem/features/hullhider/HullDir.java new file mode 100644 index 00000000..5503b1a2 --- /dev/null +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/hullhider/HullDir.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.features.hullhider; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Getter +public enum HullDir { + UP(0, 1, 0, false), + DOWN(0, -1, 0, false), + NORTH(0, 0, -1, true), + SOUTH(0, 0, 1, true), + EAST(1, 0, 0, true), + WEST(-1, 0, 0, true); + + private final int dx; + private final int dy; + private final int dz; + private final boolean sideWays; + + private static final HullDir[] Y = new HullDir[]{NORTH, SOUTH, EAST, WEST}; + private static final HullDir[] Z = new HullDir[]{UP, DOWN, EAST, WEST}; + private static final HullDir[] X = new HullDir[]{UP, DOWN, NORTH, SOUTH}; + + public HullDir[] getSurrounding() { + return switch (this) { + case UP, DOWN -> Y; + case NORTH, SOUTH -> Z; + case EAST, WEST -> X; + }; + } +} diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/hullhider/Hullhider.java b/BauSystem_Main/src/de/steamwar/bausystem/features/hullhider/Hullhider.java new file mode 100644 index 00000000..42ba6daa --- /dev/null +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/hullhider/Hullhider.java @@ -0,0 +1,452 @@ +/* + * 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.hullhider; + +import de.steamwar.bausystem.BauSystem; +import de.steamwar.bausystem.region.Point; +import de.steamwar.bausystem.utils.TickEndEvent; +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.data.BlockData; +import org.bukkit.block.data.type.Slab; +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.BlockBreakEvent; +import org.bukkit.event.entity.EntityExplodeEvent; + +import java.util.*; + +public class Hullhider implements Listener { + + // TODO: + // - Slab y calc diagonal + // - Carpet y calc diagonal upwards only! + + private static final World WORLD = Bukkit.getWorlds().get(0); + + private final int minX; + private final int maxX; + private final int minY; + private final int maxY; + private final int minZ; + private final int maxZ; + + private final int width; + private final int height; + private final int depth; + + private final Set players = new HashSet<>(); + + private final BitSet airBlocks; + private final BitSet hullDirections; + private final BitSet invisible; + + public Hullhider(int minX, int maxX, int minY, int maxY, int minZ, int maxZ) { + this.minX = minX; + this.maxX = maxX; + this.minY = minY; + this.maxY = maxY; + this.minZ = minZ; + this.maxZ = maxZ; + + this.width = maxX - minX + 1; + this.height = maxY - minY + 1; + this.depth = maxZ - minZ + 1; + + this.airBlocks = new BitSet(width * height * depth); + this.invisible = new BitSet(width * height * depth); + this.hullDirections = new BitSet(width * height * depth * 6); + } + + private int toIndex(int x, int y, int z) { + return (x - minX) * height * depth + (y - minY) * depth + (z - minZ); + } + + private int toIndex(Point point) { + return toIndex(point.getX(), point.getY(), point.getZ()); + } + + public void init(boolean noFloor) { + Bukkit.getPluginManager().registerEvents(this, BauSystem.getInstance()); + for (int x = minX; x <= maxX; x++) { + for (int y = minY; y <= maxY; y++) { + for (int z = minZ; z <= maxZ; z++) { + Point p = new Point(x, y, z); + invisible.set(toIndex(p)); + Block block = p.toLocation(WORLD).getBlock(); + if (block.isEmpty()) { + airBlocks.set(toIndex(p)); + } + } + } + } + + long time = System.currentTimeMillis(); + init(minX, minY, minZ, maxX, maxY, minZ, HullDir.SOUTH); + init(minX, minY, maxZ, maxX, maxY, maxZ, HullDir.NORTH); + init(minX, minY, minZ, minX, maxY, maxZ, HullDir.EAST); + init(maxX, minY, minZ, maxX, maxY, maxZ, HullDir.WEST); + if (noFloor) { + init(minX, minY, minZ, maxX, minY, maxZ, HullDir.UP); + } + init(minX, maxY, minZ, maxX, maxY, maxZ, HullDir.DOWN); + long timeDiff = System.currentTimeMillis() - time; + Bukkit.getOnlinePlayers().forEach(player -> { + player.sendMessage("Init took: " + timeDiff + "ms"); + }); + + BlockData endStone = Material.END_STONE.createBlockData(); + for (int x = minX; x <= maxX; x++) { + for (int y = minY; y <= maxY; y++) { + for (int z = minZ; z <= maxZ; z++) { + if (invisible.get(toIndex(x, y, z))) { + for (Player player : players) { + player.sendBlockChange(new Location(WORLD, x, y, z), endStone); + } + } + } + } + } + } + + private void init(int minX, int minY, int minZ, int maxX, int maxY, int maxZ, HullDir dir) { + Set points = new HashSet<>(); + for (int x = minX; x <= maxX; x++) { + for (int y = minY; y <= maxY; y++) { + for (int z = minZ; z <= maxZ; z++) { + points.add(new Point(dir.getDx() == 0 ? x : minX - dir.getDx(), dir.getDy() == 0 ? y : minY - dir.getDy(), dir.getDz() == 0 ? z : minZ - dir.getDz())); + } + } + } + calc(points, dir); + } + + private void calc(Set points, HullDir dir) { + while (true) { + Set nextLayer = new HashSet<>(); + + for (Point point : points) { + Point next = point.add(dir.getDx(), dir.getDy(), dir.getDz()); + + if (next.getX() >= minX && next.getX() <= maxX && next.getY() >= minY && next.getY() <= maxY && next.getZ() >= minZ && next.getZ() <= maxZ) { + invisible.clear(toIndex(next)); + } + if (!empty(next, dir)) { + if (next.getX() >= minX && next.getX() <= maxX && next.getY() >= minY && next.getY() <= maxY && next.getZ() >= minZ && next.getZ() <= maxZ) { + hullDirections.set(toIndex(next) * 6 + dir.ordinal()); + } + + for (HullDir hullDir : dir.getSurrounding()) { + Point p1 = point.add(hullDir.getDx(), hullDir.getDy(), hullDir.getDz()); + Point p2 = p1.add(dir.getDx(), dir.getDy(), dir.getDz()); + if (empty(p1, hullDir) && empty(p2, dir)) { + nextLayer.add(p2); + } + if (p1.getX() >= minX && p1.getX() <= maxX && p1.getY() >= minY && p1.getY() <= maxY && p1.getZ() >= minZ && p1.getZ() <= maxZ) { + invisible.clear(toIndex(p1)); + } + if (p2.getX() >= minX && p2.getX() <= maxX && p2.getY() >= minY && p2.getY() <= maxY && p2.getZ() >= minZ && p2.getZ() <= maxZ) { + invisible.clear(toIndex(p2)); + } + } + } else { + nextLayer.add(next); + + for (HullDir hullDir : dir.getSurrounding()) { + Point p1 = point.add(hullDir.getDx(), hullDir.getDy(), hullDir.getDz()); + Point p2 = p1.add(dir.getDx(), dir.getDy(), dir.getDz()); + if (empty(p2, dir)) { + nextLayer.add(p2); + } + if (p1.getX() >= minX && p1.getX() <= maxX && p1.getY() >= minY && p1.getY() <= maxY && p1.getZ() >= minZ && p1.getZ() <= maxZ) { + invisible.clear(toIndex(p1)); + } + if (p2.getX() >= minX && p2.getX() <= maxX && p2.getY() >= minY && p2.getY() <= maxY && p2.getZ() >= minZ && p2.getZ() <= maxZ) { + invisible.clear(toIndex(p2)); + } + } + } + } + + points.clear(); + for (Point point : nextLayer) { + if (point.getX() < minX || point.getX() > maxX) { + continue; + } + if (point.getY() < minY || point.getY() > maxY) { + continue; + } + if (point.getZ() < minZ || point.getZ() > maxZ) { + continue; + } + points.add(point); + } + if (points.isEmpty()) { + break; + } + } + } + + private boolean empty(Point point, HullDir dir) { + if (point.getX() >= minX && point.getX() <= maxX && point.getY() >= minY && point.getY() <= maxY && point.getZ() >= minZ && point.getZ() <= maxZ && airBlocks.get(toIndex(point))) { + return true; + } + Block block = point.toLocation(WORLD).getBlock(); + if (block.getBlockData() instanceof Slab slab && slab.getType() == Slab.Type.DOUBLE) { + return false; + } + Material material = block.getType(); + if (material.isAir()) { + return true; + } + if (material == Material.WATER) { + return true; + } + if (material == Material.SCAFFOLDING) { + return true; + } + if (material == Material.LADDER) { + return true; + } + if (material == Material.COBWEB) { + return true; + } + if (material == Material.IRON_BARS) { + return true; + } + if (material == Material.PLAYER_HEAD || material == Material.PLAYER_WALL_HEAD) { + return true; + } + if (material == Material.END_ROD) { + return true; + } + if (material == Material.CHAIN) { + return true; + } + if (material == Material.LANTERN) { + return true; + } + if (material == Material.LIGHTNING_ROD) { + return true; + } + if (material == Material.SOUL_LANTERN) { + return true; + } + if (material == Material.VINE) { + return true; + } + if (material == Material.SCULK_VEIN) { + return true; + } + if (material == Material.LEVER) { + return true; + } + if (material == Material.END_STONE_BRICK_WALL) { + return false; + } + if (material == Material.END_STONE_BRICK_SLAB) { + return false; + } + if (material == Material.END_STONE_BRICK_STAIRS) { + return false; + } + String name = material.name(); + if (name.contains("GLASS")) { + return true; + } + if (name.contains("BANNER")) { + return true; + } + if (dir.isSideWays() && name.contains("CARPET")) { + return true; + } + if (name.contains("DOOR")) { + return true; + } + if (name.contains("SIGN")) { + return true; + } + if (name.contains("FENCE_GATE")) { + return true; + } + if (name.endsWith("_FENCE")) { + return true; + } + if (name.endsWith("_SKULL")) { + return true; + } + if (name.endsWith("_WALL")) { + return true; + } + if (dir.isSideWays() && name.endsWith("_SLAB")) { + return true; + } + if (name.endsWith("_STAIRS")) { + return true; + } + if (name.endsWith("_CANDLE")) { + return true; + } + if (name.endsWith("_BUTTON")) { + return true; + } + return false; + } + + public void close() { + HandlerList.unregisterAll(this); + airBlocks.clear(); + invisible.clear(); + hullDirections.clear(); + + for (int x = minX; x <= maxX; x++) { + for (int y = minY; y <= maxY; y++) { + for (int z = minZ; z <= maxZ; z++) { + Block block = WORLD.getBlockAt(x, y, z); + players.forEach(player -> { + player.sendBlockChange(block.getLocation(), block.getBlockData()); + }); + } + } + } + } + + public boolean togglePlayer(Player player) { + if (players.contains(player)) { + players.remove(player); + + for (int x = minX; x <= maxX; x++) { + for (int y = minY; y <= maxY; y++) { + for (int z = minZ; z <= maxZ; z++) { + Block block = WORLD.getBlockAt(x, y, z); + player.sendBlockChange(block.getLocation(), block.getBlockData()); + } + } + } + return players.isEmpty(); + } else { + players.add(player); + + BlockData endStone = Material.END_STONE.createBlockData(); + for (int x = minX; x <= maxX; x++) { + for (int y = minY; y <= maxY; y++) { + for (int z = minZ; z <= maxZ; z++) { + if (invisible.get(toIndex(x, y, z))) { + player.sendBlockChange(new Location(WORLD, x, y, z), endStone); + } + } + } + } + return false; + } + } + + private Set pointsToReshow = new HashSet<>(); + private Map> changedInTick = new HashMap<>(); + + @EventHandler + public void onBlockBreak(BlockBreakEvent event) { + Point point = new Point(event.getBlock().getX(), event.getBlock().getY(), event.getBlock().getZ()); + airBlocks.set(toIndex(point)); + Set hullDirs = new HashSet<>(); + for (HullDir hullDir : HullDir.values()) { + if (hullDirections.get(toIndex(point) * 6 + hullDir.ordinal())) { + hullDirs.add(hullDir); + } + } + + pointsToReshow.add(point); + hullDirs.forEach(dir -> { + changedInTick.computeIfAbsent(dir, __ -> new HashSet<>()).add(point); + }); + } + + @EventHandler + public void onEntityExplode(EntityExplodeEvent event) { + for (Block block : event.blockList()) { + Point point = new Point(block.getX(), block.getY(), block.getZ()); + airBlocks.set(toIndex(point)); + pointsToReshow.add(point); + Set hullDirs = new HashSet<>(); + for (HullDir hullDir : HullDir.values()) { + if (hullDirections.get(toIndex(point) * 6 + hullDir.ordinal())) { + hullDirs.add(hullDir); + } + } + hullDirs.forEach(dir -> { + changedInTick.computeIfAbsent(dir, __ -> new HashSet<>()).add(point); + }); + } + } + + @EventHandler + public void onTickEnd(TickEndEvent event) { + if (!changedInTick.isEmpty()) { + BitSet oldInvisible = BitSet.valueOf(invisible.toLongArray()); + long time = System.currentTimeMillis(); + changedInTick.forEach((dir, points) -> { + calc(points, dir); + }); + long timeDiff = System.currentTimeMillis() - time; + players.forEach(player -> { + player.sendMessage("Calculated in " + timeDiff + "ms"); + }); + + for (int x = minX; x <= maxX; x++) { + for (int y = minY; y <= maxY; y++) { + for (int z = minZ; z <= maxZ; z++) { + if (invisible.get(toIndex(x, y, z))) continue; + if (!oldInvisible.get(toIndex(x, y, z))) continue; + Location location = new Location(WORLD, x, y, z); + BlockData blockData = location.getBlock().getBlockData(); + players.forEach(player -> { + player.sendBlockChange(location, blockData); + }); + } + } + } + changedInTick.clear(); + } + + if (!pointsToReshow.isEmpty()) { + BlockData endStone = Material.END_STONE.createBlockData(); + pointsToReshow.forEach(point -> { + for (int x = point.getX() - 1; x <= point.getX() + 1; x++) { + for (int y = point.getY() - 1; y <= point.getY() + 1; y++) { + for (int z = point.getZ() - 1; z <= point.getZ() + 1; z++) { + Location location = point.toLocation(WORLD); + if (x >= minX && x <= maxX && y >= minY && y <= maxY && z >= minZ && z <= maxZ && invisible.get(toIndex(x, y, z))) { + players.forEach(player -> { + player.sendBlockChange(location, endStone); + }); + } + } + } + } + }); + pointsToReshow.clear(); + } + } +}