diff --git a/BauSystem/BauSystem_15/build.gradle.kts b/BauSystem/BauSystem_15/build.gradle.kts
new file mode 100644
index 00000000..ef9f7c38
--- /dev/null
+++ b/BauSystem/BauSystem_15/build.gradle.kts
@@ -0,0 +1,58 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2024 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 .
+ */
+
+plugins {
+ id("java")
+ id("base")
+}
+
+group = "de.steamwar"
+version = ""
+
+tasks.compileJava {
+ options.encoding = "UTF-8"
+}
+
+java {
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
+}
+
+sourceSets {
+ main {
+ java {
+ srcDirs("src/")
+ }
+ resources {
+ srcDirs("src/")
+ exclude("**/*.java", "**/*.kt")
+ }
+ }
+}
+
+dependencies {
+ compileOnly("org.projectlombok:lombok:1.18.32")
+ annotationProcessor("org.projectlombok:lombok:1.18.32")
+
+ compileOnly(project(":BauSystem:BauSystem_Main"))
+ compileOnly(project(":SpigotCore"))
+
+ compileOnly("de.steamwar:spigot:1.15")
+ compileOnly("de.steamwar:worldedit:1.15")
+}
diff --git a/BauSystem/BauSystem_15/src/de/steamwar/bausystem/utils/FlatteningWrapper15.java b/BauSystem/BauSystem_15/src/de/steamwar/bausystem/utils/FlatteningWrapper15.java
new file mode 100644
index 00000000..9407f542
--- /dev/null
+++ b/BauSystem/BauSystem_15/src/de/steamwar/bausystem/utils/FlatteningWrapper15.java
@@ -0,0 +1,243 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2022 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;
+
+import com.sk89q.worldedit.EditSession;
+import com.sk89q.worldedit.WorldEdit;
+import com.sk89q.worldedit.WorldEditException;
+import com.sk89q.worldedit.bukkit.BukkitWorld;
+import com.sk89q.worldedit.bukkit.WorldEditPlugin;
+import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
+import com.sk89q.worldedit.extent.clipboard.Clipboard;
+import com.sk89q.worldedit.extent.clipboard.io.BuiltInClipboardFormat;
+import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats;
+import com.sk89q.worldedit.extent.clipboard.io.ClipboardReader;
+import com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter;
+import com.sk89q.worldedit.function.mask.Mask;
+import com.sk89q.worldedit.function.mask.Mask2D;
+import com.sk89q.worldedit.function.operation.ForwardExtentCopy;
+import com.sk89q.worldedit.function.operation.Operations;
+import com.sk89q.worldedit.math.BlockVector3;
+import com.sk89q.worldedit.math.transform.AffineTransform;
+import com.sk89q.worldedit.regions.CuboidRegion;
+import com.sk89q.worldedit.regions.selector.CuboidRegionSelector;
+import com.sk89q.worldedit.session.ClipboardHolder;
+import com.sk89q.worldedit.world.World;
+import com.sk89q.worldedit.world.block.BaseBlock;
+import com.sk89q.worldedit.world.block.BlockTypes;
+import de.steamwar.bausystem.region.Point;
+import org.bukkit.Bukkit;
+import org.bukkit.Material;
+import org.bukkit.block.Block;
+import org.bukkit.block.data.BlockData;
+import org.bukkit.block.data.Waterlogged;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.util.Vector;
+
+import javax.annotation.Nullable;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.BiPredicate;
+import java.util.logging.Level;
+
+public class FlatteningWrapper15 implements FlatteningWrapper {
+
+ @Override
+ public boolean isNoBook(ItemStack item) {
+ return item.getType() != Material.WRITABLE_BOOK && item.getType() != Material.WRITTEN_BOOK;
+ }
+
+ private static final Set unpushable = new HashSet<>(Arrays.asList(Material.BARRIER, Material.BEACON, Material.COMMAND_BLOCK, Material.CHAIN_COMMAND_BLOCK, Material.REPEATING_COMMAND_BLOCK, Material.ENCHANTING_TABLE, Material.END_GATEWAY, Material.END_PORTAL, Material.ENDER_CHEST, Material.GRINDSTONE, Material.JIGSAW, Material.JUKEBOX, Material.NETHER_PORTAL, Material.OBSIDIAN, Material.STRUCTURE_VOID, Material.BARREL, Material.BEEHIVE, Material.BEE_NEST, Material.BLAST_FURNACE, Material.BREWING_STAND, Material.CHEST, Material.DAYLIGHT_DETECTOR, Material.DISPENSER, Material.DROPPER, Material.FURNACE, Material.HOPPER, Material.LECTERN, Material.SMOKER, Material.TRAPPED_CHEST));
+
+ // TODO: FLOWER
+ private static final Set breaking = new HashSet<>(Arrays.asList(Material.BAMBOO, Material.CACTUS, Material.CAKE, Material.CARVED_PUMPKIN, Material.CHORUS_FLOWER, Material.CHORUS_PLANT, Material.COBWEB, Material.COCOA, Material.DRAGON_EGG, Material.FIRE, Material.FLOWER_POT, Material.JACK_O_LANTERN, Material.LADDER, Material.LAVA, Material.LAVA, Material.LEVER, Material.LILY_PAD, Material.MELON, Material.NETHER_WART, Material.PUMPKIN, Material.COMPARATOR, Material.REDSTONE_WIRE, Material.REPEATER, Material.TORCH, Material.STRUCTURE_VOID, Material.SCAFFOLDING, Material.SEA_PICKLE, Material.SNOW, Material.SUGAR_CANE, Material.TORCH, Material.TRIPWIRE, Material.TRIPWIRE_HOOK, Material.TURTLE_EGG, Material.VINE, Material.WATER, Material.WHEAT));
+
+ @Override
+ public boolean isUnpusheable(Material material) {
+ if (unpushable.contains(material)) {
+ return true;
+ }
+ String name = material.name();
+ return name.contains("BANNER") || name.contains("SIGN");
+ }
+
+ @Override
+ public boolean isBreakingOnPush(Material material) {
+ if (breaking.contains(material)) {
+ return true;
+ }
+ String name = material.name();
+ return name.contains("BED") || name.contains("BUTTON") || name.contains("CARPET") || (name.contains("DOOR") && !name.contains("TRAPDOOR")) || name.contains("HEAD") || name.contains("LEAVES") || name.contains("MUSHROOM") || name.contains("PRESSURE_PLATE") || name.contains("SHULKER_BOX");
+ }
+
+ @Override
+ public boolean isWorldEditCommand(String command) {
+ if (command.startsWith("/")) {
+ command = command.replaceFirst("/", "");
+ }
+ command = command.toLowerCase();
+ return WorldEdit.getInstance().getPlatformManager().getPlatformCommandManager().getCommandManager().containsCommand(command);
+ }
+
+ private static final WorldEditPlugin WORLDEDIT_PLUGIN = Objects.requireNonNull((WorldEditPlugin) Bukkit.getPluginManager().getPlugin("WorldEdit"));
+ private static final World BUKKITWORLD = new BukkitWorld(Bukkit.getWorlds().get(0));
+
+ @Override
+ public void setSelection(Player p, Point minPoint, Point maxPoint) {
+ WORLDEDIT_PLUGIN.getSession(p).setRegionSelector(BUKKITWORLD, new CuboidRegionSelector(BUKKITWORLD, toBlockVector3(minPoint), toBlockVector3(maxPoint)));
+ }
+
+ @Override
+ public Clipboard loadSchematic(File file) {
+ Clipboard clipboard;
+ try (ClipboardReader reader = Objects.requireNonNull(ClipboardFormats.findByFile(file)).getReader(new FileInputStream(file))) {
+ clipboard = reader.read();
+ } catch (NullPointerException | IOException e) {
+ throw new SecurityException("Bausystem schematic not found", e);
+ }
+ return clipboard;
+ }
+
+ @Override
+ public EditSession paste(PasteBuilder pasteBuilder) {
+ try (EditSession e = WorldEdit.getInstance().getEditSessionFactory().getEditSession(new BukkitWorld(Bukkit.getWorlds().get(0)), -1)) {
+ Clipboard clipboard = pasteBuilder.getClipboard();
+
+ if (!pasteBuilder.getMappers().isEmpty()) {
+ BlockVector3 minimum = clipboard.getRegion().getMinimumPoint();
+ for (int x = 0; x < clipboard.getDimensions().getX(); x++) {
+ for (int y = 0; y < clipboard.getDimensions().getY(); y++) {
+ for (int z = 0; z < clipboard.getDimensions().getZ(); z++) {
+ BlockVector3 pos = minimum.add(x, y, z);
+ pasteBuilder.getMappers().forEach(mapper -> mapper.accept(clipboard, pos));
+ }
+ }
+ }
+ }
+
+ AtomicReference pastePoint = new AtomicReference<>();
+ if (!pasteBuilder.getPredicates().isEmpty()) {
+ e.setMask(new Mask() {
+ @Override
+ public boolean test(BlockVector3 blockVector3) {
+ BaseBlock block = clipboard.getFullBlock(blockVector3.subtract(pastePoint.get()).add(clipboard.getRegion().getMinimumPoint()));
+ String blockName = block.getBlockType().toString().toLowerCase();
+ for (BiPredicate predicate : pasteBuilder.getPredicates()) {
+ if (!predicate.test(block, blockName)) return false;
+ }
+ return true;
+ }
+
+ public Mask copy() {
+ return this;
+ }
+
+ @Nullable
+ @Override
+ public Mask2D toMask2D() {
+ return null;
+ }
+ });
+ }
+
+ ClipboardHolder ch = new ClipboardHolder(clipboard);
+ BlockVector3 dimensions = clipboard.getDimensions();
+ BlockVector3 v = BlockVector3.at(pasteBuilder.getPastPoint().getX(), pasteBuilder.getPastPoint().getY(), pasteBuilder.getPastPoint().getZ());
+ BlockVector3 offset = clipboard.getRegion().getMinimumPoint().subtract(clipboard.getOrigin());
+ if (pasteBuilder.isRotate()) {
+ ch.setTransform(new AffineTransform().rotateY(180));
+ v = v.add(dimensions.getX() / 2, 0, dimensions.getZ() / 2).subtract(offset.multiply(-1, 1, -1)).subtract(0, 0, 1);
+ } else {
+ v = v.subtract(dimensions.getX() / 2, 0, dimensions.getZ() / 2).subtract(offset);
+ }
+ pastePoint.set(v);
+
+ if (pasteBuilder.isReset()) {
+ e.setBlocks(new CuboidRegion(toBlockVector3(pasteBuilder.getMinPoint()), toBlockVector3(pasteBuilder.getMaxPoint())), Objects.requireNonNull(BlockTypes.AIR).getDefaultState().toBaseBlock());
+ if (pasteBuilder.getWaterLevel() != 0) {
+ e.setBlocks(new CuboidRegion(toBlockVector3(pasteBuilder.getMinPoint()), toBlockVector3(pasteBuilder.getMaxPoint()).withY(pasteBuilder.getWaterLevel())), Objects.requireNonNull(BlockTypes.WATER).getDefaultState().toBaseBlock());
+ }
+ }
+ Operations.completeBlindly(ch.createPaste(e).to(v).ignoreAirBlocks(pasteBuilder.isIgnoreAir()).build());
+ return e;
+ } catch (WorldEditException e) {
+ throw new SecurityException(e.getMessage(), e);
+ }
+ }
+
+ @Override
+ public Clipboard copy(Point minPoint, Point maxPoint, Point copyPoint) {
+ BukkitWorld bukkitWorld = new BukkitWorld(Bukkit.getWorlds().get(0));
+ CuboidRegion region = new CuboidRegion(bukkitWorld, toBlockVector3(minPoint), toBlockVector3(maxPoint));
+ BlockArrayClipboard clipboard = new BlockArrayClipboard(region);
+ try (EditSession e = WorldEdit.getInstance().getEditSessionFactory().getEditSession(bukkitWorld, -1)) {
+ ForwardExtentCopy copy = new ForwardExtentCopy(
+ e, region, clipboard, region.getMinimumPoint()
+ );
+
+ copy.setCopyingEntities(false);
+ copy.setCopyingBiomes(false);
+
+ Operations.complete(copy);
+ clipboard.setOrigin(toBlockVector3(copyPoint));
+ return clipboard;
+ } catch (WorldEditException e) {
+ Bukkit.getLogger().log(Level.SEVERE, e.getMessage(), e);
+ return null;
+ }
+ }
+
+ @Override
+ public boolean backup(Point minPoint, Point maxPoint, File file) {
+ Clipboard clipboard = copy(minPoint, maxPoint, minPoint);
+ try (ClipboardWriter writer = BuiltInClipboardFormat.SPONGE_SCHEMATIC.getWriter(new FileOutputStream(file))) {
+ writer.write(clipboard);
+ return true;
+ } catch (IOException e) {
+ Bukkit.getLogger().log(Level.SEVERE, e.getMessage(), e);
+ return false;
+ }
+ }
+
+ private BlockVector3 toBlockVector3(Point point) {
+ return BlockVector3.at(point.getX(), point.getY(), point.getZ());
+ }
+
+ @Override
+ public boolean inWater(org.bukkit.World world, Vector tntPosition) {
+ Block block = world.getBlockAt(tntPosition.getBlockX(), tntPosition.getBlockY(), tntPosition.getBlockZ());
+ if (block.getType() == Material.WATER)
+ return true;
+
+ BlockData data = block.getBlockData();
+ if (!(data instanceof Waterlogged))
+ return false;
+
+ return ((Waterlogged) data).isWaterlogged();
+ }
+}
diff --git a/BauSystem/BauSystem_15/src/de/steamwar/bausystem/utils/NMSWrapper15.java b/BauSystem/BauSystem_15/src/de/steamwar/bausystem/utils/NMSWrapper15.java
new file mode 100644
index 00000000..3b56be55
--- /dev/null
+++ b/BauSystem/BauSystem_15/src/de/steamwar/bausystem/utils/NMSWrapper15.java
@@ -0,0 +1,135 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2022 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;
+
+import com.comphenix.tinyprotocol.Reflection;
+import de.steamwar.bausystem.features.util.NoClipCommand;
+import net.minecraft.server.v1_15_R1.*;
+import org.bukkit.Bukkit;
+import org.bukkit.GameMode;
+import org.bukkit.Material;
+import org.bukkit.World;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPlayer;
+import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftItemStack;
+import org.bukkit.entity.Player;
+import org.bukkit.entity.TNTPrimed;
+import org.bukkit.inventory.ItemStack;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.LongSupplier;
+
+public class NMSWrapper15 implements NMSWrapper {
+
+ private static final Reflection.FieldAccessor playerGameMode = Reflection.getField(PlayerInteractManager.class, EnumGamemode.class, 0);
+
+ @Override
+ @SuppressWarnings("deprecation")
+ public void setInternalGameMode(Player player, GameMode gameMode) {
+ playerGameMode.set(((CraftPlayer) player).getHandle().playerInteractManager, EnumGamemode.getById(gameMode.getValue()));
+ }
+
+ @Override
+ public void setSlotToItemStack(Player player, Object o) {
+ PacketPlayInSetCreativeSlot packetPlayInSetCreativeSlot = (PacketPlayInSetCreativeSlot) o;
+ int index = packetPlayInSetCreativeSlot.b();
+ if (index >= 36 && index <= 44) {
+ index -= 36;
+ } else if (index > 44) {
+ index -= 5;
+ } else if (index <= 8) {
+ index = index - 8 + 36;
+ }
+ player.getInventory().setItem(index, CraftItemStack.asBukkitCopy(packetPlayInSetCreativeSlot.getItemStack()));
+ if (index < 9) player.getInventory().setHeldItemSlot(index);
+ player.updateInventory();
+ }
+
+ private static final Reflection.FieldAccessor gameStateChangeReason = Reflection.getField(NoClipCommand.gameStateChange, int.class, 0);
+
+ @Override
+ public void setGameStateChangeReason(Object packet) {
+ gameStateChangeReason.set(packet, 3);
+ }
+
+ @Override
+ public void setPlayerBuildAbilities(Player player) {
+ ((CraftPlayer) player).getHandle().abilities.mayBuild = true;
+ ((CraftPlayer) player).getHandle().abilities.canInstantlyBuild = true;
+ }
+
+ @Override
+ public Material pathMaterial() {
+ return Material.GRASS_PATH;
+ }
+
+ private static final int threshold = 2048;
+
+ @Override
+ public boolean checkItemStack(ItemStack item) {
+ net.minecraft.server.v1_15_R1.ItemStack nmsItem = CraftItemStack.asNMSCopy(item);
+ NBTTagCompound tag = nmsItem.getTag();
+ if (tag != null && tag.hasKey("BlockEntityTag")) {
+ NBTTagCompound blockTag = tag.getCompound("BlockEntityTag");
+ if (blockTag.hasKey("Items")) {
+ return drillDown(blockTag.getList("Items", 10), 0, 0) > threshold;
+ }
+ }
+
+ return false;
+ }
+
+ private int drillDown(NBTTagList items, int layer, int start) {
+ if (layer > 2) return start + threshold;
+ int invalid = start;
+ for (NBTBase nbtBase : items) {
+ if (!(nbtBase instanceof NBTTagCompound))
+ continue;
+ NBTTagCompound slot = (NBTTagCompound) nbtBase;
+ if (slot.hasKey("tag")) {
+ invalid += slot.getByte("Count");
+ NBTTagCompound iTag = slot.getCompound("tag");
+ if (iTag.hasKey("BlockEntityTag")) {
+ NBTTagCompound blockTag = iTag.getCompound("BlockEntityTag");
+ if (blockTag.hasKey("Items")) {
+ invalid = drillDown(blockTag.getList("Items", 10), layer + 1, invalid);
+ }
+ }
+ }
+ if (invalid > threshold)
+ break;
+ }
+ return invalid;
+ }
+
+ private final Class> explosionPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutExplosion");
+ private final Reflection.FieldAccessor a = Reflection.getField(explosionPacket, double.class, 0);
+ private final Reflection.FieldAccessor b = Reflection.getField(explosionPacket, double.class, 1);
+ private final Reflection.FieldAccessor c = Reflection.getField(explosionPacket, double.class, 2);
+ private final Reflection.FieldAccessor d = Reflection.getField(explosionPacket, float.class, 0);
+ private final Reflection.FieldAccessor e = Reflection.getField(explosionPacket, List.class, 0);
+
+ @Override
+ public Object resetExplosionKnockback(Object packet) {
+ PacketPlayOutExplosion packetPlayOutExplosion = (PacketPlayOutExplosion) packet;
+ return new PacketPlayOutExplosion(a.get(packetPlayOutExplosion), b.get(packetPlayOutExplosion), c.get(packetPlayOutExplosion), d.get(packetPlayOutExplosion), e.get(packetPlayOutExplosion), Vec3D.a);
+ }
+}
diff --git a/BauSystem/BauSystem_15/src/de/steamwar/bausystem/utils/PlaceItemWrapper15.java b/BauSystem/BauSystem_15/src/de/steamwar/bausystem/utils/PlaceItemWrapper15.java
new file mode 100644
index 00000000..cbb65fd3
--- /dev/null
+++ b/BauSystem/BauSystem_15/src/de/steamwar/bausystem/utils/PlaceItemWrapper15.java
@@ -0,0 +1,43 @@
+/*
+ * 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;
+
+import org.bukkit.Material;
+
+public class PlaceItemWrapper15 implements PlaceItemWrapper {
+
+ public PlaceItemWrapper15() {
+ for (Material material : Material.values()) {
+ if (!material.isBlock()) continue;
+ if (material.isLegacy()) continue;
+
+ String nonWall = material.name().replace("_WALL_", "").replace("WALL_", "").replace("_WALL", "");
+ try {
+ Material nonWallMaterial = Material.valueOf(nonWall);
+ if (nonWallMaterial != material && nonWallMaterial.isItem() && !nonWallMaterial.isBlock()) {
+ BLOCK_MATERIAL_TO_WALL_BLOCK_MATERIAL.put(nonWallMaterial, material);
+ }
+ } catch (Exception e) {
+ // Ignore
+ }
+ }
+ ITEM_MATERIAL_TO_BLOCK_MATERIAL.put(Material.REDSTONE, Material.REDSTONE_WIRE);
+ }
+}
diff --git a/BauSystem/BauSystem_15/src/de/steamwar/bausystem/utils/PlayerMovementWrapper15.java b/BauSystem/BauSystem_15/src/de/steamwar/bausystem/utils/PlayerMovementWrapper15.java
new file mode 100644
index 00000000..b0ed19b3
--- /dev/null
+++ b/BauSystem/BauSystem_15/src/de/steamwar/bausystem/utils/PlayerMovementWrapper15.java
@@ -0,0 +1,59 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2022 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;
+
+import com.comphenix.tinyprotocol.Reflection;
+import de.steamwar.bausystem.utils.PlayerMovementWrapper;
+import net.minecraft.server.v1_15_R1.EntityPlayer;
+import net.minecraft.server.v1_15_R1.PacketPlayInFlying;
+import net.minecraft.server.v1_15_R1.PacketPlayOutEntity;
+import net.minecraft.server.v1_15_R1.PacketPlayOutEntityTeleport;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPlayer;
+import org.bukkit.entity.Player;
+
+public class PlayerMovementWrapper15 implements PlayerMovementWrapper {
+
+ @Override
+ public void setPosition(Player player, Object object) {
+ PacketPlayInFlying packetPlayInFlying = ((PacketPlayInFlying) object);
+ EntityPlayer entityPlayer = ((CraftPlayer) player).getHandle();
+ if (Float.isNaN(packetPlayInFlying.a(Float.NaN))) {
+ entityPlayer.e(packetPlayInFlying.a(0.0), packetPlayInFlying.b(0.0), packetPlayInFlying.c(0.0));
+ } else {
+ entityPlayer.setLocation(packetPlayInFlying.a(0.0), packetPlayInFlying.b(0.0), packetPlayInFlying.c(0.0), packetPlayInFlying.a(0F), packetPlayInFlying.b(0F));
+ }
+ }
+
+ @Override
+ public Object convertToOut(Player player, Object object) {
+ PacketPlayInFlying packetPlayInFlying = ((PacketPlayInFlying) object);
+ Object packet = Reflection.newInstance(teleportPacket);
+ teleportEntity.set(packet, player.getEntityId());
+ teleportPosition.set(packet, packetPlayInFlying.a(0.0), packetPlayInFlying.b(0.0), packetPlayInFlying.c(0.0));
+ if (Float.isNaN(packetPlayInFlying.a(Float.NaN))) {
+ teleportYaw.set(packet, rotToByte(player.getLocation().getYaw()));
+ teleportPitch.set(packet, rotToByte(player.getLocation().getPitch()));
+ } else {
+ teleportYaw.set(packet, rotToByte(packetPlayInFlying.a(0.0F)));
+ teleportPitch.set(packet, rotToByte(packetPlayInFlying.b(0.0F)));
+ }
+ return packet;
+ }
+}
diff --git a/BauSystem/BauSystem_15/src/de/steamwar/bausystem/utils/TickListener15.java b/BauSystem/BauSystem_15/src/de/steamwar/bausystem/utils/TickListener15.java
new file mode 100644
index 00000000..f2b604ac
--- /dev/null
+++ b/BauSystem/BauSystem_15/src/de/steamwar/bausystem/utils/TickListener15.java
@@ -0,0 +1,23 @@
+/*
+ * 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;
+
+public class TickListener15 implements TickListener {
+}
diff --git a/BauSystem/BauSystem_18/build.gradle.kts b/BauSystem/BauSystem_18/build.gradle.kts
new file mode 100644
index 00000000..8a449c9b
--- /dev/null
+++ b/BauSystem/BauSystem_18/build.gradle.kts
@@ -0,0 +1,65 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2024 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 .
+ */
+
+plugins {
+ id("java")
+ id("base")
+}
+
+group = "de.steamwar"
+version = ""
+
+tasks.compileJava {
+ options.encoding = "UTF-8"
+}
+
+java {
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
+}
+
+sourceSets {
+ main {
+ java {
+ srcDirs("src/")
+ }
+ resources {
+ srcDirs("src/")
+ exclude("**/*.java", "**/*.kt")
+ }
+ }
+}
+
+dependencies {
+ compileOnly("org.projectlombok:lombok:1.18.32")
+ annotationProcessor("org.projectlombok:lombok:1.18.32")
+
+ compileOnly(project(":BauSystem:BauSystem_Main"))
+ compileOnly(project(":SpigotCore"))
+
+ compileOnly("de.steamwar:spigot:1.18")
+ compileOnly("de.steamwar:worldedit:1.15")
+
+ compileOnly("org.spigotmc:spigot-api:1.18-R0.1-SNAPSHOT")
+ compileOnly("it.unimi.dsi:fastutil:8.5.6")
+ compileOnly("com.mojang:datafixerupper:4.0.26")
+ compileOnly("io.netty:netty-all:4.1.68.Final")
+ compileOnly("com.mojang:authlib:1.5.25")
+ compileOnly("com.mojang:brigadier:1.0.18")
+}
diff --git a/BauSystem/BauSystem_18/src/de/steamwar/bausystem/utils/NMSWrapper18.java b/BauSystem/BauSystem_18/src/de/steamwar/bausystem/utils/NMSWrapper18.java
new file mode 100644
index 00000000..c12ce0af
--- /dev/null
+++ b/BauSystem/BauSystem_18/src/de/steamwar/bausystem/utils/NMSWrapper18.java
@@ -0,0 +1,145 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2022 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;
+
+import com.comphenix.tinyprotocol.Reflection;
+import com.comphenix.tinyprotocol.TinyProtocol;
+import de.steamwar.bausystem.features.util.NoClipCommand;
+import net.minecraft.SystemUtils;
+import net.minecraft.nbt.NBTBase;
+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.server.level.PlayerInteractManager;
+import net.minecraft.world.level.EnumGamemode;
+import net.minecraft.world.phys.Vec3D;
+import org.bukkit.Bukkit;
+import org.bukkit.GameMode;
+import org.bukkit.Material;
+import org.bukkit.World;
+import org.bukkit.craftbukkit.v1_18_R2.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer;
+import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftItemStack;
+import org.bukkit.entity.Player;
+import org.bukkit.entity.TNTPrimed;
+import org.bukkit.inventory.ItemStack;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.LongSupplier;
+
+public class NMSWrapper18 implements NMSWrapper {
+
+ private static final Reflection.FieldAccessor playerGameMode = Reflection.getField(PlayerInteractManager.class, EnumGamemode.class, 0);
+
+ @Override
+ @SuppressWarnings("deprecation")
+ public void setInternalGameMode(Player player, GameMode gameMode) {
+ playerGameMode.set(((CraftPlayer) player).getHandle().d, EnumGamemode.a(gameMode.getValue()));
+
+ }
+
+ @Override
+ public void setSlotToItemStack(Player player, Object o) {
+ PacketPlayInSetCreativeSlot packetPlayInSetCreativeSlot = (PacketPlayInSetCreativeSlot) o;
+ int index = packetPlayInSetCreativeSlot.b();
+ if (index >= 36 && index <= 44) {
+ index -= 36;
+ } else if (index > 44) {
+ index -= 5;
+ } else if (index <= 8) {
+ index = index - 8 + 36;
+ }
+ player.getInventory().setItem(index, CraftItemStack.asBukkitCopy(packetPlayInSetCreativeSlot.c()));
+ if (index < 9) player.getInventory().setHeldItemSlot(index);
+ player.updateInventory();
+ }
+
+ private static final Reflection.FieldAccessor gameStateChangeReason = Reflection.getField(NoClipCommand.gameStateChange, PacketPlayOutGameStateChange.a.class, 12);
+
+ @Override
+ public void setGameStateChangeReason(Object packet) {
+ gameStateChangeReason.set(packet, PacketPlayOutGameStateChange.d);
+ }
+
+ @Override
+ public void setPlayerBuildAbilities(Player player) {
+ ((CraftPlayer) player).getHandle().fs().d = true;
+ ((CraftPlayer) player).getHandle().fs().e = true;
+ }
+
+ @Override
+ public Material pathMaterial() {
+ return Material.DIRT_PATH;
+ }
+
+ private static final int threshold = 2048;
+
+ @Override
+ public boolean checkItemStack(ItemStack item) {
+ net.minecraft.world.item.ItemStack nmsItem = CraftItemStack.asNMSCopy(item);
+ NBTTagCompound tag = nmsItem.t();
+ if (tag != null && tag.e("BlockEntityTag")) {
+ NBTTagCompound blockTag = tag.p("BlockEntityTag");
+ if (blockTag.e("Items")) {
+ return drillDown(blockTag.c("Items", 10), 0, 0) > threshold;
+ }
+ }
+
+ return false;
+ }
+
+ private int drillDown(NBTTagList items, int layer, int start) {
+ if (layer > 2) return start + threshold;
+ int invalid = start;
+ for (NBTBase nbtBase : items) {
+ if (!(nbtBase instanceof NBTTagCompound))
+ continue;
+ NBTTagCompound slot = (NBTTagCompound) nbtBase;
+ if (slot.e("tag")) {
+ invalid += slot.f("Count");
+ NBTTagCompound iTag = slot.p("tag");
+ if (iTag.e("BlockEntityTag")) {
+ NBTTagCompound blockTag = iTag.p("BlockEntityTag");
+ if (blockTag.e("Items")) {
+ invalid = drillDown(blockTag.c("Items", 10), layer + 1, invalid);
+ }
+ }
+ }
+ if (invalid > threshold)
+ break;
+ }
+ return invalid;
+ }
+
+ private final Class> explosionPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutExplosion");
+ private final Reflection.FieldAccessor a = Reflection.getField(explosionPacket, double.class, 0);
+ private final Reflection.FieldAccessor b = Reflection.getField(explosionPacket, double.class, 1);
+ private final Reflection.FieldAccessor c = Reflection.getField(explosionPacket, double.class, 2);
+ private final Reflection.FieldAccessor d = Reflection.getField(explosionPacket, float.class, 0);
+ private final Reflection.FieldAccessor e = Reflection.getField(explosionPacket, List.class, 0);
+
+ @Override
+ public Object resetExplosionKnockback(Object packet) {
+ PacketPlayOutExplosion packetPlayOutExplosion = (PacketPlayOutExplosion) packet;
+ return new PacketPlayOutExplosion(a.get(packetPlayOutExplosion), b.get(packetPlayOutExplosion), c.get(packetPlayOutExplosion), d.get(packetPlayOutExplosion), e.get(packetPlayOutExplosion), null);
+ }
+}
diff --git a/BauSystem/BauSystem_18/src/de/steamwar/bausystem/utils/PlayerMovementWrapper18.java b/BauSystem/BauSystem_18/src/de/steamwar/bausystem/utils/PlayerMovementWrapper18.java
new file mode 100644
index 00000000..8762e418
--- /dev/null
+++ b/BauSystem/BauSystem_18/src/de/steamwar/bausystem/utils/PlayerMovementWrapper18.java
@@ -0,0 +1,57 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2022 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;
+
+import com.comphenix.tinyprotocol.Reflection;
+import de.steamwar.bausystem.utils.PlayerMovementWrapper;
+import net.minecraft.network.protocol.game.PacketPlayInFlying;
+import net.minecraft.server.level.EntityPlayer;
+import org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer;
+import org.bukkit.entity.Player;
+
+public class PlayerMovementWrapper18 implements PlayerMovementWrapper {
+
+ @Override
+ public void setPosition(Player player, Object object) {
+ PacketPlayInFlying packetPlayInFlying = ((PacketPlayInFlying) object);
+ EntityPlayer entityPlayer = ((CraftPlayer) player).getHandle();
+ if (packetPlayInFlying.h) {
+ entityPlayer.b(packetPlayInFlying.a, packetPlayInFlying.b, packetPlayInFlying.c, packetPlayInFlying.d, packetPlayInFlying.e);
+ } else {
+ entityPlayer.e(packetPlayInFlying.a, packetPlayInFlying.b, packetPlayInFlying.c);
+ }
+ }
+
+ @Override
+ public Object convertToOut(Player player, Object object) {
+ PacketPlayInFlying packetPlayInFlying = ((PacketPlayInFlying) object);
+ Object packet = Reflection.newInstance(teleportPacket);
+ teleportEntity.set(packet, player.getEntityId());
+ teleportPosition.set(packet, packetPlayInFlying.a, packetPlayInFlying.b, packetPlayInFlying.c);
+ if (packetPlayInFlying.h) {
+ teleportYaw.set(packet, rotToByte(player.getLocation().getYaw()));
+ teleportPitch.set(packet, rotToByte(player.getLocation().getPitch()));
+ } else {
+ teleportYaw.set(packet, rotToByte(packetPlayInFlying.d));
+ teleportPitch.set(packet, rotToByte(packetPlayInFlying.e));
+ }
+ return packet;
+ }
+}
diff --git a/BauSystem/BauSystem_19/build.gradle.kts b/BauSystem/BauSystem_19/build.gradle.kts
new file mode 100644
index 00000000..fd650279
--- /dev/null
+++ b/BauSystem/BauSystem_19/build.gradle.kts
@@ -0,0 +1,66 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2024 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 .
+ */
+
+plugins {
+ id("java")
+ id("base")
+}
+
+group = "de.steamwar"
+version = ""
+
+tasks.compileJava {
+ options.encoding = "UTF-8"
+}
+
+java {
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
+}
+
+sourceSets {
+ main {
+ java {
+ srcDirs("src/")
+ }
+ resources {
+ srcDirs("src/")
+ exclude("**/*.java", "**/*.kt")
+ }
+ }
+}
+
+dependencies {
+ compileOnly("org.projectlombok:lombok:1.18.32")
+ annotationProcessor("org.projectlombok:lombok:1.18.32")
+
+ compileOnly(project(":BauSystem:BauSystem_Main"))
+ compileOnly(project(":SpigotCore"))
+
+ compileOnly("de.steamwar:spigot:1.19")
+ compileOnly("de.steamwar:worldedit:1.15")
+
+ compileOnly("org.spigotmc:spigot-api:1.19-R0.1-SNAPSHOT")
+ compileOnly("io.papermc.paper:paper-api:1.19.2-R0.1-SNAPSHOT")
+ compileOnly("it.unimi.dsi:fastutil:8.5.6")
+ compileOnly("com.mojang:datafixerupper:4.0.26")
+ compileOnly("io.netty:netty-all:4.1.68.Final")
+ compileOnly("com.mojang:authlib:1.5.25")
+ compileOnly("com.mojang:brigadier:1.0.18")
+}
diff --git a/BauSystem/BauSystem_19/src/de/steamwar/bausystem/utils/NMSWrapper19.java b/BauSystem/BauSystem_19/src/de/steamwar/bausystem/utils/NMSWrapper19.java
new file mode 100644
index 00000000..ecdc33be
--- /dev/null
+++ b/BauSystem/BauSystem_19/src/de/steamwar/bausystem/utils/NMSWrapper19.java
@@ -0,0 +1,145 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2022 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;
+
+import com.comphenix.tinyprotocol.Reflection;
+import com.comphenix.tinyprotocol.TinyProtocol;
+import de.steamwar.bausystem.features.util.NoClipCommand;
+import net.minecraft.SystemUtils;
+import net.minecraft.nbt.NBTBase;
+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;
+import org.bukkit.Bukkit;
+import org.bukkit.GameMode;
+import org.bukkit.Material;
+import org.bukkit.World;
+import org.bukkit.craftbukkit.v1_19_R2.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_19_R2.entity.CraftPlayer;
+import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftItemStack;
+import org.bukkit.entity.Player;
+import org.bukkit.entity.TNTPrimed;
+import org.bukkit.inventory.ItemStack;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.LongSupplier;
+
+public class NMSWrapper19 implements NMSWrapper {
+
+ private static final Reflection.FieldAccessor playerGameMode = Reflection.getField(PlayerInteractManager.class, EnumGamemode.class, 0);
+
+ @Override
+ @SuppressWarnings("deprecation")
+ public void setInternalGameMode(Player player, GameMode gameMode) {
+ playerGameMode.set(((CraftPlayer) player).getHandle().d, EnumGamemode.a(gameMode.getValue()));
+ }
+
+ @Override
+ public void setSlotToItemStack(Player player, Object o) {
+ PacketPlayInSetCreativeSlot packetPlayInSetCreativeSlot = (PacketPlayInSetCreativeSlot) o;
+ int index = packetPlayInSetCreativeSlot.b();
+ if (index >= 36 && index <= 44) {
+ index -= 36;
+ } else if (index > 44) {
+ index -= 5;
+ } else if (index <= 8) {
+ index = index - 8 + 36;
+ }
+ player.getInventory().setItem(index, CraftItemStack.asBukkitCopy(packetPlayInSetCreativeSlot.c()));
+ if (index < 9) player.getInventory().setHeldItemSlot(index);
+ player.updateInventory();
+ }
+
+ private static final Reflection.FieldAccessor gameStateChangeReason = Reflection.getField(NoClipCommand.gameStateChange, PacketPlayOutGameStateChange.a.class, 12);
+
+ @Override
+ public void setGameStateChangeReason(Object packet) {
+ gameStateChangeReason.set(packet, PacketPlayOutGameStateChange.d);
+ }
+
+ @Override
+ public void setPlayerBuildAbilities(Player player) {
+ ((CraftPlayer) player).getHandle().fF().d = true;
+ ((CraftPlayer) player).getHandle().fF().e = true;
+ }
+
+ @Override
+ public Material pathMaterial() {
+ return Material.DIRT_PATH;
+ }
+
+ private static final int threshold = 2048;
+
+ @Override
+ public boolean checkItemStack(ItemStack item) {
+ net.minecraft.world.item.ItemStack nmsItem = CraftItemStack.asNMSCopy(item);
+ NBTTagCompound tag = nmsItem.v();
+ if (tag != null && tag.e("BlockEntityTag")) {
+ NBTTagCompound blockTag = tag.p("BlockEntityTag");
+ if (blockTag.e("Items")) {
+ return drillDown(blockTag.c("Items", 10), 0, 0) > threshold;
+ }
+ }
+
+ return false;
+ }
+
+ private int drillDown(NBTTagList items, int layer, int start) {
+ if (layer > 2) return start + threshold;
+ int invalid = start;
+ for (NBTBase nbtBase : items) {
+ if (!(nbtBase instanceof NBTTagCompound))
+ continue;
+ NBTTagCompound slot = (NBTTagCompound) nbtBase;
+ if (slot.e("tag")) {
+ invalid += slot.f("Count");
+ NBTTagCompound iTag = slot.p("tag");
+ if (iTag.e("BlockEntityTag")) {
+ NBTTagCompound blockTag = iTag.p("BlockEntityTag");
+ if (blockTag.e("Items")) {
+ invalid = drillDown(blockTag.c("Items", 10), layer + 1, invalid);
+ }
+ }
+ }
+ if (invalid > threshold)
+ break;
+ }
+ return invalid;
+ }
+
+ private final Class> explosionPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutExplosion");
+ private final Reflection.FieldAccessor a = Reflection.getField(explosionPacket, double.class, 0);
+ private final Reflection.FieldAccessor b = Reflection.getField(explosionPacket, double.class, 1);
+ private final Reflection.FieldAccessor c = Reflection.getField(explosionPacket, double.class, 2);
+ private final Reflection.FieldAccessor d = Reflection.getField(explosionPacket, float.class, 0);
+ private final Reflection.FieldAccessor e = Reflection.getField(explosionPacket, List.class, 0);
+
+ @Override
+ public Object resetExplosionKnockback(Object packet) {
+ PacketPlayOutExplosion packetPlayOutExplosion = (PacketPlayOutExplosion) packet;
+ return new PacketPlayOutExplosion(a.get(packetPlayOutExplosion), b.get(packetPlayOutExplosion), c.get(packetPlayOutExplosion), d.get(packetPlayOutExplosion), e.get(packetPlayOutExplosion), null);
+ }
+}
diff --git a/BauSystem/BauSystem_19/src/de/steamwar/bausystem/utils/PlayerMovementWrapper19.java b/BauSystem/BauSystem_19/src/de/steamwar/bausystem/utils/PlayerMovementWrapper19.java
new file mode 100644
index 00000000..9262bfea
--- /dev/null
+++ b/BauSystem/BauSystem_19/src/de/steamwar/bausystem/utils/PlayerMovementWrapper19.java
@@ -0,0 +1,61 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2022 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;
+
+import com.comphenix.tinyprotocol.Reflection;
+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 {
+
+ @Override
+ public void setPosition(Player player, Object object) {
+ PacketPlayInFlying packetPlayInFlying = ((PacketPlayInFlying) object);
+ EntityPlayer entityPlayer = ((CraftPlayer) player).getHandle();
+ if (packetPlayInFlying.h) {
+ entityPlayer.b(packetPlayInFlying.a, packetPlayInFlying.b, packetPlayInFlying.c, packetPlayInFlying.d, packetPlayInFlying.e);
+ } else {
+ entityPlayer.e(packetPlayInFlying.a, packetPlayInFlying.b, packetPlayInFlying.c);
+ }
+ }
+
+ @Override
+ public Object convertToOut(Player player, Object object) {
+ PacketPlayInFlying packetPlayInFlying = ((PacketPlayInFlying) object);
+ Object packet = Reflection.newInstance(teleportPacket);
+ teleportEntity.set(packet, player.getEntityId());
+ teleportPosition.set(packet, packetPlayInFlying.a, packetPlayInFlying.b, packetPlayInFlying.c);
+ if (packetPlayInFlying.h) {
+ teleportYaw.set(packet, rotToByte(player.getLocation().getYaw()));
+ teleportPitch.set(packet, rotToByte(player.getLocation().getPitch()));
+ } else {
+ teleportYaw.set(packet, rotToByte(packetPlayInFlying.d));
+ teleportPitch.set(packet, rotToByte(packetPlayInFlying.e));
+ }
+ return packet;
+ }
+}
diff --git a/BauSystem/BauSystem_19/src/de/steamwar/bausystem/utils/TickListener19.java b/BauSystem/BauSystem_19/src/de/steamwar/bausystem/utils/TickListener19.java
new file mode 100644
index 00000000..5191b38b
--- /dev/null
+++ b/BauSystem/BauSystem_19/src/de/steamwar/bausystem/utils/TickListener19.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;
+
+import com.destroystokyo.paper.event.server.ServerTickEndEvent;
+import com.destroystokyo.paper.event.server.ServerTickStartEvent;
+import de.steamwar.bausystem.BauSystem;
+import de.steamwar.bausystem.features.tpslimit.TPSFreezeUtils;
+import org.bukkit.Bukkit;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+
+public class TickListener19 implements TickListener, Listener {
+
+ private boolean tickStartRan = false;
+
+ public TickListener19() {
+ Bukkit.getPluginManager().registerEvents(this, BauSystem.getInstance());
+ }
+
+ @EventHandler
+ public void onServerTickStart(ServerTickStartEvent event) {
+ if (TPSFreezeUtils.isFrozen()) return;
+ Bukkit.getPluginManager().callEvent(new TickStartEvent());
+ tickStartRan = true;
+ }
+
+ @EventHandler
+ public void onServerTickEnd(ServerTickEndEvent event) {
+ if (!tickStartRan) return;
+ Bukkit.getPluginManager().callEvent(new TickEndEvent());
+ tickStartRan = false;
+ }
+}
diff --git a/BauSystem/BauSystem_20/build.gradle.kts b/BauSystem/BauSystem_20/build.gradle.kts
new file mode 100644
index 00000000..4a6b3d10
--- /dev/null
+++ b/BauSystem/BauSystem_20/build.gradle.kts
@@ -0,0 +1,64 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2024 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 .
+ */
+
+plugins {
+ id("java")
+ id("base")
+}
+
+group = "de.steamwar"
+version = ""
+
+tasks.compileJava {
+ options.encoding = "UTF-8"
+}
+
+java {
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
+}
+
+sourceSets {
+ main {
+ java {
+ srcDirs("src/")
+ }
+ resources {
+ srcDirs("src/")
+ exclude("**/*.java", "**/*.kt")
+ }
+ }
+}
+
+dependencies {
+ compileOnly("org.projectlombok:lombok:1.18.32")
+ annotationProcessor("org.projectlombok:lombok:1.18.32")
+
+ compileOnly(project(":BauSystem:BauSystem_Main"))
+ compileOnly(project(":SpigotCore"))
+
+ compileOnly("de.steamwar:spigot:1.20")
+
+ compileOnly("org.spigotmc:spigot-api:1.20-R0.1-SNAPSHOT")
+ compileOnly("it.unimi.dsi:fastutil:8.5.6")
+ compileOnly("com.mojang:datafixerupper:4.0.26")
+ compileOnly("io.netty:netty-all:4.1.68.Final")
+ compileOnly("com.mojang:authlib:1.5.25")
+ compileOnly("com.mojang:brigadier:1.0.18")
+}
diff --git a/BauSystem/BauSystem_20/src/de/steamwar/bausystem/utils/NMSWrapper20.java b/BauSystem/BauSystem_20/src/de/steamwar/bausystem/utils/NMSWrapper20.java
new file mode 100644
index 00000000..1cb81892
--- /dev/null
+++ b/BauSystem/BauSystem_20/src/de/steamwar/bausystem/utils/NMSWrapper20.java
@@ -0,0 +1,136 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2022 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;
+
+import com.comphenix.tinyprotocol.Reflection;
+import de.steamwar.bausystem.features.util.NoClipCommand;
+import net.minecraft.nbt.NBTBase;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.nbt.NBTTagList;
+import net.minecraft.network.protocol.game.PacketPlayInSetCreativeSlot;
+import net.minecraft.network.protocol.game.PacketPlayOutExplosion;
+import net.minecraft.network.protocol.game.PacketPlayOutGameStateChange;
+import net.minecraft.server.level.PlayerInteractManager;
+import net.minecraft.world.level.EnumGamemode;
+import org.bukkit.GameMode;
+import org.bukkit.Material;
+import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer;
+import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+
+import java.util.List;
+
+public class NMSWrapper20 implements NMSWrapper {
+
+ private static final Reflection.FieldAccessor playerGameMode = Reflection.getField(PlayerInteractManager.class, EnumGamemode.class, 0);
+
+ @Override
+ @SuppressWarnings("deprecation")
+ public void setInternalGameMode(Player player, GameMode gameMode) {
+ playerGameMode.set(((CraftPlayer) player).getHandle().e, EnumGamemode.a(gameMode.getValue()));
+ }
+
+ @Override
+ public void setSlotToItemStack(Player player, Object o) {
+ PacketPlayInSetCreativeSlot packetPlayInSetCreativeSlot = (PacketPlayInSetCreativeSlot) o;
+ int index = packetPlayInSetCreativeSlot.a();
+ if (index >= 36 && index <= 44) {
+ index -= 36;
+ } else if (index > 44) {
+ index -= 5;
+ } else if (index <= 8) {
+ index = index - 8 + 36;
+ }
+ player.getInventory().setItem(index, CraftItemStack.asBukkitCopy(packetPlayInSetCreativeSlot.c()));
+ if (index < 9) player.getInventory().setHeldItemSlot(index);
+ player.updateInventory();
+ }
+
+ private static final Reflection.FieldAccessor gameStateChangeReason = Reflection.getField(NoClipCommand.gameStateChange, PacketPlayOutGameStateChange.a.class, 12);
+
+ @Override
+ public void setGameStateChangeReason(Object packet) {
+ gameStateChangeReason.set(packet, PacketPlayOutGameStateChange.d);
+ }
+
+ @Override
+ public void setPlayerBuildAbilities(Player player) {
+ ((CraftPlayer) player).getHandle().fO().d = true;
+ ((CraftPlayer) player).getHandle().fO().e = true;
+ }
+
+ @Override
+ public Material pathMaterial() {
+ return Material.DIRT_PATH;
+ }
+
+ private static final int threshold = 2048;
+
+ @Override
+ public boolean checkItemStack(ItemStack item) {
+ net.minecraft.world.item.ItemStack nmsItem = CraftItemStack.asNMSCopy(item);
+ NBTTagCompound tag = nmsItem.v();
+ if (tag != null && tag.e("BlockEntityTag")) {
+ NBTTagCompound blockTag = tag.p("BlockEntityTag");
+ if (blockTag.e("Items")) {
+ return drillDown(blockTag.c("Items", 10), 0, 0) > threshold;
+ }
+ }
+
+ return false;
+ }
+
+ private int drillDown(NBTTagList items, int layer, int start) {
+ if (layer > 2) return start + threshold;
+ int invalid = start;
+ for (NBTBase nbtBase : items) {
+ if (!(nbtBase instanceof NBTTagCompound))
+ continue;
+ NBTTagCompound slot = (NBTTagCompound) nbtBase;
+ if (slot.e("tag")) {
+ invalid += slot.f("Count");
+ NBTTagCompound iTag = slot.p("tag");
+ if (iTag.e("BlockEntityTag")) {
+ NBTTagCompound blockTag = iTag.p("BlockEntityTag");
+ if (blockTag.e("Items")) {
+ invalid = drillDown(blockTag.c("Items", 10), layer + 1, invalid);
+ }
+ }
+ }
+ if (invalid > threshold)
+ break;
+ }
+ return invalid;
+ }
+
+ private final Class> explosionPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutExplosion");
+ private final Reflection.FieldAccessor a = Reflection.getField(explosionPacket, double.class, 0);
+ private final Reflection.FieldAccessor b = Reflection.getField(explosionPacket, double.class, 1);
+ private final Reflection.FieldAccessor c = Reflection.getField(explosionPacket, double.class, 2);
+ private final Reflection.FieldAccessor d = Reflection.getField(explosionPacket, float.class, 0);
+ private final Reflection.FieldAccessor e = Reflection.getField(explosionPacket, List.class, 0);
+
+ @Override
+ public Object resetExplosionKnockback(Object packet) {
+ PacketPlayOutExplosion packetPlayOutExplosion = (PacketPlayOutExplosion) packet;
+ return new PacketPlayOutExplosion(a.get(packetPlayOutExplosion), b.get(packetPlayOutExplosion), c.get(packetPlayOutExplosion), d.get(packetPlayOutExplosion), e.get(packetPlayOutExplosion), null);
+ }
+}
diff --git a/BauSystem/BauSystem_20/src/de/steamwar/bausystem/utils/PlaceItemWrapper20.java b/BauSystem/BauSystem_20/src/de/steamwar/bausystem/utils/PlaceItemWrapper20.java
new file mode 100644
index 00000000..e9fc1b0d
--- /dev/null
+++ b/BauSystem/BauSystem_20/src/de/steamwar/bausystem/utils/PlaceItemWrapper20.java
@@ -0,0 +1,43 @@
+/*
+ * 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;
+
+import org.bukkit.Material;
+import org.bukkit.block.data.BlockData;
+
+public class PlaceItemWrapper20 implements PlaceItemWrapper {
+
+ public PlaceItemWrapper20() {
+ for (Material material : Material.values()) {
+ if (!material.isBlock()) continue;
+ if (material.isLegacy()) continue;
+ BlockData blockData = material.createBlockData();
+ Material placementMaterial = blockData.getPlacementMaterial();
+ if (material == placementMaterial) continue;
+ if (placementMaterial == Material.AIR) continue;
+ if (placementMaterial.isItem() && !placementMaterial.isBlock()) {
+ ITEM_MATERIAL_TO_BLOCK_MATERIAL.put(placementMaterial, material);
+ }
+ if (material.name().contains("WALL")) {
+ BLOCK_MATERIAL_TO_WALL_BLOCK_MATERIAL.put(placementMaterial, material);
+ }
+ }
+ }
+}
diff --git a/BauSystem/BauSystem_20/src/de/steamwar/bausystem/utils/PlayerMovementWrapper20.java b/BauSystem/BauSystem_20/src/de/steamwar/bausystem/utils/PlayerMovementWrapper20.java
new file mode 100644
index 00000000..958e4bb8
--- /dev/null
+++ b/BauSystem/BauSystem_20/src/de/steamwar/bausystem/utils/PlayerMovementWrapper20.java
@@ -0,0 +1,62 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2022 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;
+
+import com.comphenix.tinyprotocol.Reflection;
+import net.minecraft.network.protocol.game.PacketPlayInFlying;
+import net.minecraft.network.protocol.game.PacketPlayOutEntityTeleport;
+import net.minecraft.server.level.EntityPlayer;
+import org.bukkit.Location;
+import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer;
+import org.bukkit.entity.Player;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+public class PlayerMovementWrapper20 implements PlayerMovementWrapper {
+
+ @Override
+ public void setPosition(Player player, Object object) {
+ PacketPlayInFlying packetPlayInFlying = ((PacketPlayInFlying) object);
+ EntityPlayer entityPlayer = ((CraftPlayer) player).getHandle();
+ if (packetPlayInFlying.h) {
+ entityPlayer.b(packetPlayInFlying.a, packetPlayInFlying.b, packetPlayInFlying.c, packetPlayInFlying.d, packetPlayInFlying.e);
+ } else {
+ entityPlayer.e(packetPlayInFlying.a, packetPlayInFlying.b, packetPlayInFlying.c);
+ }
+ }
+
+ @Override
+ public Object convertToOut(Player player, Object object) {
+ PacketPlayInFlying packetPlayInFlying = ((PacketPlayInFlying) object);
+ Object packet = Reflection.newInstance(teleportPacket);
+ teleportEntity.set(packet, player.getEntityId());
+ teleportPosition.set(packet, packetPlayInFlying.a, packetPlayInFlying.b, packetPlayInFlying.c);
+ if (packetPlayInFlying.h) {
+ teleportYaw.set(packet, rotToByte(player.getLocation().getYaw()));
+ teleportPitch.set(packet, rotToByte(player.getLocation().getPitch()));
+ } else {
+ teleportYaw.set(packet, rotToByte(packetPlayInFlying.d));
+ teleportPitch.set(packet, rotToByte(packetPlayInFlying.e));
+ }
+ return packet;
+ }
+}
diff --git a/BauSystem/BauSystem_Linkage/build.gradle.kts b/BauSystem/BauSystem_Linkage/build.gradle.kts
new file mode 100644
index 00000000..aa5e1db0
--- /dev/null
+++ b/BauSystem/BauSystem_Linkage/build.gradle.kts
@@ -0,0 +1,54 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2024 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 .
+ */
+
+plugins {
+ id("java")
+ id("base")
+}
+
+group = "de.steamwar"
+version = ""
+
+tasks.compileJava {
+ options.encoding = "UTF-8"
+}
+
+java {
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
+}
+
+sourceSets {
+ main {
+ java {
+ srcDirs("src/")
+ }
+ resources {
+ srcDirs("src/")
+ exclude("**/*.java", "**/*.kt")
+ }
+ }
+}
+
+dependencies {
+ compileOnly("org.projectlombok:lombok:1.18.32")
+ annotationProcessor("org.projectlombok:lombok:1.18.32")
+
+ compileOnly(project(":SpigotCore"))
+}
diff --git a/BauSystem/BauSystem_Linkage/src/de/steamwar/linkage/types/BauGuiItem_GENERIC.java b/BauSystem/BauSystem_Linkage/src/de/steamwar/linkage/types/BauGuiItem_GENERIC.java
new file mode 100644
index 00000000..160a7ca3
--- /dev/null
+++ b/BauSystem/BauSystem_Linkage/src/de/steamwar/linkage/types/BauGuiItem_GENERIC.java
@@ -0,0 +1,40 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2022 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.linkage.types;
+
+import de.steamwar.linkage.LinkageType;
+import de.steamwar.linkage.plan.BuildPlan;
+import de.steamwar.linkage.plan.MethodBuilder;
+
+import javax.lang.model.element.TypeElement;
+
+public class BauGuiItem_GENERIC implements LinkageType {
+
+ @Override
+ public String method() {
+ return "linkGUIItems";
+ }
+
+ @Override
+ public void generateCode(BuildPlan buildPlan, MethodBuilder methodBuilder, String s, TypeElement typeElement) {
+ buildPlan.addImport("de.steamwar.bausystem.features.gui.BauGUI");
+ methodBuilder.addLine("BauGUI.addItem(" + s + ");");
+ }
+}
diff --git a/BauSystem/BauSystem_Linkage/src/de/steamwar/linkage/types/BoundingBoxLoader_GENERIC.java b/BauSystem/BauSystem_Linkage/src/de/steamwar/linkage/types/BoundingBoxLoader_GENERIC.java
new file mode 100644
index 00000000..a86d0cc5
--- /dev/null
+++ b/BauSystem/BauSystem_Linkage/src/de/steamwar/linkage/types/BoundingBoxLoader_GENERIC.java
@@ -0,0 +1,39 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2022 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.linkage.types;
+
+import de.steamwar.linkage.LinkageType;
+import de.steamwar.linkage.plan.BuildPlan;
+import de.steamwar.linkage.plan.MethodBuilder;
+
+import javax.lang.model.element.TypeElement;
+
+public class BoundingBoxLoader_GENERIC implements LinkageType {
+
+ @Override
+ public String method() {
+ return "linkBoundingBox";
+ }
+
+ @Override
+ public void generateCode(BuildPlan buildPlan, MethodBuilder method, String instance, TypeElement typeElement) {
+ method.addLine(instance + ".load();");
+ }
+}
diff --git a/BauSystem/BauSystem_Linkage/src/de/steamwar/linkage/types/ConfigConverter_GENERIC.java b/BauSystem/BauSystem_Linkage/src/de/steamwar/linkage/types/ConfigConverter_GENERIC.java
new file mode 100644
index 00000000..58db84e7
--- /dev/null
+++ b/BauSystem/BauSystem_Linkage/src/de/steamwar/linkage/types/ConfigConverter_GENERIC.java
@@ -0,0 +1,40 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2022 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.linkage.types;
+
+import de.steamwar.linkage.LinkageType;
+import de.steamwar.linkage.plan.BuildPlan;
+import de.steamwar.linkage.plan.MethodBuilder;
+
+import javax.lang.model.element.TypeElement;
+
+public class ConfigConverter_GENERIC implements LinkageType {
+
+ @Override
+ public String method() {
+ return "link";
+ }
+
+ @Override
+ public void generateCode(BuildPlan buildPlan, MethodBuilder methodBuilder, String s, TypeElement typeElement) {
+ buildPlan.addImport("de.steamwar.bausystem.configplayer.Config");
+ methodBuilder.addLine("Config.addConfigConverter(" + s + ");");
+ }
+}
diff --git a/BauSystem/BauSystem_Linkage/src/de/steamwar/linkage/types/FAWEMaskParser_GENERIC.java b/BauSystem/BauSystem_Linkage/src/de/steamwar/linkage/types/FAWEMaskParser_GENERIC.java
new file mode 100644
index 00000000..fac3093e
--- /dev/null
+++ b/BauSystem/BauSystem_Linkage/src/de/steamwar/linkage/types/FAWEMaskParser_GENERIC.java
@@ -0,0 +1,41 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2022 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.linkage.types;
+
+import de.steamwar.linkage.LinkageType;
+import de.steamwar.linkage.plan.BuildPlan;
+import de.steamwar.linkage.plan.MethodBuilder;
+
+import javax.lang.model.element.TypeElement;
+
+public class FAWEMaskParser_GENERIC implements LinkageType {
+
+ @Override
+ public String method() {
+ return "link";
+ }
+
+ @Override
+ public void generateCode(BuildPlan buildPlan, MethodBuilder methodBuilder, String s, TypeElement typeElement) {
+ methodBuilder.addLine("if (org.bukkit.Bukkit.getPluginManager().getPlugin(\"FastAsyncWorldEdit\") != null) {");
+ methodBuilder.addLine(" " + s + ";");
+ methodBuilder.addLine("}");
+ }
+}
diff --git a/BauSystem/BauSystem_Linkage/src/de/steamwar/linkage/types/FAWEPatternParser_GENERIC.java b/BauSystem/BauSystem_Linkage/src/de/steamwar/linkage/types/FAWEPatternParser_GENERIC.java
new file mode 100644
index 00000000..60a1a4ea
--- /dev/null
+++ b/BauSystem/BauSystem_Linkage/src/de/steamwar/linkage/types/FAWEPatternParser_GENERIC.java
@@ -0,0 +1,41 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2022 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.linkage.types;
+
+import de.steamwar.linkage.LinkageType;
+import de.steamwar.linkage.plan.BuildPlan;
+import de.steamwar.linkage.plan.MethodBuilder;
+
+import javax.lang.model.element.TypeElement;
+
+public class FAWEPatternParser_GENERIC implements LinkageType {
+
+ @Override
+ public String method() {
+ return "link";
+ }
+
+ @Override
+ public void generateCode(BuildPlan buildPlan, MethodBuilder methodBuilder, String s, TypeElement typeElement) {
+ methodBuilder.addLine("if (org.bukkit.Bukkit.getPluginManager().getPlugin(\"FastAsyncWorldEdit\") != null) {");
+ methodBuilder.addLine(" " + s + ";");
+ methodBuilder.addLine("}");
+ }
+}
diff --git a/BauSystem/BauSystem_Linkage/src/de/steamwar/linkage/types/LuaLib_GENERIC.java b/BauSystem/BauSystem_Linkage/src/de/steamwar/linkage/types/LuaLib_GENERIC.java
new file mode 100644
index 00000000..570f6a33
--- /dev/null
+++ b/BauSystem/BauSystem_Linkage/src/de/steamwar/linkage/types/LuaLib_GENERIC.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.linkage.types;
+
+import de.steamwar.linkage.LinkageType;
+import de.steamwar.linkage.plan.BuildPlan;
+import de.steamwar.linkage.plan.MethodBuilder;
+
+import javax.lang.model.element.TypeElement;
+
+public class LuaLib_GENERIC implements LinkageType {
+
+ @Override
+ public String method() {
+ return "link";
+ }
+
+ @Override
+ public void generateCode(BuildPlan buildPlan, MethodBuilder methodBuilder, String s, TypeElement typeElement) {
+ buildPlan.addImport("de.steamwar.bausystem.features.script.lua.SteamWarLuaPlugin");
+ methodBuilder.addLine("SteamWarLuaPlugin.add(" + s + ");");
+ }
+}
diff --git a/BauSystem/BauSystem_Linkage/src/de/steamwar/linkage/types/PanzernAlgorithm_GENERIC.java b/BauSystem/BauSystem_Linkage/src/de/steamwar/linkage/types/PanzernAlgorithm_GENERIC.java
new file mode 100644
index 00000000..bf701923
--- /dev/null
+++ b/BauSystem/BauSystem_Linkage/src/de/steamwar/linkage/types/PanzernAlgorithm_GENERIC.java
@@ -0,0 +1,40 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2022 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.linkage.types;
+
+import de.steamwar.linkage.LinkageType;
+import de.steamwar.linkage.plan.BuildPlan;
+import de.steamwar.linkage.plan.MethodBuilder;
+
+import javax.lang.model.element.TypeElement;
+
+public class PanzernAlgorithm_GENERIC implements LinkageType {
+
+ @Override
+ public String method() {
+ return "linkPanzern";
+ }
+
+ @Override
+ public void generateCode(BuildPlan buildPlan, MethodBuilder methodBuilder, String s, TypeElement typeElement) {
+ buildPlan.addImport("de.steamwar.bausystem.features.slaves.panzern.Panzern");
+ methodBuilder.addLine("Panzern.add(" + s + ");");
+ }
+}
diff --git a/BauSystem/BauSystem_Linkage/src/de/steamwar/linkage/types/ScoreboardElement_GENERIC.java b/BauSystem/BauSystem_Linkage/src/de/steamwar/linkage/types/ScoreboardElement_GENERIC.java
new file mode 100644
index 00000000..ffd6382f
--- /dev/null
+++ b/BauSystem/BauSystem_Linkage/src/de/steamwar/linkage/types/ScoreboardElement_GENERIC.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.linkage.types;
+
+import de.steamwar.linkage.LinkageType;
+import de.steamwar.linkage.plan.BuildPlan;
+import de.steamwar.linkage.plan.MethodBuilder;
+
+import javax.lang.model.element.TypeElement;
+
+public class ScoreboardElement_GENERIC implements LinkageType {
+
+ @Override
+ public String method() {
+ return "link";
+ }
+
+ @Override
+ public void generateCode(BuildPlan buildPlan, MethodBuilder methodBuilder, String s, TypeElement typeElement) {
+ buildPlan.addImport("de.steamwar.bausystem.features.world.BauScoreboard");
+ methodBuilder.addLine("BauScoreboard.addElement(" + s + ");");
+ }
+}
diff --git a/BauSystem/BauSystem_Main/build.gradle.kts b/BauSystem/BauSystem_Main/build.gradle.kts
new file mode 100644
index 00000000..4f3955ed
--- /dev/null
+++ b/BauSystem/BauSystem_Main/build.gradle.kts
@@ -0,0 +1,67 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2024 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 .
+ */
+
+plugins {
+ id("java")
+ id("base")
+}
+
+group = "de.steamwar"
+version = ""
+
+tasks.compileJava {
+ options.encoding = "UTF-8"
+}
+
+java {
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
+}
+
+sourceSets {
+ main {
+ java {
+ srcDirs("src/", "build/generated/sources/annotationProcessor/java/main/")
+ }
+ resources {
+ srcDirs("src/")
+ exclude("**/*.java", "**/*.kt")
+ }
+ }
+}
+
+dependencies {
+ compileOnly("org.projectlombok:lombok:1.18.32")
+ annotationProcessor("org.projectlombok:lombok:1.18.32")
+
+ implementation(project(":BauSystem:BauSystem_Linkage"))
+ annotationProcessor(project(":BauSystem:BauSystem_Linkage"))
+ compileOnly(project(":SpigotCore"))
+ annotationProcessor(project(":SpigotCore"))
+
+ compileOnly("de.steamwar:spigot:1.20")
+ compileOnly("de.steamwar:fastasyncworldedit:1.18")
+ compileOnly("de.steamwar:axiompaper:RELEASE")
+
+ implementation("org.luaj:luaj-jse:3.0.1")
+ implementation(files("$projectDir/../libs/YAPION-SNAPSHOT.jar"))
+ compileOnly("org.spigotmc:spigot-api:1.20-R0.1-SNAPSHOT")
+ compileOnly("com.mojang:authlib:1.5.25")
+ compileOnly("io.netty:netty-all:4.1.68.Final")
+}
diff --git a/BauSystem/BauSystem_Main/src/BauSystem.properties b/BauSystem/BauSystem_Main/src/BauSystem.properties
new file mode 100644
index 00000000..148cccd3
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/BauSystem.properties
@@ -0,0 +1,1014 @@
+#
+# This file is a part of the SteamWar software.
+#
+# Copyright (C) 2021 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 .
+#
+PREFIX=§eBau§8System§8»
+TIME=HH:mm:ss
+DATE=........
+COMMAND_HELP_HEAD=§7---=== (§e{0}§7) ===---
+ONLY_SCHEMS=§cFolders are unselectable
+PAGE_LIST=§e Page ({0}/{1}) »»
+LIST_PREVIOUS_PAGE=§ePrevious page
+LIST_NEXT_PAGE=§eNext page
+# Permissions
+NO_PERMISSION=You are not allowed to use that here
+SPECTATOR=§fSpectator
+# Scoreboard
+SCOREBOARD_TIME=Time
+SCOREBOARD_REGION=Region
+SCOREBOARD_TRACE=Trace
+SCOREBOARD_LOADER=Loader
+SCOREBOARD_TPS=TPS
+SCOREBOARD_TPS_FROZEN=§eFrozen
+SCOREBOARD_TRACE_TICKS=Ticks
+SCOREBOARD_TECHHIDER=TechHider§8: §aOn
+SCOREBOARD_XRAY=XRay§8: §aOn
+SCOREBOARD_LOCK_TEAM=Bau Lock§8: §eTeam
+SCOREBOARD_LOCK_TEAM_AND_SERVERTEAM=Bau Lock§8: §e(Server) Team
+SCOREBOARD_LOCK_SERVERTEAM=Bau Lock§8: §eServer Team
+SCOREBOARD_LOCK_NOBODY=Bau Lock§8: §cNobody
+# Flags
+FLAG_COLOR=Color
+FLAG_TNT=TNT
+FLAG_FIRE=Fire
+FLAG_FREEZE=Freeze
+FLAG_PROTECT=Protect
+FLAG_ITEMS=Items
+FLAG_NO_GRAVITY = No Gravity
+FLAG_TESTBLOCK=Testblock
+FLAG_CHANGED=Changed
+FLAG_FIRE_ALLOW=§con
+FLAG_FIRE_DENY=§aoff
+FLAG_FREEZE_ACTIVE=§aon
+FLAG_FREEZE_INACTIVE=§coff
+FLAG_PROTECT_ACTIVE=§aon
+FLAG_PROTECT_INACTIVE=§coff
+FLAG_NO_GRAVITY_ACTIVE = §aon
+FLAG_NO_GRAVITY_INACTIVE = §coff
+FLAG_TNT_ALLOW=§aon
+FLAG_TNT_DENY=§coff
+FLAG_TNT_ONLY_TB=§7no §ebuild area
+FLAG_TNT_ONLY_BUILD=§7no §etestblock area
+FLAG_ITEMS_ACTIVE=§aon
+FLAG_ITEMS_INACTIVE=§coff
+FLAG_COLOR_WHITE=§fWhite
+FLAG_COLOR_ORANGE=§6Orange
+FLAG_COLOR_MAGENTA=§dMagenta
+FLAG_COLOR_LIGHT_BLUE=§bLight blue
+FLAG_COLOR_YELLOW=§eYellow
+FLAG_COLOR_LIME=§aLime
+## This cannot be converted
+FLAG_COLOR_PINK=§ePink
+FLAG_COLOR_GRAY=§8Gray
+FLAG_COLOR_LIGHT_GRAY=§7Light gray
+FLAG_COLOR_CYAN=§3Cyan
+FLAG_COLOR_PURPLE=§5Purple
+FLAG_COLOR_BLUE=§1Blue
+## This cannot be converted
+FLAG_COLOR_BROWN=§eBrown
+FLAG_COLOR_GREEN=§2Green
+FLAG_COLOR_RED=§cRed
+FLAG_COLOR_BLACK=§0Black
+FLAG_TESTBLOCK_NO_VALUE=§eNo Value
+FLAG_TESTBLOCK_NORTH=§eNorth
+FLAG_TESTBLOCK_SOUTH=§eSouth
+FLAG_CHANGED_NO_CHANGE=§cNo
+FLAG_CHANGED_HAS_CHANGE=§aYes
+# Region
+REGION_TYPE_NORMAL=Normal
+REGION_TYPE_BUILD=Build area
+REGION_TYPE_ONLY_TB=Dummy
+# AttributesCopy
+ATTRIBUTES_CANT_COPY=§cYou need to hold the same item type and hover over the same block to copy.
+ATTRIBUTES_NO_COPY=§cNo attributes to copy.
+ATTRIBUTES_COPIED=§eAttributes copied.
+ATTRIBUTE_REMOVE_COMMAND_HELP=§8/§eattributeremove §8[§eattribute§8|§7all§8|§7*§8]
+ATTRIBUTE_REMOVE_ALL=§eAll attributes removed.
+ATTRIBUTE_REMOVE_SINGLE=§eAttribute §7{0}§e removed.
+ATTRIBUTE_REMOVE_NOT_FOUND=§cAttribute not found
+# AutoStart
+AUTOSTART_COMMAND_HELP=§8/§etimer §8- §7Retrieve AutostartTimer Tool
+AUTOSTART_ITEM_NAME=§eAutostartTimer
+AUTOSTART_ITEM_LORE=§eRight Click Block §8- §7Start Timer
+AUTOSTART_MESSAGE_NO_REGION=§cYou are not inside any region
+AUTOSTART_MESSAGE_RESET=§eAutostartTimer restarted
+AUTOSTART_MESSAGE_START=§eAutostartTimer started
+AUTOSTART_MESSAGE_RESULT1=§eTime §7until §eexplosion §7at enemy§8:§e {0}§7 game ticks
+AUTOSTART_MESSAGE_RESULT2=§7Time difference in §egame-ticks §7until {0} seconds§8:§e {1}
+AUTOSTART_MESSAGE_RESULT3=§7positive, if too few, negative if too many
+# Backup
+BACKUP_HELP_CREATE=§8/§ebackup create §8- §7Create a region backup
+BACKUP_HELP_LOAD=§8/§ebackup load §8[§7BackupName§8] §8- §7Load a region backup
+BACKUP_HELP_LIST=§8/§ebackup list §8- §7List all region backups
+BACKUP_HELP_GUI=§8/§ebackup gui §8- §7Open the backup GUI
+BACKUP_REGION_NO_REGION=§cYou are not inside any region
+BACKUP_CREATE_SUCCESS=§7Backup created
+BACKUP_CREATE_FAILURE=§cBackup failed
+BACKUP_CREATE_NO_CHANGE=§7No changes to save
+BACKUP_LIST_HEAD=§7---=== (§eBackup §7{0}§7) ===---
+BACKUP_LIST_ENTRY=§7{0} §e[Load]
+BACKUP_LOAD_FAILURE=§cBackup load failed
+BACKUP_LOAD=§7Backup loaded
+BACKUP_INV_NAME=§eBackup
+BACKUP_ITEM_NAME=§eBackup §7from §e{0}
+BACKUP_LORE=§eClick to load
+# Bau
+BAU_COMMAND_HELP_INFO=§8/§ebau info §8- §7Alias for §8/§ebauinfo
+BAU_INFO_ITEM_NAME=§eBau-Management
+## This is used in BauInfoBauGuiItem.java
+BAU_INFO_ITEM_LORE_TNT=§7TNT§8: §e{0}
+BAU_INFO_ITEM_LORE_FREEZE=§7Freeze§8: §e{0}
+BAU_INFO_ITEM_LORE_FIRE=§7Fire§8: §e{0}
+BAU_INFO_ITEM_LORE_COLOR=§7Color§8: §e{0}
+BAU_INFO_ITEM_LORE_PROTECT=§7Protect§8: §e{0}
+BAU_INFO_ITEM_LORE_ITEMS=§7Items§8: §e{0}
+BAU_INFO_ITEM_LORE_NO_GRAVITY = §8NoGravity§8: §e{0}
+BAU_INFO_COMMAND_HELP=§8/§ebauinfo §8- §7Information regarding this build server
+BAU_INFO_COMMAND_OWNER=§7Owner§8: §e{0}
+BAU_INFO_COMMAND_MEMBER=§7{0} §8[§7{1}§8]§8: §e{2}
+BAU_INFO_COMMAND_FLAG=§7{0}§8: §7{1}
+BAU_INFO_COMMAND_TPS=§7TPS§8:§e
+# Countingwand
+COUNTINGWAND_COMMAND_HELP=§8/§ecountingwand §8- §7Receive a CountingWand
+COUNTINGWAND_ITEM_NAME=§eMeterstick
+COUNTINGWAND_ITEM_LORE1=§eLeft-Click §8- §7Set the first position
+COUNTINGWAND_ITEM_LORE2=§eRicht-Click §8- §7Set the second position
+COUNTINGWAND_MESSAGE_RCLICK=§7First position at: §8[§7{0}§8, §7{1}§8, §7{2}§8] ({3}§8) ({4}§8)
+COUNTINGWAND_MESSAGE_LCLICK=§7Second position at: §8[§7{0}§8, §7{1}§8, §7{2}§8] ({3}§8) ({4}§8)
+COUNTINGWAND_MESSAGE_VOLUME=§e{0}
+COUNTINGWAND_MESSAGE_DIMENSION=§e{0}§8, §e{1}§8, §e{2}
+# Design Endstone
+DESIGN_ENDSTONE_COMMAND_HELP=§8/§edesignendstone §8- §7Highlight endstone in design
+DESIGN_ENDSTONE_REGION_ERROR=§cThis region has no build area
+DESIGN_ENDSTONE_ENABLE=§aEndstone is highlighted
+DESIGN_ENDSTONE_DISABLE=§cEndstone is no longer hightlighted
+# Detonator
+DETONATOR_LOC_REMOVE=§e{0} removed
+DETONATOR_LOC_ADD=§e{0} added
+DETONATOR_BUTTON_SWITCH=Lever
+DETONATOR_BUTTON_WOOD_BUTTON=Button
+DETONATOR_BUTTON_STONE_BUTTON=Button
+DETONATOR_BUTTON_PRESSURE_PLATE=Pressure plate
+DETONATOR_BUTTON_WEIGHTED-PRESSURE_PLATE=Pressure plate
+DETONATOR_BUTTON_TRIPWIRE=Tripwire
+DETONATOR_BUTTON_NOTEBLOCK=Noteblock
+DETONATOR_BUTTON_DAYLIGHTSENSOR=Daylight sensor
+DETONATOR_BUTTON_POWERABLE=Activateable block
+DETONATOR_BUTTON_INVALID=Invalid
+DETONATOR_WAND_NAME=§eDetonator
+DETONATOR_WAND_LORE_1=§eLeft-Click §8- §7Sets a point to be activated
+DETONATOR_WAND_LORE_2=§eLeft-Click + Shift §8- §eAdds a point
+DETONATOR_WAND_LORE_3=§eRight-Click §8- §eActivates all points
+DETONATOR_HELP_WAND=§8/§edetonator wand §8-§7 Receive a Detonator
+DETONATOR_HELP_CLICK=§8/§edetonator click §8-§7 Activate a Detonator (main-hand -> hotbar -> inventory)
+DETONATOR_HELP_CLEAR=§8/§edetonator clear §8-§7 Clear a Detonator
+DETONATOR_HELP_AUTOSTART=§8/§edetonator autostart §8-§7 Enable a Autostarttester automatically
+DETONATOR_AUTOSTART_ENABLE=§7Autostart with detonate §aenabled
+DETONATOR_AUTOSTART_DISABLE=§7Autostart with detonate §cdisabled
+DETONATOR_POINT_ACT=§eSingle point activated
+DETONATOR_POINTS_ACT=§e{0} points activated
+DETONATOR_INVALID_POINT=§cOne point could not be activated
+DETONATOR_INVALID_POINTS=§c{0} points could not be activated
+DETONATOR_INVALID_BLOCK=§eThe block could not be addded
+# Hotbar
+HOTBAR_HELP_GENERIC=§7Saves a hotbar. While joining a bau with an empty inventory this hotbar will be used.
+HOTBAR_HELP_SAVE=§8/§ehotbar save §8-§7 Saves your current hotbar
+HOTBAR_HELP_LOAD=§8/§ehotbar load §8-§7 Loads the saved hotbar
+HOTBAR_HELP_SHOW=§8/§ehotbar show §8-§7 Displays the saved hotbar
+HOTBAR_SAVED=§7Hotbar saved
+HOTBAR_LOADED=§7Hotbar loaded
+HOTBAR_INVENTORY=Standard hotbar
+# GUI
+GUI_EDITOR_ITEM_NAME=§eGui editor
+GUI_NAME=Bau GUI
+GUI_ITEM_LORE1=§7Use this item to open the bau gui
+GUI_ITEM_LORE2=§7or press swap hands twice.
+GUI_EDITOR_TITLE=Bau GUI Editor
+GUI_EDITOR_ITEM_ROW_P=§e+1 Row
+GUI_EDITOR_ITEM_ROW_M=§e-1 Row
+GUI_EDITOR_ITEM_TRASH=§cTrashcan
+GUI_EDITOR_ITEM_TRASH_LORE=§7Drop item here
+GUI_EDITOR_ITEM_MORE=§eMore items
+GUI_EDITOR_ITEM_CLOSE=§eClose
+GUI_EDITOR_TITLE_MORE=Select item
+# Script
+## Errors
+SCRIPT_ERROR_GUI=§cError in parsing script: Line {0}
+SCRIPT_ERROR_GLOBAL=§cError in global script: Line {0}
+SCRIPT_ERROR_CLICK=§cError in script: Line {0}
+SCRIPT_ERROR_ONLY_IN_GLOBAL=§cThis function is only available in global scripts
+## CustomScript
+SCRIPT_HOTKEY_ITEM_NAME=§7Hotkey§8: §e{0}
+SCRIPT_EVENT_ITEM_NAME=§7Event§8: §e{0}
+SCRIPT_COMMAND_ITEM_NAME=§7Command§8: §e/{0}
+## Script Menu GUI
+SCRIPT_MENU_GUI_ITEM_LORE_1=§7Click to retrieve
+SCRIPT_MENU_GUI_ITEM_LORE_2=§7Shift-Click to copy
+SCRIPT_MENU_GUI_ITEM_LORE_3=§7Right-Click to edit
+SCRIPT_MENU_GUI_ITEM_LORE_4=§7Middle-Click to preview
+SCRIPT_MENU_GUI_NAME=§eScript-Menu
+SCRIPT_MENU_GUI_ITEM_ADD_NAME=§eInsert
+SCRIPT_MENU_GUI_ITEM_ADD_LORE=§7Click with a book to insert
+SCRIPT_MENU_GUI_ENTER_NAME=§eEnter a name
+SCRIPT_DEPRECATED=§cThe function §8\'§e{0}§8\'§c is deprecated and will be removed in the future. Please use §8\'§e{1}§8\'§c instead.
+# Shield Printing
+SHIELD_PRINTING_HELP_START=§8/§eshieldprinting start §8- §7Starts the shield printing
+SHIELD_PRINTING_HELP_COPY=§8/§eshieldprinting copy §8- §7Copies the shield configuration
+SHIELD_PRINTING_HELP_APPLY=§8/§eshieldprinting apply §8- §7Applies the shield configuration
+SHIELD_PRINTING_HELP_STOP=§8/§eshieldprinting stop §8- §7Stops the shield printing
+SHIELD_PRINTING_HELP_STEP_1=§81. §7Paste the schematic you want to use
+SHIELD_PRINTING_HELP_STEP_2=§82. §7Start the shield printing with §8/§eshieldprinting start
+SHIELD_PRINTING_HELP_STEP_3=§83. §7Wait until the shield printing is finished
+SHIELD_PRINTING_HELP_STEP_4=§84. §7Edit the shields if necessary
+SHIELD_PRINTING_HELP_STEP_5=§85. §7Copy the shields printing with §8/§eshieldprinting copy
+SHIELD_PRINTING_HELP_STEP_6=§86. §7Paste the original schematic
+SHIELD_PRINTING_HELP_STEP_7=§87. §7Apply the shield printing with §8/§eshieldprinting apply
+SHIELD_PRINTING_NO_REGION=§cYou are not in a region.
+SHIELD_PRINTING_NOT_RUNNING=§cThe shield printing is not running.
+SHIELD_PRINTING_BOSSBAR=§fMovements: {0}
+SHIELD_PRINTING_BOSSBAR_COPIED=§fMovements: {0} Copied: {1}
+SHIELD_PRINTING_GUI_NAME=§7Shield Printing
+SHIELD_PRINTING_GUI_APPLY=§aApply
+SHIELD_PRINTING_GUI_STATE_PREVIOUS=§7R-Click§8: §7Previous
+SHIELD_PRINTING_GUI_STATE_NEXT=§7L-Click§8: §7Next
+SHIELD_PRINTING_GUI_STATE_ACTIVE=§e> §7{0}
+SHIELD_PRINTING_GUI_STATE_INACTIVE=§8> §7{0}
+SHIELD_PRINTING_GUI_STATE_FROM_ORIGINAL=Original
+SHIELD_PRINTING_GUI_STATE_FROM_COPY=Copy
+SHIELD_PRINTING_GUI_STATE_ALWAYS_ON=On
+SHIELD_PRINTING_GUI_STATE_ALWAYS_OFF=Off
+SHIELD_PRINTING_GUI_STATE_ALWAYS_OPEN=Open
+SHIELD_PRINTING_GUI_STATE_ALWAYS_CLOSED=Closed
+SHIELD_PRINTING_GUI_STATE_FENCE=§7{0} §fFence Connections
+SHIELD_PRINTING_GUI_STATE_OPENABLE=§7{0} §fOpened
+SHIELD_PRINTING_GUI_STATE_PISTON=§7{0} §fExtended
+SHIELD_PRINTING_GUI_STATE_POWERABLE=§7{0} §fPowered
+SHIELD_PRINTING_GUI_STATE_WALL=§7{0} §fWall Connections
+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
+# Simulator
+SIMULATOR_HELP=§8/§esimulator §8-§7 Gives you the simulator wand
+SIMULATOR_CREATE_HELP=§8/§esimulator create §8[§7name§8] §8-§7 Create a new simulator
+SIMULATOR_CHANGE_HELP=§8/§esimulator change §8-§7 Change your simulator wand selection
+SIMULATOR_DELETE_HELP=§8/§esimulator delete §8[§7name§8] §8-§7 Deletes the simulator
+SIMULATOR_START_HELP=§8/§esimulator start §8[§7name§8] §8-§7 Starts the simulator
+SIMULATOR_COPY_HELP=§8/§esimulator copy §8[§7to-copy§8] §8[§7name§8] §8-§7 Copy the simulator
+SIMULATOR_GUI_ITEM_NAME=§eTNT Simulator
+SIMULATOR_NO_SIM_IN_HAND=§cNo simulator item selected
+SIMULATOR_GUI_SELECT_SIM=Simulator selection
+SIMULATOR_GUI_CREATE_SIM=§eCreate simulator
+SIMULATOR_GUI_CREATE_SIM_GUI=Create simulator
+SIMULATOR_NAME_ALREADY_EXISTS=§cSimulator already exists
+SIMULATOR_NAME_INVALID=§cInvalid name
+SIMULATOR_ERROR_COPY=§cCopy failed
+SIMULATOR_NOT_EXISTS=§cSimulator does not exist
+SIMULATOR_CREATE=§aSimulator created
+SIMULATOR_EDIT_LOCATION=§7Edit position
+SIMULATOR_EDIT_PROPERTIES=§7Edit properties
+SIMULATOR_EDIT_OTHER=§7Edit other
+SIMULATOR_EDIT_GROUP=§7Edit group
+SIMULATOR_EDIT_GROUP_MENU=§eEdit group
+SIMULATOR_WAND_NAME=§eSimulator
+SIMULATOR_WAND_NAME_SELECTED=§7Simulator §8- §e{0}
+SIMULATOR_WAND_LORE_1=§eRight click §8- §7Adds a position
+SIMULATOR_WAND_LORE_2=§eSneaking §8- §7Free movement
+SIMULATOR_WAND_LORE_3=§eLeft click §8- §7Start the simulation
+SIMULATOR_WAND_LORE_4=§eRight click in air §8- §7Opens the gui
+SIMULATOR_WAND_LORE_5=§eDouble Sneak §8- §7Swap between TNT and Redstone Block
+SIMULATOR_REGION_FROZEN=§cSimulator cannot be used inside frozen regions
+## Other
+SIMULATOR_PLUS_ONE=§7+1
+SIMULATOR_PLUS_PIXEL_SHIFT=§eShift §7Click for §e+0,0625
+SIMULATOR_PLUS_FIVE_SHIFT=§eShift §7Click for §e+5
+SIMULATOR_MINUS_ONE=§7-1
+SIMULATOR_MINUS_PIXEL_SHIFT=§eShift §7Click for §e-0,0625
+SIMULATOR_MINUS_FIVE_SHIFT=§eShift §7Click for §e-5
+SIMULATOR_POSITION_X=§7x-Position
+SIMULATOR_POSITION_Y=§7y-Position
+SIMULATOR_POSITION_Z=§7z-Position
+SIMULATOR_BACK=§eBack
+SIMULATOR_GUI_TOTAL_TNT=§7Total TNT§8: §e{0}
+SIMULATOR_DELETED=§cSimulator deleted
+## GUI
+SIMULATOR_POSITION_EDIT=§eEdit position
+SIMULATOR_POSITION_ADD=§eSet position
+SIMULATOR_GUI_TNT_SPAWN_NAME=§eTNT
+SIMULATOR_GUI_TNT_SPAWN_LORE_1=§7TNT-Count§8: §e{0}
+SIMULATOR_GUI_TNT_SPAWN_LORE_2=§7Tick§8: §e{0}
+SIMULATOR_GUI_TNT_SPAWN_LORE_3=§7Lifespan§8: §e{0}
+SIMULATOR_GUI_TNT_SPAWN_LORE_4=§7
+SIMULATOR_GUI_TNT_SPAWN_LORE_5=§7x§8: §e{0}
+SIMULATOR_GUI_TNT_SPAWN_LORE_6=§7y§8: §e{0}
+SIMULATOR_GUI_TNT_SPAWN_LORE_7=§7z§8: §e{0}
+SIMULATOR_GUI_TNT_GROUP_NAME=§eTNT group
+SIMULATOR_GUI_TNT_GROUP_LORE_1=§7Element count§8: §e{0}
+SIMULATOR_GUI_TNT_GROUP_LORE_2=§7Tick§8: §e{0}
+SIMULATOR_GUI_TNT_GROUP_LORE_3=§7
+SIMULATOR_GUI_TNT_GROUP_LORE_4=§7x§8: §e{0}
+SIMULATOR_GUI_TNT_GROUP_LORE_5=§7y§8: §e{0}
+SIMULATOR_GUI_TNT_GROUP_LORE_6=§7z§8: §e{0}
+SIMULATOR_GUI_TNT_DISABLED=§cDisabled
+SIMULATOR_GUI_NAME=Simulator
+SIMULATOR_GUI_DELETE=§cDelete TNT
+SIMULATOR_GUI_AUTO_TRACE=§eAutoTrace§8: §7{0}
+SIMULATOR_GUI_MOVE_ALL=§eMove all
+SIMULATOR_ALIGNMENT_CENTER=§7Alignment§8: §eCenter
+SIMULATOR_ALIGNMENT_POSITIVE_X=§7Alignment§8: §ePositive X
+SIMULATOR_ALIGNMENT_NEGATIVE_X=§7Alignment§8: §eNegative X
+SIMULATOR_ALIGNMENT_POSITIVE_Z=§7Alignment§8: §ePositive Z
+SIMULATOR_ALIGNMENT_NEGATIVE_Z=§7Alignment§8: §eNegative Z
+SIMULATOR_MOVE_ALL_GUI_NAME=Move TNT
+SIMULATOR_TNT_SPAWN_GUI_NAME=Configure TNT {0}
+SIMULATOR_TNT_SPAWN_EDIT_LOCATION=- Location
+SIMULATOR_TNT_SPAWN_EDIT_PROPERTIES=- Properties
+SIMULATOR_TNT_SPAWN_EDIT_OTHER=- Other
+SIMULATOR_TNT_SPAWN_LORE=§eClick to change
+SIMULATOR_TNT_SPAWN_COUNT=§7TNT-Count §8- §e{0}
+SIMULATOR_TNT_SPAWN_COUNT_ANVIL_GUI_NAME=TNT-Count
+SIMULATOR_TNT_SPAWN_TICK=§7Tick §8- §e{0}
+SIMULATOR_TNT_SPAWN_TICK_ANVIL_GUI_NAME=Tick offset
+SIMULATOR_TNT_SPAWN_FUSE=§7Lifespan §8- §e{0}
+SIMULATOR_TNT_SPAWN_FUSE_ANVIL_GUI_NAME=Fuse-Ticks
+SIMULATOR_TNT_SPAWN_VELOCITY_NAME=§7TNT
+SIMULATOR_TNT_SPAWN_VELOCITY_X=§7TNT §eJump X §8- {0}
+SIMULATOR_TNT_SPAWN_VELOCITY_Y=§7TNT §eJump Y §8- {0}
+SIMULATOR_TNT_SPAWN_VELOCITY_Z=§7TNT §eJump Z §8- {0}
+SIMULATOR_TNT_SPAWN_VELOCITY_ON=§aon
+SIMULATOR_TNT_SPAWN_VELOCITY_OFF=§coff
+SIMULATOR_TNT_SPAWN_POSITION_X=§7x-Position §8- §e{0}
+SIMULATOR_TNT_SPAWN_POSITION_Y=§7y-Position §8- §e{0}
+SIMULATOR_TNT_SPAWN_POSITION_Z=§7z-Position §8- §e{0}
+SIMULATOR_TNT_SPAWN_ACTIVATED_NAME=§7Primed by
+SIMULATOR_TNT_SPAWN_ACTIVATED_WITH=§7Primed by §8- §e{0}
+SIMULATOR_TNT_SPAWN_ACTIVATED_WITH_COMPARATOR=Comparator
+SIMULATOR_TNT_SPAWN_ACTIVATED_WITH_REPEATER=Repeater
+SIMULATOR_TNT_SPAWN_ACTIVATED_WITH_OBSERVER=Observer
+SIMULATOR_TNT_SPAWN_INACTIVE=§7> §7{0}
+SIMULATOR_TNT_SPAWN_ACTIVE=§e> §7{0}
+SIMULATOR_TNT_SPAWN_MATERIAL=§eMaterial
+SIMULATOR_TNT_SPAWN_MATERIAL_LORE_1=§7Current material§8: §e{0}
+SIMULATOR_TNT_SPAWN_MATERIAL_LORE_2=§eLeft-Click §7to change
+SIMULATOR_TNT_SPAWN_MATERIAL_LORE_3=§eRight-Click §7to reset
+SIMULATOR_TNT_SPAWN_ENABLED=§aEnabled
+SIMULATOR_TNT_SPAWN_DISABLED=§cDisabled
+SIMULATOR_MATERIAL_GUI_NAME=Change material
+SIMULATOR_MATERIAL_NAME=§e{0}
+SIMULATOR_MATERIAL_NAME_LORE=§7Material §8- §e{0}
+SIMULATOR_MATERIAL_CLICK=§eClick to choose
+SIMULATOR_TNT_SPAWN_ADD_IGNITION_PHASE=§eAdd prime phase
+SIMULATOR_TNT_SPAWN_ADD_TNT=§eAdd TNT
+SIMULATOR_TNT_SPAWN_REMOVE_TNT=§cRemove
+SIMULATOR_TNT_SPAWN_POSITION_ANVIL_GUI_NAME=Position
+# SmartPlace
+SMART_PLACE_HELP=§8/§esmartplace §8-§7 Toggles SmartPlace
+SMART_PLACE_INFO=§7Places rotatable blocks §eaway§7 from you when §esneaking§7.
+SMART_PLACE_ENABLE=§aSmartPlace activated
+SMART_PLACE_DISABLE=§cSmartPlace deactivated
+# InventoryFiller
+INVENTORY_FILL_HELP=§8/§einventoryfill §8- §7Toggles InventoryFill
+INVENTORY_FILL_INFO=§7Helps you fill containers by looking at them while sneaking and dropping the item. Or just scroll on a container to change the amount of the item inside.
+INVENTORY_FILL_ENABLE=§aInventoryFiller activated
+INVENTORY_FILL_DISABLE=§cInventoryFiller deactivated
+# Killchecker
+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
+BLOCK_COUNTER_HELP_ENABLE=§8/§eblockcounter enable §8- §7Toggles BlockCounter on
+BLOCK_COUNTER_HELP_DISABLE=§8/§eblockcounter disable §8- §7Toggles BlockCounter off
+BLOCK_COUNTER_MESSAGE=§7Damage §8> §e{0} §7Blocks §e{1} §7TNT §e{2} §7Blocks/TNT §e{3} §7Blocks/tick
+BLOCK_COUNTER_MESSAGE_SECOND=§7Damage §8> §e{0} §7Blocks §e{1} §7TNT §e{2} §7Blocks/TNT §e{3} §7Blocks/s
+BLOCK_COUNTER_ENABLE=§7BlockCounter activated
+BLOCK_COUNTER_DISABLE=§7BlockCounter deactivated
+# DepthCounter
+DEPTH_COUNTER_MESSAGE=§7Depth §8> §7
+DEPTH_COUNTER_COUNT={0}{1}§8×{2}{3}§8×{4}{5}
+DEPTH_COUNTER_HOVER=§7X§8×§7Y§8×§7Z
+DEPTH_COUNTER_TNT=§7 TNT§8: §e{0}
+# TPSLimit
+TPSLIMIT_FREEZE_HELP=§8/§etpslimit 0 §8-§7 Freeze TPS
+TPSLIMIT_LIMIT_HELP=§8/§etpslimit §8[§720>x>0.5§8] §8-§7 Slow TPS down
+TPSLIMIT_WARP_HELP=§8/§etpslimit §8[§7x>20§8] §8-§7 Speed TPS up
+TPSLIMIT_DEFAULT_HELP=§8/§etpslimit default §8-§7 Set TPS to 20
+TPSLIMIT_HELP=§8/§etpslimit §8-§7 Show current TPS
+TICK_FREEZE_HELP=§8/§etick rate 0 §8-§7 Freeze TPS
+TICK_FREEZE_HELP_2=§8/§etick freeze §8-§7 Freeze TPS
+TICK_UNFREEZE_HELP=§8/§etick unfreeze §8-§7 Set TPS to 20
+TICK_LIMIT_HELP=§8/§etick rate §8[§720>x>0.5§8] §8-§7 Slow TPS down
+TICK_WARP_HELP=§8/§etick rate §8[§7x>20§8] §8-§7 Speed TPS up
+TICK_DEFAULT_HELP=§8/§etick rate default §8-§7 Set TPS to 20
+TICK_HELP=§8/§etick rate §8-§7 Show current TPS
+TICK_STEPPING_HELP=§8/§etick step §8<§7Ticks§8> §8-§7 Step n ticks or 1 forward
+TICK_WARPING_HELP=§8/§etick warp §8<§7Ticks§8> §8<§7TPS§8> §8-§7 Warp n ticks or 1 forward
+TICK_BOSSBAR=§7Skipped §e{0}§8/§7{1}
+TPSLIMIT_GUI_ITEM_NAME=§eTPS limiter
+TPSLIMIT_GUI_ITEM_LORE=§7Currently: §e{0}
+TPSLIMIT_ANVIL_GUI=New TPS limit
+TPSLIMIT_CURRENT=§7Current TPS limit§8: §e{0}
+TPSLIMIT_SET=§eSet TPS limit to {0}
+TPSLIMIT_FROZEN=§eTPS frozen
+# Trace
+TRACE_RECORD=§aon
+TRACE_HAS_TRACES=§ehas Traces
+TRACE_IDLE_AUTO=§eauto
+TRACE_MESSAGE_START=§aTNT-Tracer started
+TRACE_MESSAGE_AUTO_START=§eAuto TNT-Tracer started
+TRACE_MESSAGE_AUTO_STOP=§cAuto TNT-Tracer stopped
+TRACE_MESSAGE_STOP=§cTNT-Tracer stopped
+TRACE_MESSAGE_CLEAR=§cAll TNT-positions deleted
+TRACE_MESSAGE_DELETE=§cTrace TNT-positions deleted
+TRACE_MESSAGE_SHOW=§aAll TNT-positions shown
+TRACE_MESSAGE_HIDE=§cAll TNT-positions hidden
+TRACE_MESSAGE_SHOW_AT=§aTNT-positions shown at {0}
+TRACE_MESSAGE_SHOW_FROM=§aAll TNT-positions shown from {0}
+TRACE_MESSAGE_SHOW_FROM_TO=§aAll TNT-positions shown from {0} to {1}
+TRACE_MESSAGE_SHOW_TO_SMALLER=§cTo must be bigger then from
+TRACE_MESSAGE_CLICK_ISOLATE=§eClick to §aisolate§8/§cunisolate
+TRACE_MESSAGE_ISOLATE=§eTNT Positions have been isolated
+TRACE_MESSAGE_BROADCAST=§e{0} shared his trace show state.
+TRACE_MESSAGE_BROADCAST_HOVER=§eClick to view
+TRACE_MESSAGE_FOLLOW=§aYou are now following {0} Trace show state
+TRACE_MESSAGE_FOLLOW_SELF=§cYou cannot follow yourself!
+TRACE_MESSAGE_UNFOLLOW=§cYou are no longer following a Trace show state
+TRACE_COMMAND_HELP_START=§8/§etrace start §8- §7Starts recording of all TNT-positions
+TRACE_COMMAND_HELP_STOP=§8/§etrace stop §8- §7Stops the TNT-Tracer
+TRACE_COMMAND_HELP_AUTO=§8/§etrace toggleauto §8- §7Automatic start of recording
+TRACE_COMMAND_HELP_SHOW=§8/§etrace show §8<§eParameter§8> - §7Shows all TNT-positions
+TRACE_COMMAND_HELP_SHOW_AT=§8/§etrace show §7at §8<§eTIME§8> - §7Shows all Trace Positions at §8<§eTIME§8>
+TRACE_COMMAND_HELP_SHOW_AT_WITH=§8/§etrace show §7at §8<§eTIME§8> §7with §8<§eParameter§8> - §7Shows all Trace Positions at §8<§eTIME§8>
+TRACE_COMMAND_HELP_SHOW_FROM=§8/§etrace show §7from §8<§eFROM§8> - §7Shows all Trace Positions from §8<§eFROM§8>
+TRACE_COMMAND_HELP_SHOW_FROM_WITH=§8/§etrace show §7from §8<§eFROM§8> §7with §8<§eParameter§8> - §7Shows all Trace Positions from §8<§eFROM§8>
+TRACE_COMMAND_HELP_SHOW_FROM_TO=§8/§etrace show §7from §8<§eFROM§8> §7to §8<§eTO§8> - §7Shows all Trace Positions from §8<§eFROM§8> to §8<§eTO§8>
+TRACE_COMMAND_HELP_SHOW_FROM_TO_WITH=§8/§etrace show §7from §8<§eFROM§8> §7to §8<§eTO§8> §7with §8<§eParameter§8> - §7Shows all Trace Positions from §8<§eFROM§8> to §8<§eTO§8>
+TRACE_COMMAND_HELP_HIDE=§8/§etrace hide §8- §7Hides all TNT-positions
+TRACE_COMMAND_HELP_DELETE=§8/§etrace delete §8[§eTrace§8] §8- §7Deletes all TNT-positions or a Trace
+TRACE_COMMAND_HELP_ISOLATE=§8/§etrace isolate §8[§eTrace§8] §8[§eTNT§8] §8- §7Isolates specific TNTs from the Trace
+TRACE_COMMAND_HELP_BROADCAST=§8/§etrace broadcast §8- §7Share your current Trace show state with others
+TRACE_COMMAND_HELP_FOLLOW=§8/§etrace follow §8[§ePlayer§8] §8- §7Follow a players Trace show state
+TRACE_COMMAND_HELP_UNFOLLOW=§8/§etrace unfollow §8- §7Unfollow the Trace show state
+TRACE_GUI_ITEM_NAME=§eTracer
+TRACE_GUI_ITEM_LORE=§7Status§8: {0}
+TRACE_GUI_NAME=Trace Gui
+TRACE_GUI_TRACE_INACTIVE=§eStart Tracer
+TRACE_GUI_TRACE_ACTIVE=§eStop Tracer
+TRACE_GUI_TRACE_ACTIVE_AUTO=§eAuto-Trace is active
+TRACE_GUI_AUTO_TRACE_INACTIVE=§eacitvate Auto-Tracer
+TRACE_GUI_AUTO_TRACE_ACTIVE=§edeactivate Auto-Tracer
+TRACE_GUI_DELETE=§eDelete trace
+# Loader
+LOADER_SETUP=§eSetup
+LOADER_RUNNING=§aRunning
+LOADER_PAUSE=§7Pause
+LOADER_END=§8Finished
+LOADER_SINGLE=§aSingle
+LOADER_MESSAGE_INTERACT=§e{0} added {1}
+LOADER_MESSAGE_UNINTERACT=§eRemoved Element
+LOADER_BUTTON_TNT=TNT
+LOADER_BUTTON_SWITCH=Lever
+LOADER_BUTTON_WOOD_BUTTON=Wooden Button
+LOADER_BUTTON_STONE_BUTTON=Stone Button
+LOADER_BUTTON_PRESSURE_PLATE=Pressure plate
+LOADER_BUTTON_WEIGHTED_PRESSURE_PLATE=Pressure plate
+LOADER_BUTTON_TRIPWIRE=Tripwire
+LOADER_BUTTON_NOTEBLOCK=Noteblock
+LOADER_BUTTON_DAYLIGHT_DETECTOR=Daylight Detector
+LOADER_BUTTON_COMPARATOR=Comparator
+LOADER_BUTTON_REPEATER=Repeater
+LOADER_BUTTON_LECTERN=Lectern
+LOADER_BUTTON_TRAPDOOR=Trapdoor
+LOADER_BUTTON_DOOR=Door
+LOADER_BUTTON_FENCEGATE=Fencegate
+LOADER_HELP_SETUP=§8/§eloader setup §8- §7Starts recording actions
+LOADER_SETUP_STOP_FIRST=§cPlease stop the current loader first!
+LOADER_HELP_START=§8/§eloader start §8- §7Playback of previously recorded actions
+LOADER_HELP_SINGLE=§8/§7loader single - §7Single playback of previously recoded actions
+LOADER_HELP_PAUSE=§8/§7loader pause §8- §7Pauses Loader
+LOADER_HELP_GUI=§8/§7loader gui §8- §7Shows Loader gui
+LOADER_HELP_STOP=§8/§eloader stop §8- §7Stops recording/playback
+LOADER_HELP_WAIT=§8/§7loader wait §8[§7Ticks§8] - §7Sets wait time between shots
+LOADER_HELP_SPEED=§8/§7loader speed §8[§7Ticks§8] - §7Sets wait time between actions
+LOADER_NO_LOADER=§cYou have no Laoder. Create one with /loader setup
+LOADER_NEW=§7Load your cannon and fire it once, to initialise the loader.
+LOADER_HOW_TO_START=§7Then, execute /§eloader start§7 to start the Loader
+LOADER_ACTIVE=§7The Loader is now active.
+LOADER_STOP=§7The Loader has been stopped.
+LOADER_SINGLE_CMD=§7The Loader does a single playback.
+LOADER_PAUSED=§7The Loader is now paused.
+LOADER_SMALL_TIME=§cThe wait time is too small
+LOADER_NEW_TIME=§7The wait time is now: {0}
+LOADER_NEW_LOAD_TIME=§7The action wait time is now: {0}
+LOADER_NOTHING_RECORDED=§cYou have not recorded anything yet!
+LOADER_GUI_TITLE=Loader GUI
+LOADER_GUI_SHOW_ALL=Show all
+LOADER_GUI_SHOW_INTERACTIONS=Show only Interactions
+LOADER_GUI_SHOW_WAITS=Show only Waits
+LOADER_GUI_SHOW_WAITS_BETWEEN_TNT=Show only Waits between TNT
+LOADER_GUI_SHOW_TNT=Show TNT
+LOADER_GUI_SHOW_WAITS_SET_ALL=§7Wait Time all
+LOADER_GUI_SHOW_WAITS_TITLE=§7Wait Time
+LOADER_GUI_SETTINGS_TITLE=Settings
+LOADER_GUI_COPY_TITLE=Copy amount
+LOADER_GUI_SETTINGS_BACK=§8Back
+LOADER_GUI_SETTINGS_COPY=§7Copy
+LOADER_GUI_SETTINGS_DELETE=§cDelete
+LOADER_GUI_WAIT_TITLE=Settings
+LOADER_GUI_WAIT_BACK=§8Back
+LOADER_GUI_CLICK_TO_EDIT=§7Click to edit
+LOADER_GUI_ITEM_NAME=§7{0}§8: §e{1}
+LOADER_SETTING_NAME=§7{0}
+LOADER_SETTING_MODES=§7Modes§8: §e{0}
+LOADER_SETTING_POWER=§7Power§8: §e{0}
+LOADER_SETTING_TICKS=§7Ticks§8: §e{0}
+LOADER_SETTING_REPEATER=§7Repeater§8: §e{0}
+LOADER_SETTING_WAIT=§7Wait§8: §e{0} Tick(s)
+LOADER_SETTING_WAIT_NAME=Wait
+LOADER_SETTING_TICKS_NAME=Ticks
+LOADER_SETTING_TICKS_REMOVE_ONE=§c-1
+LOADER_SETTING_TICKS_REMOVE_ONE_SHIFT=§7Shift§8: §c-5
+LOADER_SETTING_TICKS_ADD_ONE=§a+1
+LOADER_SETTING_TICKS_ADD_ONE_SHIFT=§7Shift§8: §a+5
+LOADER_SETTING_TNT_NAME=§cTNT
+LOADER_SETTING_TNT_X=§7X§8: §e{0}
+LOADER_SETTING_TNT_Y=§7Y§8: §e{0}
+LOADER_SETTING_TNT_Z=§7Z§8: §e{0}
+LOADER_INTERACTION_NOOP=NOOP
+LOADER_INTERACTION_PLACE=Place
+LOADER_INTERACTION_INTERACT=Interact
+LOADER_INTERACTION_POWERED=Powered
+LOADER_INTERACTION_UNPOWERED=Unpowered
+LOADER_INTERACTION_PAGE_PREV=Previous Page
+LOADER_INTERACTION_PAGE_NEXT=Next Page
+LOADER_INTERACTION_PAGE=Page {0}
+LOADER_INTERACTION_ACTIVE=Active
+LOADER_INTERACTION_INACTIVE=Inactive
+LOADER_INTERACTION_WAIT_FOR=Wait for
+LOADER_INTERACTION_NO_WAIT_FOR=No wait for
+LOADER_INTERACTION_OPEN=Open
+LOADER_INTERACTION_CLOSED=Closed
+LOADER_INTERACTION_COMPARE=Compare
+LOADER_INTERACTION_SUBTRACT=Subtract
+# Loadtimer
+LOADTIMER_HELP_OVERVIEW=§7Compete with your friends loading your cannon and get information about the cannon
+LOADTIMER_HELP_START_1=§8/§eloadtimer start §8-§7 Starts the simple Loadtimer
+LOADTIMER_HELP_START_2=§8/§7loadtimer start §8[§7full/half§8] - §7Starts the Loadtimer in a given mode
+LOADTIMER_HELP_START_3=§7Loadtimer Modes: Full -> Measures from the priming of the first TNT to the explosion of the first propellant. Is better at calculating the shot frequency. Half -> Only measures until activation
+LOADTIMER_HELP_STOP=§8/§eloadtimer stop §8-§7 Stops current Loadtimer
+LOADTIMER_GUI_GLOBAL=§eLoadtimer does not exist in the global region!
+LOADTIMER_GUI_STOP=§eStop Loadtimer
+LOADTIMER_GUI_START=§eStart Loadtimer
+LOADTIMER_GUI_TITLE=Loadtimer Mode
+LOADTIMER_GUI_FULL=§eFull
+LOADTIMER_GUI_HALF=§eHalf
+LOADTIMER_WAITING=§7Place a TNT to start...
+LOADTIMER_BOSSBAR=§7Tick: §e{0}§7(§e{1}§7) Time: §e{2}s §7Tnt: §e{3} §7Blocks
+LOADTIMER_ACTIVATED=§7Waiting until priming
+LOADTIMER_IGNITION=§7Waiting for explosion
+LOADTIMER_SUMARY_HEAD=§7---=== (§eLoadtimer-Results§7) ===---
+LOADTIMER_SUMARY_PLAYERTABLE_HEAD=§7Player: §eTNT §7(§eTNT/s§7)
+LOADTIMER_SUMARY_PLAYERTABLE_PLAYER=§7{0}: §e{1} §7(§e{2}/s§7)
+LOADTIMER_SUMARY_PLAYERTABLE_ALL=Total
+LOADTIMER_SUMARY_TIMES_HEAD=§7Time: §eSeconds §7(§eTicks§7)
+LOADTIMER_SUMARY_TIMES_START=§7 || §7Start!
+LOADTIMER_SUMARY_TIMES_ACTIVATION=§7 || Activation: §e{0}s §7(§e{1}t§7)
+LOADTIMER_SUMARY_TIMES_IGNITION=§7 || Priming: §e{0}s §7(§e{1}t§7)
+LOADTIMER_SUMARY_TIMES_EXPLOSION=§7 || Explosion: §e{0}s §7(§e{1}t§7)
+LOADTIMER_SUMARY_TIMES_LAST=§7\\/
+LOADTIMER_SUMARY_STATS_HEAD=§7Cannon-Stats§8:
+LOADTIMER_SUMARY_STATS_TNT=§7TNT: §e{0}
+LOADTIMER_SUMARY_STATS_FREQ=§7Loading frequency: §e{0}/m§8, §7Shot frequency: §e{1}/m
+# Observer
+OBSERVER_HELP=§7Right-Click an Observer to get the Trace. Flame particles have to be enabled. The Particles will be shown in the block.
+OBSERVER_HELP_ENABLE=§8/§eobserver enable §8-§7 Activates the Observer-Tracer
+OBSERVER_HELP_DISABLE=§8/§eobserver disable §8-§7 Deactivates the Observer-Tracer
+OBSERVER_HELP_DELETE=§8/§eobserver delete §8-§7 Deletes the Obersver-Tracer
+OBSERVER_HELP_RETRACE=§8/§eobserver retrace §8-§7 Retraces The Observer-Tracer
+OBSERVER_ENABLE=§7Observer trace started
+OBSERVER_DISABLE=§7Observer trace stopped
+OBSERVER_DELETE=§7Observer trace deleted
+OBSERVER_RETRACE_DONE=§7Observer trace retraced
+OBSERVER_RETRACE_NO_TRACE=§7No Observer trace to retrace
+# Other
+OTHER_ITEMS_TELEPORT_NAME=§eTeleporter
+OTHER_ITEMS_TELEPORT_GUI_NAME=Teleport
+OTHER_ITEMS_TELEPORT_PLAYER_OFFLINE=§cThis Player is offline
+OTHER_ITEMS_CLEAR_NAME=§eClear
+OTHER_ITEMS_DECLUTTER_NAME=§eDeclutter
+OTHER_ITEMS_GAMEMODE_NAME=§eGamemode
+OTHER_ITEMS_GAMEMODE_LORE_1=§eRight-Click§8:§7 Toggle between creative and spectator
+OTHER_ITEMS_GAMEMODE_LORE_2=§eLeft-Click§8:§7 Toggle between survival and adventure
+OTHER_ITEMS_KILLALL_NAME=§eKillAll
+OTHER_ITEMS_KILLALL_LORE_1=§eWithout Shift§8:§7 only this region
+OTHER_ITEMS_KILLALL_LORE_2=§eWith Shift§8:§7 global
+OTHER_ITEMS_INVENTORY_FILL_NAME=§eInventoryFill
+OTHER_ITEMS_INVENTORY_FILL_LORE_ACTIVE=§aActivated
+OTHER_ITEMS_INVENTORY_FILL_LORE_INACTIVE=§aDisabled
+OTHER_SLOT_INVALID_SLOT=§cInvalid slot
+OTHER_NOCLIP_SLOT_INFO=§7With /slot you can change the selected slot and take another block in the slot.
+OTHER_NOCLIP_SLOT_HELP_PICK=§8/§eslot pick §8-§7 Take the faced block into your inventory.
+OTHER_NOCLIP_SLOT_HELP_DROP=§8/§eslot drop §8-§7 Clears your slot
+OTHER_CLEAR_HELP_SELF=§8/§eclear §8- §7Clears your inventory
+OTHER_CLEAR_HELP_PLAYER=§8/§eclear §8[§7Player§8] §8- §7Clears a player inventory
+OTHER_CLEAR_CLEARED=§7Your inventory was cleared.
+OTHER_CLEAR_FROM=§7Your invetnory was cleared by {0}.
+OTHER_CLEAR_TO=§7The inventory of {0} §7was cleared.
+OTHER_DECLUTTER_HELP=§8/§edeclutter §8- §7Organise your inventory
+OTHER_DECLUTTER_DONE=§aYour inventory was organised.
+OTHER_GAMEMODE_UNKNOWN=§cUnknown gamemode.
+OTHER_GAMEMODE_POSSIBLE=§cPossible gamemodes: survival, adventure, creative, specator.
+OTHER_KILLALL_HELP_SELF=§8/§ekillall §8- §7Remove all entities from your region
+OTHER_KILLALL_HELP_ALL=§8/§ekillall §8[§7Global§8/Local§7] §8- §7Remove all entities from your region or globally
+OTHER_KILLALL_REGION=§a{0} Entities removed
+OTHER_KILLALL_GLOBAL=§a{0} Entities removed from the world
+OTHER_TELEPORT_HELP=§8/§etp §8[§7Player§8] §8-§7 Teleports you to another player
+OTHER_TELEPORT_SELF_0=§cBe one with yourself!
+OTHER_TELEPORT_SELF_1=§cYou need someone to play with? We have a TeamSpeak!
+OTHER_TELEPORT_SELF_2=§cBlocks left to travel: 0; ETA: 0:00
+OTHER_TELEPORT_SELF_3=§cA little Movement is important.
+OTHER_TELEPORT_SELF_4=§cFor such a distance?
+OTHER_TIME_HELP=§8/§etime §8<§7Time 0=Morining§8, §76000=Midday§8, §718000=Midnight§8> - §7Sets the time on the Build
+OTHER_TIME_INVALID=§cPlease input a time between 0 and 24000
+OTHER_TIME_RESULT=§7§oWhooosh
+OTHER_TPS_HEAD=§7TPS: 1s 10s 1m 5m 10m
+OTHER_TPS_MESSAGE=§7 §e{0}§7 §e{1}§7 §e{2}§7 §e{3}§7 §e{4}
+OTHER_TPS_SINGLE=§8TPS: §e{0}
+OTHER_WORLDSPAWN_HELP=§8/§eworldspawn §8-§e Teleport to the spawn
+OTHER_BIND_HELP=§8/§ebind §8[§7Command§8] §8-§e Bind a command on item interaction
+OTHER_BIND_ERROR=§cInvalid or unknown command
+OTHER_BIND_UNBINDABLE=§cCould not bind command
+OTHER_BIND_LORE=§eCommand§8:§7 {0}
+OTHER_BIND_MESSAGE_BIND=§7Bound command §e{0} §7to item
+OTHER_BIND_MESSAGE_UNBIND=§7Unbound command
+# DebugStick
+DEBUG_STICK_COMMAND_HELP=§8/§edebugstick §8-§7 receive a debugstick
+DEBUG_STICK_NAME=§eDebugstick
+#Skull Gui
+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
+NIGHT_VISION_ON=§eNightvision activated
+NIGHT_VISION_ITEM_ON=§7Nightvision: §eActivated
+NIGHT_VISION_ITEM_OFF=§7Nightvision: §eDeactivated
+#Navigation Wand
+NAVIGATION_WAND=§eNavigation Wand
+NAVIGATION_WAND_LEFT_CLICK=§eLeft click: jump to location
+NAVIGATION_WAND_RIGHT_CLICK=§eRight click: pass through walls
+# Material
+MATERIAL_SEARCH_PROPERTY_TRUE=§aShould have
+MATERIAL_SEARCH_PROPERTY_FALSE=§cShould not have
+MATERIAL_SEARCH_PROPERTY_IGNORE=§eIgnore
+MATERIAL_INV_NAME=§eMaterial {0}/{1}
+MATERIAL_SEARCH=§eSearch
+MATERIAL_BACK=§eBack
+MATERIAL_SEARCH_NAME=§eName
+MATERIAL_SEARCH_TRANSPARENT=§eTransparent
+MATERIAL_SEARCH_SOLID=§eSolid
+MATERIAL_SEARCH_GRAVITY=§eFalling
+MATERIAL_SEARCH_OCCLUDING=§eOccluding
+MATERIAL_SEARCH_INTERACTEABLE=§eInteractable
+MATERIAL_SEARCH_FLAMMABLE=§eFlammable
+MATERIAL_SEARCH_BURNABLE=§eBurnable
+MATERIAL_SEARCH_WATERLOGGABLE=§eWaterloggable
+MATERIAL_SEARCH_UNMOVEABLE=§eUnmoveable
+MATERIAL_SEARCH_BLASTRESISTANCE=§eBlast resistance
+MATERIAL_SEARCH_VALUE=§8: §e{0}
+MATERIAL_BLAST_RESISTANCE=§8- §eBlast resistance§8: §7{0}
+MATERIAL_HARDNESS=§8- §eHardness§8: §7{0}
+MATERIAL_TNT_BREAKABLE=§8- §eDestructible by TNT
+MATERIAL_TNT_UNBREAKABLE=§8- §eIndestructible by TNT
+MATERIAL_TRANSPARENT=§8- §eTransparent block
+MATERIAL_SOLID=§8- §eSolid block
+MATERIAL_GRAVITY=§8- §eFalling block
+MATERIAL_OCCLUDING=§8- §eOccluding block
+MATERIAL_INTERACTABLE=§8- §eInteractable block
+MATERIAL_FLAMMABLE=§8- §eFlammable block
+MATERIAL_BURNABLE=§8- §eBurnable block
+MATERIAL_WATERLOGGABLE=§8- §eWaterloggable block
+MATERIAL_UNMOVABLE=§8- §eUnmovable block
+# Region Items
+REGION_ITEM_COLOR=§7Color: §e{0}
+REGION_ITEM_COLOR_CHOOSE=Choose color
+REGION_ITEM_FIRE_ALLOW=§7Fire: §eActivated
+REGION_ITEM_FIRE_DISALLOW=§7Fire: §eDeactivated
+REGION_ITEM_FREEZE_ALLOW=§7Freeze: §eActivated
+REGION_ITEM_FREEZE_DISALLOW=§7Freeze: §eDeactivated
+REGION_ITEM_PROTECT_ALLOW=§7Protect: §eActivated
+REGION_ITEM_PROTECT_DISALLOW=§7Protect: §eDeactivated
+REGION_ITEM_RESET=§eReset
+REGION_ITEM_TESTBLOCK=§eDummy
+REGION_ITEM_TNT_OFF=§7TNT: §eDeactivated
+REGION_ITEM_TNT_ONLY_TB=§7TNT: §eonly dummy
+REGION_ITEM_TNT_ONLY_BUILD=§7TNT: §eonly build
+REGION_ITEM_TNT_ON=§7TNT: §eActivated
+REGION_ITEM_SELECTOR_TITLE=Tnt Mode
+REGION_ITEM_SELECTOR_ON=§eActivate
+REGION_ITEM_SELECTOR_ONLY_TB=§eonly dummy
+REGION_ITEM_SELECTOR_ONLY_BUILD=§eonly build
+REGION_ITEM_SELECTOR_OFF=§eDeactivate
+#Region
+REGION_COLOR_HELP_COLOR=§8/§ecolor §8[§7Color§8] §8- §7Sets the color of the region
+REGION_COLOR_HELP_COLOR_TYPE=§8/§ecolor §8[§7Color§8] §8[§7Type§8] §8- §7Sets the color of the region or globally
+REGION_COLOR_GLOBAL=§7All regions color set to §e{0}
+REGION_COLOR_NO_REGION=§cYou are currently not in any region
+REGION_FIRE_HELP=§8/§efire §8- §7Toggle fire damage
+REGION_FIRE_ENABLED=§cFire damage deactivated in this region
+REGION_FIRE_DISABLED=§aFire damage activated in this region
+REGION_FREEZE_HELP=§8/§efreeze §8- §7Toggle Freeze
+REGION_FREEZE_ENABLED=§cRegion frozen
+REGION_FREEZE_DISABLED=§aRegion thawed
+REGION_ITEMS_HELP=§8/§eitems §8- §7Toggle Items
+REGION_ITEMS_ENABLED=§aItems enabled in this region
+REGION_ITEMS_DISABLED=§cItems disabled in this region
+REGION_PROTECT_HELP=§8/§eprotect §8- §7Protect the region
+REGION_PROTECT_DISABLE=§cProtection disabled
+REGION_PROTECT_ENABLE=§aProtection enabled
+REGION_PROTECT_FALSE_REGION=§cYou are not currently in a (M)WG-region
+REGION_NO_GRAVITY_HELP = §8/§enogravity §8- §7Toggle NoGravity
+REGION_NO_GRAVITY_ENABLED = §aNoGravity enabled in this region
+REGION_NO_GRAVITY_DISABLED = §cNoGravity disabled in this region
+REGION_REGION_HELP_UNDO=§8/§eregion undo §8- §7undo the last 20 /testblock or /reset
+REGION_REGION_HELP_REDO=§8/§eregion redo §8- §7redo the last 20 §8/§7rg undo
+REGION_REGION_HELP_RESTORE=§8/§eregion restore §8- §7Resets the region, without removing your builds
+REGION_REGION_HELP_RESTORE_SCHEMATIC=§8/§eregion restore §8[§7Schematic§8] §8- §7Resets the region, withoout removing your builds
+REGION_REGION_HELP_COPYPOINT=§8/§eregion copypoint §8- §7Teleport to the regions copy point
+REGION_REGION_HELP_TESTBLOCKPOINT=§8/§eregion testblockpoint §8- §7Teleport to the regions dummy point
+REGION_REGION_HELP_CHANGESKIN_INFO=§8/§eregion changeskin §8- §7Returns the region skin
+REGION_REGION_HELP_CHANGESKIN=§8/§eregion changeskin §8[§7Skin§8] §8- §8Sets the region skin
+REGION_REGION_HELP_COPY=§8/§eregion copy [-e] [-s] §8- §8Copy the build area optional with extensions or selection at the copypoint
+REGION_REGION_HELP_PASTE=§8/§eregion paste [-a] [-s] §8[§7Skin§8] §8- §8Pastes at the copypoint optional without air and selecting the pasted region
+REGION_REGION_NOTHING_UNDO=§cNothing left to undo
+REGION_REGION_UNDID=§7Last action undone
+REGION_REGION_NOTHING_REDO=§cNothing left to redo
+REGION_REGION_REDID=§7Last action redone
+REGION_REGION_RESTORED=§7Region reset
+REGION_REGION_FAILED_RESTORE=§cError resetting the region
+REGION_REGION_COLORED=§7Region recolored
+REGION_REGION_COLORED_FAILED=§7Use §e/rg restore§7 to manually change the region's color
+REGION_REGION_FAILED_COLORED=§cError recoloring the region
+REGION_REGION_TP_COPY=§7Teleported to the copy point
+REGION_REGION_TP_TEST_BLOCK=§7Teleported to the tesblock
+REGION_REGION_TP_UNKNOWN=§cUndefined teleport point
+REGION_REGION_NO_REGION=§cYou are not inside any region
+REGION_REGION_NO_BUILD=§cThis region has no build area
+REGION_REGION_COPY_DONE=§eBuild region or selection copied
+REGION_REGION_PASTE_DONE=§eBuild region or selection pasted
+REGION_REGION_CHANGESKIN_INFO=§7Region skin is §e{0}
+REGION_REGION_CHANGESKIN_INFO_CREATOR=§7Skin created by §e{0}
+REGION_REGION_CHANGESKIN_UNKNOWN=§cRegion skin is invalid
+REGION_REGION_CHANGESKIN_INVALID=§cRegion skin is not allowed here
+REGION_REGION_CHANGESKIN_CHANGE=§7Region skin changed to §e{0}
+REGION_REGION_CHANGESKIN_CHANGE_UPDATE=§7Click §e§lHERE §7to apply the skin
+REGION_REGION_CHANGESKIN_CHANGE_UPDATE_HOVER=§8/§ereset
+REGION_RESET_HELP_RESET=§8/§ereset §8- §7Resets the region
+REGION_RESET_HELP_SCHEMATIC=§8/§ereset §8[§7Schematic§8] §8- §7Resets the region using a schematic
+REGION_RESET_RESETED=§7Region reset
+REGION_RESET_ERROR=§cError reseting the region
+REGION_RESET_NO_REGION=§cYou are currently not in any region
+REGION_TB_HELP_RESET=§8/§etestblock §8- §7Reset the dummy
+REGION_TB_HELP_RESET_EXTENSION=§8/§etestblock §8[§7ExtensionType§8] §8- §7Reset the dummy
+REGION_TB_HELP_SCHEMATIC=§8/§etestblock §8[§7Schematic§8] §8- §7Reset the dummy using a schematic
+REGION_TB_HELP_SCHEMATIC_EXTENSION=§8/§etestblock §8[§7Schematic§8] §8[§7ExtensionType§8] §8- §7Reset the dummy using a schematic
+REGION_TB_DONE=§7Dummy reset
+REGION_TB_ERROR=§cError resetting the dummy
+REGION_TB_NO_REGION=§cYou are currently not in any region
+REGION_TB_NO_SCHEMSHARING=§cYou currently cannot share schematics until {0}.
+REGION_TB_NO_SCHEMRECEIVING=§cThe Owner of this build server cannot receive any schematics until {0}.
+REGION_TNT_HELP=§8/§etnt §8- §7Change the TNT behaviour
+REGION_TNT_HELP_MODE=§8/§etnt §8[§7Mode§8] §8- §7Set TNT behaviour to a given mode
+REGION_TNT_ON=§aTNT-Damage activated
+REGION_TNT_OFF=§cTNT-Damage deactivated
+REGION_TNT_TB=§aTNT-Damage activated outside the building area
+REGION_TNT_BUILD=§aTNT-Damage activated outside the testblok area
+REGION_TNT_BUILD_DESTROY=§cAn explosion would have destroyed blocks in the building area
+REGION_TNT_TB_DESTROY=§cAn explosion would have destroyed blocks in the testblock area
+AFK_KICK_MESSAGE=§cNothing happened on this server for 15 minutes.
+AFK_WARNING_MESSAGE=§cThis server will stop in one minute if you remain inactive
+SKIN_HELP=§8/§eskin §8[§7Shortform§8] §8[§7Creator§8|§epublic§8] §8[§7Name...§8] §8- §7Creates the skin schematic. Use 'public' as creator to have no creator, then copy the message to YoyoNow by clicking
+SKIN_NO_REGION=§7You are not in a region with a changealbe skin
+SKIN_ALREADY_EXISTS=§cThis skin already exists like this
+SKIN_MESSAGE=§7Skin created
+SKIN_MESSAGE_HOVER=§eClick to copy for YoyoNow and send
+# Panzern
+PANZERN_HELP=§8/§epanzern §8[§7Block§8] §8[§7Slab§8] §8- §7Armor your WorldEdit selection
+PANZERN_PREPARE1=§71. Check, if barrels reach until border of armor.
+PANZERN_PREPARE2=§72. Carpet on the floor in walkways helps with armoring.
+PANZERN_PREPARE3=§73. Shieldtechnology should be encased.
+PANZERN_PREPARE4=§74. Standing in the region that is being armored can improve armoring.
+PANZERN_NO_WORLDEDIT=§cYou have no WorldEdit selcetion
+PANZERN_PROGRESS=§e{0} §7Blocks left, §e{1} §7Blocks per second, §e{2} §7block delta
+PANZERN_DONE=§aDone
+# Laufbau
+LAUFBAU_HELP=§8/§elaufbau §8[§7smallest§8|§7blastresistant§8] §8- §7Build a barrel in your WorldEdit selection using the traces
+LAUFBAU_HELP_SETTINGS=§8/§elaufbau settings §8- §7Opens the settings GUI
+LAUFBAU_PREPARE1=§71. Trace the cannons as often as necessary, in all modes.
+LAUFBAU_PREPARE2=§72. Try to delete all fails from the traces.
+LAUFBAU_NO_WORLDEDIT=§cYou don't have a WorldEdit selection
+LAUFBAU_STATE_FILTERING_TRACES=Filtering traces
+LAUFBAU_STATE_PROCESSING_TRACES=Connnecting traces
+LAUFBAU_STATE_CREATE_LAUF=Create Barrel
+LAUFBAU_SIMPLE_PROGRESS=§e{0}§8: §e{1}§8/§e{2} §7Time left§8: §e{3}
+LAUFBAU_DONE=§aDone
+LAUFBAU_SETTINGS_GUI_NAME=§eLaufbau
+LAUFBAU_SETTINGS_ACTIVE=§aActive
+LAUFBAU_SETTINGS_INACTIVE=§cInactive
+LAUFBAU_SETTINGS_MIXED=§e{0}§8/§e{1} §aActive
+LAUFBAU_SETTINGS_GUI_BACK=§eBack
+LAUFBAU_SETTINGS_TOGGLE=§eClick §8-§7 Toggle
+LAUFBAU_SETTINGS_ADVANCED=§eMiddle-Click §8-§7 Advanced settings
+LAUFBAU_BLOCK_COBWEB=§eCobweb
+LAUFBAU_BLOCK_GRASS_PATH=§eGrass Path
+LAUFBAU_BLOCK_SOUL_SAND=§eSoul Sand
+LAUFBAU_BLOCK_COCOA=§eCocoa
+LAUFBAU_BLOCK_TURTLE_EGG=§eTurtle Eggs
+LAUFBAU_BLOCK_CHEST=§eChest
+LAUFBAU_BLOCK_SNOW=§eSnow Layer
+LAUFBAU_BLOCK_PLAYER_WALL_HEAD=§ePlayer Wall Head
+LAUFBAU_BLOCK_STONECUTTER=§eStonecutter
+LAUFBAU_BLOCK_PLAYER_HEAD=§ePlayer Head
+LAUFBAU_BLOCK_CAKE=§eCake
+LAUFBAU_BLOCK_END_STONE_BRICK_SLAB=§eEndstone Brick Slabs
+LAUFBAU_BLOCK_SEA_PICKLE=§eSea Pickles
+LAUFBAU_BLOCK_CAMPFIRE=§eCampfire
+LAUFBAU_BLOCK_FLOWER_POT=§eFlower Pot
+LAUFBAU_BLOCK_IRON_TRAPDOOR=§eIron Trapdoor
+LAUFBAU_BLOCK_LILY_PAD=§eLily Pad
+LAUFBAU_BLOCK_WHITE_CARPET=§eCarpet
+LAUFBAU_BLOCK_END_ROD=§eEnd Rod
+LAUFBAU_BLOCK_LIGHTNING_ROD=§eLightning Rod
+LAUFBAU_BLOCK_CONDUIT=§eConduit
+LAUFBAU_BLOCK_BREWING_STAND=§eBrewing Stand
+LAUFBAU_BLOCK_BELL=§eBell
+LAUFBAU_BLOCK_GRINDSTONE=§eGrindstone
+LAUFBAU_BLOCK_HOPPER=§eHopper
+LAUFBAU_BLOCK_LANTERN=§eLantern
+LAUFBAU_BLOCK_END_STONE_BRICK_STAIRS=§eEndstone Brick Stairs
+LAUFBAU_BLOCK_CHORUS_PLANT=§eChorus Plant
+LAUFBAU_BLOCK_NETHER_BRICK_FENCE=§eNether Brick Fence
+LAUFBAU_BLOCK_IRON_BARS=§eIron Bars
+LAUFBAU_BLOCK_END_STONE_BRICK_WALL=§eEndstone Brick Walls
+LAUFBAU_BLOCK_SMALL_AMETHYST_BUD=§eSmall Amethyst Bud
+LAUFBAU_BLOCK_MEDIUM_AMETHYST_BUD=§eMedium Amethyst Bud
+LAUFBAU_BLOCK_LARGE_AMETHYST_BUD=§eLarge Amethyst Bud
+LAUFBAU_BLOCK_AMETHYST_CLUSTER=§eAmethyst Cluster
+LAUFBAU_BLOCK_CHAIN=§eChain
+LAUFBAU_BLOCK_BIG_DRIP_LEAF=§eBig Drip Leaf
+LAUFBAU_BLOCK_DRAGON_EGG=§eDragon Egg
+LAUFBAU_BLOCK_AZALEA=§eAzalea
+LAUFBAU_BLOCK_CANDLE=§eCandle
+LAUFBAU_BLOCK_CANDLE_CAKE=§eCake with Candle
+LAUFBAU_BLOCK_LECTERN=§eLectern
+LAUFBAU_FACING_NORTH=§8-§7 Facing North
+LAUFBAU_FACING_SOUTH=§8-§7 Facing South
+LAUFBAU_FACING_WEST=§8-§7 Facing West
+LAUFBAU_FACING_EAST=§8-§7 Facing East
+LAUFBAU_FACING_UP=§8-§7 Facing Up
+LAUFBAU_FACING_DOWN=§8-§7 Facing Down
+LAUFBAU_COUNT_1=§8-§7 Count 1
+LAUFBAU_COUNT_2=§8-§7 Count 2
+LAUFBAU_COUNT_3=§8-§7 Count 3
+LAUFBAU_COUNT_4=§8-§7 Count 4
+LAUFBAU_LAYERS_8=§8-§7 Layers 8
+LAUFBAU_LAYERS_7=§8-§7 Layers 7
+LAUFBAU_LAYERS_6=§8-§7 Layers 6
+LAUFBAU_LAYERS_3=§8-§7 Layers 3
+LAUFBAU_LAYERS_2=§8-§7 Layers 2
+LAUFBAU_TYPE_BOTTOM=§8-§7 Type bottom
+LAUFBAU_TYPE_TOP=§8-§7 Type top
+LAUFBAU_HALF_BOTTOM=§8-§7 Half bottom
+LAUFBAU_HALF_TOP=§8-§7 Half top
+LAUFBAU_OPEN=§8-§7 Opened
+LAUFBAU_ATTACHMENT_CEILING=§8-§7 Attachment Ceiling
+LAUFBAU_ATTACHMENT_FLOOR=§8-§7 Attachment Floor
+LAUFBAU_ATTACHMENT_DOUBLE_WALL=§8-§7 Attachment double Wall
+LAUFBAU_ATTACHMENT_SINGLE_WALL=§8-§7 Attachment single Wall
+LAUFBAU_ATTACHMENT_WALL=§8-§7 Attachment Wall
+LAUFBAU_CONNECTION_FLOOR=§8-§7 Connection Floor
+LAUFBAU_CONNECTION_NORTH=§8-§7 Connection North
+LAUFBAU_CONNECTION_SOUTH=§8-§7 Connection South
+LAUFBAU_CONNECTION_EAST=§8-§7 Connection East
+LAUFBAU_CONNECTION_WEST=§8-§7 Connection West
+LAUFBAU_CONNECTION_DOWN=§8-§7 Connection Bottom
+LAUFBAU_CONNECTION_UP=§8-§7 Connection Top
+LAUFBAU_HANGING=§8-§7 hanging
+LAUFBAU_SHAPE_STRAIGHT=§8-§7 Shape straight
+LAUFBAU_SHAPE_OUTER_LEFT=§8-§7 Shape outer links
+LAUFBAU_SHAPE_INNER_LEFT=§8-§7 Shape inner left
+LAUFBAU_TILT_NONE=§8-§7 Tilt none
+LAUFBAU_TILT_PARTIAL=§8-§7 Tilt partial
+# UTILS
+SELECT_HELP=§8/§eselect §8[§7RegionsTyp§8] §8- §7Select a region type
+SELECT_EXTENSION_HELP=§8/§eselect §8[§7RegionsTyp§8] §8[§7Extension§8] §8- §7Select a region type with or without extension
+SELECT_GLOBAL_REGION=§cThe global region cannot be selected
+SELECT_NO_TYPE=§cThis region has no {0}
+SELECT_NO_EXTENSION=§cThis region has no extension
+SELECT_MESSAGE=§7WorldEdit selection set to {0}, {1}, {2} and {3}, {4}, {5}
+SKULL_HELP=§8/§eskull §8[§eplayer§8] §8-§7 Receive a player head
+SKULL_INVALID=§cInvalid player name
+SKULL_ITEM=§e{0}§8s Head
+SPEED_HELP=§8/§espeed §8[§71§8-§710§8|§edefault§8] §8-§7 Set your flight and walking speed.
+SPEED_CURRENT=§7Current speed§8: §e{0}
+SPEED_TOO_SMALL=§c{0} is too small
+SPEED_TOO_HIGH=§c{0} is too big
+SPEED_ITEM=§eSpeed
+SPEED_ITEM_LORE=§7Currently: §e
+SPEED_TAB_NAME=Input speed
+WORLDEDIT_WAND=WorldEdit Wand
+WORLDEDIT_LEFTCLICK=Left click: select pos #1
+WORLDEDIT_RIGHTCLICK=Right click: select pos #2
+TNT_CLICK_HEADER=§8---=== §eTNT §8===---
+TNT_CLICK_ORDER=§eEntity Order§8: §e{0}
+TNT_CLICK_FUSE_TIME=§eFuseTime§8: §e{0}
+TNT_CLICK_POSITION_X=§7Position §eX§8: §e{0}
+TNT_CLICK_POSITION_Y=§7Position §eY§8: §e{0}
+TNT_CLICK_POSITION_Z=§7Position §eZ§8: §e{0}
+TNT_CLICK_VELOCITY_X=§7Velocity §eX§8: §e{0}
+TNT_CLICK_VELOCITY_Y=§7Velocity §eY§8: §e{0}
+TNT_CLICK_VELOCITY_Z=§7Velocity §eZ§8: §e{0}
+TNT_CLICK_COUNT=§7Count §8: §e{0}
+TNT_CLICK_ISOLATE=§eIsolate
+SELECT_ITEM_CHOOSE_EXTENSION=Choose extension
+SELECT_ITEM_CHOOSE_SELECTION=Choose selection
+SELECT_ITEM_NORMAL_EXTENSION=§eNormal
+SELECT_ITEM_EXTENDED_EXTENSION=§eExtension
+SELECT_ITEM_SELECT=§eSelect
+SELECT_ITEM_AUSWAHL=§7Selection: §7{0} {1}
+SELECT_ITEM_RIGHT_CLICK=§7Right-Click to change
+SELECT_ITEM_BAURAHMEN=§eBuild area
+SELECT_ITEM_BAUPLATTFORM=§eBuild platform
+SELECT_ITEM_TESTBLOCK=§eDummy
+CHESTFILLER_FILLED=§eChest filled
+CHESTFILLER_COUNT=§7{0}§8: §e§l{1}
+PISTON_HELP_1=§7Right click on piston with a slime ball to calculate the moved blocks.
+PISTON_HELP_2=§7Count is red, if one unmoveable block is present.
+PISTON_HELP_3=§7Count is yellow, if too many blocks are present.
+PISTON_INFO=§7Moved Blocks {0}{1}§8/§712
+# Warp
+WARP_LOC_X=§7X§8: §e{0}
+WARP_LOC_Y=§7Y§8: §e{0}
+WARP_LOC_Z=§7Z§8: §e{0}
+WARP_EXISTS=§7The warp with the name §e{0} §7already exists
+WARP_NAME_RESERVED=§7You can not use §c{0} §7as name for a warp
+WARP_CREATED=§7The warp §e{0} §7was created
+WARP_DELETE_HOVER=§7delete §e{0}
+WARP_DELETED=§e{0} §7deleted
+WARP_TELEPORT_HOVER=§7Teleport to §e{0}
+WARP_MATERIAL_CHOOSE=Choose material
+WARP_GUI_NAME=Warps
+WARP_GUI_NO=§cNo warps exist
+WARP_GUI_DISTANCE=§7Distance: §e{0} §7blocks
+WARP_GUI_LCLICK=§7Left click to teleport
+WARP_GUI_RCLICK=§7Right click to edit
+WARP_INFO_NAME=§7Name: §e{0}
+WARP_HELP_ADD=§8/§ewarp add §8[§7name§8] §8- §7Create a new warp
+WARP_HELP_TELEPORT=§8/§ewarp §8[§7name§8] §8- §7Teleport to a warp
+WARP_HELP_INFO=§8/§ewarp info §8[§7name§8] §8- §7Information regarding one warp
+WARP_HELP_DELETE=§8/§ewarp delete §8[§7name§8] §8- §7Delete a warp
+WARP_HELP_GUI=§8/§ewarp gui §8- §7Open the Warp-GUI
+WARP_HELP_LIST=§8/§ewarp list §8- §7List all warps
+# WORLD
+STOP_HELP=§8/§estop §8- §7Stops the server
+STOP_MESSAGE=§eServer is stopping
+KICKALL_HELP=§8/§ekickall §8- §7Kick all players from the server except the owner
+# Techhider
+TECHHIDER_HELP=§8/§etechhider §8- §7Toggle Techhider
+TECHHIDER_GLOBAL=§cNo techhider in global region
+TECHHIDER_ON=§aTechhider activated
+TECHHIDER_OFF=§cTechhider deactivated
+# XRAY
+XRAY_HELP=§8/§exray §8- §7Toggle Xray
+XRAY_GLOBAL=§cNo xray in global region
+XRAY_ON=§aXray activated
+XRAY_OFF=§cXray deactivated
+# WorldEdit
+COLORREPLACE_HELP=§8//§ecolorreplace §8[§7color§8] §8[§7color§8] §8- §7Replace all blocks of one color with another
+TYPEREPLACE_HELP=§8//§etypereplace §8[§7type§8] §8[§7type§8] §8- §7Replace all blocks of one type with another
+# Schematic
+SCHEMATIC_GUI_ITEM=§eSchematics
diff --git a/BauSystem/BauSystem_Main/src/BauSystem_de.properties b/BauSystem/BauSystem_Main/src/BauSystem_de.properties
new file mode 100644
index 00000000..4a8335e9
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/BauSystem_de.properties
@@ -0,0 +1,955 @@
+#
+# This file is a part of the SteamWar software.
+#
+# Copyright (C) 2021 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 .
+#
+PREFIX=§eBau§8System§8»
+TIME=HH:mm:ss
+DATE=........
+COMMAND_HELP_HEAD=§7---=== (§e{0}§7) ===---
+ONLY_SCHEMS=§cDu kannst hier keinen Ordner angeben
+PAGE_LIST=§e Seite ({0}/{1}) »»
+LIST_PREVIOUS_PAGE=§eVorherige Seite
+LIST_NEXT_PAGE=§eNächste Seite
+# Permission
+NO_PERMISSION=Du darfst dies hier nicht nutzen
+SPECTATOR=§fZuschauer
+# Scoreboard
+SCOREBOARD_TIME=Uhrzeit
+SCOREBOARD_REGION=Region
+SCOREBOARD_TRACE=Trace
+SCOREBOARD_LOADER=Loader
+SCOREBOARD_TPS=TPS
+SCOREBOARD_TPS_FROZEN=§eEingefroren
+SCOREBOARD_TRACE_TICKS=Ticks
+SCOREBOARD_TECHHIDER=TechHider§8: §aAn
+SCOREBOARD_XRAY=XRay§8: §aAn
+SCOREBOARD_LOCK_TEAM=Bau Lock§8: §eTeam
+SCOREBOARD_LOCK_TEAM_AND_SERVERTEAM=Bau Lock§8: §e(Server-) Team
+SCOREBOARD_LOCK_SERVERTEAM=Bau Lock§8: §eServerteam
+SCOREBOARD_LOCK_NOBODY=Bau Lock§8: §cNiemand
+# Flags
+FLAG_COLOR=Color
+FLAG_TNT=TNT
+FLAG_FIRE=Fire
+FLAG_FREEZE=Freeze
+FLAG_PROTECT=Protect
+FLAG_ITEMS=Items
+FLAG_FIRE_ALLOW=§can
+FLAG_FIRE_DENY=§aaus
+FLAG_FREEZE_ACTIVE=§aan
+FLAG_FREEZE_INACTIVE=§caus
+FLAG_PROTECT_ACTIVE=§aan
+FLAG_PROTECT_INACTIVE=§caus
+FLAG_TNT_ALLOW=§aan
+FLAG_TNT_DENY=§caus
+FLAG_TNT_ONLY_TB=§7Kein §eBaurahmen
+FLAG_TNT_ONLY_BUILD=§7Kein §eTestblock
+FLAG_ITEMS_ACTIVE=§aan
+FLAG_ITEMS_INACTIVE=§caus
+FLAG_COLOR_WHITE=§fWeiß
+FLAG_COLOR_ORANGE=§6Orange
+FLAG_COLOR_MAGENTA=§dMagenta
+FLAG_COLOR_LIGHT_BLUE=§bHellblau
+FLAG_COLOR_YELLOW=§eGelb
+FLAG_COLOR_LIME=§aHellgrün
+## This cannot be converted
+FLAG_COLOR_PINK=§ePink
+FLAG_COLOR_GRAY=§8Grau
+FLAG_COLOR_LIGHT_GRAY=§7Hellgrau
+FLAG_COLOR_CYAN=§3Cyan
+FLAG_COLOR_PURPLE=§5Lila
+FLAG_COLOR_BLUE=§1Blau
+## This cannot be converted
+FLAG_COLOR_BROWN=§eBraun
+FLAG_COLOR_GREEN=§2Grün
+FLAG_COLOR_RED=§cRot
+FLAG_COLOR_BLACK=§0Schwarz
+# Region
+REGION_TYPE_NORMAL=Normal
+REGION_TYPE_BUILD=Baubereich
+REGION_TYPE_ONLY_TB=Testblock
+# AttributesCopy
+ATTRIBUTES_CANT_COPY=§cDu musst den Item Type in der Hand halten wo du auch drauf guckst.
+ATTRIBUTES_NO_COPY=§cKeine Attribute kopiert.
+ATTRIBUTES_COPIED=§eAttribute kopiert.
+ATTRIBUTE_REMOVE_ALL=§eAlle Attribute entfernt.
+ATTRIBUTE_REMOVE_SINGLE=§eAttribut §7{0}§e entfernt.
+ATTRIBUTE_REMOVE_NOT_FOUND=§cAttribut nicht gefunden
+# AutoStart
+AUTOSTART_COMMAND_HELP=§8/§etimer §8- §7Legt den AutostartTimer ins Inventar
+AUTOSTART_ITEM_NAME=§eAutostartTimer
+AUTOSTART_ITEM_LORE=§eRechtsklick Block §8- §7Starte den Timer
+AUTOSTART_MESSAGE_NO_REGION=§cDu befindest dich derzeit in keiner Region
+AUTOSTART_MESSAGE_RESET=§eDer AutostartTimer wurde zurückgesetzt
+AUTOSTART_MESSAGE_START=§eAutostartTimer gestartet
+AUTOSTART_MESSAGE_RESULT1=§eZeit §7bis zur §eExplosion §7am Gegner§8:§e {0}§7 in game ticks
+AUTOSTART_MESSAGE_RESULT2=§7Zeitdifferenz in §egame ticks §7bis {0} Sekunden§8:§e {1}
+AUTOSTART_MESSAGE_RESULT3=§7Positiv, wenn zu wenig, negativ wenn zu viel
+# Backup
+BACKUP_HELP_CREATE=§8/§ebackup create §8- §7Erstelle ein Backup der Region
+BACKUP_HELP_LOAD=§8/§ebackup load §8[§7BackupName§8] §8- §7 Lade ein Backup
+BACKUP_HELP_LIST=§8/§ebackup list §8- §7Liste alle Backups der Region auf
+BACKUP_HELP_GUI=§8/§ebackup gui §8- §7Öffne die Backups in einer GUI
+BACKUP_REGION_NO_REGION=§cDu bist in keiner Region
+BACKUP_CREATE_SUCCESS=§7Das Backup wurde erstellt
+BACKUP_CREATE_FAILURE=§cDas Backup erstellen ist schiefgegangen
+BACKUP_CREATE_NO_CHANGE=§7Die Region hat keine Veränderung
+BACKUP_LIST_HEAD=§7---=== (§eBackup §7{0}§7) ===---
+BACKUP_LIST_ENTRY=§7{0} §e[Laden]
+BACKUP_LOAD_FAILURE=§cDas Backup laden ist schiefgegangen
+BACKUP_LOAD=§7Backup geladen
+BACKUP_INV_NAME=§eBackup
+BACKUP_ITEM_NAME=§eBackup §7von §e{0}
+BACKUP_LORE=§eKlicken zum Laden
+# Bau
+BAU_COMMAND_HELP_INFO=§8/§ebau info §8- §7Alias für §8/§ebauinfo
+BAU_INFO_ITEM_NAME=§eBau-Management
+## This is used in BauInfoBauGuiItem.java
+BAU_INFO_ITEM_LORE_TNT=§7TNT§8: §e{0}
+BAU_INFO_ITEM_LORE_FREEZE=§7Freeze§8: §e{0}
+BAU_INFO_ITEM_LORE_DAMAGE=§7Damage§8: §e{0}
+BAU_INFO_ITEM_LORE_FIRE=§7Feuer§8: §e{0}
+BAU_INFO_ITEM_LORE_COLOR=§7Farbe§8: §e{0}
+BAU_INFO_ITEM_LORE_PROTECT=§7Protect§8: §e{0}
+BAU_INFO_COMMAND_HELP=§8/§ebauinfo §8- §7Gibt Informationen über den Bau
+BAU_INFO_COMMAND_OWNER=§7Besitzer§8: §e{0}
+BAU_INFO_COMMAND_MEMBER=§7{0} §8[§7{1}§8]§8: §e{2}
+BAU_INFO_COMMAND_FLAG=§7{0}§8: §7{1}
+BAU_INFO_COMMAND_TPS=§7TPS§8:§e
+# Countingwand
+COUNTINGWAND_COMMAND_HELP=§8/§ecountingwand §8- §7Gibt dir ein CountingWand
+COUNTINGWAND_ITEM_NAME=§eZollstock
+COUNTINGWAND_ITEM_LORE1=§eLinksklick §8- §7Setzt die 1. Position
+COUNTINGWAND_ITEM_LORE2=§eRechtsklick §8- §7Setzt die 2. Position
+COUNTINGWAND_MESSAGE_RCLICK=§7Erste Position bei: §8[§7{0}§8, §7{1}§8, §7{2}§8] ({3}§8) ({4}§8)
+COUNTINGWAND_MESSAGE_LCLICK=§7Zweite Position bei: §8[§7{0}§8, §7{1}§8, §7{2}§8] ({3}§8) ({4}§8)
+COUNTINGWAND_MESSAGE_VOLUME=§e{0}
+COUNTINGWAND_MESSAGE_DIMENSION=§e{0}§8, §e{1}§8, §e{2}
+# Design Endstone
+DESIGN_ENDSTONE_COMMAND_HELP=§8/§edesign endstone §8- §7Zeige End Stone im Design
+DESIGN_ENDSTONE_REGION_ERROR=§cDiese Region hat keinen Baubereich
+DESIGN_ENDSTONE_ENABLE=§aEndstone im Design ist angezeigt
+DESIGN_ENDSTONE_DISABLE=§cEndstone im Design ist versteckt
+# Detonator
+DETONATOR_LOC_REMOVE=§e{0} entfernt
+DETONATOR_LOC_ADD=§e{0} hinzugefügt
+DETONATOR_BUTTON_SWITCH=Hebel
+DETONATOR_BUTTON_WOOD_BUTTON=Knopf
+DETONATOR_BUTTON_STONE_BUTTON=Knopf
+DETONATOR_BUTTON_PRESSURE_PLATE=Druckplatte
+DETONATOR_BUTTON_WEIGHTED-PRESSURE_PLATE=Druckplatte
+DETONATOR_BUTTON_TRIPWIRE=Tripwire
+DETONATOR_BUTTON_NOTEBLOCK=Noteblock
+DETONATOR_BUTTON_DAYLIGHTSENSOR=Tageslichtsensor
+DETONATOR_BUTTON_POWERABLE=Aktivierbarer Block
+DETONATOR_BUTTON_INVALID=Invalider
+DETONATOR_WAND_NAME=§eFernzünder
+DETONATOR_WAND_LORE_1=§eLinks Klick §8- §7Setzte einen Punkt zum Aktivieren
+DETONATOR_WAND_LORE_2=§eLinks Klick + Shift §8- §eFüge einen Punkt hinzu
+DETONATOR_WAND_LORE_3=§eRechts Klick §8- §eLöse alle Punkte aus
+DETONATOR_HELP_WAND=§8/§edetonator wand §8-§7 Gibt den Fernzünder
+DETONATOR_HELP_CLICK=§8/§edetonator click §8-§7 Aktiviere einen Fernzünder (Haupthand -> Hotbar -> Inventar)
+DETONATOR_HELP_CLEAR=§8/§edetonator clear §8-§7 Cleare einen Fernzünder
+DETONATOR_HELP_AUTOSTART=§8/§edetonator autostart §8-§7 Automatisch den Autostarttester Aktivieren
+DETONATOR_AUTOSTART_ENABLE=§7Autostart beim detonate §aangeschaltet
+DETONATOR_AUTOSTART_DISABLE=§7Autostart beim detonate §causgeschaltet
+DETONATOR_POINT_ACT=§eEinen Punkt ausgelöst
+DETONATOR_POINTS_ACT=§e{0} Punkte ausgelöst
+DETONATOR_INVALID_POINT=§cEin Punkt konnte nicht ausgeführt werden
+DETONATOR_INVALID_POINTS=§c{0} Punkte konnten nicht ausgeführt werden
+DETONATOR_INVALID_BLOCK=§eDer Block konnte nicht hinzugefügt werden
+# Hotbar
+HOTBAR_HELP_GENERIC=§7Speichert eine Hotbar. Diese wird beim Joinen eines Bauserver, wo du ein Leeres Inventar hast geladen.
+HOTBAR_HELP_SAVE=§8/§ehotbar save §8-§7 Speicher deine Aktuelle Hotbar
+HOTBAR_HELP_LOAD=§8/§ehotbar load §8-§7 Lade deine Standard Hotbar
+HOTBAR_HELP_SHOW=§8/§ehotbar show §8-§7 Zeigt dir deine Standard Hotbar
+HOTBAR_SAVED=§7Deine Hotbar wurde als Standard gespeichert
+HOTBAR_LOADED=§7Deine Standard Hotbar wurde geladen
+HOTBAR_INVENTORY=Standard Hotbar
+# GUI
+GUI_EDITOR_ITEM_NAME=§eGui Editor
+GUI_NAME=Bau GUI
+GUI_ITEM_LORE1=§7Du kannst dieses Item zum Öffnen der BauGUI nutzen
+GUI_ITEM_LORE2=§7oder Doppel F (Swap hands) drücken.
+GUI_EDITOR_TITLE=Bau GUI Editor
+GUI_EDITOR_ITEM_ROW_P=§e+1 Zeile
+GUI_EDITOR_ITEM_ROW_M=§e-1 Zeile
+GUI_EDITOR_ITEM_TRASH=§cTrashcan
+GUI_EDITOR_ITEM_TRASH_LORE=§7Item hier rein Legen
+GUI_EDITOR_ITEM_MORE=§eMehr Items
+GUI_EDITOR_ITEM_CLOSE=§eSchließen
+GUI_EDITOR_TITLE_MORE=Item auswählen
+# Script
+## CustomScript
+SCRIPT_HOTKEY_ITEM_NAME=§7Hotkey§8: §e{0}
+SCRIPT_EVENT_ITEM_NAME=§7Event§8: §e{0}
+SCRIPT_COMMAND_ITEM_NAME=§7Befehl§8: §e/{0}
+SCRIPT_ERROR_ONLY_IN_GLOBAL=§cDieses Skript kann nur als globales Skript ausgeführt werden
+## Script Menu GUI
+SCRIPT_MENU_GUI_ITEM_LORE_1=§7Klicke zum rausnehmen
+SCRIPT_MENU_GUI_ITEM_LORE_2=§7Shiftklick zum kopieren
+SCRIPT_MENU_GUI_ITEM_LORE_3=§7Rechtsklick zum editieren
+SCRIPT_MENU_GUI_ITEM_LORE_4=§7Mittelklick zum anschauen
+SCRIPT_MENU_GUI_NAME=§eSkript-Menü
+SCRIPT_MENU_GUI_ITEM_ADD_NAME=§eHinzufügen
+SCRIPT_MENU_GUI_ITEM_ADD_LORE=§7Klicke mit einem Buch zum hinzufügen
+SCRIPT_DEPRECATED=§cDie Funktion §e{0}§c ist veraltet und wird demnächst entfernt. Bitte benutze §e{1}§c.
+# Shield Printing
+SHIELD_PRINTING_HELP_START=§8/§eshieldprinting start §8- §7Starte das Schild drucken
+SHIELD_PRINTING_HELP_COPY=§8/§eshieldprinting copy §8- §7Kopiert die Schilder
+SHIELD_PRINTING_HELP_APPLY=§8/§eshieldprinting apply §8- §7Wendet die Schilder an
+SHIELD_PRINTING_HELP_STOP=§8/§eshieldprinting stop §8- §7Stoppt das Schild drucken
+SHIELD_PRINTING_HELP_STEP_1=§81. §7Füge die Schematic in die Welt ein
+SHIELD_PRINTING_HELP_STEP_2=§82. §7Starte das Schild drucken mit §8/§eshieldprinting start
+SHIELD_PRINTING_HELP_STEP_3=§83. §7Warte bis alle Schilde ausgefahren sind
+SHIELD_PRINTING_HELP_STEP_4=§84. §7Editiere die Schilde wenn nötig
+SHIELD_PRINTING_HELP_STEP_5=§85. §7Kopiere das gedruckte mit §8/§eshieldprinting copy
+SHIELD_PRINTING_HELP_STEP_6=§86. §7Füge die originale Schematic wieder ein
+SHIELD_PRINTING_HELP_STEP_7=§87. §7Wende das gedruckte mit §8/§eshieldprinting apply§7 an
+SHIELD_PRINTING_NO_REGION=§cDu bist in keiner Region.
+SHIELD_PRINTING_NOT_RUNNING=§cShield printing ist nicht aktiv.
+SHIELD_PRINTING_BOSSBAR=§fBewegungen: {0}
+SHIELD_PRINTING_BOSSBAR_COPIED=§fBewegungen: {0} Kopiert: {1}
+SHIELD_PRINTING_GUI_NAME=§7Schild Drucken
+SHIELD_PRINTING_GUI_APPLY=§aAnwenden
+SHIELD_PRINTING_GUI_STATE_PREVIOUS=§7R-Click§8: §7Vorherige
+SHIELD_PRINTING_GUI_STATE_NEXT=§7L-Click§8: §7Nächste
+SHIELD_PRINTING_GUI_STATE_ACTIVE=§e> §7{0}
+SHIELD_PRINTING_GUI_STATE_INACTIVE=§8> §7{0}
+SHIELD_PRINTING_GUI_STATE_FROM_ORIGINAL=Original
+SHIELD_PRINTING_GUI_STATE_FROM_COPY=Kopie
+SHIELD_PRINTING_GUI_STATE_ALWAYS_ON=An
+SHIELD_PRINTING_GUI_STATE_ALWAYS_OFF=Aus
+SHIELD_PRINTING_GUI_STATE_ALWAYS_OPEN=Offen
+SHIELD_PRINTING_GUI_STATE_ALWAYS_CLOSED=Geschlossen
+SHIELD_PRINTING_GUI_STATE_FENCE=§7{0} §fZaun Verbindungen
+SHIELD_PRINTING_GUI_STATE_OPENABLE=§7{0} §fGeöffnet
+SHIELD_PRINTING_GUI_STATE_PISTON=§7{0} §fAusgefahren
+SHIELD_PRINTING_GUI_STATE_POWERABLE=§7{0} §fAktiviert
+SHIELD_PRINTING_GUI_STATE_WALL=§7{0} §fWand Verbindungen
+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
+# Simulator
+SIMULATOR_HELP=§8/§esimulator §8-§7 Legt dir den Simulatorstab ins Inventar
+SIMULATOR_CREATE_HELP=§8/§esimulator create §8[§7name§8] §8-§7 Erstelle einen neuen Simulator
+SIMULATOR_CHANGE_HELP=§8/§esimulator change §8-§7 Wechsel zu einem anderen Simulator
+SIMULATOR_DELETE_HELP=§8/§esimulator delete §8[§7name§8] §8-§7 Löscht den Simulator
+SIMULATOR_START_HELP=§8/§esimulator start §8[§7name§8] §8-§7 Startet die Simulation
+SIMULATOR_COPY_HELP=§8/§esimulator copy §8[§7to-copy§8] §8[§7name§8] §8-§7 Kopiert einen Simulator
+SIMULATOR_GUI_ITEM_NAME=§eTNT Simulator
+SIMULATOR_NO_SIM_IN_HAND=§cKein Simulator Item gewählt
+SIMULATOR_GUI_SELECT_SIM=Simulator wählen
+SIMULATOR_GUI_CREATE_SIM=§eSimulator erstellen
+SIMULATOR_GUI_CREATE_SIM_GUI=Simulator erstellen
+SIMULATOR_NAME_ALREADY_EXISTS=§cSimulator existiert bereits
+SIMULATOR_NAME_INVALID=§cUngültiger Name
+SIMULATOR_ERROR_COPY=§cFehler beim kopieren
+SIMULATOR_NOT_EXISTS=§cSimulator existiert nicht
+SIMULATOR_CREATE=§aSimulator erstellt
+SIMULATOR_EDIT_LOCATION=§7Editiere Positionen
+SIMULATOR_EDIT_PROPERTIES=§7Editiere Eigenschaften
+SIMULATOR_EDIT_OTHER=§7Editiere Andere
+SIMULATOR_EDIT_GROUP=§7Editiere Gruppe
+SIMULATOR_EDIT_GROUP_MENU=§eEditiere Gruppe
+SIMULATOR_WAND_NAME=§eKanonensimulator
+SIMULATOR_WAND_NAME_SELECTED=§7Kanonensimulator §8- §e{0}
+SIMULATOR_WAND_LORE_1=§eRechtsklick §8- §7Füge eine Position hinzu
+SIMULATOR_WAND_LORE_2=§eSneaken §8- §7Freie Bewegung
+SIMULATOR_WAND_LORE_3=§eLinksklick §8- §7Starte die Simulation
+SIMULATOR_WAND_LORE_4=§eRechtsklick Luft §8- §7Öffne die GUI
+SIMULATOR_WAND_LORE_5=§eDoppel Shift §8- §7Wechsel zwischen TNT und Redstone Block
+SIMULATOR_REGION_FROZEN=§cSimulator kann nicht in eingefrorenen Regionen genutzt werden
+## Other
+SIMULATOR_PLUS_ONE=§7+1
+SIMULATOR_PLUS_PIXEL_SHIFT=§eShift §7Click für §e+0,0625
+SIMULATOR_PLUS_FIVE_SHIFT=§eShift §7Click für §e+5
+SIMULATOR_MINUS_ONE=§7-1
+SIMULATOR_MINUS_PIXEL_SHIFT=§eShift §7Click für §e-0,0625
+SIMULATOR_MINUS_FIVE_SHIFT=§eShift §7Click für §e-5
+SIMULATOR_POSITION_X=§7x-Position
+SIMULATOR_POSITION_Y=§7y-Position
+SIMULATOR_POSITION_Z=§7z-Position
+SIMULATOR_BACK=§eZurück
+SIMULATOR_GUI_TOTAL_TNT=§7Gesamt TNT§8: §e{0}
+SIMULATOR_DELETED=§cSimulator gelöscht
+## GUI
+SIMULATOR_POSITION_EDIT=§ePosition bearbeiten
+SIMULATOR_POSITION_ADD=§ePosition setzen
+SIMULATOR_GUI_TNT_SPAWN_NAME=§eTNT
+SIMULATOR_GUI_TNT_SPAWN_LORE_1=§7TNT-Anzahl§8: §e{0}
+SIMULATOR_GUI_TNT_SPAWN_LORE_3=§7Lebensdauer§8: §e{0}
+SIMULATOR_GUI_TNT_GROUP_NAME=§eTNT Gruppe
+SIMULATOR_GUI_TNT_GROUP_LORE_1=§7Elementanzahl§8: §e{0}
+SIMULATOR_GUI_TNT_DISABLED=§cDisabled
+SIMULATOR_GUI_NAME=Kanonensimulator
+SIMULATOR_GUI_DELETE=§cTNT löschen
+SIMULATOR_GUI_AUTO_TRACE=§eAutoTrace§8: §7{0}
+SIMULATOR_GUI_MOVE_ALL=§eAlle Verschieben
+SIMULATOR_ALIGNMENT_CENTER=§7Verschiebung§8: §eMitte
+SIMULATOR_ALIGNMENT_POSITIVE_X=§7Verschiebung§8: §ePositive X
+SIMULATOR_ALIGNMENT_NEGATIVE_X=§7Verschiebung§8: §eNegative X
+SIMULATOR_ALIGNMENT_POSITIVE_Z=§7Verschiebung§8: §ePositive Z
+SIMULATOR_ALIGNMENT_NEGATIVE_Z=§7Verschiebung§8: §eNegative Z
+SIMULATOR_MOVE_ALL_GUI_NAME=TNT Verschieben
+SIMULATOR_TNT_SPAWN_GUI_NAME=TNT konfigurieren {0}
+SIMULATOR_TNT_SPAWN_LORE=§eZum Ändern klicken
+SIMULATOR_TNT_SPAWN_COUNT=§7TNT-Anzahl §8- §e{0}
+SIMULATOR_TNT_SPAWN_COUNT_ANVIL_GUI_NAME=Anzahl TNT
+SIMULATOR_TNT_SPAWN_TICK=§7Tick §8- §e{0}
+SIMULATOR_TNT_SPAWN_TICK_ANVIL_GUI_NAME=Tick Offset
+SIMULATOR_TNT_SPAWN_FUSE=§7Lebensdauer §8- §e{0}
+SIMULATOR_TNT_SPAWN_FUSE_ANVIL_GUI_NAME=Fuse-Ticks
+SIMULATOR_TNT_SPAWN_VELOCITY_NAME=§7TNT
+SIMULATOR_TNT_SPAWN_VELOCITY_X=§7TNT §eSprung X §8- {0}
+SIMULATOR_TNT_SPAWN_VELOCITY_Y=§7TNT §eSprung Y §8- {0}
+SIMULATOR_TNT_SPAWN_VELOCITY_Z=§7TNT §eSprung Z §8- {0}
+SIMULATOR_TNT_SPAWN_VELOCITY_ON=§aan
+SIMULATOR_TNT_SPAWN_VELOCITY_OFF=§caus
+SIMULATOR_TNT_SPAWN_POSITION_X=§7x-Position §8- §e{0}
+SIMULATOR_TNT_SPAWN_POSITION_Y=§7y-Position §8- §e{0}
+SIMULATOR_TNT_SPAWN_POSITION_Z=§7z-Position §8- §e{0}
+SIMULATOR_TNT_SPAWN_ACTIVATED_WITH=§7Gezündet durch §8- §e{0}
+SIMULATOR_TNT_SPAWN_ACTIVATED_WITH_COMPARATOR=Comparator
+SIMULATOR_TNT_SPAWN_ACTIVATED_WITH_REPEATER=Repeater
+SIMULATOR_TNT_SPAWN_ACTIVATED_WITH_OBSERVER=Observer
+SIMULATOR_TNT_SPAWN_MATERIAL=§eMaterial
+SIMULATOR_TNT_SPAWN_MATERIAL_LORE_1=§7Jetziges Material§8: §e{0}
+SIMULATOR_TNT_SPAWN_MATERIAL_LORE_2=§eLink-Click §7Zum ändern
+SIMULATOR_TNT_SPAWN_MATERIAL_LORE_3=§eRechts-Click §7Zum zurücksetzten
+SIMULATOR_MATERIAL_GUI_NAME=Material ändern
+SIMULATOR_MATERIAL_CLICK=§eKlicken zum wählen
+SIMULATOR_TNT_SPAWN_ADD_IGNITION_PHASE=§eZündphase hinzufügen
+SIMULATOR_TNT_SPAWN_ADD_TNT=§eTNT hinzufügen
+SIMULATOR_TNT_SPAWN_REMOVE_TNT=§cEntfernen
+SIMULATOR_TNT_SPAWN_POSITION_ANVIL_GUI_NAME=Position
+# SmartPlace
+SMART_PLACE_HELP=§8/§esmartplace §8-§7 Toggled SmartPlace
+SMART_PLACE_INFO=§7Plaziert rotierbare Blöcke beim §esneaken§7 von dir §eweg§7.
+SMART_PLACE_ENABLE=§aSmartPlace aktiviert
+SMART_PLACE_DISABLE=§cSmartPlace deaktiviert
+# InventoryFiller
+INVENTORY_FILL_HELP=§8/§einventoryfill §8- §7Toggled InventoryFill
+INVENTORY_FILL_INFO=§7Hilft dir, Behälter zu füllen, indem du sie beim sneaken ansiehst und den Gegenstand fallen lässt. Oder scrolle einfach auf einen Behälter, um die Menge des gehaltenen Gegenstandes darin zu ändern.
+INVENTORY_FILL_ENABLE=§aInventoryFiller activated
+INVENTORY_FILL_DISABLE=§cInventoryFiller deactivated
+# Killchecker
+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
+BLOCK_COUNTER_HELP_ENABLE=§8/§eblockcounter enable §8- §7Schalte den BlockCounter an
+BLOCK_COUNTER_HELP_DISABLE=§8/§eblockcounter disable §8- §7Schalte den BlockCounter aus
+BLOCK_COUNTER_MESSAGE=§7Schaden §8> §e{0} §7Blöcke §e{1} §7TNT §e{2} §7Blöcke/TNT §e{3} §7Blöcke/tick
+BLOCK_COUNTER_MESSAGE_SECOND=§7Schaden §8> §e{0} §7Blöcke §e{1} §7TNT §e{2} §7Blöcke/TNT §e{3} §7Blöcke/s
+BLOCK_COUNTER_ENABLE=§7BlockCounter angemacht
+BLOCK_COUNTER_DISABLE=§7BlockCounter ausgemacht
+# DepthCounter
+DEPTH_COUNTER_MESSAGE=§7Tiefe §8> §7
+# TPSLimit
+TPSLIMIT_FREEZE_HELP=§8/§etpslimit 0 §8-§7 Friere TPS ein
+TPSLIMIT_LIMIT_HELP=§8/§etpslimit §8[§720>x>0.5§8] §8-§7 Verlangsame die TPS
+TPSLIMIT_WARP_HELP=§8/§etpslimit §8[§7x>20§8] §8-§7 Beschleunige die TPS
+TPSLIMIT_DEFAULT_HELP=§8/§etpslimit default §8-§7 Setze die TPS auf 20
+TPSLIMIT_HELP=§8/§etpslimit §8-§7 Zeige die jetzige TPS
+TICK_FREEZE_HELP=§8/§etick rate 0 §8-§7 Friere TPS ein
+TICK_FREEZE_HELP_2=§8/§etick freeze §8-§7 Friere TPS ein
+TICK_UNFREEZE_HELP=§8/§etick unfreeze §8-§7 Setze die TPS auf 20
+TICK_LIMIT_HELP=§8/§etick rate §8[§720>x>0.5§8] §8-§7 Verlangsame die TPS
+TICK_WARP_HELP=§8/§etick rate §8[§7x>20§8] §8-§7 Beschleunige die TPS
+TICK_DEFAULT_HELP=§8/§etick rate default §8-§7 Setze die TPS auf 20
+TICK_HELP=§8/§etick rate §8-§7 Zeige die jetzige TPS
+TICK_STEPPING_HELP=§8/§etick step §8<§7Ticks§8> §8-§7 Spule n ticks oder 1 vor
+TICK_WARPING_HELP=§8/§etick warp §8<§7Ticks§8> §8<§7TPS§8> §8-§7 Warpe n ticks oder 1 vor
+TICK_BOSSBAR=§e{0}§8/§7{1} gesprungen
+TPSLIMIT_GUI_ITEM_NAME=§eTPS Limiter
+TPSLIMIT_GUI_ITEM_LORE=§7Aktuell: §e{0}
+TPSLIMIT_ANVIL_GUI=Neues TPS Limit
+TPSLIMIT_CURRENT=§7Jetziges TPS limit§8: §e{0}
+TPSLIMIT_SET=§eTPS limit auf {0} gesetzt.
+TPSLIMIT_FROZEN=§eTPS eingefroren.
+# Trace
+TRACE_RECORD=§aan
+TRACE_HAS_TRACES=§ehat Traces
+TRACE_MESSAGE_START=§aTNT-Tracer gestartet
+TRACE_MESSAGE_AUTO_START=§eAuto TNT-Tracer gestartet
+TRACE_MESSAGE_AUTO_STOP=§cAuto TNT-Tracer gestoppt
+TRACE_MESSAGE_STOP=§cTNT-Tracer gestoppt
+TRACE_MESSAGE_CLEAR=§cAlle TNT-Positionen gelöscht
+TRACE_MESSAGE_CLICK_ISOLATE=§eKlicken zum §aisolieren§8/§causblenden
+TRACE_MESSAGE_SHOW_AT=§aTNT-positions angezeigt bei {0}
+TRACE_MESSAGE_SHOW_FROM=§aAll TNT-positions angezeigt von {0}
+TRACE_MESSAGE_SHOW_FROM_TO=§aAll TNT-positions angezeigt von {0} bis {1}
+TRACE_MESSAGE_BROADCAST=§e{0} teilte seinen Trace-Show-Status.
+TRACE_MESSAGE_BROADCAST_HOVER=§eZum Ansehen klicken.
+TRACE_MESSAGE_FOLLOW=§aSie folgen nun {0} Trace show state
+TRACE_MESSAGE_FOLLOW_SELF=§cSie können sich selbst nicht folgen!
+TRACE_MESSAGE_UNFOLLOW=§cSie folgen nicht mehr dem Status einer Trace-Show
+TRACE_MESSAGE_SHOW_TO_SMALLER=§cBis muss größer als von sein
+TRACE_MESSAGE_ISOLATE=§eTNT Positionen wurden isoliert
+TRACE_COMMAND_HELP_BROADCAST=§8/§etrace broadcast §8- §7Teilt den aktuellen Trace-Show-Status mit anderen
+TRACE_COMMAND_HELP_FOLLOW=§8/§etrace follow §8[§ePlayer§8] §8- §7Verfolgen eines Spielers Status anzeigen
+TRACE_COMMAND_HELP_UNFOLLOW=§8/§etrace unfollow §8- §7Den Status der Trace-Anzeige aufheben
+TRACE_COMMAND_HELP_START=§8/§etrace start §8- §7Startet die Aufnahme aller TNT-Positionen
+TRACE_COMMAND_HELP_STOP=§8/§etrace stop §8- §7Stoppt den TNT-Tracer
+TRACE_COMMAND_HELP_AUTO=§8/§etrace toggleauto §8- §7Automatischer Aufnahmenstart
+TRACE_COMMAND_HELP_SHOW=§8/§etrace show §8<§eParameter§8> - §7Zeigt alle TNT-Positionen
+TRACE_COMMAND_HELP_SHOW_AT=§8/§etrace show §8(§etime§8|§7fuse§8) §7at §8<§eTIME§8> - §7Zeigt alle TNT-Positionen bei §8<§eTIME§8> an
+TRACE_COMMAND_HELP_SHOW_FROM=§8/§etrace show §8(§etime§8|§7fuse§8) §7from §8<§eFROM§8> - §7Zeigt alle TNT-Positionen von §8<§eFROM§8>
+TRACE_COMMAND_HELP_SHOW_FROM_TO=§8/§etrace show §8(§etime§8|§7fuse§8) §7from §8<§eFROM§8> §7to §8<§eTO§8> - §7Zeigt alle TNT-Positionen zwischen §8<§eFROM§8> und §8<§eTO§8>
+TRACE_COMMAND_HELP_HIDE=§8/§etrace hide §8- §7Versteckt alle TNT-Positionen
+TRACE_COMMAND_HELP_DELETE=§8/§etrace delete §8- §7Löscht alle TNT-Positionen
+TRACE_COMMAND_HELP_ISOLATE=§8/§etrace isolate §8[§eTrace§8] §8[§eTNT§8] §8- §7Isoliert spezifische TNTs des Traces
+TRACE_GUI_ITEM_NAME=§eTracer
+TRACE_GUI_ITEM_LORE=§7Status§8: {0}
+TRACE_GUI_NAME=Tracer Gui
+TRACE_GUI_TRACE_INACTIVE=§eTracer Starten
+TRACE_GUI_TRACE_ACTIVE=§eTracer Stoppen
+TRACE_GUI_TRACE_ACTIVE_AUTO=§eAuto-Trace ist Aktiv
+TRACE_GUI_AUTO_TRACE_INACTIVE=§eAuto-Tracer Aktivieren
+TRACE_GUI_AUTO_TRACE_ACTIVE=§eAuto-Tracer Deaktivieren
+TRACE_GUI_DELETE=§eTrace Löschen
+# Loader
+LOADER_SETUP=§eEinrichtung
+LOADER_RUNNING=§aLaufend
+LOADER_PAUSE=§7Pausiert
+LOADER_END=§8Beendet
+LOADER_SINGLE=§aEinmal
+LOADER_MESSAGE_INTERACT=§e{0} hinzugefügt {1}
+LOADER_BUTTON_TNT=TNT
+LOADER_BUTTON_SWITCH=Hebel
+LOADER_BUTTON_WOOD_BUTTON=Holzknopf
+LOADER_BUTTON_STONE_BUTTON=Steinknopf
+LOADER_BUTTON_PRESSURE_PLATE=Druckplatte
+LOADER_BUTTON_WEIGHTED_PRESSURE_PLATE=Druckplatte
+LOADER_BUTTON_TRIPWIRE=Tripwire
+LOADER_BUTTON_NOTEBLOCK=Noteblock
+LOADER_BUTTON_DAYLIGHT_DETECTOR=Tageslichtsensor
+LOADER_BUTTON_COMPARATOR=Comparator
+LOADER_BUTTON_REPEATER=Repeater
+LOADER_BUTTON_LECTERN=Lectern
+LOADER_BUTTON_TRAPDOOR=Trapdoor
+LOADER_BUTTON_DOOR=Door
+LOADER_BUTTON_FENCEGATE=Fencegate
+LOADER_HELP_SETUP=§8/§eloader setup §8- §7Startet die Aufnahme der Aktionen
+LOADER_SETUP_STOP_FIRST=§cBitte stoppe zuerst den Loader
+LOADER_HELP_START=§8/§eloader start §8- §7Spielt die zuvor aufgenommenen Aktionen ab
+LOADER_HELP_SINGLE=§8/§eloader single §8- §7Spielt die zuvor aufgenommenen Aktionen einmal ab
+LOADER_HELP_PAUSE=§8/§7loader pause §8- §7Pausiert das Abspielen
+LOADER_HELP_GUI=§8/§7loader settings §8- §7Zeigt die Einstellungen an
+LOADER_HELP_STOP=§8/§eloader stop §8- §7Stoppt die Aufnahme bzw. das Abspielen
+LOADER_HELP_WAIT=§8/§7loader wait §8[§7Ticks§8] - §7Setzt die Wartezeit zwischen Schüssen
+LOADER_HELP_SPEED=§8/§7loader speed §8[§7Ticks§8] - §7Setzt die Wartezeit zwischen Aktionen
+LOADER_NO_LOADER=§cDu hast noch keinen Loader. Erstelle dir einen mit /loader setup
+LOADER_NEW=§7Belade und feuer einmal die Kanone ab, um den Loader zu initialisieren.
+LOADER_HOW_TO_START=§7Führe dann /§eloader start§7 um den Loader zu starten
+LOADER_ACTIVE=§7Der Loader ist nun aktiviert.
+LOADER_STOP=§7Der Loader ist nun gestoppt.
+LOADER_SINGLE_CMD=§7Der Loader spielt nun einmal ab.
+LOADER_PAUSED=§7Der Loader ist nun pausiert.
+LOADER_SMALL_TIME=§cDie Wartezeit ist zu klein
+LOADER_NEW_TIME=§7Die Schusswartezeit ist nun: {0}
+LOADER_NEW_LOAD_TIME=§7Die Setzwartezeit ist nun: {0}
+LOADER_NOTHING_RECORDED=§cEs wurden keine Elemente aufgenommen!
+LOADER_GUI_TITLE=Loader Einstellungen
+LOADER_GUI_SHOW_ALL=Zeige alles
+LOADER_GUI_SHOW_INTERACTIONS=Zeige Interaktionen
+LOADER_GUI_SHOW_WAITS=Zeige Wartezeiten
+LOADER_GUI_SHOW_WAITS_BETWEEN_TNT=Zeige Wartezeiten zwischen TNT
+LOADER_GUI_SHOW_TNT=Zeige TNT
+LOADER_GUI_SHOW_WAITS_SET_ALL=§7Wait Time alle
+LOADER_GUI_SHOW_WAITS_TITLE=§7Wartezeit
+LOADER_GUI_SETTINGS_TITLE=Einstellungen
+LOADER_GUI_COPY_TITLE=Anzahl Kopien
+LOADER_GUI_SETTINGS_BACK=§8Zurück
+LOADER_GUI_SETTINGS_COPY=§7Kopieren
+LOADER_GUI_SETTINGS_DELETE=§cLöschen
+LOADER_GUI_WAIT_TITLE=Wartezeit
+LOADER_GUI_WAIT_BACK=§8Zurück
+LOADER_GUI_CLICK_TO_EDIT=§7Klicke zum editieren
+LOADER_GUI_ITEM_NAME=§7{0}§8: §e{1}
+LOADER_SETTING_NAME=§7{0}
+LOADER_SETTING_MODES=§7Modi§8: §e{0}
+LOADER_SETTING_POWER=§7Redstone Stärke§8: §e{0}
+LOADER_SETTING_TICKS=§7Ticks§8: §e{0}
+LOADER_SETTING_REPEATER=§7Repeater§8: §e{0}
+LOADER_SETTING_WAIT=§7Wartezeit§8: §e{0} Tick(s)
+LOADER_SETTING_WAIT_NAME=Wartezeit
+LOADER_SETTING_TICKS_NAME=Ticks
+LOADER_SETTING_TICKS_REMOVE_ONE=§c-1
+LOADER_SETTING_TICKS_REMOVE_ONE_SHIFT=§7Shift§8: §c-5
+LOADER_SETTING_TICKS_ADD_ONE=§a+1
+LOADER_SETTING_TICKS_ADD_ONE_SHIFT=§7Shift§8: §a+5
+LOADER_SETTING_TNT_NAME=§cTNT
+LOADER_SETTING_TNT_X=§7X§8: §e{0}
+LOADER_SETTING_TNT_Y=§7Y§8: §e{0}
+LOADER_SETTING_TNT_Z=§7Z§8: §e{0}
+LOADER_INTERACTION_NOOP=NOOP
+LOADER_INTERACTION_PLACE=Platzieren
+LOADER_INTERACTION_INTERACT=Interagiere
+LOADER_INTERACTION_POWERED=Aktiviert
+LOADER_INTERACTION_UNPOWERED=Deaktiviert
+LOADER_INTERACTION_PAGE_PREV=Vorherige Seite
+LOADER_INTERACTION_PAGE_NEXT=Nächste Seite
+LOADER_INTERACTION_PAGE=Seite {0}
+LOADER_INTERACTION_ACTIVE=Aktiviert
+LOADER_INTERACTION_INACTIVE=Deaktiviert
+LOADER_INTERACTION_WAIT_FOR=Darauf warten
+LOADER_INTERACTION_NO_WAIT_FOR=Nicht darauf warten
+LOADER_INTERACTION_OPEN=Geöffnet
+LOADER_INTERACTION_CLOSED=Geschlossen
+LOADER_INTERACTION_COMPARE=Vergleichen
+LOADER_INTERACTION_SUBTRACT=Subtrahieren
+# Loadtimer
+LOADTIMER_HELP_OVERVIEW=§7Messe dich und deine Freunde beim Beladen einer Kanone und bekomme informationen über die Kanone
+LOADTIMER_HELP_START_1=§8/§eloadtimer start §8-§7 Startet den einfachen Loadtimer
+LOADTIMER_HELP_START_2=§8/§7loadtimer start §8[§7full/half§8] - §7Starte den Timer in einem bestimmten Modus
+LOADTIMER_HELP_START_3=§7Loadtimer Modis: Full -> Misst vom ersten TNT bis zur Treib-Explosion, kann somit besser die Schuss Frequent berechnen. Half -> Misst nur bis zur Aktivierung
+LOADTIMER_HELP_STOP=§8/§eloadtimer stop §8-§7 Stoppe den Aktuellen Loadtimer
+LOADTIMER_GUI_GLOBAL=§eLoadtimer gibt es nicht in der Global Region!
+LOADTIMER_GUI_STOP=§eLoadtimer stoppen
+LOADTIMER_GUI_START=§eLoadtimer starten
+LOADTIMER_GUI_TITLE=Loadtimer Modus
+LOADTIMER_GUI_FULL=§eFull
+LOADTIMER_GUI_HALF=§eHalf
+LOADTIMER_WAITING=§7Platziere ein TNT zum starten...
+LOADTIMER_BOSSBAR=§7Tick: §e{0}§7(§e{1}§7) Zeit: §e{2}s §7Tnt: §e{3} §7Blöcke
+LOADTIMER_ACTIVATED=§7Warte auf Zündung
+LOADTIMER_IGNITION=§7Warte auf Explosion
+LOADTIMER_SUMARY_HEAD=§7---=== (§eLoadtimer-Auswertung§7) ===---
+LOADTIMER_SUMARY_PLAYERTABLE_HEAD=§7Spieler: §eTNT §7(§eTNT/s§7)
+LOADTIMER_SUMARY_PLAYERTABLE_PLAYER=§7{0}: §e{1} §7(§e{2}/s§7)
+LOADTIMER_SUMARY_PLAYERTABLE_ALL=Insgesamt
+LOADTIMER_SUMARY_TIMES_HEAD=§7Zeiten: §eSekunden §7(§eTicks§7)
+LOADTIMER_SUMARY_TIMES_START=§7 || §7Start!
+LOADTIMER_SUMARY_TIMES_ACTIVATION=§7 || Aktivierung: §e{0}s §7(§e{1}t§7)
+LOADTIMER_SUMARY_TIMES_IGNITION=§7 || Zündung: §e{0}s §7(§e{1}t§7)
+LOADTIMER_SUMARY_TIMES_EXPLOSION=§7 || Explosion: §e{0}s §7(§e{1}t§7)
+LOADTIMER_SUMARY_TIMES_LAST=§7\\/
+LOADTIMER_SUMARY_STATS_HEAD=§7Kanonen-Stats§8:
+LOADTIMER_SUMARY_STATS_TNT=§7TNT: §e{0}
+LOADTIMER_SUMARY_STATS_FREQ=§7Belade Frequenz: §e{0}/m§8, §7Schuss Frequenz: §e{1}/m
+# Observer
+OBSERVER_HELP=§7Rechts-Klicke einen Observer um den Trace zu bekommen. Hierfür müssen Flammenpartikel an sein. Die Partikel werden im Block angezeigt.
+OBSERVER_HELP_ENABLE=§8/§eobserver enable §8-§7 Aktiviere den Observer Tracer
+OBSERVER_HELP_DISABLE=§8/§eobserver disable §8-§7 Deaktiviere den Observer Tracer
+OBSERVER_HELP_DELETE=§8/§eobserver delete §8-§7 Lösche den Observer Tracer
+OBSERVER_HELP_RETRACE=§8/§eobserver retrace §8-§7 Retrace den Observer Tracer
+OBSERVER_ENABLE=§7Observer Trace gestartet
+OBSERVER_DISABLE=§7Observer Trace gestoppt
+OBSERVER_DELETE=§7Observer Trace gelöscht
+OBSERVER_RETRACE_DONE=§7Observer Trace neu berechnet
+OBSERVER_RETRACE_NO_TRACE=§7Kein Observer Trace zum neu berechnen
+# Other
+OTHER_ITEMS_TELEPORT_NAME=§eTeleporter
+OTHER_ITEMS_TELEPORT_GUI_NAME=Teleportieren
+OTHER_ITEMS_TELEPORT_PLAYER_OFFLINE=§cDer Spieler ist Offline
+OTHER_ITEMS_CLEAR_NAME=§eClear
+OTHER_ITEMS_DECLUTTER_NAME=§eDeclutter
+OTHER_ITEMS_GAMEMODE_NAME=§eGamemode
+OTHER_ITEMS_GAMEMODE_LORE_1=§eRechts-Click§8:§7 Umschalten zwischen Creative und Spectator
+OTHER_ITEMS_GAMEMODE_LORE_2=§eLinks-Click§8:§7 Umschalten zwischen Survival und Adventure
+OTHER_ITEMS_KILLALL_NAME=§eKillAll
+OTHER_ITEMS_KILLALL_LORE_1=§eOhne Shift§8:§7 nur die Region
+OTHER_ITEMS_KILLALL_LORE_2=§eMit Shift§8:§7 Global
+OTHER_ITEMS_INVENTORY_FILL_LORE_ACTIVE=§aAktiviert
+OTHER_ITEMS_INVENTORY_FILL_LORE_INACTIVE=§aDeaktiviert
+OTHER_SLOT_INVALID_SLOT=§cInvalider Slot
+OTHER_NOCLIP_SLOT_INFO=§7Mit /slot kannst du den ausgewählten Slot ändern und einen anderen Block in den Slot nehmen.
+OTHER_NOCLIP_SLOT_HELP_PICK=§8/§eslot pick §8-§7 Lege den angeguckten Block ins Inventar
+OTHER_NOCLIP_SLOT_HELP_DROP=§8/§eslot drop §8-§7 Cleared deinen Slot
+OTHER_CLEAR_HELP_SELF=§8/§eclear §8- §7Leere dein Inventar
+OTHER_CLEAR_HELP_PLAYER=§8/§eclear §8[§7Player§8] §8- §7Leere ein Spieler Inventar
+OTHER_CLEAR_CLEARED=§7Dein Inventar wurde geleert.
+OTHER_CLEAR_FROM=§7Dein Inventar wurde von {0} §7geleert.
+OTHER_CLEAR_TO=§7Das Inventar von {0} §7wurde geleert.
+OTHER_DECLUTTER_HELP=§8/§edeclutter §8- §7Räume dein Inventar auf
+OTHER_DECLUTTER_DONE=§aDein Inventar wurde aufgeräumt.
+OTHER_GAMEMODE_UNKNOWN=§cUnbekannter Spielmodus.
+OTHER_GAMEMODE_POSSIBLE=§cMögliche Spielmodi: survival, adventure, creative, specator.
+OTHER_KILLALL_HELP_SELF=§8/§ekillall §8- §7Entferne alle Entities aus deiner Region
+OTHER_KILLALL_HELP_ALL=§8/§ekillall §8[§7Global§8/Local§7] §8- §7Entferne alle Entities aus deiner Region oder global
+OTHER_KILLALL_REGION=§a{0} Entities aus der Region entfernt
+OTHER_KILLALL_GLOBAL=§a{0} Entities aus der Welt entfernt
+OTHER_TELEPORT_HELP=§8/§etp §8[§7Player§8] §8-§7 Teleportiere dich zu einem Spieler
+OTHER_TELEPORT_SELF_0=§cSei eins mit dir selbst!
+OTHER_TELEPORT_SELF_1=§cDu brauchst Leute zum spielen? Wir haben auch einen TeamSpeak!
+OTHER_TELEPORT_SELF_2=§cNoch zu reisende Blöcke: 0; ETA: 0:00
+OTHER_TELEPORT_SELF_3=§cEin wenig bewegung muss ein.
+OTHER_TELEPORT_SELF_4=§cFür eine solche Distanz?
+OTHER_TIME_HELP=§8/§etime §8<§7Zeit 0=Morgen§8, §76000=Mittag§8, §718000=Mitternacht§8> - §7Setzt die Zeit auf dem Bau
+OTHER_TIME_INVALID=§cBitte gib eine Zahl zwischen 0 und 24000 an
+OTHER_TIME_RESULT=§7§oWhooosh
+OTHER_TPS_HEAD=§7TPS: 1s 10s 1m 5m 10m
+OTHER_TPS_MESSAGE=§7 §e{0}§7 §e{1}§7 §e{2}§7 §e{3}§7 §e{4}
+OTHER_TPS_SINGLE=§8TPS: §e{0}
+OTHER_BIND_HELP=§8/§ebind §8[§7Command§8] §8-§e Binde ein Befehl auf Item Interaktion
+OTHER_BIND_ERROR=§cFalscher oder unbekannter Befehl
+OTHER_BIND_UNBINDABLE=§cBefehl konnte nicht gebunden werden
+OTHER_BIND_MESSAGE_BIND=§7Befehl §e{0} §7ans Item gebunden
+OTHER_BIND_MESSAGE_UNBIND=§7Befehl entbunden
+# DebugStick
+DEBUG_STICK_COMMAND_HELP=§8/§edebugstick §8-§7 Erhalte einen DebugStick
+DEBUG_STICK_NAME=§eDebugstick
+#Skull Gui
+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
+NIGHT_VISION_ON=§eNightvision aktiviert
+NIGHT_VISION_ITEM_ON=§7Nightvision: §eAktiviert
+NIGHT_VISION_ITEM_OFF=§7Nightvision: §eDeaktiviert
+#Navigation Wand
+NAVIGATION_WAND=§eNavigation Wand
+NAVIGATION_WAND_LEFT_CLICK=§eLeft click: jump to location
+NAVIGATION_WAND_RIGHT_CLICK=§eRight click: pass through walls
+# Material
+MATERIAL_SEARCH_PROPERTY_TRUE=§aHat
+MATERIAL_SEARCH_PROPERTY_FALSE=§cHat nicht
+MATERIAL_SEARCH_PROPERTY_IGNORE=§eEgal
+MATERIAL_INV_NAME=§eMaterial {0}/{1}
+MATERIAL_SEARCH=§eSuchen
+MATERIAL_BACK=§eZurück
+MATERIAL_SEARCH_NAME=§eName
+MATERIAL_SEARCH_TRANSPARENT=§eTransparent
+MATERIAL_SEARCH_SOLID=§eSolide
+MATERIAL_SEARCH_GRAVITY=§eFallend
+MATERIAL_SEARCH_OCCLUDING=§eOccluding
+MATERIAL_SEARCH_INTERACTEABLE=§eInterargierbar
+MATERIAL_SEARCH_FLAMMABLE=§eFlammbar
+MATERIAL_SEARCH_BURNABLE=§eBrennbar
+MATERIAL_SEARCH_WATERLOGGABLE=§eWasserspeicherbar
+MATERIAL_SEARCH_BLASTRESISTANCE=§eBlast Resistance
+MATERIAL_SEARCH_VALUE=§8: §e{0}
+MATERIAL_BLAST_RESISTANCE=§8- §eBlast Resistance§8: §7{0}
+MATERIAL_HARDNESS=§8- §eHärte§8: §7{0}
+MATERIAL_TNT_BREAKABLE=§8- §eZerstörbar durch TNT
+MATERIAL_TNT_UNBREAKABLE=§8- §eNicht Zerstörbar durch TNT
+MATERIAL_TRANSPARENT=§8- §eTransparenter Block
+MATERIAL_SOLID=§8- §eSolider Block
+MATERIAL_GRAVITY=§8- §eFallender Block
+MATERIAL_OCCLUDING=§8- §eOccluding Block
+MATERIAL_INTERACTABLE=§8- §eInterargierbarer Block
+MATERIAL_FLAMMABLE=§8- §eFlammbarer Block
+MATERIAL_BURNABLE=§8- §eBrennbarer Block
+MATERIAL_WATERLOGGABLE=§8- §eWasserspeicherbarer Block
+# Region Items
+REGION_ITEM_COLOR=§7Color: §e{0}
+REGION_ITEM_COLOR_CHOOSE=Farbe Wählen
+REGION_ITEM_FIRE_ALLOW=§7Feuer: §eEingeschaltet
+REGION_ITEM_FIRE_DISALLOW=§7Feuer: §eAusgeschaltet
+REGION_ITEM_FREEZE_ALLOW=§7Freeze: §eEingeschaltet
+REGION_ITEM_FREEZE_DISALLOW=§7Freeze: §eAusgeschaltet
+REGION_ITEM_PROTECT_ALLOW=§7Protect: §eEingeschaltet
+REGION_ITEM_PROTECT_DISALLOW=§7Protect: §eAusgeschaltet
+REGION_ITEM_RESET=§eReset
+REGION_ITEM_TESTBLOCK=§eTestblock
+REGION_ITEM_TNT_OFF=§7TNT: §eAusgeschaltet
+REGION_ITEM_TNT_ONLY_TB=§7TNT: §enur Testblock
+REGION_ITEM_TNT_ONLY_BUILD=§7TNT: §enur Baubereich
+REGION_ITEM_TNT_ON=§7TNT: §eEingeschaltet
+REGION_ITEM_SELECTOR_TITLE=Tnt Modus
+REGION_ITEM_SELECTOR_ON=§eEinschalten
+REGION_ITEM_SELECTOR_ONLY_TB=§enur Testblock
+REGION_ITEM_SELECTOR_ONLY_BUILD=§enur Baubereich
+REGION_ITEM_SELECTOR_OFF=§eAusschalten
+#Region
+REGION_COLOR_HELP_COLOR=§8/§ecolor §8[§7Color§8] §8- §7Setze die Farbe der Region
+REGION_COLOR_HELP_COLOR_TYPE=§8/§ecolor §8[§7Color§8] §8[§7Type§8] §8- §7Setze die Farbe der Region oder Global
+REGION_COLOR_GLOBAL=§7Alle Regions farben auf §e{0}§7 gesetzt
+REGION_COLOR_NO_REGION=§cDu befindest dich derzeit in keiner Region
+REGION_FIRE_HELP=§8/§efire §8- §7Toggle Feuerschaden
+REGION_FIRE_ENABLED=§cRegions Feuerschaden deaktiviert
+REGION_FIRE_DISABLED=§aRegions Feuerschaden aktiviert
+REGION_FREEZE_HELP=§8/§efreeze §8- §7Toggle Freeze
+REGION_FREEZE_ENABLED=§cRegion eingefroren
+REGION_FREEZE_DISABLED=§aRegion aufgetaut
+REGION_ITEMS_HELP=§8/§eitems §8- §7Toggle Items
+REGION_ITEMS_ENABLED=§aItems aktiviert in dieser Region
+REGION_ITEMS_DISABLED=§cItems deaktiviert in dieser Region
+REGION_PROTECT_HELP=§8/§eprotect §8- §7Schütze die Region
+REGION_PROTECT_DISABLE=§cBoden Schutz aufgehoben
+REGION_PROTECT_ENABLE=§aBoden geschützt
+REGION_PROTECT_FALSE_REGION=§cDu befindest dich derzeit in keiner (M)WG-Region
+REGION_NO_GRAVITY_HELP = §8/§enogravity §8- §7Toggle NoGravity
+REGION_NO_GRAVITY_ENABLED = §aNoGravity aktiviert in dieser Region
+REGION_NO_GRAVITY_DISABLED = §cNoGravity deaktiviert in dieser Region
+REGION_REGION_HELP_UNDO=§8/§eregion undo §8- §7Mache die letzten 20 /testblock oder /reset rückgängig
+REGION_REGION_HELP_REDO=§8/§eregion redo §8- §7Wiederhole die letzten 20 §8/§7rg undo
+REGION_REGION_HELP_RESTORE=§8/§eregion restore §8- §7Setzte die Region zurück, ohne das Gebaute zu löschen
+REGION_REGION_HELP_RESTORE_SCHEMATIC=§8/§eregion restore §8[§7Schematic§8] §8- §7Setzte die Region zurück, ohne das Gebaute zu löschen
+REGION_REGION_HELP_COPYPOINT=§8/§eregion copypoint §8- §7Teleportiere dich zum Regions Kopierpunkt
+REGION_REGION_HELP_TESTBLOCKPOINT=§8/§eregion testblockpoint §8- §7Teleportiere dich zum Regions Testblockpunkt
+REGION_REGION_HELP_CHANGESKIN_INFO=§8/§eregion changeskin §8- §7Gebe den Regions Skin aus
+REGION_REGION_HELP_CHANGESKIN=§8/§eregion changeskin §8[§7Skin§8] §8- §8Setzte den Regions Skin
+REGION_REGION_HELP_COPY=§8/§eregion copy [-e] [-s] §8- §8Kopieren des Baubereichs optional mit Erweiterungen oder Auswahl am Kopierpunkt
+REGION_REGION_HELP_PASTE=§8/§eregion paste [-a] [-s] §8[§7Skin§8] §8- §8Einfügen am Kopierpunkt optional ohne Luft und Auswahl des eingefügten Bereichs
+REGION_REGION_NOTHING_UNDO=§cNichts zum rückgängig machen
+REGION_REGION_UNDID=§7Letzte Aktion rückgangig gemacht
+REGION_REGION_NOTHING_REDO=§cNichts zum wiederhohlen
+REGION_REGION_REDID=§7Letzte Aktion wiederhohlt
+REGION_REGION_RESTORED=§7Region zurückgesetzt
+REGION_REGION_FAILED_RESTORE=§cFehler beim Zurücksetzen der Region
+REGION_REGION_COLORED=§7Region umgefärbt
+REGION_REGION_COLORED_FAILED=§7Nutze §e/rg restore§7 um manuell die Farbe zu ändern
+REGION_REGION_FAILED_COLORED=§cFehler beim umfärben der Region
+REGION_REGION_TP_COPY=§7Zum Kopierpunkt teleportiert
+REGION_REGION_TP_TEST_BLOCK=§7Zum Testblock teleportiert
+REGION_REGION_TP_UNKNOWN=§cNicht definierter Teleportierpunkt
+REGION_REGION_NO_REGION=§cDu bist in keiner Region
+REGION_REGION_NO_BUILD=§cDiese Region hat kein Baugebiet
+REGION_REGION_COPY_DONE=§eBauregion oder Selektion kopiert
+REGION_REGION_PASTE_DONE=§eBauregion oder Selektion eingefügt
+REGION_REGION_CHANGESKIN_INFO=§7Regions Skin ist §e{0}
+REGION_REGION_CHANGESKIN_INFO_CREATOR=§7Skin erstellt von §e{0}
+REGION_REGION_CHANGESKIN_UNKNOWN=§cRegions Skin ist nicht valide
+REGION_REGION_CHANGESKIN_INVALID=§cRegions Skin ist nicht erlaubt hier
+REGION_REGION_CHANGESKIN_CHANGE=§7Regions Skin ist auf §e{0}§7 geändert
+REGION_REGION_CHANGESKIN_CHANGE_UPDATE=§7Klicke §e§lHIER §7um den Skin anzuwenden
+REGION_REGION_CHANGESKIN_CHANGE_UPDATE_HOVER=§8/§ereset
+REGION_RESET_HELP_RESET=§8/§ereset §8- §7Setzte die Region zurück
+REGION_RESET_HELP_SCHEMATIC=§8/§ereset §8[§7Schematic§8] §8- §7Setzte die Region mit einer Schematic zurück
+REGION_RESET_RESETED=§7Region zurückgesetzt
+REGION_RESET_ERROR=§cFehler beim Zurücksetzen der Region
+REGION_RESET_NO_REGION=§cDu befindest dich derzeit in keiner Region
+REGION_TB_HELP_RESET=§8/§etestblock §8- §7Setzte den Testblock zurück
+REGION_TB_HELP_RESET_EXTENSION=§8/§etestblock §8[§7ExtensionType§8] §8- §7Setzte den Testblock zurück
+REGION_TB_HELP_SCHEMATIC=§8/§etestblock §8[§7Schematic§8] §8- §7Setzte den Testblock mit einer Schematic zurück
+REGION_TB_HELP_SCHEMATIC_EXTENSION=§8/§etestblock §8[§7Schematic§8] §8[§7ExtensionType§8] §8- §7Setzte den Testblock mit einer Schematic zurück
+REGION_TB_DONE=§7Testblock zurückgesetzt
+REGION_TB_ERROR=§cFehler beim Zurücksetzen des Testblocks
+REGION_TB_NO_REGION=§cDu befindest dich derzeit in keiner Region
+REGION_TB_NO_SCHEMSHARING=§cDu kannst aktuell keine Schematics teilen bis {0}.
+REGION_TB_NO_SCHEMRECEIVING=§cDer Besitzer dieses Bauservers kann keine Schematics erhalten bis {0}.
+REGION_TNT_HELP=§8/§etnt §8- §7Ändere das TNT verhalten
+REGION_TNT_HELP_MODE=§8/§etnt §8[§7Mode§8] §8- §7Setzte das TNT verhalten auf einen Modus
+REGION_TNT_ON=§aTNT-Schaden aktiviert
+REGION_TNT_OFF=§cTNT-Schaden deaktiviert
+REGION_TNT_TB=§aTNT-Schaden außerhalb Baurahmen aktiviert
+REGION_TNT_BUILD_DESTROY=§cEine Explosion hätte Blöcke im Baubereich zerstört
+REGION_TNT_TB_DESTROY=§cEine Explosion hätte Blöcke im Testblockbereich zerstört
+AFK_KICK_MESSAGE=§cAuf diesem Server ist seit 15 Minuten nichts passiert.
+AFK_WARNING_MESSAGE=§cDieser Server wird bei weiterer Inaktivität in einer Minute gestoppt
+SKIN_HELP=§8/§eskin §8[§7Kuerzel§8] §8[§7Creator§8|§epublic§8] §8[§7Name...§8] §8- §7Erstellt die Skin Schematics. 'public' als Creator nutzen für keinen Creator, danach die nachricht an YoyoNow kopieren (mit Click kopieren)
+SKIN_NO_REGION=§7Du steht in keiner Region, welche mit einem Skin versehen werden kann
+SKIN_ALREADY_EXISTS=§cDieser Skin existiert in der Form bereits
+SKIN_MESSAGE=§7Skin erstellt
+SKIN_MESSAGE_HOVER=§eKlicken zum kopieren für YoyoNow und an diesen senden
+# Panzern
+PANZERN_HELP=§8/§epanzern §8[§7Block§8] §8[§7Slab§8] §8- §7Panzer deine WorldEdit Auswahl
+PANZERN_PREPARE1=§71. Gucke nochmal nach, ob Läufe auch bis zur Panzergrenze führen.
+PANZERN_PREPARE2=§72. Teppich in Gänge auf dem Boden vereinfacht das panzern.
+PANZERN_PREPARE3=§73. Schildtechnik sollte explizit eingeschlossen sein.
+PANZERN_PREPARE4=§74. Innerhalb der zu panzernden Region zu stehen, beim Befehlausführen kann das Panzern verbessern.
+PANZERN_NO_WORLDEDIT=§cDu hast keine WorldEdit Selection
+PANZERN_PROGRESS=§e{0} §7Blöcke übrig, §e{1} §7Blöcke pro Sekunde, §e{2} §7Block Delta
+PANZERN_DONE=§aZuende gepanzert
+# Laufbau
+LAUFBAU_HELP=§8/§elaufbau §8[§7smallest§8|§7blastresistant§8] §8- §7Baue einen Lauf in deiner WorldEdit Auswahl mit den Traces
+LAUFBAU_HELP_SETTINGS=§8/§elaufbau settings §8- §7Öffnet die Settings GUI
+LAUFBAU_PREPARE1=§71. Trace die Kanonen so oft wie nötig, in allen Modi.
+LAUFBAU_PREPARE2=§72. Versuche alle Fails aus dem Trace zu löschen.
+LAUFBAU_NO_WORLDEDIT=§cDu hast keine WorldEdit Selection
+LAUFBAU_STATE_FILTERING_TRACES=Traces filtern
+LAUFBAU_STATE_PROCESSING_TRACES=Traces verbinden
+LAUFBAU_STATE_CREATE_LAUF=Lauf erstellen
+LAUFBAU_SIMPLE_PROGRESS=§e{0}§8: §e{1}§8/§e{2} §7Übrige Zeit §8: §e{3}
+LAUFBAU_DONE=§aZuende gebaut
+LAUFBAU_SETTINGS_GUI_NAME=§eLaufbau
+LAUFBAU_SETTINGS_ACTIVE=§aAktiv
+LAUFBAU_SETTINGS_INACTIVE=§cInaktiv
+LAUFBAU_SETTINGS_MIXED=§e{0}§8/§e{1} §aAktiv
+LAUFBAU_SETTINGS_GUI_BACK=§eBack
+LAUFBAU_SETTINGS_TOGGLE=§eClick §8-§7 Toggle
+LAUFBAU_SETTINGS_ADVANCED=§eMiddle-Click §8-§7 Erweiterte Einstellung
+LAUFBAU_BLOCK_COBWEB=§eCobweb
+LAUFBAU_BLOCK_GRASS_PATH=§eGrass Path
+LAUFBAU_BLOCK_SOUL_SAND=§eSoul Sand
+LAUFBAU_BLOCK_COCOA=§eCocoa
+LAUFBAU_BLOCK_TURTLE_EGG=§eTurtle Eggs
+LAUFBAU_BLOCK_CHEST=§eChest
+LAUFBAU_BLOCK_SNOW=§eSnow Layer
+LAUFBAU_BLOCK_PLAYER_WALL_HEAD=§ePlayer Wall Head
+LAUFBAU_BLOCK_STONECUTTER=§eStonecutter
+LAUFBAU_BLOCK_PLAYER_HEAD=§ePlayer Head
+LAUFBAU_BLOCK_CAKE=§eCake
+LAUFBAU_BLOCK_END_STONE_BRICK_SLAB=§eEndstone Brick Slabs
+LAUFBAU_BLOCK_SEA_PICKLE=§eSea Pickles
+LAUFBAU_BLOCK_CAMPFIRE=§eCampfire
+LAUFBAU_BLOCK_FLOWER_POT=§eFlower Pot
+LAUFBAU_BLOCK_IRON_TRAPDOOR=§eIron Trapdoor
+LAUFBAU_BLOCK_LILY_PAD=§eLily Pad
+LAUFBAU_BLOCK_WHITE_CARPET=§eCarpet
+LAUFBAU_BLOCK_END_ROD=§eEnd Rod
+LAUFBAU_BLOCK_LIGHTNING_ROD=§eLightning Rod
+LAUFBAU_BLOCK_CONDUIT=§eConduit
+LAUFBAU_BLOCK_BREWING_STAND=§eBrewing Stand
+LAUFBAU_BLOCK_BELL=§eBell
+LAUFBAU_BLOCK_GRINDSTONE=§eGrindstone
+LAUFBAU_BLOCK_HOPPER=§eHopper
+LAUFBAU_BLOCK_LANTERN=§eLantern
+LAUFBAU_BLOCK_END_STONE_BRICK_STAIRS=§eEndstone Brick Stairs
+LAUFBAU_BLOCK_CHORUS_PLANT=§eChorus Plant
+LAUFBAU_BLOCK_NETHER_BRICK_FENCE=§eNether Brick Fence
+LAUFBAU_BLOCK_IRON_BARS=§eIron Bars
+LAUFBAU_BLOCK_END_STONE_BRICK_WALL=§eEndstone Brick Walls
+LAUFBAU_BLOCK_CHAIN=§eChain
+LAUFBAU_BLOCK_BIG_DRIP_LEAF=§eBig Drip Leaf
+LAUFBAU_BLOCK_DRAGON_EGG=§eDragon Egg
+LAUFBAU_BLOCK_AZALEA=§eAzalea
+LAUFBAU_BLOCK_CANDLE=§eKerze
+LAUFBAU_BLOCK_CANDLE_CAKE=§eKuchen mit Kerze
+LAUFBAU_BLOCK_LECTERN=§eLectern
+LAUFBAU_FACING_NORTH=§8-§7 Richtung Norden
+LAUFBAU_FACING_SOUTH=§8-§7 Richtung Süden
+LAUFBAU_FACING_WEST=§8-§7 Richtung Westen
+LAUFBAU_FACING_EAST=§8-§7 Richtung Osten
+LAUFBAU_FACING_UP=§8-§7 Richtung Oben
+LAUFBAU_FACING_DOWN=§8-§7 Richtung Unten
+LAUFBAU_COUNT_1=§8-§7 Anzahl 1
+LAUFBAU_COUNT_2=§8-§7 Anzahl 2
+LAUFBAU_COUNT_3=§8-§7 Anzahl 3
+LAUFBAU_COUNT_4=§8-§7 Anzahl 4
+LAUFBAU_LAYERS_8=§8-§7 Ebenen 8
+LAUFBAU_LAYERS_7=§8-§7 Ebenen 7
+LAUFBAU_LAYERS_6=§8-§7 Ebenen 6
+LAUFBAU_LAYERS_3=§8-§7 Ebenen 3
+LAUFBAU_LAYERS_2=§8-§7 Ebenen 2
+LAUFBAU_TYPE_BOTTOM=§8-§7 Type Unten
+LAUFBAU_TYPE_TOP=§8-§7 Type Oben
+LAUFBAU_HALF_BOTTOM=§8-§7 Hälfte Unten
+LAUFBAU_HALF_TOP=§8-§7 Hälfte Oben
+LAUFBAU_OPEN=§8-§7 Geöffnet
+LAUFBAU_ATTACHMENT_CEILING=§8-§7 Befestigung Decke
+LAUFBAU_ATTACHMENT_FLOOR=§8-§7 Befestigung Boden
+LAUFBAU_ATTACHMENT_DOUBLE_WALL=§8-§7 Befestigung beidseitige Wand
+LAUFBAU_ATTACHMENT_SINGLE_WALL=§8-§7 Befestigung einseitige Wand
+LAUFBAU_ATTACHMENT_WALL=§8-§7 Befestigung Wand
+LAUFBAU_CONNECTION_FLOOR=§8-§7 Verbindung Boden
+LAUFBAU_CONNECTION_NORTH=§8-§7 Verbindung Norden
+LAUFBAU_CONNECTION_SOUTH=§8-§7 Verbindung Süden
+LAUFBAU_CONNECTION_EAST=§8-§7 Verbindung Osten
+LAUFBAU_CONNECTION_WEST=§8-§7 Verbindung Westen
+LAUFBAU_CONNECTION_DOWN=§8-§7 Verbindung Unten
+LAUFBAU_CONNECTION_UP=§8-§7 Verbindung Oben
+LAUFBAU_HANGING=§8-§7 hängend
+LAUFBAU_SHAPE_STRAIGHT=§8-§7 Form gerade
+LAUFBAU_SHAPE_OUTER_LEFT=§8-§7 Form äußere links
+LAUFBAU_SHAPE_INNER_LEFT=§8-§7 Form innere links
+LAUFBAU_TILT_NONE=§8-§7 Neigung keine
+LAUFBAU_TILT_PARTIAL=§8-§7 Neigung teilweise
+# UTILS
+SELECT_HELP=§8/§eselect §8[§7RegionsTyp§8] §8- §7Wähle einen RegionsTyp aus
+SELECT_EXTENSION_HELP=§8/§eselect §8[§7RegionsTyp§8] §8[§7Extension§8] §8- §7Wähle einen RegionsTyp aus mit oder ohne Extension
+SELECT_GLOBAL_REGION=§cDie globale Region kannst du nicht auswählen
+SELECT_NO_TYPE=§cDiese Region hat keinen {0}
+SELECT_NO_EXTENSION=§cDiese Region hat keine Ausfahrmaße
+SELECT_MESSAGE=§7WorldEdit auswahl auf {0}, {1}, {2} und {3}, {4}, {5} gesetzt
+SKULL_HELP=§8/§eskull §8[§eSpieler§8] §8-§7 Gibt einen SpielerKopf
+SKULL_INVALID=§cUngültiger Spieler
+SKULL_ITEM=§e{0}§8s Kopf
+SPEED_HELP=§8/§espeed §8[§71§8-§710§8|§edefault§8] §8-§7 Setzte deine Flug- und Laufgeschindigkeit.
+SPEED_CURRENT=§7Aktuelle geschwindigkeit§8: §e{0}
+SPEED_TOO_SMALL=§c{0} ist zu klein
+SPEED_TOO_HIGH=§c{0} ist zu hoch
+SPEED_ITEM=§eGeschwindigkeit
+SPEED_ITEM_LORE=§7Aktuell: §e
+SPEED_TAB_NAME=Geschwindigkeit eingeben
+WORLDEDIT_WAND=WorldEdit Wand
+WORLDEDIT_LEFTCLICK=Left click: select pos #1
+WORLDEDIT_RIGHTCLICK=Right click: select pos #2
+TNT_CLICK_HEADER=§8---=== §eTNT §8===---
+TNT_CLICK_ORDER=§eEntity Order§8: §e{0}
+TNT_CLICK_FUSE_TIME=§eFuseTime§8: §e{0}
+TNT_CLICK_POSITION_X=§7Position §eX§8: §e{0}
+TNT_CLICK_POSITION_Y=§7Position §eY§8: §e{0}
+TNT_CLICK_POSITION_Z=§7Position §eZ§8: §e{0}
+TNT_CLICK_VELOCITY_X=§7Velocity §eX§8: §e{0}
+TNT_CLICK_VELOCITY_Y=§7Velocity §eY§8: §e{0}
+TNT_CLICK_VELOCITY_Z=§7Velocity §eZ§8: §e{0}
+TNT_CLICK_COUNT=§7Anzahl §8: §e{0}
+TNT_CLICK_ISOLATE=§eIsolieren
+SELECT_ITEM_CHOOSE_EXTENSION=Extension auswählen
+SELECT_ITEM_CHOOSE_SELECTION=Auswahl auswählen
+SELECT_ITEM_NORMAL_EXTENSION=§eNormal
+SELECT_ITEM_EXTENDED_EXTENSION=§eAusgefahren
+SELECT_ITEM_SELECT=§eSelect
+SELECT_ITEM_AUSWAHL=§7Auswahl: §7{0} {1}
+SELECT_ITEM_RIGHT_CLICK=§7Rechtklick zum ändern
+SELECT_ITEM_BAURAHMEN=§eBaurahmen
+SELECT_ITEM_BAUPLATTFORM=§eBauplattform
+SELECT_ITEM_TESTBLOCK=§eTestblock
+CHESTFILLER_FILLED=§eKiste gefüllt
+PISTON_HELP_1=§7Rechts Klick auf Piston mit einem Slime Ball berechnet dir die bewegten Blöcke.
+PISTON_HELP_2=§7Die Anzahl ist Rot, wenn ein unmovable Block vorhanden ist.
+PISTON_HELP_3=§7Die Anzahl ist Gelb, wenn zu viele Blöcke vorhanden sind.
+PISTON_INFO=§7Bewegte Blöcke {0}{1}§8/§712
+# Warp
+WARP_LOC_X=§7X§8: §e{0}
+WARP_LOC_Y=§7Y§8: §e{0}
+WARP_LOC_Z=§7Z§8: §e{0}
+WARP_EXISTS=§7Ein Warp mit dem namen §e{0} §7existiert bereits
+WARP_NAME_RESERVED=§7Du kannst nicht §c{0} §7als name für einen Warp nutzen
+WARP_CREATED=§7Der Warp §e{0} §7wurde erstellt
+WARP_DELETE_HOVER=§e{0} §7löschen
+WARP_DELETED=§e{0} §7wurde gelöscht
+WARP_TELEPORT_HOVER=§7Zu §e{0} §7teleportieren
+WARP_MATERIAL_CHOOSE=Material auswählen
+WARP_GUI_NAME=Warps
+WARP_GUI_NO=§cHier gibt es noch keine Warps
+WARP_GUI_DISTANCE=§7Distanz: §e{0} §7Blöcke
+WARP_GUI_LCLICK=§7Links klicken zum teleportieren
+WARP_GUI_RCLICK=§7Rechts klicken zum editieren
+WARP_INFO_NAME=§7Name: §e{0}
+WARP_HELP_ADD=§8/§ewarp add §8[§7Name§8] §8- §7Erstelle einen neuen Warp Punkt
+WARP_HELP_TELEPORT=§8/§ewarp §8[§7Name§8] §8- §7Teleportiere dich zu einen Warp-Punkt
+WARP_HELP_INFO=§8/§ewarp info §8[§7Name§8] §8- §7Infos zu einem Punkt
+WARP_HELP_DELETE=§8/§ewarp delete §8[§7Name§8] §8- §7Lösche einen Warp
+WARP_HELP_GUI=§8/§ewarp gui §8- §7Öffne die Warp-GUI
+WARP_HELP_LIST=§8/§ewarp list §8- §7Liste alle Warp-Punkt auf
+# WORLD
+STOP_HELP=§8/§estop §8- §7Stoppt den Server
+STOP_MESSAGE=§eDer Server wird gestoppt
+KICKALL_HELP=§8/§ekickall §8- §7Kickt alle Spieler vom Server außer den Owner
+# Techhider
+TECHHIDER_HELP=§8/§etechhider §8- §7Techhider umschalten
+TECHHIDER_GLOBAL=§cKein Techhider in der globalen region
+TECHHIDER_ON=§aTechhider aktiviert
+TECHHIDER_OFF=§cTechHider deaktiviert
+# XRAY
+XRAY_HELP=§8/§exray §8- §7Xray umschalten
+XRAY_GLOBAL=§cKein Xray in der globalen region
+XRAY_ON=§aXray aktiviert
+XRAY_OFF=§cXray deaktiviert
+# WorldEdit
+COLORREPLACE_HELP=§8//§ecolorreplace §8[§7color§8] §8[§7color§8] §8- §7Ersetzt eine Farbe mit einer anderen
+TYPEREPLACE_HELP=§8//§etyreplace §8[§7type§8] §8[§7type§8] §8- §7Ersetzt einen Blockgruppe mit einer anderen
+# Schematics
+SCHEMATIC_GUI_ITEM=§eSchematics
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/BauSystem.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/BauSystem.java
new file mode 100644
index 00000000..23f768a7
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/BauSystem.java
@@ -0,0 +1,182 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2021 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;
+
+import com.comphenix.tinyprotocol.TinyProtocol;
+import de.steamwar.bausystem.configplayer.Config;
+import de.steamwar.bausystem.features.tpslimit.TPSFreezeUtils;
+import de.steamwar.bausystem.linkage.LinkageUtils;
+import de.steamwar.bausystem.region.loader.PrototypeLoader;
+import de.steamwar.bausystem.region.loader.RegionLoader;
+import de.steamwar.bausystem.region.loader.Updater;
+import de.steamwar.bausystem.utils.TickListener;
+import de.steamwar.bausystem.worlddata.WorldData;
+import de.steamwar.command.AbstractValidator;
+import de.steamwar.command.SWCommandUtils;
+import de.steamwar.message.Message;
+import lombok.Getter;
+import org.bukkit.Bukkit;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+import org.bukkit.event.Listener;
+import org.bukkit.plugin.Plugin;
+import org.bukkit.plugin.java.JavaPlugin;
+import org.bukkit.scheduler.BukkitRunnable;
+import org.bukkit.scheduler.BukkitTask;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
+import java.util.logging.Level;
+
+public class BauSystem extends JavaPlugin implements Listener {
+
+ // This should be treated as final!
+ public static Message MESSAGE;
+ public static final boolean DEV_SERVER = !System.getProperty("user.home").endsWith("minecraft");
+
+ @Getter
+ private static BauSystem instance;
+
+ @Override
+ public void onEnable() {
+ // LOGGER
+ fixLogging();
+
+ MESSAGE = new Message("BauSystem", getClassLoader());
+
+ instance = this;
+ SWUtils.setBausystem(instance);
+
+ try {
+ PrototypeLoader.load();
+ RegionLoader.load();
+ } catch (SecurityException e) {
+ Bukkit.getLogger().log(Level.SEVERE, e.getMessage(), e);
+ Bukkit.shutdown();
+ System.exit(1);
+ return;
+ }
+
+ new Updater(PrototypeLoader.file, PrototypeLoader::load);
+ new Updater(RegionLoader.file, RegionLoader::load);
+
+ SWCommandUtils.addValidator(Player.class, validator(Permission.BUILD));
+ SWCommandUtils.addValidator(CommandSender.class, validator(Permission.BUILD));
+ SWCommandUtils.addValidator("supervisor", validator(Permission.SUPERVISOR));
+ SWCommandUtils.addValidator("owner", validator(Permission.OWNER));
+
+ try {
+ LinkageUtils.link();
+ } catch (Exception e) {
+ Bukkit.getLogger().log(Level.SEVERE, e.getMessage(), e);
+ Bukkit.shutdown();
+ System.exit(1);
+ return;
+ }
+ TickListener.impl.init();
+ }
+
+ private AbstractValidator validator(Permission permission) {
+ return (commandSender, object, messageSender) -> {
+ if (commandSender instanceof Player) {
+ if (permission.hasPermission((Player) commandSender)) {
+ return true;
+ }
+ messageSender.send("NO_PERMISSION");
+ return false;
+ }
+ return true;
+ };
+ }
+
+ @Override
+ public void onDisable() {
+ LinkageUtils.unlink();
+
+ WorldData.write();
+ Config.getInstance().saveAll();
+ TinyProtocol.instance.close();
+ }
+
+ private void fixLogging() {
+ System.setErr(new PrintStream(new OutputStream() {
+ private StringBuilder current = new StringBuilder();
+
+ @Override
+ public void write(int b) throws IOException {
+ if (b == '\n') {
+ String logging = current.toString();
+ if (logging.contains("SLF4J")) {
+ Bukkit.getLogger().info(logging);
+ } else {
+ Bukkit.getLogger().warning(logging);
+ }
+ current = new StringBuilder();
+ } else {
+ current.append((char) b);
+ }
+ }
+ }));
+ }
+
+ public static BukkitTask runTaskLater(Plugin plugin, Runnable runnable, long delay) {
+ return new BukkitRunnable() {
+ private int counter = 1;
+
+ @Override
+ public void run() {
+ if (TPSFreezeUtils.isFrozen()) return;
+ if (counter >= delay) {
+ runnable.run();
+ cancel();
+ return;
+ }
+ counter++;
+ }
+ }.runTaskTimer(plugin, 0, 1);
+ }
+
+ public static BukkitTask runTaskTimer(Plugin plugin, Runnable runnable, long delay, long period) {
+ return new BukkitRunnable() {
+ private int counter = 1;
+ private boolean first = true;
+
+ @Override
+ public void run() {
+ if (TPSFreezeUtils.isFrozen()) return;
+ if (counter >= (first ? delay : period)) {
+ first = false;
+ runnable.run();
+ counter = 1;
+ return;
+ }
+ counter++;
+ }
+ }.runTaskTimer(plugin, 0, 1);
+ }
+
+ public static void runTaskTimer(Plugin plugin, Consumer consumer, long delay, long period) {
+ AtomicReference task = new AtomicReference<>();
+ task.set(runTaskTimer(plugin, () -> consumer.accept(task.get()), delay, period));
+ }
+}
\ No newline at end of file
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/Permission.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/Permission.java
new file mode 100644
index 00000000..0985a62d
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/Permission.java
@@ -0,0 +1,96 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2021 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;
+
+import de.steamwar.bausystem.config.BauServer;
+import de.steamwar.bausystem.features.world.BauMemberUpdate;
+import de.steamwar.bausystem.utils.BauMemberUpdateEvent;
+import de.steamwar.sql.BauweltMember;
+import de.steamwar.sql.SteamwarUser;
+import lombok.AllArgsConstructor;
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.function.Predicate;
+
+@AllArgsConstructor
+public enum Permission {
+
+ OWNER(bauweltMember -> false),
+ SUPERVISOR(bauweltMember -> {
+ return bauweltMember.isSupervisor();
+ }),
+ BUILD(bauweltMember -> {
+ if (isTempOnlySpectator(bauweltMember)) return false;
+ return bauweltMember.isBuild() || SUPERVISOR.permissionPredicate.test(bauweltMember);
+ }),
+ /**
+ * Only used for {@link BauMemberUpdate}
+ */
+ REAL_SPECTATOR(bauweltMember -> {
+ return !bauweltMember.isBuild() && !bauweltMember.isSupervisor();
+ }),
+ /**
+ * Primarily used for {@link de.steamwar.bausystem.linkage.specific.GuiItem}
+ */
+ MEMBER(bauweltMember -> {
+ return true;
+ });
+
+ private static final Set TEMP_ONLY_SPECTATOR = new HashSet<>();
+
+ private static boolean isTempOnlySpectator(BauweltMember bauweltMember) {
+ return TEMP_ONLY_SPECTATOR.contains(bauweltMember.getMemberID());
+ }
+
+ public static boolean isTempOnlySpectator(Player player) {
+ return TEMP_ONLY_SPECTATOR.contains(SteamwarUser.get(player.getUniqueId()).getId());
+ }
+
+ public static void forceOnlySpectator(Player player) {
+ TEMP_ONLY_SPECTATOR.add(SteamwarUser.get(player.getUniqueId()).getId());
+ BauMemberUpdate.baumemberUpdate();
+ }
+
+ /**
+ * Only used by {@link BauMemberUpdate}
+ */
+ public static void removeForceOnlySpectator(Player player) {
+ TEMP_ONLY_SPECTATOR.remove(SteamwarUser.get(player.getUniqueId()).getId());
+ }
+
+ private final Predicate permissionPredicate;
+
+ public boolean hasPermission(BauweltMember bauweltMember) {
+ if (bauweltMember == null) return false;
+ return permissionPredicate.test(bauweltMember);
+ }
+
+ public boolean hasPermission(Player member) {
+ if (SteamwarUser.get(member.getUniqueId()).getId() == BauServer.getInstance().getOwnerID()) {
+ return this != REAL_SPECTATOR;
+ }
+ BauweltMember bauweltMember = BauweltMember.getBauMember(BauServer.getInstance().getOwner(), member.getUniqueId());
+ if (bauweltMember == null) return this == REAL_SPECTATOR;
+ return permissionPredicate.test(bauweltMember);
+ }
+}
\ No newline at end of file
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/SWUtils.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/SWUtils.java
new file mode 100644
index 00000000..c7c9183f
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/SWUtils.java
@@ -0,0 +1,90 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2021 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;
+
+import de.steamwar.inventory.SWItem;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.experimental.UtilityClass;
+import net.md_5.bungee.api.ChatMessageType;
+import net.md_5.bungee.api.chat.TextComponent;
+import org.bukkit.Bukkit;
+import org.bukkit.Material;
+import org.bukkit.NamespacedKey;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+import org.bukkit.plugin.Plugin;
+
+import java.util.function.Function;
+
+@UtilityClass
+public class SWUtils {
+
+ @Getter
+ @Setter
+ private static Plugin bausystem;
+
+ public static void giveItemToPlayer(Player player, ItemStack itemStack) {
+ if (itemStack == null || itemStack.getType() == Material.AIR) {
+ return;
+ }
+ for (int i = 0; i < player.getInventory().getSize(); i++) {
+ ItemStack current = player.getInventory().getItem(i);
+ if (current != null && current.isSimilar(itemStack)) {
+ player.getInventory().setItem(i, null);
+ itemStack = current;
+ break;
+ }
+ }
+ ItemStack current = player.getInventory().getItemInMainHand();
+ player.getInventory().setItemInMainHand(itemStack);
+ if (current.getType() != Material.AIR) {
+ player.getInventory().addItem(current);
+ }
+ }
+
+ public static SWItem setCustomModelData(SWItem item, int customModelData) {
+ ItemMeta itemMeta = item.getItemMeta();
+ itemMeta.setCustomModelData(customModelData);
+ item.setItemMeta(itemMeta);
+ return item;
+ }
+
+ public static void sendToActionbar(Player p, String message) {
+ p.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText(message));
+ }
+
+ public static void actionBar(Function message) {
+ Bukkit.getOnlinePlayers().forEach(player -> {
+ player.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText(message.apply(player)));
+ });
+ }
+
+ public static void message(Function message) {
+ Bukkit.getOnlinePlayers().forEach(player -> {
+ player.sendMessage(message.apply(player));
+ });
+ }
+
+ public static NamespacedKey getNamespaceKey(String name) {
+ return new NamespacedKey(getBausystem(), name);
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/config/BauServer.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/config/BauServer.java
new file mode 100644
index 00000000..3dc792f0
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/config/BauServer.java
@@ -0,0 +1,52 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2021 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.config;
+
+import de.steamwar.linkage.Linked;
+import de.steamwar.providers.BauServerInfo;
+import de.steamwar.sql.SteamwarUser;
+import lombok.Getter;
+
+import java.util.UUID;
+
+@Linked
+public class BauServer {
+
+ @Getter
+ private static BauServer instance;
+
+ public BauServer() {
+ instance = this;
+ }
+
+ private Integer owner;
+
+ public UUID getOwner() {
+ return SteamwarUser.get(getOwnerID()).getUUID();
+ }
+
+ public int getOwnerID() {
+ //Lazy loading to improve startup time of the server in 1.15
+ if (owner == null) {
+ owner = BauServerInfo.getOwnerId();
+ }
+ return owner;
+ }
+}
\ No newline at end of file
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/configplayer/Config.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/configplayer/Config.java
new file mode 100644
index 00000000..edce8c58
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/configplayer/Config.java
@@ -0,0 +1,147 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2021 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.configplayer;
+
+import de.steamwar.bausystem.configplayer.serializer.ConfigurationSerializableSerializer;
+import de.steamwar.linkage.Linked;
+import de.steamwar.sql.UserConfig;
+import lombok.Getter;
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.PlayerJoinEvent;
+import org.bukkit.event.player.PlayerQuitEvent;
+import yapion.hierarchy.output.StringOutput;
+import yapion.hierarchy.types.YAPIONObject;
+import yapion.parser.YAPIONParser;
+import yapion.serializing.SerializeManager;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+import java.util.logging.Level;
+
+@Linked
+public class Config implements Listener {
+
+ static {
+ SerializeManager.add(new ConfigurationSerializableSerializer());
+ }
+
+ @Getter
+ private static Config instance;
+
+ {
+ instance = this;
+ }
+
+ private final Map playerConfigurations = new HashMap<>();
+
+ private static final Map CONFIG_CONVERTER_MAP = new HashMap<>();
+
+ public static void addConfigConverter(ConfigConverter configConverter) {
+ CONFIG_CONVERTER_MAP.putIfAbsent(configConverter.version(), configConverter);
+ }
+
+ @EventHandler
+ public void onPlayerJoin(PlayerJoinEvent event) {
+ get(event.getPlayer());
+ }
+
+ @EventHandler
+ public void onPlayerQuit(PlayerQuitEvent event) {
+ save(event.getPlayer());
+ playerConfigurations.remove(event.getPlayer().getUniqueId());
+ }
+
+ /**
+ * Get a PlayerConfig, optionally loads it from the DataBase and migrates it if necessary.
+ *
+ * @param player the player from whom to get the config.
+ * @return the config object
+ */
+ public YAPIONObject get(Player player) {
+ UUID uuid = player.getUniqueId();
+ if (!playerConfigurations.containsKey(uuid)) {
+ String s = UserConfig.getConfig(uuid, "bausystem");
+ YAPIONObject yapionObject;
+ if (s == null) {
+ yapionObject = ConfigCreator.createDefaultConfig();
+ } else {
+ yapionObject = YAPIONParser.parse(s);
+ }
+ yapionObject = update(yapionObject);
+ playerConfigurations.put(uuid, yapionObject);
+ return yapionObject;
+ }
+ return playerConfigurations.get(uuid);
+ }
+
+ public void saveAll() {
+ playerConfigurations.forEach((uuid, yapionObject) -> {
+ String string = yapionObject.toYAPION(new StringOutput()).getResult().replaceAll("\\+", "\\");
+ UserConfig.updatePlayerConfig(uuid, "bausystem", string);
+ });
+ playerConfigurations.clear();
+ }
+
+ /**
+ * Save a PlayerConfig, this does not remove the key value mapping from the map.
+ *
+ * @param player the player to save the config.
+ */
+ public void save(Player player) {
+ UUID uuid = player.getUniqueId();
+ if (playerConfigurations.containsKey(uuid)) {
+ YAPIONObject yapionObject = playerConfigurations.get(uuid);
+ String string = yapionObject.toYAPION(new StringOutput()).getResult().replaceAll("\\\\+", "\\\\");
+ UserConfig.updatePlayerConfig(uuid, "bausystem", string);
+ }
+ }
+
+ private YAPIONObject update(YAPIONObject yapionObject) {
+ int version = yapionObject.getPlainValue("@version");
+ while (version < ConfigCreator.currentVersion) {
+ ConfigConverter configConverter = CONFIG_CONVERTER_MAP.getOrDefault(version, null);
+ if (configConverter == null) {
+ Bukkit.getLogger().log(Level.SEVERE, "No updater found for version " + version);
+ return ConfigCreator.createDefaultConfig();
+ }
+ try {
+ configConverter.update(yapionObject);
+ } catch (Exception e) {
+ Bukkit.getLogger().log(Level.SEVERE, e.getMessage(), e);
+ return ConfigCreator.createDefaultConfig();
+ }
+ int newVersion = yapionObject.getPlainValue("@version");
+ if (version == newVersion) {
+ Bukkit.getLogger().log(Level.SEVERE, "Version Tag was the same after conversion");
+ return ConfigCreator.createDefaultConfig();
+ }
+ if (newVersion < version) {
+ Bukkit.getLogger().log(Level.SEVERE, "Version Tag was earlier after conversion");
+ return ConfigCreator.createDefaultConfig();
+ }
+ version = newVersion;
+ }
+ return yapionObject;
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/configplayer/ConfigConverter.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/configplayer/ConfigConverter.java
new file mode 100644
index 00000000..bf9f1fdf
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/configplayer/ConfigConverter.java
@@ -0,0 +1,37 @@
+package de.steamwar.bausystem.configplayer;
+
+import yapion.hierarchy.types.YAPIONObject;
+
+/**
+ * A new {@link ConfigConverter} should be written when you remove anything
+ * from the Config or modify any mayor part. When you move anything from
+ * any key to any other key you should write a new {@link ConfigConverter}.
+ * For adding any new key you should be able to get the default without
+ * having it in the Config. Anything you need to change you should also
+ * change the {@link ConfigCreator} accordingly, to produce the new Config.
+ */
+public interface ConfigConverter {
+
+ /**
+ * This describes the version this Converter can convert from. The version
+ * it should convert to is the version 1 above this number. But this is not
+ * a necessity. In the config Object as parameter given in {@link #update(YAPIONObject)}
+ * you should update the @version variable in the root object to the
+ * new version this converter produced.
+ *
+ * @return the version number
+ */
+ int version();
+
+ /**
+ * This method should update everything needed to go from a lower config
+ * version to a higher. It should update the @version variable
+ * accordingly. Anything else is up the implementation. If anything goes wrong
+ * do not silently exit this method, throw an Exception. The updater Code will
+ * deal with it. Never leave the inputted object in a corrupted state.
+ *
+ * @param config the config object to update
+ */
+ void update(YAPIONObject config);
+
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/configplayer/ConfigCreator.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/configplayer/ConfigCreator.java
new file mode 100644
index 00000000..f2d8a4a8
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/configplayer/ConfigCreator.java
@@ -0,0 +1,61 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2021 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.configplayer;
+
+import de.steamwar.bausystem.features.hotbar.DefaultHotbar;
+import lombok.experimental.UtilityClass;
+import yapion.hierarchy.types.YAPIONObject;
+
+@UtilityClass
+public class ConfigCreator {
+
+ public static final int currentVersion = 1;
+
+ public YAPIONObject createDefaultConfig() {
+ YAPIONObject yapionObject = new YAPIONObject();
+ // This call should never be touched
+ yapionObject.add("@version", currentVersion);
+
+ // Any initialising goes into here
+ yapionObject.add("baugui", defaultBauGui());
+
+ // Default Hotbar Gui
+ yapionObject.add("hotbar", DefaultHotbar.defaultHotbar());
+ return yapionObject;
+ }
+
+ private YAPIONObject defaultBauGui() {
+ YAPIONObject yapionObject = new YAPIONObject();
+
+ // 0: ? | 1: ? | 2: 10 | 3: 3 | 4: 7 | 5: 17 | 6: 15 | 7: ? | 8: ?
+ // 9: 5 | 10: ? | 11: ? | 12: ? | 13: ? | 14: ? | 15: ? | 16: ? | 17: 16
+ // 18: 4 | 19: ? | 20: 19 | 21: 21 | 22: 9 | 23: 0 | 24: 1 | 25: ? | 26: 11
+ // 27: 6 | 28: ? | 29: ? | 30: ? | 31: ? | 32: ? | 33: ? | 34: ? | 35: 18
+ // 36: ? | 37: 23 | 38: 20 | 39: 8 | 40: 22 | 41: 26 | 42: 12 | 43: 14 | 44: ?
+
+ yapionObject.add("10", 2).add("3", 3).add("7", 4).add("17", 5).add("15", 6);
+ yapionObject.add("5", 9).add("4", 18).add("6", 27);
+ yapionObject.add("16", 17).add("11", 26).add("18", 35);
+ yapionObject.add("19", 20).add("21", 21).add("9", 22).add("0", 23).add("1", 24);
+ yapionObject.add("23", 37).add("20", 38).add("8", 39).add("22", 40).add("26", 41).add("12", 42).add("14", 43);
+ yapionObject.add("size", 45);
+ return yapionObject;
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/configplayer/converter/.gitkeep b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/configplayer/converter/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/configplayer/serializer/ConfigurationSerializableSerializer.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/configplayer/serializer/ConfigurationSerializableSerializer.java
new file mode 100644
index 00000000..eecd1ef1
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/configplayer/serializer/ConfigurationSerializableSerializer.java
@@ -0,0 +1,90 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2021 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.configplayer.serializer;
+
+import org.bukkit.configuration.serialization.ConfigurationSerializable;
+import org.bukkit.configuration.serialization.ConfigurationSerialization;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+import yapion.hierarchy.api.groups.YAPIONAnyType;
+import yapion.hierarchy.types.YAPIONObject;
+import yapion.serializing.api.SerializerObject;
+import yapion.serializing.data.DeserializeData;
+import yapion.serializing.data.SerializeData;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+import static yapion.utils.IdentifierUtils.TYPE_IDENTIFIER;
+
+public class ConfigurationSerializableSerializer extends SerializerObject {
+
+ @Override
+ public Class type() {
+ return ConfigurationSerializable.class;
+ }
+
+ @Override
+ public boolean isInterface() {
+ return true;
+ }
+
+ @Override
+ public YAPIONObject serialize(SerializeData serializeData) {
+ YAPIONObject yapionObject = new YAPIONObject();
+ yapionObject.add(TYPE_IDENTIFIER, serializeData.object.getClass().getTypeName());
+ if (serializeData.object instanceof ItemStack) {
+ yapionObject.add(TYPE_IDENTIFIER, ItemStack.class.getTypeName());
+ }
+ if (serializeData.object instanceof ItemMeta) {
+ yapionObject.add(TYPE_IDENTIFIER, ItemMeta.class.getTypeName());
+ }
+ Map serializeDataMap = serializeData.object.serialize();
+ serializeDataMap.forEach((s, o) -> {
+ YAPIONAnyType yapionAnyType = serializeData.serialize(o);
+ if (yapionAnyType instanceof YAPIONObject) {
+ YAPIONObject object = (YAPIONObject) yapionAnyType;
+ if (object.containsKey(TYPE_IDENTIFIER) && object.getPlainValue(TYPE_IDENTIFIER).equals("com.google.common.collect.RegularImmutableList")) {
+ object.put(TYPE_IDENTIFIER, ArrayList.class.getTypeName());
+ }
+ }
+ yapionObject.add(s, yapionAnyType);
+ });
+ return yapionObject;
+ }
+
+ @Override
+ public ConfigurationSerializable deserialize(DeserializeData deserializeData) {
+ Map deserializeDataMap = new HashMap<>();
+ deserializeData.object.forEach((s, yapionAnyType) -> {
+ if (s.equals(TYPE_IDENTIFIER)) {
+ if (yapionAnyType.toString().equals("(org.bukkit.inventory.meta.ItemMeta)")) {
+ deserializeDataMap.put("==", "ItemMeta");
+ } else {
+ deserializeDataMap.put("==", deserializeData.deserialize(yapionAnyType));
+ }
+ return;
+ }
+ deserializeDataMap.put(s, deserializeData.deserialize(yapionAnyType));
+ });
+ return ConfigurationSerialization.deserializeObject(deserializeDataMap);
+ }
+}
\ No newline at end of file
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/attributescopy/AttributeRemoveCommand.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/attributescopy/AttributeRemoveCommand.java
new file mode 100644
index 00000000..689be980
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/attributescopy/AttributeRemoveCommand.java
@@ -0,0 +1,112 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2022 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.attributescopy;
+
+import de.steamwar.bausystem.BauSystem;
+import de.steamwar.command.PreviousArguments;
+import de.steamwar.command.SWCommand;
+import de.steamwar.command.TypeMapper;
+import de.steamwar.linkage.Linked;
+import de.steamwar.linkage.MinVersion;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Linked
+public class AttributeRemoveCommand extends SWCommand {
+
+ public AttributeRemoveCommand() {
+ super("removeattribute", "attributesremove");
+ }
+
+ @Register({"all"})
+ @Register({"*"})
+ public void genericCommand(@Validator Player player) {
+ ItemStack itemStack = player.getInventory().getItemInMainHand();
+ ItemMeta itemMeta = itemStack.getItemMeta();
+ itemMeta.setLore(new ArrayList<>());
+ itemStack.setItemMeta(itemMeta);
+ BauSystem.MESSAGE.send("ATTRIBUTE_REMOVE_ALL", player);
+ }
+
+ @Register(description = "ATTRIBUTE_REMOVE_COMMAND_HELP")
+ public void genericCommand(@Validator Player player, @Mapper("attribute") String attribute) {
+ ItemStack itemStack = player.getInventory().getItemInMainHand();
+ ItemMeta itemMeta = itemStack.getItemMeta();
+ if (itemMeta == null) {
+ BauSystem.MESSAGE.send("ATTRIBUTE_REMOVE_NOT_FOUND", player);
+ return;
+ }
+ List lore = itemMeta.getLore();
+ if (lore == null) {
+ BauSystem.MESSAGE.send("ATTRIBUTE_REMOVE_NOT_FOUND", player);
+ return;
+ }
+ if (lore.isEmpty()) {
+ BauSystem.MESSAGE.send("ATTRIBUTE_REMOVE_NOT_FOUND", player);
+ return;
+ }
+ if (!lore.get(0).equals("§eAttributes§8:")) {
+ BauSystem.MESSAGE.send("ATTRIBUTE_REMOVE_NOT_FOUND", player);
+ return;
+ }
+ lore.removeIf(s -> s.startsWith("§8-§7 " + attribute + "§8:"));
+ if (lore.size() == 1) {
+ itemStack.setItemMeta(null);
+ } else {
+ itemMeta.setLore(lore);
+ itemStack.setItemMeta(itemMeta);
+ }
+ BauSystem.MESSAGE.send("ATTRIBUTE_REMOVE_SINGLE", player, attribute);
+ }
+
+ @Mapper(value = "attribute", local = true)
+ public TypeMapper attribute() {
+ return new TypeMapper() {
+ @Override
+ public Collection tabCompletes(CommandSender commandSender, PreviousArguments previousArguments, String s) {
+ Player player = (Player) commandSender;
+ ItemStack itemStack = player.getInventory().getItemInMainHand();
+ ItemMeta itemMeta = itemStack.getItemMeta();
+ if (itemMeta == null) return null;
+ List lore = itemMeta.getLore();
+ if (lore == null) return null;
+ if (lore.isEmpty()) return null;
+ if (!lore.get(0).equals("§eAttributes§8:")) return null;
+ return lore.stream()
+ .skip(1)
+ .map(s1 -> s1.substring(6))
+ .map(s1 -> s1.substring(0, s1.indexOf("§8:")))
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ public String map(CommandSender commandSender, PreviousArguments previousArguments, String s) {
+ return s;
+ }
+ };
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/attributescopy/AttributeUtils.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/attributescopy/AttributeUtils.java
new file mode 100644
index 00000000..246e4ab9
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/attributescopy/AttributeUtils.java
@@ -0,0 +1,128 @@
+/*
+ * 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.attributescopy;
+
+import lombok.experimental.UtilityClass;
+import org.bukkit.block.data.BlockData;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@UtilityClass
+public class AttributeUtils {
+
+ private Map names = new HashMap<>();
+ private Map, List> getters = new HashMap<>();
+ private Map, Map> setters = new HashMap<>();
+
+ private void generate(BlockData blockData) {
+ Class extends BlockData> clazz = blockData.getClass();
+ if (getters.containsKey(clazz) && setters.containsKey(clazz)) return;
+
+ Map> methods = new HashMap<>();
+ for (Method declaredMethod : clazz.getMethods()) {
+ String s = declaredMethod.getName();
+ if (s.startsWith("get") && declaredMethod.getParameterCount() == 0) {
+ methods.computeIfAbsent(s.substring(3), aClass -> new ArrayList<>()).add(declaredMethod);
+ } else if (s.startsWith("is") && declaredMethod.getParameterCount() == 0) {
+ methods.computeIfAbsent(s.substring(2), aClass -> new ArrayList<>()).add(declaredMethod);
+ } else if (s.startsWith("set") && declaredMethod.getParameterCount() == 1) {
+ methods.computeIfAbsent(s.substring(3), aClass -> new ArrayList<>()).add(declaredMethod);
+ }
+ }
+ for (Map.Entry> entry : methods.entrySet()) {
+ if (entry.getValue().size() != 2) continue;
+ for (Method method : entry.getValue()) {
+ names.put(method, entry.getKey());
+ if (method.getName().startsWith("is") || method.getName().startsWith("get")) {
+ getters.computeIfAbsent(clazz, aClass -> new ArrayList<>()).add(method);
+ } else {
+ setters.computeIfAbsent(clazz, aClass -> new HashMap<>()).put(entry.getKey(), method);
+ }
+ }
+ }
+ }
+
+ public void copy(BlockData blockData, List attributes) {
+ generate(blockData);
+
+ getters.getOrDefault(blockData.getClass(), new ArrayList<>()).forEach(method -> {
+ try {
+ Object invoke = method.invoke(blockData);
+ if (invoke != null) {
+ attributes.add("§8-§7 " + names.get(method) + "§8:§7 " + convert(invoke));
+ }
+ } catch (Exception e) {
+ // ignore
+ }
+ });
+ }
+
+ public void paste(BlockData blockData, List attributes) {
+ generate(blockData);
+
+ for (String attribute : attributes) {
+ String[] split = attribute.split("§8:§7 ");
+ if (split.length != 2) continue;
+ String name = split[0].substring(6);
+ String value = split[1];
+ Method method = setters.getOrDefault(blockData.getClass(), new HashMap<>()).get(name);
+ if (method == null) continue;
+ try {
+ method.invoke(blockData, convert(value, method.getParameterTypes()[0]));
+ } catch (Exception e) {
+ // ignore
+ }
+ }
+ }
+
+ private String convert(Object o) {
+ if (o.getClass().isEnum()) {
+ return ((Enum>) o).name();
+ } else {
+ return o.toString();
+ }
+ }
+
+ private Object convert(String s, Class> type) {
+ if (type.isEnum()) {
+ return Enum.valueOf((Class extends Enum>) type, s);
+ } else if (type == int.class || type == Integer.class) {
+ return Integer.parseInt(s);
+ } else if (type == double.class || type == Double.class) {
+ return Double.parseDouble(s);
+ } else if (type == float.class || type == Float.class) {
+ return Float.parseFloat(s);
+ } else if (type == long.class || type == Long.class) {
+ return Long.parseLong(s);
+ } else if (type == short.class || type == Short.class) {
+ return Short.parseShort(s);
+ } else if (type == byte.class || type == Byte.class) {
+ return Byte.parseByte(s);
+ } else if (type == boolean.class || type == Boolean.class) {
+ return Boolean.parseBoolean(s);
+ } else {
+ return s;
+ }
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/attributescopy/AttributesCopyCommand.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/attributescopy/AttributesCopyCommand.java
new file mode 100644
index 00000000..808ca339
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/attributescopy/AttributesCopyCommand.java
@@ -0,0 +1,88 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2022 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.attributescopy;
+
+import de.steamwar.bausystem.BauSystem;
+import de.steamwar.command.SWCommand;
+import de.steamwar.linkage.Linked;
+import org.bukkit.FluidCollisionMode;
+import org.bukkit.Material;
+import org.bukkit.block.Block;
+import org.bukkit.block.data.BlockData;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Linked
+public class AttributesCopyCommand extends SWCommand {
+
+ public AttributesCopyCommand() {
+ super("copyattributes", "attributescopy", "ac");
+ }
+
+ @Register
+ public void genericCommand(@Validator Player player) {
+ Block block = player.getTargetBlockExact(8, FluidCollisionMode.ALWAYS);
+ if (block == null) return;
+ ItemStack mainHand = player.getInventory().getItemInMainHand();
+
+ if (!(block.getType().isItem() && block.getType() == mainHand.getType() || isSame(block, mainHand))) {
+ BauSystem.MESSAGE.send("ATTRIBUTES_CANT_COPY", player);
+ return;
+ }
+
+ BlockData blockData = block.getBlockData();
+ List attributesToCopy = new ArrayList<>();
+ if (block.getType() != mainHand.getType()) {
+ attributesToCopy.add("§8-§7 Material§8:§7 " + block.getType().name());
+ }
+ AttributeUtils.copy(blockData, attributesToCopy);
+ if (attributesToCopy.isEmpty()) {
+ BauSystem.MESSAGE.send("ATTRIBUTES_NO_COPY", player);
+ return;
+ }
+ ItemMeta itemMeta = mainHand.getItemMeta();
+ List lore = new ArrayList<>(attributesToCopy);
+ lore.add(0, "§eAttributes§8:");
+ itemMeta.setLore(lore);
+ mainHand.setItemMeta(itemMeta);
+ player.getInventory().setItemInMainHand(mainHand);
+ BauSystem.MESSAGE.send("ATTRIBUTES_COPIED", player);
+ }
+
+ private boolean isSame(Block block, ItemStack itemStack) {
+ if (itemStack.getType() == Material.REDSTONE && block.getType() == Material.REDSTONE_WIRE) return true;
+ if (itemStack.getType() == Material.PLAYER_HEAD && block.getType() == Material.PLAYER_WALL_HEAD) return true;
+ if (itemStack.getType() == Material.ZOMBIE_HEAD && block.getType() == Material.ZOMBIE_WALL_HEAD) return true;
+ if (itemStack.getType() == Material.CREEPER_HEAD && block.getType() == Material.CREEPER_WALL_HEAD) return true;
+ if (itemStack.getType() == Material.DRAGON_HEAD && block.getType() == Material.DRAGON_WALL_HEAD) return true;
+ if (itemStack.getType() == Material.SKELETON_SKULL && block.getType() == Material.SKELETON_WALL_SKULL) return true;
+ if (itemStack.getType() == Material.WITHER_SKELETON_SKULL && block.getType() == Material.WITHER_SKELETON_WALL_SKULL) return true;
+ if (itemStack.getType() == Material.TORCH && block.getType() == Material.WALL_TORCH) return true;
+ if (itemStack.getType() == Material.SOUL_TORCH && block.getType() == Material.SOUL_WALL_TORCH) return true;
+ if (itemStack.getType() == Material.REDSTONE_TORCH && block.getType() == Material.REDSTONE_WALL_TORCH) return true;
+ if (itemStack.getType() == Material.WHEAT_SEEDS && block.getType() == Material.WHEAT) return true;
+ if (itemStack.getType().name().contains("_BANNER") && block.getType().name().contains("_WALL_BANNER")) return true;
+ return false;
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/attributescopy/AttributesPlaceListener.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/attributescopy/AttributesPlaceListener.java
new file mode 100644
index 00000000..965d8bad
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/attributescopy/AttributesPlaceListener.java
@@ -0,0 +1,88 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2022 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.attributescopy;
+
+import de.steamwar.bausystem.BauSystem;
+import de.steamwar.bausystem.Permission;
+import de.steamwar.linkage.Linked;
+import org.bukkit.Bukkit;
+import org.bukkit.Material;
+import org.bukkit.OfflinePlayer;
+import org.bukkit.block.Block;
+import org.bukkit.block.Skull;
+import org.bukkit.block.data.BlockData;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.block.BlockPlaceEvent;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+
+import java.util.List;
+
+@Linked
+public class AttributesPlaceListener implements Listener {
+
+ @EventHandler
+ public void onBlockPlace(BlockPlaceEvent event) {
+ if(!Permission.BUILD.hasPermission(event.getPlayer())) return;
+ ItemStack itemStack = event.getItemInHand();
+ ItemMeta itemMeta = itemStack.getItemMeta();
+ if (itemMeta == null) return;
+ List strings = itemMeta.getLore();
+ if (strings == null) return;
+ if (strings.isEmpty()) return;
+ if (!strings.get(0).equals("§eAttributes§8:")) return;
+ Material type = event.getBlock().getType();
+ OfflinePlayer offlinePlayer = null;
+ if (event.getBlock().getState() instanceof Skull) {
+ Skull skull = (Skull) event.getBlock().getState();
+ offlinePlayer = skull.getOwningPlayer();
+ }
+ OfflinePlayer finalPlayerProfile = offlinePlayer;
+ event.setCancelled(true);
+ Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
+ Material material = strings.stream()
+ .filter(s -> s.startsWith("§8-§7 Material§8:§7 "))
+ .map(s -> s.replace("§8-§7 Material§8:§7 ", ""))
+ .map(String::toUpperCase)
+ .map(s -> {
+ try {
+ return Material.valueOf(s);
+ } catch (Exception e) {
+ return null;
+ }
+ })
+ .findFirst()
+ .orElse(type);
+ event.getBlock().setType(material, false);
+ Block block = event.getBlock();
+ BlockData blockData = block.getBlockData();
+ Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
+ if (block.getState() instanceof Skull && finalPlayerProfile != null) {
+ Skull skull = (Skull) block.getState();
+ skull.setOwningPlayer(finalPlayerProfile);
+ skull.update(true, false);
+ }
+ }, 1);
+ AttributeUtils.paste(blockData, strings);
+ block.setBlockData(blockData, false);
+ }, 1);
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/autostart/AutoStartCommand.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/autostart/AutoStartCommand.java
new file mode 100644
index 00000000..3401267e
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/autostart/AutoStartCommand.java
@@ -0,0 +1,38 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2021 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.autostart;
+
+import de.steamwar.bausystem.SWUtils;
+import de.steamwar.command.SWCommand;
+import de.steamwar.linkage.Linked;
+import org.bukkit.entity.Player;
+
+@Linked
+public class AutoStartCommand extends SWCommand {
+
+ public AutoStartCommand() {
+ super("timer", "autostarttimer", "at", "autostart");
+ }
+
+ @Register(description = "AUTOSTART_COMMAND_HELP")
+ public void genericCommand(@Validator Player p) {
+ SWUtils.giveItemToPlayer(p, AutostartListener.getWandItem(p));
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/autostart/AutoStartGuiItem.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/autostart/AutoStartGuiItem.java
new file mode 100644
index 00000000..f622d70b
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/autostart/AutoStartGuiItem.java
@@ -0,0 +1,55 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2021 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.autostart;
+
+import de.steamwar.bausystem.Permission;
+import de.steamwar.bausystem.linkage.specific.BauGuiItem;
+import de.steamwar.linkage.Linked;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.event.inventory.ClickType;
+import org.bukkit.inventory.ItemStack;
+
+@Linked
+public class AutoStartGuiItem extends BauGuiItem {
+
+ public AutoStartGuiItem() {
+ super(24);
+ }
+
+ @Override
+ public ItemStack getItem(Player player) {
+ ItemStack itemStack = AutostartListener.getWandItem(player);
+ itemStack.setType(Material.FIREWORK_STAR);
+ return itemStack;
+ }
+
+ @Override
+ public boolean click(ClickType click, Player p) {
+ p.closeInventory();
+ p.performCommand("timer");
+ return false;
+ }
+
+ @Override
+ public Permission permission() {
+ return Permission.BUILD;
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/autostart/AutostartListener.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/autostart/AutostartListener.java
new file mode 100644
index 00000000..28126ec5
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/autostart/AutostartListener.java
@@ -0,0 +1,150 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2021 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.autostart;
+
+import de.steamwar.bausystem.BauSystem;
+import de.steamwar.bausystem.Permission;
+import de.steamwar.bausystem.features.tpslimit.TPSUtils;
+import de.steamwar.bausystem.region.Region;
+import de.steamwar.bausystem.region.RegionUtils;
+import de.steamwar.bausystem.region.utils.RegionExtensionType;
+import de.steamwar.bausystem.region.utils.RegionType;
+import de.steamwar.bausystem.utils.ItemUtils;
+import de.steamwar.inventory.SWItem;
+import de.steamwar.linkage.Linked;
+import lombok.Getter;
+import org.bukkit.Bukkit;
+import org.bukkit.Material;
+import org.bukkit.block.data.type.Chest;
+import org.bukkit.configuration.file.FileConfiguration;
+import org.bukkit.configuration.file.YamlConfiguration;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.entity.EntityExplodeEvent;
+import org.bukkit.event.inventory.InventoryCloseEvent;
+import org.bukkit.event.player.PlayerInteractEvent;
+import org.bukkit.inventory.ItemStack;
+
+import java.io.File;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+@Linked
+public class AutostartListener implements Listener {
+
+ @Getter
+ public static AutostartListener instance;
+
+ {
+ instance = this;
+ }
+
+ public static ItemStack getWandItem(Player player) {
+ ItemStack itemStack = new SWItem(Material.FIREWORK_STAR, BauSystem.MESSAGE.parse("AUTOSTART_ITEM_NAME", player), Arrays.asList(BauSystem.MESSAGE.parse("AUTOSTART_ITEM_LORE", player)), false, null).getItemStack();
+ ItemUtils.setItem(itemStack, "autostart");
+ return itemStack;
+ }
+
+ @Getter
+ private Map regionStartTime = new HashMap<>();
+
+ @EventHandler
+ public void onPlayerInteract(PlayerInteractEvent event) {
+ if(!Permission.BUILD.hasPermission(event.getPlayer())) return;
+ if (!ItemUtils.isItem(event.getItem(), "autostart")) {
+ return;
+ }
+ if (event.getClickedBlock() == null) {
+ return;
+ }
+ if (event.getClickedBlock().getBlockData() instanceof Chest) {
+ return;
+ }
+ if (event.getClickedBlock().getType() == Material.BEDROCK) {
+ event.getClickedBlock().setType(Material.SLIME_BLOCK);
+ Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
+ event.getClickedBlock().setType(Material.BEDROCK, false);
+ }, 1);
+ }
+ activate(event.getPlayer());
+ }
+
+ @EventHandler
+ public void onInventoryClose(InventoryCloseEvent event) {
+ if (!(event.getPlayer() instanceof Player)) {
+ return;
+ }
+ if(!Permission.BUILD.hasPermission((Player) event.getPlayer())) return;
+ if (!ItemUtils.isItem(event.getPlayer().getInventory().getItemInMainHand(), "autostart")) {
+ return;
+ }
+ if (event.getInventory().getLocation() == null) {
+ return;
+ }
+ if (event.getInventory().getLocation().getBlock().getBlockData() instanceof Chest) {
+ activate((Player) event.getPlayer());
+ }
+ }
+
+ public void activate(Player player) {
+ Region region = Region.getRegion(player.getLocation());
+ if (region.isGlobal()) {
+ BauSystem.MESSAGE.send("AUTOSTART_MESSAGE_NO_REGION", player);
+ return;
+ }
+ if (!region.hasType(RegionType.TESTBLOCK)) {
+ BauSystem.MESSAGE.send("AUTOSTART_MESSAGE_NO_REGION", player);
+ return;
+ }
+ if (regionStartTime.containsKey(region)) {
+ BauSystem.MESSAGE.send("AUTOSTART_MESSAGE_RESET", player);
+ } else {
+ BauSystem.MESSAGE.send("AUTOSTART_MESSAGE_START", player);
+ }
+ regionStartTime.put(region, TPSUtils.currentRealTick.get());
+ }
+
+ @EventHandler
+ public void onEntityExplode(EntityExplodeEvent event) {
+ if (regionStartTime.isEmpty()) {
+ return;
+ }
+
+ event.blockList().forEach(block -> {
+ Region region = Region.getRegion(block.getLocation());
+ if (!regionStartTime.containsKey(region)) return;
+ if (!region.hasType(RegionType.TESTBLOCK)) return;
+ if (!region.inRegion(block.getLocation(), RegionType.TESTBLOCK, RegionExtensionType.EXTENSION)) return;
+ long tickDiff = TPSUtils.currentRealTick.get() - regionStartTime.remove(region);
+ long preFightDurationInSeconds = getPreFightDurationInSeconds(region);
+ RegionUtils.message(region, "AUTOSTART_MESSAGE_RESULT1", tickDiff);
+ RegionUtils.message(region, "AUTOSTART_MESSAGE_RESULT2", preFightDurationInSeconds, ((preFightDurationInSeconds * 20) - tickDiff));
+ RegionUtils.message(region, "AUTOSTART_MESSAGE_RESULT3");
+ });
+ }
+
+ private int getPreFightDurationInSeconds(Region region) {
+ File file = region.gameModeConfig();
+ if (file == null) return 30;
+ FileConfiguration config = YamlConfiguration.loadConfiguration(file);
+ return config.getInt("Times.PreFightDuration", 30);
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/backup/BackupCommand.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/backup/BackupCommand.java
new file mode 100644
index 00000000..b7911275
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/backup/BackupCommand.java
@@ -0,0 +1,154 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2021 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.backup;
+
+import com.sk89q.worldedit.EditSession;
+import de.steamwar.bausystem.BauSystem;
+import de.steamwar.bausystem.Permission;
+import de.steamwar.bausystem.region.Color;
+import de.steamwar.bausystem.region.Region;
+import de.steamwar.bausystem.region.flags.Flag;
+import de.steamwar.bausystem.region.flags.flagvalues.ColorMode;
+import de.steamwar.bausystem.region.tags.Tag;
+import de.steamwar.bausystem.utils.PasteBuilder;
+import de.steamwar.command.SWCommand;
+import de.steamwar.command.SWCommandUtils;
+import de.steamwar.command.TypeMapper;
+import de.steamwar.command.TypeValidator;
+import de.steamwar.inventory.SWItem;
+import de.steamwar.inventory.SWListInv;
+import de.steamwar.linkage.Linked;
+import net.md_5.bungee.api.chat.ClickEvent;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Linked
+public class BackupCommand extends SWCommand {
+
+ public BackupCommand() {
+ super("backup", "bu");
+ }
+
+ static boolean checkGlobalRegion(Region region, Player p) {
+ if (region.isGlobal()) {
+ BauSystem.MESSAGE.send("BACKUP_REGION_NO_REGION", p);
+ return true;
+ }
+ return false;
+ }
+
+ @Register(value = "create", description = "BACKUP_HELP_CREATE")
+ public void backupCreate(@Validator("owner") Player p) {
+ Region region = Region.getRegion(p.getLocation());
+ if (checkGlobalRegion(region, p)) {
+ return;
+ }
+ if (!region.get(Tag.CHANGED)) {
+ BauSystem.MESSAGE.send("BACKUP_CREATE_NO_CHANGE", p);
+ return;
+ }
+ if (region.backup()) {
+ BauSystem.MESSAGE.send("BACKUP_CREATE_SUCCESS", p);
+ } else {
+ BauSystem.MESSAGE.send("BACKUP_CREATE_FAILURE", p);
+ }
+ }
+
+ @Register(value = "load", description = "BACKUP_HELP_LOAD")
+ public void backupLoad(@Validator("owner") Player p, @Mapper("backupName") String backupName) {
+ Region region = Region.getRegion(p.getLocation());
+ if (checkGlobalRegion(region, p)) {
+ return;
+ }
+
+ File backupFile = region.getBackupFile(backupName.replace('_', ' '));
+ if (backupFile == null) {
+ BauSystem.MESSAGE.send("BACKUP_LOAD_FAILURE", p);
+ return;
+ }
+ EditSession editSession = new PasteBuilder(new PasteBuilder.FileProvider(backupFile))
+ .pastePoint(region.getMinPoint().add(region.getPrototype().getSizeX() / 2, 0, region.getPrototype().getSizeZ() / 2))
+ .minPoint(region.getMinPoint())
+ .maxPoint(region.getMaxPoint())
+ .waterLevel(region.getWaterLevel())
+ .run();
+ region.remember(editSession);
+ BauSystem.MESSAGE.send("BACKUP_LOAD", p);
+ }
+
+ @Register(value = "list", description = "BACKUP_HELP_LIST")
+ public void backupList(Player p) {
+ Region region = Region.getRegion(p.getLocation());
+ if (checkGlobalRegion(region, p)) {
+ return;
+ }
+ List backups = listBackup(p);
+ BauSystem.MESSAGE.send("BACKUP_LIST_HEAD", p, backups.size());
+ backups.forEach(s -> {
+ BauSystem.MESSAGE.send("BACKUP_LIST_ENTRY", p, "/backup load " + s, new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/backup load " + s), s);
+ });
+ }
+
+ @Register(value = "gui", description = "BACKUP_HELP_GUI")
+ public void backupGui(Player p) {
+ Region region = Region.getRegion(p.getLocation());
+ if (checkGlobalRegion(region, p)) {
+ return;
+ }
+ List backups = listBackup(p);
+ List> swListEntries = new ArrayList<>();
+ List lore = Arrays.asList(BauSystem.MESSAGE.parse("BACKUP_LORE", p));
+ for (int i = 0; i < backups.size(); i++) {
+ String s = backups.get(i);
+ SWItem swItem = new SWItem(Material.BRICK, BauSystem.MESSAGE.parse("BACKUP_ITEM_NAME", p, s), lore, false, clickType -> {});
+ swItem.getItemStack().setAmount(i + 1);
+ swListEntries.add(new SWListInv.SWListEntry<>(swItem, s));
+ }
+ SWListInv swListInv = new SWListInv<>(p, BauSystem.MESSAGE.parse("BACKUP_INV_NAME", p), swListEntries, (clickType, s) -> {
+ p.getOpenInventory().close();
+ p.performCommand("backup load " + s);
+ });
+ swListInv.open();
+ }
+
+ @Mapper(value = "backupName", local = true)
+ public TypeMapper backupMapper() {
+ return SWCommandUtils.createMapper(s -> s, (commandSender, s) -> listBackup((Player) commandSender));
+ }
+
+ private List listBackup(Player p) {
+ Region region = Region.getRegion(p.getLocation());
+ if (checkGlobalRegion(region, p)) {
+ return Collections.emptyList();
+ }
+ try {
+ return region.listBackup().stream().map(s -> s.substring(0, s.length() - 6).replace(' ', '_')).collect(Collectors.toList());
+ } catch (NullPointerException e) {
+ return Collections.emptyList();
+ }
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/bau/BauInfoBauGuiItem.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/bau/BauInfoBauGuiItem.java
new file mode 100644
index 00000000..d8ba9171
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/bau/BauInfoBauGuiItem.java
@@ -0,0 +1,83 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2021 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.bau;
+
+import de.steamwar.bausystem.BauSystem;
+import de.steamwar.bausystem.Permission;
+import de.steamwar.bausystem.config.BauServer;
+import de.steamwar.bausystem.linkage.specific.BauGuiItem;
+import de.steamwar.bausystem.region.Region;
+import de.steamwar.bausystem.region.flags.Flag;
+import de.steamwar.core.Core;
+import de.steamwar.inventory.SWItem;
+import de.steamwar.linkage.Linked;
+import de.steamwar.sql.SteamwarUser;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.event.inventory.ClickType;
+import org.bukkit.inventory.ItemStack;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Linked
+public class BauInfoBauGuiItem extends BauGuiItem {
+
+ public BauInfoBauGuiItem() {
+ super(7);
+ }
+
+ @Override
+ public ItemStack getItem(Player player) {
+ SWItem itemStack;
+ if (!player.getName().endsWith("⍇")) {
+ itemStack = SWItem.getPlayerSkull(SteamwarUser.get(BauServer.getInstance().getOwner()).getUserName());
+ } else {
+ itemStack = new SWItem(Material.PLAYER_HEAD, "");
+ }
+ itemStack.setName(BauSystem.MESSAGE.parse("BAU_INFO_ITEM_NAME", player));
+ Region region = Region.getRegion(player.getLocation());
+ List stringList = new ArrayList<>();
+ for (Flag flag : Flag.getFlags()) {
+ if (flag == Flag.PROTECT && region.getFloorLevel() == 0) {
+ continue;
+ }
+ if (flag == Flag.ITEMS && Core.getVersion() < 19) {
+ continue;
+ }
+ Flag.Value> value = region.get(flag);
+ if (value != null) {
+ stringList.add(BauSystem.MESSAGE.parse("BAU_INFO_ITEM_LORE_" + flag.name(), player, BauSystem.MESSAGE.parse(value.getChatValue(), player)));
+ }
+ }
+ itemStack.setLore(stringList);
+ return itemStack.getItemStack();
+ }
+
+ @Override
+ public boolean click(ClickType click, Player p) {
+ return false;
+ }
+
+ @Override
+ public Permission permission() {
+ return Permission.MEMBER;
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/bau/ForceSpectatorCommand.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/bau/ForceSpectatorCommand.java
new file mode 100644
index 00000000..7bef91df
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/bau/ForceSpectatorCommand.java
@@ -0,0 +1,73 @@
+/*
+ * 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.bau;
+
+import de.steamwar.bausystem.BauSystem;
+import de.steamwar.bausystem.Permission;
+import de.steamwar.command.PreviousArguments;
+import de.steamwar.command.SWCommand;
+import de.steamwar.command.TypeMapper;
+import de.steamwar.linkage.Linked;
+import de.steamwar.techhider.TechHider;
+import org.bukkit.Bukkit;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+
+import java.util.Collection;
+import java.util.stream.Collectors;
+
+@Linked
+public class ForceSpectatorCommand extends SWCommand {
+
+ public ForceSpectatorCommand() {
+ super("forcespectator");
+ }
+
+ @Register
+ public void forceSpectator(@Validator("supervisor") Player player, @Mapper("builder") Player other) {
+ Permission.forceOnlySpectator(other);
+ }
+
+ @Mapper("builder")
+ public TypeMapper spectatorMapper() {
+ return new TypeMapper<>() {
+ @Override
+ public Player map(CommandSender commandSender, String[] previousArguments, String s) {
+ Player player = Bukkit.getPlayer(s);
+ if (player == null) {
+ return null;
+ }
+ if (Permission.BUILD.hasPermission(player) && !Permission.SUPERVISOR.hasPermission(player)) {
+ return player;
+ }
+ return null;
+ }
+
+ @Override
+ public Collection tabCompletes(CommandSender sender, PreviousArguments previousArguments, String s) {
+ return Bukkit.getOnlinePlayers().stream()
+ .filter(Permission.BUILD::hasPermission)
+ .filter(player -> !Permission.SUPERVISOR.hasPermission(player))
+ .map(Player::getName)
+ .collect(Collectors.toList());
+ }
+ };
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/bau/InfoCommand.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/bau/InfoCommand.java
new file mode 100644
index 00000000..2d638888
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/bau/InfoCommand.java
@@ -0,0 +1,86 @@
+package de.steamwar.bausystem.features.bau;
+
+import de.steamwar.bausystem.BauSystem;
+import de.steamwar.bausystem.Permission;
+import de.steamwar.bausystem.config.BauServer;
+import de.steamwar.bausystem.region.Region;
+import de.steamwar.bausystem.region.flags.Flag;
+import de.steamwar.command.SWCommand;
+import de.steamwar.core.TPSWatcher;
+import de.steamwar.linkage.Linked;
+import de.steamwar.linkage.LinkedInstance;
+import de.steamwar.sql.BauweltMember;
+import de.steamwar.sql.SteamwarUser;
+import org.bukkit.entity.Player;
+
+import java.util.*;
+
+@Linked
+public class InfoCommand extends SWCommand {
+
+ @LinkedInstance
+ public BauServer bauServer;
+
+ public InfoCommand() {
+ super("bauinfo");
+ }
+
+ @Register(description = "BAU_INFO_COMMAND_HELP")
+ public void genericCommand(Player p) {
+ BauSystem.MESSAGE.send("BAU_INFO_COMMAND_OWNER", p, SteamwarUser.get(bauServer.getOwnerID()).getUserName());
+ Region region = Region.getRegion(p.getLocation());
+ for (Flag flag : Flag.getFlags()) {
+ if (flag == Flag.PROTECT && region.getFloorLevel() == 0) {
+ continue;
+ }
+ Flag.Value> value = region.get(flag);
+ if (value != null) {
+ BauSystem.MESSAGE.send("BAU_INFO_COMMAND_FLAG", p, BauSystem.MESSAGE.parse(flag.getChatValue(), p), BauSystem.MESSAGE.parse(value.getChatValue(), p));
+ }
+ }
+
+ if (Permission.BUILD.hasPermission(p)) {
+ List members = BauweltMember.getMembers(bauServer.getOwnerID());
+ Map> memberByPermission = new HashMap<>();
+ members.forEach(member -> {
+ if (Permission.SUPERVISOR.hasPermission(member)) {
+ memberByPermission.computeIfAbsent(Permission.SUPERVISOR, __ -> new ArrayList<>()).add(member);
+ } else if (Permission.BUILD.hasPermission(member)) {
+ memberByPermission.computeIfAbsent(Permission.BUILD, __ -> new ArrayList<>()).add(member);
+ } else {
+ memberByPermission.computeIfAbsent(Permission.MEMBER, __ -> new ArrayList<>()).add(member);
+ }
+ });
+
+ List supervisor = memberByPermission.getOrDefault(Permission.SUPERVISOR, Collections.emptyList());
+ BauSystem.MESSAGE.send("BAU_INFO_COMMAND_MEMBER", p, "§eSupervisor", supervisor.size(), supervisor.isEmpty() ? "§8" : joining(supervisor));
+
+ List builder = memberByPermission.getOrDefault(Permission.BUILD, Collections.emptyList());
+ BauSystem.MESSAGE.send("BAU_INFO_COMMAND_MEMBER", p, "§6Builder", builder.size(), builder.isEmpty() ? "§8" : joining(builder));
+
+ List spectator = memberByPermission.getOrDefault(Permission.MEMBER, Collections.emptyList());
+ BauSystem.MESSAGE.send("BAU_INFO_COMMAND_MEMBER", p, "§7Spectator", spectator.size(), spectator.isEmpty() ? "§8" : joining(spectator));
+ }
+
+ StringBuilder tpsMessage = new StringBuilder();
+ tpsMessage.append(BauSystem.MESSAGE.parsePrefixed("BAU_INFO_COMMAND_TPS", p));
+ tpsMessage.append(" ").append(TPSWatcher.getTPS(TPSWatcher.TPSType.ONE_SECOND));
+ tpsMessage.append(" ").append(TPSWatcher.getTPS(TPSWatcher.TPSType.TEN_SECONDS));
+ tpsMessage.append(" ").append(TPSWatcher.getTPS(TPSWatcher.TPSType.ONE_MINUTE));
+ tpsMessage.append(" ").append(TPSWatcher.getTPS(TPSWatcher.TPSType.FIVE_MINUTES));
+ tpsMessage.append(" ").append(TPSWatcher.getTPS(TPSWatcher.TPSType.TEN_MINUTES));
+ p.sendMessage(tpsMessage.toString());
+ }
+
+ private String joining(List bauweltMembers) {
+ StringBuilder st = new StringBuilder();
+ for (int i = 0; i < bauweltMembers.size(); i++) {
+ if (i != 0) {
+ st.append("§8, ");
+ }
+ st.append("§7");
+ st.append(SteamwarUser.get(bauweltMembers.get(i).getMemberID()).getUserName());
+ }
+ return st.toString();
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/cannon/CannonDetector.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/cannon/CannonDetector.java
new file mode 100644
index 00000000..3ab4fe70
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/cannon/CannonDetector.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.features.cannon;
+
+import de.steamwar.bausystem.BauSystem;
+import de.steamwar.bausystem.features.cannon.depth.Depth;
+import de.steamwar.bausystem.features.cannon.depth.DepthManager;
+import de.steamwar.bausystem.region.Region;
+import de.steamwar.bausystem.region.utils.RegionType;
+import de.steamwar.linkage.Linked;
+import org.bukkit.Bukkit;
+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.util.Vector;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+@Linked
+public class CannonDetector implements Listener {
+
+ private Map velocities = new HashMap<>();
+ private Map> propulsionOfProjectile = new HashMap<>();
+
+ @EventHandler(priority = EventPriority.LOW)
+ public void onEntityExplode(EntityExplodeEvent event) {
+ if (!(event.getEntity() instanceof TNTPrimed)) {
+ return;
+ }
+
+ TNTPrimed tnt = (TNTPrimed) event.getEntity();
+ propulsionOfProjectile.remove(tnt);
+
+ DepthManager.update(tnt, event.blockList());
+
+ List tnts = Bukkit.getWorlds().get(0).getEntitiesByClass(TNTPrimed.class)
+ .stream()
+ .filter(entity -> entity != tnt)
+ .filter(entity -> entity.getFuseTicks() > 1)
+ .filter(entity -> entity.getLocation().distance(event.getLocation()) <= 8)
+ .collect(Collectors.toList());
+
+ if (tnts.isEmpty()) {
+ return;
+ }
+
+ boolean isEmpty = velocities.isEmpty();
+ tnts.forEach(tntPrimed -> {
+ velocities.put(tntPrimed, tntPrimed.getVelocity().clone());
+ propulsionOfProjectile.computeIfAbsent(tntPrimed, __ -> new HashSet<>()).add(tnt.getUniqueId());
+ });
+
+ if (!isEmpty) {
+ return;
+ }
+
+ BauSystem.runTaskLater(BauSystem.getInstance(), () -> {
+ Map> grouped = new HashMap<>();
+ velocities.forEach((tntPrimed, vector) -> {
+ boolean xBiggest = Math.abs(vector.getX()) > Math.abs(vector.getZ());
+ boolean zBiggest = Math.abs(vector.getZ()) > Math.abs(vector.getX());
+ Vector vec = new Vector(xBiggest ? Math.signum(vector.getX()) : 0, Math.round(vector.getY() * 100), zBiggest ? Math.signum(vector.getZ()) : 0);
+ grouped.computeIfAbsent(new CannonKey(propulsionOfProjectile.get(tntPrimed), vec), ignored -> new ArrayList<>()).add(tntPrimed);
+ });
+ grouped.forEach((cannonKey, tntPrimeds) -> {
+ if (tntPrimeds.size() <= 5) return;
+ Region region = Region.getRegion(tntPrimeds.get(0).getLocation());
+ if (region.isGlobal()) return;
+ if (!region.hasType(RegionType.TESTBLOCK)) return;
+ Depth depth = new Depth(region);
+ DepthManager.init(tntPrimeds, depth);
+ });
+ velocities.clear();
+ }, 1);
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/cannon/CannonKey.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/cannon/CannonKey.java
new file mode 100644
index 00000000..c260e8a9
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/cannon/CannonKey.java
@@ -0,0 +1,37 @@
+/*
+ * 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.cannon;
+
+import lombok.AllArgsConstructor;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import org.bukkit.entity.TNTPrimed;
+import org.bukkit.util.Vector;
+
+import java.util.Set;
+import java.util.UUID;
+
+@AllArgsConstructor
+@EqualsAndHashCode
+@Getter
+public final class CannonKey {
+ private Set propulsions;
+ private Vector velocityVector;
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/cannon/depth/Depth.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/cannon/depth/Depth.java
new file mode 100644
index 00000000..a13d6250
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/cannon/depth/Depth.java
@@ -0,0 +1,109 @@
+/*
+ * 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.cannon.depth;
+
+import de.steamwar.bausystem.BauSystem;
+import de.steamwar.bausystem.region.Region;
+import de.steamwar.bausystem.region.RegionUtils;
+import de.steamwar.bausystem.region.utils.RegionExtensionType;
+import de.steamwar.bausystem.region.utils.RegionType;
+import net.md_5.bungee.api.chat.BaseComponent;
+import net.md_5.bungee.api.chat.HoverEvent;
+import net.md_5.bungee.api.chat.TextComponent;
+import org.bukkit.block.Block;
+import org.bukkit.entity.Player;
+import org.bukkit.util.Vector;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+public class Depth {
+
+ private Region region;
+ private Vector minVector = null;
+ private Vector maxVector = null;
+ private int tntCount = 0;
+
+ public Depth(Region region) {
+ this.region = region;
+ }
+
+ public void update(List blocks) {
+ List blocksList = blocks.stream()
+ .filter(block -> region.inRegion(block.getLocation(), RegionType.TESTBLOCK, RegionExtensionType.EXTENSION))
+ .collect(Collectors.toList());
+ tntCount++;
+ for (Block block : blocksList) {
+ internalUpdate(block);
+ }
+ }
+
+ public void finish() {
+ if (maxVector == null || minVector == null) return;
+ Vector dimensions = maxVector.subtract(minVector);
+ dimensions.setX(Math.abs(dimensions.getX()));
+ dimensions.setY(Math.abs(dimensions.getY()));
+ dimensions.setZ(Math.abs(dimensions.getZ()));
+
+ RegionUtils.message(region, player -> {
+ player.spigot().sendMessage(getMessage(player, dimensions.getBlockX() + 1, dimensions.getBlockY() + 1, dimensions.getBlockZ() + 1, tntCount));
+ });
+ }
+
+ private void internalUpdate(Block block) {
+ if (minVector == null) {
+ minVector = block.getLocation().toVector();
+ }
+ minVector.setX(Math.min(minVector.getX(), block.getX()));
+ minVector.setY(Math.min(minVector.getY(), block.getY()));
+ minVector.setZ(Math.min(minVector.getZ(), block.getZ()));
+
+ if (maxVector == null) {
+ maxVector = block.getLocation().toVector();
+ }
+ maxVector.setX(Math.max(maxVector.getX(), block.getX()));
+ maxVector.setY(Math.max(maxVector.getY(), block.getY()));
+ maxVector.setZ(Math.max(maxVector.getZ(), block.getZ()));
+ }
+
+ private static BaseComponent[] getMessage(Player player, int x, int y, int z, int tntCount) {
+ final Set dimensions = new HashSet<>();
+ dimensions.add(x);
+ dimensions.add(y);
+ dimensions.add(z);
+
+ int max = getMax(dimensions);
+
+ TextComponent headerComponent = new TextComponent(BauSystem.MESSAGE.parse("DEPTH_COUNTER_MESSAGE", player));
+
+ TextComponent depthComponent = new TextComponent(BauSystem.MESSAGE.parse("DEPTH_COUNTER_COUNT", player, x == max ? "§e" : "§7", x, y == max ? "§e" : "§7", y, z == max ? "§e" : "§7", z));
+ depthComponent.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new TextComponent[]{new TextComponent(BauSystem.MESSAGE.parse("DEPTH_COUNTER_HOVER", player))}));
+
+ TextComponent tntComponent = new TextComponent(BauSystem.MESSAGE.parse("DEPTH_COUNTER_TNT", player, tntCount));
+
+ return new BaseComponent[]{headerComponent, depthComponent, tntComponent};
+ }
+
+ private static int getMax(Set values) {
+ return values.stream().max(Integer::compare).orElse(0);
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/cannon/depth/DepthManager.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/cannon/depth/DepthManager.java
new file mode 100644
index 00000000..21789d7b
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/cannon/depth/DepthManager.java
@@ -0,0 +1,48 @@
+/*
+ * 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.cannon.depth;
+
+import lombok.experimental.UtilityClass;
+import org.bukkit.block.Block;
+import org.bukkit.entity.TNTPrimed;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@UtilityClass
+public class DepthManager {
+
+ private Map depths = new HashMap<>();
+
+ public void init(List list, Depth depth) {
+ for (TNTPrimed tnt : list) {
+ depths.putIfAbsent(tnt, depth);
+ }
+ }
+
+ public void update(TNTPrimed tnt, List blocks) {
+ Depth depth = depths.remove(tnt);
+ if (depth == null) return;
+ depth.update(blocks);
+ if (depths.containsValue(depth)) return;
+ depth.finish();
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/countingwand/Countingwand.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/countingwand/Countingwand.java
new file mode 100644
index 00000000..fb9316b9
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/countingwand/Countingwand.java
@@ -0,0 +1,107 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2021 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.countingwand;
+
+import de.steamwar.bausystem.BauSystem;
+import de.steamwar.bausystem.region.Point;
+import de.steamwar.bausystem.shared.Pair;
+import de.steamwar.bausystem.utils.ItemUtils;
+import de.steamwar.inventory.SWItem;
+import lombok.experimental.UtilityClass;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+
+@UtilityClass
+public class Countingwand {
+
+ public static ItemStack getWandItem(Player player) {
+ ItemStack itemStack = new SWItem(Material.STICK, BauSystem.MESSAGE.parse("COUNTINGWAND_ITEM_NAME", player), Arrays.asList(BauSystem.MESSAGE.parse("COUNTINGWAND_ITEM_LORE1", player), BauSystem.MESSAGE.parse("COUNTINGWAND_ITEM_LORE2", player)), false, null).getItemStack();
+ ItemUtils.setItem(itemStack, "countingwand");
+ ItemMeta itemMeta = itemStack.getItemMeta();
+ itemMeta.setCustomModelData(1);
+ itemStack.setItemMeta(itemMeta);
+ return itemStack;
+ }
+
+ private final Map> selections = new HashMap<>();
+
+ public boolean isCountingwand(ItemStack itemStack) {
+ return ItemUtils.isItem(itemStack, "countingwand");
+ }
+
+ public void checkSelection(final Point point, final boolean pos1, final Player p) {
+ Pair selection = selections.get(p.getUniqueId().toString());
+ final boolean newPos;
+ if (selection != null) {
+ if (pos1) {
+ newPos = !point.equals(selection.setKey(point));
+ } else {
+ newPos = !point.equals(selection.setValue(point));
+ }
+ } else {
+ if (pos1) {
+ selection = new Pair<>(point, null);
+ } else {
+ selection = new Pair<>(null, point);
+ }
+ selections.put(p.getUniqueId().toString(), selection);
+ newPos = true;
+ }
+
+ if (newPos) {
+ String dimension = getDimensions(p, selection.getKey(), selection.getValue());
+ String volume = getVolume(p, selection.getKey(), selection.getValue());
+ if (pos1) {
+ BauSystem.MESSAGE.send("COUNTINGWAND_MESSAGE_RCLICK", p, point.getX(), point.getY(), point.getZ(), dimension, volume);
+ } else {
+ BauSystem.MESSAGE.send("COUNTINGWAND_MESSAGE_LCLICK", p, point.getX(), point.getY(), point.getZ(), dimension, volume);
+ }
+ }
+ }
+
+ public void removePlayer(Player p) {
+ selections.remove(p.getUniqueId().toString());
+ }
+
+ public String getVolume(Player player, final Point point1, final Point point2) {
+ if (point1 == null || point2 == null) {
+ return BauSystem.MESSAGE.parse("COUNTINGWAND_MESSAGE_VOLUME", player, 0);
+ }
+ return BauSystem.MESSAGE.parse("COUNTINGWAND_MESSAGE_VOLUME", player, getAmount(point1, point2));
+ }
+
+ public String getDimensions(Player player, final Point point1, final Point point2) {
+ if (point1 == null || point2 == null) {
+ return BauSystem.MESSAGE.parse("COUNTINGWAND_MESSAGE_DIMENSION", player, 0, 0, 0);
+ }
+ return BauSystem.MESSAGE.parse("COUNTINGWAND_MESSAGE_DIMENSION", player, Math.abs(point1.getX() - point2.getX()) + 1, Math.abs(point1.getY() - point2.getY()) + 1, Math.abs(point1.getZ() - point2.getZ()) + 1);
+ }
+
+ public int getAmount(final Point point1, final Point point2) {
+ return (Math.abs(point1.getX() - point2.getX()) + 1) * (Math.abs(point1.getY() - point2.getY()) + 1) * (Math.abs(point1.getZ() - point2.getZ()) + 1);
+ }
+}
\ No newline at end of file
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/countingwand/CountingwandCommand.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/countingwand/CountingwandCommand.java
new file mode 100644
index 00000000..c7d9b270
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/countingwand/CountingwandCommand.java
@@ -0,0 +1,39 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2021 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.countingwand;
+
+import de.steamwar.bausystem.SWUtils;
+import de.steamwar.command.SWCommand;
+import de.steamwar.linkage.Linked;
+import org.bukkit.entity.Player;
+
+
+@Linked
+public class CountingwandCommand extends SWCommand {
+
+ public CountingwandCommand() {
+ super("countingwand", "/countingwand", "cwand", "/cwand", "zollstock", "/zollstock");
+ }
+
+ @Register(description = "COUNTINGWAND_COMMAND_HELP")
+ public void genericCommand(final Player p) {
+ SWUtils.giveItemToPlayer(p, Countingwand.getWandItem(p));
+ }
+}
\ No newline at end of file
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/countingwand/CountingwandGuiItem.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/countingwand/CountingwandGuiItem.java
new file mode 100644
index 00000000..dbeae70d
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/countingwand/CountingwandGuiItem.java
@@ -0,0 +1,53 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2021 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.countingwand;
+
+import de.steamwar.bausystem.Permission;
+import de.steamwar.bausystem.linkage.specific.BauGuiItem;
+import de.steamwar.linkage.Linked;
+import org.bukkit.entity.Player;
+import org.bukkit.event.inventory.ClickType;
+import org.bukkit.inventory.ItemStack;
+
+
+@Linked
+public class CountingwandGuiItem extends BauGuiItem {
+
+ public CountingwandGuiItem() {
+ super(22);
+ }
+
+ @Override
+ public ItemStack getItem(Player player) {
+ return Countingwand.getWandItem(player);
+ }
+
+ @Override
+ public boolean click(ClickType click, Player p) {
+ p.closeInventory();
+ p.performCommand("countingwand");
+ return false;
+ }
+
+ @Override
+ public Permission permission() {
+ return Permission.MEMBER;
+ }
+}
\ No newline at end of file
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/countingwand/CountingwandListener.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/countingwand/CountingwandListener.java
new file mode 100644
index 00000000..7233670a
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/countingwand/CountingwandListener.java
@@ -0,0 +1,75 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2021 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.countingwand;
+
+import de.steamwar.bausystem.region.Point;
+import de.steamwar.linkage.Linked;
+import org.bukkit.FluidCollisionMode;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.block.Action;
+import org.bukkit.event.block.BlockBreakEvent;
+import org.bukkit.event.player.PlayerInteractEvent;
+import org.bukkit.event.player.PlayerQuitEvent;
+import org.bukkit.util.RayTraceResult;
+
+import java.util.Objects;
+
+
+@Linked
+public class CountingwandListener implements Listener {
+
+ @EventHandler
+ public void onBlockBreak(final BlockBreakEvent event) {
+ if (!Countingwand.isCountingwand(event.getPlayer().getInventory().getItemInMainHand())) {
+ return;
+ }
+
+ event.setCancelled(true);
+ Countingwand.checkSelection(Point.fromLocation(event.getBlock().getLocation()), true, event.getPlayer());
+ }
+
+ @EventHandler
+ public void onPlayerInteract(final PlayerInteractEvent event) {
+ if (!Countingwand.isCountingwand(event.getItem())) {
+ return;
+ }
+
+ if (event.getAction() == Action.RIGHT_CLICK_AIR || event.getAction() == Action.LEFT_CLICK_AIR) {
+ RayTraceResult rayTraceResult = event.getPlayer().rayTraceBlocks(200, FluidCollisionMode.NEVER);
+ if (rayTraceResult == null) {
+ return;
+ }
+ Countingwand.checkSelection(Point.fromLocation(Objects.requireNonNull(rayTraceResult.getHitBlock()).getLocation()), event.getAction() == Action.LEFT_CLICK_AIR, event.getPlayer());
+ return;
+ }
+ if (event.getAction() != Action.RIGHT_CLICK_BLOCK) {
+ return;
+ }
+
+ event.setCancelled(true);
+ Countingwand.checkSelection(Point.fromLocation(Objects.requireNonNull(event.getClickedBlock()).getLocation()), false, event.getPlayer());
+ }
+
+ @EventHandler
+ public void onLeave(PlayerQuitEvent event) {
+ Countingwand.removePlayer(event.getPlayer());
+ }
+}
\ No newline at end of file
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/design/endstone/DesignEndStone.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/design/endstone/DesignEndStone.java
new file mode 100644
index 00000000..f04fd5e9
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/design/endstone/DesignEndStone.java
@@ -0,0 +1,141 @@
+/*
+ * 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.design.endstone;
+
+import de.steamwar.bausystem.BauSystem;
+import de.steamwar.bausystem.region.Region;
+import de.steamwar.entity.REntity;
+import de.steamwar.entity.REntityServer;
+import de.steamwar.entity.RFallingBlockEntity;
+import net.md_5.bungee.api.ChatMessageType;
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.World;
+import org.bukkit.entity.Player;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class DesignEndStone {
+
+ private static final World WORLD = Bukkit.getWorlds().get(0);
+
+ private int minX, minY, minZ, maxX, maxY, maxZ;
+ private REntityServer entityServer = new REntityServer();
+ private List entities = new ArrayList<>();
+ private Set locations = new HashSet<>();
+ private boolean wsOrAs;
+ private double maxBlastResistance;
+
+ public DesignEndStone(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();
+ wsOrAs = region.getName().startsWith("ws") || region.getName().startsWith("as");
+ maxBlastResistance = wsOrAs ? 6.1 : 9.0;
+
+ entityServer.setCallback((player, rEntity, entityAction) -> {
+ if (entityAction != REntityServer.EntityAction.ATTACK) return;
+ Location location = new Location(WORLD, rEntity.getX(), rEntity.getY(), rEntity.getZ());
+ Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
+ location.getBlock().breakNaturally();
+ calc();
+ }, 1);
+ });
+ }
+
+ public void calc() {
+ entities.forEach(REntity::die);
+ entities.clear();
+ locations.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);
+ if (wsOrAs) {
+ calc(minX, minY, minZ, maxX, minY, maxZ, 0, 1, 0, maxY - minY + 1);
+ } else {
+ int airBlocks = 0;
+ double minAirBlocks = (maxX - minX) * (maxZ - minZ) * 0.1;
+ for (int x = minX; x < maxX; x++) {
+ for (int z = minZ; z < maxZ; z++) {
+ if (WORLD.getBlockAt(x, minY, z).getType().isAir()) {
+ airBlocks++;
+ if (airBlocks > minAirBlocks) break;
+ }
+ }
+ }
+ if (airBlocks > minAirBlocks) {
+ calc(minX, minY, minZ, maxX, minY, maxZ, 0, 1, 0, maxY - minY + 1);
+ }
+ }
+ calc(minX, maxY, minZ, maxX, maxY, maxZ, 0, -1, 0, maxY - minY + 1);
+ }
+
+ private void calc(int minX, int minY, int minZ, int maxX, int maxY, int maxZ, int dirX, int dirY, int dirZ, int steps) {
+ for (int x = minX; x <= maxX; x++) {
+ for (int y = minY; y <= maxY; y++) {
+ for (int z = minZ; z <= maxZ; z++) {
+ for (int step = 0; step < steps; step++) {
+ int cx = x + step * dirX;
+ int cy = y + step * dirY;
+ int cz = z + step * dirZ;
+ Material material = WORLD.getBlockAt(cx, cy, cz).getType();
+
+ if (material != Material.WATER && material != Material.LAVA && material.getBlastResistance() >= maxBlastResistance) {
+ Location location = new Location(WORLD, cx + 0.5, cy, cz + 0.5);
+ if (!locations.add(location)) break;
+ RFallingBlockEntity entity = new RFallingBlockEntity(entityServer, location, Material.RED_STAINED_GLASS);
+ entity.setNoGravity(true);
+ entity.setGlowing(true);
+ entities.add(entity);
+ break;
+ } else if (!material.isAir() && material != Material.WATER && material != Material.LAVA) {
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public void toggle(Player player) {
+ if (entityServer.getPlayers().contains(player)) {
+ entityServer.removePlayer(player);
+ BauSystem.MESSAGE.sendPrefixless("DESIGN_ENDSTONE_DISABLE", player, ChatMessageType.ACTION_BAR);
+ } else {
+ entityServer.addPlayer(player);
+ calc();
+ BauSystem.MESSAGE.sendPrefixless("DESIGN_ENDSTONE_ENABLE", player, ChatMessageType.ACTION_BAR);
+ }
+ }
+
+ public boolean removePlayer(Player player) {
+ entityServer.removePlayer(player);
+ return entityServer.getPlayers().isEmpty();
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/design/endstone/DesignEndStoneCommand.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/design/endstone/DesignEndStoneCommand.java
new file mode 100644
index 00000000..92934c23
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/design/endstone/DesignEndStoneCommand.java
@@ -0,0 +1,97 @@
+/*
+ * 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.design.endstone;
+
+import de.steamwar.bausystem.BauSystem;
+import de.steamwar.bausystem.region.Region;
+import de.steamwar.bausystem.region.utils.RegionType;
+import de.steamwar.bausystem.utils.BauMemberUpdateEvent;
+import de.steamwar.command.SWCommand;
+import de.steamwar.linkage.Linked;
+import org.bukkit.Location;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.block.BlockBreakEvent;
+import org.bukkit.event.block.BlockPlaceEvent;
+import org.bukkit.event.player.PlayerQuitEvent;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+
+@Linked
+public class DesignEndStoneCommand extends SWCommand implements Listener {
+
+ public DesignEndStoneCommand() {
+ super("designendstone");
+ }
+
+ private Map designEndStoneMap = new HashMap<>();
+
+ @Register(description = "DESIGN_ENDSTONE_COMMAND_HELP")
+ public void genericCommand(@Validator Player player) {
+ Region region = Region.getRegion(player.getLocation());
+ if (!region.hasType(RegionType.BUILD)) {
+ BauSystem.MESSAGE.send("DESIGN_ENDSTONE_REGION_ERROR", player);
+ return;
+ }
+ designEndStoneMap.computeIfAbsent(region, DesignEndStone::new).toggle(player);
+ }
+
+ @EventHandler
+ public void onPlayerQuit(PlayerQuitEvent event) {
+ disableDesignEndStone(event.getPlayer());
+ }
+
+ @EventHandler
+ public void onBauMemberUpdate(BauMemberUpdateEvent event) {
+ event.getNewSpectator().forEach(this::disableDesignEndStone);
+ }
+
+ private void disableDesignEndStone(Player player) {
+ new HashSet<>(designEndStoneMap.entrySet()).forEach(regionDesignEndStoneEntry -> {
+ if (regionDesignEndStoneEntry.getValue().removePlayer(player)) {
+ designEndStoneMap.remove(regionDesignEndStoneEntry.getKey());
+ }
+ });
+ }
+
+ @EventHandler
+ public void onBlockPlace(BlockPlaceEvent event) {
+ update(event.getBlock().getLocation());
+ }
+
+ @EventHandler
+ public void onBlockBreak(BlockBreakEvent event) {
+ BauSystem.runTaskLater(BauSystem.getInstance(), () -> {
+ update(event.getBlock().getLocation());
+ }, 1);
+ }
+
+ private void update(Location location) {
+ Region region = Region.getRegion(location);
+ DesignEndStone designEndStone = designEndStoneMap.get(region);
+ if (designEndStone == null) {
+ return;
+ }
+ designEndStone.calc();
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/detonator/Detoblock.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/detonator/Detoblock.java
new file mode 100644
index 00000000..85facc7a
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/detonator/Detoblock.java
@@ -0,0 +1,44 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2021 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.detonator;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+@AllArgsConstructor
+@RequiredArgsConstructor
+@Getter
+public enum Detoblock {
+ SWITCH(0, true, "DETONATOR_BUTTON_SWITCH"),
+ WOOD_BUTTON(30, "DETONATOR_BUTTON_WOOD_BUTTON"),
+ STONE_BUTTON(20, "DETONATOR_BUTTON_STONE_BUTTON"),
+ PRESSURE_PLATE(30, "DETONATOR_BUTTON_PRESSURE_PLATE"),
+ WEIGHTED_PRESSURE_PLATE(20, "DETONATOR_BUTTON_WEIGHTED-PRESSURE_PLATE"),
+ TRIPWIRE(30, "DETONATOR_BUTTON_TRIPWIRE"),
+ NOTEBLOCK(1, "DETONATOR_BUTTON_NOTEBLOCK"),
+ DAYLIGHTSENSOR(0, true, "DETONATOR_BUTTON_DAYLIGHTSENSOR"),
+ POWERABLE(0, true, "DETONATOR_BUTTON_POWERABLE"),
+ INVALID(-1, "DETONATOR_BUTTON_INVALID");
+
+ private final int time;
+ private boolean toggle;
+ private final String name;
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/detonator/Detonator.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/detonator/Detonator.java
new file mode 100644
index 00000000..80d5b33b
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/detonator/Detonator.java
@@ -0,0 +1,225 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2021 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.detonator;
+
+import de.steamwar.bausystem.BauSystem;
+import de.steamwar.bausystem.SWUtils;
+import de.steamwar.bausystem.configplayer.Config;
+import de.steamwar.bausystem.features.autostart.AutostartListener;
+import de.steamwar.bausystem.features.detonator.storage.DetonatorStorage;
+import de.steamwar.bausystem.features.detonator.storage.ItemStorage;
+import de.steamwar.entity.REntityServer;
+import de.steamwar.entity.RFallingBlockEntity;
+import lombok.experimental.UtilityClass;
+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.block.data.*;
+import org.bukkit.block.data.type.DaylightDetector;
+import org.bukkit.block.data.type.Switch;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.util.Vector;
+
+import java.util.*;
+
+@UtilityClass
+public class Detonator {
+
+ private static final Map ENTITIES_MAP = new HashMap<>();
+ private static final Vector HALF = new Vector(0.5, 0, 0.5);
+
+ public static boolean isDetonator(ItemStack itemStack) {
+ return ItemStorage.isDetonator(itemStack);
+ }
+
+ public static void showDetonator(Player p, List locs) {
+ if (ENTITIES_MAP.containsKey(p)) return;
+ REntityServer entities = new REntityServer();
+ entities.setCallback((player, rEntity, entityAction) -> {
+ Vector vector = new Vector(rEntity.getX(), rEntity.getY(), rEntity.getZ());
+ DetonatorListener.addLocationToDetonator(vector.toLocation(player.getWorld()).getBlock().getLocation(), player);
+ DetonatorListener.HAS_UPDATED.add(player);
+ });
+ entities.addPlayer(p);
+ ENTITIES_MAP.put(p, entities);
+
+ locs.forEach(location -> {
+ RFallingBlockEntity entity = new RFallingBlockEntity(entities, location.clone().add(HALF), Material.RED_STAINED_GLASS);
+ entity.setNoGravity(true);
+ });
+ }
+
+ public static void hideDetonator(Player p) {
+ ENTITIES_MAP.remove(p).close();
+ }
+
+ public static boolean hasActiveDetonatorShow(Player p) {
+ return ENTITIES_MAP.containsKey(p);
+ }
+
+ public static void activateDetonator(DetonatorStorage detonator) {
+ Player p = detonator.getPlayer();
+ if (Config.getInstance().get(p).getPlainValueOrDefault("detonator-autostart", false)) {
+ AutostartListener.instance.activate(p);
+ }
+
+ Map> deactivate = new HashMap<>();
+ Set invalid = new HashSet<>();
+
+ for (Location location : detonator.getLocations()) {
+ Block block = location.getBlock();
+ Detoblock detoblock = getBlock(block);
+
+ if (detoblock == Detoblock.INVALID) {
+ invalid.add(location);
+ continue;
+ }
+
+ if (detoblock.isToggle()) {
+ setBlockPower(block, !getBlockPower(block));
+ updateButton(block, detoblock);
+ } else {
+ setBlockPower(block, true);
+ updateButton(block, detoblock);
+ deactivate.computeIfAbsent(detoblock.getTime(), integer -> new HashSet<>()).add(block);
+ }
+ }
+
+ int s = detonator.getLocations().size() - invalid.size();
+ if (s == 1) {
+ SWUtils.sendToActionbar(p, BauSystem.MESSAGE.parse("DETONATOR_POINT_ACT", p));
+ } else {
+ SWUtils.sendToActionbar(p, BauSystem.MESSAGE.parse("DETONATOR_POINTS_ACT", p, s));
+ }
+
+ if (!invalid.isEmpty()) {
+ int invalidPoints = invalid.size();
+ if (invalidPoints == 1) {
+ BauSystem.MESSAGE.send("DETONATOR_INVALID_POINT", p);
+ } else {
+ BauSystem.MESSAGE.send("DETONATOR_INVALID_POINTS", p, invalidPoints);
+ }
+ detonator.write();
+ }
+
+ deactivate.forEach((integer, blocks1) ->
+ Bukkit.getScheduler().scheduleSyncDelayedTask(BauSystem.getInstance(), () -> blocks1.forEach(block -> {
+ setBlockPower(block, false);
+ updateButton(block, getBlock(block));
+ }), integer));
+ }
+
+ public static void updateButton(Block block, Detoblock detoblock) {
+ if (block.getBlockData() instanceof Switch) {
+ Switch sw = (Switch) block.getBlockData();
+ FaceAttachable.AttachedFace face = sw.getAttachedFace();
+ if (face == FaceAttachable.AttachedFace.FLOOR) {
+ update(block.getRelative(BlockFace.DOWN));
+ } else if (face == FaceAttachable.AttachedFace.CEILING) {
+ update(block.getRelative(BlockFace.UP));
+ } else {
+ update(block.getRelative(sw.getFacing().getOppositeFace()));
+ }
+ } else if (detoblock == Detoblock.TRIPWIRE) {
+ update(block);
+ } else if (detoblock == Detoblock.PRESSURE_PLATE || detoblock == Detoblock.WEIGHTED_PRESSURE_PLATE) {
+ update(block.getRelative(BlockFace.DOWN));
+ }
+ }
+
+ private static void update(Block block) {
+ BlockData data = block.getBlockData();
+ block.setType(Material.BARRIER, true);
+ block.setBlockData(data, true);
+ }
+
+ private static void setBlockPower(Block block, boolean state) {
+ BlockData data = block.getBlockData();
+ if (data instanceof Powerable) {
+ Powerable pow = (Powerable) data;
+ pow.setPowered(state);
+ }
+ if (data instanceof Openable) {
+ Openable openable = (Openable) data;
+ openable.setOpen(state);
+ }
+ if (data instanceof DaylightDetector) {
+ DaylightDetector detector = (DaylightDetector) data;
+ detector.setInverted(state);
+ }
+ if (data instanceof AnaloguePowerable) {
+ AnaloguePowerable powerable = (AnaloguePowerable) data;
+ if (block.getType() == Material.REDSTONE_WIRE) {
+ powerable.setPower(state ? 15 : 0);
+ } else {
+ powerable.setPower(state ? 1 : 0);
+ }
+ }
+ block.setBlockData(data);
+ }
+
+ private static boolean getBlockPower(Block block) {
+ BlockData data = block.getBlockData();
+ if (data instanceof Powerable) {
+ Powerable pow = (Powerable) data;
+ return pow.isPowered();
+ }
+ if (data instanceof DaylightDetector) {
+ DaylightDetector detector = (DaylightDetector) data;
+ return detector.isInverted();
+ }
+ if (data instanceof AnaloguePowerable) {
+ AnaloguePowerable powerable = (AnaloguePowerable) data;
+ return powerable.getPower() > 0;
+ }
+ return false;
+ }
+
+ static Detoblock getBlock(Block block) {
+ switch (block.getType()) {
+ case LEVER:
+ return Detoblock.SWITCH;
+ case TRIPWIRE:
+ return Detoblock.TRIPWIRE;
+ case NOTE_BLOCK:
+ return Detoblock.NOTEBLOCK;
+ case DAYLIGHT_DETECTOR:
+ return Detoblock.DAYLIGHTSENSOR;
+ case HEAVY_WEIGHTED_PRESSURE_PLATE:
+ case LIGHT_WEIGHTED_PRESSURE_PLATE:
+ return Detoblock.WEIGHTED_PRESSURE_PLATE;
+ default:
+ if (block.getType().name().contains("STONE_BUTTON")) {
+ return Detoblock.STONE_BUTTON;
+ } else if (block.getType().name().contains("BUTTON")) {
+ return Detoblock.WOOD_BUTTON;
+ } else if (block.getType().name().contains("PRESSURE_PLATE")) {
+ return Detoblock.PRESSURE_PLATE;
+ } else if (block.getBlockData() instanceof Powerable) {
+ return Detoblock.POWERABLE;
+ } else {
+ return Detoblock.INVALID;
+ }
+ }
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/detonator/DetonatorBauGuiItem.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/detonator/DetonatorBauGuiItem.java
new file mode 100644
index 00000000..3f633663
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/detonator/DetonatorBauGuiItem.java
@@ -0,0 +1,52 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2021 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.detonator;
+
+import de.steamwar.bausystem.Permission;
+import de.steamwar.bausystem.linkage.specific.BauGuiItem;
+import de.steamwar.linkage.Linked;
+import org.bukkit.entity.Player;
+import org.bukkit.event.inventory.ClickType;
+import org.bukkit.inventory.ItemStack;
+
+@Linked
+public class DetonatorBauGuiItem extends BauGuiItem {
+
+ public DetonatorBauGuiItem() {
+ super(8);
+ }
+
+ @Override
+ public ItemStack getItem(Player player) {
+ return DetonatorCommand.getWAND(player);
+ }
+
+ @Override
+ public boolean click(ClickType click, Player p) {
+ p.closeInventory();
+ p.performCommand("detonator wand");
+ return false;
+ }
+
+ @Override
+ public Permission permission() {
+ return Permission.BUILD;
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/detonator/DetonatorCommand.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/detonator/DetonatorCommand.java
new file mode 100644
index 00000000..c8d493bd
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/detonator/DetonatorCommand.java
@@ -0,0 +1,87 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2021 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.detonator;
+
+import de.steamwar.bausystem.BauSystem;
+import de.steamwar.bausystem.SWUtils;
+import de.steamwar.bausystem.configplayer.Config;
+import de.steamwar.bausystem.features.detonator.storage.DetonatorStorage;
+import de.steamwar.bausystem.features.detonator.storage.ItemStorage;
+import de.steamwar.bausystem.utils.ItemUtils;
+import de.steamwar.command.SWCommand;
+import de.steamwar.linkage.Linked;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+
+import java.util.Arrays;
+
+@Linked
+public class DetonatorCommand extends SWCommand {
+
+ public static ItemStack getWAND(Player player) {
+ ItemStack wand = new ItemStack(Material.BLAZE_ROD);
+ ItemStorage.setDetonator(wand);
+ ItemMeta meta = wand.getItemMeta();
+
+ meta.setDisplayName(BauSystem.MESSAGE.parse("DETONATOR_WAND_NAME", player));
+ meta.setLore(Arrays.asList(BauSystem.MESSAGE.parse("DETONATOR_WAND_LORE_1", player),
+ BauSystem.MESSAGE.parse("DETONATOR_WAND_LORE_2", player),
+ BauSystem.MESSAGE.parse("DETONATOR_WAND_LORE_3", player)));
+ meta.setCustomModelData(3);
+
+ wand.setItemMeta(meta);
+ ItemUtils.setItem(wand, "detonator");
+ return wand;
+ }
+
+ public DetonatorCommand() {
+ super("detonator", "dt");
+ }
+
+ @Register(value = "wand", description = "DETONATOR_HELP_WAND")
+ public void giveWand(@Validator Player p) {
+ SWUtils.giveItemToPlayer(p, getWAND(p));
+ }
+
+ @Register(value = "click", description = "DETONATOR_HELP_CLICK")
+ public void clickDetonator(@Validator Player p) {
+ Detonator.activateDetonator(new ItemStorage(p));
+ }
+
+ @Register(value = "clear", description = "DETONATOR_HELP_CLEAR")
+ public void clearDetonator(Player p) {
+ DetonatorStorage storage = new ItemStorage(p);
+ storage.clear();
+ storage.write();
+ }
+
+ @Register(value = "autostart", description = "DETONATOR_HELP_AUTOSTART")
+ public void toggleAutostartTimer(Player p) {
+ boolean current = Config.getInstance().get(p).getPlainValueOrDefault("detonator-autostart", false);
+ Config.getInstance().get(p).put("detonator-autostart", !current);
+ if (!current) {
+ BauSystem.MESSAGE.send("DETONATOR_AUTOSTART_ENABLE", p);
+ } else {
+ BauSystem.MESSAGE.send("DETONATOR_AUTOSTART_DISABLE", p);
+ }
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/detonator/DetonatorListener.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/detonator/DetonatorListener.java
new file mode 100644
index 00000000..cabe00c6
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/detonator/DetonatorListener.java
@@ -0,0 +1,129 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2021 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.detonator;
+
+import de.steamwar.bausystem.BauSystem;
+import de.steamwar.bausystem.Permission;
+import de.steamwar.bausystem.SWUtils;
+import de.steamwar.bausystem.features.detonator.storage.DetonatorStorage;
+import de.steamwar.bausystem.features.detonator.storage.ItemStorage;
+import de.steamwar.linkage.Linked;
+import org.bukkit.Location;
+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.block.BlockBreakEvent;
+import org.bukkit.event.player.PlayerInteractEvent;
+import org.bukkit.event.player.PlayerItemHeldEvent;
+import org.bukkit.event.player.PlayerMoveEvent;
+import org.bukkit.event.player.PlayerSwapHandItemsEvent;
+
+import java.util.HashSet;
+import java.util.Set;
+
+@Linked
+public class DetonatorListener implements Listener {
+
+ static final Set HAS_UPDATED = new HashSet<>();
+
+ static void addLocationToDetonator(Location location, Player p) {
+ Detoblock detoblock = Detonator.getBlock(location.getBlock());
+ if (detoblock == Detoblock.INVALID) {
+ SWUtils.sendToActionbar(p, BauSystem.MESSAGE.parse("DETONATOR_INVALID_BLOCK", p));
+ return;
+ }
+ DetonatorStorage detonator = new ItemStorage(p);
+ if (!p.isSneaking()) {
+ detonator.clear();
+ }
+ if (detonator.getLocations().contains(location)) {
+ detonator.removeLocation(location);
+ SWUtils.sendToActionbar(p, BauSystem.MESSAGE.parse("DETONATOR_LOC_REMOVE", p, BauSystem.MESSAGE.parse(detoblock.getName(), p)));
+ } else {
+ detonator.addLocation(location);
+ SWUtils.sendToActionbar(p, BauSystem.MESSAGE.parse("DETONATOR_LOC_ADD", p, BauSystem.MESSAGE.parse(detoblock.getName(), p)));
+ }
+ detonator.write();
+ }
+
+ @EventHandler
+ public void onBlockBreak(BlockBreakEvent event) {
+ if(!Permission.BUILD.hasPermission(event.getPlayer())) return;
+ Player p = event.getPlayer();
+ if (Detonator.isDetonator(p.getInventory().getItemInMainHand())) {
+ event.setCancelled(true);
+ addLocationToDetonator(event.getBlock().getLocation(), p);
+ HAS_UPDATED.add(event.getPlayer());
+ }
+ }
+
+ @EventHandler
+ public void onPlayerInteract(PlayerInteractEvent event) {
+ if(!Permission.BUILD.hasPermission(event.getPlayer())) return;
+ if (!Detonator.isDetonator(event.getItem())) {
+ return;
+ }
+
+ if (event.getAction() == Action.RIGHT_CLICK_AIR || event.getAction() == Action.RIGHT_CLICK_BLOCK) {
+ event.setCancelled(true);
+ DetonatorStorage detonator = new ItemStorage(event.getPlayer());
+ Detonator.activateDetonator(detonator);
+ HAS_UPDATED.add(event.getPlayer());
+ }
+ }
+
+ @EventHandler(ignoreCancelled = true)
+ public void onPlayerMove(PlayerMoveEvent event) {
+ if (!Permission.BUILD.hasPermission(event.getPlayer()) ||!Detonator.isDetonator(event.getPlayer().getInventory().getItemInMainHand())) {
+ if (Detonator.hasActiveDetonatorShow(event.getPlayer())) {
+ Detonator.hideDetonator(event.getPlayer());
+ }
+ } else {
+ if (!Detonator.hasActiveDetonatorShow(event.getPlayer())) {
+ Detonator.showDetonator(event.getPlayer(), new ItemStorage(event.getPlayer()).getLocations());
+ }
+ }
+
+ if (HAS_UPDATED.contains(event.getPlayer())) {
+ HAS_UPDATED.remove(event.getPlayer());
+ if (Detonator.hasActiveDetonatorShow(event.getPlayer())) {
+ Detonator.hideDetonator(event.getPlayer());
+ Detonator.showDetonator(event.getPlayer(), new ItemStorage(event.getPlayer()).getLocations());
+ }
+ }
+ }
+
+ @EventHandler
+ public void onPlayerItemHeld(PlayerItemHeldEvent event) {
+ if(!Permission.BUILD.hasPermission(event.getPlayer())) return;
+ if (Detonator.isDetonator(event.getPlayer().getInventory().getItemInMainHand())) {
+ HAS_UPDATED.add(event.getPlayer());
+ }
+ }
+
+ @EventHandler
+ public void onPlayerSwapHandItems(PlayerSwapHandItemsEvent event) {
+ if(!Permission.BUILD.hasPermission(event.getPlayer())) return;
+ if (Detonator.isDetonator(event.getMainHandItem()) || Detonator.isDetonator(event.getOffHandItem())) {
+ HAS_UPDATED.add(event.getPlayer());
+ }
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/detonator/storage/DetonatorStorage.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/detonator/storage/DetonatorStorage.java
new file mode 100644
index 00000000..104abf29
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/detonator/storage/DetonatorStorage.java
@@ -0,0 +1,40 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2021 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.detonator.storage;
+
+import org.bukkit.Location;
+import org.bukkit.entity.Player;
+
+import java.util.List;
+
+public interface DetonatorStorage {
+
+ void addLocation(Location location);
+
+ List getLocations();
+
+ void removeLocation(Location location);
+
+ void clear();
+
+ void write();
+
+ Player getPlayer();
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/detonator/storage/ItemStorage.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/detonator/storage/ItemStorage.java
new file mode 100644
index 00000000..71b6edb4
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/detonator/storage/ItemStorage.java
@@ -0,0 +1,191 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2021 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.detonator.storage;
+
+import de.steamwar.bausystem.SWUtils;
+import lombok.Getter;
+import org.bukkit.Location;
+import org.bukkit.NamespacedKey;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+import org.bukkit.persistence.PersistentDataContainer;
+import org.bukkit.persistence.PersistentDataType;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.function.IntFunction;
+
+public class ItemStorage implements DetonatorStorage {
+
+ private static final NamespacedKey DETO_KEY = SWUtils.getNamespaceKey("deto");
+ private static final NamespacedKey SLOT_KEY = SWUtils.getNamespaceKey("slots");
+ private static final IntFunction DETOLOCATION_BUILDER = integer -> SWUtils.getNamespaceKey("deto_loc_" + integer);
+ @Getter
+ private final Player p;
+ private final List locs;
+ private int slot;
+ private ItemStack itemStack;
+
+ public ItemStorage(Player p) {
+ this.p = p;
+ if (isDetonator(p.getInventory().getItemInMainHand())) {
+ slot = p.getInventory().getHeldItemSlot();
+ itemStack = p.getInventory().getItemInMainHand();
+ } else {
+ ItemStack[] inv = p.getInventory().getContents();
+ for (int i = 0; i < inv.length; i++) {
+ if (isDetonator(inv[i])) {
+ slot = i;
+ itemStack = inv[i];
+ break;
+ }
+ }
+ }
+ locs = read();
+ }
+
+ public static ItemStack setDetonator(ItemStack item) {
+ ItemMeta meta = item.getItemMeta();
+ if (meta == null) {
+ return item;
+ }
+ PersistentDataContainer dataContainer = meta.getPersistentDataContainer();
+ dataContainer.set(DETO_KEY, PersistentDataType.BYTE, (byte) 1);
+ dataContainer.set(SLOT_KEY, PersistentDataType.INTEGER, 0);
+ item.setItemMeta(meta);
+ return item;
+ }
+
+ public static boolean isDetonator(ItemStack item) {
+ if (item == null) {
+ return false;
+ }
+ ItemMeta meta = item.getItemMeta();
+ if (meta == null) {
+ return false;
+ }
+ PersistentDataContainer container = meta.getPersistentDataContainer();
+ return container.has(DETO_KEY, PersistentDataType.BYTE) && container.has(SLOT_KEY, PersistentDataType.INTEGER);
+ }
+
+ @Override
+ public void addLocation(Location location) {
+ locs.add(location);
+ }
+
+ @Override
+ public List getLocations() {
+ return locs;
+ }
+
+ @Override
+ public void removeLocation(Location location) {
+ locs.remove(location);
+ }
+
+ @Override
+ public void clear() {
+ internalClear();
+ locs.clear();
+ apply();
+ }
+
+ @Override
+ public void write() {
+ write(locs);
+ }
+
+ @Override
+ public Player getPlayer() {
+ return p;
+ }
+
+ private List read() {
+ List tempLocs = new LinkedList<>();
+
+ if (itemStack == null) {
+ return Collections.emptyList();
+ }
+ ItemMeta meta = itemStack.getItemMeta();
+ if (meta == null) {
+ return Collections.emptyList();
+ }
+ PersistentDataContainer container = meta.getPersistentDataContainer();
+
+ int length = length();
+ for (int i = 0; i < length; i++) {
+ NamespacedKey key = DETOLOCATION_BUILDER.apply(i);
+ int[] loc = container.get(key, PersistentDataType.INTEGER_ARRAY);
+ tempLocs.add(new Location(p.getWorld(), loc[0], loc[1], loc[2]));
+ }
+
+ return tempLocs;
+ }
+
+ private int length() {
+ ItemMeta meta = itemStack.getItemMeta();
+ PersistentDataContainer container = meta.getPersistentDataContainer();
+ return container.get(SLOT_KEY, PersistentDataType.INTEGER);
+ }
+
+ private void internalClear() {
+ ItemMeta meta = itemStack.getItemMeta();
+ PersistentDataContainer container = meta.getPersistentDataContainer();
+
+ int length = length();
+ for (int i = 0; i < length; i++) {
+ NamespacedKey key = DETOLOCATION_BUILDER.apply(i);
+ container.remove(key);
+ }
+
+ container.set(SLOT_KEY, PersistentDataType.INTEGER, 0);
+
+ itemStack.setItemMeta(meta);
+ }
+
+ private void write(List locs) {
+ internalClear();
+ ItemMeta meta = itemStack.getItemMeta();
+ PersistentDataContainer container = meta.getPersistentDataContainer();
+
+ int i = 0;
+ for (Location loc : locs) {
+ NamespacedKey key = DETOLOCATION_BUILDER.apply(i);
+ int[] slotLoc = new int[]{
+ loc.getBlockX(),
+ loc.getBlockY(),
+ loc.getBlockZ()
+ };
+ container.set(key, PersistentDataType.INTEGER_ARRAY, slotLoc);
+ i++;
+ }
+
+ container.set(SLOT_KEY, PersistentDataType.INTEGER, i);
+
+ itemStack.setItemMeta(meta);
+ apply();
+ }
+
+ private void apply() {
+ p.getInventory().setItem(slot, itemStack);
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/gui/BauGUI.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/gui/BauGUI.java
new file mode 100644
index 00000000..3b8dc600
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/gui/BauGUI.java
@@ -0,0 +1,120 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2021 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.gui;
+
+import de.steamwar.bausystem.BauSystem;
+import de.steamwar.bausystem.Permission;
+import de.steamwar.bausystem.SWUtils;
+import de.steamwar.bausystem.features.gui.editor.BauGuiMapping;
+import de.steamwar.bausystem.linkage.LinkageUtils;
+import de.steamwar.bausystem.linkage.specific.BauGuiItem;
+import de.steamwar.inventory.SWInventory;
+import lombok.experimental.UtilityClass;
+import net.md_5.bungee.api.ChatColor;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+
+import java.util.*;
+
+@UtilityClass
+public class BauGUI {
+
+ private static final Map ITEMS = new HashMap<>();
+
+ public static Map getITEMS() {
+ if (ITEMS.isEmpty()) LinkageUtils.linkGUIItems();
+ return ITEMS;
+ }
+
+ private static final Set OPEN_INVS = new HashSet<>();
+ private static boolean updating = false;
+
+ public static ItemStack getGUI_ITEM(Player p) {
+ ItemStack GUI_ITEM = new ItemStack(Material.NETHER_STAR);
+ ItemMeta meta = GUI_ITEM.getItemMeta();
+ meta.setDisplayName(ChatColor.YELLOW + BauSystem.MESSAGE.parse("GUI_NAME", p));
+ GUI_ITEM.setItemMeta(meta);
+ return GUI_ITEM;
+ }
+
+ public static void addItem(BauGuiItem item) {
+ if (ITEMS.containsKey(item.getId())) {
+ throw new IllegalArgumentException("Duplicate Id Key: " + item.getId() + ". From Classes: " + ITEMS.get(item.getId()).getClass().getName() + " and " + item.getClass().getName());
+ }
+ ITEMS.put(item.getId(), item);
+ }
+
+ public static void openBauGui(Player p) {
+ if (!updating) {
+ OPEN_INVS.add(p);
+ }
+ BauGuiMapping mapping = BauGuiMapping.getGuiMapping(p);
+ SWInventory inv = new SWInventory(p, mapping.getSize(), BauSystem.MESSAGE.parse("GUI_NAME", p));
+ getITEMS().values().forEach(item -> {
+ if (!mapping.isShown(item.getId())) {
+ return;
+ }
+
+ inv.setItem(mapping.getSlot(item.getId()), permissionLore(item.getItem(p), item.permission(), p), clickType -> {
+ if (item.permission().hasPermission(p)) {
+ if (item.click(clickType, p)) {
+ update();
+ }
+ } else {
+ p.closeInventory();
+ BauSystem.MESSAGE.send("NO_PERMISSION", p);
+ }
+ });
+ });
+ inv.addCloseCallback(clickType -> {
+ if (!updating) {
+ OPEN_INVS.remove(p);
+ }
+ });
+ inv.open();
+ }
+
+ public static void update() {
+ updating = true;
+ OPEN_INVS.forEach(BauGUI::openBauGui);
+ updating = false;
+ }
+
+ public static void giveItem(Player p) {
+ SWUtils.giveItemToPlayer(p, getGUI_ITEM(p));
+ }
+
+ private static ItemStack permissionLore(ItemStack itemStack, Permission permission, Player p) {
+ ItemMeta meta = itemStack.getItemMeta();
+ if (!permission.hasPermission(p)) {
+ List lore = meta.getLore();
+ if (lore == null) {
+ lore = Collections.singletonList(BauSystem.MESSAGE.parse("NO_PERMISSION", p));
+ } else {
+ lore.add(BauSystem.MESSAGE.parse("NO_PERMISSION", p));
+ }
+ meta.setLore(lore);
+ }
+ itemStack.setItemMeta(meta);
+ return itemStack;
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/gui/BauGUICommand.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/gui/BauGUICommand.java
new file mode 100644
index 00000000..f6810ef7
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/gui/BauGUICommand.java
@@ -0,0 +1,49 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2021 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.gui;
+
+import de.steamwar.bausystem.features.gui.editor.BauGuiEditor;
+import de.steamwar.command.SWCommand;
+import de.steamwar.inventory.SWItem;
+import de.steamwar.linkage.Linked;
+import org.bukkit.entity.Player;
+
+@Linked
+public class BauGUICommand extends SWCommand {
+
+ public BauGUICommand() {
+ super("gui", "baugui", "g");
+ }
+
+ @Register(help = true)
+ public void genericHelp(Player p, String... args) {
+ BauGUI.openBauGui(p);
+ }
+
+ @Register("item")
+ public void giveItem(Player p) {
+ BauGUI.giveItem(p);
+ }
+
+ @Register("editor")
+ public void openEditor(Player p) {
+ BauGuiEditor.openGuiEditor(p, new SWItem().getItemStack());
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/gui/BauGuiBauGuiItem.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/gui/BauGuiBauGuiItem.java
new file mode 100644
index 00000000..c03b458a
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/gui/BauGuiBauGuiItem.java
@@ -0,0 +1,60 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2021 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.gui;
+
+import de.steamwar.bausystem.BauSystem;
+import de.steamwar.bausystem.Permission;
+import de.steamwar.bausystem.linkage.specific.BauGuiItem;
+import de.steamwar.inventory.SWItem;
+import de.steamwar.linkage.Linked;
+import net.md_5.bungee.api.ChatColor;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.event.inventory.ClickType;
+import org.bukkit.inventory.ItemStack;
+
+import java.util.Arrays;
+
+@Linked
+public class BauGuiBauGuiItem extends BauGuiItem {
+
+ public BauGuiBauGuiItem() {
+ super(14);
+ }
+
+ @Override
+ public ItemStack getItem(Player player) {
+ return new SWItem(Material.NETHER_STAR, ChatColor.YELLOW + BauSystem.MESSAGE.parse("GUI_NAME", player),
+ Arrays.asList(BauSystem.MESSAGE.parse("GUI_ITEM_LORE1", player), BauSystem.MESSAGE.parse("GUI_ITEM_LORE2", player)), false, clickType -> {
+ }).getItemStack();
+ }
+
+ @Override
+ public boolean click(ClickType click, Player p) {
+ p.closeInventory();
+ BauGUI.giveItem(p);
+ return false;
+ }
+
+ @Override
+ public Permission permission() {
+ return Permission.MEMBER;
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/gui/BauGuiListener.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/gui/BauGuiListener.java
new file mode 100644
index 00000000..5895019f
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/gui/BauGuiListener.java
@@ -0,0 +1,43 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2021 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.gui;
+
+import de.steamwar.linkage.Linked;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.block.Action;
+import org.bukkit.event.player.PlayerInteractEvent;
+
+@Linked
+public class BauGuiListener implements Listener {
+
+ @EventHandler
+ public void onPlayerInteract(PlayerInteractEvent event) {
+ if (event.getItem() == null) {
+ return;
+ }
+
+ if (event.getAction() == Action.RIGHT_CLICK_BLOCK || event.getAction() == Action.RIGHT_CLICK_AIR) {
+ if (event.getItem().isSimilar(BauGUI.getGUI_ITEM(event.getPlayer()))) {
+ BauGUI.openBauGui(event.getPlayer());
+ }
+ }
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/gui/editor/BauGuiEditor.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/gui/editor/BauGuiEditor.java
new file mode 100644
index 00000000..733e7213
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/gui/editor/BauGuiEditor.java
@@ -0,0 +1,233 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2021 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.gui.editor;
+
+import de.steamwar.bausystem.BauSystem;
+import de.steamwar.bausystem.features.gui.BauGUI;
+import de.steamwar.bausystem.linkage.specific.BauGuiItem;
+import de.steamwar.inventory.SWItem;
+import de.steamwar.inventory.SWListInv;
+import de.steamwar.linkage.Linked;
+import lombok.Getter;
+import org.bukkit.Bukkit;
+import org.bukkit.Material;
+import org.bukkit.NamespacedKey;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.entity.EntityDamageEvent;
+import org.bukkit.event.inventory.InventoryClickEvent;
+import org.bukkit.event.inventory.InventoryCloseEvent;
+import org.bukkit.event.inventory.InventoryDragEvent;
+import org.bukkit.event.player.PlayerMoveEvent;
+import org.bukkit.event.player.PlayerQuitEvent;
+import org.bukkit.inventory.Inventory;
+import org.bukkit.inventory.InventoryView;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+import org.bukkit.persistence.PersistentDataType;
+
+import java.util.*;
+
+@Linked
+public class BauGuiEditor implements Listener {
+
+ @Getter
+ private static final List open_Edits = new ArrayList<>();
+
+ public static void openGuiEditor(Player p, ItemStack cursor) {
+ BauGuiMapping mapping = BauGuiMapping.getGuiMapping(p);
+ Inventory inv = Bukkit.createInventory(null, mapping.getSize() + 9, BauSystem.MESSAGE.parse("GUI_EDITOR_TITLE", p));
+ for (Map.Entry e : mapping.getMapping().entrySet()) {
+ if (e.getValue() >= 0) {
+ if (e.getValue() < mapping.getSize()) {
+ inv.setItem(e.getValue(), addId(BauGUI.getITEMS().get(e.getKey()).getItem(p), e.getKey()));
+ }
+ }
+ }
+
+ for (int j = mapping.getSize(); j < mapping.getSize() + 9; j++) {
+ inv.setItem(j, new SWItem(Material.WHITE_STAINED_GLASS_PANE, "").getItemStack());
+ }
+
+ inv.setItem(mapping.getSize() + 3, new SWItem(mapping.getSize() == 9 * 5 ? Material.GRAY_STAINED_GLASS_PANE : Material.LIME_STAINED_GLASS_PANE, BauSystem.MESSAGE.parse("GUI_EDITOR_ITEM_ROW_P", p)).getItemStack());
+ inv.setItem(mapping.getSize() + 2, new SWItem(mapping.getSize() == 9 ? Material.GRAY_STAINED_GLASS_PANE : Material.RED_STAINED_GLASS_PANE, BauSystem.MESSAGE.parse("GUI_EDITOR_ITEM_ROW_M", p)).getItemStack());
+
+ inv.setItem(mapping.getSize() + 5, new SWItem(Material.BARRIER, BauSystem.MESSAGE.parse("GUI_EDITOR_ITEM_TRASH", p), Arrays.asList(BauSystem.MESSAGE.parse("GUI_EDITOR_ITEM_TRASH_LORE", p)), false, clickType -> {
+ }).getItemStack());
+ inv.setItem(mapping.getSize() + 6, new SWItem(Material.SCUTE, BauSystem.MESSAGE.parse("GUI_EDITOR_ITEM_MORE", p)).getItemStack());
+ inv.setItem(mapping.getSize() + 8, new SWItem(Material.ARROW, BauSystem.MESSAGE.parse("GUI_EDITOR_ITEM_CLOSE", p)).getItemStack());
+
+ p.openInventory(inv);
+ p.getOpenInventory().setCursor(cursor == null ? new SWItem().getItemStack() : cursor);
+ open_Edits.add(p);
+ BauGuiMapping.startWatchdog();
+ }
+
+ private static ItemStack addId(ItemStack itemStack, int id) {
+ ItemMeta meta = itemStack.getItemMeta();
+ meta.getPersistentDataContainer().set(new NamespacedKey(BauSystem.getInstance(), "gui-item-id"), PersistentDataType.INTEGER, id);
+ itemStack.setItemMeta(meta);
+ return itemStack;
+ }
+
+ private static int getId(ItemStack itemStack) {
+ ItemMeta meta = itemStack.getItemMeta();
+ return meta.getPersistentDataContainer().get(new NamespacedKey(BauSystem.getInstance(), "gui-item-id"), PersistentDataType.INTEGER);
+ }
+
+ @EventHandler
+ public void onInventoryClick(InventoryClickEvent event) {
+ if (!open_Edits.contains(event.getWhoClicked())) {
+ return;
+ }
+
+ ItemStack i = event.getCurrentItem();
+ Player p = (Player) event.getWhoClicked();
+ BauGuiMapping mapping = BauGuiMapping.getGuiMapping(p);
+ if (event.getClickedInventory() == p.getInventory()) {
+ event.setCancelled(true);
+ }
+
+ if (event.getHotbarButton() != -1) {
+ event.setCancelled(true);
+ }
+
+ if (i == null) {
+ return;
+ }
+
+ if (event.getSlot() - mapping.getSize() >= 0) {
+ event.setCancelled(true);
+ }
+
+ switch (event.getSlot() - mapping.getSize()) {
+ case 2:
+ if (mapping.getSize() == 9) {
+ return;
+ }
+ saveMapping(p);
+ mapping.setSize(mapping.getSize() - 9);
+ openGuiEditor(p, event.getCursor());
+ break;
+ case 3:
+ if (mapping.getSize() == 9 * 5) {
+ return;
+ }
+ saveMapping(p);
+ mapping.setSize(mapping.getSize() + 9);
+ openGuiEditor(p, event.getCursor());
+ break;
+ case 5:
+ event.getView().setCursor(new SWItem().getItemStack());
+ break;
+ case 6:
+ saveMapping(p);
+ List> items = new ArrayList<>();
+ for (BauGuiItem item : BauGUI.getITEMS().values()) {
+ if (mapping.isShown(item.getId())) {
+ continue;
+ }
+ SWItem ip = new SWItem();
+ ip.setItemStack(item.getItem(p));
+ items.add(new SWListInv.SWListEntry(ip, item));
+ }
+ if (items.isEmpty()) {
+ return;
+ }
+ SWListInv inv = new SWListInv<>(p, BauSystem.MESSAGE.parse("GUI_EDITOR_TITLE_MORE", p), items, (clickType, item) -> {
+ openGuiEditor(p, addId(item.getItem(p), item.getId()));
+ });
+ inv.open();
+ break;
+ case 8:
+ saveMapping(p);
+ BauGUI.openBauGui(p);
+ break;
+ default:
+ }
+ }
+
+ @EventHandler
+ public void onEntityDamage(EntityDamageEvent event) {
+ if (open_Edits.contains(event.getEntity())) {
+ event.setCancelled(true);
+ }
+ }
+
+ @EventHandler
+ public void onPlayerMove(PlayerMoveEvent event) {
+ if (open_Edits.contains(event.getPlayer())) {
+ event.setCancelled(true);
+ }
+ }
+
+ @EventHandler
+ public void onPlayerQuit(PlayerQuitEvent event) {
+ open_Edits.remove(event.getPlayer());
+ }
+
+ @EventHandler
+ public void onInventoryDrag(InventoryDragEvent event) {
+ if (open_Edits.contains(event.getWhoClicked())) {
+ if (event.getRawSlots().stream().anyMatch(integer -> event.getView().getInventory(integer) == event.getWhoClicked().getInventory())) {
+ event.setCancelled(true);
+ }
+ }
+ }
+
+ private void saveMapping(Player p) {
+ saveMapping(p.getOpenInventory(), BauGuiMapping.getGuiMapping(p));
+ }
+
+ private void saveMapping(InventoryView view, BauGuiMapping mapping) {
+ if (mapping.isSaved()) {
+ return;
+ }
+ HashMap newMapping = new HashMap<>();
+
+ for (int i = 0; i < view.getTopInventory().getContents().length; i++) {
+ ItemStack itemStack = view.getTopInventory().getContents()[i];
+ if (itemStack == null || itemStack.getType() == Material.AIR || i >= mapping.getSize()) continue;
+ newMapping.put(getId(itemStack), i);
+ }
+
+ for (Map.Entry e : BauGUI.getITEMS().entrySet()) {
+ if (!newMapping.containsKey(e.getKey())) {
+ newMapping.put(e.getKey(), -1);
+ }
+ }
+
+ mapping.setMapping(newMapping);
+ mapping.save();
+ }
+
+ @EventHandler
+ public void onInventoryClose(InventoryCloseEvent event) {
+ if (!open_Edits.contains(event.getPlayer())) {
+ return;
+ }
+
+ Player p = (Player) event.getPlayer();
+
+ saveMapping(event.getView(), BauGuiMapping.getGuiMapping(p));
+ open_Edits.remove(p);
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/gui/editor/BauGuiEditorGuiItem.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/gui/editor/BauGuiEditorGuiItem.java
new file mode 100644
index 00000000..b1a76041
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/gui/editor/BauGuiEditorGuiItem.java
@@ -0,0 +1,54 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2021 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.gui.editor;
+
+import de.steamwar.bausystem.BauSystem;
+import de.steamwar.bausystem.Permission;
+import de.steamwar.bausystem.linkage.specific.BauGuiItem;
+import de.steamwar.inventory.SWItem;
+import de.steamwar.linkage.Linked;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.event.inventory.ClickType;
+import org.bukkit.inventory.ItemStack;
+
+@Linked
+public class BauGuiEditorGuiItem extends BauGuiItem {
+
+ public BauGuiEditorGuiItem() {
+ super(25);
+ }
+
+ @Override
+ public Permission permission() {
+ return Permission.MEMBER;
+ }
+
+ @Override
+ public ItemStack getItem(Player player) {
+ return new SWItem(Material.IRON_PICKAXE, BauSystem.MESSAGE.parse("GUI_EDITOR_ITEM_NAME", player)).getItemStack();
+ }
+
+ @Override
+ public boolean click(ClickType click, Player p) {
+ p.performCommand("gui editor");
+ return false;
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/gui/editor/BauGuiMapping.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/gui/editor/BauGuiMapping.java
new file mode 100644
index 00000000..90cc1fb4
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/gui/editor/BauGuiMapping.java
@@ -0,0 +1,138 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2021 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.gui.editor;
+
+import de.steamwar.bausystem.BauSystem;
+import de.steamwar.bausystem.configplayer.Config;
+import de.steamwar.bausystem.features.gui.BauGUI;
+import de.steamwar.bausystem.linkage.specific.BauGuiItem;
+import lombok.Getter;
+import lombok.Setter;
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+import org.bukkit.scheduler.BukkitTask;
+import yapion.hierarchy.types.YAPIONObject;
+
+import java.util.*;
+
+public class BauGuiMapping {
+
+ private static final HashMap fromUUID = new HashMap<>();
+ private static final List mappings = new ArrayList<>();
+
+ private static BukkitTask task;
+
+ public static void startWatchdog() {
+ if (task == null) {
+ Bukkit.getScheduler().runTaskTimer(BauSystem.getInstance(), bukkitTask -> {
+ mappings.forEach(BauGuiMapping::tick);
+ if (BauGuiEditor.getOpen_Edits().isEmpty()) {
+ bukkitTask.cancel();
+ task = null;
+ }
+ }, 1, 1);
+ }
+ }
+
+ @Getter
+ private final YAPIONObject object;
+ @Getter
+ private final Player owner;
+ @Getter
+ @Setter
+ private boolean saved;
+
+ protected BauGuiMapping(YAPIONObject object, Player p) {
+ this.object = object;
+ this.owner = p;
+ fromUUID.put(p.getUniqueId(), this);
+ mappings.add(this);
+ }
+
+ public static BauGuiMapping getGuiMapping(Player p) {
+ BauGuiMapping mapping = fromUUID.get(p.getUniqueId());
+
+ if (mapping == null) {
+ YAPIONObject yapionObject = Config.getInstance().get(p);
+ mapping = new BauGuiMapping(yapionObject.getObject("baugui"), p);
+ }
+
+ return mapping;
+ }
+
+ public boolean isShown(int id) {
+ return object.getPlainValueOrDefault(Integer.toString(id), -1) >= 0;
+ }
+
+ public int getSlot(int id) {
+ return object.getPlainValue(Integer.toString(id));
+ }
+
+ public Map getMapping() {
+ return internalReadMap();
+ }
+
+ public void setMapping(Map mapping) {
+ internalWriteMap(mapping);
+ }
+
+ public int getSize() {
+ return object.getPlainValueOrDefault("size", 45);
+ }
+
+ public void setSize(int size) {
+ if (size % 9 != 0) {
+ return;
+ }
+
+ object.add("size", size);
+ }
+
+ private void tick() {
+ this.saved = false;
+ }
+
+ public void save() {
+ if (saved) return;
+ YAPIONObject config = Config.getInstance().get(owner);
+ config.add("baugui", object);
+ Config.getInstance().save(owner);
+ saved = true;
+ }
+
+ private Map internalReadMap() {
+ Map map = new HashMap<>();
+ for (Map.Entry e : BauGUI.getITEMS().entrySet()) {
+ Integer value = object.getPlainValueOrDefault(e.getKey().toString(), -1);
+ if (value == -1) continue;
+ map.put(e.getKey(), value);
+ }
+ return map;
+ }
+
+ private void internalWriteMap(Map map) {
+ for (Map.Entry e : BauGUI.getITEMS().entrySet()) {
+ object.remove(e.getKey().toString());
+ Integer value = map.getOrDefault(e.getKey(), -1);
+ if (value == -1) continue;
+ object.add(e.getKey().toString(), value);
+ }
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/hotbar/DefaultHotbar.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/hotbar/DefaultHotbar.java
new file mode 100644
index 00000000..9e374ee4
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/hotbar/DefaultHotbar.java
@@ -0,0 +1,93 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2021 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.hotbar;
+
+import de.steamwar.bausystem.configplayer.Config;
+import de.steamwar.core.Core;
+import lombok.experimental.UtilityClass;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import yapion.hierarchy.types.YAPIONArray;
+import yapion.hierarchy.types.YAPIONObject;
+import yapion.hierarchy.types.YAPIONValue;
+import yapion.parser.YAPIONParser;
+import yapion.serializing.YAPIONDeserializer;
+import yapion.serializing.YAPIONSerializer;
+
+import java.util.HashSet;
+import java.util.Set;
+
+@UtilityClass
+public class DefaultHotbar {
+
+ private final YAPIONArray DEFAULT_HOTBAR = YAPIONParser.parse("{[{@type(org.bukkit.inventory.ItemStack)v(2230)type(WOODEN_AXE)meta{@type(org.bukkit.inventory.meta.ItemMeta)meta-type(UNSPECIFIC)display-name(WorldEdit Wand)lore{@type(java.util.ArrayList)values[Left click: select pos #1,Right click: select pos #2]}}},{@type(org.bukkit.inventory.ItemStack)v(2230)type(COMPASS)meta{@type(org.bukkit.inventory.meta.ItemMeta)meta-type(UNSPECIFIC)display-name(Navigation Wand)lore{@type(java.util.ArrayList)values[Left click: jump to location,Right click: pass through walls]}}},null,null,null,null,null,null,{@type(org.bukkit.inventory.ItemStack)v(2230)type(NETHER_STAR)meta{@type(org.bukkit.inventory.meta.ItemMeta)meta-type(UNSPECIFIC)display-name(§eBau GUI)}},null,null,{@type(org.bukkit.inventory.ItemStack)v(3117)type(ELYTRA)},null]}").getArray("");
+
+ public void updateHotbar(Player p) {
+ ItemStack[] hotbar = new ItemStack[13];
+ System.arraycopy(p.getInventory().getContents(), 0, hotbar, 0, 9);
+ System.arraycopy(p.getInventory().getArmorContents(), 0, hotbar, 9, 4);
+ YAPIONArray yapionArray = new YAPIONArray();
+ for (ItemStack itemStack : hotbar) {
+ if (itemStack != null) {
+ yapionArray.add(YAPIONSerializer.serialize(itemStack));
+ } else {
+ yapionArray.add(new YAPIONValue<>(null));
+ }
+ }
+ Config.getInstance().get(p).add("hotbar-" + Core.getVersion(), yapionArray);
+ Config.getInstance().save(p);
+ }
+
+ public void setHotbar(Player p) {
+ ItemStack[] hotbar = getItems(p);
+ ItemStack[] inv = p.getInventory().getContents();
+ ItemStack[] armor = p.getInventory().getArmorContents();
+ System.arraycopy(hotbar, 0, inv, 0, 9);
+ System.arraycopy(hotbar, 9, armor, 0, 4);
+ p.getInventory().setContents(inv);
+ p.getInventory().setArmorContents(armor);
+ }
+
+ public ItemStack[] getItems(Player p) {
+ Config.getInstance().get(p).remap("hotbar", "hotbar-19");
+ YAPIONArray yapionArray = Config.getInstance().get(p).getYAPIONArrayOrSetDefault("hotbar-" + Core.getVersion(), defaultHotbar());
+ ItemStack[] hotbar = new ItemStack[13];
+ Set invalid = new HashSet<>();
+ yapionArray.forEach((integer, yapionAnyType) -> {
+ if (yapionAnyType instanceof YAPIONValue) {
+ hotbar[integer] = null;
+ } else {
+ try {
+ hotbar[integer] = YAPIONDeserializer.deserialize((YAPIONObject) yapionAnyType);
+ } catch (Exception e) {
+ invalid.add(integer);
+ hotbar[integer] = null;
+ }
+ }
+ });
+ invalid.forEach(i -> yapionArray.set(i, new YAPIONValue<>(null)));
+ if (!invalid.isEmpty()) Config.getInstance().save(p);
+ return hotbar;
+ }
+
+ public YAPIONArray defaultHotbar() {
+ return DEFAULT_HOTBAR;
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/hotbar/HotbarCommand.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/hotbar/HotbarCommand.java
new file mode 100644
index 00000000..77a13716
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/hotbar/HotbarCommand.java
@@ -0,0 +1,60 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2021 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.hotbar;
+
+import de.steamwar.bausystem.BauSystem;
+import de.steamwar.command.SWCommand;
+import de.steamwar.inventory.SWInventory;
+import de.steamwar.linkage.Linked;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+
+@Linked
+public class HotbarCommand extends SWCommand {
+
+ public HotbarCommand() {
+ super("hotbar", "hb");
+ addDefaultHelpMessage("HOTBAR_HELP_GENERIC");
+ }
+
+ @Register(value = "load", description = "HOTBAR_HELP_LOAD")
+ public void loadHotbar(@Validator Player p) {
+ DefaultHotbar.setHotbar(p);
+ BauSystem.MESSAGE.send("HOTBAR_LOADED", p);
+ }
+
+ @Register(value = "save", description = "HOTBAR_HELP_SAVE")
+ public void saveHotbar(Player p) {
+ DefaultHotbar.updateHotbar(p);
+ BauSystem.MESSAGE.send("HOTBAR_SAVED", p);
+ }
+
+ @Register(value = "show", description = "HOTBAR_HELP_SHOW")
+ public void showHotbar(Player p) {
+ SWInventory inv = new SWInventory(p, 18, BauSystem.MESSAGE.parse("HOTBAR_INVENTORY", p));
+ ItemStack[] hotbar = DefaultHotbar.getItems(p);
+ for (int i = 0; i < hotbar.length; i++) {
+ if (hotbar[i] == null) continue;
+ inv.setItem(i, hotbar[i], clickType -> {
+ });
+ }
+ inv.open();
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/hotbar/HotbarListener.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/hotbar/HotbarListener.java
new file mode 100644
index 00000000..8a4c78de
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/hotbar/HotbarListener.java
@@ -0,0 +1,48 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2021 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.hotbar;
+
+import de.steamwar.bausystem.Permission;
+import de.steamwar.linkage.Linked;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.PlayerJoinEvent;
+
+@Linked
+public class HotbarListener implements Listener {
+
+ @EventHandler(priority = EventPriority.LOWEST)
+ public void onPlayerJoin(PlayerJoinEvent event) {
+ if(!Permission.BUILD.hasPermission(event.getPlayer())) return;
+ if (allNull(event.getPlayer().getInventory().getContents()) && allNull(event.getPlayer().getInventory().getArmorContents())) {
+ DefaultHotbar.setHotbar(event.getPlayer());
+ }
+ }
+
+ private boolean allNull(Object[] arr) {
+ for (Object o : arr) {
+ if (o != null) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/inventoryfiller/InventoryFillBauGuiItem.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/inventoryfiller/InventoryFillBauGuiItem.java
new file mode 100644
index 00000000..7e94710a
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/inventoryfiller/InventoryFillBauGuiItem.java
@@ -0,0 +1,42 @@
+package de.steamwar.bausystem.features.inventoryfiller;
+
+import de.steamwar.bausystem.BauSystem;
+import de.steamwar.bausystem.Permission;
+import de.steamwar.bausystem.configplayer.Config;
+import de.steamwar.bausystem.linkage.specific.BauGuiItem;
+import de.steamwar.inventory.SWItem;
+import de.steamwar.linkage.Linked;
+import de.steamwar.linkage.LinkedInstance;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.event.inventory.ClickType;
+import org.bukkit.inventory.ItemStack;
+
+import java.util.Collections;
+
+@Linked
+public class InventoryFillBauGuiItem extends BauGuiItem {
+ public InventoryFillBauGuiItem() {
+ super(34);
+ }
+
+ @LinkedInstance
+ public InventoryFillerCommand command;
+
+ @Override
+ public Permission permission() {
+ return Permission.MEMBER;
+ }
+
+ @Override
+ public ItemStack getItem(Player player) {
+ String loreKey = Config.getInstance().get(player).getPlainValueOrDefault("inventoryfill", false) ? "OTHER_ITEMS_INVENTORY_FILL_LORE_ACTIVE" : "OTHER_ITEMS_INVENTORY_FILL_LORE_INACTIVE";
+ return new SWItem(Material.HOPPER, BauSystem.MESSAGE.parse("OTHER_ITEMS_INVENTORY_FILL_NAME", player), Collections.singletonList(BauSystem.MESSAGE.parse(loreKey, player)), false, clickType -> {}).getItemStack();
+ }
+
+ @Override
+ public boolean click(ClickType click, Player p) {
+ command.toggle(p);
+ return false;
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/inventoryfiller/InventoryFiller.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/inventoryfiller/InventoryFiller.java
new file mode 100644
index 00000000..d69e3410
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/inventoryfiller/InventoryFiller.java
@@ -0,0 +1,124 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2022 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.inventoryfiller;
+
+import de.steamwar.bausystem.BauSystem;
+import de.steamwar.bausystem.Permission;
+import de.steamwar.bausystem.configplayer.Config;
+import de.steamwar.linkage.Linked;
+import net.md_5.bungee.api.ChatMessageType;
+import org.bukkit.Material;
+import org.bukkit.block.Block;
+import org.bukkit.block.Container;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.PlayerDropItemEvent;
+import org.bukkit.event.player.PlayerItemHeldEvent;
+import org.bukkit.inventory.Inventory;
+import org.bukkit.inventory.ItemStack;
+
+@Linked
+public class InventoryFiller implements Listener {
+
+ @EventHandler
+ public void onPlayerDropItem(PlayerDropItemEvent event) {
+ if(!Permission.BUILD.hasPermission(event.getPlayer())) return;
+ if (!Config.getInstance().get(event.getPlayer()).getPlainValueOrDefault("inventoryfill", false)) return;
+ if (!event.getPlayer().isSneaking()) return;
+ Block block = event.getPlayer().getTargetBlockExact(5);
+ if (block == null) return;
+ if (!(block.getState() instanceof Container)) return;
+ Container container = (Container) block.getState();
+ Inventory inventory = container.getInventory();
+ ItemStack itemStack = event.getItemDrop().getItemStack().clone();
+ itemStack.setAmount(itemStack.getType().getMaxStackSize());
+ event.setCancelled(true);
+ for (int i = 0; i < inventory.getSize(); i++) {
+ inventory.setItem(i, itemStack);
+ }
+ BauSystem.MESSAGE.sendPrefixless("CHESTFILLER_FILLED", event.getPlayer(), ChatMessageType.ACTION_BAR);
+ }
+
+ /**
+ * For MacOS user: https://www.curseforge.com/minecraft/mc-mods/shift-scroll-fix
+ */
+ @EventHandler
+ public void onPlayerItemHeld(PlayerItemHeldEvent event) {
+ if(!Permission.BUILD.hasPermission(event.getPlayer())) return;
+ if (!Config.getInstance().get(event.getPlayer()).getPlainValueOrDefault("inventoryfill", false)) return;
+ if (!event.getPlayer().isSneaking()) return;
+ ItemStack itemStack = event.getPlayer().getInventory().getItemInMainHand();
+ if (itemStack.getType() == Material.AIR) return;
+ Block block = event.getPlayer().getTargetBlockExact(5);
+ if (block == null) return;
+ if (!(block.getState() instanceof Container)) return;
+ Container container = (Container) block.getState();
+ Inventory inventory = container.getInventory();
+ event.setCancelled(true);
+
+ int upperBound = event.getPreviousSlot() + 4;
+ if (upperBound > 8) upperBound -= 8;
+ int lowerBound = event.getPreviousSlot() - 5;
+ if (lowerBound < 0) lowerBound += 8;
+
+ int direction = 0;
+ if (event.getPreviousSlot() != 0 && (event.getNewSlot() > event.getPreviousSlot() || (upperBound < event.getPreviousSlot() && event.getNewSlot() < upperBound))) {
+ direction = 1;
+ } else if (event.getPreviousSlot() == 0 && event.getNewSlot() > upperBound) {
+ direction = -1;
+ } else if (event.getNewSlot() < event.getPreviousSlot() || (lowerBound > event.getPreviousSlot() && event.getNewSlot() >= lowerBound)) {
+ direction = -1;
+ } else if (event.getPreviousSlot() == 0) {
+ direction = 1;
+ }
+
+ int count = 0;
+ int emptySlot = -1;
+ int filledSlot = -1;
+ for (int i = 0; i < inventory.getSize(); i++) {
+ ItemStack current = inventory.getItem(i);
+ if (current == null) {
+ if (emptySlot == -1) emptySlot = i;
+ continue;
+ }
+ if (current.getType() == itemStack.getType()) {
+ count += current.getAmount();
+ int emptyAmount = itemStack.getType().getMaxStackSize() - current.getAmount();
+ if (direction == -1) filledSlot = Math.max(filledSlot, i);
+ if (emptyAmount > 0) filledSlot = Math.min(i, filledSlot);
+ if (emptyAmount > 0 && filledSlot == -1) filledSlot = i;
+ }
+ }
+
+ int slotToUse = direction == -1 ? filledSlot : (filledSlot == -1 ? emptySlot : filledSlot);
+ if (slotToUse == -1) return;
+
+ ItemStack current = inventory.getItem(slotToUse);
+ if (current == null) {
+ ItemStack now = itemStack.clone();
+ now.setAmount(1);
+ inventory.setItem(slotToUse, now);
+ } else {
+ current.setAmount(current.getAmount() + direction);
+ }
+
+ BauSystem.MESSAGE.sendPrefixless("CHESTFILLER_COUNT", event.getPlayer(), ChatMessageType.ACTION_BAR, itemStack.getType(), count + direction);
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/inventoryfiller/InventoryFillerCommand.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/inventoryfiller/InventoryFillerCommand.java
new file mode 100644
index 00000000..4804a9d7
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/inventoryfiller/InventoryFillerCommand.java
@@ -0,0 +1,28 @@
+package de.steamwar.bausystem.features.inventoryfiller;
+
+import de.steamwar.bausystem.BauSystem;
+import de.steamwar.bausystem.SWUtils;
+import de.steamwar.bausystem.configplayer.Config;
+import de.steamwar.command.SWCommand;
+import de.steamwar.linkage.Linked;
+import org.bukkit.entity.Player;
+
+@Linked
+public class InventoryFillerCommand extends SWCommand {
+
+ public InventoryFillerCommand() {
+ super("inventoryfill");
+ }
+
+ @Register(description = {"INVENTORY_FILL_HELP", "INVENTORY_FILL_INFO"})
+ public void toggle(Player player) {
+ boolean inventoryFill = Config.getInstance().get(player).getPlainValueOrDefault("inventoryfill", false);
+ Config.getInstance().get(player).put("inventoryfill", !inventoryFill);
+ if (!inventoryFill) {
+ SWUtils.sendToActionbar(player, BauSystem.MESSAGE.parse("INVENTORY_FILL_ENABLE", player));
+ BauSystem.MESSAGE.send("INVENTORY_FILL_INFO", player);
+ }else {
+ SWUtils.sendToActionbar(player, BauSystem.MESSAGE.parse("INVENTORY_FILL_DISABLE", player));
+ }
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/killchecker/Cuboid.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/killchecker/Cuboid.java
new file mode 100644
index 00000000..08632a9c
--- /dev/null
+++ b/BauSystem/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/BauSystem_Main/src/de/steamwar/bausystem/features/killchecker/KillcheckerCommand.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/killchecker/KillcheckerCommand.java
new file mode 100644
index 00000000..9ca7a480
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/killchecker/KillcheckerCommand.java
@@ -0,0 +1,115 @@
+/*
+ * 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 de.steamwar.bausystem.BauSystem;
+import de.steamwar.bausystem.region.Region;
+import de.steamwar.bausystem.utils.BauMemberUpdateEvent;
+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;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.block.BlockBreakEvent;
+import org.bukkit.event.block.BlockPlaceEvent;
+import org.bukkit.event.player.PlayerQuitEvent;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+@Linked
+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(@Validator Player player, @OptionalValue("-outline") @StaticValue(value = {"-area", "-outline"}, allowISE = true) boolean onlyOutline) {
+ Region region = Region.getRegion(player.getLocation());
+ KillcheckerVisualizer killcheckerVisualizer = visualizers.computeIfAbsent(region, region1 -> new KillcheckerVisualizer(region1, bossBarService));
+ killcheckerVisualizer.recalc();
+ killcheckerVisualizer.show(player, onlyOutline);
+ BauSystem.MESSAGE.send("KILLCHECKER_ENABLE", player);
+ }
+
+ @Register(value = "disable", description = "KILLCHECKER_HELP_DISABLE")
+ public void disableCommand(Player player) {
+ Region region = Region.getRegion(player.getLocation());
+ KillcheckerVisualizer killcheckerVisualizer = visualizers.get(region);
+ if (killcheckerVisualizer != null) {
+ if (killcheckerVisualizer.hide(player)) {
+ visualizers.remove(region);
+ }
+ }
+ BauSystem.MESSAGE.send("KILLCHECKER_DISABLE", player);
+ }
+
+ @EventHandler
+ public void onBauMemberUpdate(BauMemberUpdateEvent event) {
+ event.getNewSpectator().forEach(this::hide);
+ }
+
+ @EventHandler
+ public void onPlayerQuit(PlayerQuitEvent event) {
+ hide(event.getPlayer());
+ }
+
+ private void hide(Player player) {
+ new HashSet<>(visualizers.entrySet()).forEach(regionKillcheckerVisualizerEntry -> {
+ if (regionKillcheckerVisualizerEntry.getValue().hide(player)) {
+ visualizers.remove(regionKillcheckerVisualizerEntry.getKey());
+ }
+ });
+ }
+
+ private void recalc(Block block) {
+ Region region = Region.getRegion(block.getLocation());
+ KillcheckerVisualizer killcheckerVisualizer = visualizers.get(region);
+ if (killcheckerVisualizer != null) {
+ killcheckerVisualizer.recalc();
+ }
+ }
+
+ @EventHandler
+ public void onBlockPlace(BlockPlaceEvent event) {
+ recalc(event.getBlock());
+ }
+
+ @EventHandler
+ public void onBlockBreak(BlockBreakEvent event) {
+ Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
+ recalc(event.getBlock());
+ }, 1);
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/killchecker/KillcheckerVisualizer.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/killchecker/KillcheckerVisualizer.java
new file mode 100644
index 00000000..04822aa8
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/killchecker/KillcheckerVisualizer.java
@@ -0,0 +1,390 @@
+/*
+ * 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 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;
+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.entity.Player;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+public class KillcheckerVisualizer {
+
+ private static final Material[] MATERIALS = new Material[]{
+ Material.LIME_STAINED_GLASS,
+ Material.LIME_CONCRETE,
+ Material.GREEN_STAINED_GLASS,
+ Material.GREEN_CONCRETE,
+ Material.YELLOW_STAINED_GLASS,
+ Material.YELLOW_CONCRETE,
+ Material.ORANGE_STAINED_GLASS,
+ Material.ORANGE_CONCRETE,
+ Material.RED_STAINED_GLASS,
+ Material.RED_CONCRETE,
+ Material.PURPLE_STAINED_GLASS,
+ Material.PURPLE_CONCRETE,
+ Material.BLACK_STAINED_GLASS,
+ Material.BLACK_CONCRETE,
+ };
+ private static final World WORLD = Bukkit.getWorlds().get(0);
+
+ private static final double SURROUND = 4.5;
+
+ private final Point minPoint;
+ private final Point maxPoint;
+
+ private final int yArea;
+ private final int zArea;
+ private final int xArea;
+
+ 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 final REntityServer outline = new REntityServer();
+ private final REntityServer inner = new REntityServer();
+
+ 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(); x++) {
+ for (int y = minPoint.getY(); y < maxPoint.getY(); y++) {
+ 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++) {
+ for (int dy = (int) cuboid.getY(); dy <= cuboid.getDy(); dy++) {
+ for (int dz = (int) cuboid.getZ(); dz <= cuboid.getDz(); dz++) {
+ points.add(new Point(dx, dy, dz));
+ }
+ }
+ }
+ }
+ }
+ }
+ cannonCount = cuboids.size();
+
+ Map kill = new HashMap<>();
+ 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() - SURROUND && x < cuboid.getDx() + SURROUND && z >= cuboid.getZ() - SURROUND && z < cuboid.getDz() + SURROUND) {
+ cuboidSet.add(cuboid);
+ }
+ }
+ if (cuboidSet.size() > 1) {
+ 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()));
+ }
+ }
+ }
+
+ 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() - SURROUND && y < cuboid.getDy() + SURROUND && z >= cuboid.getZ() - SURROUND && z < cuboid.getDz() + SURROUND) {
+ cuboidSet.add(cuboid);
+ }
+ }
+ if (cuboidSet.size() > 1) {
+ 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() + 1, y, z);
+ xPoints.add(p2);
+ kill.put(p2, Math.max(kill.getOrDefault(p2, 0), cuboidSet.size()));
+ }
+ }
+ }
+
+ 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() - SURROUND && x < cuboid.getDx() + SURROUND && y >= cuboid.getY() - SURROUND && y < cuboid.getDy() + SURROUND) {
+ cuboidSet.add(cuboid);
+ }
+ }
+ if (cuboidSet.size() > 1) {
+ 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() + 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;
+ outline.getPlayers().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();
+ rEntities.remove(point);
+ killCount.remove(point);
+ }
+ }
+ kill.forEach((point, count) -> {
+ if (rEntities.containsKey(point)) {
+ if (killCount.get(point) == count && outlinePoints.contains(point) == outlinePointsCacheLast.contains(point)) return;
+ rEntities.get(point).die();
+ }
+ 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(Math.min(Math.max(percent, 0), 1));
+
+ 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<>();
+ points.add(new Point(x, y, z));
+ while (!points.isEmpty()) {
+ Point point = points.iterator().next();
+ points.remove(point);
+ if (!checked.add(point)) continue;
+ if (point.getX() < minPoint.getX() || point.getX() > maxPoint.getX()) continue;
+ if (point.getY() < minPoint.getY() || point.getY() > maxPoint.getY()) continue;
+ if (point.getZ() < minPoint.getZ() || point.getZ() > maxPoint.getZ()) continue;
+
+ if (WORLD.getBlockAt(point.getX() + 1, point.getY(), point.getZ()).getType() == type) {
+ points.add(new Point(point.getX() + 1, point.getY(), point.getZ()));
+ }
+ if (WORLD.getBlockAt(point.getX(), point.getY() + 1, point.getZ()).getType() == type) {
+ points.add(new Point(point.getX(), point.getY() + 1, point.getZ()));
+ }
+ if (WORLD.getBlockAt(point.getX(), point.getY(), point.getZ() + 1).getType() == type) {
+ points.add(new Point(point.getX(), point.getY(), point.getZ() + 1));
+ }
+ if (WORLD.getBlockAt(point.getX() - 1, point.getY(), point.getZ()).getType() == type) {
+ points.add(new Point(point.getX() - 1, point.getY(), point.getZ()));
+ }
+ if (WORLD.getBlockAt(point.getX(), point.getY() - 1, point.getZ()).getType() == type) {
+ points.add(new Point(point.getX(), point.getY() - 1, point.getZ()));
+ }
+ if (WORLD.getBlockAt(point.getX(), point.getY(), point.getZ() - 1).getType() == type) {
+ points.add(new Point(point.getX(), point.getY(), point.getZ() - 1));
+ }
+ }
+
+ int minX = Integer.MAX_VALUE;
+ int maxX = Integer.MIN_VALUE;
+ int minY = Integer.MAX_VALUE;
+ int maxY = Integer.MIN_VALUE;
+ int minZ = Integer.MAX_VALUE;
+ int maxZ = Integer.MIN_VALUE;
+ for (Point point : checked) {
+ if (point.getX() < minX) minX = point.getX();
+ if (point.getX() > maxX) maxX = point.getX();
+ if (point.getY() < minY) minY = point.getY();
+ if (point.getY() > maxY) maxY = point.getY();
+ if (point.getZ() < minZ) minZ = point.getZ();
+ if (point.getZ() > maxZ) maxZ = point.getZ();
+ }
+
+ return new Cuboid(minX, minY, minZ, maxX, maxY, maxZ);
+ }
+
+ public void show(Player player, boolean onlyOutline) {
+ outline.addPlayer(player);
+ if (!onlyOutline) {
+ inner.addPlayer(player);
+ } else {
+ inner.removePlayer(player);
+ }
+ updateBossBar(player);
+ }
+
+ public boolean hide(Player player) {
+ outline.removePlayer(player);
+ inner.removePlayer(player);
+ bossBarService.remove(player, region, "killchecker");
+ if (outline.getPlayers().isEmpty() && inner.getPlayers().isEmpty()) {
+ outline.close();
+ inner.close();
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/Loader.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/Loader.java
new file mode 100644
index 00000000..d8489a0f
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/Loader.java
@@ -0,0 +1,380 @@
+/*
+ * 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.loader;
+
+import de.steamwar.bausystem.BauSystem;
+import de.steamwar.bausystem.Permission;
+import de.steamwar.bausystem.features.loader.elements.LoaderElement;
+import de.steamwar.bausystem.features.loader.elements.LoaderInteractionElement;
+import de.steamwar.bausystem.features.loader.elements.impl.LoaderTNT;
+import de.steamwar.bausystem.features.loader.elements.impl.LoaderWait;
+import de.steamwar.bausystem.shared.EnumDisplay;
+import de.steamwar.inventory.SWAnvilInv;
+import de.steamwar.inventory.SWItem;
+import de.steamwar.inventory.SWListInv;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import org.bukkit.Bukkit;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.inventory.ClickType;
+import org.bukkit.event.player.PlayerQuitEvent;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class Loader implements Listener {
+
+ private static final Map LOADER_MAP = new HashMap<>();
+
+ public static Loader getLoader(Player player) {
+ return LOADER_MAP.get(player);
+ }
+
+ public static void newLoader(Player player) {
+ LOADER_MAP.put(player, new Loader(player));
+ }
+
+ private final Player p;
+
+ @Getter
+ private Stage stage = Stage.SETUP;
+ private LoaderRecorder recorder;
+
+ private List elements = new ArrayList<>();
+ private int currentElement = 0;
+ private long waitTime = 0;
+
+ public Loader(Player p) {
+ this.p = p;
+ this.recorder = new LoaderRecorder(p, elements);
+ Bukkit.getPluginManager().registerEvents(this, BauSystem.getInstance());
+
+ BauSystem.runTaskTimer(BauSystem.getInstance(), () -> {
+ if (stage != Stage.RUNNING) return;
+ if(!Permission.BUILD.hasPermission(p)) return;
+ if (waitTime > 0) {
+ waitTime--;
+ return;
+ }
+ if (currentElement >= elements.size()) {
+ currentElement = 0;
+ if (stage == Stage.SINGLE) {
+ stage = Stage.PAUSE;
+ return;
+ }
+ }
+
+ while (currentElement < elements.size()) {
+ LoaderElement element = elements.get(currentElement);
+ currentElement++;
+ element.execute(delay -> waitTime = delay);
+ if (waitTime > 0) {
+ if (element instanceof LoaderTNT) currentElement--;
+ return;
+ }
+ }
+ }, 0, 1);
+ }
+
+ public void single() {
+ if (stage == Stage.END) return;
+ if (stage == Stage.RUNNING) return;
+ stage = Stage.SINGLE;
+ if (recorder != null) {
+ recorder.stop();
+ recorder = null;
+ }
+ if (elements.isEmpty()) {
+ BauSystem.MESSAGE.send("LOADER_NOTHING_RECORDED", p);
+ stop();
+ }
+ }
+
+ public void start() {
+ if (stage == Stage.END) return;
+ if (stage == Stage.RUNNING) return;
+ stage = Stage.RUNNING;
+ if (recorder != null) {
+ recorder.stop();
+ recorder = null;
+ }
+ if (elements.isEmpty()) {
+ BauSystem.MESSAGE.send("LOADER_NOTHING_RECORDED", p);
+ stop();
+ }
+ }
+
+ public void pause() {
+ if (stage == Stage.END) return;
+ if (stage == Stage.PAUSE) return;
+ stage = Stage.PAUSE;
+ if (recorder != null) {
+ recorder.stop();
+ recorder = null;
+ }
+ }
+
+ public void stop() {
+ stage = Stage.END;
+ if (recorder != null) {
+ recorder.stop();
+ recorder = null;
+ }
+ elements.clear();
+ LOADER_MAP.remove(p);
+ }
+
+ public boolean setTicksBetweenShots(int delay) {
+ if (elements.size() == 0) return false;
+ LoaderElement loaderElement = elements.get(elements.size() - 1);
+ if (loaderElement instanceof LoaderWait) {
+ ((LoaderWait) loaderElement).setDelay(delay);
+ return true;
+ }
+ return false;
+ }
+
+ public void setTicksBetweenBlocks(int delay) {
+ for (int i = 0; i < elements.size() - 1; i++) {
+ LoaderElement loaderElement = elements.get(i);
+ if (loaderElement instanceof LoaderWait) {
+ ((LoaderWait) loaderElement).setDelay(delay);
+ }
+ }
+ }
+
+ public void gui(SettingsSorting settingsSorting) {
+ List> list = new ArrayList<>();
+ AtomicBoolean allWait = new AtomicBoolean(true);
+ Runnable updateRunnable = () -> {
+ list.clear();
+ for (int i = 0; i < elements.size(); i++) {
+ LoaderElement previous = i > 0 ? elements.get(i - 1) : null;
+ LoaderElement current = elements.get(i);
+ LoaderElement next = i < elements.size() - 1 ? elements.get(i + 1) : null;
+
+ if (!settingsSorting.shouldShow(previous, current, next)) {
+ continue;
+ }
+
+ if ((!(current instanceof LoaderWait))) {
+ allWait.set(false);
+ }
+
+ SWItem item = current.menu(p);
+ if (current instanceof LoaderInteractionElement>) {
+ LoaderInteractionElement> interactionElement = (LoaderInteractionElement>) current;
+ List lore = new ArrayList<>();
+ if (item.getItemMeta() != null && item.getItemMeta().getLore() != null) {
+ lore.addAll(item.getItemMeta().getLore());
+ lore.add("§8");
+ }
+ lore.add(BauSystem.MESSAGE.parse("LOADER_SETTING_MODES", p, interactionElement.size()));
+ lore.add(BauSystem.MESSAGE.parse("LOADER_GUI_CLICK_TO_EDIT", p));
+ item.setLore(lore);
+ } else {
+ List lore = new ArrayList<>();
+ if (item.getItemMeta() != null && item.getItemMeta().getLore() != null) {
+ lore.addAll(item.getItemMeta().getLore());
+ lore.add("§8");
+ }
+ lore.add(BauSystem.MESSAGE.parse("LOADER_GUI_CLICK_TO_EDIT", p));
+ item.setLore(lore);
+ }
+ list.add(new SWListInv.SWListEntry<>(item, current));
+ }
+ if (list.isEmpty()) {
+ allWait.set(false);
+ }
+ };
+ updateRunnable.run();
+
+ SWListInv swListInv = new SWListInv<>(p, BauSystem.MESSAGE.parse("LOADER_GUI_TITLE", p), false, list, (clickType, loaderElement) -> {});
+ swListInv.setCallback((clickType, entry) -> entry.click(p, () -> {
+ updateRunnable.run();
+ swListInv.open();
+ }));
+
+ SWItem settingItem = new SWItem(settingsSorting.getMaterial(), "§e" + BauSystem.MESSAGE.parse(settingsSorting.getName(), p), clickType -> {
+ if (clickType == ClickType.LEFT) {
+ int index = settingsSorting.ordinal() + 1;
+ if (index >= SettingsSorting.LENGTH) {
+ index = 0;
+ }
+ gui(SettingsSorting.values()[index]);
+ } else if (clickType == ClickType.RIGHT) {
+ int index = settingsSorting.ordinal() - 1;
+ if (index < 0) {
+ index = SettingsSorting.LENGTH - 1;
+ }
+ gui(SettingsSorting.values()[index]);
+ }
+ });
+ List strings = new ArrayList<>();
+ for (SettingsSorting setting : SettingsSorting.values()) {
+ if (setting == settingsSorting) {
+ strings.add("§e> §7" + BauSystem.MESSAGE.parse(setting.getName(), p));
+ } else {
+ strings.add("§8> §7" + BauSystem.MESSAGE.parse(setting.getName(), p));
+ }
+ }
+ settingItem.setLore(strings);
+ swListInv.setItem(48, settingItem);
+
+ if (allWait.get()) {
+ SWItem setWait = new SWItem(Material.PAPER, BauSystem.MESSAGE.parse("LOADER_GUI_SHOW_WAITS_SET_ALL", p), clickType -> {
+ SWAnvilInv swAnvilInv = new SWAnvilInv(p, BauSystem.MESSAGE.parse("LOADER_GUI_SHOW_WAITS_TITLE", p), "");
+ swAnvilInv.setCallback(s -> {
+ try {
+ long delay = Math.max(Long.parseLong(s), 0);
+ list.forEach(loaderElementSWListEntry -> {
+ ((LoaderWait) loaderElementSWListEntry.getObject()).setDelay(delay);
+ });
+ } catch (NumberFormatException ignored) {
+ }
+ gui(settingsSorting);
+ });
+ swAnvilInv.open();
+ });
+ swListInv.setItem(50, setWait);
+ } else {
+ swListInv.setItem(50, new SWItem(Material.GRAY_STAINED_GLASS_PANE, "§8"));
+ }
+ swListInv.open();
+ }
+
+ @EventHandler
+ public void onPlayerQuit(PlayerQuitEvent event) {
+ if (event.getPlayer() != p) return;
+ stop();
+ }
+
+ public String getProgress() {
+ return Math.max(currentElement, 1) + "§8/§7" + elements.size();
+ }
+
+ public enum SettingsSorting {
+ ALL {
+ @Override
+ public Material getMaterial() {
+ return Material.STRUCTURE_VOID;
+ }
+
+ @Override
+ public String getName() {
+ return "LOADER_GUI_SHOW_ALL";
+ }
+
+ @Override
+ public boolean shouldShow(LoaderElement previous, LoaderElement current, LoaderElement next) {
+ return true;
+ }
+ },
+ WAIT {
+ @Override
+ public Material getMaterial() {
+ return Material.CLOCK;
+ }
+
+ @Override
+ public String getName() {
+ return "LOADER_GUI_SHOW_WAITS";
+ }
+
+ @Override
+ public boolean shouldShow(LoaderElement previous, LoaderElement current, LoaderElement next) {
+ return current instanceof LoaderWait;
+ }
+ },
+ WAIT_BETWEEN_TNT {
+ @Override
+ public Material getMaterial() {
+ return Material.REDSTONE_BLOCK;
+ }
+
+ @Override
+ public String getName() {
+ return "LOADER_GUI_SHOW_WAITS_BETWEEN_TNT";
+ }
+
+ @Override
+ public boolean shouldShow(LoaderElement previous, LoaderElement current, LoaderElement next) {
+ return previous instanceof LoaderTNT && current instanceof LoaderWait && next instanceof LoaderTNT;
+ }
+ },
+ INTERACTIONS {
+ @Override
+ public Material getMaterial() {
+ return Material.REPEATER;
+ }
+
+ @Override
+ public String getName() {
+ return "LOADER_GUI_SHOW_INTERACTIONS";
+ }
+
+ @Override
+ public boolean shouldShow(LoaderElement previous, LoaderElement current, LoaderElement next) {
+ return current instanceof LoaderInteractionElement && !(current instanceof LoaderTNT);
+ }
+ },
+ TNT {
+ @Override
+ public Material getMaterial() {
+ return Material.TNT;
+ }
+
+ @Override
+ public String getName() {
+ return "LOADER_GUI_SHOW_TNT";
+ }
+
+ @Override
+ public boolean shouldShow(LoaderElement previous, LoaderElement current, LoaderElement next) {
+ return current instanceof LoaderTNT;
+ }
+ },
+ ;
+
+ public static int LENGTH = SettingsSorting.values().length;
+
+ public abstract Material getMaterial();
+ public abstract String getName();
+ public abstract boolean shouldShow(LoaderElement previous, LoaderElement current, LoaderElement next);
+ }
+
+ @AllArgsConstructor
+ public enum Stage implements EnumDisplay {
+ SETUP("LOADER_SETUP"),
+ RUNNING("LOADER_RUNNING"),
+ SINGLE("LOADER_SINGLE"),
+ PAUSE("LOADER_PAUSE"),
+ END("LOADER_END");
+
+ @Getter
+ private final String chatValue;
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/LoaderCommand.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/LoaderCommand.java
new file mode 100644
index 00000000..021ad330
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/LoaderCommand.java
@@ -0,0 +1,122 @@
+/*
+ * 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.loader;
+
+import de.steamwar.bausystem.BauSystem;
+import de.steamwar.bausystem.utils.BauMemberUpdateEvent;
+import de.steamwar.command.SWCommand;
+import de.steamwar.linkage.Linked;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+
+@Linked
+public class LoaderCommand extends SWCommand implements Listener {
+
+ public LoaderCommand() {
+ super("loader");
+ }
+
+ private boolean loaderNullCheck(Loader loader, Player p) {
+ if (loader == null) {
+ BauSystem.MESSAGE.send("LOADER_NO_LOADER", p);
+ return true;
+ }
+ return false;
+ }
+
+ @Register(value = "setup", description = "LOADER_HELP_SETUP")
+ public void setupLoader(@Validator Player player) {
+ if (Loader.getLoader(player) != null) {
+ BauSystem.MESSAGE.send("LOADER_SETUP_STOP_FIRST", player);
+ return;
+ }
+ Loader.newLoader(player);
+ BauSystem.MESSAGE.send("LOADER_NEW", player);
+ BauSystem.MESSAGE.send("LOADER_HOW_TO_START", player);
+ }
+
+ @Register(value = "start", description = "LOADER_HELP_START")
+ public void startLoader(@Validator Player player) {
+ Loader loader = Loader.getLoader(player);
+ if (loaderNullCheck(loader, player)) return;
+ loader.start();
+ BauSystem.MESSAGE.send("LOADER_ACTIVE", player);
+ }
+
+ @Register(value = "stop", description = "LOADER_HELP_STOP")
+ public void stopLoader(@Validator Player player) {
+ Loader loader = Loader.getLoader(player);
+ if (loaderNullCheck(loader, player)) return;
+ loader.stop();
+ BauSystem.MESSAGE.send("LOADER_STOP", player);
+ }
+
+ @Register(value = "pause", description = "LOADER_HELP_PAUSE")
+ public void pauseLoader(@Validator Player player) {
+ Loader loader = Loader.getLoader(player);
+ if (loaderNullCheck(loader, player)) return;
+ loader.pause();
+ BauSystem.MESSAGE.send("LOADER_PAUSED", player);
+ }
+
+ @Register(value = "gui", description = "LOADER_HELP_GUI")
+ public void guiLoader(@Validator Player player) {
+ Loader loader = Loader.getLoader(player);
+ if (loaderNullCheck(loader, player)) return;
+ loader.gui(Loader.SettingsSorting.ALL);
+ }
+
+ @Register(value = "wait", description = "LOADER_HELP_WAIT")
+ public void shotDelayLoader(@Validator Player p, @Min(intValue = 1) @ErrorMessage("LOADER_SMALL_TIME") int delay) {
+ Loader loader = Loader.getLoader(p);
+ if (loaderNullCheck(loader, p)) return;
+ if (loader.setTicksBetweenShots(delay)) {
+ BauSystem.MESSAGE.send("LOADER_NEW_TIME", p, delay);
+ } else {
+ BauSystem.MESSAGE.send("LOADER_SMALL_TIME", p);
+ }
+ }
+
+ @Register(value = "speed", description = "LOADER_HELP_SPEED")
+ public void speedLoader(@Validator Player p, @Min(intValue = 0) @ErrorMessage("LOADER_SMALL_TIME") int delay) {
+ Loader loader = Loader.getLoader(p);
+ if (loaderNullCheck(loader, p)) return;
+ BauSystem.MESSAGE.send("LOADER_NEW_LOAD_TIME", p, delay);
+ loader.setTicksBetweenBlocks(delay);
+ }
+
+ @Register(value = "single", description = "LOADER_HELP_SINGLE")
+ public void singleLoader(@Validator Player p) {
+ Loader loader = Loader.getLoader(p);
+ if (loaderNullCheck(loader, p)) return;
+ loader.single();
+ BauSystem.MESSAGE.send("LOADER_SINGLE_CMD", p);
+ }
+
+ @EventHandler
+ public void onBauMemberUpdate(BauMemberUpdateEvent event) {
+ event.getNewSpectator().forEach(player -> {
+ Loader loader = Loader.getLoader(player);
+ if (loader == null) return;
+ loader.stop();
+ });
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/LoaderRecorder.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/LoaderRecorder.java
new file mode 100644
index 00000000..cc3dce23
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/LoaderRecorder.java
@@ -0,0 +1,231 @@
+/*
+ * 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.loader;
+
+import de.steamwar.bausystem.BauSystem;
+import de.steamwar.bausystem.SWUtils;
+import de.steamwar.bausystem.features.loader.elements.LoaderElement;
+import de.steamwar.bausystem.features.loader.elements.LoaderInteractionElement;
+import de.steamwar.bausystem.features.loader.elements.impl.*;
+import de.steamwar.bausystem.features.tpslimit.TPSUtils;
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.block.Block;
+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.Action;
+import org.bukkit.event.block.BlockBreakEvent;
+import org.bukkit.event.block.BlockPlaceEvent;
+import org.bukkit.event.player.PlayerInteractEvent;
+import org.bukkit.event.player.PlayerMoveEvent;
+import org.bukkit.inventory.EquipmentSlot;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+
+public class LoaderRecorder implements Listener {
+
+ private Player player;
+ private List loaderElementList;
+ private long lastInteraction = TPSUtils.currentRealTick.get();
+
+ public LoaderRecorder(Player player, List loaderElementList) {
+ this.player = player;
+ this.loaderElementList = loaderElementList;
+ Bukkit.getPluginManager().registerEvents(this, BauSystem.getInstance());
+ }
+
+ public void stop() {
+ addWaitTime(true);
+ HandlerList.unregisterAll(this);
+ player = null;
+ blockSet.clear();
+ }
+
+ private void addWaitTime(boolean last) {
+ if (loaderElementList.isEmpty()) {
+ lastInteraction = TPSUtils.currentRealTick.get();
+ return;
+ }
+ if (loaderElementList.get(loaderElementList.size() - 1) instanceof LoaderWait) {
+ return;
+ }
+ long diff = TPSUtils.currentRealTick.get() - lastInteraction;
+ if (last) diff = 120;
+ lastInteraction = TPSUtils.currentRealTick.get();
+ loaderElementList.add(new LoaderWait(diff));
+ }
+
+ @EventHandler
+ public void onBlockPlace(BlockPlaceEvent event) {
+ if (event.getPlayer() != player) return;
+ if (event.getBlock().getType() != Material.TNT) return;
+
+ addWaitTime(false);
+ loaderElementList.add(new LoaderTNT(event.getBlock().getLocation()));
+ message("LOADER_BUTTON_TNT");
+ }
+
+ @EventHandler
+ public void onBlockBreak(BlockBreakEvent event) {
+ if (event.getPlayer() != player) return;
+
+ boolean removedOne = false;
+ for (int i = 0; i < loaderElementList.size(); i++) {
+ LoaderElement element = loaderElementList.get(i);
+ if (!(element instanceof LoaderInteractionElement)) continue;
+ LoaderInteractionElement interactionElement = (LoaderInteractionElement) element;
+ if (interactionElement.getLocation().equals(event.getBlock().getLocation())) {
+ loaderElementList.remove(i);
+ if (i > 0) {
+ loaderElementList.remove(i - 1);
+ }
+ removedOne = true;
+ break;
+ }
+ }
+
+ if (removedOne) {
+ if (event.getBlock().getType() != Material.TNT) {
+ event.setCancelled(true);
+ }
+ SWUtils.sendToActionbar(player, BauSystem.MESSAGE.parse("LOADER_MESSAGE_UNINTERACT", player));
+ }
+ }
+
+ @EventHandler
+ public void onPlayerInteract(PlayerInteractEvent event) {
+ if (event.getPlayer() != player) return;
+ if (player.isSneaking()) return;
+ if (event.getAction() != Action.RIGHT_CLICK_BLOCK && event.getAction() != Action.PHYSICAL) return;
+ if (event.getClickedBlock().getType() == Material.OBSERVER) return;
+ if (event.getHand() == EquipmentSlot.OFF_HAND) return;
+
+ addWaitTime(false);
+ Block block = event.getClickedBlock();
+ getLoaderInteractionElement(block, (loaderInteractionElement, s) -> {
+ loaderElementList.add(loaderInteractionElement);
+ message(s);
+ });
+ }
+
+ private Map blockSet = new HashMap<>();
+ private Map movementSet = new HashMap<>();
+
+ @EventHandler
+ public void onPlayerMove(PlayerMoveEvent event) {
+ if (event.getPlayer() != player) return;
+
+ Block fromBlock = event.getFrom().getBlock();
+ Block toBlock = event.getTo().getBlock();
+ calcMovementBlocks(fromBlock, toBlock);
+
+ fromBlock = fromBlock.getRelative(0, 1, 0);
+ toBlock = toBlock.getRelative(0, 1, 0);
+ calcMovementBlocks(fromBlock, toBlock);
+ }
+
+ private void calcMovementBlocks(Block fromBlock, Block toBlock) {
+ if (!blockSet.containsKey(toBlock.getLocation())) {
+ Long startTime = blockSet.remove(fromBlock.getLocation());
+ LoaderMovement loaderMovement = movementSet.remove(fromBlock.getLocation());
+ if (loaderMovement != null && startTime != null) {
+ loaderMovement.setInitialTicks(TPSUtils.currentRealTick.get() - startTime);
+ }
+
+ blockSet.put(toBlock.getLocation(), TPSUtils.currentRealTick.get());
+
+ addWaitTime(false);
+ loaderMovement = null;
+ Material type = toBlock.getType();
+ switch (type) {
+ case TRIPWIRE:
+ loaderMovement = new LoaderMovement(toBlock.getLocation(), "LOADER_BUTTON_TRIPWIRE", Material.STRING);
+ message("LOADER_BUTTON_TRIPWIRE");
+ break;
+ case LIGHT_WEIGHTED_PRESSURE_PLATE:
+ case HEAVY_WEIGHTED_PRESSURE_PLATE:
+ loaderMovement = new LoaderMovement(toBlock.getLocation(), "LOADER_BUTTON_WEIGHTED_PRESSURE_PLATE", type);
+ message("LOADER_BUTTON_WEIGHTED_PRESSURE_PLATE");
+ break;
+ default:
+ if (type.name().endsWith("PRESSURE_PLATE")) {
+ loaderMovement = new LoaderMovement(toBlock.getLocation(), "LOADER_BUTTON_PRESSURE_PLATE", type);
+ message("LOADER_BUTTON_PRESSURE_PLATE");
+ }
+ break;
+ }
+ if (loaderMovement != null) {
+ movementSet.put(toBlock.getLocation(), loaderMovement);
+ loaderElementList.add(loaderMovement);
+ }
+ }
+ }
+
+ public static void getLoaderInteractionElement(Block block, BiConsumer, String> consumer) {
+ Material type = block.getType();
+ switch (type) {
+ case COMPARATOR:
+ consumer.accept(new LoaderComparator(block.getLocation()), "LOADER_BUTTON_COMPARATOR");
+ break;
+ case REPEATER:
+ consumer.accept(new LoaderRepeater(block.getLocation()), "LOADER_BUTTON_REPEATER");
+ break;
+ case NOTE_BLOCK:
+ consumer.accept(new LoaderNoteBlock(block.getLocation()), "LOADER_BUTTON_NOTEBLOCK");
+ break;
+ case LEVER:
+ consumer.accept(new LoaderLever(block.getLocation()), "LOADER_BUTTON_SWITCH");
+ break;
+ case DAYLIGHT_DETECTOR:
+ consumer.accept(new LoaderDaylightDetector(block.getLocation()), "LOADER_BUTTON_DAYLIGHT_DETECTOR");
+ break;
+ case LECTERN:
+ consumer.accept(new LoaderLectern(block.getLocation()), "LOADER_BUTTON_LECTERN");
+ break;
+ case IRON_TRAPDOOR:
+ case IRON_DOOR:
+ break;
+ default:
+ if (type.name().endsWith("_TRAPDOOR")) {
+ consumer.accept(new LoaderOpenable(block.getLocation(), "LOADER_BUTTON_TRAPDOOR", type), "LOADER_BUTTON_TRAPDOOR");
+ } else if (type.name().endsWith("_DOOR")) {
+ consumer.accept(new LoaderOpenable(block.getLocation(), "LOADER_BUTTON_DOOR", type), "LOADER_BUTTON_DOOR");
+ } else if (type.name().endsWith("FENCE_GATE")) {
+ consumer.accept(new LoaderOpenable(block.getLocation(), "LOADER_BUTTON_FENCEGATE", type), "LOADER_BUTTON_FENCEGATE");
+ } else if (type.name().endsWith("STONE_BUTTON")) {
+ consumer.accept(new LoaderTicks(block.getLocation(), "LOADER_BUTTON_STONE_BUTTON", type, 20), "LOADER_BUTTON_STONE_BUTTON");
+ } else if (type.name().endsWith("BUTTON")) {
+ consumer.accept(new LoaderTicks(block.getLocation(), "LOADER_BUTTON_WOOD_BUTTON", type, 30), "LOADER_BUTTON_WOOD_BUTTON");
+ }
+ break;
+ }
+ }
+
+ private void message(String type) {
+ SWUtils.sendToActionbar(player, BauSystem.MESSAGE.parse("LOADER_MESSAGE_INTERACT", player, BauSystem.MESSAGE.parse(type, player), loaderElementList.size()));
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/LoaderScoreboardElement.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/LoaderScoreboardElement.java
new file mode 100644
index 00000000..fdcda57a
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/LoaderScoreboardElement.java
@@ -0,0 +1,55 @@
+/*
+ * 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.loader;
+
+import de.steamwar.bausystem.BauSystem;
+import de.steamwar.bausystem.Permission;
+import de.steamwar.bausystem.region.Region;
+import de.steamwar.bausystem.utils.ScoreboardElement;
+import de.steamwar.linkage.Linked;
+import org.bukkit.entity.Player;
+
+@Linked
+public class LoaderScoreboardElement implements ScoreboardElement {
+
+ @Override
+ public ScoreboardGroup getGroup() {
+ return ScoreboardGroup.OTHER;
+ }
+
+ @Override
+ public int order() {
+ return 2;
+ }
+
+ @Override
+ public String get(Region region, Player p) {
+ if(!Permission.BUILD.hasPermission(p)) return null;
+ Loader loader = Loader.getLoader(p);
+ if (loader == null) return null;
+ if (loader.getStage() == Loader.Stage.RUNNING) {
+ return "§e" + BauSystem.MESSAGE.parse("SCOREBOARD_LOADER", p) + "§8: §a" + loader.getProgress();
+ } else if (loader.getStage() == Loader.Stage.PAUSE) {
+ return "§e" + BauSystem.MESSAGE.parse("SCOREBOARD_LOADER", p) + "§8: §c" + loader.getProgress();
+ } else {
+ return "§e" + BauSystem.MESSAGE.parse("SCOREBOARD_LOADER", p) + "§8: " + BauSystem.MESSAGE.parse(loader.getStage().getChatValue(), p);
+ }
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/elements/LoaderElement.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/elements/LoaderElement.java
new file mode 100644
index 00000000..658a9f8d
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/elements/LoaderElement.java
@@ -0,0 +1,31 @@
+/*
+ * 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.loader.elements;
+
+import de.steamwar.inventory.SWItem;
+import org.bukkit.entity.Player;
+
+import java.util.function.Consumer;
+
+public interface LoaderElement {
+ SWItem menu(Player player);
+ void execute(Consumer delay);
+ void click(Player player, Runnable backAction);
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/elements/LoaderInteractionElement.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/elements/LoaderInteractionElement.java
new file mode 100644
index 00000000..67539fd8
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/elements/LoaderInteractionElement.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.loader.elements;
+
+import de.steamwar.bausystem.BauSystem;
+import de.steamwar.inventory.SWAnvilInv;
+import de.steamwar.inventory.SWInventory;
+import de.steamwar.inventory.SWItem;
+import de.steamwar.inventory.SWListInv;
+import lombok.Getter;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.block.Block;
+import org.bukkit.block.BlockFace;
+import org.bukkit.block.data.*;
+import org.bukkit.block.data.type.Door;
+import org.bukkit.block.data.type.Switch;
+import org.bukkit.entity.Player;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Consumer;
+
+public abstract class LoaderInteractionElement & LoaderSettingsEnum> implements LoaderElement {
+
+ @Getter
+ protected final Location location;
+ protected int currentShot = 0;
+ protected T defaultSetting;
+ protected T newSetting;
+ protected T[] allSettings;
+ protected List elements = new ArrayList<>();
+ protected List extraPower = new ArrayList<>();
+ protected List extraTicks = new ArrayList<>();
+ protected int settingsGuiSize = 0;
+
+ protected LoaderInteractionElement(Location location, T defaultSetting, T newSetting, T[] allSettings) {
+ this.location = location;
+
+ this.defaultSetting = defaultSetting;
+ this.newSetting = newSetting;
+ this.allSettings = allSettings;
+
+ elements.add(defaultSetting);
+ extraPower.add(0);
+ extraTicks.add(0L);
+
+ for (T element : allSettings) {
+ settingsGuiSize = Math.max(element.getPos(), settingsGuiSize);
+ }
+ while (settingsGuiSize % 9 != 0) settingsGuiSize++;
+ settingsGuiSize += 9;
+ }
+
+ @Override
+ public void execute(Consumer delay) {
+ if (currentShot >= elements.size()) currentShot = 0;
+ if (checkBlockInWorld()) {
+ BlockData blockData = location.getBlock().getBlockData();
+ elements.get(currentShot).execute(location, blockData, this, extraPower.get(currentShot), extraTicks.get(currentShot), delay);
+ }
+ currentShot++;
+ if (currentShot >= elements.size()) currentShot = 0;
+ }
+
+ @Override
+ public void click(Player player, Runnable backAction) {
+ List> entries = new ArrayList<>();
+ Runnable updateRunnable = () -> {
+ entries.clear();
+ for (int i = 0; i < elements.size(); i++) {
+ SWItem swItem = elements.get(i).menu(player, this, extraPower.get(i), extraTicks.get(i));
+ swItem.setLore(Arrays.asList(BauSystem.MESSAGE.parse("LOADER_GUI_CLICK_TO_EDIT", player)));
+ entries.add(new SWListInv.SWListEntry<>(swItem, i));
+ }
+ };
+ updateRunnable.run();
+
+ SWListInv listInv = new SWListInv<>(player, "Interaction Settings", false, entries, (clickType, entry) -> {});
+ listInv.setCallback((clickType, entry) -> {
+ openIndividualSettingsMenu(player, entry, () -> {
+ updateRunnable.run();
+ listInv.open();
+ }, () -> {
+ elements.remove((int) entry);
+ extraPower.remove((int) entry);
+ extraTicks.remove((int) entry);
+ if (elements.isEmpty()) {
+ elements.add(newSetting);
+ extraPower.add(0);
+ extraTicks.add(1L);
+ }
+ click(player, backAction);
+ });
+ });
+ listInv.setItem(48, new SWItem(Material.ARROW, "§7Back", clickType -> {
+ backAction.run();
+ }));
+ listInv.setItem(50, new SWItem(Material.GHAST_SPAWN_EGG, "§7Insert another Setting", clickType -> {
+ elements.add(defaultSetting);
+ extraPower.add(0);
+ extraTicks.add(1L);
+ openIndividualSettingsMenu(player, elements.size() - 1, () -> {
+ updateRunnable.run();
+ listInv.open();
+ }, () -> {
+ elements.remove(elements.size() - 1);
+ extraPower.remove(extraPower.size() - 1);
+ extraTicks.remove(extraTicks.size() - 1);
+ if (elements.isEmpty()) {
+ elements.add(newSetting);
+ extraPower.add(0);
+ extraTicks.add(1L);
+ }
+ click(player, backAction);
+ });
+ }));
+ listInv.open();
+ }
+
+ private void openIndividualSettingsMenu(Player player, int index, Runnable back, Runnable delete) {
+ T currentElement = elements.get(index);
+ int guiSize = settingsGuiSize;
+ int powerStart = guiSize - 9;
+ if (currentElement.hasPower(this)) {
+ guiSize += 18;
+ }
+ int ticksStart = guiSize - 9;
+ if (currentElement.hasTicks(this)) {
+ guiSize += 9;
+ }
+
+ SWInventory swInventory = new SWInventory(player, guiSize, BauSystem.MESSAGE.parse("LOADER_GUI_SETTINGS_TITLE", player));
+ for (int i = guiSize - 9; i < guiSize; i++) swInventory.setItem(i, new SWItem(Material.GRAY_STAINED_GLASS_PANE, "§7", clickType -> {}));
+ swInventory.setItem(guiSize - 9, new SWItem(Material.ARROW, BauSystem.MESSAGE.parse("LOADER_GUI_SETTINGS_BACK", player)).getItemStack(), clickType -> back.run());
+ swInventory.setItem(guiSize - 5, new SWItem(Material.WOODEN_AXE, BauSystem.MESSAGE.parse("LOADER_GUI_SETTINGS_COPY", player)).getItemStack(), clickType -> {
+ SWAnvilInv swAnvilInv = new SWAnvilInv(player, BauSystem.MESSAGE.parse("LOADER_GUI_COPY_TITLE", player), "1");
+ swAnvilInv.setCallback(s -> {
+ try {
+ int count = Integer.parseInt(s);
+ if (count < 1) count = 1;
+ if (count > 65536) count = 65536;
+
+ int power = extraPower.get(index);
+ long ticks = extraTicks.get(index);
+ for (int i = 0; i < count; i++) {
+ elements.add(currentElement);
+ extraPower.add(power);
+ extraTicks.add(ticks);
+ }
+
+ if (count == 1) {
+ openIndividualSettingsMenu(player, elements.size() - 1, back, delete);
+ } else {
+ back.run();
+ }
+ } catch (NumberFormatException e) {
+ back.run();
+ }
+ });
+ swAnvilInv.open();
+ });
+ swInventory.setItem(guiSize - 1, new SWItem(Material.BARRIER, BauSystem.MESSAGE.parse("LOADER_GUI_SETTINGS_DELETE", player)).getItemStack(), clickType -> delete.run());
+
+ for (T element : allSettings) {
+ SWItem item = element.menu(player, this, extraPower.get(index), extraTicks.get(index));
+ if (element == currentElement) {
+ item.setEnchanted(true);
+ }
+
+ swInventory.setItem(element.getPos(), item.getItemStack(), clickType -> {
+ elements.set(index, element);
+ openIndividualSettingsMenu(player, index, back, delete);
+ });
+ }
+
+ if (currentElement.hasPower(this)) {
+ for (int power = 0; power < 16; power++) {
+ int finalPowerPosition = power;
+ if (power >= 9) finalPowerPosition++;
+ SWItem powerItem = new SWItem(Material.REDSTONE, BauSystem.MESSAGE.parse("LOADER_SETTING_POWER", player, power), Arrays.asList(), false, clickType -> {});
+ powerItem.getItemStack().setAmount(Math.max(power, 1));
+ if (extraPower.get(index) == power) powerItem.setEnchanted(true);
+
+ int finalPower = power;
+ swInventory.setItem(finalPowerPosition + powerStart, powerItem.getItemStack(), clickType -> {
+ extraPower.set(index, finalPower);
+ openIndividualSettingsMenu(player, index, back, delete);
+ });
+ }
+ }
+
+ if (currentElement.hasTicks(this)) {
+ swInventory.setItem(ticksStart + 3, new SWItem(SWItem.getDye(1), BauSystem.MESSAGE.parse("LOADER_SETTING_TICKS_REMOVE_ONE", player), Arrays.asList(BauSystem.MESSAGE.parse("LOADER_SETTING_TICKS_REMOVE_ONE_SHIFT", player)), false, clickType -> {}).getItemStack(), clickType -> {
+ long ticks = extraTicks.get(index);
+ ticks -= clickType.isShiftClick() ? 5 : 1;
+ if (ticks < 1) ticks = 1;
+ extraTicks.set(index, ticks);
+ openIndividualSettingsMenu(player, index, back, delete);
+ });
+
+ SWItem ticksItem = new SWItem(Material.CLOCK, BauSystem.MESSAGE.parse("LOADER_SETTING_TICKS", player, extraTicks.get(index)), Arrays.asList(BauSystem.MESSAGE.parse("LOADER_GUI_CLICK_TO_EDIT", player)), false, clickType -> {});
+ ticksItem.getItemStack().setAmount((int) Math.min(extraTicks.get(index), 64));
+ swInventory.setItem(ticksStart + 4, ticksItem.getItemStack(), clickType -> {
+ SWAnvilInv swAnvilInv = new SWAnvilInv(player, BauSystem.MESSAGE.parse("LOADER_SETTING_TICKS_NAME", player), extraTicks.get(index) + "");
+ swAnvilInv.setCallback(s -> {
+ try {
+ long ticks = Long.parseLong(s);
+ if (ticks < 1) ticks = 1;
+ extraTicks.set(index, ticks);
+ } catch (NumberFormatException ignored) {
+ }
+ openIndividualSettingsMenu(player, index, back, delete);
+ });
+ swAnvilInv.open();
+ });
+
+ swInventory.setItem(ticksStart + 5, new SWItem(SWItem.getDye(10), BauSystem.MESSAGE.parse("LOADER_SETTING_TICKS_ADD_ONE", player), Arrays.asList(BauSystem.MESSAGE.parse("LOADER_SETTING_TICKS_ADD_ONE_SHIFT", player)), false, clickType -> {}).getItemStack(), clickType -> {
+ long ticks = extraTicks.get(index);
+ ticks += clickType.isShiftClick() ? 5 : 1;
+ extraTicks.set(index, ticks);
+ openIndividualSettingsMenu(player, index, back, delete);
+ });
+ }
+
+ swInventory.open();
+ }
+
+ protected void update(BlockData blockData) {
+ Block block = location.getBlock();
+ if (blockData instanceof Door) {
+ Door door = (Door) blockData;
+ block.setBlockData(door);
+ if (door.getHalf() == Bisected.Half.BOTTOM) {
+ updateBlock(block.getRelative(BlockFace.UP), door.isOpen() ? 15 : 0);
+ } else {
+ updateBlock(block.getRelative(BlockFace.DOWN), door.isOpen() ? 15 : 0);
+ }
+ } else if (blockData instanceof Switch) {
+ Switch sw = (Switch) blockData;
+ updateBlock(block, sw);
+ FaceAttachable.AttachedFace face = sw.getAttachedFace();
+ if (face == FaceAttachable.AttachedFace.FLOOR) {
+ updateBlock(block.getRelative(BlockFace.DOWN), sw.isPowered() ? 15 : 0);
+ } else if (face == FaceAttachable.AttachedFace.CEILING) {
+ updateBlock(block.getRelative(BlockFace.UP), sw.isPowered() ? 15 : 0);
+ } else {
+ updateBlock(block.getRelative(sw.getFacing().getOppositeFace()), sw.isPowered() ? 15 : 0);
+ }
+ } else if (blockData instanceof Powerable) {
+ updateBlock(block, blockData);
+ updateBlock(block.getRelative(BlockFace.DOWN), ((Powerable) blockData).isPowered() ? 15 : 0);
+ } else if (blockData instanceof AnaloguePowerable) {
+ updateBlock(block, blockData);
+ updateBlock(block.getRelative(BlockFace.DOWN), ((AnaloguePowerable) blockData).getPower());
+ } else {
+ updateBlock(block, blockData);
+ }
+ }
+
+ private void updateBlock(Block block, int powered) {
+ BlockData data = block.getBlockData();
+ if (data instanceof Door) {
+ Door door = (Door) data;
+ door.setOpen(powered > 0);
+ block.setBlockData(door);
+ return;
+ } else if (data instanceof Powerable) {
+ Powerable powerable = (Powerable) data;
+ powerable.setPowered(powered > 0);
+ } else if (data instanceof AnaloguePowerable) {
+ AnaloguePowerable analoguePowerable = (AnaloguePowerable) data;
+ analoguePowerable.setPower(powered);
+ }
+ updateBlock(block, data);
+ }
+
+ private void updateBlock(Block block, BlockData data) {
+ block.setType(Material.BARRIER);
+ block.setBlockData(data);
+ }
+
+ public abstract boolean checkBlockInWorld();
+
+ protected final String translateItemName(String name, Player player) {
+ return BauSystem.MESSAGE.parse("LOADER_SETTING_NAME", player, BauSystem.MESSAGE.parse(name, player));
+ }
+
+ public int size() {
+ return elements.size();
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/elements/LoaderSettingsEnum.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/elements/LoaderSettingsEnum.java
new file mode 100644
index 00000000..f1e628d0
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/elements/LoaderSettingsEnum.java
@@ -0,0 +1,44 @@
+/*
+ * 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.loader.elements;
+
+import de.steamwar.bausystem.BauSystem;
+import de.steamwar.inventory.SWItem;
+import org.bukkit.Location;
+import org.bukkit.entity.Player;
+
+import java.util.function.Consumer;
+
+public interface LoaderSettingsEnum, E extends Enum & LoaderSettingsEnum> {
+ int getPos();
+ SWItem menu(Player player, P parent, int power, long ticks);
+ default boolean hasPower(P parent) {
+ return false;
+ }
+ default boolean hasTicks(P parent) {
+ return false;
+ }
+
+ void execute(Location location, T blockData, P parent, int power, long ticks, Consumer delay);
+
+ default String translateItemName(String name, String mode, Player player, Object... args) {
+ return BauSystem.MESSAGE.parse("LOADER_GUI_ITEM_NAME", player, BauSystem.MESSAGE.parse(name, player), BauSystem.MESSAGE.parse(mode, player, args));
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/elements/impl/LoaderComparator.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/elements/impl/LoaderComparator.java
new file mode 100644
index 00000000..fa7a032e
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/elements/impl/LoaderComparator.java
@@ -0,0 +1,122 @@
+/*
+ * 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.loader.elements.impl;
+
+import de.steamwar.bausystem.features.loader.elements.LoaderInteractionElement;
+import de.steamwar.bausystem.features.loader.elements.LoaderSettingsEnum;
+import de.steamwar.inventory.SWItem;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.block.data.type.Comparator;
+import org.bukkit.entity.Player;
+
+import java.util.Arrays;
+import java.util.function.Consumer;
+
+public class LoaderComparator extends LoaderInteractionElement {
+
+ public LoaderComparator(Location location) {
+ super(location, ComparatorSettingsEnum.INTERACT, ComparatorSettingsEnum.NOOP, ComparatorSettingsEnum.values());
+ }
+
+ public enum ComparatorSettingsEnum implements LoaderSettingsEnum {
+ NOOP {
+ @Override
+ public int getPos() {
+ return 2;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderComparator parent, int power, long ticks) {
+ return new SWItem(Material.STRUCTURE_VOID, translateItemName("LOADER_BUTTON_COMPARATOR", "LOADER_INTERACTION_NOOP", player));
+ }
+
+ @Override
+ public void execute(Location location, Comparator blockData, LoaderComparator parent, int power, long ticks, Consumer delay) {
+ }
+ },
+
+ INTERACT {
+ @Override
+ public int getPos() {
+ return 3;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderComparator parent, int power, long ticks) {
+ return new SWItem(Material.STICK, translateItemName("LOADER_BUTTON_COMPARATOR", "LOADER_INTERACTION_INTERACT", player));
+ }
+
+ @Override
+ public void execute(Location location, Comparator blockData, LoaderComparator parent, int power, long ticks, Consumer delay) {
+ blockData.setMode(blockData.getMode() == Comparator.Mode.COMPARE ? Comparator.Mode.SUBTRACT : Comparator.Mode.COMPARE);
+ parent.update(blockData);
+ }
+ },
+
+ COMPARE {
+ @Override
+ public int getPos() {
+ return 5;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderComparator parent, int power, long ticks) {
+ return new SWItem(Material.COMPARATOR, translateItemName("LOADER_BUTTON_COMPARATOR", "LOADER_INTERACTION_COMPARE", player));
+ }
+
+ @Override
+ public void execute(Location location, Comparator blockData, LoaderComparator parent, int power, long ticks, Consumer delay) {
+ blockData.setMode(Comparator.Mode.COMPARE);
+ parent.update(blockData);
+ }
+ },
+
+ SUBTRACT {
+ @Override
+ public int getPos() {
+ return 6;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderComparator parent, int power, long ticks) {
+ return new SWItem(Material.COMPARATOR, translateItemName("LOADER_BUTTON_COMPARATOR", "LOADER_INTERACTION_SUBTRACT", player));
+ }
+
+ @Override
+ public void execute(Location location, Comparator blockData, LoaderComparator parent, int power, long ticks, Consumer delay) {
+ blockData.setMode(Comparator.Mode.SUBTRACT);
+ parent.update(blockData);
+ }
+ }
+ }
+
+ @Override
+ public SWItem menu(Player player) {
+ SWItem swItem = new SWItem(Material.COMPARATOR, "§7Comparator");
+ swItem.setLore(Arrays.asList("§7Modes§8: §e" + elements.size(), "§8", "§7Click to edit"));
+ return swItem;
+ }
+
+ @Override
+ public boolean checkBlockInWorld() {
+ return location.getBlock().getType() == Material.COMPARATOR;
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/elements/impl/LoaderDaylightDetector.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/elements/impl/LoaderDaylightDetector.java
new file mode 100644
index 00000000..caacd6b0
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/elements/impl/LoaderDaylightDetector.java
@@ -0,0 +1,126 @@
+/*
+ * 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.loader.elements.impl;
+
+import de.steamwar.bausystem.features.loader.elements.LoaderInteractionElement;
+import de.steamwar.bausystem.features.loader.elements.LoaderSettingsEnum;
+import de.steamwar.inventory.SWItem;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.block.data.type.DaylightDetector;
+import org.bukkit.entity.Player;
+
+import java.util.function.Consumer;
+
+public class LoaderDaylightDetector extends LoaderInteractionElement {
+
+ public LoaderDaylightDetector(Location location) {
+ super(location, DaylightSettingsEnum.INTERACT, DaylightSettingsEnum.NOOP, DaylightSettingsEnum.values());
+ }
+
+ public enum DaylightSettingsEnum implements LoaderSettingsEnum {
+ NOOP {
+ @Override
+ public int getPos() {
+ return 2;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderDaylightDetector parent, int power, long ticks) {
+ return new SWItem(Material.STRUCTURE_VOID, translateItemName("LOADER_BUTTON_DAYLIGHT_DETECTOR", "LOADER_INTERACTION_NOOP", player));
+ }
+
+ @Override
+ public void execute(Location location, DaylightDetector blockData, LoaderDaylightDetector parent, int power, long ticks, Consumer delay) {
+ }
+ },
+
+ INTERACT {
+ @Override
+ public int getPos() {
+ return 3;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderDaylightDetector parent, int power, long ticks) {
+ return new SWItem(Material.STICK, translateItemName("LOADER_BUTTON_DAYLIGHT_DETECTOR", "LOADER_INTERACTION_INTERACT", player));
+ }
+
+ @Override
+ public void execute(Location location, DaylightDetector blockData, LoaderDaylightDetector parent, int power, long ticks, Consumer delay) {
+ blockData.setInverted(!blockData.isInverted());
+ blockData.setPower(15 - blockData.getPower());
+ parent.update(blockData);
+ }
+ },
+
+ DAY_MODE {
+ @Override
+ public int getPos() {
+ return 5;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderDaylightDetector parent, int power, long ticks) {
+ return new SWItem(Material.DAYLIGHT_DETECTOR, translateItemName("LOADER_BUTTON_DAYLIGHT_DETECTOR", "LOADER_INTERACTION_UNPOWERED", player));
+ }
+
+ @Override
+ public void execute(Location location, DaylightDetector blockData, LoaderDaylightDetector parent, int power, long ticks, Consumer delay) {
+ if (blockData.isInverted()) {
+ blockData.setInverted(false);
+ blockData.setPower(15 - blockData.getPower());
+ parent.update(blockData);
+ }
+ }
+ },
+
+ NIGHT_MODE {
+ @Override
+ public int getPos() {
+ return 6;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderDaylightDetector parent, int power, long ticks) {
+ return new SWItem(Material.DAYLIGHT_DETECTOR, translateItemName("LOADER_BUTTON_DAYLIGHT_DETECTOR", "LOADER_INTERACTION_POWERED", player));
+ }
+
+ @Override
+ public void execute(Location location, DaylightDetector blockData, LoaderDaylightDetector parent, int power, long ticks, Consumer delay) {
+ if (!blockData.isInverted()) {
+ blockData.setInverted(true);
+ blockData.setPower(15 - blockData.getPower());
+ parent.update(blockData);
+ }
+ }
+ }
+ }
+
+ @Override
+ public SWItem menu(Player player) {
+ return new SWItem(Material.DAYLIGHT_DETECTOR, translateItemName("LOADER_BUTTON_DAYLIGHT_DETECTOR", player));
+ }
+
+ @Override
+ public boolean checkBlockInWorld() {
+ return location.getBlock().getType() == Material.DAYLIGHT_DETECTOR;
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/elements/impl/LoaderLectern.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/elements/impl/LoaderLectern.java
new file mode 100644
index 00000000..8282cf69
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/elements/impl/LoaderLectern.java
@@ -0,0 +1,520 @@
+/*
+ * 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.loader.elements.impl;
+
+import de.steamwar.bausystem.features.loader.elements.LoaderInteractionElement;
+import de.steamwar.bausystem.features.loader.elements.LoaderSettingsEnum;
+import de.steamwar.inventory.SWItem;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.block.Lectern;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.BookMeta;
+
+import java.util.function.Consumer;
+
+public class LoaderLectern extends LoaderInteractionElement {
+
+
+ public LoaderLectern(Location location) {
+ super(location, LecternSettingsEnum.NEXT_PAGE, LecternSettingsEnum.NOOP, LecternSettingsEnum.values());
+ }
+
+ public enum LecternSettingsEnum implements LoaderSettingsEnum {
+ NOOP {
+ @Override
+ public int getPos() {
+ return 2;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderLectern parent, int power, long ticks) {
+ return new SWItem(Material.STRUCTURE_VOID, translateItemName("LOADER_BUTTON_LECTERN", "LOADER_INTERACTION_NOOP", player));
+ }
+
+ @Override
+ public void execute(Location location, org.bukkit.block.data.type.Lectern blockData, LoaderLectern parent, int power, long ticks, Consumer delay) {
+ }
+ },
+
+ PREVIOUS_PAGE {
+ @Override
+ public int getPos() {
+ return 5;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderLectern parent, int power, long ticks) {
+ return new SWItem(Material.STICK, translateItemName("LOADER_BUTTON_LECTERN", "LOADER_INTERACTION_PAGE_PREV", player));
+ }
+
+ @Override
+ public void execute(Location location, org.bukkit.block.data.type.Lectern blockData, LoaderLectern parent, int power, long ticks, Consumer delay) {
+ if (!blockData.hasBook()) return;
+ Lectern lectern = (Lectern) location.getBlock().getState();
+ if (lectern.getPage() > 0) {
+ lectern.setPage(lectern.getPage() - 1);
+ parent.update(blockData);
+ }
+ }
+ },
+
+ NEXT_PAGE {
+ @Override
+ public int getPos() {
+ return 6;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderLectern parent, int power, long ticks) {
+ return new SWItem(Material.STICK, translateItemName("LOADER_BUTTON_LECTERN", "LOADER_INTERACTION_PAGE_NEXT", player));
+ }
+
+ @Override
+ public void execute(Location location, org.bukkit.block.data.type.Lectern blockData, LoaderLectern parent, int power, long ticks, Consumer delay) {
+ if (!blockData.hasBook()) return;
+ Lectern lectern = (Lectern) location.getBlock().getState();
+ ItemStack itemStack = lectern.getInventory().getItem(0);
+ if (itemStack == null) return;
+ int pages = ((BookMeta) itemStack.getItemMeta()).getPages().size();
+ if (lectern.getPage() < pages) {
+ lectern.setPage(lectern.getPage() + 1);
+ parent.update(blockData);
+ }
+ }
+ },
+
+ PAGE_1 {
+ @Override
+ public int getPos() {
+ return 9;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderLectern parent, int power, long ticks) {
+ SWItem swItem = new SWItem(Material.LECTERN, translateItemName("LOADER_BUTTON_LECTERN", "LOADER_INTERACTION_PAGE", player, 1));
+ swItem.getItemStack().setAmount(1);
+ return swItem;
+ }
+
+ @Override
+ public void execute(Location location, org.bukkit.block.data.type.Lectern blockData, LoaderLectern parent, int power, long ticks, Consumer delay) {
+ if (!blockData.hasBook()) return;
+ Lectern lectern = (Lectern) location.getBlock().getState();
+ ItemStack itemStack = lectern.getInventory().getItem(0);
+ if (itemStack == null) return;
+ int pages = ((BookMeta) itemStack.getItemMeta()).getPages().size();
+ if (1 <= pages) {
+ lectern.setPage(1);
+ parent.update(blockData);
+ }
+ }
+ },
+
+ PAGE_2 {
+ @Override
+ public int getPos() {
+ return 10;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderLectern parent, int power, long ticks) {
+ SWItem swItem = new SWItem(Material.LECTERN, translateItemName("LOADER_BUTTON_LECTERN", "LOADER_INTERACTION_PAGE", player, 2));
+ swItem.getItemStack().setAmount(2);
+ return swItem;
+ }
+
+ @Override
+ public void execute(Location location, org.bukkit.block.data.type.Lectern blockData, LoaderLectern parent, int power, long ticks, Consumer delay) {
+ if (!blockData.hasBook()) return;
+ Lectern lectern = (Lectern) location.getBlock().getState();
+ ItemStack itemStack = lectern.getInventory().getItem(0);
+ if (itemStack == null) return;
+ int pages = ((BookMeta) itemStack.getItemMeta()).getPages().size();
+ if (2 <= pages) {
+ lectern.setPage(2);
+ parent.update(blockData);
+ }
+ }
+ },
+
+ PAGE_3 {
+ @Override
+ public int getPos() {
+ return 11;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderLectern parent, int power, long ticks) {
+ SWItem swItem = new SWItem(Material.LECTERN, translateItemName("LOADER_BUTTON_LECTERN", "LOADER_INTERACTION_PAGE", player, 3));
+ swItem.getItemStack().setAmount(3);
+ return swItem;
+ }
+
+ @Override
+ public void execute(Location location, org.bukkit.block.data.type.Lectern blockData, LoaderLectern parent, int power, long ticks, Consumer delay) {
+ if (!blockData.hasBook()) return;
+ Lectern lectern = (Lectern) location.getBlock().getState();
+ ItemStack itemStack = lectern.getInventory().getItem(0);
+ if (itemStack == null) return;
+ int pages = ((BookMeta) itemStack.getItemMeta()).getPages().size();
+ if (3 <= pages) {
+ lectern.setPage(3);
+ parent.update(blockData);
+ }
+ }
+ },
+
+ PAGE_4 {
+ @Override
+ public int getPos() {
+ return 12;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderLectern parent, int power, long ticks) {
+ SWItem swItem = new SWItem(Material.LECTERN, translateItemName("LOADER_BUTTON_LECTERN", "LOADER_INTERACTION_PAGE", player, 4));
+ swItem.getItemStack().setAmount(4);
+ return swItem;
+ }
+
+ @Override
+ public void execute(Location location, org.bukkit.block.data.type.Lectern blockData, LoaderLectern parent, int power, long ticks, Consumer delay) {
+ if (!blockData.hasBook()) return;
+ Lectern lectern = (Lectern) location.getBlock().getState();
+ ItemStack itemStack = lectern.getInventory().getItem(0);
+ if (itemStack == null) return;
+ int pages = ((BookMeta) itemStack.getItemMeta()).getPages().size();
+ if (4 <= pages) {
+ lectern.setPage(4);
+ parent.update(blockData);
+ }
+ }
+ },
+
+ PAGE_5 {
+ @Override
+ public int getPos() {
+ return 13;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderLectern parent, int power, long ticks) {
+ SWItem swItem = new SWItem(Material.LECTERN, translateItemName("LOADER_BUTTON_LECTERN", "LOADER_INTERACTION_PAGE", player, 5));
+ swItem.getItemStack().setAmount(5);
+ return swItem;
+ }
+
+ @Override
+ public void execute(Location location, org.bukkit.block.data.type.Lectern blockData, LoaderLectern parent, int power, long ticks, Consumer delay) {
+ if (!blockData.hasBook()) return;
+ Lectern lectern = (Lectern) location.getBlock().getState();
+ ItemStack itemStack = lectern.getInventory().getItem(0);
+ if (itemStack == null) return;
+ int pages = ((BookMeta) itemStack.getItemMeta()).getPages().size();
+ if (5 <= pages) {
+ lectern.setPage(5);
+ parent.update(blockData);
+ }
+ }
+ },
+
+ PAGE_6 {
+ @Override
+ public int getPos() {
+ return 14;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderLectern parent, int power, long ticks) {
+ SWItem swItem = new SWItem(Material.LECTERN, translateItemName("LOADER_BUTTON_LECTERN", "LOADER_INTERACTION_PAGE", player, 6));
+ swItem.getItemStack().setAmount(6);
+ return swItem;
+ }
+
+ @Override
+ public void execute(Location location, org.bukkit.block.data.type.Lectern blockData, LoaderLectern parent, int power, long ticks, Consumer delay) {
+ if (!blockData.hasBook()) return;
+ Lectern lectern = (Lectern) location.getBlock().getState();
+ ItemStack itemStack = lectern.getInventory().getItem(0);
+ if (itemStack == null) return;
+ int pages = ((BookMeta) itemStack.getItemMeta()).getPages().size();
+ if (6 <= pages) {
+ lectern.setPage(6);
+ parent.update(blockData);
+ }
+ }
+ },
+
+ PAGE_7 {
+ @Override
+ public int getPos() {
+ return 15;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderLectern parent, int power, long ticks) {
+ SWItem swItem = new SWItem(Material.LECTERN, translateItemName("LOADER_BUTTON_LECTERN", "LOADER_INTERACTION_PAGE", player, 7));
+ swItem.getItemStack().setAmount(7);
+ return swItem;
+ }
+
+ @Override
+ public void execute(Location location, org.bukkit.block.data.type.Lectern blockData, LoaderLectern parent, int power, long ticks, Consumer delay) {
+ if (!blockData.hasBook()) return;
+ Lectern lectern = (Lectern) location.getBlock().getState();
+ ItemStack itemStack = lectern.getInventory().getItem(0);
+ if (itemStack == null) return;
+ int pages = ((BookMeta) itemStack.getItemMeta()).getPages().size();
+ if (7 <= pages) {
+ lectern.setPage(7);
+ parent.update(blockData);
+ }
+ }
+ },
+
+ PAGE_8 {
+ @Override
+ public int getPos() {
+ return 16;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderLectern parent, int power, long ticks) {
+ SWItem swItem = new SWItem(Material.LECTERN, translateItemName("LOADER_BUTTON_LECTERN", "LOADER_INTERACTION_PAGE", player, 8));
+ swItem.getItemStack().setAmount(8);
+ return swItem;
+ }
+
+ @Override
+ public void execute(Location location, org.bukkit.block.data.type.Lectern blockData, LoaderLectern parent, int power, long ticks, Consumer delay) {
+ if (!blockData.hasBook()) return;
+ Lectern lectern = (Lectern) location.getBlock().getState();
+ ItemStack itemStack = lectern.getInventory().getItem(0);
+ if (itemStack == null) return;
+ int pages = ((BookMeta) itemStack.getItemMeta()).getPages().size();
+ if (8 <= pages) {
+ lectern.setPage(8);
+ parent.update(blockData);
+ }
+ }
+ },
+
+ PAGE_9 {
+ @Override
+ public int getPos() {
+ return 17;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderLectern parent, int power, long ticks) {
+ SWItem swItem = new SWItem(Material.LECTERN, translateItemName("LOADER_BUTTON_LECTERN", "LOADER_INTERACTION_PAGE", player, 9));
+ swItem.getItemStack().setAmount(9);
+ return swItem;
+ }
+
+ @Override
+ public void execute(Location location, org.bukkit.block.data.type.Lectern blockData, LoaderLectern parent, int power, long ticks, Consumer delay) {
+ if (!blockData.hasBook()) return;
+ Lectern lectern = (Lectern) location.getBlock().getState();
+ ItemStack itemStack = lectern.getInventory().getItem(0);
+ if (itemStack == null) return;
+ int pages = ((BookMeta) itemStack.getItemMeta()).getPages().size();
+ if (9 <= pages) {
+ lectern.setPage(9);
+ parent.update(blockData);
+ }
+ }
+ },
+
+ PAGE_10 {
+ @Override
+ public int getPos() {
+ return 19;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderLectern parent, int power, long ticks) {
+ SWItem swItem = new SWItem(Material.LECTERN, translateItemName("LOADER_BUTTON_LECTERN", "LOADER_INTERACTION_PAGE", player, 10));
+ swItem.getItemStack().setAmount(10);
+ return swItem;
+ }
+
+ @Override
+ public void execute(Location location, org.bukkit.block.data.type.Lectern blockData, LoaderLectern parent, int power, long ticks, Consumer delay) {
+ if (!blockData.hasBook()) return;
+ Lectern lectern = (Lectern) location.getBlock().getState();
+ ItemStack itemStack = lectern.getInventory().getItem(0);
+ if (itemStack == null) return;
+ int pages = ((BookMeta) itemStack.getItemMeta()).getPages().size();
+ if (10 <= pages) {
+ lectern.setPage(10);
+ parent.update(blockData);
+ }
+ }
+ },
+
+ PAGE_11 {
+ @Override
+ public int getPos() {
+ return 20;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderLectern parent, int power, long ticks) {
+ SWItem swItem = new SWItem(Material.LECTERN, translateItemName("LOADER_BUTTON_LECTERN", "LOADER_INTERACTION_PAGE", player, 11));
+ swItem.getItemStack().setAmount(11);
+ return swItem;
+ }
+
+ @Override
+ public void execute(Location location, org.bukkit.block.data.type.Lectern blockData, LoaderLectern parent, int power, long ticks, Consumer delay) {
+ if (!blockData.hasBook()) return;
+ Lectern lectern = (Lectern) location.getBlock().getState();
+ ItemStack itemStack = lectern.getInventory().getItem(0);
+ if (itemStack == null) return;
+ int pages = ((BookMeta) itemStack.getItemMeta()).getPages().size();
+ if (11 <= pages) {
+ lectern.setPage(11);
+ parent.update(blockData);
+ }
+ }
+ },
+
+ PAGE_12 {
+ @Override
+ public int getPos() {
+ return 21;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderLectern parent, int power, long ticks) {
+ SWItem swItem = new SWItem(Material.LECTERN, translateItemName("LOADER_BUTTON_LECTERN", "LOADER_INTERACTION_PAGE", player, 12));
+ swItem.getItemStack().setAmount(12);
+ return swItem;
+ }
+
+ @Override
+ public void execute(Location location, org.bukkit.block.data.type.Lectern blockData, LoaderLectern parent, int power, long ticks, Consumer delay) {
+ if (!blockData.hasBook()) return;
+ Lectern lectern = (Lectern) location.getBlock().getState();
+ ItemStack itemStack = lectern.getInventory().getItem(0);
+ if (itemStack == null) return;
+ int pages = ((BookMeta) itemStack.getItemMeta()).getPages().size();
+ if (12 <= pages) {
+ lectern.setPage(12);
+ parent.update(blockData);
+ }
+ }
+ },
+
+ PAGE_13 {
+ @Override
+ public int getPos() {
+ return 23;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderLectern parent, int power, long ticks) {
+ SWItem swItem = new SWItem(Material.LECTERN, translateItemName("LOADER_BUTTON_LECTERN", "LOADER_INTERACTION_PAGE", player, 13));
+ swItem.getItemStack().setAmount(13);
+ return swItem;
+ }
+
+ @Override
+ public void execute(Location location, org.bukkit.block.data.type.Lectern blockData, LoaderLectern parent, int power, long ticks, Consumer delay) {
+ if (!blockData.hasBook()) return;
+ Lectern lectern = (Lectern) location.getBlock().getState();
+ ItemStack itemStack = lectern.getInventory().getItem(0);
+ if (itemStack == null) return;
+ int pages = ((BookMeta) itemStack.getItemMeta()).getPages().size();
+ if (13 <= pages) {
+ lectern.setPage(13);
+ parent.update(blockData);
+ }
+ }
+ },
+
+ PAGE_14 {
+ @Override
+ public int getPos() {
+ return 24;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderLectern parent, int power, long ticks) {
+ SWItem swItem = new SWItem(Material.LECTERN, translateItemName("LOADER_BUTTON_LECTERN", "LOADER_INTERACTION_PAGE", player, 14));
+ swItem.getItemStack().setAmount(14);
+ return swItem;
+ }
+
+ @Override
+ public void execute(Location location, org.bukkit.block.data.type.Lectern blockData, LoaderLectern parent, int power, long ticks, Consumer delay) {
+ if (!blockData.hasBook()) return;
+ Lectern lectern = (Lectern) location.getBlock().getState();
+ ItemStack itemStack = lectern.getInventory().getItem(0);
+ if (itemStack == null) return;
+ int pages = ((BookMeta) itemStack.getItemMeta()).getPages().size();
+ if (14 <= pages) {
+ lectern.setPage(14);
+ parent.update(blockData);
+ }
+ }
+ },
+
+ PAGE_15 {
+ @Override
+ public int getPos() {
+ return 25;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderLectern parent, int power, long ticks) {
+ SWItem swItem = new SWItem(Material.LECTERN, translateItemName("LOADER_BUTTON_LECTERN", "LOADER_INTERACTION_PAGE", player, 15));
+ swItem.getItemStack().setAmount(15);
+ return swItem;
+ }
+
+ @Override
+ public void execute(Location location, org.bukkit.block.data.type.Lectern blockData, LoaderLectern parent, int power, long ticks, Consumer delay) {
+ if (!blockData.hasBook()) return;
+ Lectern lectern = (Lectern) location.getBlock().getState();
+ ItemStack itemStack = lectern.getInventory().getItem(0);
+ if (itemStack == null) return;
+ int pages = ((BookMeta) itemStack.getItemMeta()).getPages().size();
+ if (15 <= pages) {
+ lectern.setPage(15);
+ parent.update(blockData);
+ }
+ }
+ }
+ }
+
+ @Override
+ public SWItem menu(Player player) {
+ return new SWItem(Material.LECTERN, translateItemName("LOADER_BUTTON_LECTERN", player));
+ }
+
+ @Override
+ public boolean checkBlockInWorld() {
+ return location.getBlock().getType() == Material.LECTERN;
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/elements/impl/LoaderLever.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/elements/impl/LoaderLever.java
new file mode 100644
index 00000000..c77d914d
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/elements/impl/LoaderLever.java
@@ -0,0 +1,119 @@
+/*
+ * 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.loader.elements.impl;
+
+import de.steamwar.bausystem.features.loader.elements.LoaderInteractionElement;
+import de.steamwar.bausystem.features.loader.elements.LoaderSettingsEnum;
+import de.steamwar.inventory.SWItem;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.block.data.type.Switch;
+import org.bukkit.entity.Player;
+
+import java.util.function.Consumer;
+
+public class LoaderLever extends LoaderInteractionElement {
+
+ public LoaderLever(Location location) {
+ super(location, LeverSettingsEnum.INTERACT, LeverSettingsEnum.NOOP, LeverSettingsEnum.values());
+ }
+
+ public enum LeverSettingsEnum implements LoaderSettingsEnum {
+
+ NOOP {
+ @Override
+ public int getPos() {
+ return 2;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderLever parent, int power, long ticks) {
+ return new SWItem(Material.STRUCTURE_VOID, translateItemName("LOADER_BUTTON_SWITCH", "LOADER_INTERACTION_NOOP", player));
+ }
+
+ @Override
+ public void execute(Location location, Switch blockData, LoaderLever parent, int power, long ticks, Consumer delay) {
+ }
+ },
+
+ INTERACT {
+ @Override
+ public int getPos() {
+ return 3;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderLever parent, int power, long ticks) {
+ return new SWItem(Material.STICK, translateItemName("LOADER_BUTTON_SWITCH", "LOADER_INTERACTION_INTERACT", player));
+ }
+
+ @Override
+ public void execute(Location location, Switch blockData, LoaderLever parent, int power, long ticks, Consumer delay) {
+ blockData.setPowered(!blockData.isPowered());
+ parent.update(blockData);
+ }
+ },
+
+ POWER_OFF {
+ @Override
+ public int getPos() {
+ return 5;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderLever parent, int power, long ticks) {
+ return new SWItem(Material.LEVER, translateItemName("LOADER_BUTTON_SWITCH", "LOADER_INTERACTION_INACTIVE", player));
+ }
+
+ @Override
+ public void execute(Location location, Switch blockData, LoaderLever parent, int power, long ticks, Consumer delay) {
+ blockData.setPowered(false);
+ parent.update(blockData);
+ }
+ },
+
+ POWER_ON {
+ @Override
+ public int getPos() {
+ return 6;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderLever parent, int power, long ticks) {
+ return new SWItem(Material.LEVER, translateItemName("LOADER_BUTTON_SWITCH", "LOADER_INTERACTION_ACTIVE", player));
+ }
+
+ @Override
+ public void execute(Location location, Switch blockData, LoaderLever parent, int power, long ticks, Consumer delay) {
+ blockData.setPowered(true);
+ }
+ }
+ }
+
+ @Override
+ public SWItem menu(Player player) {
+ return new SWItem(Material.LEVER, translateItemName("LOADER_BUTTON_LECTERN", player));
+ }
+
+ @Override
+ public boolean checkBlockInWorld() {
+ return location.getBlock().getType() == Material.LEVER;
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/elements/impl/LoaderMovement.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/elements/impl/LoaderMovement.java
new file mode 100644
index 00000000..3fc89e5d
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/elements/impl/LoaderMovement.java
@@ -0,0 +1,165 @@
+/*
+ * 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.loader.elements.impl;
+
+import de.steamwar.bausystem.BauSystem;
+import de.steamwar.bausystem.features.loader.elements.LoaderInteractionElement;
+import de.steamwar.bausystem.features.loader.elements.LoaderSettingsEnum;
+import de.steamwar.inventory.SWItem;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.block.data.AnaloguePowerable;
+import org.bukkit.block.data.BlockData;
+import org.bukkit.block.data.Powerable;
+import org.bukkit.entity.Player;
+
+import java.util.function.Consumer;
+
+public class LoaderMovement extends LoaderInteractionElement {
+
+ private String name;
+ private Material material;
+ private boolean analogue;
+
+ public LoaderMovement(Location location, String name, Material material) {
+ super(location, MovementSettingsEnum.NO_WAIT_FOR, MovementSettingsEnum.NOOP, MovementSettingsEnum.values());
+ this.name = name;
+ this.material = material;
+ this.analogue = location.getBlock().getBlockData() instanceof AnaloguePowerable;
+ }
+
+ public void setInitialTicks(long ticks) {
+ if (ticks < 1) ticks = 1;
+ extraTicks.set(currentShot, ticks);
+ }
+
+ public enum MovementSettingsEnum implements LoaderSettingsEnum {
+
+ NOOP {
+ @Override
+ public int getPos() {
+ return 2;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderMovement parent, int power, long ticks) {
+ return new SWItem(Material.STRUCTURE_VOID, translateItemName(parent.name, "LOADER_INTERACTION_NOOP", player));
+ }
+
+ @Override
+ public void execute(Location location, BlockData blockData, LoaderMovement parent, int power, long ticks, Consumer delay) {
+ }
+ },
+
+ NO_WAIT_FOR {
+ @Override
+ public int getPos() {
+ return 5;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderMovement parent, int power, long ticks) {
+ SWItem swItem = new SWItem(parent.material, translateItemName(parent.name, "LOADER_INTERACTION_WAIT_FOR", player));
+ swItem.getItemStack().setAmount((int) Math.min(ticks, 64));
+ return swItem;
+ }
+
+ @Override
+ public boolean hasPower(LoaderMovement parent) {
+ return parent.analogue;
+ }
+
+ @Override
+ public boolean hasTicks(LoaderMovement parent) {
+ return true;
+ }
+
+ @Override
+ public void execute(Location location, BlockData blockData, LoaderMovement parent, int power, long ticks, Consumer delay) {
+ if (blockData instanceof AnaloguePowerable) {
+ AnaloguePowerable analoguePowerable = (AnaloguePowerable) location.getBlock().getBlockData();
+ analoguePowerable.setPower(power);
+ parent.update(analoguePowerable);
+ } else if (blockData instanceof Powerable) {
+ Powerable powerable = (Powerable) location.getBlock().getBlockData();
+ if (ticks < 0) {
+ powerable.setPowered(power > 0);
+ } else {
+ powerable.setPowered(true);
+ }
+ parent.update(powerable);
+ }
+
+ BauSystem.runTaskLater(BauSystem.getInstance(), () -> {
+ if (blockData instanceof AnaloguePowerable) {
+ AnaloguePowerable analoguePowerable = (AnaloguePowerable) blockData;
+ analoguePowerable.setPower(0);
+ parent.update(analoguePowerable);
+ } else {
+ Powerable powerable = (Powerable) blockData;
+ powerable.setPowered(false);
+ parent.update(powerable);
+ }
+ }, ticks);
+ }
+ },
+
+ WAIT_FOR {
+ @Override
+ public int getPos() {
+ return 6;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderMovement parent, int power, long ticks) {
+ SWItem swItem = new SWItem(parent.material, translateItemName(parent.name, "LOADER_INTERACTION_NO_WAIT_FOR", player));
+ swItem.getItemStack().setAmount((int) Math.min(ticks, 64));
+ return swItem;
+ }
+
+ @Override
+ public boolean hasPower(LoaderMovement parent) {
+ return parent.analogue;
+ }
+
+ @Override
+ public boolean hasTicks(LoaderMovement parent) {
+ return true;
+ }
+
+ @Override
+ public void execute(Location location, BlockData blockData, LoaderMovement parent, int power, long ticks, Consumer delay) {
+ NO_WAIT_FOR.execute(location, blockData, parent, power, ticks, delay);
+ delay.accept(ticks);
+ }
+ }
+ }
+
+ @Override
+ public SWItem menu(Player player) {
+ return new SWItem(material, translateItemName(name, player));
+ }
+
+ @Override
+ public boolean checkBlockInWorld() {
+ if (material == Material.STRING && location.getBlock().getType() == Material.TRIPWIRE) return true;
+ return location.getBlock().getType() == material;
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/elements/impl/LoaderNoteBlock.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/elements/impl/LoaderNoteBlock.java
new file mode 100644
index 00000000..042243e0
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/elements/impl/LoaderNoteBlock.java
@@ -0,0 +1,87 @@
+/*
+ * 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.loader.elements.impl;
+
+import de.steamwar.bausystem.features.loader.elements.LoaderInteractionElement;
+import de.steamwar.bausystem.features.loader.elements.LoaderSettingsEnum;
+import de.steamwar.inventory.SWItem;
+import org.bukkit.Instrument;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.block.data.type.NoteBlock;
+import org.bukkit.entity.Player;
+
+import java.util.function.Consumer;
+
+public class LoaderNoteBlock extends LoaderInteractionElement {
+
+ public LoaderNoteBlock(Location location) {
+ super(location, NoteBlockSettingsEnum.INTERACT, NoteBlockSettingsEnum.NOOP, NoteBlockSettingsEnum.values());
+ }
+
+ public enum NoteBlockSettingsEnum implements LoaderSettingsEnum {
+
+ NOOP {
+ @Override
+ public int getPos() {
+ return 2;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderNoteBlock parent, int power, long ticks) {
+ return new SWItem(Material.STRUCTURE_VOID, translateItemName("LOADER_BUTTON_NOTEBLOCK", "LOADER_INTERACTION_NOOP", player));
+ }
+
+ @Override
+ public void execute(Location location, NoteBlock blockData, LoaderNoteBlock parent, int power, long ticks, Consumer delay) {
+
+ }
+ },
+
+ INTERACT {
+ @Override
+ public int getPos() {
+ return 3;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderNoteBlock parent, int power, long ticks) {
+ return new SWItem(Material.NOTE_BLOCK, translateItemName("LOADER_BUTTON_NOTEBLOCK", "LOADER_INTERACTION_INTERACT", player));
+ }
+
+ @Override
+ public void execute(Location location, NoteBlock blockData, LoaderNoteBlock parent, int power, long ticks, Consumer delay) {
+ if (blockData.getInstrument() == Instrument.BANJO) blockData.setInstrument(Instrument.BIT);
+ else blockData.setInstrument(Instrument.BANJO);
+ parent.update(blockData);
+ }
+ }
+ }
+
+ @Override
+ public SWItem menu(Player player) {
+ return new SWItem(Material.NOTE_BLOCK, translateItemName("LOADER_BUTTON_NOTEBLOCK", player));
+ }
+
+ @Override
+ public boolean checkBlockInWorld() {
+ return location.getBlock().getType() == Material.NOTE_BLOCK;
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/elements/impl/LoaderOpenable.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/elements/impl/LoaderOpenable.java
new file mode 100644
index 00000000..46197b00
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/elements/impl/LoaderOpenable.java
@@ -0,0 +1,125 @@
+/*
+ * 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.loader.elements.impl;
+
+import de.steamwar.bausystem.features.loader.elements.LoaderInteractionElement;
+import de.steamwar.bausystem.features.loader.elements.LoaderSettingsEnum;
+import de.steamwar.inventory.SWItem;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.block.data.Openable;
+import org.bukkit.entity.Player;
+
+import java.util.function.Consumer;
+
+public class LoaderOpenable extends LoaderInteractionElement {
+
+ private String name;
+ private Material material;
+
+ public LoaderOpenable(Location location, String name, Material material) {
+ super(location, OpenableSettingsEnum.INTERACT, OpenableSettingsEnum.NOOP, OpenableSettingsEnum.values());
+ this.name = name;
+ this.material = material;
+ }
+
+ public enum OpenableSettingsEnum implements LoaderSettingsEnum {
+
+ NOOP {
+ @Override
+ public int getPos() {
+ return 2;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderOpenable parent, int power, long ticks) {
+ return new SWItem(Material.STRUCTURE_VOID, translateItemName(parent.name, "LOADER_INTERACTION_NOOP", player));
+ }
+
+ @Override
+ public void execute(Location location, Openable blockData, LoaderOpenable parent, int power, long ticks, Consumer delay) {
+ }
+ },
+
+ INTERACT {
+ @Override
+ public int getPos() {
+ return 3;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderOpenable parent, int power, long ticks) {
+ return new SWItem(Material.STICK, translateItemName(parent.name, "LOADER_INTERACTION_INTERACT", player));
+ }
+
+ @Override
+ public void execute(Location location, Openable blockData, LoaderOpenable parent, int power, long ticks, Consumer delay) {
+ blockData.setOpen(!blockData.isOpen());
+ parent.update(blockData);
+ }
+ },
+
+ CLOSED {
+ @Override
+ public int getPos() {
+ return 5;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderOpenable parent, int power, long ticks) {
+ return new SWItem(parent.material, translateItemName(parent.name, "LOADER_INTERACTION_CLOSED", player));
+ }
+
+ @Override
+ public void execute(Location location, Openable blockData, LoaderOpenable parent, int power, long ticks, Consumer delay) {
+ blockData.setOpen(false);
+ parent.update(blockData);
+ }
+ },
+
+ OPEN {
+ @Override
+ public int getPos() {
+ return 6;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderOpenable parent, int power, long ticks) {
+ return new SWItem(parent.material, translateItemName(parent.name, "LOADER_INTERACTION_OPEN", player));
+ }
+
+ @Override
+ public void execute(Location location, Openable blockData, LoaderOpenable parent, int power, long ticks, Consumer delay) {
+ blockData.setOpen(true);
+ parent.update(blockData);
+ }
+ }
+ }
+
+ @Override
+ public SWItem menu(Player player) {
+ return new SWItem(material, translateItemName(name, player));
+ }
+
+ @Override
+ public boolean checkBlockInWorld() {
+ return location.getBlock().getType() == material;
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/elements/impl/LoaderRepeater.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/elements/impl/LoaderRepeater.java
new file mode 100644
index 00000000..d40e4211
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/elements/impl/LoaderRepeater.java
@@ -0,0 +1,169 @@
+/*
+ * 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.loader.elements.impl;
+
+import de.steamwar.bausystem.BauSystem;
+import de.steamwar.bausystem.features.loader.elements.LoaderInteractionElement;
+import de.steamwar.bausystem.features.loader.elements.LoaderSettingsEnum;
+import de.steamwar.inventory.SWItem;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.block.data.type.Repeater;
+import org.bukkit.entity.Player;
+
+import java.util.function.Consumer;
+
+public class LoaderRepeater extends LoaderInteractionElement {
+
+ public LoaderRepeater(Location location) {
+ super(location, RepeaterSettingsEnum.INTERACT, RepeaterSettingsEnum.NOOP, RepeaterSettingsEnum.values());
+ }
+
+ public enum RepeaterSettingsEnum implements LoaderSettingsEnum {
+
+ NOOP {
+ @Override
+ public int getPos() {
+ return 1;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderRepeater parent, int power, long ticks) {
+ return new SWItem(Material.STRUCTURE_VOID, translateItemName("LOADER_BUTTON_REPEATER", "LOADER_INTERACTION_NOOP", player));
+ }
+
+ @Override
+ public void execute(Location location, Repeater blockData, LoaderRepeater parent, int power, long ticks, Consumer delay) {
+
+ }
+ },
+
+ INTERACT {
+ @Override
+ public int getPos() {
+ return 2;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderRepeater parent, int power, long ticks) {
+ return new SWItem(Material.STICK, translateItemName("LOADER_BUTTON_REPEATER", "LOADER_INTERACTION_INTERACT", player));
+ }
+
+ @Override
+ public void execute(Location location, Repeater blockData, LoaderRepeater parent, int power, long ticks, Consumer delay) {
+ int repeaterDelay = blockData.getDelay();
+ repeaterDelay++;
+ if (repeaterDelay > 4) repeaterDelay = 1;
+ blockData.setDelay(repeaterDelay);
+ parent.update(blockData);
+ }
+ },
+
+ DELAY_1 {
+ @Override
+ public int getPos() {
+ return 4;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderRepeater parent, int power, long ticks) {
+ SWItem swItem = new SWItem(Material.REPEATER, BauSystem.MESSAGE.parse("LOADER_SETTING_REPEATER", player, 1));
+ swItem.getItemStack().setAmount(1);
+ return swItem;
+ }
+
+ @Override
+ public void execute(Location location, Repeater blockData, LoaderRepeater parent, int power, long ticks, Consumer delay) {
+ blockData.setDelay(1);
+ parent.update(blockData);
+ }
+ },
+
+ DELAY_2 {
+ @Override
+ public int getPos() {
+ return 5;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderRepeater parent, int power, long ticks) {
+ SWItem swItem = new SWItem(Material.REPEATER, BauSystem.MESSAGE.parse("LOADER_SETTING_REPEATER", player, 2));
+ swItem.getItemStack().setAmount(2);
+ return swItem;
+ }
+
+ @Override
+ public void execute(Location location, Repeater blockData, LoaderRepeater parent, int power, long ticks, Consumer delay) {
+ blockData.setDelay(2);
+ parent.update(blockData);
+ }
+ },
+
+ DELAY_3 {
+ @Override
+ public int getPos() {
+ return 6;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderRepeater parent, int power, long ticks) {
+ SWItem swItem = new SWItem(Material.REPEATER, BauSystem.MESSAGE.parse("LOADER_SETTING_REPEATER", player, 3));
+ swItem.getItemStack().setAmount(3);
+ return swItem;
+ }
+
+ @Override
+ public void execute(Location location, Repeater blockData, LoaderRepeater parent, int power, long ticks, Consumer delay) {
+ blockData.setDelay(3);
+ parent.update(blockData);
+ }
+ },
+
+ DELAY_4 {
+ @Override
+ public int getPos() {
+ return 7;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderRepeater parent, int power, long ticks) {
+ SWItem swItem = new SWItem(Material.REPEATER, BauSystem.MESSAGE.parse("LOADER_SETTING_REPEATER", player, 4));
+ swItem.getItemStack().setAmount(4);
+ return swItem;
+ }
+
+ @Override
+ public void execute(Location location, Repeater blockData, LoaderRepeater parent, int power, long ticks, Consumer delay) {
+ blockData.setDelay(4);
+ parent.update(blockData);
+ }
+ }
+ }
+
+ @Override
+ public SWItem menu(Player player) {
+ return new SWItem(Material.REPEATER, translateItemName("LOADER_BUTTON_REPEATER", player));
+ }
+
+ @Override
+ public boolean checkBlockInWorld() {
+ return location.getBlock().getType() == Material.REPEATER;
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/elements/impl/LoaderTNT.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/elements/impl/LoaderTNT.java
new file mode 100644
index 00000000..f0ee034e
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/elements/impl/LoaderTNT.java
@@ -0,0 +1,98 @@
+/*
+ * 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.loader.elements.impl;
+
+import de.steamwar.bausystem.BauSystem;
+import de.steamwar.bausystem.features.loader.elements.LoaderInteractionElement;
+import de.steamwar.bausystem.features.loader.elements.LoaderSettingsEnum;
+import de.steamwar.inventory.SWItem;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.block.Block;
+import org.bukkit.block.data.BlockData;
+import org.bukkit.entity.Player;
+
+import java.util.Arrays;
+import java.util.function.Consumer;
+
+public class LoaderTNT extends LoaderInteractionElement {
+
+ public LoaderTNT(Location location) {
+ super(location, TNTSettingsEnum.PLACE, TNTSettingsEnum.NOOP, TNTSettingsEnum.values());
+ }
+
+ public enum TNTSettingsEnum implements LoaderSettingsEnum {
+ NOOP {
+ @Override
+ public int getPos() {
+ return 2;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderTNT parent, int power, long ticks) {
+ return new SWItem(Material.STRUCTURE_VOID, translateItemName("LOADER_BUTTON_TNT", "LOADER_INTERACTION_NOOP", player));
+ }
+
+ @Override
+ public void execute(Location location, BlockData blockData, LoaderTNT parent, int power, long ticks, Consumer delay) {
+ }
+ },
+
+ PLACE {
+ @Override
+ public int getPos() {
+ return 3;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderTNT parent, int power, long ticks) {
+ return new SWItem(Material.TNT, translateItemName("LOADER_BUTTON_TNT", "LOADER_INTERACTION_PLACE", player));
+ }
+
+ @Override
+ public void execute(Location location, BlockData blockData, LoaderTNT parent, int power, long ticks, Consumer delay) {
+ location.getBlock().setType(Material.TNT);
+ }
+ }
+ }
+
+ @Override
+ public SWItem menu(Player player) {
+ SWItem item = new SWItem(Material.TNT, BauSystem.MESSAGE.parse("LOADER_SETTING_TNT_NAME", player));
+ item.setLore(Arrays.asList(BauSystem.MESSAGE.parse("LOADER_SETTING_TNT_X", player, location.getBlockX()), BauSystem.MESSAGE.parse("LOADER_SETTING_TNT_Y", player, location.getBlockY()), BauSystem.MESSAGE.parse("LOADER_SETTING_TNT_Z", player, location.getBlockZ())));
+ return item;
+ }
+
+ @Override
+ public void execute(Consumer delay) {
+ Block block = location.getBlock();
+ if (block.getType() != Material.AIR && block.getType() != Material.WATER) {
+ delay.accept(1L);
+ return;
+ }
+
+ super.execute(delay);
+ }
+
+ @Override
+ public boolean checkBlockInWorld() {
+ return true;
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/elements/impl/LoaderTicks.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/elements/impl/LoaderTicks.java
new file mode 100644
index 00000000..32e65f61
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/elements/impl/LoaderTicks.java
@@ -0,0 +1,116 @@
+/*
+ * 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.loader.elements.impl;
+
+import de.steamwar.bausystem.BauSystem;
+import de.steamwar.bausystem.features.loader.elements.LoaderInteractionElement;
+import de.steamwar.bausystem.features.loader.elements.LoaderSettingsEnum;
+import de.steamwar.inventory.SWItem;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.block.data.Powerable;
+import org.bukkit.entity.Player;
+
+import java.util.function.Consumer;
+
+public class LoaderTicks extends LoaderInteractionElement {
+
+ private String name;
+ private Material material;
+ private int ticks;
+
+ public LoaderTicks(Location location, String name, Material material, int ticks) {
+ super(location, TicksSettingsEnum.NO_WAIT_FOR, TicksSettingsEnum.NOOP, TicksSettingsEnum.values());
+ this.name = name;
+ this.material = material;
+ this.ticks = ticks;
+ }
+
+ public enum TicksSettingsEnum implements LoaderSettingsEnum {
+
+ NOOP {
+ @Override
+ public int getPos() {
+ return 2;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderTicks parent, int power, long ticks) {
+ return new SWItem(Material.STRUCTURE_VOID, translateItemName(parent.name, "LOADER_INTERACTION_NOOP", player));
+ }
+
+ @Override
+ public void execute(Location location, Powerable blockData, LoaderTicks parent, int power, long ticks, Consumer delay) {
+ }
+ },
+
+ NO_WAIT_FOR {
+ @Override
+ public int getPos() {
+ return 5;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderTicks parent, int power, long ticks) {
+ return new SWItem(parent.material, translateItemName(parent.name, "LOADER_INTERACTION_NO_WAIT_FOR", player, ticks));
+ }
+
+ @Override
+ public void execute(Location location, Powerable blockData, LoaderTicks parent, int power, long ticks, Consumer delay) {
+ Powerable powerable = (Powerable) location.getBlock().getBlockData();
+ powerable.setPowered(true);
+ parent.update(powerable);
+
+ BauSystem.runTaskLater(BauSystem.getInstance(), () -> {
+ powerable.setPowered(false);
+ parent.update(powerable);
+ }, parent.ticks);
+ }
+ },
+
+ WAIT_FOR {
+ @Override
+ public int getPos() {
+ return 6;
+ }
+
+ @Override
+ public SWItem menu(Player player, LoaderTicks parent, int power, long ticks) {
+ return new SWItem(parent.material, translateItemName(parent.name, "LOADER_INTERACTION_WAIT_FOR", player, ticks));
+ }
+
+ @Override
+ public void execute(Location location, Powerable blockData, LoaderTicks parent, int power, long ticks, Consumer delay) {
+ NO_WAIT_FOR.execute(location, blockData, parent, power, ticks, delay);
+ delay.accept((long) parent.ticks);
+ }
+ }
+ }
+
+ @Override
+ public SWItem menu(Player player) {
+ return new SWItem(material, translateItemName(name, player));
+ }
+
+ @Override
+ public boolean checkBlockInWorld() {
+ return location.getBlock().getType() == material;
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/elements/impl/LoaderWait.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/elements/impl/LoaderWait.java
new file mode 100644
index 00000000..41fcb977
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loader/elements/impl/LoaderWait.java
@@ -0,0 +1,89 @@
+/*
+ * 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.loader.elements.impl;
+
+import de.steamwar.bausystem.BauSystem;
+import de.steamwar.bausystem.features.loader.elements.LoaderElement;
+import de.steamwar.inventory.SWAnvilInv;
+import de.steamwar.inventory.SWInventory;
+import de.steamwar.inventory.SWItem;
+import lombok.Getter;
+import lombok.Setter;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+
+import java.util.Arrays;
+import java.util.function.Consumer;
+
+public class LoaderWait implements LoaderElement {
+
+ @Getter
+ @Setter
+ private long delay;
+
+ public LoaderWait(long delay) {
+ this.delay = delay;
+ }
+
+ @Override
+ public SWItem menu(Player player) {
+ SWItem swItem = new SWItem(Material.CLOCK, BauSystem.MESSAGE.parse("LOADER_SETTING_WAIT", player, delay));
+ swItem.getItemStack().setAmount((int) Math.max(Math.min(delay, 64), 1));
+ if (delay == 0) swItem.setEnchanted(true);
+ swItem.setLore(Arrays.asList(BauSystem.MESSAGE.parse("LOADER_GUI_CLICK_TO_EDIT", player)));
+ return swItem;
+ }
+
+ @Override
+ public void execute(Consumer wait) {
+ wait.accept(delay);
+ }
+
+ @Override
+ public void click(Player player, Runnable backAction) {
+ SWInventory swInventory = new SWInventory(player, 18, BauSystem.MESSAGE.parse("LOADER_GUI_WAIT_TITLE", player));
+ for (int i = 9; i < 18; i++) swInventory.setItem(i, new SWItem(Material.GRAY_STAINED_GLASS_PANE, "§7"));
+ swInventory.setItem(9, new SWItem(Material.ARROW, BauSystem.MESSAGE.parse("LOADER_GUI_WAIT_BACK", player)).getItemStack(), clickType -> backAction.run());
+
+ swInventory.setItem(3, new SWItem(SWItem.getDye(1), BauSystem.MESSAGE.parse("LOADER_SETTING_TICKS_REMOVE_ONE", player), Arrays.asList(BauSystem.MESSAGE.parse("LOADER_SETTING_TICKS_REMOVE_ONE_SHIFT", player)), false, clickType -> {}).getItemStack(), clickType -> {
+ delay -= clickType.isShiftClick() ? 5 : 1;
+ if (delay < 0) delay = 0;
+ swInventory.setItem(4, menu(player));
+ });
+ swInventory.setItem(4, menu(player).getItemStack(), clickType -> {
+ SWAnvilInv swAnvilInv = new SWAnvilInv(player, BauSystem.MESSAGE.parse("LOADER_SETTING_WAIT_NAME", player), delay + "");
+ swAnvilInv.setCallback(s -> {
+ try {
+ delay = Long.parseLong(s);
+ if (delay < 0) delay = 0;
+ } catch (NumberFormatException ignored) {
+ }
+ click(player, backAction);
+ });
+ swAnvilInv.open();
+ });
+ swInventory.setItem(5, new SWItem(SWItem.getDye(10), BauSystem.MESSAGE.parse("LOADER_SETTING_TICKS_ADD_ONE", player), Arrays.asList(BauSystem.MESSAGE.parse("LOADER_SETTING_TICKS_ADD_ONE_SHIFT", player)), false, clickType -> {}).getItemStack(), clickType -> {
+ delay += clickType.isShiftClick() ? 5 : 1;
+ swInventory.setItem(4, menu(player));
+ });
+
+ swInventory.open();
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loadtimer/Loadtimer.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loadtimer/Loadtimer.java
new file mode 100644
index 00000000..116edba5
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loadtimer/Loadtimer.java
@@ -0,0 +1,234 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2021 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.loadtimer;
+
+import de.steamwar.bausystem.BauSystem;
+import de.steamwar.bausystem.features.tpslimit.TPSUtils;
+import de.steamwar.bausystem.region.Region;
+import de.steamwar.bausystem.region.utils.RegionExtensionType;
+import de.steamwar.bausystem.region.utils.RegionType;
+import lombok.Getter;
+import org.bukkit.Bukkit;
+import org.bukkit.Material;
+import org.bukkit.boss.BarColor;
+import org.bukkit.boss.BarStyle;
+import org.bukkit.boss.BossBar;
+import org.bukkit.entity.Player;
+import org.bukkit.event.HandlerList;
+import org.bukkit.event.Listener;
+import org.bukkit.event.block.Action;
+import org.bukkit.event.block.BlockPlaceEvent;
+import org.bukkit.event.entity.EntityExplodeEvent;
+import org.bukkit.event.player.PlayerInteractEvent;
+import org.bukkit.scheduler.BukkitTask;
+
+import java.text.DecimalFormat;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class Loadtimer implements Listener {
+
+ @Getter
+ private static final Map timers = new HashMap<>();
+ private final Region region;
+ private final Map tntPlaced = new HashMap<>();
+ private final BukkitTask task;
+ private final Map bars = new HashMap<>();
+ private Stage stage;
+ private final boolean finishOnActive;
+
+ private long start = -1;
+ private long activate = -1;
+ private long ignite = -1;
+ private long explode = -1;
+
+ public Loadtimer(Region region, boolean finishOnActive) {
+ this.finishOnActive = finishOnActive;
+ this.region = region;
+ this.stage = Stage.WAITING;
+ Bukkit.getPluginManager().registerEvents(this, BauSystem.getInstance());
+ task = BauSystem.runTaskTimer(BauSystem.getInstance(), () -> {
+ if (stage == Stage.COUNTING) {
+ long timeSinceStart = TPSUtils.currentRealTick.get() - start;
+ long timeSinceHalf = timeSinceStart / 2;
+ double timeSec = (timeSinceStart / 20d);
+ String sec = new DecimalFormat("#.#").format(timeSec);
+ AtomicInteger tnt = new AtomicInteger();
+ tntPlaced.forEach((player, integer) -> tnt.addAndGet(integer));
+ sendBarToRegion(BarStyle.SEGMENTED_20, "LOADTIMER_BOSSBAR", BarColor.GREEN, (timeSinceStart % 20d) / 20, timeSinceStart, timeSinceHalf, sec, tnt);
+ } else if (stage == Stage.WAITING) {
+ sendBarToRegion(BarStyle.SOLID, "LOADTIMER_WAITING", BarColor.GREEN, 1);
+ } else if (stage == Stage.ACTIVATED || stage == Stage.IGNITION) {
+ sendBarToRegion(BarStyle.SOLID, stage == Stage.ACTIVATED ? "LOADTIMER_ACTIVATED" : "LOADTIMER_IGNITION", BarColor.YELLOW, 1);
+ }
+ }, 1, 1);
+ }
+
+ private void sendBarToRegion(BarStyle style, String message, BarColor color, double progress, Object... objects) {
+ Bukkit.getOnlinePlayers().forEach(player -> {
+ if (Region.getRegion(player.getLocation()).equals(region)) {
+ BossBar bar = getOrDefault(player);
+ bar.setStyle(style);
+ bar.setTitle(BauSystem.MESSAGE.parse(message, player, objects));
+ bar.setColor(color);
+ bar.setProgress(progress);
+ } else if (bars.containsKey(player)) {
+ bars.remove(player).removeAll();
+ }
+ });
+ }
+
+ public static boolean hasTimer(Region r) {
+ return timers.containsKey(r);
+ }
+
+ public static Loadtimer getTimer(Region r) {
+ return timers.get(r);
+ }
+
+ public static Loadtimer createLoadtimer(Region r, boolean finishOnActive) {
+ return timers.computeIfAbsent(r, region1 -> new Loadtimer(region1, finishOnActive));
+ }
+
+ private BossBar getOrDefault(Player player) {
+ return bars.computeIfAbsent(player, player1 -> {
+ BossBar bar1 = Bukkit.createBossBar("%PLACEHOLDER%", BarColor.GREEN, BarStyle.SEGMENTED_20);
+ bar1.addPlayer(player1);
+ return bar1;
+ });
+ }
+
+ public void onTntPlace(BlockPlaceEvent event) {
+ if (stage == Stage.WAITING) {
+ this.stage = Stage.COUNTING;
+ this.start = TPSUtils.currentRealTick.get();
+ }
+
+ if (stage == Stage.COUNTING) {
+ tntPlaced.put(event.getPlayer(), tntPlaced.getOrDefault(event.getPlayer(), 0) + 1);
+ }
+ }
+
+ public void onInteract(PlayerInteractEvent event) {
+ if (stage == Stage.COUNTING) {
+ if (event.getAction() == Action.RIGHT_CLICK_BLOCK) {
+ Material type = event.getClickedBlock().getType();
+ if (type == Material.IRON_TRAPDOOR) {
+ return;
+ }
+ if (type.name().contains("_BUTTON") || type == Material.LEVER || type.name().contains("_TRAPDOOR") || type == Material.NOTE_BLOCK) {
+ setActivate();
+ }
+ } else if (event.getAction() == Action.PHYSICAL) {
+ setActivate();
+ }
+ }
+ }
+
+ public void onTntSpawn() {
+ if ((stage == Stage.COUNTING || stage == Stage.ACTIVATED)) {
+ stage = Stage.IGNITION;
+ ignite = TPSUtils.currentRealTick.get();
+ if (activate == -1)
+ activate = TPSUtils.currentRealTick.get();
+ if (finishOnActive) {
+ stage = Stage.END;
+ print();
+ delete();
+ }
+ }
+ }
+
+ public void onTntExplode(EntityExplodeEvent event) {
+ if (region.inRegion(event.getLocation(), RegionType.BUILD, RegionExtensionType.EXTENSION) && stage == Stage.IGNITION) {
+ stage = Stage.END;
+ explode = TPSUtils.currentRealTick.get();
+ print();
+ delete();
+ }
+ }
+
+ private void setActivate() {
+ activate = TPSUtils.currentRealTick.get();
+ stage = Stage.ACTIVATED;
+ if (finishOnActive) {
+ print();
+ delete();
+ }
+ }
+
+ public void print() {
+ long loadTime = activate - start;
+ int allTnt = 0;
+ for (Map.Entry e : tntPlaced.entrySet()) {
+ allTnt += e.getValue();
+ }
+
+ long ignTime = ignite - activate;
+ long explTime = explode - ignTime - activate;
+ if (explTime < 0)
+ explTime = loadTime;
+
+ int finalAllTnt = allTnt;
+ long finalExplTime = explTime;
+ Bukkit.getOnlinePlayers().forEach(player -> {
+ if (Region.getRegion(player.getLocation()).equals(region)) {
+ BauSystem.MESSAGE.sendPrefixless("LOADTIMER_SUMARY_HEAD", player);
+ BauSystem.MESSAGE.sendPrefixless("LOADTIMER_SUMARY_PLAYERTABLE_HEAD", player);
+ for (Map.Entry e : tntPlaced.entrySet()) {
+ BauSystem.MESSAGE.sendPrefixless("LOADTIMER_SUMARY_PLAYERTABLE_PLAYER", player, e.getKey().getName(), e.getValue(), new DecimalFormat("#.#").format(e.getValue() / (loadTime / 20D)));
+ }
+ if (tntPlaced.size() > 1) {
+ BauSystem.MESSAGE.sendPrefixless("LOADTIMER_SUMARY_PLAYERTABLE_PLAYER", player, BauSystem.MESSAGE.parse("LOADTIMER_SUMARY_PLAYERTABLE_ALL", player), finalAllTnt, new DecimalFormat("#.#").format(finalAllTnt / (loadTime / 20D)));
+ }
+ player.sendMessage("");
+ BauSystem.MESSAGE.sendPrefixless("LOADTIMER_SUMARY_TIMES_HEAD", player);
+ BauSystem.MESSAGE.sendPrefixless("LOADTIMER_SUMARY_TIMES_START", player);
+ BauSystem.MESSAGE.sendPrefixless("LOADTIMER_SUMARY_TIMES_ACTIVATION", player, new DecimalFormat("#.#").format((loadTime / 20D)), loadTime);
+ if (!finishOnActive) {
+ BauSystem.MESSAGE.sendPrefixless("LOADTIMER_SUMARY_TIMES_IGNITION", player, new DecimalFormat("#.#").format((ignTime / 20D)), ignTime);
+ BauSystem.MESSAGE.sendPrefixless("LOADTIMER_SUMARY_TIMES_EXPLOSION", player, new DecimalFormat("#.#").format((finalExplTime / 20D)), finalExplTime);
+ }
+ BauSystem.MESSAGE.sendPrefixless("LOADTIMER_SUMARY_TIMES_LAST", player);
+ player.sendMessage("");
+ BauSystem.MESSAGE.sendPrefixless("LOADTIMER_SUMARY_STATS_HEAD", player);
+ BauSystem.MESSAGE.sendPrefixless("LOADTIMER_SUMARY_STATS_TNT", player, finalAllTnt);
+ BauSystem.MESSAGE.sendPrefixless("LOADTIMER_SUMARY_STATS_FREQ", player, 60D / (loadTime / 20D), 60D / ((finalExplTime + Math.max(ignTime, 0) + loadTime) / 20D));
+ }
+ });
+ }
+
+ public void delete() {
+ HandlerList.unregisterAll(this);
+ timers.remove(region, this);
+ bars.forEach((player, bossBar) -> bossBar.removeAll());
+ bars.clear();
+ task.cancel();
+ }
+
+ private enum Stage {
+ WAITING,
+ COUNTING,
+ ACTIVATED,
+ IGNITION,
+ END
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loadtimer/LoadtimerCommand.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loadtimer/LoadtimerCommand.java
new file mode 100644
index 00000000..503c1b47
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loadtimer/LoadtimerCommand.java
@@ -0,0 +1,59 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2021 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.loadtimer;
+
+import de.steamwar.bausystem.region.Region;
+import de.steamwar.command.SWCommand;
+import de.steamwar.linkage.Linked;
+import org.bukkit.entity.Player;
+
+@Linked
+public class LoadtimerCommand extends SWCommand {
+ public LoadtimerCommand() {
+ super("loadtimer", "lt", "stopuhr");
+ addDefaultHelpMessage("LOADTIMER_HELP_OVERVIEW");
+ }
+
+ @Register(value = "start", description = "LOADTIMER_HELP_START_1")
+ public void start(@Validator Player p) {
+ start(p, TimerMode.HALF);
+ }
+
+ @Register(value = "start", description = {"LOADTIMER_HELP_START_2", "LOADTIMER_HELP_START_3"})
+ public void start(@Validator Player p, TimerMode mode) {
+ Region r = Region.getRegion(p.getLocation());
+ if (r.isGlobal()) return;
+ if (!Loadtimer.hasTimer(r))
+ Loadtimer.createLoadtimer(r, mode == TimerMode.HALF);
+ }
+
+ @Register(value = "stop", description = "LOADTIMER_HELP_STOP")
+ public void stop(@Validator Player p) {
+ Region r = Region.getRegion(p.getLocation());
+ if (r.isGlobal()) return;
+ if (Loadtimer.hasTimer(r))
+ Loadtimer.getTimer(r).delete();
+ }
+
+ public enum TimerMode {
+ FULL,
+ HALF
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loadtimer/LoadtimerGuiItem.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loadtimer/LoadtimerGuiItem.java
new file mode 100644
index 00000000..f190d9f0
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loadtimer/LoadtimerGuiItem.java
@@ -0,0 +1,76 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2021 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.loadtimer;
+
+import de.steamwar.bausystem.BauSystem;
+import de.steamwar.bausystem.Permission;
+import de.steamwar.bausystem.linkage.specific.BauGuiItem;
+import de.steamwar.bausystem.region.Region;
+import de.steamwar.inventory.SWInventory;
+import de.steamwar.inventory.SWItem;
+import de.steamwar.linkage.Linked;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.event.inventory.ClickType;
+import org.bukkit.inventory.ItemStack;
+
+@Linked
+public class LoadtimerGuiItem extends BauGuiItem {
+
+ public LoadtimerGuiItem() {
+ super(28);
+ }
+
+ @Override
+ public Permission permission() {
+ return Permission.MEMBER;
+ }
+
+ @Override
+ public ItemStack getItem(Player player) {
+ Region r = Region.getRegion(player.getLocation());
+ if (r.isGlobal())
+ return new SWItem(Material.BOWL, BauSystem.MESSAGE.parse("LOADTIMER_GUI_GLOBAL", player)).getItemStack();
+ if (Loadtimer.hasTimer(r)) {
+ return new SWItem(Material.BOW, BauSystem.MESSAGE.parse("LOADTIMER_GUI_STOP", player)).getItemStack();
+ } else {
+ return new SWItem(Material.BOW, BauSystem.MESSAGE.parse("LOADTIMER_GUI_START", player)).getItemStack();
+ }
+ }
+
+ @Override
+ public boolean click(ClickType click, Player p) {
+ Region r = Region.getRegion(p.getLocation());
+ if (r.isGlobal()) return false;
+ if (Loadtimer.hasTimer(r)) {
+ p.performCommand("lt stop");
+ } else {
+ SWInventory inv = new SWInventory(p, 9, BauSystem.MESSAGE.parse("LOADTIMER_GUI_TITLE", p));
+ inv.setItem(1, Material.OAK_PLANKS, BauSystem.MESSAGE.parse("LOADTIMER_GUI_FULL", p), clickType -> {
+ p.performCommand("lt start full");
+ });
+ inv.setItem(7, Material.OAK_SLAB, BauSystem.MESSAGE.parse("LOADTIMER_GUI_HALF", p), clickType -> {
+ p.performCommand("lt start half");
+ });
+ inv.open();
+ }
+ return true;
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loadtimer/LoadtimerListener.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loadtimer/LoadtimerListener.java
new file mode 100644
index 00000000..ddbaed9a
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/loadtimer/LoadtimerListener.java
@@ -0,0 +1,77 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2021 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.loadtimer;
+
+import de.steamwar.bausystem.region.Region;
+import de.steamwar.linkage.Linked;
+import org.bukkit.Material;
+import org.bukkit.entity.EntityType;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.block.BlockPlaceEvent;
+import org.bukkit.event.entity.EntityExplodeEvent;
+import org.bukkit.event.entity.EntitySpawnEvent;
+import org.bukkit.event.player.PlayerInteractEvent;
+
+import static de.steamwar.bausystem.features.loadtimer.Loadtimer.*;
+
+@Linked
+public class LoadtimerListener implements Listener {
+
+ @EventHandler
+ public void onBlockPlace(BlockPlaceEvent event) {
+ if (!getTimers().isEmpty() && event.getBlockPlaced().getType() == Material.TNT) {
+ Region r = Region.getRegion(event.getBlock().getLocation());
+ if (hasTimer(r)) {
+ getTimer(r).onTntPlace(event);
+ }
+ }
+ }
+
+ @EventHandler
+ public void onPlayerInteract(PlayerInteractEvent event) {
+ if (!getTimers().isEmpty()) {
+ Region r = Region.getRegion(event.getPlayer().getLocation());
+ if (hasTimer(r)) {
+ getTimer(r).onInteract(event);
+ }
+ }
+ }
+
+ @EventHandler
+ public void onEntitySpawn(EntitySpawnEvent event) {
+ if (!getTimers().isEmpty() && event.getEntityType() == EntityType.PRIMED_TNT) {
+ Region r = Region.getRegion(event.getLocation());
+ if (hasTimer(r)) {
+ getTimer(r).onTntSpawn();
+ }
+ }
+ }
+
+ @EventHandler
+ public void onEntityExplode(EntityExplodeEvent event) {
+ if (!getTimers().isEmpty() && event.getEntityType() == EntityType.PRIMED_TNT) {
+ Region r = Region.getRegion(event.getLocation());
+ if (hasTimer(r)) {
+ getTimer(r).onTntExplode(event);
+ }
+ }
+ }
+}
diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/observer/ObserverTracer.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/observer/ObserverTracer.java
new file mode 100644
index 00000000..9f641e65
--- /dev/null
+++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/observer/ObserverTracer.java
@@ -0,0 +1,272 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2021 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.observer;
+
+import com.comphenix.tinyprotocol.Reflection;
+import de.steamwar.bausystem.region.Point;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.Particle;
+import org.bukkit.block.Block;
+import org.bukkit.block.BlockFace;
+import org.bukkit.block.data.Bisected;
+import org.bukkit.block.data.BlockData;
+import org.bukkit.block.data.type.Observer;
+import org.bukkit.block.data.type.*;
+import org.bukkit.entity.Player;
+
+import java.util.*;
+
+public class ObserverTracer {
+
+ private static final Set ALLOWED = EnumSet.of(BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST, BlockFace.WEST, BlockFace.UP, BlockFace.DOWN);
+
+ private Player player;
+ private Block block;
+ Set