diff --git a/src/main/java/fr/xephi/authme/AuthMe.java b/src/main/java/fr/xephi/authme/AuthMe.java index c4db43463..740e0fa64 100644 --- a/src/main/java/fr/xephi/authme/AuthMe.java +++ b/src/main/java/fr/xephi/authme/AuthMe.java @@ -21,7 +21,6 @@ import fr.xephi.authme.datasource.FlatFile; import fr.xephi.authme.datasource.MySQL; import fr.xephi.authme.datasource.SQLite; import fr.xephi.authme.hooks.BungeeCordMessage; -import fr.xephi.authme.hooks.EssSpawn; import fr.xephi.authme.hooks.PluginHooks; import fr.xephi.authme.listener.AuthMeBlockListener; import fr.xephi.authme.listener.AuthMeEntityListener; @@ -45,8 +44,7 @@ import fr.xephi.authme.security.PasswordSecurity; import fr.xephi.authme.security.crypts.SHA256; import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.Settings; -import fr.xephi.authme.settings.SettingsMigrationService; -import fr.xephi.authme.settings.Spawn; +import fr.xephi.authme.settings.SpawnLoader; import fr.xephi.authme.settings.properties.DatabaseSettings; import fr.xephi.authme.settings.properties.EmailSettings; import fr.xephi.authme.settings.properties.HooksSettings; @@ -55,6 +53,7 @@ import fr.xephi.authme.settings.properties.PurgeSettings; import fr.xephi.authme.settings.properties.RestrictionSettings; import fr.xephi.authme.settings.properties.SecuritySettings; import fr.xephi.authme.util.CollectionUtils; +import fr.xephi.authme.util.FileUtils; import fr.xephi.authme.util.GeoLiteAPI; import fr.xephi.authme.util.MigrationService; import fr.xephi.authme.util.StringUtils; @@ -116,7 +115,6 @@ public class AuthMe extends JavaPlugin { public NewAPI api; public SendMailSSL mail; public DataManager dataManager; - public Location essentialsSpawn; /* * Plugin Hooks * TODO: Move into modules @@ -134,6 +132,7 @@ public class AuthMe extends JavaPlugin { private DataSource database; private IpAddressManager ipAddressManager; private PluginHooks pluginHooks; + private SpawnLoader spawnLoader; /** * Get the plugin's instance. @@ -249,10 +248,13 @@ public class AuthMe extends JavaPlugin { passwordSecurity = new PasswordSecurity(getDataSource(), newSettings, Bukkit.getPluginManager()); ipAddressManager = new IpAddressManager(newSettings); + // Initialize spawn loader + spawnLoader = new SpawnLoader(getDataFolder(), newSettings, pluginHooks); + // Set up the permissions manager and command handler permsMan = initializePermissionsManager(); commandHandler = initializeCommandHandler(permsMan, messages, passwordSecurity, newSettings, ipAddressManager, - pluginHooks); + pluginHooks, spawnLoader); // Set up Metrics MetricsStarter.setupMetrics(plugin, newSettings); @@ -269,10 +271,6 @@ public class AuthMe extends JavaPlugin { // Set up the mail API setupMailApi(); - // Hooks - // Check Essentials - checkEssentialsSpawn(); - // Check if the ProtocolLib is available. If so we could listen for // inventory protection checkProtocolLib(); @@ -293,7 +291,7 @@ public class AuthMe extends JavaPlugin { // Set up the management ProcessService processService = new ProcessService(newSettings, messages, this, ipAddressManager, - passwordSecurity, pluginHooks); + passwordSecurity, pluginHooks, spawnLoader); management = new Management(this, processService, database, PlayerCache.getInstance()); // Set up the BungeeCord hook @@ -303,7 +301,7 @@ public class AuthMe extends JavaPlugin { reloadSupportHook(); // Register event listeners - registerEventListeners(); + registerEventListeners(messages, pluginHooks, spawnLoader); // Purge on start if enabled autoPurge(); @@ -337,7 +335,7 @@ public class AuthMe extends JavaPlugin { database.reload(); messages.reload(newSettings.getMessagesFile()); passwordSecurity.reload(newSettings); - Spawn.reload(); + spawnLoader.initialize(newSettings); } /** @@ -369,7 +367,7 @@ public class AuthMe extends JavaPlugin { /** * Register all event listeners. */ - private void registerEventListeners() { + private void registerEventListeners(Messages messages, PluginHooks pluginHooks, SpawnLoader spawnLoader) { // Get the plugin manager instance PluginManager pluginManager = server.getPluginManager(); @@ -377,7 +375,7 @@ public class AuthMe extends JavaPlugin { pluginManager.registerEvents(new AuthMePlayerListener(this), this); pluginManager.registerEvents(new AuthMeBlockListener(), this); pluginManager.registerEvents(new AuthMeEntityListener(), this); - pluginManager.registerEvents(new AuthMeServerListener(this, messages, pluginHooks), this); + pluginManager.registerEvents(new AuthMeServerListener(this, messages, pluginHooks, spawnLoader), this); // Try to register 1.6 player listeners try { @@ -425,12 +423,13 @@ public class AuthMe extends JavaPlugin { private CommandHandler initializeCommandHandler(PermissionsManager permissionsManager, Messages messages, PasswordSecurity passwordSecurity, NewSetting settings, - IpAddressManager ipAddressManager, PluginHooks pluginHooks) { + IpAddressManager ipAddressManager, PluginHooks pluginHooks, + SpawnLoader spawnLoader) { HelpProvider helpProvider = new HelpProvider(permissionsManager, settings.getProperty(HELP_HEADER)); Set baseCommands = CommandInitializer.buildCommands(); CommandMapper mapper = new CommandMapper(baseCommands, permissionsManager); CommandService commandService = new CommandService(this, mapper, helpProvider, messages, passwordSecurity, - permissionsManager, settings, ipAddressManager, pluginHooks); + permissionsManager, settings, ipAddressManager, pluginHooks, spawnLoader); return new CommandHandler(commandService); } @@ -465,7 +464,7 @@ public class AuthMe extends JavaPlugin { private NewSetting createNewSetting() { File configFile = new File(getDataFolder(), "config.yml"); - return SettingsMigrationService.copyFileFromResource(configFile, "config.yml") + return FileUtils.copyFileFromResource(configFile, "config.yml") ? new NewSetting(configFile, getDataFolder()) : null; } @@ -633,21 +632,6 @@ public class AuthMe extends JavaPlugin { }); } - // Get the Essentials plugin - public void checkEssentialsSpawn() { - if (server.getPluginManager().isPluginEnabled("EssentialsSpawn")) { - try { - essentialsSpawn = new EssSpawn().getLocation(); - ConsoleLogger.info("Hooked correctly with EssentialsSpawn"); - } catch (Exception e) { - essentialsSpawn = null; - ConsoleLogger.showError("Can't read the /plugins/Essentials/spawn.yml file!"); - } - } else { - essentialsSpawn = null; - } - } - // Check the presence of the ProtocolLib plugin public void checkProtocolLib() { if (!server.getPluginManager().isPluginEnabled("ProtocolLib")) { @@ -748,7 +732,7 @@ public class AuthMe extends JavaPlugin { // Return the spawn location of a player @Deprecated public Location getSpawnLocation(Player player) { - return Spawn.getInstance().getSpawnLocation(player); + return spawnLoader.getSpawnLocation(player); } private void scheduleRecallEmailTask() { diff --git a/src/main/java/fr/xephi/authme/command/CommandInitializer.java b/src/main/java/fr/xephi/authme/command/CommandInitializer.java index ca89c86ab..b4150ade6 100644 --- a/src/main/java/fr/xephi/authme/command/CommandInitializer.java +++ b/src/main/java/fr/xephi/authme/command/CommandInitializer.java @@ -159,7 +159,7 @@ public final class CommandInitializer { .labels("getip", "ip") .description("Get player's IP") .detailedDescription("Get the IP address of the specified online player.") - .withArgument("player", "Player Name", false) + .withArgument("player", "Player name", false) .permissions(OP_ONLY, AdminPermission.GET_IP) .executableCommand(new GetIpCommand()) .build(); @@ -170,7 +170,6 @@ public final class CommandInitializer { .labels("spawn", "home") .description("Teleport to spawn") .detailedDescription("Teleport to the spawn.") - .withArgument("player", "Player Name", false) .permissions(OP_ONLY, AdminPermission.SPAWN) .executableCommand(new SpawnCommand()) .build(); diff --git a/src/main/java/fr/xephi/authme/command/CommandService.java b/src/main/java/fr/xephi/authme/command/CommandService.java index 745adea3c..a43a08028 100644 --- a/src/main/java/fr/xephi/authme/command/CommandService.java +++ b/src/main/java/fr/xephi/authme/command/CommandService.java @@ -12,6 +12,7 @@ import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.process.Management; import fr.xephi.authme.security.PasswordSecurity; import fr.xephi.authme.settings.NewSetting; +import fr.xephi.authme.settings.SpawnLoader; import fr.xephi.authme.settings.domain.Property; import org.bukkit.command.CommandSender; @@ -32,6 +33,7 @@ public class CommandService { private final NewSetting settings; private final IpAddressManager ipAddressManager; private final PluginHooks pluginHooks; + private final SpawnLoader spawnLoader; /** * Constructor. @@ -47,7 +49,7 @@ public class CommandService { */ public CommandService(AuthMe authMe, CommandMapper commandMapper, HelpProvider helpProvider, Messages messages, PasswordSecurity passwordSecurity, PermissionsManager permissionsManager, NewSetting settings, - IpAddressManager ipAddressManager, PluginHooks pluginHooks) { + IpAddressManager ipAddressManager, PluginHooks pluginHooks, SpawnLoader spawnLoader) { this.authMe = authMe; this.messages = messages; this.helpProvider = helpProvider; @@ -57,6 +59,7 @@ public class CommandService { this.settings = settings; this.ipAddressManager = ipAddressManager; this.pluginHooks = pluginHooks; + this.spawnLoader = spawnLoader; } /** @@ -202,4 +205,8 @@ public class CommandService { return pluginHooks; } + public SpawnLoader getSpawnLoader() { + return spawnLoader; + } + } diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/FirstSpawnCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/FirstSpawnCommand.java index 3370edf61..da7b45e45 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/FirstSpawnCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/FirstSpawnCommand.java @@ -1,9 +1,7 @@ package fr.xephi.authme.command.executable.authme; -import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.PlayerCommand; -import fr.xephi.authme.settings.Spawn; import org.bukkit.entity.Player; import java.util.List; @@ -12,16 +10,10 @@ public class FirstSpawnCommand extends PlayerCommand { @Override public void runCommand(Player player, List arguments, CommandService commandService) { - // Make sure the command executor is a player - try { - if (Spawn.getInstance().getFirstSpawn() != null) { - player.teleport(Spawn.getInstance().getFirstSpawn()); - } else { - player.sendMessage("[AuthMe] First spawn has failed, please try to define the first spawn"); - } - } catch (NullPointerException ex) { - // TODO ljacqu 20151119: Catching NullPointerException is never a good idea. Find what can cause one instead - ConsoleLogger.showError(ex.getMessage()); + if (commandService.getSpawnLoader().getFirstSpawn() != null) { + player.teleport(commandService.getSpawnLoader().getFirstSpawn()); + } else { + player.sendMessage("[AuthMe] First spawn has failed, please try to define the first spawn"); } } } diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/SetFirstSpawnCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/SetFirstSpawnCommand.java index 31942bc7c..a3cc7d454 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/SetFirstSpawnCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/SetFirstSpawnCommand.java @@ -1,9 +1,7 @@ package fr.xephi.authme.command.executable.authme; -import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.PlayerCommand; -import fr.xephi.authme.settings.Spawn; import org.bukkit.entity.Player; import java.util.List; @@ -12,14 +10,10 @@ public class SetFirstSpawnCommand extends PlayerCommand { @Override public void runCommand(Player player, List arguments, CommandService commandService) { - try { - if (Spawn.getInstance().setFirstSpawn(player.getLocation())) { - player.sendMessage("[AuthMe] Correctly defined new first spawn point"); - } else { - player.sendMessage("[AuthMe] SetFirstSpawn has failed, please retry"); - } - } catch (NullPointerException ex) { - ConsoleLogger.showError(ex.getMessage()); + if (commandService.getSpawnLoader().setFirstSpawn(player.getLocation())) { + player.sendMessage("[AuthMe] Correctly defined new first spawn point"); + } else { + player.sendMessage("[AuthMe] SetFirstSpawn has failed, please retry"); } } } diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/SetSpawnCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/SetSpawnCommand.java index f62433007..3201ea4ca 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/SetSpawnCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/SetSpawnCommand.java @@ -1,9 +1,7 @@ package fr.xephi.authme.command.executable.authme; -import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.PlayerCommand; -import fr.xephi.authme.settings.Spawn; import org.bukkit.entity.Player; import java.util.List; @@ -12,14 +10,10 @@ public class SetSpawnCommand extends PlayerCommand { @Override public void runCommand(Player player, List arguments, CommandService commandService) { - try { - if (Spawn.getInstance().setSpawn(player.getLocation())) { - player.sendMessage("[AuthMe] Correctly defined new spawn point"); - } else { - player.sendMessage("[AuthMe] SetSpawn has failed, please retry"); - } - } catch (NullPointerException ex) { - ConsoleLogger.showError(ex.getMessage()); + if (commandService.getSpawnLoader().setSpawn(player.getLocation())) { + player.sendMessage("[AuthMe] Correctly defined new spawn point"); + } else { + player.sendMessage("[AuthMe] SetSpawn has failed, please retry"); } } } diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/SpawnCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/SpawnCommand.java index ddcc78157..04f8e563d 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/SpawnCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/SpawnCommand.java @@ -1,9 +1,7 @@ package fr.xephi.authme.command.executable.authme; -import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.PlayerCommand; -import fr.xephi.authme.settings.Spawn; import org.bukkit.entity.Player; import java.util.List; @@ -12,14 +10,10 @@ public class SpawnCommand extends PlayerCommand { @Override public void runCommand(Player player, List arguments, CommandService commandService) { - try { - if (Spawn.getInstance().getSpawn() != null) { - player.teleport(Spawn.getInstance().getSpawn()); - } else { - player.sendMessage("[AuthMe] Spawn has failed, please try to define the spawn"); - } - } catch (NullPointerException ex) { - ConsoleLogger.showError(ex.getMessage()); + if (commandService.getSpawnLoader().getSpawn() != null) { + player.teleport(commandService.getSpawnLoader().getSpawn()); + } else { + player.sendMessage("[AuthMe] Spawn has failed, please try to define the spawn"); } } } diff --git a/src/main/java/fr/xephi/authme/listener/AuthMeServerListener.java b/src/main/java/fr/xephi/authme/listener/AuthMeServerListener.java index 141a2ea96..d0f16357c 100644 --- a/src/main/java/fr/xephi/authme/listener/AuthMeServerListener.java +++ b/src/main/java/fr/xephi/authme/listener/AuthMeServerListener.java @@ -6,6 +6,7 @@ import fr.xephi.authme.hooks.PluginHooks; import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.output.Messages; import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.settings.SpawnLoader; import fr.xephi.authme.util.GeoLiteAPI; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; @@ -21,11 +22,13 @@ public class AuthMeServerListener implements Listener { private final AuthMe plugin; private final Messages messages; private final PluginHooks pluginHooks; + private final SpawnLoader spawnLoader; - public AuthMeServerListener(AuthMe plugin, Messages messages, PluginHooks pluginHooks) { + public AuthMeServerListener(AuthMe plugin, Messages messages, PluginHooks pluginHooks, SpawnLoader spawnLoader) { this.plugin = plugin; this.messages = messages; this.pluginHooks = pluginHooks; + this.spawnLoader = spawnLoader; } @EventHandler(priority = EventPriority.HIGHEST) @@ -60,7 +63,7 @@ public class AuthMeServerListener implements Listener { pluginHooks.unhookCombatPlus(); ConsoleLogger.info("CombatTagPlus has been disabled: unhooking"); } else if ("EssentialsSpawn".equalsIgnoreCase(pluginName)) { - plugin.essentialsSpawn = null; + spawnLoader.unloadEssentialsSpawn(); ConsoleLogger.info("EssentialsSpawn has been disabled: unhooking"); } @@ -87,7 +90,7 @@ public class AuthMeServerListener implements Listener { } else if ("CombatTagPlus".equalsIgnoreCase(pluginName)) { pluginHooks.tryHookToCombatPlus(); } else if ("EssentialsSpawn".equalsIgnoreCase(pluginName)) { - plugin.checkEssentialsSpawn(); + spawnLoader.loadEssentialsSpawn(); } if (pluginName.equalsIgnoreCase("ProtocolLib")) { diff --git a/src/main/java/fr/xephi/authme/process/ProcessService.java b/src/main/java/fr/xephi/authme/process/ProcessService.java index f021d9d95..2ffbc8770 100644 --- a/src/main/java/fr/xephi/authme/process/ProcessService.java +++ b/src/main/java/fr/xephi/authme/process/ProcessService.java @@ -8,6 +8,7 @@ import fr.xephi.authme.output.Messages; import fr.xephi.authme.security.PasswordSecurity; import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.settings.NewSetting; +import fr.xephi.authme.settings.SpawnLoader; import fr.xephi.authme.settings.domain.Property; import org.bukkit.command.CommandSender; import org.bukkit.event.Event; @@ -24,15 +25,17 @@ public class ProcessService { private final IpAddressManager ipAddressManager; private final PasswordSecurity passwordSecurity; private final PluginHooks pluginHooks; + private final SpawnLoader spawnLoader; public ProcessService(NewSetting settings, Messages messages, AuthMe authMe, IpAddressManager ipAddressManager, - PasswordSecurity passwordSecurity, PluginHooks pluginHooks) { + PasswordSecurity passwordSecurity, PluginHooks pluginHooks, SpawnLoader spawnLoader) { this.settings = settings; this.messages = messages; this.authMe = authMe; this.ipAddressManager = ipAddressManager; this.passwordSecurity = passwordSecurity; this.pluginHooks = pluginHooks; + this.spawnLoader = spawnLoader; } public T getProperty(Property property) { @@ -91,4 +94,8 @@ public class ProcessService { return pluginHooks; } + public SpawnLoader getSpawnLoader() { + return spawnLoader; + } + } diff --git a/src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java b/src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java index 14840c93f..838ac0e6d 100644 --- a/src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java +++ b/src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java @@ -16,7 +16,6 @@ import fr.xephi.authme.process.Process; import fr.xephi.authme.process.ProcessService; import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.Settings; -import fr.xephi.authme.settings.Spawn; import fr.xephi.authme.settings.properties.HooksSettings; import fr.xephi.authme.settings.properties.PluginSettings; import fr.xephi.authme.settings.properties.RegistrationSettings; @@ -90,7 +89,7 @@ public class AsynchronousJoin implements Process { }); return; } - final Location spawnLoc = Spawn.getInstance().getSpawnLocation(player); + final Location spawnLoc = service.getSpawnLoader().getSpawnLocation(player); final boolean isAuthAvailable = database.isAuthAvailable(name); if (isAuthAvailable) { if (!Settings.noTeleport) { @@ -212,11 +211,14 @@ public class AsynchronousJoin implements Process { } private boolean needFirstSpawn() { - if (player.hasPlayedBefore()) + if (player.hasPlayedBefore()) { return false; - Location firstSpawn = Spawn.getInstance().getFirstSpawn(); - if (firstSpawn == null || firstSpawn.getWorld() == null) + } + Location firstSpawn = service.getSpawnLoader().getFirstSpawn(); + if (firstSpawn == null) { return false; + } + FirstSpawnTeleportEvent tpEvent = new FirstSpawnTeleportEvent(player, player.getLocation(), firstSpawn); plugin.getServer().getPluginManager().callEvent(tpEvent); if (!tpEvent.isCancelled()) { diff --git a/src/main/java/fr/xephi/authme/settings/NewSetting.java b/src/main/java/fr/xephi/authme/settings/NewSetting.java index 79557d9f8..00a597249 100644 --- a/src/main/java/fr/xephi/authme/settings/NewSetting.java +++ b/src/main/java/fr/xephi/authme/settings/NewSetting.java @@ -24,7 +24,7 @@ import java.util.Arrays; import java.util.List; import java.util.Map; -import static fr.xephi.authme.settings.SettingsMigrationService.copyFileFromResource; +import static fr.xephi.authme.util.FileUtils.copyFileFromResource; /** * The new settings manager. diff --git a/src/main/java/fr/xephi/authme/settings/SettingsMigrationService.java b/src/main/java/fr/xephi/authme/settings/SettingsMigrationService.java index dc4480277..72806a8ba 100644 --- a/src/main/java/fr/xephi/authme/settings/SettingsMigrationService.java +++ b/src/main/java/fr/xephi/authme/settings/SettingsMigrationService.java @@ -1,7 +1,6 @@ package fr.xephi.authme.settings; import com.google.common.annotations.VisibleForTesting; -import fr.xephi.authme.AuthMe; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.settings.domain.Property; import fr.xephi.authme.settings.propertymap.PropertyMap; @@ -10,14 +9,11 @@ import org.bukkit.configuration.file.FileConfiguration; import java.io.File; import java.io.FileWriter; import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; import static fr.xephi.authme.settings.properties.RegistrationSettings.DELAY_JOIN_MESSAGE; import static fr.xephi.authme.settings.properties.RegistrationSettings.REMOVE_JOIN_MESSAGE; import static fr.xephi.authme.settings.properties.RegistrationSettings.REMOVE_LEAVE_MESSAGE; import static fr.xephi.authme.settings.properties.RestrictionSettings.ALLOWED_NICKNAME_CHARACTERS; -import static java.lang.String.format; /** * Service for verifying that the configuration is up-to-date. @@ -131,41 +127,4 @@ public final class SettingsMigrationService { return false; } - - // ------- - // Utilities - // ------- - - /** - * Copy a resource file (from the JAR) to the given file if it doesn't exist. - * - * @param destinationFile The file to check and copy to (outside of JAR) - * @param resourcePath Absolute path to the resource file (path to file within JAR) - * @return False if the file does not exist and could not be copied, true otherwise - */ - public static boolean copyFileFromResource(File destinationFile, String resourcePath) { - if (destinationFile.exists()) { - return true; - } else if (!destinationFile.getParentFile().exists() && !destinationFile.getParentFile().mkdirs()) { - ConsoleLogger.showError("Cannot create parent directories for '" + destinationFile + "'"); - return false; - } - - // ClassLoader#getResourceAsStream does not deal with the '\' path separator: replace to '/' - final String normalizedPath = resourcePath.replace("\\", "/"); - try (InputStream is = AuthMe.class.getClassLoader().getResourceAsStream(normalizedPath)) { - if (is == null) { - ConsoleLogger.showError(format("Cannot copy resource '%s' to file '%s': cannot load resource", - resourcePath, destinationFile.getPath())); - } else { - Files.copy(is, destinationFile.toPath()); - return true; - } - } catch (IOException e) { - ConsoleLogger.logException(format("Cannot copy resource '%s' to file '%s':", - resourcePath, destinationFile.getPath()), e); - } - return false; - } - } diff --git a/src/main/java/fr/xephi/authme/settings/Spawn.java b/src/main/java/fr/xephi/authme/settings/Spawn.java deleted file mode 100644 index 6d88c72b4..000000000 --- a/src/main/java/fr/xephi/authme/settings/Spawn.java +++ /dev/null @@ -1,144 +0,0 @@ -package fr.xephi.authme.settings; - -import fr.xephi.authme.AuthMe; -import fr.xephi.authme.cache.auth.PlayerCache; -import fr.xephi.authme.util.StringUtils; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.World; -import org.bukkit.entity.Player; - -import java.io.File; - -/** - * @author Xephi59 - * @version $Revision: 1.0 $ - */ -public class Spawn extends CustomConfiguration { - - private static Spawn spawn; - private static String[] spawnPriority; - - private Spawn() { - super(new File(Settings.PLUGIN_FOLDER, "spawn.yml")); - load(); - save(); - spawnPriority = Settings.spawnPriority.split(","); - } - - public static void reload() { - spawn = new Spawn(); - } - - /** - * Method getInstance. - * - * @return Spawn - */ - public static Spawn getInstance() { - if (spawn == null) { - spawn = new Spawn(); - } - return spawn; - } - - public boolean setSpawn(Location location) { - if (location == null || location.getWorld() == null) { - return false; - } - set("spawn.world", location.getWorld().getName()); - set("spawn.x", location.getX()); - set("spawn.y", location.getY()); - set("spawn.z", location.getZ()); - set("spawn.yaw", location.getYaw()); - set("spawn.pitch", location.getPitch()); - save(); - return true; - } - - public boolean setFirstSpawn(Location location) { - if (location == null || location.getWorld() == null) { - return false; - } - set("firstspawn.world", location.getWorld().getName()); - set("firstspawn.x", location.getX()); - set("firstspawn.y", location.getY()); - set("firstspawn.z", location.getZ()); - set("firstspawn.yaw", location.getYaw()); - set("firstspawn.pitch", location.getPitch()); - save(); - return true; - } - - public Location getSpawn() { - if (containsAll("spawn.world", "spawn.x", "spawn.y", "spawn.z", "spawn.yaw", "spawn.pitch")) { - String worldName = getString("spawn.world"); - World world = Bukkit.getWorld(worldName); - if (!StringUtils.isEmpty(worldName) && world != null) { - return new Location( - world, getDouble("spawn.x"), getDouble("spawn.y"), getDouble("spawn.z"), - Float.parseFloat(getString("spawn.yaw")), Float.parseFloat(getString("spawn.pitch")) - ); - } - } - return null; - } - - public Location getFirstSpawn() { - if (containsAll("firstspawn.world", "firstspawn.x", "firstspawn.y", - "firstspawn.z", "firstspawn.yaw", "firstspawn.pitch")) { - String worldName = getString("firstspawn.world"); - World world = Bukkit.getWorld(worldName); - if (!StringUtils.isEmpty(worldName) && world != null) { - return new Location( - world, getDouble("firstspawn.x"), getDouble("firstspawn.y"), getDouble("firstspawn.z"), - Float.parseFloat(getString("firstspawn.yaw")), Float.parseFloat(getString("firstspawn.pitch")) - ); - } - } - return null; - } - - // Return the spawn location of a player - public Location getSpawnLocation(Player player) { - AuthMe plugin = AuthMe.getInstance(); - if (plugin == null || player == null || player.getWorld() == null) { - return null; - } - - World world = player.getWorld(); - Location spawnLoc = null; - for (String priority : spawnPriority) { - switch (priority.toLowerCase()) { - case "default": - if (world.getSpawnLocation() != null) { - spawnLoc = world.getSpawnLocation(); - } - break; - case "multiverse": - if (Settings.multiverse) { - spawnLoc = plugin.getPluginHooks().getMultiverseSpawn(world); - } - break; - case "essentials": - spawnLoc = plugin.essentialsSpawn; - break; - case "authme": - String playerNameLower = player.getName().toLowerCase(); - if (PlayerCache.getInstance().isAuthenticated(playerNameLower)) { - spawnLoc = getSpawn(); - } else if ((getFirstSpawn() != null) && (!player.hasPlayedBefore() || - (!plugin.getDataSource().isAuthAvailable(playerNameLower)))) { - spawnLoc = getFirstSpawn(); - } else { - spawnLoc = getSpawn(); - } - break; - } - if (spawnLoc != null) { - return spawnLoc; - } - } - return world.getSpawnLocation(); // return default location - } -} diff --git a/src/main/java/fr/xephi/authme/settings/SpawnLoader.java b/src/main/java/fr/xephi/authme/settings/SpawnLoader.java new file mode 100644 index 000000000..5deb5c16d --- /dev/null +++ b/src/main/java/fr/xephi/authme/settings/SpawnLoader.java @@ -0,0 +1,261 @@ +package fr.xephi.authme.settings; + +import fr.xephi.authme.AuthMe; +import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.cache.auth.PlayerCache; +import fr.xephi.authme.hooks.PluginHooks; +import fr.xephi.authme.settings.properties.RestrictionSettings; +import fr.xephi.authme.util.FileUtils; +import fr.xephi.authme.util.StringUtils; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; + +import java.io.File; +import java.io.IOException; + +/** + * Manager for spawn points. It loads spawn definitions from AuthMe and third-party plugins + * and is responsible for returning the correct spawn point as per the settings. + *

+ * The spawn priority setting defines from which sources and in which order the spawn point + * should be taken from. In AuthMe, we can distinguish between the regular spawn and a "first spawn", + * to which players will be teleported who have joined for the first time. + */ +public class SpawnLoader { + + private final File authMeConfigurationFile; + private final PluginHooks pluginHooks; + private FileConfiguration authMeConfiguration; + private String[] spawnPriority; + private Location essentialsSpawn; + + /** + * Constructor. + * + * @param pluginFolder The AuthMe data folder + * @param settings The setting instance + * @param pluginHooks The plugin hooks instance + */ + public SpawnLoader(File pluginFolder, NewSetting settings, PluginHooks pluginHooks) { + File spawnFile = new File(pluginFolder, "spawn.yml"); + // TODO ljacqu 20160312: Check if resource could be copied and handle the case if not + FileUtils.copyFileFromResource(spawnFile, "spawn.yml"); + this.authMeConfigurationFile = new File(pluginFolder, "spawn.yml"); + this.pluginHooks = pluginHooks; + initialize(settings); + } + + /** + * Retrieve the relevant settings and load the AuthMe spawn.yml file. + * + * @param settings The settings instance + */ + public void initialize(NewSetting settings) { + spawnPriority = settings.getProperty(RestrictionSettings.SPAWN_PRIORITY).split(","); + authMeConfiguration = YamlConfiguration.loadConfiguration(authMeConfigurationFile); + loadEssentialsSpawn(); + } + + /** + * Return the AuthMe spawn location. + * + * @return The location of the regular AuthMe spawn point + */ + public Location getSpawn() { + return getLocationFromConfiguration(authMeConfiguration, "spawn"); + } + + /** + * Set the AuthMe spawn point. + * + * @param location The location to use + * @return True upon success, false otherwise + */ + public boolean setSpawn(Location location) { + return setLocation("spawn", location); + } + + /** + * Return the AuthMe first spawn location. + * + * @return The location of the AuthMe spawn point for first timers + */ + public Location getFirstSpawn() { + return getLocationFromConfiguration(authMeConfiguration, "firstspawn"); + } + + /** + * Set the AuthMe first spawn location. + * + * @param location The location to use + * @return True upon success, false otherwise + */ + public boolean setFirstSpawn(Location location) { + return setLocation("firstspawn", location); + } + + /** + * Load the spawn point defined in EssentialsSpawn. + */ + public void loadEssentialsSpawn() { + // EssentialsSpawn cannot run without Essentials, so it's fine to get the Essentials data folder + File essentialsFolder = pluginHooks.getEssentialsDataFolder(); + if (essentialsFolder == null) { + return; + } + + File essentialsSpawnFile = new File(essentialsFolder, "spawn.yml"); + if (essentialsSpawnFile.exists()) { + essentialsSpawn = getLocationFromConfiguration( + YamlConfiguration.loadConfiguration(essentialsSpawnFile), "spawns.default"); + } else { + essentialsSpawn = null; + ConsoleLogger.info("Essentials spawn file not found: '" + essentialsSpawnFile.getAbsolutePath() + "'"); + } + } + + /** + * Unset the spawn point defined in EssentialsSpawn. + */ + public void unloadEssentialsSpawn() { + essentialsSpawn = null; + } + + /** + * Return the spawn location for the given player. The source of the spawn location varies + * depending on the spawn priority setting. + * + * @param player The player to retrieve the spawn point for + * @return The spawn location, or the default spawn location upon failure + * @see RestrictionSettings#SPAWN_PRIORITY + */ + public Location getSpawnLocation(Player player) { + AuthMe plugin = AuthMe.getInstance(); + if (plugin == null || player == null || player.getWorld() == null) { + return null; + } + + World world = player.getWorld(); + Location spawnLoc = null; + // TODO ljacqu 20160312: We should trim() the entries + for (String priority : spawnPriority) { + switch (priority.toLowerCase()) { + case "default": + if (world.getSpawnLocation() != null) { + spawnLoc = world.getSpawnLocation(); + } + break; + case "multiverse": + if (Settings.multiverse) { + spawnLoc = pluginHooks.getMultiverseSpawn(world); + } + break; + case "essentials": + spawnLoc = essentialsSpawn; + break; + case "authme": + String playerNameLower = player.getName().toLowerCase(); + if (PlayerCache.getInstance().isAuthenticated(playerNameLower)) { + spawnLoc = getSpawn(); + } else if (getFirstSpawn() != null && (!player.hasPlayedBefore() || + !plugin.getDataSource().isAuthAvailable(playerNameLower))) { + spawnLoc = getFirstSpawn(); + } else { + spawnLoc = getSpawn(); + } + break; + } + if (spawnLoc != null) { + return spawnLoc; + } + } + return world.getSpawnLocation(); // return default location + } + + /** + * Save the location under the given prefix. + * + * @param prefix The prefix to save the spawn under + * @param location The location to persist + * @return True upon success, false otherwise + */ + private boolean setLocation(String prefix, Location location) { + if (location != null && location.getWorld() != null) { + authMeConfiguration.set(prefix + ".world", location.getWorld().getName()); + authMeConfiguration.set(prefix + ".x", location.getX()); + authMeConfiguration.set(prefix + ".y", location.getY()); + authMeConfiguration.set(prefix + ".z", location.getZ()); + authMeConfiguration.set(prefix + ".yaw", location.getYaw()); + authMeConfiguration.set(prefix + ".pitch", location.getPitch()); + return saveAuthMeConfig(); + } + return false; + } + + private boolean saveAuthMeConfig() { + // TODO ljacqu 20160312: Investigate whether this utility should be put in a Utils class + try { + authMeConfiguration.save(authMeConfigurationFile); + return true; + } catch (IOException e) { + ConsoleLogger.logException("Could not save spawn config (" + authMeConfigurationFile + ")", e); + } + return false; + } + + /** + * Build a {@link Location} object from the given path in the file configuration. + * + * @param configuration The file configuration to read from + * @param pathPrefix The path to get the spawn point from + * @return Location corresponding to the values in the path + */ + private static Location getLocationFromConfiguration(FileConfiguration configuration, String pathPrefix) { + if (containsAllSpawnFields(configuration, pathPrefix)) { + String prefix = pathPrefix + "."; + String worldName = configuration.getString(prefix + "world"); + World world = Bukkit.getWorld(worldName); + if (!StringUtils.isEmpty(worldName) && world != null) { + return new Location(world, configuration.getDouble(prefix + "x"), + configuration.getDouble(prefix + "y"), configuration.getDouble(prefix + "z"), + getFloat(configuration, prefix + "yaw"), getFloat(configuration, prefix + "pitch")); + } + } + return null; + } + + /** + * Return whether the file configuration contains all fields necessary to define a spawn + * under the given path. + * + * @param configuration The file configuration to use + * @param pathPrefix The path to verify + * @return True if all spawn fields are present, false otherwise + */ + private static boolean containsAllSpawnFields(FileConfiguration configuration, String pathPrefix) { + String[] fields = {"world", "x", "y", "z", "yaw", "pitch"}; + for (String field : fields) { + if (!configuration.contains(pathPrefix + "." + field)) { + return false; + } + } + return true; + } + + /** + * Retrieve a property as a float from the given file configuration. + * + * @param configuration The file configuration to use + * @param path The path of the property to retrieve + * @return The float + */ + private static float getFloat(FileConfiguration configuration, String path) { + Object value = configuration.get(path); + // This behavior is consistent with FileConfiguration#getDouble + return (value instanceof Number) ? ((Number) value).floatValue() : 0; + } +} diff --git a/src/main/java/fr/xephi/authme/util/FileUtils.java b/src/main/java/fr/xephi/authme/util/FileUtils.java new file mode 100644 index 000000000..d34c41f86 --- /dev/null +++ b/src/main/java/fr/xephi/authme/util/FileUtils.java @@ -0,0 +1,52 @@ +package fr.xephi.authme.util; + +import fr.xephi.authme.AuthMe; +import fr.xephi.authme.ConsoleLogger; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; + +import static java.lang.String.format; + +/** + * File utilities. + */ +public class FileUtils { + + private FileUtils() { + } + + /** + * Copy a resource file (from the JAR) to the given file if it doesn't exist. + * + * @param destinationFile The file to check and copy to (outside of JAR) + * @param resourcePath Absolute path to the resource file (path to file within JAR) + * @return False if the file does not exist and could not be copied, true otherwise + */ + public static boolean copyFileFromResource(File destinationFile, String resourcePath) { + if (destinationFile.exists()) { + return true; + } else if (!destinationFile.getParentFile().exists() && !destinationFile.getParentFile().mkdirs()) { + ConsoleLogger.showError("Cannot create parent directories for '" + destinationFile + "'"); + return false; + } + + // ClassLoader#getResourceAsStream does not deal with the '\' path separator: replace to '/' + final String normalizedPath = resourcePath.replace("\\", "/"); + try (InputStream is = AuthMe.class.getClassLoader().getResourceAsStream(normalizedPath)) { + if (is == null) { + ConsoleLogger.showError(format("Cannot copy resource '%s' to file '%s': cannot load resource", + resourcePath, destinationFile.getPath())); + } else { + Files.copy(is, destinationFile.toPath()); + return true; + } + } catch (IOException e) { + ConsoleLogger.logException(format("Cannot copy resource '%s' to file '%s':", + resourcePath, destinationFile.getPath()), e); + } + return false; + } +} diff --git a/src/test/java/fr/xephi/authme/command/CommandServiceTest.java b/src/test/java/fr/xephi/authme/command/CommandServiceTest.java index b2f1b283f..242e9eac1 100644 --- a/src/test/java/fr/xephi/authme/command/CommandServiceTest.java +++ b/src/test/java/fr/xephi/authme/command/CommandServiceTest.java @@ -11,6 +11,7 @@ import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.process.Management; import fr.xephi.authme.security.PasswordSecurity; import fr.xephi.authme.settings.NewSetting; +import fr.xephi.authme.settings.SpawnLoader; import fr.xephi.authme.settings.domain.Property; import fr.xephi.authme.settings.properties.SecuritySettings; import org.bukkit.command.CommandSender; @@ -44,6 +45,7 @@ public class CommandServiceTest { private NewSetting settings; private IpAddressManager ipAddressManager; private PluginHooks pluginHooks; + private SpawnLoader spawnLoader; @Before public void setUpService() { @@ -56,8 +58,9 @@ public class CommandServiceTest { settings = mock(NewSetting.class); ipAddressManager = mock(IpAddressManager.class); pluginHooks = mock(PluginHooks.class); + spawnLoader = mock(SpawnLoader.class); commandService = new CommandService(authMe, commandMapper, helpProvider, messages, passwordSecurity, - permissionsManager, settings, ipAddressManager, pluginHooks); + permissionsManager, settings, ipAddressManager, pluginHooks, spawnLoader); } @Test diff --git a/src/test/java/fr/xephi/authme/settings/SpawnLoaderTest.java b/src/test/java/fr/xephi/authme/settings/SpawnLoaderTest.java new file mode 100644 index 000000000..8bd9da4bc --- /dev/null +++ b/src/test/java/fr/xephi/authme/settings/SpawnLoaderTest.java @@ -0,0 +1,68 @@ +package fr.xephi.authme.settings; + +import com.google.common.io.Files; +import fr.xephi.authme.TestHelper; +import fr.xephi.authme.hooks.PluginHooks; +import fr.xephi.authme.settings.properties.RestrictionSettings; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.configuration.file.YamlConfiguration; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import java.io.File; +import java.io.IOException; + +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +/** + * Test for {@link SpawnLoader}. + */ +public class SpawnLoaderTest { + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + private File testFolder; + private NewSetting settings; + + @Before + public void setup() throws IOException { + // Copy test config into a new temporary folder + testFolder = temporaryFolder.newFolder(); + File source = TestHelper.getJarFile("/spawn/spawn-firstspawn.yml"); + File destination = new File(testFolder, "spawn.yml"); + Files.copy(source, destination); + + // Create a settings mock with default values + settings = mock(NewSetting.class); + given(settings.getProperty(RestrictionSettings.SPAWN_PRIORITY)) + .willReturn("authme, essentials, multiverse, default"); + } + + @Test + public void shouldSetSpawn() { + // given + SpawnLoader spawnLoader = new SpawnLoader(testFolder, settings, mock(PluginHooks.class)); + World world = mock(World.class); + given(world.getName()).willReturn("new_world"); + Location newSpawn = new Location(world, 123, 45.0, -67.89); + + // when + boolean result = spawnLoader.setSpawn(newSpawn); + + // then + assertThat(result, equalTo(true)); + YamlConfiguration configuration = YamlConfiguration.loadConfiguration(new File(testFolder, "spawn.yml")); + assertThat(configuration.getDouble("spawn.x"), equalTo(123.0)); + assertThat(configuration.getDouble("spawn.y"), equalTo(45.0)); + assertThat(configuration.getDouble("spawn.z"), equalTo(-67.89)); + assertThat(configuration.getString("spawn.world"), equalTo("new_world")); + } + +} diff --git a/src/test/resources/spawn/spawn-firstspawn.yml b/src/test/resources/spawn/spawn-firstspawn.yml new file mode 100644 index 000000000..dbb7bd89c --- /dev/null +++ b/src/test/resources/spawn/spawn-firstspawn.yml @@ -0,0 +1,15 @@ +# Sample spawn.yml file with both spawn and firstspawn defined +spawn: + world: 'world' + x: 300.12 + y: 120.34 + z: -89.12 + yaw: 0.23 + pitch: 112.25 +firstspawn: + world: 'firstspawn' + x: -30.12 + y: 204.43 + z: 10.32 + yaw: 0.00 + pitch: 10.23