Splits Gate into Gate, GateLayout and GateHandler, and creates a new portal package with portal related classes
This commit is contained in:
300
src/main/java/net/knarcraft/stargate/portal/Gate.java
Normal file
300
src/main/java/net/knarcraft/stargate/portal/Gate.java
Normal file
@ -0,0 +1,300 @@
|
||||
package net.knarcraft.stargate.portal;
|
||||
|
||||
import net.knarcraft.stargate.BlockLocation;
|
||||
import net.knarcraft.stargate.EconomyHandler;
|
||||
import net.knarcraft.stargate.Stargate;
|
||||
import org.bukkit.Material;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
* A gate describes the physical structure of a stargate
|
||||
*
|
||||
* <p>While the portal class represents a portal in space, the Gate class represents the physical gate/portal entrance.</p>
|
||||
*/
|
||||
public class Gate {
|
||||
|
||||
private final String filename;
|
||||
private final GateLayout layout;
|
||||
private final HashMap<Character, Material> types;
|
||||
|
||||
//Gate materials
|
||||
private Material portalOpenBlock;
|
||||
private Material portalClosedBlock;
|
||||
private Material portalButton;
|
||||
|
||||
// Economy information
|
||||
private int useCost;
|
||||
private int createCost;
|
||||
private int destroyCost;
|
||||
private boolean toOwner;
|
||||
|
||||
/**
|
||||
* Instantiates a new gate
|
||||
*
|
||||
* @param filename <p>The name of the gate which equal the name of the file</p>
|
||||
* @param layout <p>The character layout defined in the gate file</p>
|
||||
* @param types <p>The block types the different layout characters represent</p>
|
||||
* @param portalOpenBlock <p>The material to set the non-frame to when the portal is open</p>
|
||||
* @param portalClosedBlock <p>The material to set the non-frame to when the portal is closed</p>
|
||||
* @param portalButton <p>The material to use for the portal button</p>
|
||||
* @param useCost <p>The cost of using a portal with this gate layout (-1 to disable)</p>
|
||||
* @param createCost <p>The cost of creating 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>
|
||||
*/
|
||||
public Gate(String filename, GateLayout layout, HashMap<Character, Material> types, Material portalOpenBlock,
|
||||
Material portalClosedBlock, Material portalButton, int useCost, int createCost, int destroyCost,
|
||||
boolean toOwner) {
|
||||
this.filename = filename;
|
||||
this.layout = layout;
|
||||
this.types = types;
|
||||
this.portalOpenBlock = portalOpenBlock;
|
||||
this.portalClosedBlock = portalClosedBlock;
|
||||
this.portalButton = portalButton;
|
||||
this.useCost = useCost;
|
||||
this.createCost = createCost;
|
||||
this.destroyCost = destroyCost;
|
||||
this.toOwner = toOwner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the layout of this gate
|
||||
*
|
||||
* @return <p>The layout of this gate</p>
|
||||
*/
|
||||
public GateLayout getLayout() {
|
||||
return layout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the material types each layout character represents
|
||||
*
|
||||
* @return <p>The material types each layout character represents</p>
|
||||
*/
|
||||
public HashMap<Character, Material> getTypes() {
|
||||
return types;
|
||||
}
|
||||
|
||||
public Material getControlBlock() {
|
||||
return types.get('-');
|
||||
}
|
||||
|
||||
public String getFilename() {
|
||||
return filename;
|
||||
}
|
||||
|
||||
public Material getPortalOpenBlock() {
|
||||
return portalOpenBlock;
|
||||
}
|
||||
|
||||
public void setPortalOpenBlock(Material type) {
|
||||
portalOpenBlock = type;
|
||||
}
|
||||
|
||||
public Material getPortalClosedBlock() {
|
||||
return portalClosedBlock;
|
||||
}
|
||||
|
||||
public void setPortalClosedBlock(Material type) {
|
||||
portalClosedBlock = type;
|
||||
}
|
||||
|
||||
public Material getPortalButton() {
|
||||
return portalButton;
|
||||
}
|
||||
|
||||
public int getUseCost() {
|
||||
if (useCost < 0) return EconomyHandler.useCost;
|
||||
return useCost;
|
||||
}
|
||||
|
||||
public Integer getCreateCost() {
|
||||
if (createCost < 0) return EconomyHandler.createCost;
|
||||
return createCost;
|
||||
}
|
||||
|
||||
public Integer getDestroyCost() {
|
||||
if (destroyCost < 0) return EconomyHandler.destroyCost;
|
||||
return destroyCost;
|
||||
}
|
||||
|
||||
public Boolean getToOwner() {
|
||||
return toOwner;
|
||||
}
|
||||
|
||||
public boolean matches(BlockLocation topLeft, int modX, int modZ) {
|
||||
return matches(topLeft, modX, modZ, false);
|
||||
}
|
||||
|
||||
public boolean matches(BlockLocation topLeft, int modX, int modZ, boolean onCreate) {
|
||||
HashMap<Character, Material> portalTypes = new HashMap<>(types);
|
||||
Character[][] layout = this.layout.getLayout();
|
||||
for (int y = 0; y < layout.length; y++) {
|
||||
for (int x = 0; x < layout[y].length; x++) {
|
||||
Character key = layout[y][x];
|
||||
|
||||
if (key.equals(GateHandler.getEntranceCharacter()) || key.equals(GateHandler.getExitCharacter())) {
|
||||
if (Stargate.ignoreEntrance) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Material type = topLeft.modRelative(x, y, 0, modX, 1, modZ).getType();
|
||||
|
||||
// Ignore entrance if it's air and we're creating a new gate
|
||||
if (onCreate && type == Material.AIR) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (type != portalClosedBlock && type != portalOpenBlock) {
|
||||
Stargate.debug("Gate::Matches", "Entrance/Exit Material Mismatch: " + type);
|
||||
return false;
|
||||
}
|
||||
} else if (!key.equals(GateHandler.getAnythingCharacter())) {
|
||||
Material id = portalTypes.get(key);
|
||||
if (id == null) {
|
||||
portalTypes.put(key, topLeft.modRelative(x, y, 0, modX, 1, modZ).getType());
|
||||
} else if (topLeft.modRelative(x, y, 0, modX, 1, modZ).getType() != id) {
|
||||
Stargate.debug("Gate::Matches", "Block Type Mismatch: " + topLeft.modRelative(x, y, 0, modX, 1, modZ).getType() + " != " + id);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves this gate to a file
|
||||
*
|
||||
* <p>This method will save the gate to its filename in the given folder.</p>
|
||||
*
|
||||
* @param gateFolder <p>The folder to save the gate file in</p>
|
||||
*/
|
||||
public void save(String gateFolder) {
|
||||
try {
|
||||
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(gateFolder + filename));
|
||||
|
||||
writeConfig(bufferedWriter, "portal-open", portalOpenBlock.name());
|
||||
writeConfig(bufferedWriter, "portal-closed", portalClosedBlock.name());
|
||||
writeConfig(bufferedWriter, "button", portalButton.name());
|
||||
|
||||
//Save the values necessary for economy
|
||||
saveEconomyValues(bufferedWriter);
|
||||
|
||||
//Store type material type to use for frame blocks
|
||||
saveFrameBlockTypes(bufferedWriter);
|
||||
|
||||
bufferedWriter.newLine();
|
||||
|
||||
//Save the layout
|
||||
layout.save(bufferedWriter);
|
||||
|
||||
bufferedWriter.close();
|
||||
} catch (IOException ex) {
|
||||
Stargate.log.log(Level.SEVERE, "Could not save Gate " + filename + " - " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves current economy related values using a buffered writer
|
||||
*
|
||||
* @param bufferedWriter <p>The buffered writer to write to</p>
|
||||
* * @throws IOException <p>If unable to write to the buffered writer</p>
|
||||
*/
|
||||
private void saveEconomyValues(BufferedWriter bufferedWriter) throws IOException {
|
||||
if (useCost != -1) {
|
||||
writeConfig(bufferedWriter, "usecost", useCost);
|
||||
}
|
||||
if (createCost != -1) {
|
||||
writeConfig(bufferedWriter, "createcost", createCost);
|
||||
}
|
||||
if (destroyCost != -1) {
|
||||
writeConfig(bufferedWriter, "destroycost", destroyCost);
|
||||
}
|
||||
writeConfig(bufferedWriter, "toowner", toOwner);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the types of blocks used for the gate frame/border using a buffered writer
|
||||
*
|
||||
* @param bufferedWriter <p>The buffered writer to write to</p>
|
||||
* @throws IOException <p>If unable to write to the buffered writer</p>
|
||||
*/
|
||||
private void saveFrameBlockTypes(BufferedWriter bufferedWriter) throws IOException {
|
||||
for (Map.Entry<Character, Material> entry : types.entrySet()) {
|
||||
Character type = entry.getKey();
|
||||
Material value = entry.getValue();
|
||||
// Skip control values
|
||||
if (type.equals(GateHandler.getAnythingCharacter()) ||
|
||||
type.equals(GateHandler.getEntranceCharacter()) ||
|
||||
type.equals(GateHandler.getExitCharacter())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bufferedWriter.append(type);
|
||||
bufferedWriter.append('=');
|
||||
if (value != null) {
|
||||
bufferedWriter.append(value.toString());
|
||||
}
|
||||
bufferedWriter.newLine();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an integer to a config
|
||||
*
|
||||
* @param bufferedWriter <p>The buffered writer to write the config to</p>
|
||||
* @param key <p>The config key to save</p>
|
||||
* @param value <p>The value of the config key</p>
|
||||
* @throws IOException <p>If unable to write to the buffered writer</p>
|
||||
*/
|
||||
private void writeConfig(BufferedWriter bufferedWriter, String key, int value) throws IOException {
|
||||
writeConfig(bufferedWriter, "%s=%d", key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a boolean to a config
|
||||
*
|
||||
* @param bufferedWriter <p>The buffered writer to write the config to</p>
|
||||
* @param key <p>The config key to save</p>
|
||||
* @param value <p>The value of the config key</p>
|
||||
* @throws IOException <p>If unable to write to the buffered writer</p>
|
||||
*/
|
||||
private void writeConfig(BufferedWriter bufferedWriter, String key, boolean value) throws IOException {
|
||||
writeConfig(bufferedWriter, "%s=%b", key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a string to a config
|
||||
*
|
||||
* @param bufferedWriter <p>The buffered writer to write the config to</p>
|
||||
* @param key <p>The config key to save</p>
|
||||
* @param value <p>The value of the config key</p>
|
||||
* @throws IOException <p>If unable to write to the buffered writer</p>
|
||||
*/
|
||||
private void writeConfig(BufferedWriter bufferedWriter, String key, String value) throws IOException {
|
||||
writeConfig(bufferedWriter, "%s=%s", key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a formatted string to a buffered writer
|
||||
*
|
||||
* @param bufferedWriter <p>The buffered writer to write the formatted string to</p>
|
||||
* @param format <p>The format to use</p>
|
||||
* @param key <p>The config key to save</p>
|
||||
* @param value <p>The config value to save</p>
|
||||
* @throws IOException <p>If unable to write to the buffered writer</p>
|
||||
*/
|
||||
private void writeConfig(BufferedWriter bufferedWriter, String format, String key, Object value) throws IOException {
|
||||
bufferedWriter.append(String.format(format, key, value));
|
||||
bufferedWriter.newLine();
|
||||
}
|
||||
|
||||
}
|
344
src/main/java/net/knarcraft/stargate/portal/GateHandler.java
Normal file
344
src/main/java/net/knarcraft/stargate/portal/GateHandler.java
Normal file
@ -0,0 +1,344 @@
|
||||
package net.knarcraft.stargate.portal;
|
||||
|
||||
import net.knarcraft.stargate.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;
|
||||
|
||||
public class GateHandler {
|
||||
|
||||
private static final Character ANYTHING = ' ';
|
||||
private static final Character ENTRANCE = '.';
|
||||
private static final Character EXIT = '*';
|
||||
|
||||
private static Material defaultPortalBlockOpen = Material.NETHER_PORTAL;
|
||||
private static Material defaultPortalBlockClosed = Material.AIR;
|
||||
private static Material defaultButton = Material.STONE_BUTTON;
|
||||
|
||||
private static final HashMap<String, Gate> gates = new HashMap<>();
|
||||
private static final HashMap<Material, List<Gate>> controlBlocks = new HashMap<>();
|
||||
private static final HashSet<Material> frameBlocks = new HashSet<>();
|
||||
|
||||
private GateHandler() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the character used for blocks that are not part of the gate
|
||||
*
|
||||
* @return <p>The character used for blocks that are not part of the gate</p>
|
||||
*/
|
||||
public static Character getAnythingCharacter() {
|
||||
return ANYTHING;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the character used for defining the entrance
|
||||
*
|
||||
* @return <p>The character used for defining the entrance</p>
|
||||
*/
|
||||
public static Character getEntranceCharacter() {
|
||||
return ENTRANCE;
|
||||
}
|
||||
|
||||
public static Character getExitCharacter() {
|
||||
return EXIT;
|
||||
}
|
||||
|
||||
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<List<Character>> design = new ArrayList<>();
|
||||
HashMap<Character, Material> types = new HashMap<>();
|
||||
HashMap<String, String> config = new HashMap<>();
|
||||
HashSet<Material> 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<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 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<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;
|
||||
}
|
||||
|
||||
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<String, String> 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 <p>The config to read</p>
|
||||
* @param fileName <p>The config file the config belongs to</p>
|
||||
* @param key <p>The config key to read</p>
|
||||
* @param defaultMaterial <p>The default material to use, in case the config is invalid</p>
|
||||
* @return <p>The material to use</p>
|
||||
*/
|
||||
private static Material readConfig(HashMap<String, String> 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 <p>The folder containing the gates</p>
|
||||
*/
|
||||
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 <p>The folder containing gate config files</p>
|
||||
*/
|
||||
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 <p>The name of the gate file</p>
|
||||
* @param gateFolder <p>The folder containing gates</p>
|
||||
*/
|
||||
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
|
||||
*
|
||||
* <p>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.</p>
|
||||
*
|
||||
* @param block <p>The control block to check</p>
|
||||
* @return <p>A list of gates using the given control block</p>
|
||||
*/
|
||||
public static Gate[] getGatesByControlBlock(Block block) {
|
||||
return getGatesByControlBlock(block.getType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the gates with the given control block
|
||||
*
|
||||
* @param type <p>The type of the control block to check</p>
|
||||
* @return <p>A list of gates using the given material for control block</p>
|
||||
*/
|
||||
public static Gate[] getGatesByControlBlock(Material type) {
|
||||
Gate[] result = new Gate[0];
|
||||
List<Gate> lookup = controlBlocks.get(type);
|
||||
|
||||
if (lookup != null) {
|
||||
result = lookup.toArray(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a portal by its name (filename before .gate)
|
||||
*
|
||||
* @param name <p>The name of the gate to get</p>
|
||||
* @return <p>The gate with the given name</p>
|
||||
*/
|
||||
public static Gate getGateByName(String name) {
|
||||
return gates.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of loaded gate configurations
|
||||
*
|
||||
* @return <p>The number of loaded gate configurations</p>
|
||||
*/
|
||||
public static int getGateCount() {
|
||||
return gates.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given material is used for the frame of any portals
|
||||
*
|
||||
* @param type <p>The material type to check</p>
|
||||
* @return <p>True if the material is used for the frame of at least one portal</p>
|
||||
*/
|
||||
public static boolean isGateBlock(Material type) {
|
||||
return frameBlocks.contains(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all loaded gates
|
||||
*/
|
||||
public static void clearGates() {
|
||||
gates.clear();
|
||||
controlBlocks.clear();
|
||||
frameBlocks.clear();
|
||||
}
|
||||
|
||||
}
|
202
src/main/java/net/knarcraft/stargate/portal/GateLayout.java
Normal file
202
src/main/java/net/knarcraft/stargate/portal/GateLayout.java
Normal file
@ -0,0 +1,202 @@
|
||||
package net.knarcraft.stargate.portal;
|
||||
|
||||
import net.knarcraft.stargate.RelativeBlockVector;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The gate layout describes where every part of the gate should be
|
||||
*
|
||||
* <p>The gate layout parses a layout described by a Character matrix and stores the different parts of the gate as
|
||||
* relative block vectors.</p>
|
||||
*/
|
||||
public class GateLayout {
|
||||
|
||||
private Character [][] layout;
|
||||
private final HashMap<RelativeBlockVector, Integer> exits = new HashMap<>();
|
||||
private RelativeBlockVector[] entrances = new RelativeBlockVector[0];
|
||||
private RelativeBlockVector[] border = new RelativeBlockVector[0];
|
||||
private RelativeBlockVector[] controls = new RelativeBlockVector[0];
|
||||
private RelativeBlockVector exitBlock = null;
|
||||
|
||||
/**
|
||||
* Instantiates a new gate layout
|
||||
*
|
||||
* @param layout <p>A character array describing the layout</p>
|
||||
*/
|
||||
public GateLayout(Character[][] layout) {
|
||||
this.layout = layout;
|
||||
readLayout();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the character array describing this layout
|
||||
*
|
||||
* @return <p>The character array describing this layout</p>
|
||||
*/
|
||||
public Character[][] getLayout() {
|
||||
return this.layout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the locations of entrances for this gate
|
||||
*
|
||||
* @return <p>The locations of entrances for this gate</p>
|
||||
*/
|
||||
public RelativeBlockVector[] getEntrances() {
|
||||
return entrances;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the locations of border blocks for the gate described by this layout
|
||||
*
|
||||
* <p>A border block is basically any block of the frame. In terms of the nether gate, the border blocks are every
|
||||
* block of the gate that's not air when the gate is closed. The sign and button are not border blocks.</p>
|
||||
*
|
||||
* @return <p>The locations of border blocks for this gate</p>
|
||||
*/
|
||||
public RelativeBlockVector[] getBorder() {
|
||||
return border;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the exit block defined in the layout
|
||||
*
|
||||
* @return <p>The exit block defined in the layout</p>
|
||||
*/
|
||||
public RelativeBlockVector getExit() {
|
||||
return exitBlock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets other possible exits of the gate
|
||||
*
|
||||
* @return <p>Other possible gate exits</p>
|
||||
*/
|
||||
public HashMap<RelativeBlockVector, Integer> getExits() {
|
||||
return exits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the locations of the control blocks for this gate
|
||||
*
|
||||
* <p>The control blocks are the blocks where a sign can be placed to create a portal.</p>
|
||||
*
|
||||
* @return <p>The locations of the control blocks for this gate</p>
|
||||
*/
|
||||
public RelativeBlockVector[] getControls() {
|
||||
return controls;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the gate layout using a buffered writer
|
||||
*
|
||||
* @param bufferedWriter <p>The buffered writer to write to</p>
|
||||
* @throws IOException <p>If unable to write to the buffered writer</p>
|
||||
*/
|
||||
public void save(BufferedWriter bufferedWriter) throws IOException {
|
||||
for (Character[] line : this.layout) {
|
||||
for (Character symbol : line) {
|
||||
bufferedWriter.append(symbol);
|
||||
}
|
||||
bufferedWriter.newLine();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the gate layout to relative block vectors
|
||||
*/
|
||||
private void readLayout() {
|
||||
List<RelativeBlockVector> entranceList = new ArrayList<>();
|
||||
List<RelativeBlockVector> borderList = new ArrayList<>();
|
||||
List<RelativeBlockVector> controlList = new ArrayList<>();
|
||||
RelativeBlockVector[] relativeExits = new RelativeBlockVector[layout[0].length];
|
||||
RelativeBlockVector lastExit = null;
|
||||
|
||||
int[] exitDepths = readLayout(controlList, entranceList, borderList);
|
||||
|
||||
//Generate other possible exits
|
||||
for (int x = 0; x < exitDepths.length; x++) {
|
||||
relativeExits[x] = new RelativeBlockVector(x, exitDepths[x], 0);
|
||||
}
|
||||
|
||||
//Add non-null exits to the exits list
|
||||
for (int x = relativeExits.length - 1; x >= 0; x--) {
|
||||
if (relativeExits[x] != null) {
|
||||
lastExit = relativeExits[x];
|
||||
} else {
|
||||
relativeExits[x] = lastExit;
|
||||
}
|
||||
|
||||
if (exitDepths[x] > 0) {
|
||||
this.exits.put(relativeExits[x], x);
|
||||
}
|
||||
}
|
||||
|
||||
this.entrances = entranceList.toArray(this.entrances);
|
||||
this.border = borderList.toArray(this.border);
|
||||
this.controls = controlList.toArray(this.controls);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the given layout matrix, filling in the given lists of relative block vectors
|
||||
*
|
||||
* @param controlList <p>The list of control blocks to save to</p>
|
||||
* @param entranceList <p>The list of entrances to save to</p>
|
||||
* @param borderList <p>The list of border blocks to save to</p>
|
||||
* @return <p>A list of depths of possible extra exits</p>
|
||||
*/
|
||||
private int[] readLayout(List<RelativeBlockVector> controlList, List<RelativeBlockVector> entranceList,
|
||||
List<RelativeBlockVector> borderList) {
|
||||
//Store the depth/line of each
|
||||
int[] exitDepths = new int[layout[0].length];
|
||||
|
||||
int lineCount = layout.length;
|
||||
for (int lineIndex = 0; lineIndex < lineCount; lineIndex++) {
|
||||
int rowSize = layout[lineIndex].length;
|
||||
for (int rowIndex = 0; rowIndex < rowSize; rowIndex++) {
|
||||
Character key = layout[lineIndex][rowIndex];
|
||||
parseLayoutCharacter(key, rowIndex, lineIndex, exitDepths, controlList, entranceList, borderList);
|
||||
}
|
||||
}
|
||||
return exitDepths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses one character of the layout
|
||||
*
|
||||
* @param key <p>The character read</p>
|
||||
* @param rowIndex <p>The row of the read character</p>
|
||||
* @param lineIndex <p>The line of the read character</p>
|
||||
* @param exitDepths <p>The list of exit depths to save to</p>
|
||||
* @param controlList <p>The list of control blocks to save to</p>
|
||||
* @param entranceList <p>The list of entrances to save to</p>
|
||||
* @param borderList <p>The list of border blocks to save to</p>
|
||||
*/
|
||||
private void parseLayoutCharacter(Character key, int rowIndex, int lineIndex, int[] exitDepths,
|
||||
List<RelativeBlockVector> controlList, List<RelativeBlockVector> entranceList,
|
||||
List<RelativeBlockVector> borderList) {
|
||||
//Add control blocks
|
||||
if (key.equals('-')) {
|
||||
controlList.add(new RelativeBlockVector(rowIndex, lineIndex, 0));
|
||||
}
|
||||
|
||||
if (key.equals(GateHandler.getEntranceCharacter()) || key.equals(GateHandler.getExitCharacter())) {
|
||||
//Register entrances
|
||||
entranceList.add(new RelativeBlockVector(rowIndex, lineIndex, 0));
|
||||
//Find the lowest exit block at a given x position
|
||||
exitDepths[rowIndex] = lineIndex;
|
||||
//Register exit
|
||||
if (key.equals(GateHandler.getExitCharacter())) {
|
||||
this.exitBlock = new RelativeBlockVector(rowIndex, lineIndex, 0);
|
||||
}
|
||||
} else if (!key.equals(GateHandler.getAnythingCharacter())) {
|
||||
//Add border
|
||||
borderList.add(new RelativeBlockVector(rowIndex, lineIndex, 0));
|
||||
}
|
||||
}
|
||||
}
|
928
src/main/java/net/knarcraft/stargate/portal/Portal.java
Normal file
928
src/main/java/net/knarcraft/stargate/portal/Portal.java
Normal file
@ -0,0 +1,928 @@
|
||||
package net.knarcraft.stargate.portal;
|
||||
|
||||
import net.knarcraft.stargate.BlockLocation;
|
||||
import net.knarcraft.stargate.BloxPopulator;
|
||||
import net.knarcraft.stargate.EconomyHandler;
|
||||
import net.knarcraft.stargate.RelativeBlockVector;
|
||||
import net.knarcraft.stargate.Stargate;
|
||||
import net.knarcraft.stargate.event.StargateActivateEvent;
|
||||
import net.knarcraft.stargate.event.StargateCloseEvent;
|
||||
import net.knarcraft.stargate.event.StargateDeactivateEvent;
|
||||
import net.knarcraft.stargate.event.StargateOpenEvent;
|
||||
import net.knarcraft.stargate.event.StargatePortalEvent;
|
||||
import org.bukkit.Axis;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.block.Sign;
|
||||
import org.bukkit.block.data.Bisected;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.block.data.Powerable;
|
||||
import org.bukkit.entity.Boat;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.entity.Vehicle;
|
||||
import org.bukkit.entity.minecart.RideableMinecart;
|
||||
import org.bukkit.event.player.PlayerMoveEvent;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class Portal {
|
||||
|
||||
// Gate location block info
|
||||
private final BlockLocation topLeft;
|
||||
private final int modX;
|
||||
private final int modZ;
|
||||
private final float rotX;
|
||||
private final Axis rot;
|
||||
|
||||
// Block references
|
||||
private final BlockLocation id;
|
||||
private final Gate gate;
|
||||
private final World world;
|
||||
private BlockLocation button;
|
||||
private BlockLocation[] frame;
|
||||
private BlockLocation[] entrances;
|
||||
// Gate information
|
||||
private String name;
|
||||
private String destination;
|
||||
private String lastDestination = "";
|
||||
private String network;
|
||||
private String ownerName;
|
||||
private UUID ownerUUID;
|
||||
private boolean verified;
|
||||
private boolean fixed;
|
||||
private Map<PortalOption, Boolean> options;
|
||||
|
||||
// In-use information
|
||||
private Player player;
|
||||
private Player activePlayer;
|
||||
private List<String> destinations = new ArrayList<>();
|
||||
private boolean isOpen = false;
|
||||
private long openTime;
|
||||
|
||||
/**
|
||||
* Instantiates a new portal
|
||||
*
|
||||
* @param topLeft <p>The top-left block of the portal. This is used to decide the positions of the rest of the portal</p>
|
||||
* @param modX <p></p>
|
||||
* @param modZ <p></p>
|
||||
* @param rotX <p></p>
|
||||
* @param id <p>The location of the portal's id block, which is the sign which activated the portal</p>
|
||||
* @param button <p>The location of the portal's open button</p>
|
||||
* @param destination <p>The destination defined on the sign's destination line</p>
|
||||
* @param name <p>The name of the portal defined on the sign's first line</p>
|
||||
* @param verified <p>Whether the portal's gate has been verified to match its template</p>
|
||||
* @param network <p>The network the portal belongs to, defined on the sign's network line</p>
|
||||
* @param gate <p>The gate template this portal uses</p>
|
||||
* @param ownerUUID <p>The UUID of the gate's owner</p>
|
||||
* @param ownerName <p>The name of the gate's owner</p>
|
||||
* @param options <p>A map containing all possible portal options</p>
|
||||
*/
|
||||
Portal(BlockLocation topLeft, int modX, int modZ, float rotX, BlockLocation id, BlockLocation button,
|
||||
String destination, String name, boolean verified, String network, Gate gate, UUID ownerUUID,
|
||||
String ownerName, Map<PortalOption, Boolean> options) {
|
||||
this.topLeft = topLeft;
|
||||
this.modX = modX;
|
||||
this.modZ = modZ;
|
||||
this.rotX = rotX;
|
||||
this.rot = rotX == 0.0F || rotX == 180.0F ? Axis.X : Axis.Z;
|
||||
this.id = id;
|
||||
this.destination = destination;
|
||||
this.button = button;
|
||||
this.verified = verified;
|
||||
this.network = network;
|
||||
this.name = name;
|
||||
this.gate = gate;
|
||||
this.ownerUUID = ownerUUID;
|
||||
this.ownerName = ownerName;
|
||||
this.options = options;
|
||||
this.world = topLeft.getWorld();
|
||||
this.fixed = destination.length() > 0 || this.isRandom() || this.isBungee();
|
||||
|
||||
if (this.isAlwaysOn() && !this.isFixed()) {
|
||||
this.options.put(PortalOption.ALWAYS_ON, false);
|
||||
Stargate.debug("Portal", "Can not create a non-fixed always-on gate. Setting AlwaysOn = false");
|
||||
}
|
||||
|
||||
if (this.isRandom() && !this.isAlwaysOn()) {
|
||||
this.options.put(PortalOption.ALWAYS_ON, true);
|
||||
Stargate.debug("Portal", "Gate marked as random, set to always-on");
|
||||
}
|
||||
|
||||
if (verified) {
|
||||
this.drawSign();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the special characters |, : and # from a portal name
|
||||
*
|
||||
* @param input <p>The name to filter</p>
|
||||
* @return <p>The filtered name</p>
|
||||
*/
|
||||
public static String filterName(String input) {
|
||||
if (input == null) {
|
||||
return "";
|
||||
}
|
||||
return input.replaceAll("[|:#]", "").trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether this portal is currently open
|
||||
*
|
||||
* @return <p>Whether this portal is open</p>
|
||||
*/
|
||||
public boolean isOpen() {
|
||||
return isOpen || isAlwaysOn();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether this portal is always on
|
||||
*
|
||||
* @return <p>Whether this portal is always on</p>
|
||||
*/
|
||||
public boolean isAlwaysOn() {
|
||||
return this.options.get(PortalOption.ALWAYS_ON);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether this portal is hidden
|
||||
*
|
||||
* @return <p>Whether this portal is hidden</p>
|
||||
*/
|
||||
public boolean isHidden() {
|
||||
return this.options.get(PortalOption.HIDDEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether this portal is private
|
||||
*
|
||||
* @return <p>Whether this portal is private</p>
|
||||
*/
|
||||
public boolean isPrivate() {
|
||||
return this.options.get(PortalOption.PRIVATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether this portal is free
|
||||
*
|
||||
* @return <p>Whether this portal is free</p>
|
||||
*/
|
||||
public boolean isFree() {
|
||||
return this.options.get(PortalOption.FREE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether this portal is backwards
|
||||
*
|
||||
* <p>A backwards portal is one where players exit through the back.</p>
|
||||
*
|
||||
* @return <p>Whether this portal is backwards</p>
|
||||
*/
|
||||
public boolean isBackwards() {
|
||||
return this.options.get(PortalOption.BACKWARDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether this portal is shown on the network even if it's always on
|
||||
*
|
||||
* @return <p>Whether portal gate is shown</p>
|
||||
*/
|
||||
public boolean isShown() {
|
||||
return this.options.get(PortalOption.SHOW);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether this portal shows no network
|
||||
*
|
||||
* @return <p>Whether this portal shows no network/p>
|
||||
*/
|
||||
public boolean isNoNetwork() {
|
||||
return this.options.get(PortalOption.NO_NETWORK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether this portal goes to a random location on the network
|
||||
*
|
||||
* @return <p>Whether this portal goes to a random location</p>
|
||||
*/
|
||||
public boolean isRandom() {
|
||||
return this.options.get(PortalOption.RANDOM);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether this portal is a bungee portal
|
||||
*
|
||||
* @return <p>Whether this portal is a bungee portal</p>
|
||||
*/
|
||||
public boolean isBungee() {
|
||||
return this.options.get(PortalOption.BUNGEE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the rotation of the portal in degrees
|
||||
*
|
||||
* @return <p>The rotation of the portal</p>
|
||||
*/
|
||||
public float getRotation() {
|
||||
return rotX;
|
||||
}
|
||||
|
||||
public Axis getAxis() {
|
||||
return rot;
|
||||
}
|
||||
|
||||
public Player getActivePlayer() {
|
||||
return activePlayer;
|
||||
}
|
||||
|
||||
public String getNetwork() {
|
||||
return network;
|
||||
}
|
||||
|
||||
public void setNetwork(String network) {
|
||||
this.network = network;
|
||||
}
|
||||
|
||||
public long getOpenTime() {
|
||||
return openTime;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = filterName(name);
|
||||
drawSign();
|
||||
}
|
||||
|
||||
public Portal getDestination(Player player) {
|
||||
if (isRandom()) {
|
||||
destinations = PortalHandler.getDestinations(this, player, getNetwork());
|
||||
if (destinations.size() == 0) {
|
||||
return null;
|
||||
}
|
||||
String dest = destinations.get((new Random()).nextInt(destinations.size()));
|
||||
destinations.clear();
|
||||
return PortalHandler.getByName(dest, getNetwork());
|
||||
}
|
||||
return PortalHandler.getByName(destination, getNetwork());
|
||||
}
|
||||
|
||||
public Portal getDestination() {
|
||||
return getDestination(null);
|
||||
}
|
||||
|
||||
public void setDestination(Portal destination) {
|
||||
setDestination(destination.getName());
|
||||
}
|
||||
|
||||
public void setDestination(String destination) {
|
||||
this.destination = destination;
|
||||
}
|
||||
|
||||
public String getDestinationName() {
|
||||
return destination;
|
||||
}
|
||||
|
||||
public Gate getGate() {
|
||||
return gate;
|
||||
}
|
||||
|
||||
public String getOwnerName() {
|
||||
return ownerName;
|
||||
}
|
||||
|
||||
public UUID getOwnerUUID() {
|
||||
return ownerUUID;
|
||||
}
|
||||
|
||||
public void setOwner(UUID owner) {
|
||||
this.ownerUUID = owner;
|
||||
}
|
||||
|
||||
public boolean isOwner(Player player) {
|
||||
if (this.ownerUUID != null) {
|
||||
return player.getUniqueId().compareTo(this.ownerUUID) == 0;
|
||||
} else {
|
||||
return player.getName().equalsIgnoreCase(this.ownerName);
|
||||
}
|
||||
}
|
||||
|
||||
public BlockLocation[] getEntrances() {
|
||||
if (entrances == null) {
|
||||
RelativeBlockVector[] space = gate.getLayout().getEntrances();
|
||||
entrances = new BlockLocation[space.length];
|
||||
int i = 0;
|
||||
|
||||
for (RelativeBlockVector vector : space) {
|
||||
entrances[i++] = getBlockAt(vector);
|
||||
}
|
||||
}
|
||||
return entrances;
|
||||
}
|
||||
|
||||
public BlockLocation[] getFrame() {
|
||||
if (frame == null) {
|
||||
RelativeBlockVector[] border = gate.getLayout().getBorder();
|
||||
frame = new BlockLocation[border.length];
|
||||
int i = 0;
|
||||
|
||||
for (RelativeBlockVector vector : border) {
|
||||
frame[i++] = getBlockAt(vector);
|
||||
}
|
||||
}
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
public BlockLocation getSign() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public World getWorld() {
|
||||
return world;
|
||||
}
|
||||
|
||||
public BlockLocation getButton() {
|
||||
return button;
|
||||
}
|
||||
|
||||
public void setButton(BlockLocation button) {
|
||||
this.button = button;
|
||||
}
|
||||
|
||||
public boolean open(boolean force) {
|
||||
return open(null, force);
|
||||
}
|
||||
|
||||
public boolean open(Player openFor, boolean force) {
|
||||
// Call the StargateOpenEvent
|
||||
StargateOpenEvent event = new StargateOpenEvent(openFor, this, force);
|
||||
Stargate.server.getPluginManager().callEvent(event);
|
||||
if (event.isCancelled()) return false;
|
||||
force = event.getForce();
|
||||
|
||||
if (isOpen() && !force) return false;
|
||||
|
||||
Material openType = gate.getPortalOpenBlock();
|
||||
Axis ax = openType == Material.NETHER_PORTAL ? rot : null;
|
||||
for (BlockLocation inside : getEntrances()) {
|
||||
Stargate.blockPopulatorQueue.add(new BloxPopulator(inside, openType, ax));
|
||||
}
|
||||
|
||||
isOpen = true;
|
||||
openTime = System.currentTimeMillis() / 1000;
|
||||
Stargate.openList.add(this);
|
||||
Stargate.activeList.remove(this);
|
||||
|
||||
// Open remote gate
|
||||
if (!isAlwaysOn()) {
|
||||
player = openFor;
|
||||
|
||||
Portal end = getDestination();
|
||||
// Only open dest if it's not-fixed or points at this gate
|
||||
if (!isRandom() && end != null && (!end.isFixed() || end.getDestinationName().equalsIgnoreCase(getName())) && !end.isOpen()) {
|
||||
end.open(openFor, false);
|
||||
end.setDestination(this);
|
||||
if (end.isVerified()) end.drawSign();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void close(boolean force) {
|
||||
if (!isOpen) return;
|
||||
// Call the StargateCloseEvent
|
||||
StargateCloseEvent event = new StargateCloseEvent(this, force);
|
||||
Stargate.server.getPluginManager().callEvent(event);
|
||||
if (event.isCancelled()) return;
|
||||
force = event.getForce();
|
||||
|
||||
if (isAlwaysOn() && !force) return; // Only close always-open if forced
|
||||
|
||||
// Close this gate, then the dest gate.
|
||||
Material closedType = gate.getPortalClosedBlock();
|
||||
for (BlockLocation inside : getEntrances()) {
|
||||
Stargate.blockPopulatorQueue.add(new BloxPopulator(inside, closedType));
|
||||
}
|
||||
|
||||
player = null;
|
||||
isOpen = false;
|
||||
Stargate.openList.remove(this);
|
||||
Stargate.activeList.remove(this);
|
||||
|
||||
if (!isAlwaysOn()) {
|
||||
Portal end = getDestination();
|
||||
|
||||
if (end != null && end.isOpen()) {
|
||||
end.deactivate(); // Clear it's destination first.
|
||||
end.close(false);
|
||||
}
|
||||
}
|
||||
|
||||
deactivate();
|
||||
}
|
||||
|
||||
public boolean isOpenFor(Player player) {
|
||||
if (!isOpen) {
|
||||
return false;
|
||||
}
|
||||
if ((isAlwaysOn()) || (this.player == null)) {
|
||||
return true;
|
||||
}
|
||||
return (player != null) && (player.getName().equalsIgnoreCase(this.player.getName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether this portal points to a fixed exit portal
|
||||
*
|
||||
* @return <p>True if this portal points to a fixed exit portal</p>
|
||||
*/
|
||||
public boolean isFixed() {
|
||||
return fixed;
|
||||
}
|
||||
|
||||
public void setFixed(boolean fixed) {
|
||||
this.fixed = fixed;
|
||||
}
|
||||
|
||||
public boolean isPowered() {
|
||||
RelativeBlockVector[] controls = gate.getLayout().getControls();
|
||||
|
||||
for (RelativeBlockVector vector : controls) {
|
||||
BlockData data = getBlockAt(vector).getBlock().getBlockData();
|
||||
|
||||
if (data instanceof Powerable && ((Powerable) data).isPowered()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Teleports a player to this portal
|
||||
*
|
||||
* @param player <p>The player to teleport</p>
|
||||
* @param origin <p>The portal the player teleports from</p>
|
||||
* @param event <p>The player move event triggering the event</p>
|
||||
*/
|
||||
public void teleport(Player player, Portal origin, PlayerMoveEvent event) {
|
||||
Location traveller = player.getLocation();
|
||||
Location exit = getExit(player, traveller);
|
||||
|
||||
//Rotate the player to face out from the portal
|
||||
int adjust = 180;
|
||||
if (isBackwards() != origin.isBackwards()) {
|
||||
adjust = 0;
|
||||
}
|
||||
exit.setYaw(traveller.getYaw() - origin.getRotation() + this.getRotation() + adjust);
|
||||
|
||||
// Call the StargatePortalEvent to allow plugins to change destination
|
||||
if (!origin.equals(this)) {
|
||||
StargatePortalEvent stargatePortalEvent = new StargatePortalEvent(player, origin, this, exit);
|
||||
Stargate.server.getPluginManager().callEvent(stargatePortalEvent);
|
||||
// Teleport is cancelled
|
||||
if (stargatePortalEvent.isCancelled()) {
|
||||
origin.teleport(player, origin, event);
|
||||
return;
|
||||
}
|
||||
// Update exit if needed
|
||||
exit = stargatePortalEvent.getExit();
|
||||
}
|
||||
|
||||
// If no event is passed in, assume it's a teleport, and act as such
|
||||
if (event == null) {
|
||||
exit.setYaw(this.getRotation());
|
||||
player.teleport(exit);
|
||||
} else {
|
||||
// The new method to teleport in a move event is set the "to" field.
|
||||
event.setTo(exit);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Teleports a vehicle to this portal
|
||||
*
|
||||
* @param vehicle <p>The vehicle to teleport</p>
|
||||
*/
|
||||
public void teleport(final Vehicle vehicle) {
|
||||
Location traveller = new Location(this.world, vehicle.getLocation().getX(), vehicle.getLocation().getY(),
|
||||
vehicle.getLocation().getZ());
|
||||
Stargate.log.info(Stargate.getString("prefix") + "Location of vehicle is " + traveller);
|
||||
Location exit = getExit(vehicle, traveller);
|
||||
|
||||
double velocity = vehicle.getVelocity().length();
|
||||
|
||||
// Stop and teleport
|
||||
vehicle.setVelocity(new Vector());
|
||||
|
||||
// Get new velocity
|
||||
final Vector newVelocity = new Vector(modX, 0.0F, modZ);
|
||||
newVelocity.multiply(velocity);
|
||||
|
||||
List<Entity> passengers = vehicle.getPassengers();
|
||||
World vehicleWorld = exit.getWorld();
|
||||
if (vehicleWorld == null) {
|
||||
Stargate.log.warning(Stargate.getString("prefix") + "Unable to get the world to teleport the vehicle to");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!passengers.isEmpty()) {
|
||||
if (vehicle instanceof RideableMinecart || vehicle instanceof Boat) {
|
||||
putPlayerInNewVehicle(vehicle, passengers, vehicleWorld, exit, newVelocity);
|
||||
return;
|
||||
}
|
||||
vehicle.eject();
|
||||
handleVehiclePassengers(vehicle, passengers, vehicle, exit);
|
||||
vehicle.teleport(exit);
|
||||
Stargate.server.getScheduler().scheduleSyncDelayedTask(Stargate.stargate, () -> vehicle.setVelocity(newVelocity), 3);
|
||||
} else {
|
||||
Stargate.log.info(Stargate.getString("prefix") + "Teleported vehicle to " + exit);
|
||||
vehicle.teleport(exit);
|
||||
Stargate.server.getScheduler().scheduleSyncDelayedTask(Stargate.stargate, () -> {
|
||||
vehicle.setVelocity(newVelocity);
|
||||
}, 1);
|
||||
}
|
||||
}
|
||||
|
||||
private void putPlayerInNewVehicle(Vehicle vehicle, List<Entity> passengers, World vehicleWorld, Location exit, Vector newVelocity) {
|
||||
Vehicle newVehicle = vehicleWorld.spawn(exit, vehicle.getClass());
|
||||
vehicle.eject();
|
||||
vehicle.remove();
|
||||
handleVehiclePassengers(vehicle, passengers, newVehicle, exit);
|
||||
Stargate.server.getScheduler().scheduleSyncDelayedTask(Stargate.stargate, () -> newVehicle.setVelocity(newVelocity), 1);
|
||||
}
|
||||
|
||||
private void handleVehiclePassengers(Vehicle sourceVehicle, List<Entity> passengers, Vehicle targetVehicle, Location exit) {
|
||||
for (Entity passenger : passengers) {
|
||||
passenger.eject();
|
||||
Stargate.log.info("Teleporting passenger" + passenger + " to " + exit);
|
||||
if (!passenger.teleport(exit)) {
|
||||
Stargate.log.info("Failed to teleport passenger");
|
||||
}
|
||||
Stargate.server.getScheduler().scheduleSyncDelayedTask(Stargate.stargate, () -> targetVehicle.addPassenger(passenger), 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the exit location for a given entity and current location
|
||||
*
|
||||
* @param entity <p>The entity to teleport (used to determine distance from portal to avoid suffocation)</p>
|
||||
* @param traveller <p>The location of the entity travelling</p>
|
||||
* @return <p>The location the entity should be teleported to.</p>
|
||||
*/
|
||||
public Location getExit(Entity entity, Location traveller) {
|
||||
Location exitLocation = null;
|
||||
// Check if the gate has an exit block
|
||||
if (gate.getLayout().getExit() != null) {
|
||||
BlockLocation exit = getBlockAt(gate.getLayout().getExit());
|
||||
int back = (isBackwards()) ? -1 : 1;
|
||||
double entitySize = Math.ceil((float) Math.max(entity.getBoundingBox().getWidthX(), entity.getBoundingBox().getWidthZ()));
|
||||
exitLocation = exit.modRelativeLoc(0D, 0D, entitySize, traveller.getYaw(), traveller.getPitch(), modX * back, 1, modZ * back);
|
||||
} else {
|
||||
Stargate.log.log(Level.WARNING, Stargate.getString("prefix") + "Missing destination point in .gate file " + gate.getFilename());
|
||||
}
|
||||
|
||||
if (exitLocation != null) {
|
||||
//Prevent traveller from spawning inside a slab
|
||||
BlockData blockData = getWorld().getBlockAt(exitLocation).getBlockData();
|
||||
if (blockData instanceof Bisected && ((Bisected) blockData).getHalf() == Bisected.Half.BOTTOM) {
|
||||
exitLocation.add(0, 0.5, 0);
|
||||
}
|
||||
|
||||
exitLocation.setPitch(traveller.getPitch());
|
||||
return exitLocation;
|
||||
} else {
|
||||
Stargate.log.log(Level.WARNING, Stargate.getString("prefix") + "Unable to generate exit location");
|
||||
}
|
||||
return traveller;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the chunk the portal is located at is loaded
|
||||
*
|
||||
* @return <p>True if the chunk containing the portal is loaded</p>
|
||||
*/
|
||||
public boolean isChunkLoaded() {
|
||||
//TODO: Improve this in the case where the portal sits between two chunks
|
||||
return getWorld().isChunkLoaded(topLeft.getBlock().getChunk());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the identity (sign) location of the portal
|
||||
*
|
||||
* @return <p>The identity location of the portal</p>
|
||||
*/
|
||||
public BlockLocation getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public int getModX() {
|
||||
return this.modX;
|
||||
}
|
||||
|
||||
public int getModZ() {
|
||||
return this.modZ;
|
||||
}
|
||||
|
||||
public float getRotX() {
|
||||
return this.rotX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the location of the top-left block of the portal
|
||||
*
|
||||
* @return <p>The location of the top-left portal block</p>
|
||||
*/
|
||||
public BlockLocation getTopLeft() {
|
||||
return this.topLeft;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that all control blocks in this portal follows its gate template
|
||||
*
|
||||
* @return <p>True if all control blocks were verified</p>
|
||||
*/
|
||||
public boolean isVerified() {
|
||||
verified = true;
|
||||
if (!Stargate.verifyPortals) {
|
||||
return true;
|
||||
}
|
||||
for (RelativeBlockVector control : gate.getLayout().getControls()) {
|
||||
verified = verified && getBlockAt(control).getBlock().getType().equals(gate.getControlBlock());
|
||||
}
|
||||
return verified;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the result of the last portal verification
|
||||
*
|
||||
* @return <p>True if this portal was verified</p>
|
||||
*/
|
||||
public boolean wasVerified() {
|
||||
if (!Stargate.verifyPortals) {
|
||||
return true;
|
||||
}
|
||||
return verified;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if all blocks in a gate matches the gate template
|
||||
*
|
||||
* @return <p>True if all blocks match the gate template</p>
|
||||
*/
|
||||
public boolean checkIntegrity() {
|
||||
if (!Stargate.verifyPortals) {
|
||||
return true;
|
||||
}
|
||||
return gate.matches(topLeft, modX, modZ);
|
||||
}
|
||||
|
||||
/**
|
||||
* Activates this portal for the given player
|
||||
*
|
||||
* @param player <p>The player to activate the portal for</p>
|
||||
* @return <p>True if the portal was activated</p>
|
||||
*/
|
||||
public boolean activate(Player player) {
|
||||
destinations.clear();
|
||||
destination = "";
|
||||
Stargate.activeList.add(this);
|
||||
activePlayer = player;
|
||||
String network = getNetwork();
|
||||
destinations = PortalHandler.getDestinations(this, player, network);
|
||||
if (Stargate.sortLists) {
|
||||
Collections.sort(destinations);
|
||||
}
|
||||
if (Stargate.destMemory && !lastDestination.isEmpty() && destinations.contains(lastDestination)) {
|
||||
destination = lastDestination;
|
||||
}
|
||||
|
||||
StargateActivateEvent event = new StargateActivateEvent(this, player, destinations, destination);
|
||||
Stargate.server.getPluginManager().callEvent(event);
|
||||
if (event.isCancelled()) {
|
||||
Stargate.activeList.remove(this);
|
||||
return false;
|
||||
}
|
||||
destination = event.getDestination();
|
||||
destinations = event.getDestinations();
|
||||
drawSign();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivates this portal
|
||||
*/
|
||||
public void deactivate() {
|
||||
StargateDeactivateEvent event = new StargateDeactivateEvent(this);
|
||||
Stargate.server.getPluginManager().callEvent(event);
|
||||
if (event.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Stargate.activeList.remove(this);
|
||||
if (isFixed()) {
|
||||
return;
|
||||
}
|
||||
destinations.clear();
|
||||
destination = "";
|
||||
activePlayer = null;
|
||||
drawSign();
|
||||
}
|
||||
|
||||
public boolean isActive() {
|
||||
return isFixed() || (destinations.size() > 0);
|
||||
}
|
||||
|
||||
public void cycleDestination(Player player) {
|
||||
cycleDestination(player, 1);
|
||||
}
|
||||
|
||||
public void cycleDestination(Player player, int dir) {
|
||||
boolean activate = false;
|
||||
if (!isActive() || getActivePlayer() != player) {
|
||||
// If the event is cancelled, return
|
||||
if (!activate(player)) {
|
||||
return;
|
||||
}
|
||||
Stargate.debug("cycleDestination", "Network Size: " + PortalHandler.getNetwork(network).size());
|
||||
Stargate.debug("cycleDestination", "Player has access to: " + destinations.size());
|
||||
activate = true;
|
||||
}
|
||||
|
||||
if (destinations.size() == 0) {
|
||||
Stargate.sendMessage(player, Stargate.getString("destEmpty"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Stargate.destMemory || !activate || lastDestination.isEmpty()) {
|
||||
int index = destinations.indexOf(destination);
|
||||
index += dir;
|
||||
if (index >= destinations.size())
|
||||
index = 0;
|
||||
else if (index < 0)
|
||||
index = destinations.size() - 1;
|
||||
destination = destinations.get(index);
|
||||
lastDestination = destination;
|
||||
}
|
||||
openTime = System.currentTimeMillis() / 1000;
|
||||
drawSign();
|
||||
}
|
||||
|
||||
public final void drawSign() {
|
||||
BlockState state = id.getBlock().getState();
|
||||
if (!(state instanceof Sign)) {
|
||||
Stargate.log.warning(Stargate.getString("prefix") + "Sign block is not a Sign object");
|
||||
Stargate.debug("Portal::drawSign", "Block: " + id.getBlock().getType() + " @ " + id.getBlock().getLocation());
|
||||
return;
|
||||
}
|
||||
Sign sign = (Sign) state;
|
||||
Stargate.setLine(sign, 0, "-" + name + "-");
|
||||
int max = destinations.size() - 1;
|
||||
int done = 0;
|
||||
|
||||
if (!isActive()) {
|
||||
Stargate.setLine(sign, ++done, Stargate.getString("signRightClick"));
|
||||
Stargate.setLine(sign, ++done, Stargate.getString("signToUse"));
|
||||
if (!isNoNetwork()) {
|
||||
Stargate.setLine(sign, ++done, "(" + network + ")");
|
||||
}
|
||||
} else {
|
||||
// Awesome new logic for Bungee gates
|
||||
if (isBungee()) {
|
||||
Stargate.setLine(sign, ++done, Stargate.getString("bungeeSign"));
|
||||
Stargate.setLine(sign, ++done, ">" + destination + "<");
|
||||
Stargate.setLine(sign, ++done, "[" + network + "]");
|
||||
} else if (isFixed()) {
|
||||
if (isRandom()) {
|
||||
Stargate.setLine(sign, ++done, "> " + Stargate.getString("signRandom") + " <");
|
||||
} else {
|
||||
Stargate.setLine(sign, ++done, ">" + destination + "<");
|
||||
}
|
||||
if (isNoNetwork()) {
|
||||
Stargate.setLine(sign, ++done, "");
|
||||
} else {
|
||||
Stargate.setLine(sign, ++done, "(" + network + ")");
|
||||
}
|
||||
Portal dest = PortalHandler.getByName(destination, network);
|
||||
if (dest == null && !isRandom()) {
|
||||
Stargate.setLine(sign, ++done, Stargate.getString("signDisconnected"));
|
||||
} else {
|
||||
Stargate.setLine(sign, ++done, "");
|
||||
}
|
||||
} else {
|
||||
int index = destinations.indexOf(destination);
|
||||
if ((index == max) && (max > 1) && (++done <= 3)) {
|
||||
if (EconomyHandler.useEconomy() && EconomyHandler.freeGatesGreen) {
|
||||
Portal dest = PortalHandler.getByName(destinations.get(index - 2), network);
|
||||
boolean green = Stargate.isFree(activePlayer, this, dest);
|
||||
Stargate.setLine(sign, done, (green ? ChatColor.DARK_GREEN : "") + destinations.get(index - 2));
|
||||
} else {
|
||||
Stargate.setLine(sign, done, destinations.get(index - 2));
|
||||
}
|
||||
}
|
||||
if ((index > 0) && (++done <= 3)) {
|
||||
if (EconomyHandler.useEconomy() && EconomyHandler.freeGatesGreen) {
|
||||
Portal dest = PortalHandler.getByName(destinations.get(index - 1), network);
|
||||
boolean green = Stargate.isFree(activePlayer, this, dest);
|
||||
Stargate.setLine(sign, done, (green ? ChatColor.DARK_GREEN : "") + destinations.get(index - 1));
|
||||
} else {
|
||||
Stargate.setLine(sign, done, destinations.get(index - 1));
|
||||
}
|
||||
}
|
||||
if (++done <= 3) {
|
||||
if (EconomyHandler.useEconomy() && EconomyHandler.freeGatesGreen) {
|
||||
Portal dest = PortalHandler.getByName(destination, network);
|
||||
boolean green = Stargate.isFree(activePlayer, this, dest);
|
||||
Stargate.setLine(sign, done, (green ? ChatColor.DARK_GREEN : "") + ">" + destination + "<");
|
||||
} else {
|
||||
Stargate.setLine(sign, done, " >" + destination + "< ");
|
||||
}
|
||||
}
|
||||
if ((max >= index + 1) && (++done <= 3)) {
|
||||
if (EconomyHandler.useEconomy() && EconomyHandler.freeGatesGreen) {
|
||||
Portal dest = PortalHandler.getByName(destinations.get(index + 1), network);
|
||||
boolean green = Stargate.isFree(activePlayer, this, dest);
|
||||
Stargate.setLine(sign, done, (green ? ChatColor.DARK_GREEN : "") + destinations.get(index + 1));
|
||||
} else {
|
||||
Stargate.setLine(sign, done, destinations.get(index + 1));
|
||||
}
|
||||
}
|
||||
if ((max >= index + 2) && (++done <= 3)) {
|
||||
if (EconomyHandler.useEconomy() && EconomyHandler.freeGatesGreen) {
|
||||
Portal dest = PortalHandler.getByName(destinations.get(index + 2), network);
|
||||
boolean green = Stargate.isFree(activePlayer, this, dest);
|
||||
Stargate.setLine(sign, done, (green ? ChatColor.DARK_GREEN : "") + destinations.get(index + 2));
|
||||
} else {
|
||||
Stargate.setLine(sign, done, destinations.get(index + 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (done++; done <= 3; done++) {
|
||||
sign.setLine(done, "");
|
||||
}
|
||||
|
||||
sign.update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the block at a relative block vector location
|
||||
*
|
||||
* @param vector <p>The relative block vector</p>
|
||||
* @return <p>The block at the given relative position</p>
|
||||
*/
|
||||
BlockLocation getBlockAt(RelativeBlockVector vector) {
|
||||
return topLeft.modRelative(vector.getRight(), vector.getDepth(), vector.getDistance(), modX, 1, modZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("Portal [id=%s, network=%s name=%s, type=%s]", id, network, name, gate.getFilename());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((name == null) ? 0 : name.hashCode());
|
||||
result = prime * result + ((network == null) ? 0 : network.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
Portal other = (Portal) obj;
|
||||
if (name == null) {
|
||||
if (other.name != null)
|
||||
return false;
|
||||
} else if (!name.equalsIgnoreCase(other.name))
|
||||
return false;
|
||||
if (network == null) {
|
||||
return other.network == null;
|
||||
} else {
|
||||
return network.equalsIgnoreCase(other.network);
|
||||
}
|
||||
}
|
||||
}
|
951
src/main/java/net/knarcraft/stargate/portal/PortalHandler.java
Normal file
951
src/main/java/net/knarcraft/stargate/portal/PortalHandler.java
Normal file
@ -0,0 +1,951 @@
|
||||
package net.knarcraft.stargate.portal;
|
||||
|
||||
import net.knarcraft.stargate.BlockLocation;
|
||||
import net.knarcraft.stargate.RelativeBlockVector;
|
||||
import net.knarcraft.stargate.Stargate;
|
||||
import net.knarcraft.stargate.TwoTuple;
|
||||
import net.knarcraft.stargate.event.StargateCreateEvent;
|
||||
import net.knarcraft.stargate.utility.EconomyHelper;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.block.Sign;
|
||||
import org.bukkit.block.data.Directional;
|
||||
import org.bukkit.block.data.type.WallSign;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.block.SignChangeEvent;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Scanner;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class PortalHandler {
|
||||
// Static variables used to store portal lists
|
||||
private static final Map<BlockLocation, Portal> lookupBlocks = new HashMap<>();
|
||||
private static final Map<BlockLocation, Portal> lookupEntrances = new HashMap<>();
|
||||
private static final Map<BlockLocation, Portal> lookupControls = new HashMap<>();
|
||||
private static final List<Portal> allPortals = new ArrayList<>();
|
||||
private static final HashMap<String, List<String>> allPortalsNet = new HashMap<>();
|
||||
private static final HashMap<String, HashMap<String, Portal>> lookupNamesNet = new HashMap<>();
|
||||
|
||||
// A list of Bungee gates
|
||||
private static final Map<String, Portal> bungeePortals = new HashMap<>();
|
||||
|
||||
private PortalHandler() {
|
||||
|
||||
}
|
||||
|
||||
public static List<String> getNetwork(String network) {
|
||||
return allPortalsNet.get(network.toLowerCase());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all destinations in the network viewable by the given player
|
||||
*
|
||||
* @param entrancePortal <p>The portal the user is entering from</p>
|
||||
* @param player <p>The player who wants to see destinations</p>
|
||||
* @param network <p>The network to get destinations from</p>
|
||||
* @return <p>All destinations the player can go to</p>
|
||||
*/
|
||||
public static List<String> getDestinations(Portal entrancePortal, Player player, String network) {
|
||||
List<String> destinations = new ArrayList<>();
|
||||
for (String destination : allPortalsNet.get(network.toLowerCase())) {
|
||||
Portal portal = getByName(destination, network);
|
||||
if (portal == null) {
|
||||
continue;
|
||||
}
|
||||
// Check if destination is a random gate
|
||||
if (portal.isRandom()) {
|
||||
continue;
|
||||
}
|
||||
// Check if destination is always open (Don't show if so)
|
||||
if (portal.isAlwaysOn() && !portal.isShown()) {
|
||||
continue;
|
||||
}
|
||||
// Check if destination is this portal
|
||||
if (destination.equalsIgnoreCase(entrancePortal.getName())) {
|
||||
continue;
|
||||
}
|
||||
// Check if destination is a fixed gate not pointing to this gate
|
||||
if (portal.isFixed() && !portal.getDestinationName().equalsIgnoreCase(entrancePortal.getName())) {
|
||||
continue;
|
||||
}
|
||||
// Allow random use by non-players (Minecarts)
|
||||
if (player == null) {
|
||||
destinations.add(portal.getName());
|
||||
continue;
|
||||
}
|
||||
// Check if this player can access the dest world
|
||||
if (!Stargate.canAccessWorld(player, portal.getWorld().getName())) {
|
||||
Stargate.log.info("cannot access world");
|
||||
continue;
|
||||
}
|
||||
// Visible to this player.
|
||||
if (Stargate.canSee(player, portal)) {
|
||||
destinations.add(portal.getName());
|
||||
}
|
||||
}
|
||||
return destinations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Un-registers the given portal
|
||||
*
|
||||
* @param portal <p>The portal to un-register</p>
|
||||
* @param removeAll <p>Whether to remove the portal from the list of all portals</p>
|
||||
*/
|
||||
public static void unregisterPortal(Portal portal, boolean removeAll) {
|
||||
Stargate.debug("Unregister", "Unregistering gate " + portal.getName());
|
||||
portal.close(true);
|
||||
|
||||
for (BlockLocation block : portal.getFrame()) {
|
||||
lookupBlocks.remove(block);
|
||||
}
|
||||
// Include the sign and button
|
||||
lookupBlocks.remove(portal.getId());
|
||||
lookupControls.remove(portal.getId());
|
||||
if (portal.getButton() != null) {
|
||||
lookupBlocks.remove(portal.getButton());
|
||||
lookupControls.remove(portal.getButton());
|
||||
}
|
||||
|
||||
for (BlockLocation entrance : portal.getEntrances()) {
|
||||
lookupEntrances.remove(entrance);
|
||||
}
|
||||
|
||||
if (removeAll) {
|
||||
allPortals.remove(portal);
|
||||
}
|
||||
|
||||
if (portal.isBungee()) {
|
||||
bungeePortals.remove(portal.getName().toLowerCase());
|
||||
} else {
|
||||
lookupNamesNet.get(portal.getNetwork().toLowerCase()).remove(portal.getName().toLowerCase());
|
||||
allPortalsNet.get(portal.getNetwork().toLowerCase()).remove(portal.getName().toLowerCase());
|
||||
|
||||
for (String originName : allPortalsNet.get(portal.getNetwork().toLowerCase())) {
|
||||
Portal origin = getByName(originName, portal.getNetwork());
|
||||
if (origin == null) continue;
|
||||
if (!origin.getDestinationName().equalsIgnoreCase(portal.getName())) continue;
|
||||
if (!origin.isVerified()) continue;
|
||||
if (origin.isFixed()) origin.drawSign();
|
||||
if (origin.isAlwaysOn()) origin.close(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (portal.getId().getBlock().getBlockData() instanceof WallSign) {
|
||||
Sign sign = (Sign) portal.getId().getBlock().getState();
|
||||
sign.setLine(0, portal.getName());
|
||||
sign.setLine(1, "");
|
||||
sign.setLine(2, "");
|
||||
sign.setLine(3, "");
|
||||
sign.update();
|
||||
}
|
||||
|
||||
saveAllGates(portal.getWorld());
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a portal
|
||||
*
|
||||
* @param portal <p>The portal to register</p>
|
||||
*/
|
||||
private static void registerPortal(Portal portal) {
|
||||
portal.setFixed(portal.getDestinationName().length() > 0 || portal.isRandom() || portal.isBungee());
|
||||
|
||||
// Bungee gates are stored in their own list
|
||||
if (portal.isBungee()) {
|
||||
bungeePortals.put(portal.getName().toLowerCase(), portal);
|
||||
} else {
|
||||
// Check if network exists in our network list
|
||||
if (!lookupNamesNet.containsKey(portal.getNetwork().toLowerCase())) {
|
||||
Stargate.debug("register", "Network " + portal.getNetwork() + " not in lookupNamesNet, adding");
|
||||
lookupNamesNet.put(portal.getNetwork().toLowerCase(), new HashMap<>());
|
||||
}
|
||||
lookupNamesNet.get(portal.getNetwork().toLowerCase()).put(portal.getName().toLowerCase(), portal);
|
||||
|
||||
// Check if this network exists
|
||||
if (!allPortalsNet.containsKey(portal.getNetwork().toLowerCase())) {
|
||||
Stargate.debug("register", "Network " + portal.getNetwork() + " not in allPortalsNet, adding");
|
||||
allPortalsNet.put(portal.getNetwork().toLowerCase(), new ArrayList<>());
|
||||
}
|
||||
allPortalsNet.get(portal.getNetwork().toLowerCase()).add(portal.getName().toLowerCase());
|
||||
}
|
||||
|
||||
for (BlockLocation block : portal.getFrame()) {
|
||||
lookupBlocks.put(block, portal);
|
||||
}
|
||||
// Include the sign and button
|
||||
lookupBlocks.put(portal.getId(), portal);
|
||||
lookupControls.put(portal.getId(), portal);
|
||||
if (portal.getButton() != null) {
|
||||
lookupBlocks.put(portal.getButton(), portal);
|
||||
lookupControls.put(portal.getButton(), portal);
|
||||
}
|
||||
|
||||
|
||||
for (BlockLocation entrance : portal.getEntrances()) {
|
||||
lookupEntrances.put(entrance, portal);
|
||||
}
|
||||
|
||||
allPortals.add(portal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new portal
|
||||
*
|
||||
* @param event <p>The sign change event which initialized the creation</p>
|
||||
* @param player <p>The player who's creating the portal</p>
|
||||
* @return <p>The created portal</p>
|
||||
*/
|
||||
public static Portal createPortal(SignChangeEvent event, Player player) {
|
||||
BlockLocation id = new BlockLocation(event.getBlock());
|
||||
Block idParent = id.getParent();
|
||||
if (idParent == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (GateHandler.getGatesByControlBlock(idParent).length == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (getByBlock(idParent) != null) {
|
||||
Stargate.debug("createPortal", "idParent belongs to existing gate");
|
||||
return null;
|
||||
}
|
||||
|
||||
BlockLocation parent = new BlockLocation(player.getWorld(), idParent.getX(), idParent.getY(), idParent.getZ());
|
||||
BlockLocation topLeft = null;
|
||||
String name = filterName(event.getLine(0));
|
||||
String destinationName = filterName(event.getLine(1));
|
||||
String network = filterName(event.getLine(2));
|
||||
String options = filterName(event.getLine(3)).toLowerCase();
|
||||
|
||||
Map<PortalOption, Boolean> portalOptions = getPortalOptions(player, destinationName, options);
|
||||
|
||||
// Moved the layout check so as to avoid invalid messages when not making a gate
|
||||
int modX = 0;
|
||||
int modZ = 0;
|
||||
float rotX = 0f;
|
||||
BlockFace buttonFacing = BlockFace.DOWN;
|
||||
|
||||
if (idParent.getX() > id.getBlock().getX()) {
|
||||
modZ -= 1;
|
||||
rotX = 90f;
|
||||
buttonFacing = BlockFace.WEST;
|
||||
} else if (idParent.getX() < id.getBlock().getX()) {
|
||||
modZ += 1;
|
||||
rotX = 270f;
|
||||
buttonFacing = BlockFace.EAST;
|
||||
} else if (idParent.getZ() > id.getBlock().getZ()) {
|
||||
modX += 1;
|
||||
rotX = 180f;
|
||||
buttonFacing = BlockFace.NORTH;
|
||||
} else if (idParent.getZ() < id.getBlock().getZ()) {
|
||||
modX -= 1;
|
||||
rotX = 0f;
|
||||
buttonFacing = BlockFace.SOUTH;
|
||||
}
|
||||
|
||||
Gate[] possibleGates = GateHandler.getGatesByControlBlock(idParent);
|
||||
Gate gate = null;
|
||||
RelativeBlockVector buttonVector = null;
|
||||
|
||||
for (Gate possibility : possibleGates) {
|
||||
if (gate != null || buttonVector != null) {
|
||||
break;
|
||||
}
|
||||
RelativeBlockVector[] vectors = possibility.getLayout().getControls();
|
||||
RelativeBlockVector otherControl = null;
|
||||
|
||||
for (RelativeBlockVector vector : vectors) {
|
||||
BlockLocation tl = parent.modRelative(-vector.getRight(), -vector.getDepth(), -vector.getDistance(), modX, 1, modZ);
|
||||
|
||||
if (gate == null) {
|
||||
if (possibility.matches(tl, modX, modZ, true)) {
|
||||
gate = possibility;
|
||||
topLeft = tl;
|
||||
|
||||
if (otherControl != null) {
|
||||
buttonVector = otherControl;
|
||||
}
|
||||
}
|
||||
} else if (otherControl != null) {
|
||||
buttonVector = vector;
|
||||
}
|
||||
|
||||
otherControl = vector;
|
||||
}
|
||||
}
|
||||
|
||||
if ((gate == null) || (buttonVector == null)) {
|
||||
Stargate.debug("createPortal", "Could not find matching gate layout");
|
||||
return null;
|
||||
}
|
||||
|
||||
// If the player is trying to create a Bungee gate without permissions, drop out here
|
||||
// Do this after the gate layout check, in the least
|
||||
if (options.indexOf(PortalOption.BUNGEE.getCharacterRepresentation()) != -1) {
|
||||
if (!Stargate.hasPerm(player, "stargate.admin.bungee")) {
|
||||
Stargate.sendMessage(player, Stargate.getString("bungeeDeny"));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (portalOptions.get(PortalOption.BUNGEE)) {
|
||||
if (!Stargate.enableBungee) {
|
||||
Stargate.sendMessage(player, Stargate.getString("bungeeDisabled"));
|
||||
return null;
|
||||
} else if (destinationName.isEmpty() || network.isEmpty()) {
|
||||
Stargate.sendMessage(player, Stargate.getString("bungeeEmpty"));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Debug
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (PortalOption option : portalOptions.keySet()) {
|
||||
builder.append(option.getCharacterRepresentation()).append(" = ").append(portalOptions.get(option)).append(" ");
|
||||
}
|
||||
Stargate.debug("createPortal", builder.toString());
|
||||
|
||||
if (!portalOptions.get(PortalOption.BUNGEE) && (network.length() < 1 || network.length() > 11)) {
|
||||
network = Stargate.getDefaultNetwork();
|
||||
}
|
||||
|
||||
boolean deny = false;
|
||||
String denyMsg = "";
|
||||
|
||||
// Check if the player can create gates on this network
|
||||
if (!portalOptions.get(PortalOption.BUNGEE) && !Stargate.canCreate(player, network)) {
|
||||
Stargate.debug("createPortal", "Player doesn't have create permissions on network. Trying personal");
|
||||
if (Stargate.canCreatePersonal(player)) {
|
||||
network = player.getName();
|
||||
if (network.length() > 11) network = network.substring(0, 11);
|
||||
Stargate.debug("createPortal", "Creating personal portal");
|
||||
Stargate.sendMessage(player, Stargate.getString("createPersonal"));
|
||||
} else {
|
||||
Stargate.debug("createPortal", "Player does not have access to network");
|
||||
deny = true;
|
||||
denyMsg = Stargate.getString("createNetDeny");
|
||||
//return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the player can create this gate layout
|
||||
String gateName = gate.getFilename();
|
||||
gateName = gateName.substring(0, gateName.indexOf('.'));
|
||||
if (!deny && !Stargate.canCreateGate(player, gateName)) {
|
||||
Stargate.debug("createPortal", "Player does not have access to gate layout");
|
||||
deny = true;
|
||||
denyMsg = Stargate.getString("createGateDeny");
|
||||
}
|
||||
|
||||
// Check if the user can create gates to this world.
|
||||
if (!portalOptions.get(PortalOption.BUNGEE) && !deny && destinationName.length() > 0) {
|
||||
Portal p = getByName(destinationName, network);
|
||||
if (p != null) {
|
||||
String world = p.getWorld().getName();
|
||||
if (!Stargate.canAccessWorld(player, world)) {
|
||||
Stargate.debug("canCreate", "Player does not have access to destination world");
|
||||
deny = true;
|
||||
denyMsg = Stargate.getString("createWorldDeny");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Bleh, gotta check to make sure none of this gate belongs to another gate. Boo slow.
|
||||
for (RelativeBlockVector v : gate.getLayout().getBorder()) {
|
||||
BlockLocation b = topLeft.modRelative(v.getRight(), v.getDepth(), v.getDistance(), modX, 1, modZ);
|
||||
if (getByBlock(b.getBlock()) != null) {
|
||||
Stargate.debug("createPortal", "Gate conflicts with existing gate");
|
||||
Stargate.sendMessage(player, Stargate.getString("createConflict"));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
BlockLocation button = null;
|
||||
Portal portal;
|
||||
portal = new Portal(topLeft, modX, modZ, rotX, id, button, destinationName, name, false, network,
|
||||
gate, player.getUniqueId(), player.getName(), portalOptions);
|
||||
|
||||
int cost = Stargate.getCreateCost(player, gate);
|
||||
|
||||
// Call StargateCreateEvent
|
||||
StargateCreateEvent cEvent = new StargateCreateEvent(player, portal, event.getLines(), deny, denyMsg, cost);
|
||||
Stargate.server.getPluginManager().callEvent(cEvent);
|
||||
if (cEvent.isCancelled()) {
|
||||
return null;
|
||||
}
|
||||
if (cEvent.getDeny()) {
|
||||
Stargate.sendMessage(player, cEvent.getDenyReason());
|
||||
return null;
|
||||
}
|
||||
|
||||
cost = cEvent.getCost();
|
||||
|
||||
// Name & Network can be changed in the event, so do these checks here.
|
||||
if (portal.getName().length() < 1 || portal.getName().length() > 11) {
|
||||
Stargate.debug("createPortal", "Name length error");
|
||||
Stargate.sendMessage(player, Stargate.getString("createNameLength"));
|
||||
return null;
|
||||
}
|
||||
|
||||
// Don't do network checks for bungee gates
|
||||
if (portal.isBungee()) {
|
||||
if (bungeePortals.get(portal.getName().toLowerCase()) != null) {
|
||||
Stargate.debug("createPortal::Bungee", "Gate Exists");
|
||||
Stargate.sendMessage(player, Stargate.getString("createExists"));
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
if (getByName(portal.getName(), portal.getNetwork()) != null) {
|
||||
Stargate.debug("createPortal", "Name Error");
|
||||
Stargate.sendMessage(player, Stargate.getString("createExists"));
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check if there are too many gates in this network
|
||||
List<String> netList = allPortalsNet.get(portal.getNetwork().toLowerCase());
|
||||
if (Stargate.maxGates > 0 && netList != null && netList.size() >= Stargate.maxGates) {
|
||||
Stargate.sendMessage(player, Stargate.getString("createFull"));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (cost > 0) {
|
||||
if (!Stargate.chargePlayer(player, cost)) {
|
||||
EconomyHelper.sendInsufficientFundsMessage(name, player, cost);
|
||||
Stargate.debug("createPortal", "Insufficient Funds");
|
||||
return null;
|
||||
}
|
||||
EconomyHelper.sendDeductMessage(name, player, cost);
|
||||
}
|
||||
|
||||
// No button on an always-open gate.
|
||||
if (!portalOptions.get(PortalOption.ALWAYS_ON)) {
|
||||
button = topLeft.modRelative(buttonVector.getRight(), buttonVector.getDepth(), buttonVector.getDistance() + 1, modX, 1, modZ);
|
||||
Directional buttonData = (Directional) Bukkit.createBlockData(gate.getPortalButton());
|
||||
buttonData.setFacing(buttonFacing);
|
||||
button.getBlock().setBlockData(buttonData);
|
||||
portal.setButton(button);
|
||||
}
|
||||
|
||||
registerPortal(portal);
|
||||
portal.drawSign();
|
||||
// Open always on gate
|
||||
if (portal.isRandom() || portal.isBungee()) {
|
||||
portal.open(true);
|
||||
} else if (portal.isAlwaysOn()) {
|
||||
Portal dest = getByName(destinationName, portal.getNetwork());
|
||||
if (dest != null) {
|
||||
portal.open(true);
|
||||
dest.drawSign();
|
||||
}
|
||||
// Set the inside of the gate to its closed material
|
||||
} else {
|
||||
for (BlockLocation inside : portal.getEntrances()) {
|
||||
inside.setType(portal.getGate().getPortalClosedBlock());
|
||||
}
|
||||
}
|
||||
|
||||
// Don't do network stuff for bungee gates
|
||||
if (!portal.isBungee()) {
|
||||
// Open any always on gate pointing at this gate
|
||||
for (String originName : allPortalsNet.get(portal.getNetwork().toLowerCase())) {
|
||||
Portal origin = getByName(originName, portal.getNetwork());
|
||||
if (origin == null) continue;
|
||||
if (!origin.getDestinationName().equalsIgnoreCase(portal.getName())) continue;
|
||||
if (!origin.isVerified()) continue;
|
||||
if (origin.isFixed()) origin.drawSign();
|
||||
if (origin.isAlwaysOn()) origin.open(true);
|
||||
}
|
||||
}
|
||||
|
||||
saveAllGates(portal.getWorld());
|
||||
|
||||
return portal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all portal options to be applied to a new gate
|
||||
*
|
||||
* @param player <p>The player creating the portal</p>
|
||||
* @param destinationName <p>The destination of the portal</p>
|
||||
* @param options <p>The string on the option line of the sign</p>
|
||||
* @return <p>A map containing all portal options and their values</p>
|
||||
*/
|
||||
private static Map<PortalOption, Boolean> getPortalOptions(Player player, String destinationName, String options) {
|
||||
Map<PortalOption, Boolean> portalOptions = new HashMap<>();
|
||||
for (PortalOption option : PortalOption.values()) {
|
||||
portalOptions.put(option, options.indexOf(option.getCharacterRepresentation()) != -1 &&
|
||||
Stargate.canOption(player, option));
|
||||
}
|
||||
|
||||
// Can not create a non-fixed always-on gate.
|
||||
if (portalOptions.get(PortalOption.ALWAYS_ON) && destinationName.length() == 0) {
|
||||
portalOptions.put(PortalOption.ALWAYS_ON, false);
|
||||
}
|
||||
|
||||
// Show isn't useful if always on is false
|
||||
if (portalOptions.get(PortalOption.SHOW) && !portalOptions.get(PortalOption.ALWAYS_ON)) {
|
||||
portalOptions.put(PortalOption.SHOW, false);
|
||||
}
|
||||
|
||||
// Random gates are always on and can't be shown
|
||||
if (portalOptions.get(PortalOption.RANDOM)) {
|
||||
portalOptions.put(PortalOption.ALWAYS_ON, true);
|
||||
portalOptions.put(PortalOption.SHOW, false);
|
||||
}
|
||||
|
||||
// Bungee gates are always on and don't support Random
|
||||
if (portalOptions.get(PortalOption.BUNGEE)) {
|
||||
portalOptions.put(PortalOption.ALWAYS_ON, true);
|
||||
portalOptions.put(PortalOption.RANDOM, false);
|
||||
}
|
||||
return portalOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a portal given its name
|
||||
*
|
||||
* @param name <p>The name of the portal</p>
|
||||
* @param network <p>The network the portal is connected to</p>
|
||||
* @return <p>The portal with the given name or null</p>
|
||||
*/
|
||||
public static Portal getByName(String name, String network) {
|
||||
if (!lookupNamesNet.containsKey(network.toLowerCase())) {
|
||||
return null;
|
||||
}
|
||||
return lookupNamesNet.get(network.toLowerCase()).get(name.toLowerCase());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a portal given its entrance
|
||||
*
|
||||
* @param location <p>The location of the portal's entrance</p>
|
||||
* @return <p>The portal at the given location</p>
|
||||
*/
|
||||
public static Portal getByEntrance(Location location) {
|
||||
return lookupEntrances.get(new BlockLocation(location.getWorld(), location.getBlockX(), location.getBlockY(),
|
||||
location.getBlockZ()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a portal given its entrance
|
||||
*
|
||||
* @param block <p>The block at the portal's entrance</p>
|
||||
* @return <p>The portal at the given block's location</p>
|
||||
*/
|
||||
public static Portal getByEntrance(Block block) {
|
||||
return lookupEntrances.get(new BlockLocation(block));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a portal given a location adjacent to its entrance
|
||||
*
|
||||
* @param loc <p>A location adjacent to the portal's entrance</p>
|
||||
* @return <p>The portal adjacent to the given location</p>
|
||||
*/
|
||||
public static Portal getByAdjacentEntrance(Location loc) {
|
||||
int centerX = loc.getBlockX();
|
||||
int centerY = loc.getBlockY();
|
||||
int centerZ = loc.getBlockZ();
|
||||
World world = loc.getWorld();
|
||||
Portal portal = lookupEntrances.get(new BlockLocation(world, centerX, centerY, centerZ));
|
||||
if (portal != null) {
|
||||
return portal;
|
||||
}
|
||||
portal = lookupEntrances.get(new BlockLocation(world, centerX + 1, centerY, centerZ));
|
||||
if (portal != null) {
|
||||
return portal;
|
||||
}
|
||||
portal = lookupEntrances.get(new BlockLocation(world, centerX - 1, centerY, centerZ));
|
||||
if (portal != null) {
|
||||
return portal;
|
||||
}
|
||||
portal = lookupEntrances.get(new BlockLocation(world, centerX, centerY, centerZ + 1));
|
||||
if (portal != null) {
|
||||
return portal;
|
||||
}
|
||||
portal = lookupEntrances.get(new BlockLocation(world, centerX, centerY, centerZ - 1));
|
||||
if (portal != null) {
|
||||
return portal;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a portal given its control block (the block type used for the sign and button)
|
||||
*
|
||||
* @param block <p>The portal's control block</p>
|
||||
* @return <p>The gate with the given control block</p>
|
||||
*/
|
||||
public static Portal getByControl(Block block) {
|
||||
return lookupControls.get(new BlockLocation(block));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a portal given a block
|
||||
*
|
||||
* @param block <p>One of the loaded lookup blocks</p>
|
||||
* @return <p>The portal corresponding to the block</p>
|
||||
*/
|
||||
public static Portal getByBlock(Block block) {
|
||||
return lookupBlocks.get(new BlockLocation(block));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a bungee gate given its name
|
||||
*
|
||||
* @param name <p>The name of the bungee gate to get</p>
|
||||
* @return <p>A bungee gate</p>
|
||||
*/
|
||||
public static Portal getBungeeGate(String name) {
|
||||
return bungeePortals.get(name.toLowerCase());
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves all gates for the given world
|
||||
*
|
||||
* @param world <p>The world to save gates for</p>
|
||||
*/
|
||||
public static void saveAllGates(World world) {
|
||||
Stargate.managedWorlds.add(world.getName());
|
||||
String loc = Stargate.getSaveLocation() + "/" + world.getName() + ".db";
|
||||
|
||||
try {
|
||||
BufferedWriter bw = new BufferedWriter(new FileWriter(loc, false));
|
||||
|
||||
for (Portal portal : allPortals) {
|
||||
String wName = portal.getWorld().getName();
|
||||
if (!wName.equalsIgnoreCase(world.getName())) continue;
|
||||
StringBuilder builder = new StringBuilder();
|
||||
BlockLocation button = portal.getButton();
|
||||
|
||||
builder.append(portal.getName());
|
||||
builder.append(':');
|
||||
builder.append(portal.getId().toString());
|
||||
builder.append(':');
|
||||
builder.append((button != null) ? button.toString() : "");
|
||||
builder.append(':');
|
||||
builder.append(portal.getModX());
|
||||
builder.append(':');
|
||||
builder.append(portal.getModZ());
|
||||
builder.append(':');
|
||||
builder.append(portal.getRotX());
|
||||
builder.append(':');
|
||||
builder.append(portal.getTopLeft().toString());
|
||||
builder.append(':');
|
||||
builder.append(portal.getGate().getFilename());
|
||||
builder.append(':');
|
||||
builder.append(portal.isFixed() ? portal.getDestinationName() : "");
|
||||
builder.append(':');
|
||||
builder.append(portal.getNetwork());
|
||||
builder.append(':');
|
||||
UUID owner = portal.getOwnerUUID();
|
||||
if (owner != null) {
|
||||
builder.append(portal.getOwnerUUID().toString());
|
||||
} else {
|
||||
builder.append(portal.getOwnerName());
|
||||
}
|
||||
builder.append(':');
|
||||
builder.append(portal.isHidden());
|
||||
builder.append(':');
|
||||
builder.append(portal.isAlwaysOn());
|
||||
builder.append(':');
|
||||
builder.append(portal.isPrivate());
|
||||
builder.append(':');
|
||||
builder.append(portal.getWorld().getName());
|
||||
builder.append(':');
|
||||
builder.append(portal.isFree());
|
||||
builder.append(':');
|
||||
builder.append(portal.isBackwards());
|
||||
builder.append(':');
|
||||
builder.append(portal.isShown());
|
||||
builder.append(':');
|
||||
builder.append(portal.isNoNetwork());
|
||||
builder.append(':');
|
||||
builder.append(portal.isRandom());
|
||||
builder.append(':');
|
||||
builder.append(portal.isBungee());
|
||||
|
||||
bw.append(builder.toString());
|
||||
bw.newLine();
|
||||
}
|
||||
|
||||
bw.close();
|
||||
} catch (Exception e) {
|
||||
Stargate.log.log(Level.SEVERE, "Exception while writing stargates to " + loc + ": " + e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all loaded gates and gate data from all worlds
|
||||
*/
|
||||
public static void clearGates() {
|
||||
lookupBlocks.clear();
|
||||
lookupNamesNet.clear();
|
||||
lookupEntrances.clear();
|
||||
lookupControls.clear();
|
||||
allPortals.clear();
|
||||
allPortalsNet.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all gates loaded in a given world
|
||||
*
|
||||
* @param world <p>The world containing the portals to clear</p>
|
||||
*/
|
||||
public static void clearGates(World world) {
|
||||
//This is necessary
|
||||
List<Portal> portalsToRemove = new ArrayList<>();
|
||||
allPortals.forEach((portal) -> {
|
||||
if (portal.getWorld().equals(world)) {
|
||||
portalsToRemove.add(portal);
|
||||
}
|
||||
});
|
||||
|
||||
clearGates(portalsToRemove);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears a given list of portals from all relevant variables
|
||||
*
|
||||
* @param portalsToRemove <p>A list of portals to remove</p>
|
||||
*/
|
||||
private static void clearGates(List<Portal> portalsToRemove) {
|
||||
List<String> portalNames = new ArrayList<>();
|
||||
portalsToRemove.forEach((portal) -> portalNames.add(portal.getName()));
|
||||
lookupBlocks.keySet().removeIf((key) -> portalsToRemove.contains(lookupBlocks.get(key)));
|
||||
lookupNamesNet.keySet().forEach((network) -> lookupNamesNet.get(network).keySet().removeIf((key) ->
|
||||
portalsToRemove.contains(lookupNamesNet.get(network).get(key))));
|
||||
//Remove any networks with no portals
|
||||
lookupNamesNet.keySet().removeIf((key) -> lookupNamesNet.get(key).isEmpty());
|
||||
lookupEntrances.keySet().removeIf((key) -> portalsToRemove.contains(lookupEntrances.get(key)));
|
||||
lookupControls.keySet().removeIf((key) -> portalsToRemove.contains(lookupControls.get(key)));
|
||||
allPortals.removeIf(portalsToRemove::contains);
|
||||
allPortalsNet.keySet().forEach((network) -> allPortalsNet.get(network).removeIf(portalNames::contains));
|
||||
//Remove any networks with no portals
|
||||
allPortalsNet.keySet().removeIf((network) -> allPortalsNet.get(network).isEmpty());
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all gates for the given world
|
||||
*
|
||||
* @param world <p>The world to load gates for</p>
|
||||
* @return <p>True if gates could be loaded</p>
|
||||
*/
|
||||
public static boolean loadAllGates(World world) {
|
||||
String location = Stargate.getSaveLocation();
|
||||
|
||||
File database = new File(location, world.getName() + ".db");
|
||||
|
||||
if (database.exists()) {
|
||||
return loadGates(world, database);
|
||||
} else {
|
||||
Stargate.log.info(Stargate.getString("prefix") + "{" + world.getName() + "} No stargates for world ");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all the given gates
|
||||
*
|
||||
* @param world <p>The world to load gates for</p>
|
||||
* @param database <p>The database file containing the gates</p>
|
||||
* @return <p>True if the gates were loaded successfully</p>
|
||||
*/
|
||||
private static boolean loadGates(World world, File database) {
|
||||
int lineIndex = 0;
|
||||
try {
|
||||
Scanner scanner = new Scanner(database);
|
||||
while (scanner.hasNextLine()) {
|
||||
lineIndex++;
|
||||
String line = scanner.nextLine().trim();
|
||||
|
||||
//Ignore empty and comment lines
|
||||
if (line.startsWith("#") || line.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//Check if the min. required portal data is present
|
||||
String[] portalData = line.split(":");
|
||||
if (portalData.length < 8) {
|
||||
Stargate.log.info(Stargate.getString("prefix") + "Invalid line - " + lineIndex);
|
||||
continue;
|
||||
}
|
||||
|
||||
loadGate(portalData, world, lineIndex);
|
||||
}
|
||||
scanner.close();
|
||||
|
||||
// Open any always-on gates. Do this here as it should be more efficient than in the loop.
|
||||
TwoTuple<Integer, Integer> portalCounts = openAlwaysOpenGates();
|
||||
|
||||
Stargate.log.info(String.format("%s{%s} Loaded %d stargates with %d set as always-on",
|
||||
Stargate.getString("prefix"), world.getName(), portalCounts.getSecondValue(),
|
||||
portalCounts.getFirstValue()));
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
Stargate.log.log(Level.SEVERE, "Exception while reading stargates from " + database.getName() + ": " + lineIndex);
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads one gate from a data array
|
||||
*
|
||||
* @param portalData <p>The array describing the portal</p>
|
||||
* @param world <p>The world to create the portal in</p>
|
||||
* @param lineIndex <p>The line index to report in case the user needs to fix an error</p>
|
||||
*/
|
||||
private static void loadGate(String[] portalData, World world, int lineIndex) {
|
||||
//Load min. required portal data
|
||||
String name = portalData[0];
|
||||
BlockLocation sign = new BlockLocation(world, portalData[1]);
|
||||
BlockLocation button = (portalData[2].length() > 0) ? new BlockLocation(world, portalData[2]) : null;
|
||||
int modX = Integer.parseInt(portalData[3]);
|
||||
int modZ = Integer.parseInt(portalData[4]);
|
||||
float rotX = Float.parseFloat(portalData[5]);
|
||||
BlockLocation topLeft = new BlockLocation(world, portalData[6]);
|
||||
Gate gate = GateHandler.getGateByName(portalData[7]);
|
||||
if (gate == null) {
|
||||
Stargate.log.info(Stargate.getString("prefix") + "Gate layout on line " + lineIndex +
|
||||
" does not exist [" + portalData[7] + "]");
|
||||
return;
|
||||
}
|
||||
|
||||
//Load extra portal data
|
||||
String destination = (portalData.length > 8) ? portalData[8] : "";
|
||||
String network = (portalData.length > 9) ? portalData[9] : Stargate.getDefaultNetwork();
|
||||
if (network.isEmpty()) {
|
||||
network = Stargate.getDefaultNetwork();
|
||||
}
|
||||
String ownerString = (portalData.length > 10) ? portalData[10] : "";
|
||||
|
||||
// Attempt to get owner as UUID
|
||||
UUID ownerUUID = null;
|
||||
String ownerName;
|
||||
if (ownerString.length() > 16) {
|
||||
try {
|
||||
ownerUUID = UUID.fromString(ownerString);
|
||||
OfflinePlayer offlineOwner = Bukkit.getServer().getOfflinePlayer(ownerUUID);
|
||||
ownerName = offlineOwner.getName();
|
||||
} catch (IllegalArgumentException ex) {
|
||||
// neither name nor UUID, so keep it as-is
|
||||
ownerName = ownerString;
|
||||
Stargate.debug("loadAllGates", "Invalid stargate owner string: " + ownerString);
|
||||
}
|
||||
} else {
|
||||
ownerName = ownerString;
|
||||
}
|
||||
|
||||
//Creates the new portal
|
||||
Portal portal = new Portal(topLeft, modX, modZ, rotX, sign, button, destination, name, false,
|
||||
network, gate, ownerUUID, ownerName, getPortalOptions(portalData));
|
||||
|
||||
registerPortal(portal);
|
||||
portal.close(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all portal options stored in the portal data
|
||||
*
|
||||
* @param portalData <p>The string list containing all information about a portal</p>
|
||||
* @return <p>A map between portal options and booleans</p>
|
||||
*/
|
||||
private static Map<PortalOption, Boolean> getPortalOptions(String[] portalData) {
|
||||
Map<PortalOption, Boolean> portalOptions = new HashMap<>();
|
||||
for (PortalOption option : PortalOption.values()) {
|
||||
int saveIndex = option.getSaveIndex();
|
||||
portalOptions.put(option, portalData.length > saveIndex && Boolean.parseBoolean(portalData[saveIndex]));
|
||||
}
|
||||
return portalOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens all always open gates
|
||||
*
|
||||
* @return <p>A TwoTuple where the first value is the number of always open gates and the second value is the total number of gates</p>
|
||||
*/
|
||||
private static TwoTuple<Integer, Integer> openAlwaysOpenGates() {
|
||||
int portalCount = 0;
|
||||
int openCount = 0;
|
||||
for (Iterator<Portal> iterator = allPortals.iterator(); iterator.hasNext(); ) {
|
||||
Portal portal = iterator.next();
|
||||
if (portal == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Verify portal integrity/register portal
|
||||
if (!portal.wasVerified() && (!portal.isVerified() || !portal.checkIntegrity())) {
|
||||
destroyInvalidStarGate(portal);
|
||||
iterator.remove();
|
||||
continue;
|
||||
}
|
||||
portalCount++;
|
||||
|
||||
//Open the gate if it's set as always open or if it's a bungee gate
|
||||
if (portal.isFixed() && (Stargate.enableBungee && portal.isBungee() || portal.getDestination() != null &&
|
||||
portal.isAlwaysOn())) {
|
||||
portal.open(true);
|
||||
openCount++;
|
||||
}
|
||||
}
|
||||
return new TwoTuple<>(openCount, portalCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys a star gate which has failed its integrity test
|
||||
*
|
||||
* @param portal <p>The portal of the star gate</p>
|
||||
*/
|
||||
private static void destroyInvalidStarGate(Portal portal) {
|
||||
// DEBUG
|
||||
for (RelativeBlockVector control : portal.getGate().getLayout().getControls()) {
|
||||
if (!portal.getBlockAt(control).getBlock().getType().equals(portal.getGate().getControlBlock())) {
|
||||
Stargate.debug("loadAllGates", "Control Block Type == " + portal.getBlockAt(control).getBlock().getType().name());
|
||||
}
|
||||
}
|
||||
PortalHandler.unregisterPortal(portal, false);
|
||||
Stargate.log.info(Stargate.getString("prefix") + "Destroying stargate at " + portal.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes all star gate portals
|
||||
*/
|
||||
public static void closeAllGates() {
|
||||
Stargate.log.info("Closing all stargates.");
|
||||
for (Portal portal : allPortals) {
|
||||
if (portal != null) {
|
||||
portal.close(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the special characters |, : and # from a portal name
|
||||
*
|
||||
* @param input <p>The name to filter</p>
|
||||
* @return <p>The filtered name</p>
|
||||
*/
|
||||
public static String filterName(String input) {
|
||||
if (input == null) {
|
||||
return "";
|
||||
}
|
||||
return input.replaceAll("[|:#]", "").trim();
|
||||
}
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
package net.knarcraft.stargate.portal;
|
||||
|
||||
public enum PortalOption {
|
||||
|
||||
/**
|
||||
* This option allows a portal to be hidden from others
|
||||
*/
|
||||
HIDDEN('h', "stargate.option.hidden", 11),
|
||||
|
||||
/**
|
||||
* This option allows a portal that's always on and does not need to be activated or opened each time
|
||||
*/
|
||||
ALWAYS_ON('a', "stargate.option.alwayson", 12),
|
||||
|
||||
/**
|
||||
* This option allows a portal that's private to the stargate's owner
|
||||
*/
|
||||
PRIVATE('p', "stargate.option.private", 13),
|
||||
|
||||
/**
|
||||
* This option allows a portal that's free even if stargates usually are not
|
||||
*/
|
||||
FREE('f', "stargate.option.free", 15),
|
||||
|
||||
/**
|
||||
* This option allows a portal where players exit through the back of the portal
|
||||
*/
|
||||
BACKWARDS('b', "stargate.option.backwards", 16),
|
||||
|
||||
/**
|
||||
* This option shows the gate in the network list even if it's always on
|
||||
*/
|
||||
SHOW('s', "stargate.option.show", 17),
|
||||
|
||||
/**
|
||||
* This option hides the network name on the sign
|
||||
*/
|
||||
NO_NETWORK('n', "stargate.option.nonetwork", 18),
|
||||
|
||||
/**
|
||||
* This option allows a portal where players teleport to a random exit portal in the network
|
||||
*/
|
||||
RANDOM('r', "stargate.option.random", 19),
|
||||
|
||||
/**
|
||||
* This option allows a portal to teleport to another server connected through BungeeCord
|
||||
*/
|
||||
BUNGEE('u', "stargate.admin.bungee", 20);
|
||||
|
||||
private final char characterRepresentation;
|
||||
private final String permissionString;
|
||||
private final int saveIndex;
|
||||
|
||||
/**
|
||||
* Instantiates a new portal options
|
||||
*
|
||||
* @param characterRepresentation <p>The character representation used on the sign to allow this option</p>
|
||||
* @param permissionString <p>The permission necessary to use this option</p>
|
||||
*/
|
||||
PortalOption(final char characterRepresentation, String permissionString, int saveIndex) {
|
||||
this.characterRepresentation = characterRepresentation;
|
||||
this.permissionString = permissionString;
|
||||
this.saveIndex = saveIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the character representation used to enable this setting on the sign
|
||||
*
|
||||
* @return <p>The character representation of this option</p>
|
||||
*/
|
||||
public char getCharacterRepresentation() {
|
||||
return this.characterRepresentation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the permission necessary to use this option
|
||||
*
|
||||
* @return <p>The permission necessary for this option</p>
|
||||
*/
|
||||
public String getPermissionString() {
|
||||
return this.permissionString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of the save file this option is stored at
|
||||
*
|
||||
* @return <p>This option's save index</p>
|
||||
*/
|
||||
public int getSaveIndex() {
|
||||
return this.saveIndex;
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user