diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/cuboid/Cuboid2Command.java b/BauSystem_Main/src/de/steamwar/bausystem/features/cuboid/Cuboid2Command.java
new file mode 100644
index 00000000..68464b3f
--- /dev/null
+++ b/BauSystem_Main/src/de/steamwar/bausystem/features/cuboid/Cuboid2Command.java
@@ -0,0 +1,72 @@
+/*
+ * 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.cuboid;
+
+import de.steamwar.bausystem.shared.Pair;
+import de.steamwar.bausystem.utils.WorldEditUtils;
+import de.steamwar.command.SWCommand;
+import de.steamwar.linkage.Linked;
+import org.bukkit.Location;
+import org.bukkit.entity.Player;
+import org.bukkit.util.Vector;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.zip.GZIPOutputStream;
+
+@Linked
+public class Cuboid2Command extends SWCommand {
+
+ public Cuboid2Command() {
+ super("cuboid");
+ }
+
+ @Register
+ public void command(Player player) {
+ Pair selection = WorldEditUtils.getSelection(player);
+ if (selection == null) {
+ player.sendMessage("§cDu musst erst eine Auswahl treffen!");
+ return;
+ }
+ Location min = selection.getKey();
+ Location max = selection.getValue();
+
+ Vector minVec = new Vector(Math.min(min.getX(), max.getX()), Math.min(min.getY(), max.getY()), Math.min(min.getZ(), max.getZ()));
+ Vector maxVec = new Vector(Math.max(min.getX(), max.getX()), Math.max(min.getY(), max.getY()), Math.max(min.getZ(), max.getZ()));
+
+ CuboidSchematicWriter cuboidSchematicWriter = new CuboidSchematicWriter(minVec, maxVec);
+ cuboidSchematicWriter.create(player, cuboidSchematic -> {
+ AtomicInteger counter = new AtomicInteger();
+ try {
+ cuboidSchematic.write(new GZIPOutputStream(new OutputStream() {
+ @Override
+ public void write(int b) throws IOException {
+ counter.incrementAndGet();
+ }
+ }));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ player.sendMessage("§aDie Schematic ist " + counter.get() + " Bytes groß!");
+ System.out.println(cuboidSchematic);
+ });
+ }
+}
diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/cuboid/CuboidCommand.java b/BauSystem_Main/src/de/steamwar/bausystem/features/cuboid/CuboidCommand.java
new file mode 100644
index 00000000..6f33f813
--- /dev/null
+++ b/BauSystem_Main/src/de/steamwar/bausystem/features/cuboid/CuboidCommand.java
@@ -0,0 +1,200 @@
+/*
+ * 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.cuboid;
+
+import de.steamwar.bausystem.BauSystem;
+import de.steamwar.bausystem.shared.Pair;
+import de.steamwar.bausystem.utils.WorldEditUtils;
+import de.steamwar.command.SWCommand;
+import lombok.ToString;
+import org.bukkit.Axis;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.scheduler.BukkitRunnable;
+import org.bukkit.util.Vector;
+
+import java.util.*;
+import java.util.function.Supplier;
+
+// @Linked
+public class CuboidCommand extends SWCommand {
+
+ public CuboidCommand() {
+ super("cuboid");
+ }
+
+ @Register
+ public void genericCommand(Player player) {
+ Pair selection = WorldEditUtils.getSelection(player);
+ if (selection == null) {
+ player.sendMessage("§cDu musst erst eine Auswahl treffen!");
+ return;
+ }
+ Location min = selection.getKey();
+ Location max = selection.getValue();
+
+ Vector minVec = new Vector(Math.min(min.getX(), max.getX()), Math.min(min.getY(), max.getY()), Math.min(min.getZ(), max.getZ()));
+ Vector maxVec = new Vector(Math.max(min.getX(), max.getX()), Math.max(min.getY(), max.getY()), Math.max(min.getZ(), max.getZ()));
+
+ Map blocks = new HashMap<>();
+ for (int x = min.getBlockX(); x <= max.getBlockX(); x++) {
+ for (int y = min.getBlockY(); y <= max.getBlockY(); y++) {
+ for (int z = min.getBlockZ(); z <= max.getBlockZ(); z++) {
+ blocks.put(new Vector(x, y, z), player.getWorld().getBlockAt(x, y, z).getType());
+ }
+ }
+ }
+
+ List left = new ArrayList<>();
+ left.add(minVec);
+
+ long time = System.currentTimeMillis();
+ List cuboids = new ArrayList<>();
+ runTickEfficient(left::isEmpty, () -> {
+ System.out.println(left.size());
+ Vector current = left.remove(0);
+ if (current.getX() > maxVec.getX() || current.getY() > maxVec.getY() || current.getZ() > maxVec.getZ()) {
+ return;
+ }
+
+ BlockCuboid cuboid = new BlockCuboid(current, blocks.get(current));
+ expand(cuboid, maxVec, blocks);
+ cuboids.add(cuboid);
+
+ left.add(new Vector(cuboid.min.getX(), cuboid.min.getY(), cuboid.max.getZ() + 1));
+ left.add(new Vector(cuboid.min.getX(), cuboid.max.getY() + 1, cuboid.min.getZ()));
+ left.add(new Vector(cuboid.max.getX() + 1, cuboid.min.getY(), cuboid.min.getZ()));
+ }, () -> {
+ System.out.println((System.currentTimeMillis() - time) + " ms " + cuboids);
+ });
+ }
+
+ private void runTickEfficient(Supplier finished, Runnable next, Runnable finishedCallback) {
+ new BukkitRunnable() {
+ @Override
+ public void run() {
+ if (finished.get()) {
+ cancel();
+ finishedCallback.run();
+ return;
+ }
+ long time = System.currentTimeMillis();
+ while (System.currentTimeMillis() - time < 50) {
+ if (finished.get()) {
+ cancel();
+ finishedCallback.run();
+ return;
+ }
+ next.run();
+ }
+ }
+ }.runTaskTimer(BauSystem.getInstance(), 0, 1);
+ }
+
+ @ToString
+ private static class BlockCuboid {
+ private final Vector min;
+ private final Vector max;
+ private final Material material;
+
+ public BlockCuboid(Vector min, Material material) {
+ this.min = min.clone();
+ this.max = min.clone();
+ this.material = material;
+ }
+ }
+
+ private void expand(BlockCuboid cuboid, Vector max, Map blocks) {
+ List axes = new ArrayList<>();
+ axes.add(Axis.X);
+ axes.add(Axis.Y);
+ axes.add(Axis.Z);
+ do {
+ Vector sizes = cuboid.max.clone().subtract(cuboid.min).add(new Vector(1, 1, 1));
+ axes.sort(Comparator.comparing(axis -> {
+ switch (axis) {
+ case X:
+ return sizes.getX();
+ case Y:
+ return sizes.getY();
+ case Z:
+ return sizes.getZ();
+ default:
+ return 0.0;
+ }
+ }));
+ axes.removeIf(axis -> {
+ Vector direction = new Vector();
+ switch (axis) {
+ case X:
+ direction.setX(1);
+ break;
+ case Y:
+ direction.setY(1);
+ break;
+ case Z:
+ direction.setZ(1);
+ break;
+ }
+ return !expand(cuboid, max, direction, blocks);
+ });
+ } while (!axes.isEmpty());
+ }
+
+ private boolean expand(BlockCuboid cuboid, Vector max, Vector direction, Map blocks) {
+ if (direction.getX() == 1) {
+ if (cuboid.max.getX() + 1 > max.getX()) {
+ return false;
+ }
+ for (double y = cuboid.min.getY(); y <= cuboid.max.getY(); y++) {
+ for (double z = cuboid.min.getZ(); z <= cuboid.max.getZ(); z++) {
+ if (blocks.get(new Vector(cuboid.max.getX() + 1, y, z)) != cuboid.material) {
+ return false;
+ }
+ }
+ }
+ } else if (direction.getY() == 1) {
+ if (cuboid.max.getY() + 1 > max.getY()) {
+ return false;
+ }
+ for (double x = cuboid.min.getX(); x <= cuboid.max.getX(); x++) {
+ for (double z = cuboid.min.getZ(); z <= cuboid.max.getZ(); z++) {
+ if (blocks.get(new Vector(x, cuboid.max.getY() + 1, z)) != cuboid.material) {
+ return false;
+ }
+ }
+ }
+ } else if (direction.getZ() == 1) {
+ if (cuboid.max.getZ() + 1 > max.getZ()) {
+ return false;
+ }
+ for (double x = cuboid.min.getX(); x <= cuboid.max.getX(); x++) {
+ for (double y = cuboid.min.getY(); y <= cuboid.max.getY(); y++) {
+ if (blocks.get(new Vector(x, y, cuboid.max.getZ() + 1)) != cuboid.material) {
+ return false;
+ }
+ }
+ }
+ }
+ cuboid.max.add(direction);
+ return true;
+ }
+}
diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/cuboid/CuboidSchematic.java b/BauSystem_Main/src/de/steamwar/bausystem/features/cuboid/CuboidSchematic.java
new file mode 100644
index 00000000..5505d15a
--- /dev/null
+++ b/BauSystem_Main/src/de/steamwar/bausystem/features/cuboid/CuboidSchematic.java
@@ -0,0 +1,94 @@
+/*
+ * 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.cuboid;
+
+import com.sk89q.jnbt.*;
+import lombok.Getter;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Getter
+public class CuboidSchematic {
+ private Pos offset;
+ private Pos size;
+ private Map> cuboids = new HashMap<>();
+
+ public CuboidSchematic(Pos offset, Pos size) {
+ this.offset = offset;
+ this.size = size;
+ }
+
+ public void addCuboid(TypedCuboid cuboid) {
+ cuboids.computeIfAbsent(cuboid.blockData, s -> new ArrayList<>()).add(cuboid);
+ }
+
+ public void write(OutputStream outputStream) {
+ Map schematic = new HashMap<>();
+ schematic.put("OffsetX", new IntTag(offset.x));
+ schematic.put("OffsetY", new IntTag(offset.y));
+ schematic.put("OffsetZ", new IntTag(offset.z));
+ schematic.put("SizeX", new IntTag(size.x));
+ schematic.put("SizeY", new IntTag(size.y));
+ schematic.put("SizeZ", new IntTag(size.z));
+
+ Map cuboidMap = new HashMap<>();
+ for (Map.Entry> entry : cuboids.entrySet()) {
+ byte[] cuboidArray = new byte[entry.getValue().size() * 15];
+ for (int i = 0; i < entry.getValue().size(); i++) {
+ TypedCuboid cuboid = entry.getValue().get(i);
+ List bytes = new ArrayList<>();
+ writeVarInt(cuboid.x, bytes);
+ writeVarInt(cuboid.y, bytes);
+ writeVarInt(cuboid.z, bytes);
+ writeVarInt(cuboid.dx, bytes);
+ writeVarInt(cuboid.dy, bytes);
+ writeVarInt(cuboid.dz, bytes);
+ }
+ cuboidMap.put(entry.getKey(), new ByteArrayTag(cuboidArray));
+ }
+ schematic.put("Cuboids", new CompoundTag(cuboidMap));
+
+ CompoundTag compoundTag = new CompoundTag(schematic);
+ try {
+ NBTOutputStream nbtOutputStream = new NBTOutputStream(outputStream);
+ nbtOutputStream.writeTag(compoundTag);
+ nbtOutputStream.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private static void writeVarInt(int value, List bytes) {
+ while (true) {
+ if (value < 128) {
+ bytes.add((byte) value);
+ return;
+ } else {
+ bytes.add((byte) (value & 127 | 128));
+ value >>>= 7;
+ }
+ }
+ }
+}
diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/cuboid/CuboidSchematicWriter.java b/BauSystem_Main/src/de/steamwar/bausystem/features/cuboid/CuboidSchematicWriter.java
new file mode 100644
index 00000000..7ca835c4
--- /dev/null
+++ b/BauSystem_Main/src/de/steamwar/bausystem/features/cuboid/CuboidSchematicWriter.java
@@ -0,0 +1,199 @@
+/*
+ * 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.cuboid;
+
+import de.steamwar.bausystem.BauSystem;
+import org.bukkit.Axis;
+import org.bukkit.entity.Player;
+import org.bukkit.scheduler.BukkitRunnable;
+import org.bukkit.util.Consumer;
+import org.bukkit.util.Vector;
+
+import java.util.*;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
+public class CuboidSchematicWriter {
+
+ private static final List AXES = Arrays.asList(
+ new Axis[]{Axis.X, Axis.Y, Axis.Z},
+ new Axis[]{Axis.X, Axis.Z, Axis.Y},
+ new Axis[]{Axis.Y, Axis.X, Axis.Z},
+ new Axis[]{Axis.Y, Axis.Z, Axis.X},
+ new Axis[]{Axis.Z, Axis.X, Axis.Y},
+ new Axis[]{Axis.Z, Axis.Y, Axis.X}
+ );
+
+ private Pos min;
+ private Pos max;
+
+ public CuboidSchematicWriter(Vector min, Vector max) {
+ this.min = new Pos(min);
+ this.max = new Pos(max);
+ }
+
+ public void create(Player player, Consumer consumer) {
+ // Setup
+ Pos playerPos = new Pos(player.getLocation());
+ Pos offset = min.sub(playerPos);
+ Map blocks = CuboidSchematicWriter.getBlocks(player, min, max);
+ Map> reverseBlocks = reverse(blocks);
+
+ // Create CuboidSchematic
+ CuboidSchematic schematic = new CuboidSchematic(offset, max.sub(min));
+
+ // Create Cuboids
+ long time = System.currentTimeMillis();
+ List left = new ArrayList<>();
+ left.add(new Pos(0, 0, 0));
+ runTickEfficient(left::isEmpty, () -> {
+ Pos current = left.remove(0);
+ String block = blocks.remove(current);
+ if (block == null) {
+ return;
+ }
+
+ TypedCuboid cuboid = null;
+ for (Axis[] axes : AXES) {
+ TypedCuboid currentCuboid = new TypedCuboid(current.x, current.y, current.z, block);
+ expand(currentCuboid, new ArrayList<>(Arrays.asList(axes)), reverseBlocks);
+ if (cuboid == null || currentCuboid.size() > cuboid.size()) {
+ cuboid = currentCuboid;
+ }
+ }
+ Set cuboidBlocks = reverseBlocks.get(block);
+ for (int x = cuboid.x; x < cuboid.x + cuboid.dx; x++) {
+ for (int y = cuboid.y; y < cuboid.y + cuboid.dy; y++) {
+ for (int z = cuboid.z; z < cuboid.z + cuboid.dz; z++) {
+ Pos pos = new Pos(x, y, z);
+ cuboidBlocks.remove(pos);
+ blocks.remove(pos);
+ }
+ }
+ }
+ schematic.addCuboid(cuboid);
+
+ left.add(new Pos(current.x + cuboid.dx + 1, current.y, current.z));
+ left.add(new Pos(current.x, current.y + cuboid.dy + 1, current.z));
+ left.add(new Pos(current.x, current.y, current.z + cuboid.dz + 1));
+ }, () -> {
+ long elapsed = System.currentTimeMillis() - time;
+ long cuboids = schematic.getCuboids().values().stream().mapToLong(List::size).sum();
+ System.out.println("Finished in " + elapsed + "ms with " + cuboids + " cuboids");
+ System.out.println("Average: " + (cuboids / elapsed) + " cuboids per ms");
+ System.out.println("Types: " + schematic.getCuboids().entrySet().stream().map(e -> e.getKey() + ": " + e.getValue().size()).collect(Collectors.joining(", ")));
+ consumer.accept(schematic);
+ });
+ }
+
+ private void runTickEfficient(Supplier finished, Runnable next, Runnable finishedCallback) {
+ new BukkitRunnable() {
+ @Override
+ public void run() {
+ long time = System.currentTimeMillis();
+ while (System.currentTimeMillis() - time < 50) {
+ if (finished.get()) {
+ cancel();
+ finishedCallback.run();
+ return;
+ }
+ next.run();
+ }
+ }
+ }.runTaskTimer(BauSystem.getInstance(), 0, 1);
+ }
+
+ private static Map getBlocks(Player player, Pos min, Pos max) {
+ Map blocks = new HashMap<>();
+ for (int x = min.x; x <= max.x; x++) {
+ for (int y = min.y; y <= max.y; y++) {
+ for (int z = min.z; z <= max.z; z++) {
+ blocks.put(new Pos(x - min.x, y - min.y, z - min.z), player.getWorld().getBlockAt(x, y, z).getBlockData().getAsString());
+ }
+ }
+ }
+ return blocks;
+ }
+
+ private static Map> reverse(Map input) {
+ Map> output = new HashMap<>();
+ for (Map.Entry entry : input.entrySet()) {
+ output.computeIfAbsent(entry.getValue(), k -> new HashSet<>()).add(entry.getKey());
+ }
+ return output;
+ }
+
+ private static void expand(TypedCuboid cuboid, List axes, Map> reverseBlocks) {
+ while (!axes.isEmpty()) {
+ axes.removeIf(axis -> {
+ Vector direction = new Vector();
+ switch (axis) {
+ case X:
+ direction.setX(1);
+ break;
+ case Y:
+ direction.setY(1);
+ break;
+ case Z:
+ direction.setZ(1);
+ break;
+ }
+ return !expand(cuboid, direction, reverseBlocks);
+ });
+ }
+ }
+
+ private static boolean expand(TypedCuboid cuboid, Vector direction, Map> reverseBlocks) {
+ Set blocks = reverseBlocks.get(cuboid.blockData);
+ if (direction.getX() == 1) {
+ for (int y = cuboid.y; y < cuboid.y + cuboid.dy; y++) {
+ for (int z = cuboid.z; z < cuboid.z + cuboid.dz; z++) {
+ if (!blocks.contains(new Pos(cuboid.x + cuboid.dx + 1, y, z))) {
+ return false;
+ }
+ }
+ }
+ } else if (direction.getY() == 1) {
+ for (int x = cuboid.x; x < cuboid.x + cuboid.dx; x++) {
+ for (int z = cuboid.z; z < cuboid.z + cuboid.dz; z++) {
+ if (!blocks.contains(new Pos(x, cuboid.y + cuboid.dy + 1, z))) {
+ return false;
+ }
+ }
+ }
+ } else if (direction.getZ() == 1) {
+ for (int x = cuboid.x; x < cuboid.x + cuboid.dx; x++) {
+ for (int y = cuboid.y; y < cuboid.y + cuboid.dy; y++) {
+ if (!blocks.contains(new Pos(x, y, cuboid.z + cuboid.dz + 1))) {
+ return false;
+ }
+ }
+ }
+ }
+ if (direction.getX() == 1) {
+ cuboid.dx++;
+ } else if (direction.getY() == 1) {
+ cuboid.dy++;
+ } else if (direction.getZ() == 1) {
+ cuboid.dz++;
+ }
+ return true;
+ }
+}
diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/cuboid/Pos.java b/BauSystem_Main/src/de/steamwar/bausystem/features/cuboid/Pos.java
new file mode 100644
index 00000000..e746d750
--- /dev/null
+++ b/BauSystem_Main/src/de/steamwar/bausystem/features/cuboid/Pos.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.cuboid;
+
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import org.bukkit.Location;
+import org.bukkit.util.Vector;
+
+@ToString
+@EqualsAndHashCode
+public class Pos {
+ public final int x;
+ public final int y;
+ public final int z;
+
+ public Pos(int x, int y, int z) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ }
+
+ public Pos(Location loc) {
+ this.x = loc.getBlockX();
+ this.y = loc.getBlockY();
+ this.z = loc.getBlockZ();
+ }
+
+ public Pos(Vector vec) {
+ this.x = vec.getBlockX();
+ this.y = vec.getBlockY();
+ this.z = vec.getBlockZ();
+ }
+
+ public Pos add(int x, int y, int z) {
+ return new Pos(this.x + x, this.y + y, this.z + z);
+ }
+
+ public Pos add(Pos pos) {
+ return new Pos(this.x + pos.x, this.y + pos.y, this.z + pos.z);
+ }
+
+ public Pos sub(int x, int y, int z) {
+ return new Pos(this.x - x, this.y - y, this.z - z);
+ }
+
+ public Pos sub(Pos pos) {
+ return new Pos(this.x - pos.x, this.y - pos.y, this.z - pos.z);
+ }
+}
diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/cuboid/TypedCuboid.java b/BauSystem_Main/src/de/steamwar/bausystem/features/cuboid/TypedCuboid.java
new file mode 100644
index 00000000..55afd8c6
--- /dev/null
+++ b/BauSystem_Main/src/de/steamwar/bausystem/features/cuboid/TypedCuboid.java
@@ -0,0 +1,71 @@
+/*
+ * 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.cuboid;
+
+import lombok.ToString;
+
+@ToString
+public class TypedCuboid {
+
+ public int x;
+ public int y;
+ public int z;
+ public int dx;
+ public int dy;
+ public int dz;
+
+ public String blockData;
+
+ public TypedCuboid(int x, int y, int z, String blockData) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+
+ this.dx = 1;
+ this.dy = 1;
+ this.dz = 1;
+
+ this.blockData = blockData;
+ }
+
+ public boolean intersects(TypedCuboid cuboid) {
+ int minx = x - cuboid.dx;
+ int miny = y - cuboid.dy;
+ int minz = z - cuboid.dz;
+ int maxx = minx + dx + cuboid.dx;
+ int maxy = miny + dy + cuboid.dy;
+ int maxz = minz + dz + cuboid.dz;
+ return maxx > cuboid.x && maxy > cuboid.y && maxz > cuboid.z && minx < cuboid.x && miny < cuboid.y && minz < cuboid.z;
+ }
+
+ public boolean intersects(Pos pos) {
+ int minx = x - dx;
+ int miny = y - dy;
+ int minz = z - dz;
+ int maxx = minx + dx;
+ int maxy = miny + dy;
+ int maxz = minz + dz;
+ return maxx > pos.x && maxy > pos.y && maxz > pos.z && minx < pos.x && miny < pos.y && minz < pos.z;
+ }
+
+ public long size() {
+ return (long) dx * (long) dy * (long) dz;
+ }
+}