diff --git a/SpigotCore_Main/src/de/steamwar/util/SchematicSelector.java b/SpigotCore_Main/src/de/steamwar/util/SchematicSelector.java
new file mode 100644
index 0000000..5cd4ef7
--- /dev/null
+++ b/SpigotCore_Main/src/de/steamwar/util/SchematicSelector.java
@@ -0,0 +1,438 @@
+/*
+ * 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.util;
+
+import com.google.common.collect.Lists;
+import de.steamwar.inventory.*;
+import de.steamwar.sql.SchematicNode;
+import de.steamwar.sql.SchematicType;
+import de.steamwar.sql.SteamwarUser;
+import lombok.*;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Consumer;
+
+public class SchematicSelector {
+
+ @Getter
+ private final Player player;
+ @Getter
+ private final SteamwarUser user;
+ @Getter
+ private final Consumer callback;
+ @Getter
+ private final SelectorTarget target;
+ @Getter
+ private final SelectorFilter filter = new SelectorFilter();
+ private SchematicSelectorInjectable injectable;
+ private boolean useHooks;
+ @Setter
+ @Getter
+ private PublicMode publicMode = PublicMode.ALL;
+ @Setter
+ @Getter
+ private String title = "{0} auswahl: {1}";
+ @Setter
+ @Getter
+ private boolean singleDirOpen;
+
+ public SchematicSelector(Player player, SelectorTarget target, Consumer callback) {
+ this.player = player;
+ this.user = SteamwarUser.get(player.getUniqueId());
+ this.target = target;
+ this.callback = callback;
+ this.singleDirOpen = !target.target.isDirs();
+ }
+
+ public SchematicSelector(Player player, SelectorTarget target, SchematicSelectorInjectable injectable, Consumer callback) {
+ this(player, target, callback);
+ this.useHooks = true;
+ this.injectable = injectable;
+ }
+
+ public void open() {
+ if(useHooks) {
+ injectable.onSelectorCreate(this);
+ }
+ openList(null, publicMode == PublicMode.PUBLIC_ONLY);
+ }
+
+ private void openList(SchematicNode parent, boolean publics) {
+ List nodes = new ArrayList<>();
+
+ if(filter.isFilter()) {
+ nodes.addAll(SchematicNode.getAllSchematicsAccessibleByUser(publics?0:user.getId()));
+ nodes.removeIf(node -> {
+ if(useHooks) {
+ injectable.onNodeFilter(this, node);
+ }
+ return !filter.matches(node);
+ });
+ if(target.target == SelectorTarget.Target.DIRECTORY) {
+ nodes.removeIf(node -> !node.isDir());
+ }
+ if(target.target == SelectorTarget.Target.SCHEMATIC_TYPE) {
+ nodes.removeIf(node -> node.isDir() || !node.getType().equals(target.type.toDB()));
+ }
+ } else {
+ switch (target.target) {
+ case DIRECTORY:
+ if(parent == null) {
+ nodes.addAll(SchematicNode.getSchematicsAccessibleByUser(publics?0:user.getId(), null));
+ nodes.removeIf(node -> !node.isDir());
+ } else {
+ nodes.addAll(SchematicNode.getSchematicDirectoryInNode(parent.getId()));
+ }
+ break;
+ case SCHEMATIC_TYPE:
+ nodes.addAll(SchematicNode.getAccessibleSchematicsOfTypeInParent(publics?0:user.getId(), target.type.toDB(), parent==null?0:parent.getId()));
+ if(target.rank >= 0) {
+ nodes.removeIf(node -> node.getRank() > target.rank);
+ }
+ break;
+ default:
+ nodes.addAll(SchematicNode.getSchematicsAccessibleByUser(publics?0:user.getId(), parent == null?0:parent.getId()));
+ }
+
+ if(singleDirOpen && nodes.size() == 1 && nodes.get(0).isDir()) {
+ openList(nodes.get(0), publics);
+ return;
+ }
+ }
+
+ List> list = new ArrayList<>();
+
+ if(parent != null) {
+ list.add(new SWListInv.SWListEntry<>(new SWItem(Material.ARROW, "§eZurück", clickType -> {}), null));
+ }
+
+ for (SchematicNode node : nodes) {
+ if(node.getName().equals("//copy")) continue;
+ Material m;
+ if (node.getItem().isEmpty())
+ m = node.isDir()?SWItem.getMaterial("CHEST"):SWItem.getMaterial("CAULDRON_ITEM");
+ else
+ m = SWItem.getMaterial(node.getItem());
+
+ String name = "§" + (filter.getName().isEmpty()?"e":"7") + node.getName();
+
+ if(!filter.getName().isEmpty()) {
+ name = name.replace(filter.getName(), "§e" + filter.getName() + "§7");
+ }
+
+ SWItem item = new SWItem(m, name, Collections.singletonList(node.isDir() ? "§9Ordner" : "§7" + node.getSchemtype().name()), !node.isDir() && !node.getSchemtype().writeable(), click -> {
+ });
+ if(!node.isDir()) {
+ if(node.getRank() > 0)
+ item.setLore(Lists.newArrayList("§7" + node.getSchemtype().name(), "§8Rang " + node.getRank()));
+ }
+ list.add(new SWListInv.SWListEntry<>(item, node));
+ }
+
+ SWListInv inv = new SWListInv<>(player, MessageFormat.format(title, target.target.getName(), filter.getName().isEmpty()?(parent == null?"/":parent.generateBreadcrumbs(user)):filter.getName()), false, list, (clickType, node) -> {
+ if(node == null) {
+ openList(getParent(parent), publics);
+ return;
+ }
+ if(node.isDir()) {
+ if(filter.isFilter() && target.target.isDirs()) {
+ player.closeInventory();
+ callback.accept(node);
+ return;
+ }
+ filter.reset();
+ openList(node, publics);
+ return;
+ }
+ player.closeInventory();
+ callback.accept(node);
+ });
+ if(publicMode == PublicMode.ALL) {
+ if(publics) {
+ inv.setItem(48, Material.BUCKET, "§7Eigene Schematics", clickType -> openList(null, false));
+ } else {
+ inv.setItem(48, Material.GLASS, "§7Public Schematics", clickType -> openList(null, true));
+ }
+ }
+ if(target.target.isDirs()) {
+ inv.setItem(49, SWItem.getDye(10), "§7Ordner auswählen", clickType -> {
+ player.closeInventory();
+ callback.accept(parent);
+ });
+ }
+ if(!publics) {
+ inv.setItem(50, Material.CHEST, "§7Neuer Ordner", clickType -> createFolderIn(parent));
+ }
+ inv.setItem(51, Material.NAME_TAG, "§7Filter", clickType -> openFilter(publics));
+
+ if(useHooks) {
+ injectable.onListRender(this, inv, parent);
+ }
+ inv.open();
+ }
+
+ private void createFolderIn(SchematicNode parent) {
+ SWAnvilInv inv = new SWAnvilInv(player, "Ordner Erstellen");
+ inv.setItem(Material.CHEST);
+ inv.setCallback(s -> {
+ if(useHooks) {
+ if(injectable.onFolderCreate(this, s)) {
+ SchematicNode.createSchematicDirectory(user.getId(), s, parent==null?0:parent.getId());
+ openList(parent, false);
+ }
+ } else {
+ SchematicNode.createSchematicDirectory(user.getId(), s, parent==null?0:parent.getId());
+ openList(parent, false);
+ }
+ });
+ inv.open();
+ }
+
+ private void openFilter(boolean publics) {
+ SWInventory inv = new SWInventory(player, 9, "Filter");
+ InvCallback nameCallback = clickType -> {
+ if(clickType.isRightClick()) {
+ filter.setName("");
+ openFilter(publics);
+ } else {
+ SWAnvilInv swAnvilInv = new SWAnvilInv(player, "Name eingeben");
+ swAnvilInv.setItem(Material.NAME_TAG);
+ swAnvilInv.setCallback(s -> {
+ filter.setName(s);
+ openFilter(publics);
+ });
+ swAnvilInv.open();
+ }
+ };
+ if(filter.getName().isEmpty()) {
+ inv.setItem(0, Material.NAME_TAG, "§7Nach namen suchen...", nameCallback);
+ } else {
+ inv.setItem(0, Material.NAME_TAG, "§7Nach namen suchen...", Collections.singletonList("§7Suchwort: §e" + filter.getName()), true, nameCallback);
+ }
+
+ InvCallback ownerCallback = clickType -> {
+ if(clickType.isRightClick()) {
+ filter.setOwner(null);
+ openFilter(publics);
+ } else {
+ SWAnvilInv swAnvilInv = new SWAnvilInv(player, "Besitzer eingeben");
+ swAnvilInv.setItem(Material.PLAYER_HEAD);
+ swAnvilInv.setCallback(s -> {
+ filter.setOwner(SteamwarUser.get(s).getId());
+ openFilter(publics);
+ });
+ swAnvilInv.open();
+ }
+ };
+ if(filter.getOwner() == null) {
+ inv.setItem(1, Material.PLAYER_HEAD, "§7Nach Besitzer suchen...", ownerCallback);
+ } else {
+ SteamwarUser user = SteamwarUser.get(filter.getOwner());
+ SWItem item = SWItem.getPlayerSkull(user.getUserName());
+ item.setName("§7Nach Besitzer suchen...");
+ item.setEnchanted(true);
+ item.setLore(Collections.singletonList("§7Besitzer: §e" + user.getUserName()));
+ item.setCallback(ownerCallback);
+ inv.setItem(1, item);
+ }
+
+ if(target.target != SelectorTarget.Target.SCHEMATIC_TYPE) {
+ InvCallback schemTypeCallback = clickType -> {
+ if(clickType.isRightClick()) {
+ filter.setType(null);
+ openFilter(publics);
+ } else {
+ List> types = new ArrayList<>();
+ SchematicType.values().forEach(schematicType -> {
+ types.add(new SWListInv.SWListEntry<>(new SWItem(SWItem.getMaterial("STONE_BUTTON"), "§e" + schematicType.name(), Collections.emptyList(), schematicType.fightType(), n -> {}), schematicType));
+ });
+ SWListInv listInv = new SWListInv<>(player, "Typ wählen...", types, (clickType1, schematicType) -> {
+ filter.setType(schematicType);
+ openFilter(publics);
+ });
+ listInv.open();
+ }
+ };
+
+ if(filter.getType() == null) {
+ inv.setItem(2, SWItem.getMaterial("STONE_BUTTON"), "§7Nach Typ Filtern...", schemTypeCallback);
+ } else {
+ inv.setItem(2, SWItem.getMaterial("STONE_BUTTON"), "§7Nach Typ Filtern...", Collections.singletonList("§7Typ: §e" + filter.getType().name()), true, schemTypeCallback);
+ }
+ }
+
+ InvCallback materialCallback = clickType -> {
+ if(clickType.isRightClick()) {
+ filter.setItem(null);
+ openFilter(publics);
+ } else {
+ UtilGui.openMaterialSelector(player, material -> {
+ filter.setItem(material);
+ openFilter(publics);
+ });
+ }
+ };
+
+ final int iSlot = target.target== SelectorTarget.Target.SCHEMATIC_TYPE?2:3;
+
+ if(filter.getItem() == null) {
+ inv.setItem(iSlot, Material.STONE, "§7Nach Item Filtern...", materialCallback);
+ } else {
+ inv.setItem(iSlot, filter.getItem(), "§7Nach Item Filtern...", Collections.singletonList("§7Item: §e" + filter.getItem().name()), true, materialCallback);
+ }
+
+ inv.setItem(7, SWItem.getDye(1), "§eAbbrechen", clickType -> {
+ filter.reset();
+ openList(null, publics);
+ });
+ inv.setItem(8, SWItem.getDye(10), "§eSuchen...", clickType -> {
+ filter.setFilter(true);
+ if(useHooks) {
+ injectable.onFilterApply(this);
+ }
+ openList(null, publics);
+ });
+
+ if(useHooks) {
+ injectable.onFilterRender(this, inv);
+ }
+
+ inv.open();
+ }
+
+ private static SchematicNode getParent(SchematicNode node) {
+ if(node.getParent() == null) {
+ return null;
+ }
+ return node.getParentNode();
+ }
+
+ public static SelectorTarget selectSchematic() {
+ return new SelectorTarget(SelectorTarget.Target.SCHEMATIC, null, -1);
+ }
+
+ public static SelectorTarget selectDirectory() {
+ return new SelectorTarget(SelectorTarget.Target.DIRECTORY, null, -1);
+ }
+
+ public static SelectorTarget selectSchematicNode() {
+ return new SelectorTarget(SelectorTarget.Target.SCHEMATIC_NODE, null, -1);
+ }
+
+ public static SelectorTarget selectSchematicType(SchematicType type) {
+ return new SelectorTarget(SelectorTarget.Target.SCHEMATIC_TYPE, type, -1);
+ }
+
+ public static SelectorTarget selectSchematicTypeWithRank(SchematicType type, int rank) {
+ return new SelectorTarget(SelectorTarget.Target.SCHEMATIC_TYPE, type, rank);
+ }
+
+ private static class SelectorTarget {
+
+ private final Target target;
+ private final SchematicType type;
+ private final int rank;
+
+ private SelectorTarget(Target target, SchematicType type, int rank) {
+ this.target = target;
+ this.type = type;
+ this.rank = rank;
+ }
+
+ @AllArgsConstructor
+ private enum Target {
+ SCHEMATIC("Schematic", false),
+ DIRECTORY("Ordner", true),
+ SCHEMATIC_NODE("Schematic/Ordner", true),
+ SCHEMATIC_TYPE("Schematic", false);
+
+ @Getter
+ private String name;
+ @Getter
+ private boolean dirs;
+ }
+ }
+
+ @NoArgsConstructor
+ @Getter
+ @Setter
+ public static class SelectorFilter {
+
+ private boolean filter;
+
+ private String name = "";
+ private Integer owner = null;
+ private SchematicType type = null;
+ private Material item = null;
+
+ public void reset() {
+ name = "";
+ owner = null;
+ type = null;
+ item = null;
+ filter = false;
+ }
+
+ public boolean matches(SchematicNode node) {
+ boolean matches = true;
+ if(!name.isEmpty()) {
+ if(!node.getName().contains(name)) {
+ matches = false;
+ }
+ }
+
+ if(owner != null) {
+ if(node.getOwner() != owner) {
+ matches = false;
+ }
+ }
+
+ if(type != null) {
+ if(node.isDir() || !node.getType().equals(type.toDB())) {
+ matches = false;
+ }
+ }
+
+ if(item != null) {
+ String i;
+ if(node.getItem().isEmpty()) {
+ i = node.isDir()?"CHEST":"CAULDRON";
+ } else {
+ i = node.getItem();
+ }
+ if(!item.name().equals(i)) {
+ matches = false;
+ }
+ }
+ return matches;
+ }
+ }
+
+ public enum PublicMode {
+ ALL,
+ PRIVATE_ONLY,
+ PUBLIC_ONLY
+ }
+}
diff --git a/SpigotCore_Main/src/de/steamwar/util/SchematicSelectorInjectable.java b/SpigotCore_Main/src/de/steamwar/util/SchematicSelectorInjectable.java
new file mode 100644
index 0000000..35629d0
--- /dev/null
+++ b/SpigotCore_Main/src/de/steamwar/util/SchematicSelectorInjectable.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.util;
+
+import de.steamwar.inventory.SWInventory;
+import de.steamwar.inventory.SWListInv;
+import de.steamwar.sql.SchematicNode;
+
+public interface SchematicSelectorInjectable {
+
+ void onSelectorCreate(SchematicSelector selector);
+
+ void onListRender(SchematicSelector selector, SWListInv inv, SchematicNode parent);
+
+ void onFilterRender(SchematicSelector selector, SWInventory inventory);
+
+ void onFilterApply(SchematicSelector selector);
+
+ boolean onFolderCreate(SchematicSelector selector, String name);
+
+ void onNodeFilter(SchematicSelector selector, SchematicNode node);
+}
diff --git a/SpigotCore_Main/src/de/steamwar/util/SchematicSelectorInjectableAdapter.java b/SpigotCore_Main/src/de/steamwar/util/SchematicSelectorInjectableAdapter.java
new file mode 100644
index 0000000..883ac84
--- /dev/null
+++ b/SpigotCore_Main/src/de/steamwar/util/SchematicSelectorInjectableAdapter.java
@@ -0,0 +1,47 @@
+/*
+ * 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.util;
+
+import de.steamwar.inventory.SWInventory;
+import de.steamwar.inventory.SWListInv;
+import de.steamwar.sql.SchematicNode;
+import org.bukkit.entity.Player;
+
+public abstract class SchematicSelectorInjectableAdapter implements SchematicSelectorInjectable {
+ @Override
+ public void onSelectorCreate(SchematicSelector selector) {}
+
+ @Override
+ public void onListRender(SchematicSelector selector, SWListInv inv, SchematicNode parent) {}
+
+ @Override
+ public void onFilterRender(SchematicSelector selector, SWInventory inventory) {}
+
+ @Override
+ public void onFilterApply(SchematicSelector selector) {}
+
+ @Override
+ public boolean onFolderCreate(SchematicSelector selector, String name) {
+ return true;
+ }
+
+ @Override
+ public void onNodeFilter(SchematicSelector selector, SchematicNode node) {}
+}
diff --git a/SpigotCore_Main/src/de/steamwar/util/UtilGui.java b/SpigotCore_Main/src/de/steamwar/util/UtilGui.java
new file mode 100644
index 0000000..cfb9012
--- /dev/null
+++ b/SpigotCore_Main/src/de/steamwar/util/UtilGui.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.util;
+
+import de.steamwar.inventory.SWItem;
+import de.steamwar.inventory.SWListInv;
+import lombok.experimental.UtilityClass;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.function.Consumer;
+
+@UtilityClass
+public class UtilGui {
+
+ public static void openMaterialSelector(Player player, Consumer callback) {
+ openMaterialSelector(player, "Material auswählen", callback);
+ }
+
+ public static void openMaterialSelector(Player player, String title, Consumer callback) {
+ List> materials = new LinkedList<>();
+ for(Material material : Material.values()){
+ if(material.name().startsWith(Material.LEGACY_PREFIX))
+ continue;
+ SWItem item = new SWItem(material, "§7" + material.name());
+ if(item.getItemMeta() != null && material.isItem()) {
+ materials.add(new SWListInv.SWListEntry<>(item, material));
+ }
+ }
+
+ SWListInv swListInv = new SWListInv<>(player, title, materials, (clickType3, material) -> callback.accept(material));
+ swListInv.open();
+ }
+}