Listener optimizations (#1884)

* Drop CraftBukkit support, listeners cleanup

* Codestyle

* Codestyle

* Remove useless player.saveData() calls

* Micro optimization on the join process

* Requested changes

* Fix unit test

* Test onPlayerHeldItem listener

* Requested changes

* Remove unused import
This commit is contained in:
Gabriele C 2019-08-11 23:47:50 +02:00 committed by GitHub
parent e2f2ff0763
commit d30580d5d4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 498 additions and 557 deletions

View File

@ -125,8 +125,10 @@ public class AuthMe extends JavaPlugin {
logger = ConsoleLoggerFactory.get(AuthMe.class);
// Check server version
if (!isClassLoaded("org.bukkit.event.player.PlayerInteractAtEntityEvent")) {
logger.warning("You are running an unsupported server version! AuthMe requires MC 1.8.X or later!");
if (!isClassLoaded("org.spigotmc.event.player.PlayerSpawnLocationEvent")
|| !isClassLoaded("org.bukkit.event.player.PlayerInteractAtEntityEvent")) {
logger.warning("You are running an unsupported server version!"
+ "AuthMe requires Spigot 1.8.X or later!");
stopOrUnload();
return;
}

View File

@ -25,5 +25,4 @@ public class BlockListener implements Listener {
event.setCancelled(true);
}
}
}

View File

@ -1,8 +1,5 @@
package fr.xephi.authme.listener;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.output.ConsoleLoggerFactory;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.entity.Projectile;
import org.bukkit.event.EventHandler;
@ -16,32 +13,19 @@ import org.bukkit.event.entity.EntityShootBowEvent;
import org.bukkit.event.entity.EntityTargetEvent;
import org.bukkit.event.entity.FoodLevelChangeEvent;
import org.bukkit.event.entity.ProjectileLaunchEvent;
import org.bukkit.projectiles.ProjectileSource;
import javax.inject.Inject;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class EntityListener implements Listener {
private final ConsoleLogger logger = ConsoleLoggerFactory.get(EntityListener.class);
private final ListenerService listenerService;
private Method getShooter;
private boolean shooterIsLivingEntity;
@Inject
EntityListener(ListenerService listenerService) {
this.listenerService = listenerService;
try {
getShooter = Projectile.class.getDeclaredMethod("getShooter");
shooterIsLivingEntity = getShooter.getReturnType() == LivingEntity.class;
} catch (NoSuchMethodException | SecurityException e) {
logger.logException("Cannot load getShooter() method on Projectile class", e);
}
}
// Note #360: npc status can be used to bypass security!!!
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onDamage(EntityDamageEvent event) {
if (listenerService.shouldCancelEvent(event)) {
@ -81,6 +65,7 @@ public class EntityListener implements Listener {
}
}
//TODO sgdc3 20190808: We listen at the same event twice, does it make any sense?
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
public void onEntityInteract(EntityInteractEvent event) {
if (listenerService.shouldCancelEvent(event)) {
@ -97,26 +82,10 @@ public class EntityListener implements Listener {
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onProjectileLaunch(ProjectileLaunchEvent event) {
if (event.getEntity() == null) {
return;
}
final Projectile projectile = event.getEntity();
Projectile projectile = event.getEntity();
// In the Bukkit API prior to 1.7, getShooter() returns a LivingEntity instead of a ProjectileSource
Object shooterRaw = null;
if (shooterIsLivingEntity) {
try {
if (getShooter == null) {
getShooter = Projectile.class.getMethod("getShooter");
}
shooterRaw = getShooter.invoke(projectile);
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
logger.logException("Error getting shooter", e);
}
} else {
shooterRaw = projectile.getShooter();
}
if (shooterRaw instanceof Player && listenerService.shouldCancelEvent((Player) shooterRaw)) {
ProjectileSource shooter = projectile.getShooter();
if (shooter instanceof Player && listenerService.shouldCancelEvent((Player) shooter)) {
event.setCancelled(true);
}
}
@ -127,5 +96,4 @@ public class EntityListener implements Listener {
event.setCancelled(true);
}
}
}

View File

@ -1,66 +0,0 @@
package fr.xephi.authme.listener;
import fr.xephi.authme.permission.PermissionNode;
import fr.xephi.authme.permission.PermissionsManager;
import org.bukkit.entity.Player;
import java.util.function.BiFunction;
/**
* Represents a player joining the server, which depending on the available
* information may be his name or the actual Player object.
*/
public final class JoiningPlayer {
private final String name;
private final BiFunction<PermissionsManager, PermissionNode, Boolean> permissionLookupFunction;
/**
* Hidden constructor.
*
* @param name the player's name
* @param permFunction the function to use for permission lookups
*/
private JoiningPlayer(String name, BiFunction<PermissionsManager, PermissionNode, Boolean> permFunction) {
this.name = name;
this.permissionLookupFunction = permFunction;
}
/**
* Creates a {@link JoiningPlayer} instance from the given name.
*
* @param name the player's name
* @return the created instance
*/
public static JoiningPlayer fromName(String name) {
return new JoiningPlayer(name, (manager, perm) -> manager.hasPermissionOffline(name, perm));
}
/**
* Creates a {@link JoiningPlayer} instance from the given Player object.
*
* @param player the player
* @return the created instance
*/
public static JoiningPlayer fromPlayerObject(Player player) {
return new JoiningPlayer(player.getName(), (manager, perm) -> manager.hasPermission(player, perm));
}
/**
* @return the player's name
*/
public String getName() {
return name;
}
/**
* Returns the function to use for permission lookups. Takes two arguments: the PermissionsManager instance,
* and the permission node to look up. The result is a boolean indicating whether or not this joining player
* has permission.
*
* @return the permissions lookup function to use
*/
public BiFunction<PermissionsManager, PermissionNode, Boolean> getPermissionLookupFunction() {
return permissionLookupFunction;
}
}

View File

@ -31,7 +31,7 @@ import java.util.regex.Pattern;
* Service for performing various verifications when a player joins.
*/
public class OnJoinVerifier implements Reloadable {
private final ConsoleLogger logger = ConsoleLoggerFactory.get(OnJoinVerifier.class);
@Inject
@ -67,16 +67,16 @@ public class OnJoinVerifier implements Reloadable {
/**
* Checks if Antibot is enabled.
*
* @param joiningPlayer the joining player to check
* @param name the joining player name to check
* @param isAuthAvailable whether or not the player is registered
* @throws FailedVerificationException if the verification fails
*/
public void checkAntibot(JoiningPlayer joiningPlayer, boolean isAuthAvailable) throws FailedVerificationException {
if (isAuthAvailable || permissionsManager.hasPermission(joiningPlayer, PlayerStatePermission.BYPASS_ANTIBOT)) {
public void checkAntibot(String name, boolean isAuthAvailable) throws FailedVerificationException {
if (isAuthAvailable || permissionsManager.hasPermissionOffline(name, PlayerStatePermission.BYPASS_ANTIBOT)) {
return;
}
if (antiBotService.shouldKick()) {
antiBotService.addPlayerKick(joiningPlayer.getName());
antiBotService.addPlayerKick(name);
throw new FailedVerificationException(MessageKey.KICK_ANTIBOT);
}
}
@ -170,16 +170,16 @@ public class OnJoinVerifier implements Reloadable {
/**
* Checks that the player's country is admitted.
*
* @param joiningPlayer the joining player to verify
* @param name the joining player name to verify
* @param address the player address
* @param isAuthAvailable whether or not the user is registered
* @throws FailedVerificationException if the verification fails
*/
public void checkPlayerCountry(JoiningPlayer joiningPlayer, String address,
public void checkPlayerCountry(String name, String address,
boolean isAuthAvailable) throws FailedVerificationException {
if ((!isAuthAvailable || settings.getProperty(ProtectionSettings.ENABLE_PROTECTION_REGISTERED))
&& settings.getProperty(ProtectionSettings.ENABLE_PROTECTION)
&& !permissionsManager.hasPermission(joiningPlayer, PlayerStatePermission.BYPASS_COUNTRY_CHECK)
&& !permissionsManager.hasPermissionOffline(name, PlayerStatePermission.BYPASS_COUNTRY_CHECK)
&& !validationService.isCountryAdmitted(address)) {
throw new FailedVerificationException(MessageKey.COUNTRY_BANNED_ERROR);
}

View File

@ -1,30 +1,23 @@
package fr.xephi.authme.listener;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.data.QuickCommandsProtectionManager;
import fr.xephi.authme.data.auth.PlayerAuth;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.output.ConsoleLoggerFactory;
import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.message.Messages;
import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.permission.PlayerStatePermission;
import fr.xephi.authme.permission.handlers.PermissionLoadUserException;
import fr.xephi.authme.process.Management;
import fr.xephi.authme.service.AntiBotService;
import fr.xephi.authme.service.BukkitService;
import fr.xephi.authme.service.JoinMessageService;
import fr.xephi.authme.service.TeleportationService;
import fr.xephi.authme.service.ValidationService;
import fr.xephi.authme.service.bungeecord.BungeeSender;
import fr.xephi.authme.service.bungeecord.MessageType;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.SpawnLoader;
import fr.xephi.authme.settings.properties.HooksSettings;
import fr.xephi.authme.settings.properties.PluginSettings;
import fr.xephi.authme.settings.properties.RegistrationSettings;
import fr.xephi.authme.settings.properties.RestrictionSettings;
import fr.xephi.authme.util.PlayerUtils;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.entity.HumanEntity;
@ -59,7 +52,6 @@ import org.bukkit.event.player.PlayerShearEntityEvent;
import org.bukkit.inventory.InventoryView;
import javax.inject.Inject;
import java.util.HashSet;
import java.util.Set;
import static fr.xephi.authme.settings.properties.RestrictionSettings.ALLOWED_MOVEMENT_RADIUS;
@ -70,8 +62,6 @@ import static fr.xephi.authme.settings.properties.RestrictionSettings.ALLOW_UNAU
*/
public class PlayerListener implements Listener {
private final ConsoleLogger logger = ConsoleLoggerFactory.get(PlayerListener.class);
@Inject
private Settings settings;
@Inject
@ -100,106 +90,104 @@ public class PlayerListener implements Listener {
private PermissionsManager permissionsManager;
@Inject
private QuickCommandsProtectionManager quickCommandsProtectionManager;
@Inject
private BungeeSender bungeeSender;
private boolean isAsyncPlayerPreLoginEventCalled = false;
private Set<String> unresolvedPlayerHostname = new HashSet<>();
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) {
String cmd = event.getMessage().split(" ")[0].toLowerCase();
if (settings.getProperty(HooksSettings.USE_ESSENTIALS_MOTD) && "/motd".equals(cmd)) {
// Lowest priority to apply fast protection checks
@EventHandler(priority = EventPriority.LOWEST)
public void onAsyncPlayerPreLoginEventLowest(AsyncPlayerPreLoginEvent event) {
if (event.getLoginResult() != AsyncPlayerPreLoginEvent.Result.ALLOWED) {
return;
}
if (settings.getProperty(RestrictionSettings.ALLOW_COMMANDS).contains(cmd)) {
final String name = event.getName();
// NOTE: getAddress() sometimes returning null, we don't want to handle this race condition
if (event.getAddress() == null) {
event.disallow(AsyncPlayerPreLoginEvent.Result.KICK_OTHER,
messages.retrieveSingle(name, MessageKey.KICK_UNRESOLVED_HOSTNAME));
return;
}
if (validationService.isUnrestricted(name)) {
return;
}
// Non-blocking checks
try {
onJoinVerifier.checkSingleSession(name);
onJoinVerifier.checkIsValidName(name);
} catch (FailedVerificationException e) {
event.setKickMessage(messages.retrieveSingle(name, e.getReason(), e.getArgs()));
event.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_OTHER);
}
}
/*
* Login/join/leave events
*/
// Note: at this stage (HIGHEST priority) the user's permission data should already have been loaded by
// the permission handler, we don't need to call permissionsManager.loadUserData()
@EventHandler(priority = EventPriority.HIGHEST)
public void onAsyncPlayerPreLoginEventHighest(AsyncPlayerPreLoginEvent event) {
if (event.getLoginResult() != AsyncPlayerPreLoginEvent.Result.ALLOWED) {
return;
}
final String name = event.getName();
if (validationService.isUnrestricted(name)) {
return;
}
// Slow, blocking checks
try {
final PlayerAuth auth = dataSource.getAuth(name);
final boolean isAuthAvailable = auth != null;
onJoinVerifier.checkKickNonRegistered(isAuthAvailable);
onJoinVerifier.checkAntibot(name, isAuthAvailable);
onJoinVerifier.checkNameCasing(name, auth);
final String ip = event.getAddress().getHostAddress();
onJoinVerifier.checkPlayerCountry(name, ip, isAuthAvailable);
} catch (FailedVerificationException e) {
event.setKickMessage(messages.retrieveSingle(name, e.getReason(), e.getArgs()));
event.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_OTHER);
}
}
// Note: We can't teleport the player in the PlayerLoginEvent listener
// as the new player location will be reverted by the server.
@EventHandler(priority = EventPriority.LOW)
public void onPlayerLogin(PlayerLoginEvent event) {
final Player player = event.getPlayer();
if (!quickCommandsProtectionManager.isAllowed(player.getName())) {
event.setCancelled(true);
player.kickPlayer(messages.retrieveSingle(player, MessageKey.QUICK_COMMAND_PROTECTION_KICK));
final String name = player.getName();
if (validationService.isUnrestricted(name)) {
return;
}
if (listenerService.shouldCancelEvent(player)) {
event.setCancelled(true);
messages.send(player, MessageKey.DENIED_COMMAND);
}
onJoinVerifier.refusePlayerForFullServer(event);
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onPlayerChat(AsyncPlayerChatEvent event) {
if (settings.getProperty(RestrictionSettings.ALLOW_CHAT)) {
return;
}
@EventHandler(priority = EventPriority.NORMAL)
public void onPlayerJoin(PlayerJoinEvent event) {
final Player player = event.getPlayer();
final boolean mayPlayerSendChat = !listenerService.shouldCancelEvent(player)
|| permissionsManager.hasPermission(player, PlayerStatePermission.ALLOW_CHAT_BEFORE_LOGIN);
if (mayPlayerSendChat) {
removeUnauthorizedRecipients(event);
} else {
event.setCancelled(true);
messages.send(player, MessageKey.DENIED_CHAT);
}
}
private void removeUnauthorizedRecipients(AsyncPlayerChatEvent event) {
if (settings.getProperty(RestrictionSettings.HIDE_CHAT)) {
event.getRecipients().removeIf(listenerService::shouldCancelEvent);
if (event.getRecipients().isEmpty()) {
event.setCancelled(true);
}
}
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
public void onPlayerMove(PlayerMoveEvent event) {
if (settings.getProperty(ALLOW_UNAUTHED_MOVEMENT) && settings.getProperty(ALLOWED_MOVEMENT_RADIUS) <= 0) {
return;
if (!PlayerListener19Spigot.isPlayerSpawnLocationEventCalled()) {
teleportationService.teleportOnJoin(player);
}
/*
* Limit player X and Z movements to 1 block
* Deny player Y+ movements (allows falling)
*/
Location from = event.getFrom();
Location to = event.getTo();
if (from.getBlockX() == to.getBlockX()
&& from.getBlockZ() == to.getBlockZ()
&& from.getY() - to.getY() >= 0) {
return;
}
quickCommandsProtectionManager.processJoin(player);
Player player = event.getPlayer();
if (!listenerService.shouldCancelEvent(player)) {
return;
}
management.performJoin(player);
if (!settings.getProperty(RestrictionSettings.ALLOW_UNAUTHED_MOVEMENT)) {
// "cancel" the event
event.setTo(event.getFrom());
return;
}
if (settings.getProperty(RestrictionSettings.NO_TELEPORT)) {
return;
}
Location spawn = spawnLoader.getSpawnLocation(player);
if (spawn != null && spawn.getWorld() != null) {
if (!player.getWorld().equals(spawn.getWorld())) {
player.teleport(spawn);
} else if (spawn.distance(player.getLocation()) > settings.getProperty(ALLOWED_MOVEMENT_RADIUS)) {
player.teleport(spawn);
}
}
teleportationService.teleportNewPlayerToFirstSpawn(player);
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onJoinMessage(PlayerJoinEvent event) {
final Player player = event.getPlayer();
// Note: join message can be null, despite api documentation says not
if (settings.getProperty(RegistrationSettings.REMOVE_JOIN_MESSAGE)) {
event.setJoinMessage(null);
return;
@ -229,131 +217,11 @@ public class PlayerListener implements Listener {
}
}
@EventHandler(priority = EventPriority.NORMAL)
public void onPlayerJoin(PlayerJoinEvent event) {
final Player player = event.getPlayer();
if (unresolvedPlayerHostname.remove(player.getName())) {
try {
runOnJoinChecks(JoiningPlayer.fromPlayerObject(player), PlayerUtils.getPlayerIp(player));
} catch (FailedVerificationException e) {
player.kickPlayer(messages.retrieveSingle(player, e.getReason(), e.getArgs()));
return;
}
}
if (!PlayerListener19Spigot.isPlayerSpawnLocationEventCalled()) {
teleportationService.teleportOnJoin(player);
}
quickCommandsProtectionManager.processJoin(player);
management.performJoin(player);
teleportationService.teleportNewPlayerToFirstSpawn(player);
}
private void runOnJoinChecks(JoiningPlayer joiningPlayer, String ip) throws FailedVerificationException {
// Fast stuff
final String name = joiningPlayer.getName();
onJoinVerifier.checkSingleSession(name);
onJoinVerifier.checkIsValidName(name);
// Get the auth later as this may cause the single session check to fail
// Slow stuff
final PlayerAuth auth = dataSource.getAuth(name);
final boolean isAuthAvailable = auth != null;
onJoinVerifier.checkKickNonRegistered(isAuthAvailable);
onJoinVerifier.checkAntibot(joiningPlayer, isAuthAvailable);
onJoinVerifier.checkNameCasing(name, auth);
onJoinVerifier.checkPlayerCountry(joiningPlayer, ip, isAuthAvailable);
}
// Note #831: AsyncPlayerPreLoginEvent is not fired by all servers in offline mode
// e.g. CraftBukkit does not fire it. So we need to run crucial things with PlayerLoginEvent.
// Single session feature can be implemented for Spigot and CraftBukkit by canceling a kick
// event caused by "logged in from another location". The nicer way, but only for Spigot, would be
// to check in the AsyncPlayerPreLoginEvent. To support all servers, we use the less nice way.
@EventHandler(priority = EventPriority.HIGHEST)
public void onAsyncPlayerPreLoginEvent(AsyncPlayerPreLoginEvent event) {
isAsyncPlayerPreLoginEventCalled = true;
if (!settings.getProperty(PluginSettings.USE_ASYNC_PRE_LOGIN_EVENT)) {
return;
}
final String name = event.getName();
if (validationService.isUnrestricted(name)) {
return;
}
// Keep pre-UUID compatibility
try {
permissionsManager.loadUserData(event.getUniqueId());
} catch (PermissionLoadUserException e) {
logger.logException("Unable to load the permission data of user " + name, e);
}
// getAddress() sometimes returning null if not yet resolved
// skip it and let PlayerLoginEvent to handle it
if (event.getAddress() == null) {
unresolvedPlayerHostname.add(event.getName());
return;
} else {
unresolvedPlayerHostname.remove(event.getName());
}
try {
runOnJoinChecks(JoiningPlayer.fromName(name), event.getAddress().getHostAddress());
} catch (FailedVerificationException e) {
event.setKickMessage(messages.retrieveSingle(name, e.getReason(), e.getArgs()));
event.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_OTHER);
}
}
//Note: We can't teleport the player in the PlayerLoginEvent listener
//as the new player location will be reverted by the server.
@EventHandler(priority = EventPriority.LOW)
public void onPlayerLogin(PlayerLoginEvent event) {
final Player player = event.getPlayer();
final String name = player.getName();
if (validationService.isUnrestricted(name)) {
return;
}
if (onJoinVerifier.refusePlayerForFullServer(event)) {
return;
}
if (event.getResult() != PlayerLoginEvent.Result.ALLOWED) {
return;
}
if (event.getAddress() == null) { // Address still null
unresolvedPlayerHostname.add(name);
return;
} else {
unresolvedPlayerHostname.remove(name);
}
if (!isAsyncPlayerPreLoginEventCalled || !settings.getProperty(PluginSettings.USE_ASYNC_PRE_LOGIN_EVENT)) {
try {
// Player.getAddress() can be null at this event, use event.getAddress()
runOnJoinChecks(JoiningPlayer.fromPlayerObject(player), event.getAddress().getHostAddress());
} catch (FailedVerificationException e) {
event.setKickMessage(messages.retrieveSingle(player, e.getReason(), e.getArgs()));
event.setResult(PlayerLoginEvent.Result.KICK_OTHER);
}
}
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onPlayerQuit(PlayerQuitEvent event) {
Player player = event.getPlayer();
// Note: quit message can be null, despite api documentation says not
if (settings.getProperty(RegistrationSettings.REMOVE_LEAVE_MESSAGE)) {
event.setQuitMessage(null);
} else if (settings.getProperty(RegistrationSettings.REMOVE_UNLOGGED_LEAVE_MESSAGE)) {
@ -385,6 +253,195 @@ public class PlayerListener implements Listener {
}
}
/*
* Chat/command events
*/
private void removeUnauthorizedRecipients(AsyncPlayerChatEvent event) {
if (settings.getProperty(RestrictionSettings.HIDE_CHAT)) {
event.getRecipients().removeIf(listenerService::shouldCancelEvent);
if (event.getRecipients().isEmpty()) {
event.setCancelled(true);
}
}
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onPlayerChat(AsyncPlayerChatEvent event) {
if (settings.getProperty(RestrictionSettings.ALLOW_CHAT)) {
return;
}
final Player player = event.getPlayer();
final boolean mayPlayerSendChat = !listenerService.shouldCancelEvent(player)
|| permissionsManager.hasPermission(player, PlayerStatePermission.ALLOW_CHAT_BEFORE_LOGIN);
if (mayPlayerSendChat) {
removeUnauthorizedRecipients(event);
} else {
event.setCancelled(true);
messages.send(player, MessageKey.DENIED_CHAT);
}
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) {
String cmd = event.getMessage().split(" ")[0].toLowerCase();
if (settings.getProperty(HooksSettings.USE_ESSENTIALS_MOTD) && "/motd".equals(cmd)) {
return;
}
if (settings.getProperty(RestrictionSettings.ALLOW_COMMANDS).contains(cmd)) {
return;
}
final Player player = event.getPlayer();
if (!quickCommandsProtectionManager.isAllowed(player.getName())) {
event.setCancelled(true);
player.kickPlayer(messages.retrieveSingle(player, MessageKey.QUICK_COMMAND_PROTECTION_KICK));
return;
}
if (listenerService.shouldCancelEvent(player)) {
event.setCancelled(true);
messages.send(player, MessageKey.DENIED_COMMAND);
}
}
/*
* Movement events
*/
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
public void onPlayerMove(PlayerMoveEvent event) {
if (settings.getProperty(ALLOW_UNAUTHED_MOVEMENT) && settings.getProperty(ALLOWED_MOVEMENT_RADIUS) <= 0) {
return;
}
Location from = event.getFrom();
Location to = event.getTo();
if (to == null) {
return;
}
/*
* Limit player X and Z movements to 1 block
* Deny player Y+ movements (allows falling)
*/
if (from.getBlockX() == to.getBlockX()
&& from.getBlockZ() == to.getBlockZ()
&& from.getY() - to.getY() >= 0) {
return;
}
Player player = event.getPlayer();
if (!listenerService.shouldCancelEvent(player)) {
return;
}
if (!settings.getProperty(RestrictionSettings.ALLOW_UNAUTHED_MOVEMENT)) {
// "cancel" the event
event.setTo(event.getFrom());
return;
}
if (settings.getProperty(RestrictionSettings.NO_TELEPORT)) {
return;
}
Location spawn = spawnLoader.getSpawnLocation(player);
if (spawn != null && spawn.getWorld() != null) {
if (!player.getWorld().equals(spawn.getWorld())) {
player.teleport(spawn);
} else if (spawn.distance(player.getLocation()) > settings.getProperty(ALLOWED_MOVEMENT_RADIUS)) {
player.teleport(spawn);
}
}
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
public void onPlayerRespawn(PlayerRespawnEvent event) {
if (settings.getProperty(RestrictionSettings.NO_TELEPORT)) {
return;
}
if (!listenerService.shouldCancelEvent(event)) {
return;
}
Location spawn = spawnLoader.getSpawnLocation(event.getPlayer());
if (spawn != null && spawn.getWorld() != null) {
event.setRespawnLocation(spawn);
}
}
/*
* Entity/block interaction events
*/
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onPlayerInteract(PlayerInteractEvent event) {
if (listenerService.shouldCancelEvent(event)) {
event.setCancelled(true);
}
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onPlayerInteractEntity(PlayerInteractEntityEvent event) {
if (listenerService.shouldCancelEvent(event)) {
event.setCancelled(true);
}
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onPlayerInteractAtEntity(PlayerInteractAtEntityEvent event) {
if (listenerService.shouldCancelEvent(event)) {
event.setCancelled(true);
}
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onPlayerHitPlayerEvent(EntityDamageByEntityEvent event) {
if (listenerService.shouldCancelEvent(event)) {
event.setCancelled(true);
}
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onPlayerShear(PlayerShearEntityEvent event) {
if (listenerService.shouldCancelEvent(event)) {
event.setCancelled(true);
}
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onPlayerFish(PlayerFishEvent event) {
if (listenerService.shouldCancelEvent(event)) {
event.setCancelled(true);
}
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onPlayerBedEnter(PlayerBedEnterEvent event) {
if (listenerService.shouldCancelEvent(event)) {
event.setCancelled(true);
}
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onPlayerEditBook(PlayerEditBookEvent event) {
if (listenerService.shouldCancelEvent(event)) {
event.setCancelled(true);
}
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onSignChange(SignChangeEvent event) {
Player player = event.getPlayer();
if (listenerService.shouldCancelEvent(player)) {
event.setCancelled(true);
}
}
/*
* Inventory interactions
*/
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onPlayerPickupItem(PlayerPickupItemEvent event) {
if (listenerService.shouldCancelEvent(event)) {
@ -393,14 +450,14 @@ public class PlayerListener implements Listener {
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onPlayerHeldItem(PlayerItemHeldEvent event) {
public void onPlayerDropItem(PlayerDropItemEvent event) {
if (listenerService.shouldCancelEvent(event)) {
event.setCancelled(true);
}
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onPlayerInteract(PlayerInteractEvent event) {
public void onPlayerHeldItem(PlayerItemHeldEvent event) {
if (listenerService.shouldCancelEvent(event)) {
event.setCancelled(true);
}
@ -444,95 +501,4 @@ public class PlayerListener implements Listener {
event.setCancelled(true);
}
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onPlayerHitPlayerEvent(EntityDamageByEntityEvent event) {
if (listenerService.shouldCancelEvent(event)) {
event.setCancelled(true);
}
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onPlayerInteractEntity(PlayerInteractEntityEvent event) {
if (listenerService.shouldCancelEvent(event)) {
event.setCancelled(true);
}
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onPlayerDropItem(PlayerDropItemEvent event) {
if (listenerService.shouldCancelEvent(event)) {
event.setCancelled(true);
}
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onPlayerBedEnter(PlayerBedEnterEvent event) {
if (listenerService.shouldCancelEvent(event)) {
event.setCancelled(true);
}
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onSignChange(SignChangeEvent event) {
Player player = event.getPlayer();
if (listenerService.shouldCancelEvent(player)) {
event.setCancelled(true);
}
}
//TODO: check this, why do we need to update the quit loc? -sgdc3
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
public void onPlayerRespawn(PlayerRespawnEvent event) {
if (settings.getProperty(RestrictionSettings.NO_TELEPORT)) {
return;
}
if (!listenerService.shouldCancelEvent(event)) {
return;
}
Player player = event.getPlayer();
String name = player.getName().toLowerCase();
Location spawn = spawnLoader.getSpawnLocation(player);
if (settings.getProperty(RestrictionSettings.SAVE_QUIT_LOCATION) && dataSource.isAuthAvailable(name)) {
PlayerAuth auth = PlayerAuth.builder()
.name(name)
.realName(player.getName())
.location(spawn)
.build();
dataSource.updateQuitLoc(auth);
bungeeSender.sendAuthMeBungeecordMessage(MessageType.REFRESH_QUITLOC, name);
}
if (spawn != null && spawn.getWorld() != null) {
event.setRespawnLocation(spawn);
}
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onPlayerShear(PlayerShearEntityEvent event) {
if (listenerService.shouldCancelEvent(event)) {
event.setCancelled(true);
}
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onPlayerFish(PlayerFishEvent event) {
if (listenerService.shouldCancelEvent(event)) {
event.setCancelled(true);
}
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onPlayerEditBook(PlayerEditBookEvent event) {
if (listenerService.shouldCancelEvent(event)) {
event.setCancelled(true);
}
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onPlayerInteractAtEntity(PlayerInteractAtEntityEvent event) {
if (listenerService.shouldCancelEvent(event)) {
event.setCancelled(true);
}
}
}

View File

@ -21,5 +21,4 @@ public class PlayerListener19 implements Listener {
event.setCancelled(true);
}
}
}

View File

@ -33,5 +33,4 @@ public class PlayerListener19Spigot implements Listener {
event.setSpawnLocation(customSpawnLocation);
}
}
}

View File

@ -32,11 +32,6 @@ public class ServerListener implements Listener {
@EventHandler(priority = EventPriority.HIGHEST)
public void onPluginDisable(PluginDisableEvent event) {
// Make sure the plugin instance isn't null
if (event.getPlugin() == null) {
return;
}
final String pluginName = event.getPlugin().getName();
// Call the onPluginDisable method in the permissions manager
@ -63,11 +58,6 @@ public class ServerListener implements Listener {
@EventHandler(priority = EventPriority.HIGHEST)
public void onPluginEnable(PluginEnableEvent event) {
// Make sure the plugin instance isn't null
if (event.getPlugin() == null) {
return;
}
final String pluginName = event.getPlugin().getName();
// Call the onPluginEnable method in the permissions manager

View File

@ -146,6 +146,9 @@ public enum MessageKey {
/** The server is full, try again later! */
KICK_FULL_SERVER("on_join_validation.kick_full_server"),
/** An error occurred: unresolved player hostname! **/
KICK_UNRESOLVED_HOSTNAME("error.kick_unresolved_hostname"),
/** Usage: /email add &lt;email&gt; &lt;confirmEmail&gt; */
USAGE_ADD_EMAIL("email.usage_email_add"),

View File

@ -4,7 +4,6 @@ import com.google.common.annotations.VisibleForTesting;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.initialization.Reloadable;
import fr.xephi.authme.output.ConsoleLoggerFactory;
import fr.xephi.authme.listener.JoiningPlayer;
import fr.xephi.authme.permission.handlers.LuckPermsHandler;
import fr.xephi.authme.permission.handlers.PermissionHandler;
import fr.xephi.authme.permission.handlers.PermissionHandlerException;
@ -227,18 +226,6 @@ public class PermissionsManager implements Reloadable {
return player.hasPermission(permissionNode.getNode());
}
/**
* Check if the given player has permission for the given permission node.
*
* @param joiningPlayer The player to check
* @param permissionNode The permission node to verify
*
* @return true if the player has permission, false otherwise
*/
public boolean hasPermission(JoiningPlayer joiningPlayer, PermissionNode permissionNode) {
return joiningPlayer.getPermissionLookupFunction().apply(this, permissionNode);
}
/**
* Check if a player has permission for the given permission node. This is for offline player checks.
* If no permissions system is used, then the player will not have permission.

View File

@ -89,8 +89,9 @@ public class AsynchronousJoin implements AsynchronousProcess {
}
if (service.getProperty(RestrictionSettings.FORCE_SURVIVAL_MODE)
&& player.getGameMode() != GameMode.SURVIVAL
&& !service.hasPermission(player, PlayerStatePermission.BYPASS_FORCE_SURVIVAL)) {
bukkitService.runTask(() -> player.setGameMode(GameMode.SURVIVAL));
bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(() -> player.setGameMode(GameMode.SURVIVAL));
}
if (service.getProperty(HooksSettings.DISABLE_SOCIAL_SPY)) {

View File

@ -100,7 +100,6 @@ public class ProcessSyncPlayerLogin implements SynchronousProcess {
// The Login event now fires (as intended) after everything is processed
bukkitService.callEvent(new LoginEvent(player));
player.saveData();
// Login is done, display welcome message
welcomeMessageConfiguration.sendWelcomeMessage(player);

View File

@ -41,7 +41,6 @@ public class ProcessSyncEmailRegister implements SynchronousProcess {
service.send(player, MessageKey.ACCOUNT_NOT_ACTIVATED);
limboService.replaceTasksAfterRegistration(player);
player.saveData();
bukkitService.callEvent(new RegisterEvent(player));
logger.fine(player.getName() + " registered " + PlayerUtils.getPlayerIp(player));
}

View File

@ -67,7 +67,6 @@ public class ProcessSyncPasswordRegister implements SynchronousProcess {
service.send(player, MessageKey.ADD_EMAIL_MESSAGE);
}
player.saveData();
bukkitService.callEvent(new RegisterEvent(player));
logger.fine(player.getName() + " registered " + PlayerUtils.getPlayerIp(player));

View File

@ -128,7 +128,6 @@ public class AsynchronousUnregister implements AsynchronousProcess {
if (service.getProperty(RegistrationSettings.FORCE)) {
teleportationService.teleportOnJoin(player);
player.saveData();
bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(() -> {
limboService.createLimboPlayer(player, false);

View File

@ -82,14 +82,6 @@ public final class PluginSettings implements SettingsHolder {
public static final Property<Boolean> USE_ASYNC_TASKS =
newProperty("settings.useAsyncTasks", true);
@Comment({
"By default we handle the AsyncPlayerPreLoginEvent which makes the plugin faster",
"but it is incompatible with any permission plugin not included in our compatibility list.",
"If you have issues with permission checks on player join please disable this option."
})
public static final Property<Boolean> USE_ASYNC_PRE_LOGIN_EVENT =
newProperty("settings.useAsyncPreLoginEvent", true);
@Comment("The name of the server, used in some placeholders.")
public static final Property<String> SERVER_NAME = newProperty("settings.serverName", "Your Minecraft Server");

View File

@ -41,6 +41,7 @@ error:
unexpected_error: '&4An unexpected error occurred, please contact an administrator!'
kick_for_vip: '&3A VIP player has joined the server when it was full!'
logged_in: '&cYou''re already logged in!'
kick_unresolved_hostname: '&cAn error occurred: unresolved player hostname!'
# AntiBot
antibot:

View File

@ -1,6 +1,5 @@
package fr.xephi.authme.listener;
import fr.xephi.authme.ReflectionTestUtils;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.entity.Projectile;
@ -200,8 +199,6 @@ public class EntityListenerTest {
@Test
public void shouldHandleOldShooterMethod() {
// given
ReflectionTestUtils.setField(listener, "shooterIsLivingEntity", true);
ReflectionTestUtils.setField(listener, "getShooter", null);
Projectile projectile = mock(Projectile.class);
Player shooter = mock(Player.class);
given(projectile.getShooter()).willReturn(shooter);

View File

@ -1,8 +1,8 @@
package fr.xephi.authme.listener;
import com.google.common.collect.Sets;
import fr.xephi.authme.ClassCollector;
import fr.xephi.authme.TestHelper;
import org.bukkit.event.Cancellable;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
@ -27,13 +27,6 @@ public final class ListenerConsistencyTest {
private static List<Class<? extends Listener>> classes;
private static final Set<String> CANCELED_EXCEPTIONS = Sets.newHashSet(
"PlayerListener#onPlayerJoin", "PlayerListener#onPlayerLogin",
"PlayerListener#onPlayerQuit", "ServerListener#onPluginDisable",
"ServerListener#onServerPing", "ServerListener#onPluginEnable",
"PlayerListener#onJoinMessage", "PlayerListener#onAsyncPlayerPreLoginEvent",
"PlayerListener19Spigot#onPlayerSpawn");
@BeforeClass
public static void collectListenerClasses() {
ClassCollector collector = new ClassCollector(TestHelper.SOURCES_FOLDER, TestHelper.PROJECT_ROOT + "listener");
@ -84,7 +77,7 @@ public final class ListenerConsistencyTest {
Method[] methods = listenerClass.getDeclaredMethods();
for (Method method : methods) {
if (isTestableMethod(method) && method.isAnnotationPresent(EventHandler.class)) {
if (CANCELED_EXCEPTIONS.contains(clazz + "#" + method.getName())) {
if (!method.getParameterTypes()[0].isAssignableFrom(Cancellable.class)) {
continue;
}

View File

@ -374,27 +374,27 @@ public class OnJoinVerifierTest {
@Test
public void shouldAllowUser() throws FailedVerificationException {
// given
JoiningPlayer joiningPlayer = JoiningPlayer.fromName("Bobby");
String name = "Bobby";
boolean isAuthAvailable = false;
given(permissionsManager.hasPermission(joiningPlayer, PlayerStatePermission.BYPASS_ANTIBOT)).willReturn(false);
given(permissionsManager.hasPermissionOffline(name, PlayerStatePermission.BYPASS_ANTIBOT)).willReturn(false);
given(antiBotService.shouldKick()).willReturn(false);
// when
onJoinVerifier.checkAntibot(joiningPlayer, isAuthAvailable);
onJoinVerifier.checkAntibot(name, isAuthAvailable);
// then
verify(permissionsManager).hasPermission(joiningPlayer, PlayerStatePermission.BYPASS_ANTIBOT);
verify(permissionsManager).hasPermissionOffline(name, PlayerStatePermission.BYPASS_ANTIBOT);
verify(antiBotService).shouldKick();
}
@Test
public void shouldAllowUserWithAuth() throws FailedVerificationException {
// given
JoiningPlayer joiningPlayer = JoiningPlayer.fromName("Lacey");
String name = "Lacey";
boolean isAuthAvailable = true;
// when
onJoinVerifier.checkAntibot(joiningPlayer, isAuthAvailable);
onJoinVerifier.checkAntibot(name, isAuthAvailable);
// then
verifyZeroInteractions(permissionsManager, antiBotService);
@ -403,32 +403,32 @@ public class OnJoinVerifierTest {
@Test
public void shouldAllowUserWithBypassPermission() throws FailedVerificationException {
// given
JoiningPlayer joiningPlayer = JoiningPlayer.fromName("Steward");
String name = "Steward";
boolean isAuthAvailable = false;
given(permissionsManager.hasPermission(joiningPlayer, PlayerStatePermission.BYPASS_ANTIBOT)).willReturn(true);
given(permissionsManager.hasPermissionOffline(name, PlayerStatePermission.BYPASS_ANTIBOT)).willReturn(true);
// when
onJoinVerifier.checkAntibot(joiningPlayer, isAuthAvailable);
onJoinVerifier.checkAntibot(name, isAuthAvailable);
// then
verify(permissionsManager).hasPermission(joiningPlayer, PlayerStatePermission.BYPASS_ANTIBOT);
verify(permissionsManager).hasPermissionOffline(name, PlayerStatePermission.BYPASS_ANTIBOT);
verifyZeroInteractions(antiBotService);
}
@Test
public void shouldKickUserForFailedAntibotCheck() throws FailedVerificationException {
public void shouldKickUserForFailedAntibotCheck() {
// given
JoiningPlayer joiningPlayer = JoiningPlayer.fromName("D3");
String name = "D3";
boolean isAuthAvailable = false;
given(permissionsManager.hasPermission(joiningPlayer, PlayerStatePermission.BYPASS_ANTIBOT)).willReturn(false);
given(permissionsManager.hasPermissionOffline(name, PlayerStatePermission.BYPASS_ANTIBOT)).willReturn(false);
given(antiBotService.shouldKick()).willReturn(true);
// when / then
try {
onJoinVerifier.checkAntibot(joiningPlayer, isAuthAvailable);
onJoinVerifier.checkAntibot(name, isAuthAvailable);
fail("Expected exception to be thrown");
} catch (FailedVerificationException e) {
verify(permissionsManager).hasPermission(joiningPlayer, PlayerStatePermission.BYPASS_ANTIBOT);
verify(permissionsManager).hasPermissionOffline(name, PlayerStatePermission.BYPASS_ANTIBOT);
verify(antiBotService).shouldKick();
}
@ -439,18 +439,18 @@ public class OnJoinVerifierTest {
*/
@Test
public void shouldNotCheckCountry() throws FailedVerificationException {
JoiningPlayer joiningPlayer = JoiningPlayer.fromName("david");
String name = "david";
String ip = "127.0.0.1";
// protection setting disabled
given(settings.getProperty(ProtectionSettings.ENABLE_PROTECTION)).willReturn(false);
given(settings.getProperty(ProtectionSettings.ENABLE_PROTECTION_REGISTERED)).willReturn(true);
onJoinVerifier.checkPlayerCountry(joiningPlayer, ip, false);
onJoinVerifier.checkPlayerCountry(name, ip, false);
verifyZeroInteractions(validationService);
// protection for registered players disabled
given(settings.getProperty(ProtectionSettings.ENABLE_PROTECTION_REGISTERED)).willReturn(false);
onJoinVerifier.checkPlayerCountry(joiningPlayer, ip, true);
onJoinVerifier.checkPlayerCountry(name, ip, true);
verifyZeroInteractions(validationService);
}
@ -458,12 +458,12 @@ public class OnJoinVerifierTest {
public void shouldCheckAndAcceptUnregisteredPlayerCountry() throws FailedVerificationException {
// given
String ip = "192.168.0.1";
JoiningPlayer joiningPlayer = JoiningPlayer.fromName("lucas");
String name = "lucas";
given(settings.getProperty(ProtectionSettings.ENABLE_PROTECTION)).willReturn(true);
given(validationService.isCountryAdmitted(ip)).willReturn(true);
// when
onJoinVerifier.checkPlayerCountry(joiningPlayer, ip, false);
onJoinVerifier.checkPlayerCountry(name, ip, false);
// then
verify(validationService).isCountryAdmitted(ip);
@ -473,13 +473,13 @@ public class OnJoinVerifierTest {
public void shouldCheckAndAcceptRegisteredPlayerCountry() throws FailedVerificationException {
// given
String ip = "192.168.10.24";
JoiningPlayer joiningPlayer = JoiningPlayer.fromName("gabriel");
String name = "gabriel";
given(settings.getProperty(ProtectionSettings.ENABLE_PROTECTION)).willReturn(true);
given(settings.getProperty(ProtectionSettings.ENABLE_PROTECTION_REGISTERED)).willReturn(true);
given(validationService.isCountryAdmitted(ip)).willReturn(true);
// when
onJoinVerifier.checkPlayerCountry(joiningPlayer, ip, true);
onJoinVerifier.checkPlayerCountry(name, ip, true);
// then
verify(validationService).isCountryAdmitted(ip);
@ -489,7 +489,7 @@ public class OnJoinVerifierTest {
public void shouldThrowForBannedCountry() throws FailedVerificationException {
// given
String ip = "192.168.40.0";
JoiningPlayer joiningPlayer = JoiningPlayer.fromName("bob");
String name = "bob";
given(settings.getProperty(ProtectionSettings.ENABLE_PROTECTION)).willReturn(true);
given(validationService.isCountryAdmitted(ip)).willReturn(false);
@ -497,7 +497,7 @@ public class OnJoinVerifierTest {
expectValidationExceptionWith(MessageKey.COUNTRY_BANNED_ERROR);
// when
onJoinVerifier.checkPlayerCountry(joiningPlayer, ip, false);
onJoinVerifier.checkPlayerCountry(name, ip, false);
}
private void expectValidationExceptionWith(MessageKey messageKey, String... args) {

View File

@ -1,6 +1,5 @@
package fr.xephi.authme.listener;
import fr.xephi.authme.TestHelper;
import fr.xephi.authme.data.QuickCommandsProtectionManager;
import fr.xephi.authme.data.auth.PlayerAuth;
import fr.xephi.authme.datasource.DataSource;
@ -29,6 +28,7 @@ import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryOpenEvent;
import org.bukkit.event.player.AsyncPlayerChatEvent;
import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
import org.bukkit.event.player.PlayerBedEnterEvent;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
import org.bukkit.event.player.PlayerDropItemEvent;
@ -38,12 +38,14 @@ import org.bukkit.event.player.PlayerInteractAtEntityEvent;
import org.bukkit.event.player.PlayerInteractEntityEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerItemConsumeEvent;
import org.bukkit.event.player.PlayerItemHeldEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerKickEvent;
import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerPickupItemEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.player.PlayerRespawnEvent;
import org.bukkit.event.player.PlayerShearEntityEvent;
import org.bukkit.inventory.InventoryView;
import org.junit.Test;
@ -58,6 +60,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.UUID;
import static com.google.common.collect.Sets.newHashSet;
import static fr.xephi.authme.listener.EventCancelVerifier.withServiceMock;
@ -71,7 +74,6 @@ import static org.junit.Assert.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.doThrow;
@ -188,7 +190,8 @@ public class PlayerListenerTest {
.check(listener::onPlayerConsumeItem, PlayerItemConsumeEvent.class)
.check(listener::onPlayerInteract, PlayerInteractEvent.class)
.check(listener::onPlayerPickupItem, PlayerPickupItemEvent.class)
.check(listener::onPlayerInteractEntity, PlayerInteractEntityEvent.class);
.check(listener::onPlayerInteractEntity, PlayerInteractEntityEvent.class)
.check(listener::onPlayerHeldItem, PlayerItemHeldEvent.class);
}
@Test
@ -536,6 +539,98 @@ public class PlayerListenerTest {
verifyNoModifyingCalls(event);
}
@Test
public void shouldIgnorePlayerRespawnWithNoTeleport() {
// given
Player player = mock(Player.class);
Location respawnLocation = mock(Location.class);
PlayerRespawnEvent event = spy(new PlayerRespawnEvent(player, respawnLocation, false));
given(settings.getProperty(RestrictionSettings.NO_TELEPORT)).willReturn(true);
// when
listener.onPlayerRespawn(event);
// then
verifyZeroInteractions(listenerService);
verify(event, never()).setRespawnLocation(any());
}
@Test
public void shouldIgnorePlayerRespawn() {
// given
Player player = mock(Player.class);
Location respawnLocation = mock(Location.class);
PlayerRespawnEvent event = spy(new PlayerRespawnEvent(player, respawnLocation, false));
given(settings.getProperty(RestrictionSettings.NO_TELEPORT)).willReturn(false);
given(listenerService.shouldCancelEvent(event)).willReturn(false);
// when
listener.onPlayerRespawn(event);
// then
verifyZeroInteractions(spawnLoader);
verify(event, never()).setRespawnLocation(any());
}
@Test
public void shouldHandlePlayerRespawn() {
// given
Player player = mock(Player.class);
Location originalLocation = mock(Location.class);
Location newLocation = mock(Location.class);
World world = mock(World.class);
given(newLocation.getWorld()).willReturn(world);
PlayerRespawnEvent event = spy(new PlayerRespawnEvent(player, originalLocation, false));
given(settings.getProperty(RestrictionSettings.NO_TELEPORT)).willReturn(false);
given(listenerService.shouldCancelEvent(event)).willReturn(true);
given(spawnLoader.getSpawnLocation(player)).willReturn(newLocation);
// when
listener.onPlayerRespawn(event);
// then
verify(spawnLoader).getSpawnLocation(player);
verify(event).setRespawnLocation(newLocation);
}
@Test
public void shouldIgnorePlayerRespawnUnloadedWorld() {
// given
Player player = mock(Player.class);
Location originalLocation = mock(Location.class);
Location newLocation = mock(Location.class);
given(newLocation.getWorld()).willReturn(null);
PlayerRespawnEvent event = spy(new PlayerRespawnEvent(player, originalLocation, false));
given(settings.getProperty(RestrictionSettings.NO_TELEPORT)).willReturn(false);
given(listenerService.shouldCancelEvent(event)).willReturn(true);
given(spawnLoader.getSpawnLocation(player)).willReturn(newLocation);
// when
listener.onPlayerRespawn(event);
// then
verify(spawnLoader).getSpawnLocation(player);
verify(event, never()).setRespawnLocation(any());
}
@Test
public void shouldHandlePlayerRespawnNoChanges() {
// given
Player player = mock(Player.class);
Location originalLocation = mock(Location.class);
PlayerRespawnEvent event = spy(new PlayerRespawnEvent(player, originalLocation, false));
given(settings.getProperty(RestrictionSettings.NO_TELEPORT)).willReturn(false);
given(listenerService.shouldCancelEvent(event)).willReturn(true);
given(spawnLoader.getSpawnLocation(player)).willReturn(null);
// when
listener.onPlayerRespawn(event);
// then
verify(spawnLoader).getSpawnLocation(player);
verify(event, never()).setRespawnLocation(any());
}
@Test
public void shouldHandlePlayerJoining() {
// given
@ -606,60 +701,113 @@ public class PlayerListenerTest {
}
@Test
public void shouldPerformAllJoinVerificationsSuccessfully() throws FailedVerificationException {
public void shouldPerformAllJoinVerificationsSuccessfullyPreLoginLowest() throws FailedVerificationException {
// given
String name = "someone";
UUID uniqueId = UUID.fromString("753493c9-33ba-4a4a-bf61-1bce9d3c9a71");
String ip = "12.34.56.78";
AsyncPlayerPreLoginEvent preLoginEvent = spy(new AsyncPlayerPreLoginEvent(name, mockAddrWithIp(ip), uniqueId));
given(validationService.isUnrestricted(name)).willReturn(false);
// when
listener.onAsyncPlayerPreLoginEventLowest(preLoginEvent);
// then
verify(validationService).isUnrestricted(name);
verify(onJoinVerifier).checkSingleSession(name);
verify(onJoinVerifier).checkIsValidName(name);
verifyZeroInteractions(dataSource);
verifyNoModifyingCalls(preLoginEvent);
}
@Test
public void shouldKickPreLoginLowestUnresolvedHostname() throws FailedVerificationException {
// given
String name = "someone";
UUID uniqueId = UUID.fromString("753493c9-33ba-4a4a-bf61-1bce9d3c9a71");
@SuppressWarnings("ConstantConditions")
AsyncPlayerPreLoginEvent preLoginEvent = spy(new AsyncPlayerPreLoginEvent(name, null, uniqueId));
given(messages.retrieveSingle(name, MessageKey.KICK_UNRESOLVED_HOSTNAME)).willReturn("Unresolved hostname");
// when
listener.onAsyncPlayerPreLoginEventLowest(preLoginEvent);
// then
verify(preLoginEvent).disallow(AsyncPlayerPreLoginEvent.Result.KICK_OTHER, "Unresolved hostname");
verifyNoMoreInteractions(onJoinVerifier);
}
@Test
public void shouldPerformAllJoinVerificationsSuccessfullyPreLoginHighest() throws FailedVerificationException {
// given
String name = "someone";
UUID uniqueId = UUID.fromString("753493c9-33ba-4a4a-bf61-1bce9d3c9a71");
String ip = "12.34.56.78";
AsyncPlayerPreLoginEvent preLoginEvent = spy(new AsyncPlayerPreLoginEvent(name, mockAddrWithIp(ip), uniqueId));
given(validationService.isUnrestricted(name)).willReturn(false);
PlayerAuth auth = PlayerAuth.builder().name(name).build();
given(dataSource.getAuth(name)).willReturn(auth);
// when
listener.onAsyncPlayerPreLoginEventHighest(preLoginEvent);
// then
verify(validationService).isUnrestricted(name);
verify(onJoinVerifier).checkKickNonRegistered(true);
verify(onJoinVerifier).checkAntibot(name, true);
verify(onJoinVerifier).checkNameCasing(name, auth);
verify(onJoinVerifier).checkPlayerCountry(name, ip, true);
verifyNoModifyingCalls(preLoginEvent);
}
@Test
public void shouldPerformAllJoinVerificationsSuccessfullyLogin() {
// given
String name = "someone";
Player player = mockPlayerWithName(name);
String ip = "12.34.56.78";
PlayerLoginEvent event = spy(new PlayerLoginEvent(player, "", mockAddrWithIp(ip)));
PlayerLoginEvent loginEvent = spy(new PlayerLoginEvent(player, "", mockAddrWithIp(ip)));
given(validationService.isUnrestricted(name)).willReturn(false);
given(onJoinVerifier.refusePlayerForFullServer(event)).willReturn(false);
PlayerAuth auth = PlayerAuth.builder().name(name).build();
given(dataSource.getAuth(name)).willReturn(auth);
given(onJoinVerifier.refusePlayerForFullServer(loginEvent)).willReturn(false);
// when
listener.onPlayerLogin(event);
listener.onPlayerLogin(loginEvent);
// then
verify(validationService).isUnrestricted(name);
verify(onJoinVerifier).refusePlayerForFullServer(event);
verify(onJoinVerifier).checkSingleSession(name);
verify(onJoinVerifier).checkIsValidName(name);
verify(onJoinVerifier).checkAntibot(any(JoiningPlayer.class), eq(true));
verify(onJoinVerifier).checkKickNonRegistered(true);
verify(onJoinVerifier).checkNameCasing(name, auth);
verify(onJoinVerifier).checkPlayerCountry(any(JoiningPlayer.class), eq(ip), eq(true));
verifyNoModifyingCalls(event);
verify(onJoinVerifier).refusePlayerForFullServer(loginEvent);
verifyZeroInteractions(dataSource);
verifyNoModifyingCalls(loginEvent);
}
@Test
public void shouldAbortPlayerJoinForInvalidName() throws FailedVerificationException {
// given
String name = "inval!dName";
Player player = mockPlayerWithName(name);
TestHelper.mockPlayerIp(player, "33.32.33.33");
PlayerLoginEvent event = spy(new PlayerLoginEvent(player, "", player.getAddress().getAddress()));
UUID uniqueId = UUID.fromString("753493c9-33ba-4a4a-bf61-1bce9d3c9a71");
InetAddress ip = mockAddrWithIp("33.32.33.33");
AsyncPlayerPreLoginEvent event = spy(new AsyncPlayerPreLoginEvent(name, ip, uniqueId));
given(validationService.isUnrestricted(name)).willReturn(false);
given(onJoinVerifier.refusePlayerForFullServer(event)).willReturn(false);
FailedVerificationException exception = new FailedVerificationException(
MessageKey.INVALID_NAME_CHARACTERS, "[a-z]");
doThrow(exception).when(onJoinVerifier).checkIsValidName(name);
String message = "Invalid characters!";
given(messages.retrieveSingle(player, exception.getReason(), exception.getArgs())).willReturn(message);
given(messages.retrieveSingle(name, exception.getReason(), exception.getArgs())).willReturn(message);
// when
listener.onPlayerLogin(event);
listener.onAsyncPlayerPreLoginEventLowest(event);
// then
verify(validationService).isUnrestricted(name);
verify(onJoinVerifier).refusePlayerForFullServer(event);
verify(onJoinVerifier).checkSingleSession(name);
verify(onJoinVerifier).checkIsValidName(name);
// Check that we don't talk with the data source before performing checks that don't require it
verifyZeroInteractions(dataSource);
verify(event).setKickMessage(message);
verify(event).setResult(PlayerLoginEvent.Result.KICK_OTHER);
verify(event).setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_OTHER);
}
@Test
@ -950,6 +1098,13 @@ public class PlayerListenerTest {
verifyNoMoreInteractions(event);
}
private static void verifyNoModifyingCalls(AsyncPlayerPreLoginEvent event) {
verify(event, atLeast(0)).getLoginResult();
verify(event, atLeast(0)).getAddress();
verify(event, atLeast(0)).getName();
verifyNoMoreInteractions(event);
}
private static InetAddress mockAddrWithIp(String ip) {
InetAddress addr = mock(InetAddress.class);
given(addr.getHostAddress()).willReturn(ip);

View File

@ -81,19 +81,6 @@ public class ServerListenerTest {
checkDisableHandling("UnknownPlugin", () -> verifyZeroInteractions(pluginHookService, spawnLoader));
}
@Test
public void shouldHandlePluginWithNullName() {
PluginEnableEvent enableEvent = mock(PluginEnableEvent.class);
given(enableEvent.getPlugin()).willReturn(null);
serverListener.onPluginEnable(enableEvent);
verifyNoMoreInteractionsAndReset();
PluginDisableEvent disableEvent = mock(PluginDisableEvent.class);
given(disableEvent.getPlugin()).willReturn(null);
serverListener.onPluginDisable(disableEvent);
verifyNoMoreInteractionsAndReset();
}
private void checkEnableHandling(String pluginName, Runnable verifier) {
PluginEnableEvent event = mockEventWithPluginName(PluginEnableEvent.class, pluginName);
serverListener.onPluginEnable(event);

View File

@ -1,6 +1,5 @@
package fr.xephi.authme.permission;
import fr.xephi.authme.listener.JoiningPlayer;
import org.bukkit.Server;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
@ -13,13 +12,8 @@ import org.mockito.junit.MockitoJUnitRunner;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
/**
* Test for {@link PermissionsManager}.
@ -154,26 +148,4 @@ public class PermissionsManagerTest {
// then
assertThat(result, equalTo(true));
}
@Test
public void shouldHandleJoiningPlayerPermissionChecksWithProperMethod() {
// given
Player player = mock(Player.class);
JoiningPlayer fromPlayer = JoiningPlayer.fromPlayerObject(player);
JoiningPlayer fromName = JoiningPlayer.fromName("Chris");
PermissionsManager permManagerSpy = spy(permissionsManager);
given(permManagerSpy.hasPermission(any(Player.class), eq(PlayerPermission.LOGIN))).willReturn(true);
given(permManagerSpy.hasPermissionOffline(anyString(), eq(PlayerPermission.UNREGISTER))).willReturn(true);
// when
boolean resultFromPlayer = permManagerSpy.hasPermission(fromPlayer, PlayerPermission.LOGIN);
boolean resultFromName = permManagerSpy.hasPermission(fromName, PlayerPermission.UNREGISTER);
// then
assertThat(resultFromPlayer, equalTo(true));
assertThat(resultFromName, equalTo(true));
verify(permManagerSpy).hasPermission(player, PlayerPermission.LOGIN);
verify(permManagerSpy).hasPermissionOffline(fromName.getName(), PlayerPermission.UNREGISTER);
}
}