Greatly refactors gate loading

This commit is contained in:
Kristian Knarvik 2021-02-28 21:53:27 +01:00
parent 504ef1b52f
commit 44dfa2a10d
3 changed files with 210 additions and 93 deletions

View File

@ -23,7 +23,7 @@ public class Gate {
private final String filename; private final String filename;
private final GateLayout layout; private final GateLayout layout;
private final HashMap<Character, Material> types; private final Map<Character, Material> types;
//Gate materials //Gate materials
private Material portalOpenBlock; private Material portalOpenBlock;
@ -50,7 +50,7 @@ public class Gate {
* @param destroyCost <p>The cost of destroying a portal with this gate layout (-1 to disable)</p> * @param destroyCost <p>The cost of destroying a portal with this gate layout (-1 to disable)</p>
* @param toOwner <p>Whether any payment should go to the owner of the gate, as opposed to just disappearing</p> * @param toOwner <p>Whether any payment should go to the owner of the gate, as opposed to just disappearing</p>
*/ */
public Gate(String filename, GateLayout layout, HashMap<Character, Material> types, Material portalOpenBlock, public Gate(String filename, GateLayout layout, Map<Character, Material> types, Material portalOpenBlock,
Material portalClosedBlock, Material portalButton, int useCost, int createCost, int destroyCost, Material portalClosedBlock, Material portalButton, int useCost, int createCost, int destroyCost,
boolean toOwner) { boolean toOwner) {
this.filename = filename; this.filename = filename;
@ -79,7 +79,7 @@ public class Gate {
* *
* @return <p>The material types each layout character represents</p> * @return <p>The material types each layout character represents</p>
*/ */
public HashMap<Character, Material> getTypes() { public Map<Character, Material> getTypes() {
return types; return types;
} }

View File

@ -10,7 +10,9 @@ import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Scanner; import java.util.Scanner;
import java.util.Set;
import java.util.logging.Level; import java.util.logging.Level;
/** /**
@ -72,7 +74,12 @@ public class GateHandler {
return CONTROL_BLOCK; return CONTROL_BLOCK;
} }
public static void registerGate(Gate gate) { /**
* Register a gate into the list of available gates
*
* @param gate <p>The gate to register</p>
*/
private static void registerGate(Gate gate) {
gates.put(gate.getFilename(), gate); gates.put(gate.getFilename(), gate);
Material blockID = gate.getControlBlock(); Material blockID = gate.getControlBlock();
@ -84,7 +91,13 @@ public class GateHandler {
controlBlocks.get(blockID).add(gate); controlBlocks.get(blockID).add(gate);
} }
public static Gate loadGate(File file) { /**
* Loads a gate
*
* @param file <p>The file containing the gate's layout</p>
* @return <p>The loaded gate or null if unable to load the gate</p>
*/
private static Gate loadGate(File file) {
try (Scanner scanner = new Scanner(file)) { try (Scanner scanner = new Scanner(file)) {
return loadGate(file.getName(), file.getParent(), scanner); return loadGate(file.getName(), file.getParent(), scanner);
} catch (Exception ex) { } catch (Exception ex) {
@ -93,88 +106,56 @@ public class GateHandler {
} }
} }
public static Gate loadGate(String fileName, String parentFolder, Scanner scanner) { /**
boolean designing = false; * Loads a gate
*
* @param fileName <p>The name of the file containing the gate layout</p>
* @param parentFolder <p>The parent folder of the layout file</p>
* @param scanner <p>The scanner to use for reading the gate layout</p>
* @return <p>The loaded gate or null if unable to load the gate</p>
*/
private static Gate loadGate(String fileName, String parentFolder, Scanner scanner) {
List<List<Character>> design = new ArrayList<>(); List<List<Character>> design = new ArrayList<>();
HashMap<Character, Material> types = new HashMap<>(); Map<Character, Material> types = new HashMap<>();
HashMap<String, String> config = new HashMap<>(); Map<String, String> config = new HashMap<>();
HashSet<Material> frameTypes = new HashSet<>(); Set<Material> frameTypes = new HashSet<>();
int cols = 0;
// Init types map //Initialize types map
types.put(ENTRANCE, Material.AIR); types.put(ENTRANCE, Material.AIR);
types.put(EXIT, Material.AIR); types.put(EXIT, Material.AIR);
types.put(ANYTHING, Material.AIR); types.put(ANYTHING, Material.AIR);
try { //Read the file into appropriate lists and maps
while (scanner.hasNextLine()) { int cols = readGateFile(scanner, types, fileName, design, frameTypes, config);
String line = scanner.nextLine(); if (cols < 0) {
return null;
if (designing) { }
List<Character> row = new ArrayList<>(); Character[][] layout = generateLayoutMatrix(design, cols);
if (line.length() > cols) { //Create and validate the new gate
cols = line.length(); Gate gate = createGate(config, fileName, layout, types);
} if (gate == null) {
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; return null;
} finally {
if (scanner != null) {
scanner.close();
}
} }
Character[][] layout = new Character[design.size()][cols]; //Update list of all frame blocks
frameBlocks.addAll(frameTypes);
//y = relative line number of layout file gate.save(parentFolder + "/"); // Updates format for version changes
for (int y = 0; y < design.size(); y++) { return gate;
List<Character> 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;
}
/**
* Creates a new gate
*
* @param config <p>The config map to get configuration values from</p>
* @param fileName <p>The name of the saved gate config file</p>
* @param layout <p>The layout matrix of the new gate</p>
* @param types <p>The mapping for used gate material types</p>
* @return <p>A new gate or null if the config is invalid</p>
*/
private static Gate createGate(Map<String, String> config, String fileName, Character[][] layout,
Map<Character, Material> types) {
Material portalOpenBlock = readConfig(config, fileName, "portal-open", defaultPortalBlockOpen); Material portalOpenBlock = readConfig(config, fileName, "portal-open", defaultPortalBlockOpen);
Material portalClosedBlock = readConfig(config, fileName, "portal-closed", defaultPortalBlockClosed); Material portalClosedBlock = readConfig(config, fileName, "portal-closed", defaultPortalBlockClosed);
Material portalButton = readConfig(config, fileName, "button", defaultButton); Material portalButton = readConfig(config, fileName, "button", defaultButton);
@ -186,26 +167,163 @@ public class GateHandler {
Gate gate = new Gate(fileName, new GateLayout(layout), types, portalOpenBlock, portalClosedBlock, portalButton, useCost, Gate gate = new Gate(fileName, new GateLayout(layout), types, portalOpenBlock, portalClosedBlock, portalButton, useCost,
createCost, destroyCost, toOwner); createCost, destroyCost, toOwner);
if (!validateGate(gate, fileName)) {
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; 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; return gate;
} }
private static int readConfig(HashMap<String, String> config, String fileName, String key, int defaultInteger) { /**
* Validate that a gate is valid
*
* @param gate <p>The gate to validate</p>
* @param fileName <p>The filename of the loaded gate file</p>
* @return <p>True if the gate is valid. False otherwise</p>
*/
private static boolean validateGate(Gate gate, String fileName) {
if (gate.getLayout().getControls().length != 2) {
Stargate.log.log(Level.SEVERE, "Could not load Gate " + fileName +
" - Gates must have exactly 2 control points.");
return false;
}
if (!MaterialHelper.isButtonCompatible(gate.getPortalButton())) {
Stargate.log.log(Level.SEVERE, "Could not load Gate " + fileName +
" - Gate button must be a type of button.");
return false;
}
return true;
}
/**
* Generates a matrix storing the gate layout
*
* @param design <p>The design of the gate layout</p>
* @param cols <p>The largest amount of columns in the design</p>
* @return <p>A matrix containing the gate's layout</p>
*/
private static Character[][] generateLayoutMatrix(List<List<Character>> design, int cols) {
Character[][] layout = new Character[design.size()][cols];
for (int lineIndex = 0; lineIndex < design.size(); lineIndex++) {
List<Character> row = design.get(lineIndex);
Character[] result = new Character[cols];
for (int rowIndex = 0; rowIndex < cols; rowIndex++) {
if (rowIndex < row.size()) {
result[rowIndex] = row.get(rowIndex);
} else {
//Add spaces to all lines which are too short
result[rowIndex] = ' ';
}
}
layout[lineIndex] = result;
}
return layout;
}
/**
* Reads the gate file
*
* @param scanner <p>The scanner to read from</p>
* @param types <p>The map of characters to store valid symbols in</p>
* @param fileName <p>The filename of the loaded gate config file</p>
* @param design <p>The list to store the loaded design to</p>
* @param frameTypes <p>The set of gate frame types to store to</p>
* @param config <p>The map of config values to store to</p>
* @return <p>The column count/width of the loaded gate</p>
*/
private static int readGateFile(Scanner scanner, Map<Character, Material> types, String fileName,
List<List<Character>> design, Set<Material> frameTypes, Map<String, String> config) {
boolean designing = false;
int cols = 0;
try {
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
if (designing) {
cols = readGateDesignLine(line, cols, types, fileName, design);
if (cols < 0) {
return -1;
}
} else {
if (!line.isEmpty() && !line.startsWith("#")) {
readGateConfigValue(line, types, frameTypes, config);
} 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 -1;
} finally {
if (scanner != null) {
scanner.close();
}
}
return cols;
}
/**
* Reads one design line of the gate layout file
*
* @param line <p>The line to read</p>
* @param cols <p>The current max columns value of the design</p>
* @param types <p>The map of characters to check for valid symbols</p>
* @param fileName <p>The filename of the loaded gate config file</p>
* @param design <p>The list to store the loaded design to</p>
* @return <p>The new max columns value of the design</p>
*/
private static int readGateDesignLine(String line, int cols, Map<Character, Material> types, String fileName,
List<List<Character>> design) {
List<Character> 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 -1;
}
row.add(symbol);
}
design.add(row);
return cols;
}
/**
* Reads one config value from the gate layout file
*
* @param line <p>The line to read</p>
* @param types <p>The map of characters to materials to store to</p>
* @param frameTypes <p>The set of gate frame types to store to</p>
* @param config <p>The map of config values to store to</p>
* @throws Exception <p>If an invalid material is encountered</p>
*/
private static void readGateConfigValue(String line, Map<Character, Material> types, Set<Material> frameTypes,
Map<String, String> config) throws Exception {
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);
}
}
private static int readConfig(Map<String, String> config, String fileName, String key, int defaultInteger) {
if (config.containsKey(key)) { if (config.containsKey(key)) {
try { try {
return Integer.parseInt(config.get(key)); return Integer.parseInt(config.get(key));
@ -226,7 +344,7 @@ public class GateHandler {
* @param defaultMaterial <p>The default material to use, in case the config is invalid</p> * @param defaultMaterial <p>The default material to use, in case the config is invalid</p>
* @return <p>The material to use</p> * @return <p>The material to use</p>
*/ */
private static Material readConfig(HashMap<String, String> config, String fileName, String key, Material defaultMaterial) { private static Material readConfig(Map<String, String> config, String fileName, String key, Material defaultMaterial) {
if (config.containsKey(key)) { if (config.containsKey(key)) {
Material material = Material.getMaterial(config.get(key)); Material material = Material.getMaterial(config.get(key));
if (material != null) { if (material != null) {

View File

@ -5,7 +5,6 @@ import net.knarcraft.stargate.RelativeBlockVector;
import java.io.BufferedWriter; import java.io.BufferedWriter;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
/** /**