diff --git a/src/main/java/fr/xephi/authme/events/SpawnTeleportEvent.java b/src/main/java/fr/xephi/authme/events/SpawnTeleportEvent.java index 3a429bc3..28f61111 100644 --- a/src/main/java/fr/xephi/authme/events/SpawnTeleportEvent.java +++ b/src/main/java/fr/xephi/authme/events/SpawnTeleportEvent.java @@ -21,6 +21,7 @@ public class SpawnTeleportEvent extends AbstractTeleportEvent { * @param to The teleport destination * @param isAuthenticated Whether or not the player is logged in */ + // TODO ljacqu 20160611: We only ever call this with from = player.getLocation() -> could be done in constructor public SpawnTeleportEvent(Player player, Location from, Location to, boolean isAuthenticated) { super(false, player, from, to); this.isAuthenticated = isAuthenticated; diff --git a/src/main/java/fr/xephi/authme/process/ProcessService.java b/src/main/java/fr/xephi/authme/process/ProcessService.java index f171e368..775344d7 100644 --- a/src/main/java/fr/xephi/authme/process/ProcessService.java +++ b/src/main/java/fr/xephi/authme/process/ProcessService.java @@ -9,8 +9,6 @@ import fr.xephi.authme.settings.domain.Property; import fr.xephi.authme.util.ValidationService; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -import org.bukkit.event.Event; -import org.bukkit.plugin.PluginManager; import javax.inject.Inject; @@ -25,9 +23,6 @@ public class ProcessService { @Inject private Messages messages; - @Inject - private PluginManager pluginManager; - @Inject private ValidationService validationService; @@ -95,15 +90,6 @@ public class ProcessService { return messages.retrieveSingle(key); } - /** - * Emit an event. - * - * @param event the event to emit - */ - public void callEvent(Event event) { - pluginManager.callEvent(event); - } - public boolean validateEmail(String email) { return validationService.validateEmail(email); } 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 fcbfc0bd..d56649b4 100644 --- a/src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java +++ b/src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java @@ -7,16 +7,13 @@ import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.cache.limbo.LimboCache; import fr.xephi.authme.cache.limbo.LimboPlayer; import fr.xephi.authme.datasource.DataSource; -import fr.xephi.authme.events.FirstSpawnTeleportEvent; import fr.xephi.authme.events.ProtectInventoryEvent; -import fr.xephi.authme.events.SpawnTeleportEvent; import fr.xephi.authme.hooks.PluginHooks; import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.permission.PlayerStatePermission; import fr.xephi.authme.process.AsynchronousProcess; import fr.xephi.authme.process.ProcessService; -import fr.xephi.authme.settings.Settings; -import fr.xephi.authme.settings.SpawnLoader; +import fr.xephi.authme.util.TeleportationService; import fr.xephi.authme.settings.properties.HooksSettings; import fr.xephi.authme.settings.properties.PluginSettings; import fr.xephi.authme.settings.properties.RegistrationSettings; @@ -29,8 +26,6 @@ import fr.xephi.authme.util.Utils; import fr.xephi.authme.util.Utils.GroupType; import org.apache.commons.lang.reflect.MethodUtils; import org.bukkit.GameMode; -import org.bukkit.Location; -import org.bukkit.Material; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.potion.PotionEffect; @@ -44,6 +39,9 @@ import static fr.xephi.authme.settings.properties.RestrictionSettings.PROTECT_IN public class AsynchronousJoin implements AsynchronousProcess { + private static final boolean DISABLE_COLLISIONS = MethodUtils + .getAccessibleMethod(LivingEntity.class, "setCollidable", new Class[]{}) != null; + @Inject private AuthMe plugin; @@ -63,24 +61,22 @@ public class AsynchronousJoin implements AsynchronousProcess { private PluginHooks pluginHooks; @Inject - private SpawnLoader spawnLoader; + private TeleportationService teleportationService; @Inject private BukkitService bukkitService; - private static final boolean DISABLE_COLLISIONS = MethodUtils - .getAccessibleMethod(LivingEntity.class, "setCollidable", new Class[]{}) != null; - AsynchronousJoin() { } - public void processJoin(final Player player) { - if (Utils.isUnrestricted(player)) { - return; - } + public void processJoin(final Player player) { final String name = player.getName().toLowerCase(); final String ip = Utils.getPlayerIp(player); + if (isPlayerUnrestricted(name)) { + return; + } + // Prevent player collisions in 1.9 if (DISABLE_COLLISIONS) { player.setCollidable(false); @@ -113,50 +109,15 @@ public class AsynchronousJoin implements AsynchronousProcess { return; } - if (service.getProperty(RestrictionSettings.MAX_JOIN_PER_IP) > 0 - && !service.hasPermission(player, PlayerStatePermission.ALLOW_MULTIPLE_ACCOUNTS) - && !"127.0.0.1".equalsIgnoreCase(ip) - && !"localhost".equalsIgnoreCase(ip) - && hasJoinedIp(player.getName(), ip)) { - - bukkitService.scheduleSyncDelayedTask(new Runnable() { - @Override - public void run() { - player.kickPlayer(service.retrieveSingleMessage(MessageKey.SAME_IP_ONLINE)); - } - }); + if (!validatePlayerCountForIp(player, ip)) { return; } - final Location spawnLoc = spawnLoader.getSpawnLocation(player); final boolean isAuthAvailable = database.isAuthAvailable(name); - // TODO: continue cleanup from this -sgdc3 if (isAuthAvailable) { - // Registered - - // Groups logic Utils.setGroup(player, GroupType.NOTLOGGEDIN); - - // Spawn logic - if (!service.getProperty(RestrictionSettings.NO_TELEPORT)) { - if (Settings.isTeleportToSpawnEnabled || (Settings.isForceSpawnLocOnJoinEnabled && Settings.getForcedWorlds.contains(player.getWorld().getName()))) { - bukkitService.scheduleSyncDelayedTask(new Runnable() { - @Override - public void run() { - SpawnTeleportEvent tpEvent = new SpawnTeleportEvent(player, player.getLocation(), spawnLoc, playerCache.isAuthenticated(name)); - service.callEvent(tpEvent); - if (!tpEvent.isCancelled() && player.isOnline() && tpEvent.getTo() != null - && tpEvent.getTo().getWorld() != null) { - player.teleport(tpEvent.getTo()); - } - } - }); - } - } - placePlayerSafely(player, spawnLoc); - - // Limbo cache + teleportationService.teleportOnJoin(player); limboCache.updateLimboPlayer(player); // Protect inventory @@ -193,27 +154,13 @@ public class AsynchronousJoin implements AsynchronousProcess { // Groups logic Utils.setGroup(player, GroupType.UNREGISTERED); + teleportationService.teleportOnJoin(player); // Skip if registration is optional if (!service.getProperty(RegistrationSettings.FORCE)) { return; } - // Spawn logic - if (!Settings.noTeleport && !needFirstSpawn(player) && Settings.isTeleportToSpawnEnabled - || (Settings.isForceSpawnLocOnJoinEnabled && Settings.getForcedWorlds.contains(player.getWorld().getName()))) { - bukkitService.scheduleSyncDelayedTask(new Runnable() { - @Override - public void run() { - SpawnTeleportEvent tpEvent = new SpawnTeleportEvent(player, player.getLocation(), spawnLoc, playerCache.isAuthenticated(name)); - service.callEvent(tpEvent); - if (!tpEvent.isCancelled() && player.isOnline() && tpEvent.getTo() != null - && tpEvent.getTo().getWorld() != null) { - player.teleport(tpEvent.getTo()); - } - } - }); - } } // The user is not logged in @@ -274,58 +221,12 @@ public class AsynchronousJoin implements AsynchronousProcess { } } - private boolean needFirstSpawn(final Player player) { - if (player.hasPlayedBefore()) { - return false; - } - Location firstSpawn = spawnLoader.getFirstSpawn(); - if (firstSpawn == null) { - return false; - } - - FirstSpawnTeleportEvent tpEvent = new FirstSpawnTeleportEvent(player, player.getLocation(), firstSpawn); - plugin.getServer().getPluginManager().callEvent(tpEvent); - if (!tpEvent.isCancelled()) { - if (player.isOnline() && tpEvent.getTo() != null && tpEvent.getTo().getWorld() != null) { - final Location fLoc = tpEvent.getTo(); - bukkitService.scheduleSyncDelayedTask(new Runnable() { - @Override - public void run() { - player.teleport(fLoc); - } - }); - } - } - return true; - } - - private void placePlayerSafely(final Player player, final Location spawnLoc) { - if (spawnLoc == null || service.getProperty(RestrictionSettings.NO_TELEPORT)) - return; - if (Settings.isTeleportToSpawnEnabled || (Settings.isForceSpawnLocOnJoinEnabled && Settings.getForcedWorlds.contains(player.getWorld().getName()))) - return; - if (!player.hasPlayedBefore()) - return; - bukkitService.scheduleSyncDelayedTask(new Runnable() { - @Override - public void run() { - if (spawnLoc.getWorld() == null) { - return; - } - Material cur = player.getLocation().getBlock().getType(); - Material top = player.getLocation().add(0, 1, 0).getBlock().getType(); - if (cur == Material.PORTAL || cur == Material.ENDER_PORTAL - || top == Material.PORTAL || top == Material.ENDER_PORTAL) { - service.send(player, MessageKey.UNSAFE_QUIT_LOCATION); - player.teleport(spawnLoc); - } - } - - }); + private boolean isPlayerUnrestricted(String name) { + return service.getProperty(RestrictionSettings.UNRESTRICTED_NAMES).contains(name); } /** - * Return whether the name is restricted based on the restriction setting. + * Returns whether the name is restricted based on the restriction settings. * * @param name The name to check * @param ip The IP address of the player @@ -353,14 +254,39 @@ public class AsynchronousJoin implements AsynchronousProcess { return nameFound; } - private boolean hasJoinedIp(String name, String ip) { + /** + * Checks whether the maximum number of accounts has been exceeded for the given IP address (according to + * settings and permissions). If this is the case, the player is kicked. + * + * @param player the player to verify + * @param ip the ip address of the player + * @return true if the verification is OK (no infraction), false if player has been kicked + */ + private boolean validatePlayerCountForIp(final Player player, String ip) { + if (service.getProperty(RestrictionSettings.MAX_JOIN_PER_IP) > 0 + && !service.hasPermission(player, PlayerStatePermission.ALLOW_MULTIPLE_ACCOUNTS) + && !"127.0.0.1".equalsIgnoreCase(ip) + && !"localhost".equalsIgnoreCase(ip) + && countOnlinePlayersByIp(ip) > service.getProperty(RestrictionSettings.MAX_JOIN_PER_IP)) { + + bukkitService.scheduleSyncDelayedTask(new Runnable() { + @Override + public void run() { + player.kickPlayer(service.retrieveSingleMessage(MessageKey.SAME_IP_ONLINE)); + } + }); + return false; + } + return true; + } + + private int countOnlinePlayersByIp(String ip) { int count = 0; for (Player player : bukkitService.getOnlinePlayers()) { - if (ip.equalsIgnoreCase(Utils.getPlayerIp(player)) - && !player.getName().equalsIgnoreCase(name)) { - count++; + if (ip.equalsIgnoreCase(Utils.getPlayerIp(player))) { + ++count; } } - return count >= service.getProperty(RestrictionSettings.MAX_JOIN_PER_IP); + return count; } } diff --git a/src/main/java/fr/xephi/authme/process/login/ProcessSyncPlayerLogin.java b/src/main/java/fr/xephi/authme/process/login/ProcessSyncPlayerLogin.java index 78df4473..373afd94 100644 --- a/src/main/java/fr/xephi/authme/process/login/ProcessSyncPlayerLogin.java +++ b/src/main/java/fr/xephi/authme/process/login/ProcessSyncPlayerLogin.java @@ -7,14 +7,12 @@ import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.cache.limbo.LimboCache; import fr.xephi.authme.cache.limbo.LimboPlayer; import fr.xephi.authme.datasource.DataSource; -import fr.xephi.authme.events.AuthMeTeleportEvent; import fr.xephi.authme.events.LoginEvent; import fr.xephi.authme.events.RestoreInventoryEvent; -import fr.xephi.authme.events.SpawnTeleportEvent; import fr.xephi.authme.listener.AuthMePlayerListener; import fr.xephi.authme.process.ProcessService; import fr.xephi.authme.process.SynchronousProcess; -import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.util.TeleportationService; import fr.xephi.authme.settings.properties.HooksSettings; import fr.xephi.authme.settings.properties.RegistrationSettings; import fr.xephi.authme.settings.properties.RestrictionSettings; @@ -23,7 +21,6 @@ import fr.xephi.authme.util.Utils; import fr.xephi.authme.util.Utils.GroupType; import org.apache.commons.lang.reflect.MethodUtils; import org.bukkit.Bukkit; -import org.bukkit.Location; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.plugin.PluginManager; @@ -36,6 +33,9 @@ import static fr.xephi.authme.settings.properties.RestrictionSettings.PROTECT_IN public class ProcessSyncPlayerLogin implements SynchronousProcess { + private static final boolean RESTORE_COLLISIONS = MethodUtils + .getAccessibleMethod(LivingEntity.class, "setCollidable", new Class[]{}) != null; + @Inject private AuthMe plugin; @@ -54,33 +54,12 @@ public class ProcessSyncPlayerLogin implements SynchronousProcess { @Inject private PluginManager pluginManager; - private final boolean restoreCollisions = MethodUtils - .getAccessibleMethod(LivingEntity.class, "setCollidable", new Class[]{}) != null; + @Inject + private TeleportationService teleportationService; ProcessSyncPlayerLogin() { } - private void packQuitLocation(Player player, PlayerAuth auth) { - Utils.packCoords(auth.getQuitLocX(), auth.getQuitLocY(), auth.getQuitLocZ(), auth.getWorld(), player); - } - - private void teleportBackFromSpawn(Player player, LimboPlayer limboPlayer) { - AuthMeTeleportEvent tpEvent = new AuthMeTeleportEvent(player, limboPlayer.getLoc()); - pluginManager.callEvent(tpEvent); - if (!tpEvent.isCancelled() && tpEvent.getTo() != null) { - player.teleport(tpEvent.getTo()); - } - } - - private void teleportToSpawn(Player player) { - Location spawnL = plugin.getSpawnLocation(player); - SpawnTeleportEvent tpEvent = new SpawnTeleportEvent(player, player.getLocation(), spawnL, true); - pluginManager.callEvent(tpEvent); - if (!tpEvent.isCancelled() && tpEvent.getTo() != null) { - player.teleport(tpEvent.getTo()); - } - } - private void restoreSpeedEffects(Player player) { if (!service.getProperty(RestrictionSettings.ALLOW_UNAUTHED_MOVEMENT) && service.getProperty(RestrictionSettings.REMOVE_SPEED)) { @@ -118,23 +97,9 @@ public class ProcessSyncPlayerLogin implements SynchronousProcess { restoreOpState(player, limbo); Utils.setGroup(player, GroupType.LOGGEDIN); - if (!Settings.noTeleport) { - if (Settings.isTeleportToSpawnEnabled && !Settings.isForceSpawnLocOnJoinEnabled && Settings.getForcedWorlds.contains(player.getWorld().getName())) { - if (Settings.isSaveQuitLocationEnabled && auth.getQuitLocY() != 0) { - packQuitLocation(player, auth); - } else { - teleportBackFromSpawn(player, limbo); - } - } else if (Settings.isForceSpawnLocOnJoinEnabled && Settings.getForcedWorlds.contains(player.getWorld().getName())) { - teleportToSpawn(player); - } else if (Settings.isSaveQuitLocationEnabled && auth.getQuitLocY() != 0) { - packQuitLocation(player, auth); - } else { - teleportBackFromSpawn(player, limbo); - } - } + teleportationService.teleportOnLogin(player, auth, limbo); - if (restoreCollisions && !service.getProperty(KEEP_COLLISIONS_DISABLED)) { + if (RESTORE_COLLISIONS && !service.getProperty(KEEP_COLLISIONS_DISABLED)) { player.setCollidable(true); } diff --git a/src/main/java/fr/xephi/authme/process/register/ProcessSyncPasswordRegister.java b/src/main/java/fr/xephi/authme/process/register/ProcessSyncPasswordRegister.java index 26bf174c..cf6bba2b 100644 --- a/src/main/java/fr/xephi/authme/process/register/ProcessSyncPasswordRegister.java +++ b/src/main/java/fr/xephi/authme/process/register/ProcessSyncPasswordRegister.java @@ -102,7 +102,7 @@ public class ProcessSyncPasswordRegister implements SynchronousProcess { if (service.getProperty(HIDE_TABLIST_BEFORE_LOGIN) && plugin.inventoryProtector != null) { RestoreInventoryEvent event = new RestoreInventoryEvent(player); - service.callEvent(event); + bukkitService.callEvent(event); if (!event.isCancelled()) { plugin.inventoryProtector.sendInventoryPacket(player); } diff --git a/src/main/java/fr/xephi/authme/settings/Settings.java b/src/main/java/fr/xephi/authme/settings/Settings.java index 9684d752..c86cd83b 100644 --- a/src/main/java/fr/xephi/authme/settings/Settings.java +++ b/src/main/java/fr/xephi/authme/settings/Settings.java @@ -9,7 +9,6 @@ import fr.xephi.authme.settings.properties.RestrictionSettings; import fr.xephi.authme.settings.properties.SecuritySettings; import org.bukkit.configuration.file.FileConfiguration; -import java.util.ArrayList; import java.util.List; /** @@ -66,16 +65,11 @@ public final class Settings { isAllowRestrictedIp = load(RestrictionSettings.ENABLE_RESTRICTED_USERS); isRemoveSpeedEnabled = load(RestrictionSettings.REMOVE_SPEED); isForceSpawnLocOnJoinEnabled = load(RestrictionSettings.FORCE_SPAWN_LOCATION_AFTER_LOGIN); - isSaveQuitLocationEnabled = configFile.getBoolean("settings.restrictions.SaveQuitLocation", false); + isSaveQuitLocationEnabled = load(RestrictionSettings.SAVE_QUIT_LOCATION); getUnloggedinGroup = load(SecuritySettings.UNLOGGEDIN_GROUP); getNonActivatedGroup = configFile.getInt("ExternalBoardOptions.nonActivedUserGroup", -1); unRegisteredGroup = configFile.getString("GroupOptions.UnregisteredPlayerGroup", ""); - - getUnrestrictedName = new ArrayList<>(); - for (String name : configFile.getStringList("settings.unrestrictions.UnrestrictedName")) { - getUnrestrictedName.add(name.toLowerCase()); - } - + getUnrestrictedName = load(RestrictionSettings.UNRESTRICTED_NAMES); getRegisteredGroup = configFile.getString("GroupOptions.RegisteredPlayerGroup", ""); protectInventoryBeforeLogInEnabled = load(RestrictionSettings.PROTECT_INVENTORY_BEFORE_LOGIN); isStopEnabled = configFile.getBoolean("Security.SQLProblem.stopServer", true); diff --git a/src/main/java/fr/xephi/authme/util/BukkitService.java b/src/main/java/fr/xephi/authme/util/BukkitService.java index 5837b942..ac8a408b 100644 --- a/src/main/java/fr/xephi/authme/util/BukkitService.java +++ b/src/main/java/fr/xephi/authme/util/BukkitService.java @@ -4,7 +4,9 @@ import fr.xephi.authme.AuthMe; import fr.xephi.authme.ConsoleLogger; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; +import org.bukkit.World; import org.bukkit.entity.Player; +import org.bukkit.event.Event; import org.bukkit.scheduler.BukkitTask; import javax.inject.Inject; @@ -30,7 +32,7 @@ public class BukkitService { private Method getOnlinePlayers; @Inject - public BukkitService(AuthMe authMe) { + BukkitService(AuthMe authMe) { this.authMe = authMe; getOnlinePlayersIsCollection = initializeOnlinePlayersIsCollectionField(); } @@ -167,6 +169,27 @@ public class BukkitService { return Collections.emptyList(); } + /** + * Calls an event with the given details. + * + * @param event Event details + * @throws IllegalStateException Thrown when an asynchronous event is + * fired from synchronous code. + */ + public void callEvent(Event event) { + Bukkit.getPluginManager().callEvent(event); + } + + /** + * Gets the world with the given name. + * + * @param name the name of the world to retrieve + * @return a world with the given name, or null if none exists + */ + public World getWorld(String name) { + return Bukkit.getWorld(name); + } + /** * Method run upon initialization to verify whether or not the Bukkit implementation * returns the online players as a Collection. diff --git a/src/main/java/fr/xephi/authme/util/TeleportationService.java b/src/main/java/fr/xephi/authme/util/TeleportationService.java new file mode 100644 index 00000000..b7beaedd --- /dev/null +++ b/src/main/java/fr/xephi/authme/util/TeleportationService.java @@ -0,0 +1,144 @@ +package fr.xephi.authme.util; + +import fr.xephi.authme.cache.auth.PlayerAuth; +import fr.xephi.authme.cache.auth.PlayerCache; +import fr.xephi.authme.cache.limbo.LimboPlayer; +import fr.xephi.authme.events.AbstractTeleportEvent; +import fr.xephi.authme.events.AuthMeTeleportEvent; +import fr.xephi.authme.events.FirstSpawnTeleportEvent; +import fr.xephi.authme.events.SpawnTeleportEvent; +import fr.xephi.authme.initialization.Reloadable; +import fr.xephi.authme.output.Messages; +import fr.xephi.authme.settings.NewSetting; +import fr.xephi.authme.settings.SpawnLoader; +import fr.xephi.authme.settings.properties.RestrictionSettings; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Player; + +import javax.annotation.PostConstruct; +import javax.inject.Inject; +import java.util.HashSet; +import java.util.Set; + +import static fr.xephi.authme.settings.properties.RestrictionSettings.TELEPORT_UNAUTHED_TO_SPAWN; + +/** + * Handles teleportation (placement of player to spawn). + */ +public class TeleportationService implements Reloadable { + + @Inject + private NewSetting settings; + + @Inject + private Messages messages; + + @Inject + private BukkitService bukkitService; + + @Inject + private SpawnLoader spawnLoader; + + @Inject + private PlayerCache playerCache; + + private Set spawnOnLoginWorlds; + + TeleportationService() { } + + + @PostConstruct + @Override + public void reload() { + // Use a Set for better performance with #contains() + spawnOnLoginWorlds = new HashSet<>(settings.getProperty(RestrictionSettings.FORCE_SPAWN_ON_WORLDS)); + } + + public void teleportOnJoin(final Player player) { + if (settings.getProperty(RestrictionSettings.NO_TELEPORT)) { + return; + } else if (teleportToFirstSpawn(player)) { + return; + } + + if (settings.getProperty(TELEPORT_UNAUTHED_TO_SPAWN) || mustForceSpawnAfterLogin(player.getWorld())) { + teleportToSpawn(player, playerCache.isAuthenticated(player.getName())); + } + } + + public void teleportOnLogin(final Player player, PlayerAuth auth, LimboPlayer limbo) { + if (settings.getProperty(RestrictionSettings.NO_TELEPORT)) { + return; + } + + if (mustForceSpawnAfterLogin(player.getWorld())) { + teleportToSpawn(player, true); + } else if (settings.getProperty(TELEPORT_UNAUTHED_TO_SPAWN)) { + if (settings.getProperty(RestrictionSettings.SAVE_QUIT_LOCATION) && auth.getQuitLocY() != 0) { + Location location = buildLocationFromAuth(player, auth); + teleportBackFromSpawn(player, location); + } else { + teleportBackFromSpawn(player, limbo.getLoc()); + } + } + } + + private boolean teleportToFirstSpawn(final Player player) { + if (player.hasPlayedBefore()) { + return false; + } + Location firstSpawn = spawnLoader.getFirstSpawn(); + if (firstSpawn == null) { + return false; + } + + performTeleportation(player, new FirstSpawnTeleportEvent(player, player.getLocation(), firstSpawn)); + return true; + } + + private static boolean isEventValid(AbstractTeleportEvent event) { + return !event.isCancelled() && event.getTo() != null && event.getTo().getWorld() != null; + } + + private Location buildLocationFromAuth(Player player, PlayerAuth auth) { + World world = bukkitService.getWorld(auth.getWorld()); + if (world == null) { + world = player.getWorld(); + } + return new Location(world, auth.getQuitLocX(), auth.getQuitLocY(), auth.getQuitLocZ()); + } + + private void teleportBackFromSpawn(final Player player, final Location location) { + performTeleportation(player, new AuthMeTeleportEvent(player, location)); + } + + private void teleportToSpawn(final Player player, final boolean isAuthenticated) { + final Location spawnLoc = spawnLoader.getSpawnLocation(player); + performTeleportation(player, new SpawnTeleportEvent(player, player.getLocation(), spawnLoc, isAuthenticated)); + } + + /** + * Emits the teleportation event and performs teleportation according to it (potentially modified + * by external listeners). Note that not teleportation is performed if the event's location is empty. + * + * @param player the player to teleport + * @param event the event to emit and according to which to teleport + */ + private void performTeleportation(final Player player, final AbstractTeleportEvent event) { + bukkitService.scheduleSyncDelayedTask(new Runnable() { + @Override + public void run() { + bukkitService.callEvent(event); + if (player.isOnline() && isEventValid(event)) { + player.teleport(event.getTo()); + } + } + }); + } + + private boolean mustForceSpawnAfterLogin(World world) { + return settings.getProperty(RestrictionSettings.FORCE_SPAWN_LOCATION_AFTER_LOGIN) + && spawnOnLoginWorlds.contains(world.getName()); + } +} diff --git a/src/main/java/fr/xephi/authme/util/Utils.java b/src/main/java/fr/xephi/authme/util/Utils.java index 32cbeb9a..de935bb3 100644 --- a/src/main/java/fr/xephi/authme/util/Utils.java +++ b/src/main/java/fr/xephi/authme/util/Utils.java @@ -7,10 +7,8 @@ import fr.xephi.authme.cache.limbo.LimboPlayer; import fr.xephi.authme.events.AuthMeTeleportEvent; import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.settings.Settings; -import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.OfflinePlayer; -import org.bukkit.World; import org.bukkit.entity.Player; import java.util.Arrays; @@ -117,6 +115,7 @@ public final class Utils { return permsMan.addGroup(player, group); } + @Deprecated public static boolean isUnrestricted(Player player) { // TODO ljacqu 20160602: Checking for Settings.isAllowRestrictedIp is wrong! Nothing in the config suggests // that this setting has anything to do with unrestricted names @@ -124,32 +123,7 @@ public final class Utils { && Settings.getUnrestrictedName.contains(player.getName().toLowerCase()); } - public static void packCoords(double x, double y, double z, String w, final Player pl) { - World theWorld; - if (w.equals("unavailableworld")) { - theWorld = pl.getWorld(); - } else { - theWorld = Bukkit.getWorld(w); - } - if (theWorld == null) { - theWorld = pl.getWorld(); - } - final World world = theWorld; - final Location loc = new Location(world, x, y, z); - - Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() { - - @Override - public void run() { - AuthMeTeleportEvent tpEvent = new AuthMeTeleportEvent(pl, loc); - plugin.getServer().getPluginManager().callEvent(tpEvent); - if (!tpEvent.isCancelled()) { - pl.teleport(tpEvent.getTo()); - } - } - }); - } - + @Deprecated public static void teleportToSpawn(Player player) { if (Settings.isTeleportToSpawnEnabled && !Settings.noTeleport) { Location spawn = plugin.getSpawnLocation(player); diff --git a/src/test/java/fr/xephi/authme/TestHelper.java b/src/test/java/fr/xephi/authme/TestHelper.java index cc9480a4..e9cf35ba 100644 --- a/src/test/java/fr/xephi/authme/TestHelper.java +++ b/src/test/java/fr/xephi/authme/TestHelper.java @@ -72,6 +72,20 @@ public final class TestHelper { runnable.run(); } + /** + * Execute a {@link Runnable} passed to a mock's {@link BukkitService#scheduleSyncDelayedTask(Runnable)} + * method. Note that calling this method expects that there be a runnable sent to the method and will fail + * otherwise. + * + * @param service The mock service + */ + public static void runSyncDelayedTask(BukkitService service) { + ArgumentCaptor captor = ArgumentCaptor.forClass(Runnable.class); + verify(service).scheduleSyncDelayedTask(captor.capture()); + Runnable runnable = captor.getValue(); + runnable.run(); + } + /** * Execute a {@link Runnable} passed to a mock's {@link BukkitService#scheduleSyncDelayedTask(Runnable, long)} * method. Note that calling this method expects that there be a runnable sent to the method and will fail diff --git a/src/test/java/fr/xephi/authme/process/ProcessServiceTest.java b/src/test/java/fr/xephi/authme/process/ProcessServiceTest.java index 164494a9..c0505113 100644 --- a/src/test/java/fr/xephi/authme/process/ProcessServiceTest.java +++ b/src/test/java/fr/xephi/authme/process/ProcessServiceTest.java @@ -10,8 +10,6 @@ import fr.xephi.authme.settings.properties.SecuritySettings; import fr.xephi.authme.util.ValidationService; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -import org.bukkit.event.Event; -import org.bukkit.plugin.PluginManager; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; @@ -42,9 +40,6 @@ public class ProcessServiceTest { @Mock private Messages messages; - @Mock - private PluginManager pluginManager; - @Mock private PermissionsManager permissionsManager; @@ -157,18 +152,6 @@ public class ProcessServiceTest { verify(validationService).isEmailFreeForRegistration(email, sender); } - @Test - public void shouldEmitEvent() { - // given - Event event = mock(Event.class); - - // when - processService.callEvent(event); - - // then - verify(pluginManager).callEvent(event); - } - @Test public void shouldCheckPermission() { // given diff --git a/src/test/java/fr/xephi/authme/util/TeleportationServiceTest.java b/src/test/java/fr/xephi/authme/util/TeleportationServiceTest.java new file mode 100644 index 00000000..6309bfa9 --- /dev/null +++ b/src/test/java/fr/xephi/authme/util/TeleportationServiceTest.java @@ -0,0 +1,421 @@ +package fr.xephi.authme.util; + +import fr.xephi.authme.cache.auth.PlayerAuth; +import fr.xephi.authme.cache.auth.PlayerCache; +import fr.xephi.authme.cache.limbo.LimboPlayer; +import fr.xephi.authme.events.FirstSpawnTeleportEvent; +import fr.xephi.authme.events.SpawnTeleportEvent; +import fr.xephi.authme.output.Messages; +import fr.xephi.authme.settings.NewSetting; +import fr.xephi.authme.settings.SpawnLoader; +import fr.xephi.authme.settings.properties.RestrictionSettings; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.stubbing.Answer; + +import java.util.Arrays; + +import static fr.xephi.authme.TestHelper.runSyncDelayedTask; +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * Test for {@link TeleportationService}. + */ +@RunWith(MockitoJUnitRunner.class) +public class TeleportationServiceTest { + + @InjectMocks + private TeleportationService teleportationService; + + @Mock + private NewSetting settings; + + @Mock + private Messages messages; + + @Mock + private BukkitService bukkitService; + + @Mock + private SpawnLoader spawnLoader; + + @Mock + private PlayerCache playerCache; + + @Before + public void setUpForcedWorlds() { + given(settings.getProperty(RestrictionSettings.FORCE_SPAWN_ON_WORLDS)) + .willReturn(Arrays.asList("forced1", "OtherForced")); + teleportationService.reload(); + + given(settings.getProperty(RestrictionSettings.NO_TELEPORT)).willReturn(false); + } + + // ----------- + // JOINING + // ----------- + @Test + public void shouldNotTeleportPlayerOnJoin() { + // given + given(settings.getProperty(RestrictionSettings.NO_TELEPORT)).willReturn(true); + Player player = mock(Player.class); + + // when + teleportationService.teleportOnJoin(player); + + // then + verifyZeroInteractions(player); + verifyZeroInteractions(bukkitService); + } + + @Test + public void shouldTeleportPlayerToFirstSpawn() { + // given + Player player = mock(Player.class); + given(player.hasPlayedBefore()).willReturn(false); + given(player.isOnline()).willReturn(true); + Location firstSpawn = mockLocation(); + given(spawnLoader.getFirstSpawn()).willReturn(firstSpawn); + + // when + teleportationService.teleportOnJoin(player); + runSyncDelayedTask(bukkitService); + + // then + verify(player).teleport(firstSpawn); + verify(bukkitService).callEvent(any(FirstSpawnTeleportEvent.class)); + verify(spawnLoader).getFirstSpawn(); + verify(spawnLoader, never()).getSpawnLocation(any(Player.class)); + } + + @Test + public void shouldTeleportPlayerToSpawn() { + // given + given(settings.getProperty(RestrictionSettings.TELEPORT_UNAUTHED_TO_SPAWN)).willReturn(true); + Player player = mock(Player.class); + given(player.hasPlayedBefore()).willReturn(true); + given(player.isOnline()).willReturn(true); + Location spawn = mockLocation(); + given(spawnLoader.getSpawnLocation(player)).willReturn(spawn); + + // when + teleportationService.teleportOnJoin(player); + runSyncDelayedTask(bukkitService); + + // then + verify(player).teleport(spawn); + verify(bukkitService).callEvent(any(SpawnTeleportEvent.class)); + verify(spawnLoader).getSpawnLocation(player); + } + + @Test + // No first spawn defined, no teleport settings enabled + public void shouldNotTeleportNewPlayer() { + // given + Player player = mock(Player.class); + given(player.hasPlayedBefore()).willReturn(false); + given(player.isOnline()).willReturn(true); + given(settings.getProperty(RestrictionSettings.TELEPORT_UNAUTHED_TO_SPAWN)).willReturn(false); + given(settings.getProperty(RestrictionSettings.FORCE_SPAWN_LOCATION_AFTER_LOGIN)).willReturn(false); + given(spawnLoader.getFirstSpawn()).willReturn(null); + + // when + teleportationService.teleportOnJoin(player); + + // then + verify(player, never()).teleport(any(Location.class)); + verify(spawnLoader).getFirstSpawn(); + verify(spawnLoader, never()).getSpawnLocation(any(Player.class)); + verifyZeroInteractions(bukkitService); + } + + @Test + public void shouldTeleportPlayerDueToForcedWorld() { + // given + Player player = mock(Player.class); + given(player.hasPlayedBefore()).willReturn(true); + given(player.isOnline()).willReturn(true); + + World playerWorld = mock(World.class); + given(playerWorld.getName()).willReturn("OtherForced"); + given(player.getWorld()).willReturn(playerWorld); + given(settings.getProperty(RestrictionSettings.TELEPORT_UNAUTHED_TO_SPAWN)).willReturn(false); + given(settings.getProperty(RestrictionSettings.FORCE_SPAWN_LOCATION_AFTER_LOGIN)).willReturn(true); + + Location spawn = mockLocation(); + given(spawnLoader.getSpawnLocation(player)).willReturn(spawn); + + // when + teleportationService.teleportOnJoin(player); + runSyncDelayedTask(bukkitService); + + // then + verify(player).teleport(spawn); + verify(bukkitService).callEvent(any(SpawnTeleportEvent.class)); + verify(spawnLoader).getSpawnLocation(player); + } + + @Test + public void shouldNotTeleportPlayerForRemovedLocationInEvent() { + // given + final Player player = mock(Player.class); + given(player.hasPlayedBefore()).willReturn(true); + Location spawn = mockLocation(); + given(spawnLoader.getSpawnLocation(player)).willReturn(spawn); + given(settings.getProperty(RestrictionSettings.TELEPORT_UNAUTHED_TO_SPAWN)).willReturn(true); + doAnswer(new Answer() { + @Override + public Void answer(InvocationOnMock invocation) throws Throwable { + SpawnTeleportEvent event = (SpawnTeleportEvent) invocation.getArguments()[0]; + assertThat(event.getPlayer(), equalTo(player)); + event.setTo(null); + return null; + } + }).when(bukkitService).callEvent(any(SpawnTeleportEvent.class)); + + // when + teleportationService.teleportOnJoin(player); + runSyncDelayedTask(bukkitService); + + // then + verify(bukkitService).callEvent(any(SpawnTeleportEvent.class)); + verify(player, never()).teleport(any(Location.class)); + } + + @Test + public void shouldNotTeleportPlayerForCanceledEvent() { + // given + final Player player = mock(Player.class); + given(player.hasPlayedBefore()).willReturn(true); + Location spawn = mockLocation(); + given(spawnLoader.getSpawnLocation(player)).willReturn(spawn); + given(settings.getProperty(RestrictionSettings.TELEPORT_UNAUTHED_TO_SPAWN)).willReturn(true); + doAnswer(new Answer() { + @Override + public Void answer(InvocationOnMock invocation) throws Throwable { + SpawnTeleportEvent event = (SpawnTeleportEvent) invocation.getArguments()[0]; + assertThat(event.getPlayer(), equalTo(player)); + event.setCancelled(true); + return null; + } + }).when(bukkitService).callEvent(any(SpawnTeleportEvent.class)); + + // when + teleportationService.teleportOnJoin(player); + runSyncDelayedTask(bukkitService); + + // then + verify(bukkitService).callEvent(any(SpawnTeleportEvent.class)); + verify(player, never()).teleport(any(Location.class)); + } + + + // --------- + // LOGIN + // --------- + @Test + public void shouldNotTeleportUponLogin() { + // given + given(settings.getProperty(RestrictionSettings.NO_TELEPORT)).willReturn(true); + Player player = mock(Player.class); + PlayerAuth auth = mock(PlayerAuth.class); + LimboPlayer limbo = mock(LimboPlayer.class); + + // when + teleportationService.teleportOnLogin(player, auth, limbo); + + // then + verifyZeroInteractions(player, auth, limbo, bukkitService, spawnLoader); + } + + @Test + public void shouldTeleportPlayerToSpawnAfterLogin() { + // given + given(settings.getProperty(RestrictionSettings.FORCE_SPAWN_LOCATION_AFTER_LOGIN)).willReturn(true); + World world = mock(World.class); + given(world.getName()).willReturn("forced1"); + Player player = mock(Player.class); + given(player.getWorld()).willReturn(world); + given(player.isOnline()).willReturn(true); + Location spawn = mockLocation(); + given(spawnLoader.getSpawnLocation(player)).willReturn(spawn); + PlayerAuth auth = mock(PlayerAuth.class); + LimboPlayer limbo = mock(LimboPlayer.class); + + // when + teleportationService.teleportOnLogin(player, auth, limbo); + runSyncDelayedTask(bukkitService); + + // then + verify(player).teleport(spawn); + } + + @Test + // Check that the worlds for "force spawn loc after login" are case-sensitive + public void shouldNotTeleportToSpawnForOtherCaseInWorld() { + // given + given(settings.getProperty(RestrictionSettings.FORCE_SPAWN_LOCATION_AFTER_LOGIN)).willReturn(true); + given(settings.getProperty(RestrictionSettings.TELEPORT_UNAUTHED_TO_SPAWN)).willReturn(false); + World world = mock(World.class); + given(world.getName()).willReturn("Forced1"); // different case + Player player = mock(Player.class); + given(player.getWorld()).willReturn(world); + given(player.isOnline()).willReturn(true); + Location spawn = mockLocation(); + given(spawnLoader.getSpawnLocation(player)).willReturn(spawn); + PlayerAuth auth = mock(PlayerAuth.class); + LimboPlayer limbo = mock(LimboPlayer.class); + + // when + teleportationService.teleportOnLogin(player, auth, limbo); + + // then + verify(player, never()).teleport(spawn); + verifyZeroInteractions(bukkitService, spawnLoader); + } + + @Test + public void shouldTeleportBackToPlayerAuthLocation() { + // given + given(settings.getProperty(RestrictionSettings.FORCE_SPAWN_LOCATION_AFTER_LOGIN)).willReturn(false); + given(settings.getProperty(RestrictionSettings.TELEPORT_UNAUTHED_TO_SPAWN)).willReturn(true); + given(settings.getProperty(RestrictionSettings.SAVE_QUIT_LOCATION)).willReturn(true); + + PlayerAuth auth = createAuthWithLocation(); + auth.setWorld("myWorld"); + World world = mock(World.class); + given(bukkitService.getWorld("myWorld")).willReturn(world); + + Player player = mock(Player.class); + given(player.isOnline()).willReturn(true); + LimboPlayer limbo = mock(LimboPlayer.class); + + // when + teleportationService.teleportOnLogin(player, auth, limbo); + runSyncDelayedTask(bukkitService); + + // then + ArgumentCaptor locationCaptor = ArgumentCaptor.forClass(Location.class); + verify(player).teleport(locationCaptor.capture()); + assertCorrectLocation(locationCaptor.getValue(), auth, world); + } + + @Test + public void shouldTeleportAccordingToPlayerAuthAndPlayerWorldAsFallback() { + // given + given(settings.getProperty(RestrictionSettings.FORCE_SPAWN_LOCATION_AFTER_LOGIN)).willReturn(false); + given(settings.getProperty(RestrictionSettings.TELEPORT_UNAUTHED_TO_SPAWN)).willReturn(true); + given(settings.getProperty(RestrictionSettings.SAVE_QUIT_LOCATION)).willReturn(true); + + PlayerAuth auth = createAuthWithLocation(); + auth.setWorld("myWorld"); + given(bukkitService.getWorld("myWorld")).willReturn(null); + + Player player = mock(Player.class); + given(player.isOnline()).willReturn(true); + World world = mock(World.class); + given(player.getWorld()).willReturn(world); + LimboPlayer limbo = mock(LimboPlayer.class); + + // when + teleportationService.teleportOnLogin(player, auth, limbo); + runSyncDelayedTask(bukkitService); + + // then + ArgumentCaptor locationCaptor = ArgumentCaptor.forClass(Location.class); + verify(player).teleport(locationCaptor.capture()); + assertCorrectLocation(locationCaptor.getValue(), auth, world); + } + + @Test + public void shouldTeleportWithLimboPlayerIfAuthYCoordIsNotSet() { + // given + given(settings.getProperty(RestrictionSettings.FORCE_SPAWN_LOCATION_AFTER_LOGIN)).willReturn(false); + given(settings.getProperty(RestrictionSettings.TELEPORT_UNAUTHED_TO_SPAWN)).willReturn(true); + given(settings.getProperty(RestrictionSettings.SAVE_QUIT_LOCATION)).willReturn(true); + + PlayerAuth auth = createAuthWithLocation(); + auth.setQuitLocY(0.0); + auth.setWorld("authWorld"); + Player player = mock(Player.class); + given(player.isOnline()).willReturn(true); + World world = mock(World.class); + given(player.getWorld()).willReturn(world); + LimboPlayer limbo = mock(LimboPlayer.class); + Location location = mockLocation(); + given(limbo.getLoc()).willReturn(location); + + // when + teleportationService.teleportOnLogin(player, auth, limbo); + runSyncDelayedTask(bukkitService); + + // then + verify(player).teleport(location); + verify(bukkitService, never()).getWorld(anyString()); + } + + @Test + public void shouldTeleportWithLimboPlayerIfSaveQuitLocIsDisabled() { + // given + given(settings.getProperty(RestrictionSettings.FORCE_SPAWN_LOCATION_AFTER_LOGIN)).willReturn(false); + given(settings.getProperty(RestrictionSettings.TELEPORT_UNAUTHED_TO_SPAWN)).willReturn(true); + given(settings.getProperty(RestrictionSettings.SAVE_QUIT_LOCATION)).willReturn(false); + + PlayerAuth auth = createAuthWithLocation(); + Player player = mock(Player.class); + given(player.isOnline()).willReturn(true); + World world = mock(World.class); + given(player.getWorld()).willReturn(world); + LimboPlayer limbo = mock(LimboPlayer.class); + Location location = mockLocation(); + given(limbo.getLoc()).willReturn(location); + + // when + teleportationService.teleportOnLogin(player, auth, limbo); + runSyncDelayedTask(bukkitService); + + // then + verify(player).teleport(location); + } + + + // We check that the World in Location is set, this method creates a mock World in Location for us + private static Location mockLocation() { + Location location = mock(Location.class); + given(location.getWorld()).willReturn(mock(World.class)); + return location; + } + + private static PlayerAuth createAuthWithLocation() { + return PlayerAuth.builder() + .name("bobby") + .locX(123.45).locY(23.4).locZ(-4.567) + .build(); + } + + private void assertCorrectLocation(Location location, PlayerAuth auth, World world) { + assertThat(location.getX(), equalTo(auth.getQuitLocX())); + assertThat(location.getY(), equalTo(auth.getQuitLocY())); + assertThat(location.getZ(), equalTo(auth.getQuitLocZ())); + assertThat(location.getWorld(), equalTo(world)); + } + +}