diff --git a/src/main/java/net/knarcraft/stargate/Stargate.java b/src/main/java/net/knarcraft/stargate/Stargate.java index b8f9dd9..2bc73e3 100644 --- a/src/main/java/net/knarcraft/stargate/Stargate.java +++ b/src/main/java/net/knarcraft/stargate/Stargate.java @@ -22,6 +22,7 @@ import net.knarcraft.stargate.thread.ChunkUnloadThread; import net.knarcraft.stargate.thread.StarGateThread; import net.knarcraft.stargate.utility.EconomyHandler; import net.knarcraft.stargate.utility.FileHelper; +import net.knarcraft.stargate.utility.PortalFileHelper; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Server; @@ -532,7 +533,7 @@ public class Stargate extends JavaPlugin { public void loadAllPortals() { for (World world : getServer().getWorlds()) { if (!managedWorlds.contains(world.getName())) { - PortalHandler.loadAllPortals(world); + PortalFileHelper.loadAllPortals(world); managedWorlds.add(world.getName()); } } diff --git a/src/main/java/net/knarcraft/stargate/listener/WorldEventListener.java b/src/main/java/net/knarcraft/stargate/listener/WorldEventListener.java index 0fa3ed9..6ee5ba8 100644 --- a/src/main/java/net/knarcraft/stargate/listener/WorldEventListener.java +++ b/src/main/java/net/knarcraft/stargate/listener/WorldEventListener.java @@ -1,8 +1,8 @@ package net.knarcraft.stargate.listener; import net.knarcraft.stargate.Stargate; -import net.knarcraft.stargate.portal.PortalHandler; import net.knarcraft.stargate.portal.PortalRegistry; +import net.knarcraft.stargate.utility.PortalFileHelper; import org.bukkit.World; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; @@ -23,7 +23,7 @@ public class WorldEventListener implements Listener { @EventHandler public void onWorldLoad(WorldLoadEvent event) { if (!Stargate.managedWorlds.contains(event.getWorld().getName()) && - PortalHandler.loadAllPortals(event.getWorld())) { + PortalFileHelper.loadAllPortals(event.getWorld())) { Stargate.managedWorlds.add(event.getWorld().getName()); } } diff --git a/src/main/java/net/knarcraft/stargate/portal/PortalCreator.java b/src/main/java/net/knarcraft/stargate/portal/PortalCreator.java index 8f90732..b044a37 100644 --- a/src/main/java/net/knarcraft/stargate/portal/PortalCreator.java +++ b/src/main/java/net/knarcraft/stargate/portal/PortalCreator.java @@ -8,6 +8,7 @@ import net.knarcraft.stargate.utility.DirectionHelper; import net.knarcraft.stargate.utility.EconomyHandler; import net.knarcraft.stargate.utility.EconomyHelper; import net.knarcraft.stargate.utility.PermissionHelper; +import net.knarcraft.stargate.utility.PortalFileHelper; import org.bukkit.Bukkit; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; @@ -209,7 +210,7 @@ public class PortalCreator { PortalHandler.updatePortalsPointingAtNewPortal(portal); } - PortalHandler.saveAllPortals(portal.getWorld()); + PortalFileHelper.saveAllPortals(portal.getWorld()); return portal; } diff --git a/src/main/java/net/knarcraft/stargate/portal/PortalHandler.java b/src/main/java/net/knarcraft/stargate/portal/PortalHandler.java index f4a9708..80cb5f4 100644 --- a/src/main/java/net/knarcraft/stargate/portal/PortalHandler.java +++ b/src/main/java/net/knarcraft/stargate/portal/PortalHandler.java @@ -5,25 +5,15 @@ import net.knarcraft.stargate.container.BlockLocation; import net.knarcraft.stargate.container.RelativeBlockVector; import net.knarcraft.stargate.container.TwoTuple; import net.knarcraft.stargate.utility.PermissionHelper; -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.Sign; import org.bukkit.entity.Player; -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; /** * Keeps track of all loaded portals, and handles portal creation @@ -126,7 +116,7 @@ public class PortalHandler { * * @param portal

The portal to register

*/ - static void registerPortal(Portal portal) { + public static void registerPortal(Portal portal) { PortalRegistry.registerPortal(portal); } @@ -391,198 +381,13 @@ public class PortalHandler { return PortalRegistry.getBungeePortals().get(name.toLowerCase()); } - /** - * Saves all portals for the given world - * - * @param world

The world to save portals for

- */ - public static void saveAllPortals(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 : PortalRegistry.getAllPortals()) { - String wName = portal.getWorld().getName(); - if (!wName.equalsIgnoreCase(world.getName())) continue; - StringBuilder builder = new StringBuilder(); - BlockLocation button = portal.getStructure().getButton(); - - builder.append(portal.getName()).append(':'); - builder.append(portal.getSignLocation().toString()).append(':'); - builder.append((button != null) ? button.toString() : "").append(':'); - builder.append(0).append(':'); - builder.append(0).append(':'); - builder.append(portal.getYaw()).append(':'); - builder.append(portal.getTopLeft().toString()).append(':'); - builder.append(portal.getGate().getFilename()).append(':'); - builder.append(portal.getOptions().isFixed() ? portal.getDestinationName() : "").append(':'); - builder.append(portal.getNetwork()).append(':'); - UUID owner = portal.getOwnerUUID(); - if (owner != null) { - builder.append(portal.getOwnerUUID().toString()); - } else { - builder.append(portal.getOwnerName()); - } - builder.append(':'); - builder.append(portal.getOptions().isHidden()).append(':'); - builder.append(portal.getOptions().isAlwaysOn()).append(':'); - builder.append(portal.getOptions().isPrivate()).append(':'); - builder.append(portal.getWorld().getName()).append(':'); - builder.append(portal.getOptions().isFree()).append(':'); - builder.append(portal.getOptions().isBackwards()).append(':'); - builder.append(portal.getOptions().isShown()).append(':'); - builder.append(portal.getOptions().isNoNetwork()).append(':'); - builder.append(portal.getOptions().isRandom()).append(':'); - builder.append(portal.getOptions().isBungee()); - - bw.append(builder.toString()); - bw.newLine(); - } - - bw.close(); - } catch (Exception e) { - Stargate.logger.log(Level.SEVERE, "Exception while writing stargates to " + loc + ": " + e); - } - } - - /** - * Loads all portals for the given world - * - * @param world

The world to load portals for

- * @return

True if portals could be loaded

- */ - public static boolean loadAllPortals(World world) { - String location = Stargate.getSaveLocation(); - - File database = new File(location, world.getName() + ".db"); - - if (database.exists()) { - return loadPortals(world, database); - } else { - Stargate.logger.info(Stargate.getString("prefix") + "{" + world.getName() + "} No stargates for world "); - } - return false; - } - - /** - * Loads all the given portals - * - * @param world

The world to load portals for

- * @param database

The database file containing the portals

- * @return

True if the portals were loaded successfully

- */ - private static boolean loadPortals(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.logger.info(Stargate.getString("prefix") + "Invalid line - " + lineIndex); - continue; - } - - loadPortal(portalData, world, lineIndex); - } - scanner.close(); - - // Open any always-on gates. Do this here as it should be more efficient than in the loop. - TwoTuple portalCounts = openAlwaysOpenPortals(); - - Stargate.logger.info(String.format("%s{%s} Loaded %d stargates with %d set as always-on", - Stargate.getString("prefix"), world.getName(), portalCounts.getSecondValue(), - portalCounts.getFirstValue())); - - //Re-draw the signs in case a bug in the config prevented the portal from loading and has been fixed since - for (Portal portal : PortalRegistry.getAllPortals()) { - portal.drawSign(); - } - return true; - } catch (Exception e) { - Stargate.logger.log(Level.SEVERE, "Exception while reading stargates from " + database.getName() + ": " + lineIndex); - e.printStackTrace(); - } - return false; - } - - /** - * Loads one portal from a data array - * - * @param portalData

