package net.knarcraft.stargate.portal; import net.knarcraft.stargate.utility.EconomyHandler; import net.knarcraft.stargate.Stargate; import net.knarcraft.stargate.utility.MaterialHelper; import org.bukkit.Material; import org.bukkit.block.Block; import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Scanner; import java.util.logging.Level; /** * The gate handler keeps track of all gates */ public class GateHandler { private static final Character ANYTHING = ' '; private static final Character ENTRANCE = '.'; private static final Character EXIT = '*'; private static final Character CONTROL_BLOCK = '-'; private static Material defaultPortalBlockOpen = Material.NETHER_PORTAL; private static Material defaultPortalBlockClosed = Material.AIR; private static Material defaultButton = Material.STONE_BUTTON; private static final HashMap gates = new HashMap<>(); private static final HashMap> controlBlocks = new HashMap<>(); private static final HashSet frameBlocks = new HashSet<>(); private GateHandler() { } /** * Gets the character used for blocks that are not part of the gate * * @return

The character used for blocks that are not part of the gate

*/ public static Character getAnythingCharacter() { return ANYTHING; } /** * Gets the character used for defining the entrance * * @return

The character used for defining the entrance

*/ public static Character getEntranceCharacter() { return ENTRANCE; } /** * Gets the character used for defining the exit * * @return

The character used for defining the exit

*/ public static Character getExitCharacter() { return EXIT; } /** * Gets the character used for defining control blocks * * @return

The character used for defining control blocks

*/ public static Character getControlBlockCharacter() { return CONTROL_BLOCK; } public static void registerGate(Gate gate) { gates.put(gate.getFilename(), gate); Material blockID = gate.getControlBlock(); if (!controlBlocks.containsKey(blockID)) { controlBlocks.put(blockID, new ArrayList<>()); } controlBlocks.get(blockID).add(gate); } public static Gate loadGate(File file) { try (Scanner scanner = new Scanner(file)) { return loadGate(file.getName(), file.getParent(), scanner); } catch (Exception ex) { Stargate.log.log(Level.SEVERE, "Could not load Gate " + file.getName() + " - " + ex.getMessage()); return null; } } public static Gate loadGate(String fileName, String parentFolder, Scanner scanner) { boolean designing = false; List> design = new ArrayList<>(); HashMap types = new HashMap<>(); HashMap config = new HashMap<>(); HashSet frameTypes = new HashSet<>(); int cols = 0; // Init types map types.put(ENTRANCE, Material.AIR); types.put(EXIT, Material.AIR); types.put(ANYTHING, Material.AIR); try { while (scanner.hasNextLine()) { String line = scanner.nextLine(); if (designing) { List row = new ArrayList<>(); if (line.length() > cols) { cols = line.length(); } for (Character symbol : line.toCharArray()) { if ((symbol.equals('?')) || (!types.containsKey(symbol))) { Stargate.log.log(Level.SEVERE, "Could not load Gate " + fileName + " - Unknown symbol '" + symbol + "' in diagram"); return null; } row.add(symbol); } design.add(row); } else { if (!line.isEmpty() && !line.startsWith("#")) { String[] split = line.split("="); String key = split[0].trim(); String value = split[1].trim(); if (key.length() == 1) { Character symbol = key.charAt(0); Material id = Material.getMaterial(value); if (id == null) { throw new Exception("Invalid material in line: " + line); } types.put(symbol, id); frameTypes.add(id); } else { config.put(key, value); } } else if ((line.isEmpty()) || (!line.contains("=") && !line.startsWith("#"))) { designing = true; } } } } catch (Exception ex) { Stargate.log.log(Level.SEVERE, "Could not load Gate " + fileName + " - " + ex.getMessage()); return null; } finally { if (scanner != null) { scanner.close(); } } Character[][] layout = new Character[design.size()][cols]; //y = relative line number of layout file for (int y = 0; y < design.size(); y++) { List row = design.get(y); Character[] result = new Character[cols]; for (int x = 0; x < cols; x++) { if (x < row.size()) { result[x] = row.get(x); } else { result[x] = ' '; } } layout[y] = result; } Material portalOpenBlock = readConfig(config, fileName, "portal-open", defaultPortalBlockOpen); Material portalClosedBlock = readConfig(config, fileName, "portal-closed", defaultPortalBlockClosed); Material portalButton = readConfig(config, fileName, "button", defaultButton); int useCost = readConfig(config, fileName, "usecost", -1); int createCost = readConfig(config, fileName, "createcost", -1); int destroyCost = readConfig(config, fileName, "destroycost", -1); boolean toOwner = (config.containsKey("toowner") ? Boolean.valueOf(config.get("toowner")) : EconomyHandler.toOwner); Gate gate = new Gate(fileName, new GateLayout(layout), types, portalOpenBlock, portalClosedBlock, portalButton, useCost, createCost, destroyCost, toOwner); if (gate.getLayout().getControls().length != 2) { Stargate.log.log(Level.SEVERE, "Could not load Gate " + fileName + " - Gates must have exactly 2 control points."); return null; } if (!MaterialHelper.isButtonCompatible(gate.getPortalButton())) { Stargate.log.log(Level.SEVERE, "Could not load Gate " + fileName + " - Gate button must be a type of button."); return null; } // Merge frame types, add open mat to list frameBlocks.addAll(frameTypes); gate.save(parentFolder + "/"); // Updates format for version changes return gate; } private static int readConfig(HashMap config, String fileName, String key, int defaultInteger) { if (config.containsKey(key)) { try { return Integer.parseInt(config.get(key)); } catch (NumberFormatException ex) { Stargate.log.log(Level.WARNING, String.format("%s reading %s: %s is not numeric", ex.getClass().getName(), fileName, key)); } } return defaultInteger; } /** * Gets the material defined in the config * * @param config

The config to read

* @param fileName

The config file the config belongs to

* @param key

The config key to read

* @param defaultMaterial

The default material to use, in case the config is invalid

* @return

The material to use

*/ private static Material readConfig(HashMap config, String fileName, String key, Material defaultMaterial) { if (config.containsKey(key)) { Material material = Material.getMaterial(config.get(key)); if (material != null) { return material; } else { Stargate.log.log(Level.WARNING, String.format("Error reading %s: %s is not a material", fileName, key)); } } return defaultMaterial; } /** * Loads all gates inside the given folder * * @param gateFolder

The folder containing the gates

*/ public static void loadGates(String gateFolder) { File directory = new File(gateFolder); File[] files; if (directory.exists()) { files = directory.listFiles((file) -> file.isFile() && file.getName().endsWith(".gate")); } else { files = new File[0]; } if (files == null || files.length == 0) { //The gates folder was not found. Assume this is the first run if (directory.mkdir()) { populateDefaults(gateFolder); } } else { for (File file : files) { Gate gate = loadGate(file); if (gate != null) { registerGate(gate); } } } } /** * Writes the default gate specifications to the given folder * * @param gateFolder

The folder containing gate config files

*/ public static void populateDefaults(String gateFolder) { loadGateFromJar("nethergate.gate", gateFolder); loadGateFromJar("watergate.gate", gateFolder); } /** * Loads the given gate file from within the Jar's resources directory * * @param gateFile

The name of the gate file

* @param gateFolder

The folder containing gates

*/ private static void loadGateFromJar(String gateFile, String gateFolder) { Scanner scanner = new Scanner(Gate.class.getResourceAsStream("/gates/" + gateFile)); Gate gate = loadGate(gateFile, gateFolder, scanner); if (gate != null) { registerGate(gate); } } /** * Gets the gates with the given control block * *

The control block is the block type where the sign should be placed. It is used to decide whether a user * is creating a new portal.

* * @param block

The control block to check

* @return

A list of gates using the given control block

*/ public static Gate[] getGatesByControlBlock(Block block) { return getGatesByControlBlock(block.getType()); } /** * Gets the gates with the given control block * * @param type

The type of the control block to check

* @return

A list of gates using the given material for control block

*/ public static Gate[] getGatesByControlBlock(Material type) { Gate[] result = new Gate[0]; List lookup = controlBlocks.get(type); if (lookup != null) { result = lookup.toArray(result); } return result; } /** * Gets a portal by its name (filename before .gate) * * @param name

The name of the gate to get

* @return

The gate with the given name

*/ public static Gate getGateByName(String name) { return gates.get(name); } /** * Gets the number of loaded gate configurations * * @return

The number of loaded gate configurations

*/ public static int getGateCount() { return gates.size(); } /** * Checks whether the given material is used for the frame of any portals * * @param type

The material type to check

* @return

True if the material is used for the frame of at least one portal

*/ public static boolean isGateBlock(Material type) { return frameBlocks.contains(type); } /** * Clears all loaded gates */ public static void clearGates() { gates.clear(); controlBlocks.clear(); frameBlocks.clear(); } }