portalButtonMaterials;
//Economy information
private final int useCost;
@@ -41,34 +43,33 @@ public class Gate {
/**
* Instantiates a new gate
*
- * @param filename The name of the gate file, including extension
- * @param layout The gate layout defined in the gate file
- * @param characterMaterialMap The material types the different layout characters represent
- * @param characterTagMap The material tag types the different layout characters represent
- * @param portalOpenBlock The material to set the opening to when the portal is open
- * @param portalClosedBlock The material to set the opening to when the portal is closed
- * @param portalButton The material to use for the portal button
- * @param useCost The cost of using a portal with this gate layout (-1 to disable)
- * @param createCost The cost of creating a portal with this gate layout (-1 to disable)
- * @param destroyCost The cost of destroying a portal with this gate layout (-1 to disable)
- * @param toOwner Whether any payment should go to the owner of the gate, as opposed to just disappearing
+ * @param filename The name of the gate file, including extension
+ * @param layout The gate layout defined in the gate file
+ * @param characterMaterialsMap The material types the different layout characters represent
+ * @param portalOpenMaterials The material to set the opening to when the portal is open
+ * @param portalClosedMaterials The material to set the opening to when the portal is closed
+ * @param portalButtonMaterials The material to use for the portal button
+ * @param useCost The cost of using a portal with this gate layout (-1 to disable)
+ * @param createCost The cost of creating a portal with this gate layout (-1 to disable)
+ * @param destroyCost The cost of destroying a portal with this gate layout (-1 to disable)
+ * @param toOwner Whether any payment should go to the owner of the gate, as opposed to just disappearing
*/
public Gate(@NotNull String filename, @NotNull GateLayout layout,
- @NotNull Map characterMaterialMap,
- @NotNull Map> characterTagMap, @NotNull Material portalOpenBlock,
- @NotNull Material portalClosedBlock, @NotNull Material portalButton, int useCost, int createCost,
- int destroyCost, boolean toOwner) {
+ @NotNull Map> characterMaterialsMap,
+ @NotNull List portalOpenMaterials,
+ @NotNull List portalClosedMaterials,
+ @NotNull List portalButtonMaterials, int useCost, int createCost, int destroyCost,
+ boolean toOwner) {
this.filename = filename;
this.layout = layout;
- this.characterMaterialMap = characterMaterialMap;
- this.portalOpenBlock = portalOpenBlock;
- this.portalClosedBlock = portalClosedBlock;
- this.portalButton = portalButton;
+ this.characterMaterialMap = characterMaterialsMap;
+ this.portalOpenMaterials = portalOpenMaterials;
+ this.portalClosedMaterials = portalClosedMaterials;
+ this.portalButtonMaterials = portalButtonMaterials;
this.useCost = useCost;
this.createCost = createCost;
this.destroyCost = destroyCost;
this.toOwner = toOwner;
- this.characterTagMap = characterTagMap;
}
/**
@@ -87,7 +88,7 @@ public class Gate {
* @return The character to material map
*/
@NotNull
- public Map getCharacterMaterialMap() {
+ public Map> getCharacterMaterialMap() {
return new HashMap<>(characterMaterialMap);
}
@@ -98,18 +99,7 @@ public class Gate {
* @return True if the material is valid for control blocks
*/
public boolean isValidControlBlock(@NotNull Material material) {
- return (getControlBlock() != null) ? getControlBlock().equals(material) :
- getControlBlockTag().isTagged(material);
- }
-
- /**
- * Gets the material tag used for this gate's control blocks
- *
- * @return The material tag type used for control blocks
- */
- @NotNull
- public Tag getControlBlockTag() {
- return characterTagMap.get(GateHandler.getControlBlockCharacter());
+ return getControlBlockMaterials().contains(new BukkitMaterialSpecifier(material));
}
/**
@@ -117,8 +107,8 @@ public class Gate {
*
* @return The material type used for control blocks
*/
- @Nullable
- public Material getControlBlock() {
+ @NotNull
+ public List getControlBlockMaterials() {
return characterMaterialMap.get(GateHandler.getControlBlockCharacter());
}
@@ -138,8 +128,8 @@ public class Gate {
* @return The block type to use for the opening when open
*/
@NotNull
- public Material getPortalOpenBlock() {
- return portalOpenBlock;
+ public List getPortalOpenMaterials() {
+ return portalOpenMaterials;
}
/**
@@ -148,8 +138,8 @@ public class Gate {
* @return The block type to use for the opening when closed
*/
@NotNull
- public Material getPortalClosedBlock() {
- return portalClosedBlock;
+ public List getPortalClosedMaterials() {
+ return portalClosedMaterials;
}
/**
@@ -158,8 +148,8 @@ public class Gate {
* @return The material to use for a portal's button if using this gate type
*/
@NotNull
- public Material getPortalButton() {
- return portalButton;
+ public List getPortalButtonMaterials() {
+ return portalButtonMaterials;
}
/**
@@ -236,35 +226,27 @@ public class Gate {
* @return True if all border blocks of the gate match the layout
*/
private boolean verifyGateBorderMatches(@NotNull BlockLocation topLeft, double yaw) {
- Map characterMaterialMap = new HashMap<>(this.characterMaterialMap);
- Map> characterTagMap = new HashMap<>(this.characterTagMap);
+ Map> characterMaterialMap = new HashMap<>(this.characterMaterialMap);
for (RelativeBlockVector borderVector : layout.getBorder()) {
int rowIndex = borderVector.right();
int lineIndex = borderVector.down();
Character key = layout.getLayout()[lineIndex][rowIndex];
- Material materialInLayout = characterMaterialMap.get(key);
- Tag tagInLayout = characterTagMap.get(key);
+ List materialInLayout = characterMaterialMap.get(key);
Material materialAtLocation = topLeft.getRelativeLocation(borderVector, yaw).getType();
if (materialInLayout != null) {
- if (materialAtLocation != materialInLayout) {
+ if (!MaterialHelper.specifiersToMaterials(materialInLayout).contains(materialAtLocation)) {
Stargate.debug("Gate::Matches", String.format("Block Type Mismatch: %s != %s",
materialAtLocation, materialInLayout));
return false;
}
- } else if (tagInLayout != null) {
- if (!tagInLayout.isTagged(materialAtLocation)) {
- Stargate.debug("Gate::Matches", String.format("Block Type Mismatch: %s != %s",
- materialAtLocation, tagInLayout));
- return false;
- }
} else {
/* This generally should not happen with proper checking, but just in case a material character is not
* recognized, but still allowed in previous checks, verify the gate as long as all such instances of
* the character correspond to the same material in the physical gate. All subsequent gates will also
* need to match the first verified gate. */
- characterMaterialMap.put(key, materialAtLocation);
+ characterMaterialMap.put(key, List.of(new BukkitMaterialSpecifier(materialAtLocation)));
Stargate.debug("Gate::Matches", String.format("Missing layout material in %s. Using %s from the" +
" physical portal.", getFilename(), materialAtLocation));
}
@@ -291,7 +273,8 @@ public class Gate {
continue;
}
- if (type != portalClosedBlock && type != portalOpenBlock) {
+ if (!MaterialHelper.specifiersToMaterials(portalClosedMaterials).contains(type) &&
+ !MaterialHelper.specifiersToMaterials(portalOpenMaterials).contains(type)) {
Stargate.debug("Gate::Matches", "Entrance/Exit Material Mismatch: " + type);
return false;
}
@@ -311,9 +294,9 @@ public class Gate {
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(new File(gateFolder, filename)));
//Save main material names
- writeConfig(bufferedWriter, "portal-open", portalOpenBlock.name());
- writeConfig(bufferedWriter, "portal-closed", portalClosedBlock.name());
- writeConfig(bufferedWriter, "button", portalButton.name());
+ writeConfig(bufferedWriter, "portal-open", MaterialHelper.specifiersToString(portalOpenMaterials));
+ writeConfig(bufferedWriter, "portal-closed", MaterialHelper.specifiersToString(portalClosedMaterials));
+ writeConfig(bufferedWriter, "button", MaterialHelper.specifiersToString(portalButtonMaterials));
//Save the values necessary for economy
saveEconomyValues(bufferedWriter);
@@ -361,7 +344,7 @@ public class Gate {
* @throws IOException If unable to write to the buffered writer
*/
private void saveFrameBlockType(@NotNull BufferedWriter bufferedWriter) throws IOException {
- for (Map.Entry entry : this.characterMaterialMap.entrySet()) {
+ for (Map.Entry> entry : this.characterMaterialMap.entrySet()) {
Character key = entry.getKey();
//Skip characters not part of the frame
if (key.equals(GateHandler.getAnythingCharacter()) ||
@@ -369,11 +352,7 @@ public class Gate {
key.equals(GateHandler.getExitCharacter())) {
continue;
}
- saveFrameBlockType(key, entry.getValue().toString(), bufferedWriter);
- }
- for (Map.Entry> entry : this.characterTagMap.entrySet()) {
- saveFrameBlockType(entry.getKey(), "#" + entry.getValue().getKey().toString().replaceFirst(
- "minecraft:", ""), bufferedWriter);
+ saveFrameBlockType(key, MaterialHelper.specifiersToString(entry.getValue()), bufferedWriter);
}
}
diff --git a/src/main/java/net/knarcraft/stargate/portal/property/gate/GateHandler.java b/src/main/java/net/knarcraft/stargate/portal/property/gate/GateHandler.java
index ebdce4e..7a26ef4 100644
--- a/src/main/java/net/knarcraft/stargate/portal/property/gate/GateHandler.java
+++ b/src/main/java/net/knarcraft/stargate/portal/property/gate/GateHandler.java
@@ -1,7 +1,8 @@
package net.knarcraft.stargate.portal.property.gate;
import net.knarcraft.stargate.Stargate;
-import net.knarcraft.stargate.utility.GateReader;
+import net.knarcraft.stargate.config.material.BukkitMaterialSpecifier;
+import net.knarcraft.stargate.config.material.MaterialSpecifier;
import net.knarcraft.stargate.utility.MaterialHelper;
import org.bukkit.Bukkit;
import org.bukkit.Material;
@@ -18,6 +19,8 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
+import java.util.Set;
+import java.util.function.Predicate;
import static net.knarcraft.stargate.utility.GateReader.generateLayoutMatrix;
import static net.knarcraft.stargate.utility.GateReader.readGateConfig;
@@ -94,20 +97,13 @@ public class GateHandler {
private static void registerGate(@NotNull Gate gate) {
gates.put(gate.getFilename(), gate);
- Material blockId = gate.getControlBlock();
- if (blockId != null) {
- if (!controlBlocks.containsKey(blockId)) {
- controlBlocks.put(blockId, new ArrayList<>());
+ Set blockTypes = MaterialHelper.specifiersToMaterials(gate.getControlBlockMaterials());
+ for (Material material : blockTypes) {
+ if (!controlBlocks.containsKey(material)) {
+ controlBlocks.put(material, new ArrayList<>());
}
- controlBlocks.get(blockId).add(gate);
- return;
+ controlBlocks.get(material).add(gate);
}
-
- Tag materialTag = gate.getControlBlockTag();
- if (!controlBlockTags.containsKey(materialTag.getKey().toString())) {
- controlBlockTags.put(materialTag.getKey().toString(), new ArrayList<>());
- }
- controlBlockTags.get(materialTag.getKey().toString()).add(gate);
}
/**
@@ -138,24 +134,23 @@ public class GateHandler {
private static Gate loadGate(@NotNull String fileName, @NotNull String parentFolder,
@NotNull Scanner scanner) {
List> design = new ArrayList<>();
- Map characterMaterialMap = new HashMap<>();
- Map> characterTagMap = new HashMap<>();
+ Map> characterMaterialMap = new HashMap<>();
Map config = new HashMap<>();
//Initialize character to material map
- characterMaterialMap.put(ENTRANCE, Material.AIR);
- characterMaterialMap.put(EXIT, Material.AIR);
- characterMaterialMap.put(ANYTHING, Material.AIR);
+ characterMaterialMap.put(ENTRANCE, List.of(new BukkitMaterialSpecifier(Material.AIR)));
+ characterMaterialMap.put(EXIT, List.of(new BukkitMaterialSpecifier(Material.AIR)));
+ characterMaterialMap.put(ANYTHING, List.of(new BukkitMaterialSpecifier(Material.AIR)));
//Read the file into appropriate lists and maps
- int columns = readGateFile(scanner, characterMaterialMap, characterTagMap, fileName, design, config);
+ int columns = readGateFile(scanner, characterMaterialMap, fileName, design, config);
if (columns < 0) {
return null;
}
Character[][] layout = generateLayoutMatrix(design, columns);
//Create and validate the new gate
- Gate gate = createGate(config, fileName, layout, characterMaterialMap, characterTagMap);
+ Gate gate = createGate(config, fileName, layout, characterMaterialMap);
if (gate == null) {
return null;
}
@@ -172,28 +167,26 @@ public class GateHandler {
* @param fileName The name of the saved gate config file
* @param layout The layout matrix of the new gate
* @param characterMaterialMap A map between layout characters and the material to use
- * @param materialTagMap A map between layout characters and the material tags to use
* @return A new gate, or null if the config is invalid
*/
@Nullable
private static Gate createGate(@NotNull Map config, @NotNull String fileName,
@NotNull Character[][] layout,
- @NotNull Map characterMaterialMap,
- @NotNull Map> materialTagMap) {
+ @NotNull Map> characterMaterialMap) {
//Read relevant material types
- Material portalOpenBlock = readGateConfig(config, fileName, "portal-open", defaultPortalBlockOpen);
- Material portalClosedBlock = readGateConfig(config, fileName, "portal-closed", defaultPortalBlockClosed);
- Material portalButton = readGateConfig(config, fileName, "button", defaultButton);
+ List portalOpenBlock = readGateConfig(config, fileName, "portal-open", defaultPortalBlockOpen);
+ List portalClosedBlock = readGateConfig(config, fileName, "portal-closed", defaultPortalBlockClosed);
+ List portalButton = readGateConfig(config, fileName, "button", defaultButton);
//Read economy values
- int useCost = GateReader.readGateConfig(config, fileName, "usecost");
- int createCost = GateReader.readGateConfig(config, fileName, "createcost");
- int destroyCost = GateReader.readGateConfig(config, fileName, "destroycost");
+ int useCost = readGateConfig(config, fileName, "usecost");
+ int createCost = readGateConfig(config, fileName, "createcost");
+ int destroyCost = readGateConfig(config, fileName, "destroycost");
boolean toOwner = (config.containsKey("toowner") ? Boolean.parseBoolean(config.get("toowner")) :
Stargate.getEconomyConfig().sendPaymentToOwner());
//Create the new gate
- Gate gate = new Gate(fileName, new GateLayout(layout), characterMaterialMap, materialTagMap, portalOpenBlock,
+ Gate gate = new Gate(fileName, new GateLayout(layout), characterMaterialMap, portalOpenBlock,
portalClosedBlock, portalButton, useCost, createCost, destroyCost, toOwner);
if (!validateGate(gate, fileName)) {
@@ -217,23 +210,23 @@ public class GateHandler {
return false;
}
- if (!MaterialHelper.isButtonCompatible(gate.getPortalButton())) {
+ if (checkMaterialPredicateFail(gate.getPortalButtonMaterials(), MaterialHelper::isButtonCompatible)) {
Stargate.logSevere(String.format(failString, "Gate button must be a type of button."));
return false;
}
- if (!gate.getPortalOpenBlock().isBlock()) {
+ if (checkMaterialPredicateFail(gate.getPortalOpenMaterials(), Material::isBlock)) {
Stargate.logSevere(String.format(failString, "Gate open block must be a type of block."));
return false;
}
- if (!gate.getPortalClosedBlock().isBlock()) {
+ if (checkMaterialPredicateFail(gate.getPortalClosedMaterials(), Material::isBlock)) {
Stargate.logSevere(String.format(failString, "Gate closed block must be a type of block."));
return false;
}
- for (Material material : gate.getCharacterMaterialMap().values()) {
- if (!material.isBlock()) {
+ for (List materialSpecifiers : gate.getCharacterMaterialMap().values()) {
+ if (checkMaterialPredicateFail(materialSpecifiers, Material::isBlock)) {
Stargate.logSevere(String.format(failString, "Every gate border block must be a type of block."));
return false;
}
@@ -242,6 +235,25 @@ public class GateHandler {
return true;
}
+ /**
+ * Checks whether a predicate is true for a list of material specifiers
+ *
+ * @param materialSpecifiers The material specifiers to test
+ * @param predicate The predicate to test
+ * @return True if the predicate failed for any specified materials
+ */
+ private static boolean checkMaterialPredicateFail(@NotNull List materialSpecifiers,
+ @NotNull Predicate predicate) {
+ Set closedMaterials = MaterialHelper.specifiersToMaterials(materialSpecifiers);
+ for (Material material : closedMaterials) {
+ if (!predicate.test(material)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
/**
* Loads all gates inside the given folder
*
diff --git a/src/main/java/net/knarcraft/stargate/utility/GateReader.java b/src/main/java/net/knarcraft/stargate/utility/GateReader.java
index d8804fb..f57c6c0 100644
--- a/src/main/java/net/knarcraft/stargate/utility/GateReader.java
+++ b/src/main/java/net/knarcraft/stargate/utility/GateReader.java
@@ -1,10 +1,9 @@
package net.knarcraft.stargate.utility;
import net.knarcraft.stargate.Stargate;
-import org.bukkit.Bukkit;
+import net.knarcraft.stargate.config.material.BukkitMaterialSpecifier;
+import net.knarcraft.stargate.config.material.MaterialSpecifier;
import org.bukkit.Material;
-import org.bukkit.NamespacedKey;
-import org.bukkit.Tag;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
@@ -26,15 +25,15 @@ public final class GateReader {
*
* @param scanner The scanner to read from
* @param characterMaterialMap The map of characters to store valid symbols in
- * @param materialTagMap The map of characters to store valid tag symbols in
* @param fileName The filename of the loaded gate config file
* @param design The list to store the loaded design/layout to
* @param config The map of config values to store to
* @return The column count/width of the loaded gate
*/
- public static int readGateFile(@NotNull Scanner scanner, @NotNull Map characterMaterialMap,
- @NotNull Map> materialTagMap, @NotNull String fileName,
- @NotNull List> design, Map config) {
+ public static int readGateFile(@NotNull Scanner scanner,
+ @NotNull Map> characterMaterialMap,
+ @NotNull String fileName, @NotNull List> design,
+ @NotNull Map config) {
boolean designing = false;
int columns = 0;
try (scanner) {
@@ -43,14 +42,14 @@ public final class GateReader {
if (designing) {
//If we have reached the gate's layout/design, read it
- columns = readGateDesignLine(line, columns, characterMaterialMap, materialTagMap, fileName, design);
+ columns = readGateDesignLine(line, columns, characterMaterialMap, fileName, design);
if (columns < 0) {
return -1;
}
} else {
if (!line.isEmpty() && !line.startsWith("#")) {
//Read a normal config value
- readGateConfigValue(line, characterMaterialMap, materialTagMap, config);
+ readGateConfigValue(line, characterMaterialMap, config);
} else if ((line.isEmpty()) || (!line.contains("=") && !line.startsWith("#"))) {
//An empty line marks the start of the gate's layout/design
designing = true;
@@ -73,14 +72,12 @@ public final class GateReader {
* @param line The line to read
* @param maxColumns The current max columns value of the design
* @param characterMaterialMap The map between characters and the corresponding materials to use
- * @param materialTagMap The map between characters and the corresponding material tags to use
* @param fileName The filename of the loaded gate config file
* @param design The two-dimensional list to store the loaded design to
* @return The new max columns value of the design
*/
private static int readGateDesignLine(@NotNull String line, int maxColumns,
- @NotNull Map characterMaterialMap,
- @NotNull Map> materialTagMap,
+ @NotNull Map> characterMaterialMap,
@NotNull String fileName, @NotNull List> design) {
List row = new ArrayList<>();
@@ -91,7 +88,7 @@ public final class GateReader {
for (Character symbol : line.toCharArray()) {
//Refuse read gate designs with unknown characters
- if (symbol.equals('?') || (!characterMaterialMap.containsKey(symbol) && !materialTagMap.containsKey(symbol))) {
+ if (symbol.equals('?') || !characterMaterialMap.containsKey(symbol)) {
Stargate.logSevere(String.format("Could not load Gate %s - Unknown symbol '%s' in diagram", fileName,
symbol));
return -1;
@@ -110,12 +107,11 @@ public final class GateReader {
*
* @param line The line to read
* @param characterMaterialMap The character to material map to store to
- * @param materialTagMap The character to material tag map to store to
* @param config The config value map to store to
* @throws Exception If an invalid material is encountered
*/
- private static void readGateConfigValue(@NotNull String line, @NotNull Map characterMaterialMap,
- @NotNull Map> materialTagMap,
+ private static void readGateConfigValue(@NotNull String line,
+ @NotNull Map> characterMaterialMap,
@NotNull Map config) throws Exception {
String[] split = line.split("=");
String key = split[0].trim();
@@ -125,23 +121,12 @@ public final class GateReader {
//Read a gate frame material
Character symbol = key.charAt(0);
- if (value.startsWith("#")) {
- String tagString = value.replaceFirst("#", "");
- Tag tag = Bukkit.getTag(Tag.REGISTRY_BLOCKS, NamespacedKey.minecraft(tagString.toLowerCase()),
- Material.class);
- if (tag != null) {
- materialTagMap.put(symbol, tag);
- return;
- }
+ List materials = MaterialHelper.parseTagsAndMaterials(value);
+ if (!materials.isEmpty()) {
+ characterMaterialMap.put(symbol, materials);
} else {
- Material material = Material.matchMaterial(value);
- if (material != null) {
- //Register the map between the read symbol and the corresponding material
- characterMaterialMap.put(symbol, material);
- return;
- }
+ throw new Exception("Invalid material in line: " + line);
}
- throw new Exception("Invalid material in line: " + line);
} else {
//Read a normal config value
config.put(key, value);
@@ -180,17 +165,17 @@ public final class GateReader {
* @return The material specified in the config, or the default material if it could not be read
*/
@NotNull
- public static Material readGateConfig(@NotNull Map config, @NotNull String fileName,
- @NotNull String key, @NotNull Material defaultMaterial) {
+ public static List readGateConfig(@NotNull Map config, @NotNull String fileName,
+ @NotNull String key, @NotNull Material defaultMaterial) {
if (config.containsKey(key)) {
- Material material = Material.matchMaterial(config.get(key));
- if (material != null) {
- return material;
+ List materialSpecifiers = MaterialHelper.parseTagsAndMaterials(config.get(key));
+ if (!materialSpecifiers.isEmpty()) {
+ return materialSpecifiers;
} else {
Stargate.logWarning(String.format("Error reading %s: %s is not a material", fileName, key));
}
}
- return defaultMaterial;
+ return List.of(new BukkitMaterialSpecifier(defaultMaterial));
}
/**
diff --git a/src/main/java/net/knarcraft/stargate/utility/ListHelper.java b/src/main/java/net/knarcraft/stargate/utility/ListHelper.java
new file mode 100644
index 0000000..fa82656
--- /dev/null
+++ b/src/main/java/net/knarcraft/stargate/utility/ListHelper.java
@@ -0,0 +1,28 @@
+package net.knarcraft.stargate.utility;
+
+import java.util.List;
+import java.util.Random;
+
+/**
+ * A helper class for dealing with lists
+ */
+public final class ListHelper {
+
+ private static final Random random = new Random();
+
+ private ListHelper() {
+
+ }
+
+ /**
+ * Gets a random item from a list
+ *
+ * @param list The list to get an item from
+ * @param The type of item the list contains
+ * @return A random item
+ */
+ public static T getRandom(List list) {
+ return list.get(random.nextInt(list.size()));
+ }
+
+}
diff --git a/src/main/java/net/knarcraft/stargate/utility/MaterialHelper.java b/src/main/java/net/knarcraft/stargate/utility/MaterialHelper.java
index 466e844..2e310bc 100644
--- a/src/main/java/net/knarcraft/stargate/utility/MaterialHelper.java
+++ b/src/main/java/net/knarcraft/stargate/utility/MaterialHelper.java
@@ -1,8 +1,19 @@
package net.knarcraft.stargate.utility;
+import net.knarcraft.stargate.config.material.BukkitMaterialSpecifier;
+import net.knarcraft.stargate.config.material.BukkitTagSpecifier;
+import net.knarcraft.stargate.config.material.MaterialSpecifier;
+import org.bukkit.Bukkit;
import org.bukkit.Material;
+import org.bukkit.NamespacedKey;
import org.bukkit.Tag;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
/**
* This class helps decide properties of materials not already present in the Spigot API
@@ -50,4 +61,80 @@ public final class MaterialHelper {
return Tag.BUTTONS.isTagged(material) || isWallCoral(material) || isContainer(material);
}
+ @NotNull
+ public static String specifiersToString(@NotNull List specifiers) {
+ List names = new ArrayList<>();
+ for (MaterialSpecifier specifier : specifiers) {
+ names.add(specifier.asString());
+ }
+ return String.join(",", names);
+ }
+
+ /**
+ * Converts a list of material specifiers to a set of materials
+ *
+ * @param specifiers The material specifiers to convert
+ * @return The materials the specifiers represent
+ */
+ @NotNull
+ public static Set specifiersToMaterials(@NotNull List specifiers) {
+ Set output = new HashSet<>();
+
+ for (MaterialSpecifier specifier : specifiers) {
+ output.addAll(specifier.asMaterials());
+ }
+
+ return output;
+ }
+
+ /**
+ * Parses all materials and material tags found in the input string
+ *
+ * @param input The input string to parse
+ * @return All material specifiers found
+ */
+ @NotNull
+ public static List parseTagsAndMaterials(@NotNull String input) {
+ List specifiers = new ArrayList<>();
+
+ // Nothing to parse
+ if (input.isBlank()) {
+ return specifiers;
+ }
+
+ String[] parts;
+ if (input.contains(",")) {
+ parts = input.split(",");
+ } else {
+ parts = new String[]{input};
+ }
+
+ for (String part : parts) {
+ MaterialSpecifier materialSpecifier = parseTagOrMaterial(part.trim());
+ if (materialSpecifier != null) {
+ specifiers.add(materialSpecifier);
+ }
+ }
+
+ return specifiers;
+ }
+
+ @Nullable
+ private static MaterialSpecifier parseTagOrMaterial(@NotNull String input) {
+ if (input.startsWith("#")) {
+ String tagString = input.replaceFirst("#", "").toLowerCase();
+ Tag tag = Bukkit.getTag(Tag.REGISTRY_BLOCKS, NamespacedKey.minecraft(tagString), Material.class);
+ if (tag != null) {
+ return new BukkitTagSpecifier(tag);
+ }
+ } else {
+ Material material = Material.matchMaterial(input);
+ if (material != null) {
+ return new BukkitMaterialSpecifier(material);
+ }
+ }
+
+ return null;
+ }
+
}
diff --git a/src/main/java/net/knarcraft/stargate/utility/PermissionHelper.java b/src/main/java/net/knarcraft/stargate/utility/PermissionHelper.java
index 2416e98..dcef0e0 100644
--- a/src/main/java/net/knarcraft/stargate/utility/PermissionHelper.java
+++ b/src/main/java/net/knarcraft/stargate/utility/PermissionHelper.java
@@ -409,11 +409,13 @@ public final class PermissionHelper {
}
//Player cannot access portal
- if (!PermissionHelper.cannotAccessPortal(player, entrancePortal, destination)) {
+ if (PermissionHelper.cannotAccessPortal(player, entrancePortal, destination)) {
if (!entrancePortal.getOptions().isSilent()) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("denyMsg"));
}
new PlayerTeleporter(entrancePortal, player).teleportPlayer(entrancePortal, event);
+ Stargate.debug("PermissionHelper::playerCannotTeleport", "Closed portal because player is " +
+ "missing necessary permissions");
entrancePortal.getPortalOpener().closePortal(false);
return true;
}
diff --git a/src/main/java/net/knarcraft/stargate/utility/PortalFileHelper.java b/src/main/java/net/knarcraft/stargate/utility/PortalFileHelper.java
index 33ca2d5..98ff915 100644
--- a/src/main/java/net/knarcraft/stargate/utility/PortalFileHelper.java
+++ b/src/main/java/net/knarcraft/stargate/utility/PortalFileHelper.java
@@ -27,6 +27,7 @@ import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
+import java.util.List;
import java.util.Scanner;
import static net.knarcraft.stargate.portal.PortalSignDrawer.markPortalWithInvalidGate;
@@ -399,9 +400,15 @@ public final class PortalFileHelper {
return;
}
- Directional buttonData = (Directional) Bukkit.createBlockData(portal.getGate().getPortalButton());
- buttonData.setFacing(buttonFacing);
- button.getBlock().setBlockData(buttonData);
+ if (!MaterialHelper.isButtonCompatible(button.getType())) {
+ @NotNull List possibleMaterials = MaterialHelper.specifiersToMaterials(
+ portal.getGate().getPortalButtonMaterials()).stream().toList();
+ Material buttonType = ListHelper.getRandom(possibleMaterials);
+
+ Directional buttonData = (Directional) Bukkit.createBlockData(buttonType);
+ buttonData.setFacing(buttonFacing);
+ button.getBlock().setBlockData(buttonData);
+ }
portal.getStructure().setButton(button);
}