The array describing the portal

- * @param world

The world to create the portal in

- * @param lineIndex

The line index to report in case the user needs to fix an error

- */ - private static void loadPortal(String[] portalData, World world, int lineIndex) { - //Load min. required portal data - String name = portalData[0]; - PortalLocation portalLocation = new PortalLocation(); - portalLocation.setSignLocation(new BlockLocation(world, portalData[1])); - BlockLocation button = (portalData[2].length() > 0) ? new BlockLocation(world, portalData[2]) : null; - portalLocation.setYaw(Float.parseFloat(portalData[5])); - portalLocation.setTopLeft(new BlockLocation(world, portalData[6])); - Gate gate = GateHandler.getGateByName(portalData[7]); - if (gate == null) { - //Mark the sign as invalid to reduce some player confusion - Sign sign = (Sign) portalLocation.getSignLocation().getBlock().getState(); - Stargate.setLine(sign, 3, Stargate.getString("signInvalidGate")); - sign.update(); - - Stargate.logger.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("loadAllPortals", "Invalid stargate owner string: " + ownerString); - } - } else { - ownerName = ownerString; - } - - //Creates the new portal - Portal portal = new Portal(portalLocation, button, destination, name, - network, gate, ownerUUID, ownerName, getPortalOptions(portalData)); - - registerPortal(portal); - portal.getPortalOpener().closePortal(true); - } - /** * Gets all portal options stored in the portal data * * @param portalData

The string list containing all information about a portal

* @return

A map between portal options and booleans

*/ - private static Map getPortalOptions(String[] portalData) { + public static Map getPortalOptions(String[] portalData) { Map portalOptions = new HashMap<>(); for (PortalOption option : PortalOption.values()) { int saveIndex = option.getSaveIndex(); @@ -596,7 +401,7 @@ public class PortalHandler { * * @return

A TwoTuple where the first value is the number of always open portals and the second value is the total number of portals

*/ - private static TwoTuple openAlwaysOpenPortals() { + public static TwoTuple openAlwaysOpenPortals() { int portalCount = 0; int openCount = 0; for (Iterator iterator = PortalRegistry.getAllPortals().iterator(); iterator.hasNext(); ) { diff --git a/src/main/java/net/knarcraft/stargate/portal/PortalRegistry.java b/src/main/java/net/knarcraft/stargate/portal/PortalRegistry.java index 165e680..a12b69e 100644 --- a/src/main/java/net/knarcraft/stargate/portal/PortalRegistry.java +++ b/src/main/java/net/knarcraft/stargate/portal/PortalRegistry.java @@ -2,6 +2,7 @@ package net.knarcraft.stargate.portal; import net.knarcraft.stargate.Stargate; import net.knarcraft.stargate.container.BlockLocation; +import net.knarcraft.stargate.utility.PortalFileHelper; import org.bukkit.World; import org.bukkit.block.Sign; import org.bukkit.block.data.type.WallSign; @@ -228,7 +229,7 @@ public class PortalRegistry { sign.update(); } - PortalHandler.saveAllPortals(portal.getWorld()); + PortalFileHelper.saveAllPortals(portal.getWorld()); } /** diff --git a/src/main/java/net/knarcraft/stargate/utility/PortalFileHelper.java b/src/main/java/net/knarcraft/stargate/utility/PortalFileHelper.java new file mode 100644 index 0000000..f38fe41 --- /dev/null +++ b/src/main/java/net/knarcraft/stargate/utility/PortalFileHelper.java @@ -0,0 +1,315 @@ +package net.knarcraft.stargate.utility; + +import net.knarcraft.stargate.Stargate; +import net.knarcraft.stargate.container.BlockLocation; +import net.knarcraft.stargate.container.TwoTuple; +import net.knarcraft.stargate.portal.Gate; +import net.knarcraft.stargate.portal.GateHandler; +import net.knarcraft.stargate.portal.Portal; +import net.knarcraft.stargate.portal.PortalHandler; +import net.knarcraft.stargate.portal.PortalLocation; +import net.knarcraft.stargate.portal.PortalOptions; +import net.knarcraft.stargate.portal.PortalRegistry; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.World; +import org.bukkit.block.Sign; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Scanner; +import java.util.UUID; +import java.util.logging.Level; + +/** + * Helper class for saving and loading portal save files + */ +public final class PortalFileHelper { + + private PortalFileHelper() { + + } + + /** + * Saves all portals for the given world + * + * @param world

The world to save portals for

+ */ + public static void saveAllPortals(World world) { + Stargate.managedWorlds.add(world.getName()); + String saveFileLocation = Stargate.getSaveLocation() + "/" + world.getName() + ".db"; + + try { + BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(saveFileLocation, false)); + + for (Portal portal : PortalRegistry.getAllPortals()) { + //Skip portals in other worlds + String worldName = portal.getWorld().getName(); + if (!worldName.equalsIgnoreCase(world.getName())) { + continue; + } + //Save the portal + savePortal(bufferedWriter, portal); + } + + bufferedWriter.close(); + } catch (Exception e) { + Stargate.logger.log(Level.SEVERE, "Exception while writing stargates to " + saveFileLocation + ": " + e); + } + } + + /** + * Saves one portal + * + * @param bufferedWriter

The buffered writer to write to

+ * @param portal

The portal to save

+ * @throws IOException

If unable to write to the buffered writer

+ */ + private static void savePortal(BufferedWriter bufferedWriter, Portal portal) throws IOException { + StringBuilder builder = new StringBuilder(); + BlockLocation button = portal.getStructure().getButton(); + + //WARNING: Because of the primitive save format, any change in order will break everything! + builder.append(portal.getName()).append(':'); + builder.append(portal.getSignLocation().toString()).append(':'); + builder.append((button != null) ? button.toString() : "").append(':'); + + //Add removes config values to keep indices consistent + builder.append(0).append(':'); + builder.append(0).append(':'); + + builder.append(portal.getYaw()).append(':'); + builder.append(portal.getTopLeft().toString()).append(':'); + builder.append(portal.getGate().getFilename()).append(':'); + + //Only save the destination name if the gate is fixed as it doesn't matter otherwise + builder.append(portal.getOptions().isFixed() ? portal.getDestinationName() : "").append(':'); + + builder.append(portal.getNetwork()).append(':'); + + //Name is saved as a fallback if the UUID is unavailable + UUID owner = portal.getOwnerUUID(); + if (owner != null) { + builder.append(portal.getOwnerUUID().toString()); + } else { + builder.append(portal.getOwnerName()); + } + + //Save all the portal options + savePortalOptions(portal, builder); + + bufferedWriter.append(builder.toString()); + bufferedWriter.newLine(); + } + + /** + * Saves all portal options for the given portal + * + * @param portal

The portal to save

+ * @param builder

The string builder to append to

+ */ + private static void savePortalOptions(Portal portal, StringBuilder builder) { + PortalOptions options = portal.getOptions(); + builder.append(':'); + builder.append(options.isHidden()).append(':'); + builder.append(options.isAlwaysOn()).append(':'); + builder.append(options.isPrivate()).append(':'); + builder.append(portal.getWorld().getName()).append(':'); + builder.append(options.isFree()).append(':'); + builder.append(options.isBackwards()).append(':'); + builder.append(options.isShown()).append(':'); + builder.append(options.isNoNetwork()).append(':'); + builder.append(options.isRandom()).append(':'); + builder.append(options.isBungee()); + } + + /** + * Loads all portals for the given world + * + * @param world

The world to load portals for

+ * @return

True if portals could be loaded

+ */ + public static boolean loadAllPortals(World world) { + String location = Stargate.getSaveLocation(); + + File database = new File(location, world.getName() + ".db"); + + if (database.exists()) { + return loadPortals(world, database); + } else { + Stargate.logger.info(Stargate.getString("prefix") + "{" + world.getName() + + "} No stargates for world "); + } + return false; + } + + /** + * Loads all the given portals + * + * @param world

The world to load portals for

+ * @param database

The database file containing the portals

+ * @return

True if the portals were loaded successfully

+ */ + private static boolean loadPortals(World world, File database) { + int lineIndex = 0; + try { + Scanner scanner = new Scanner(database); + while (scanner.hasNextLine()) { + //Read the line and do whatever needs to be done + readPortalLine(scanner, ++lineIndex, world); + } + scanner.close(); + + //Do necessary tasks after all portals have loaded + doPostLoadTasks(world); + return true; + } catch (Exception e) { + Stargate.logger.log(Level.SEVERE, "Exception while reading stargates from " + database.getName() + + ": " + lineIndex); + e.printStackTrace(); + } + return false; + } + + /** + * Reads one file line containing information about one portal + * + * @param scanner

The scanner to read

+ * @param lineIndex

The index of the read line

+ * @param world

The world for which portals are currently being read

+ */ + private static void readPortalLine(Scanner scanner, int lineIndex, World world) { + String line = scanner.nextLine().trim(); + + //Ignore empty and comment lines + if (line.startsWith("#") || line.isEmpty()) { + return; + } + + //Check if the min. required portal data is present + String[] portalData = line.split(":"); + if (portalData.length < 8) { + Stargate.logger.info(Stargate.getString("prefix") + "Invalid line - " + lineIndex); + return; + } + + //Load the portal defined in the current line + loadPortal(portalData, world, lineIndex); + } + + /** + * Performs tasks which must be run after portals have loaded + * + *

This will open always on portals, print info about loaded stargates and re-draw portal signs for loaded + * portals.

+ * + * @param world

The world portals have been loaded for

+ */ + private static void doPostLoadTasks(World world) { + //Open any always-on portals. Do this here as it should be more efficient than in the loop. + TwoTuple portalCounts = PortalHandler.openAlwaysOpenPortals(); + + //Print info about loaded stargates so that admins can see if all stargates loaded + Stargate.logger.info(String.format("%s{%s} Loaded %d stargates with %d set as always-on", + Stargate.getString("prefix"), world.getName(), portalCounts.getSecondValue(), + portalCounts.getFirstValue())); + + //Re-draw the signs in case a bug in the config prevented the portal from loading and has been fixed since + for (Portal portal : PortalRegistry.getAllPortals()) { + String worldName = portal.getWorld().getName(); + if (!worldName.equalsIgnoreCase(world.getName())) { + continue; + } + portal.drawSign(); + } + } + + /** + * Loads one portal from a data array + * + * @param portalData

The array describing the portal

+ * @param world

The world to create the portal in

+ * @param lineIndex

The line index to report in case the user needs to fix an error

+ */ + private static void loadPortal(String[] portalData, World world, int lineIndex) { + //Load min. required portal data + String name = portalData[0]; + BlockLocation button = (portalData[2].length() > 0) ? new BlockLocation(world, portalData[2]) : null; + + //Load the portal's location + PortalLocation portalLocation = new PortalLocation(); + portalLocation.setSignLocation(new BlockLocation(world, portalData[1])); + portalLocation.setYaw(Float.parseFloat(portalData[5])); + portalLocation.setTopLeft(new BlockLocation(world, portalData[6])); + + //Check if the portal's gate type exists and is loaded + Gate gate = GateHandler.getGateByName(portalData[7]); + if (gate == null) { + //Mark the sign as invalid to reduce some player confusion + markPortalWithInvalidGate(portalLocation, portalData[7], lineIndex); + return; + } + + //Load extra portal data + String destination = (portalData.length > 8) ? portalData[8] : ""; + String network = (portalData.length > 9 && !portalData[9].isEmpty()) ? portalData[9] : Stargate.getDefaultNetwork(); + String ownerString = (portalData.length > 10) ? portalData[10] : ""; + + //Try to get owner as UUID + TwoTuple nameAndUUID = getPortalOwnerUUIDAndName(ownerString); + + //Create the new portal + Portal portal = new Portal(portalLocation, button, destination, name, network, gate, + nameAndUUID.getFirstValue(), nameAndUUID.getSecondValue(), PortalHandler.getPortalOptions(portalData)); + + //Register the portal, and close it in case it wasn't properly closed when the server stopped + PortalHandler.registerPortal(portal); + portal.getPortalOpener().closePortal(true); + } + + /** + * Gets the portal UUID and name from the saved owner string + * + * @param ownerString

The saved owner string. Should be a UUID, or a player name if legacy

+ * @return

A two-tuple containing the UUID and owner name. The UUID might be null if the ownerString was not a UUID

+ */ + private static TwoTuple getPortalOwnerUUIDAndName(String ownerString) { + UUID ownerUUID = null; + String ownerName; + if (ownerString.length() > 16) { + //If more than 16 characters, the string cannot be a username, so it's probably a UUID + try { + ownerUUID = UUID.fromString(ownerString); + OfflinePlayer offlineOwner = Bukkit.getServer().getOfflinePlayer(ownerUUID); + ownerName = offlineOwner.getName(); + } catch (IllegalArgumentException ex) { + //Invalid as UUID and username, so just keep it as owner name and hope the server owner fixes it + ownerName = ownerString; + Stargate.debug("loadAllPortals", "Invalid stargate owner string: " + ownerString); + } + } else { + //Old username from the pre-UUID times. Just keep it as the owner name + ownerName = ownerString; + } + return new TwoTuple<>(ownerUUID, ownerName); + } + + /** + * Marks a portal with an invalid gate by changing its sign and writing to the console + * + * @param portalLocation

The location of the portal with an invalid gate

+ * @param gateName

The name of the invalid gate type

+ * @param lineIndex

The index of the line the invalid portal was found at

+ */ + private static void markPortalWithInvalidGate(PortalLocation portalLocation, String gateName, int lineIndex) { + Sign sign = (Sign) portalLocation.getSignLocation().getBlock().getState(); + Stargate.setLine(sign, 3, Stargate.getString("signInvalidGate")); + sign.update(); + + Stargate.logger.info(Stargate.getString("prefix") + "Gate layout on line " + lineIndex + + " does not exist [" + gateName + "]"); + } + +}