diff --git a/src/main/java/net/knarcraft/stargate/portal/property/PortalStructure.java b/src/main/java/net/knarcraft/stargate/portal/property/PortalStructure.java index c5c2986..2f324d0 100644 --- a/src/main/java/net/knarcraft/stargate/portal/property/PortalStructure.java +++ b/src/main/java/net/knarcraft/stargate/portal/property/PortalStructure.java @@ -73,7 +73,7 @@ public class PortalStructure { return true; } for (RelativeBlockVector control : gate.getLayout().getControls()) { - verified = verified && portal.getBlockAt(control).getBlock().getType().equals(gate.getControlBlock()); + verified = verified && gate.isValidControlBlock(portal.getBlockAt(control).getBlock().getType()); } this.verified = verified; return verified; diff --git a/src/main/java/net/knarcraft/stargate/portal/property/gate/Gate.java b/src/main/java/net/knarcraft/stargate/portal/property/gate/Gate.java index bfde5e4..0ba8b49 100644 --- a/src/main/java/net/knarcraft/stargate/portal/property/gate/Gate.java +++ b/src/main/java/net/knarcraft/stargate/portal/property/gate/Gate.java @@ -4,6 +4,7 @@ import net.knarcraft.stargate.Stargate; import net.knarcraft.stargate.container.BlockLocation; import net.knarcraft.stargate.container.RelativeBlockVector; import org.bukkit.Material; +import org.bukkit.Tag; import java.io.BufferedWriter; import java.io.FileWriter; @@ -21,6 +22,7 @@ public class Gate { private final String filename; private final GateLayout layout; private final Map characterMaterialMap; + private final Map> characterTagMap; //Gate materials private final Material portalOpenBlock; private final Material portalClosedBlock; @@ -37,6 +39,7 @@ public class 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

@@ -45,7 +48,8 @@ public class Gate { * @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(String filename, GateLayout layout, Map characterMaterialMap, Material portalOpenBlock, + public Gate(String filename, GateLayout layout, Map characterMaterialMap, + Map> characterTagMap, Material portalOpenBlock, Material portalClosedBlock, Material portalButton, int useCost, int createCost, int destroyCost, boolean toOwner) { this.filename = filename; @@ -58,6 +62,7 @@ public class Gate { this.createCost = createCost; this.destroyCost = destroyCost; this.toOwner = toOwner; + this.characterTagMap = characterTagMap; } /** @@ -78,6 +83,25 @@ public class Gate { return new HashMap<>(characterMaterialMap); } + /** + * Checks whether the given material is valid for control blocks + * + * @param material

The material to check

+ * @return

True if the material is valid for control blocks

+ */ + public boolean isValidControlBlock(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

+ */ + public Tag getControlBlockTag() { + return characterTagMap.get(GateHandler.getControlBlockCharacter()); + } + /** * Gets the material type used for this gate's control blocks * @@ -195,15 +219,29 @@ public class Gate { */ private boolean verifyGateBorderMatches(BlockLocation topLeft, double yaw) { Map characterMaterialMap = new HashMap<>(this.characterMaterialMap); + Map> characterTagMap = new HashMap<>(this.characterTagMap); for (RelativeBlockVector borderVector : layout.getBorder()) { int rowIndex = borderVector.getRight(); int lineIndex = borderVector.getDown(); Character key = layout.getLayout()[lineIndex][rowIndex]; Material materialInLayout = characterMaterialMap.get(key); + Tag tagInLayout = characterTagMap.get(key); Material materialAtLocation = topLeft.getRelativeLocation(borderVector, yaw).getType(); - if (materialInLayout == null) { + if (materialInLayout != null) { + if (materialAtLocation != materialInLayout) { + 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 @@ -211,10 +249,6 @@ public class Gate { characterMaterialMap.put(key, materialAtLocation); Stargate.debug("Gate::Matches", String.format("Missing layout material in %s. Using %s from the" + " physical portal.", getFilename(), materialAtLocation)); - } else if (materialAtLocation != materialInLayout) { - Stargate.debug("Gate::Matches", String.format("Block Type Mismatch: %s != %s", - materialAtLocation, materialInLayout)); - return false; } } return true; 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 9558175..ba3ddbf 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 @@ -4,6 +4,7 @@ import net.knarcraft.stargate.Stargate; import net.knarcraft.stargate.utility.GateReader; import net.knarcraft.stargate.utility.MaterialHelper; import org.bukkit.Material; +import org.bukkit.Tag; import org.bukkit.block.Block; import java.io.File; @@ -34,8 +35,9 @@ public class GateHandler { private static final Material defaultPortalBlockClosed = Material.AIR; private static final Material defaultButton = Material.STONE_BUTTON; - private static final HashMap gates = new HashMap<>(); - private static final HashMap> controlBlocks = new HashMap<>(); + private static final Map gates = new HashMap<>(); + private static final Map> controlBlocks = new HashMap<>(); + private static final Map> controlBlockTags = new HashMap<>(); private GateHandler() { @@ -86,13 +88,22 @@ public class GateHandler { private static void registerGate(Gate gate) { gates.put(gate.getFilename(), gate); - Material blockID = gate.getControlBlock(); - - if (!controlBlocks.containsKey(blockID)) { - controlBlocks.put(blockID, new ArrayList<>()); + Material blockId = gate.getControlBlock(); + if (blockId != null) { + if (!controlBlocks.containsKey(blockId)) { + controlBlocks.put(blockId, new ArrayList<>()); + } else { + controlBlocks.get(blockId).add(gate); + } + return; } - controlBlocks.get(blockID).add(gate); + Tag materialTag = gate.getControlBlockTag(); + if (!controlBlockTags.containsKey(materialTag.getKey().toString())) { + controlBlockTags.put(materialTag.getKey().toString(), new ArrayList<>()); + } else { + controlBlockTags.get(materialTag.getKey().toString()).add(gate); + } } /** @@ -121,6 +132,7 @@ public class GateHandler { private static Gate loadGate(String fileName, String parentFolder, Scanner scanner) { List> design = new ArrayList<>(); Map characterMaterialMap = new HashMap<>(); + Map> characterTagMap = new HashMap<>(); Map config = new HashMap<>(); Set frameTypes = new HashSet<>(); @@ -130,14 +142,14 @@ public class GateHandler { characterMaterialMap.put(ANYTHING, Material.AIR); //Read the file into appropriate lists and maps - int columns = readGateFile(scanner, characterMaterialMap, fileName, design, frameTypes, config); + int columns = readGateFile(scanner, characterMaterialMap, characterTagMap, fileName, design, frameTypes, config); if (columns < 0) { return null; } Character[][] layout = generateLayoutMatrix(design, columns); //Create and validate the new gate - Gate gate = createGate(config, fileName, layout, characterMaterialMap); + Gate gate = createGate(config, fileName, layout, characterMaterialMap, characterTagMap); if (gate == null) { return null; } @@ -154,10 +166,12 @@ 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

*/ private static Gate createGate(Map config, String fileName, Character[][] layout, - Map characterMaterialMap) { + Map characterMaterialMap, + Map> materialTagMap) { //Read relevant material types Material portalOpenBlock = readGateConfig(config, fileName, "portal-open", defaultPortalBlockOpen); Material portalClosedBlock = readGateConfig(config, fileName, "portal-closed", defaultPortalBlockClosed); @@ -171,8 +185,8 @@ public class GateHandler { Stargate.getEconomyConfig().sendPaymentToOwner()); //Create the new gate - Gate gate = new Gate(fileName, new GateLayout(layout), characterMaterialMap, portalOpenBlock, portalClosedBlock, - portalButton, useCost, createCost, destroyCost, toOwner); + Gate gate = new Gate(fileName, new GateLayout(layout), characterMaterialMap, materialTagMap, portalOpenBlock, + portalClosedBlock, portalButton, useCost, createCost, destroyCost, toOwner); if (!validateGate(gate, fileName)) { return null; diff --git a/src/main/java/net/knarcraft/stargate/utility/GateReader.java b/src/main/java/net/knarcraft/stargate/utility/GateReader.java index 0334019..ed2120f 100644 --- a/src/main/java/net/knarcraft/stargate/utility/GateReader.java +++ b/src/main/java/net/knarcraft/stargate/utility/GateReader.java @@ -1,7 +1,10 @@ package net.knarcraft.stargate.utility; import net.knarcraft.stargate.Stargate; +import org.bukkit.Bukkit; import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.Tag; import java.util.ArrayList; import java.util.List; @@ -23,13 +26,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 frameTypes

The set to store frame/border materials to

* @param config

The map of config values to store to

* @return

The column count/width of the loaded gate

*/ - public static int readGateFile(Scanner scanner, Map characterMaterialMap, String fileName, + public static int readGateFile(Scanner scanner, Map characterMaterialMap, + Map> materialTagMap, String fileName, List> design, Set frameTypes, Map config) { boolean designing = false; int columns = 0; @@ -39,14 +44,14 @@ public final class GateReader { if (designing) { //If we have reached the gate's layout/design, read it - columns = readGateDesignLine(line, columns, characterMaterialMap, fileName, design); + columns = readGateDesignLine(line, columns, characterMaterialMap, materialTagMap, fileName, design); if (columns < 0) { return -1; } } else { if (!line.isEmpty() && !line.startsWith("#")) { //Read a normal config value - readGateConfigValue(line, characterMaterialMap, frameTypes, config); + readGateConfigValue(line, characterMaterialMap, materialTagMap, frameTypes, config); } else if ((line.isEmpty()) || (!line.contains("=") && !line.startsWith("#"))) { //An empty line marks the start of the gate's layout/design designing = true; @@ -73,11 +78,13 @@ 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(String line, int maxColumns, Map characterMaterialMap, + Map> materialTagMap, String fileName, List> design) { List row = new ArrayList<>(); @@ -88,7 +95,7 @@ public final class GateReader { for (Character symbol : line.toCharArray()) { //Refuse read gate designs with unknown characters - if (symbol.equals('?') || (!characterMaterialMap.containsKey(symbol))) { + if (symbol.equals('?') || (!characterMaterialMap.containsKey(symbol) && !materialTagMap.containsKey(symbol))) { Stargate.logSevere(String.format("Could not load Gate %s - Unknown symbol '%s' in diagram", fileName, symbol)); return -1; @@ -107,12 +114,14 @@ 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 frameTypes

The set to store gate frame/border types to

* @param config

The config value map to store to

* @throws Exception

If an invalid material is encountered

*/ private static void readGateConfigValue(String line, Map characterMaterialMap, - Set frameTypes, Map config) throws Exception { + Map> materialTagMap, Set frameTypes, + Map config) throws Exception { String[] split = line.split("="); String key = split[0].trim(); String value = split[1].trim(); @@ -120,14 +129,26 @@ public final class GateReader { if (key.length() == 1) { //Read a gate frame material Character symbol = key.charAt(0); - Material material = Material.getMaterial(value); - if (material == null) { - throw new Exception("Invalid material in line: " + line); + + 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; + } + } else { + Material material = Material.getMaterial(value); + if (material != null) { + //Register the map between the read symbol and the corresponding material + characterMaterialMap.put(symbol, material); + //Save the material as one of the frame materials used for this kind of gate + frameTypes.add(material); + return; + } } - //Register the map between the read symbol and the corresponding material - characterMaterialMap.put(symbol, material); - //Save the material as one of the frame materials used for this kind of gate - frameTypes.add(material); + throw new Exception("Invalid material in line: " + line); } else { //Read a normal config value config.put(key, value);