diff --git a/src/main/java/fr/xephi/authme/AntiBot.java b/src/main/java/fr/xephi/authme/AntiBot.java index fcdb2206d..071842bc4 100644 --- a/src/main/java/fr/xephi/authme/AntiBot.java +++ b/src/main/java/fr/xephi/authme/AntiBot.java @@ -10,8 +10,7 @@ import fr.xephi.authme.util.BukkitService; import org.bukkit.entity.Player; import javax.inject.Inject; -import java.util.ArrayList; -import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; import static fr.xephi.authme.util.BukkitService.TICKS_PER_MINUTE; import static fr.xephi.authme.util.BukkitService.TICKS_PER_SECOND; @@ -25,7 +24,8 @@ public class AntiBot { private final Messages messages; private final PermissionsManager permissionsManager; private final BukkitService bukkitService; - private final List antibotPlayers = new ArrayList<>(); + public final CopyOnWriteArrayList antibotKicked = new CopyOnWriteArrayList(); + private final CopyOnWriteArrayList antibotPlayers = new CopyOnWriteArrayList(); private AntiBotStatus antiBotStatus = AntiBotStatus.DISABLED; @Inject @@ -77,6 +77,7 @@ public class AntiBot { if (antiBotStatus == AntiBotStatus.ACTIVE) { antiBotStatus = AntiBotStatus.LISTENING; antibotPlayers.clear(); + antibotKicked.clear(); for (String s : messages.retrieve(MessageKey.ANTIBOT_AUTO_DISABLED_MESSAGE)) { bukkitService.broadcastMessage(s.replace("%m", Integer.toString(duration))); } diff --git a/src/main/java/fr/xephi/authme/AuthMe.java b/src/main/java/fr/xephi/authme/AuthMe.java index baeb1bfdf..4553b207c 100644 --- a/src/main/java/fr/xephi/authme/AuthMe.java +++ b/src/main/java/fr/xephi/authme/AuthMe.java @@ -24,7 +24,6 @@ import fr.xephi.authme.listener.AuthMeInventoryPacketAdapter; import fr.xephi.authme.listener.AuthMePlayerListener; import fr.xephi.authme.listener.AuthMePlayerListener16; import fr.xephi.authme.listener.AuthMePlayerListener18; -import fr.xephi.authme.listener.AuthMePlayerListener19; import fr.xephi.authme.listener.AuthMeServerListener; import fr.xephi.authme.listener.AuthMeTabCompletePacketAdapter; import fr.xephi.authme.listener.AuthMeTablistPacketAdapter; @@ -374,13 +373,6 @@ public class AuthMe extends JavaPlugin { pluginManager.registerEvents(initializer.get(AuthMePlayerListener18.class), this); } catch (ClassNotFoundException ignore) { } - - // Try to register 1.9 player listeners - try { - Class.forName("org.spigotmc.event.player.PlayerSpawnLocationEvent"); - pluginManager.registerEvents(initializer.get(AuthMePlayerListener19.class), this); - } catch (ClassNotFoundException ignore) { - } } private void reloadSupportHook() { diff --git a/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java b/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java index 9f2843d1c..12d45d20b 100644 --- a/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java +++ b/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java @@ -72,7 +72,7 @@ public class CacheDataSource implements DataSource { } @Override - public synchronized boolean isAuthAvailable(String user) { + public boolean isAuthAvailable(String user) { return getAuth(user) != null; } @@ -87,13 +87,13 @@ public class CacheDataSource implements DataSource { } @Override - public synchronized PlayerAuth getAuth(String user) { + public PlayerAuth getAuth(String user) { user = user.toLowerCase(); return cachedAuths.getUnchecked(user).orNull(); } @Override - public synchronized boolean saveAuth(PlayerAuth auth) { + public boolean saveAuth(PlayerAuth auth) { boolean result = source.saveAuth(auth); if (result) { cachedAuths.refresh(auth.getNickname()); @@ -102,7 +102,7 @@ public class CacheDataSource implements DataSource { } @Override - public synchronized boolean updatePassword(PlayerAuth auth) { + public boolean updatePassword(PlayerAuth auth) { boolean result = source.updatePassword(auth); if (result) { cachedAuths.refresh(auth.getNickname()); @@ -111,7 +111,7 @@ public class CacheDataSource implements DataSource { } @Override - public synchronized boolean updatePassword(String user, HashedPassword password) { + public boolean updatePassword(String user, HashedPassword password) { user = user.toLowerCase(); boolean result = source.updatePassword(user, password); if (result) { @@ -121,7 +121,7 @@ public class CacheDataSource implements DataSource { } @Override - public synchronized boolean updateSession(PlayerAuth auth) { + public boolean updateSession(PlayerAuth auth) { boolean result = source.updateSession(auth); if (result) { cachedAuths.refresh(auth.getNickname()); @@ -130,7 +130,7 @@ public class CacheDataSource implements DataSource { } @Override - public synchronized boolean updateQuitLoc(final PlayerAuth auth) { + public boolean updateQuitLoc(final PlayerAuth auth) { boolean result = source.updateQuitLoc(auth); if (result) { cachedAuths.refresh(auth.getNickname()); @@ -149,7 +149,7 @@ public class CacheDataSource implements DataSource { } @Override - public synchronized boolean removeAuth(String name) { + public boolean removeAuth(String name) { name = name.toLowerCase(); boolean result = source.removeAuth(name); if (result) { @@ -159,7 +159,7 @@ public class CacheDataSource implements DataSource { } @Override - public synchronized void close() { + public void close() { source.close(); cachedAuths.invalidateAll(); executorService.shutdown(); @@ -171,7 +171,7 @@ public class CacheDataSource implements DataSource { } @Override - public synchronized boolean updateEmail(final PlayerAuth auth) { + public boolean updateEmail(final PlayerAuth auth) { boolean result = source.updateEmail(auth); if (result) { cachedAuths.refresh(auth.getNickname()); @@ -180,17 +180,17 @@ public class CacheDataSource implements DataSource { } @Override - public synchronized List getAllAuthsByIp(final String ip) { + public List getAllAuthsByIp(final String ip) { return source.getAllAuthsByIp(ip); } @Override - public synchronized int countAuthsByEmail(final String email) { + public int countAuthsByEmail(final String email) { return source.countAuthsByEmail(email); } @Override - public synchronized void purgeBanned(final Set banned) { + public void purgeBanned(final Set banned) { source.purgeBanned(banned); cachedAuths.invalidateAll(banned); } @@ -201,17 +201,17 @@ public class CacheDataSource implements DataSource { } @Override - public synchronized boolean isLogged(String user) { + public boolean isLogged(String user) { return PlayerCache.getInstance().isAuthenticated(user); } @Override - public synchronized void setLogged(final String user) { + public void setLogged(final String user) { source.setLogged(user.toLowerCase()); } @Override - public synchronized void setUnlogged(final String user) { + public void setUnlogged(final String user) { source.setUnlogged(user.toLowerCase()); } @@ -227,7 +227,7 @@ public class CacheDataSource implements DataSource { } @Override - public synchronized boolean updateRealName(String user, String realName) { + public boolean updateRealName(String user, String realName) { boolean result = source.updateRealName(user, realName); if (result) { cachedAuths.refresh(user); @@ -236,7 +236,7 @@ public class CacheDataSource implements DataSource { } @Override - public synchronized boolean updateIp(String user, String ip) { + public boolean updateIp(String user, String ip) { boolean result = source.updateIp(user, ip); if (result) { cachedAuths.refresh(user); diff --git a/src/main/java/fr/xephi/authme/datasource/MySQL.java b/src/main/java/fr/xephi/authme/datasource/MySQL.java index 694c2fc8d..94e43e00a 100644 --- a/src/main/java/fr/xephi/authme/datasource/MySQL.java +++ b/src/main/java/fr/xephi/authme/datasource/MySQL.java @@ -106,34 +106,36 @@ public class MySQL implements DataSource { ds = hikariDataSource; } - private synchronized void setConnectionArguments() throws RuntimeException { + private void setConnectionArguments() throws RuntimeException { ds = new HikariDataSource(); ds.setPoolName("AuthMeMYSQLPool"); - ds.setDriverClassName("com.mysql.jdbc.Driver"); - ds.setJdbcUrl("jdbc:mysql://" + this.host + ":" + this.port + "/" + this.database); - ds.addDataSourceProperty("rewriteBatchedStatements", "true"); - ds.addDataSourceProperty("jdbcCompliantTruncation", "false"); - ds.addDataSourceProperty("cachePrepStmts", "true"); - ds.addDataSourceProperty("prepStmtCacheSize", "250"); - ds.addDataSourceProperty("prepStmtCacheSqlLimit", "2048"); - //set utf-8 as default encoding + // Database URL + ds.setJdbcUrl("jdbc:mysql://" + this.host + ":" + this.port + "/" + this.database); + + // Auth + ds.setUsername(this.username); + ds.setPassword(this.password); + + // Encoding ds.addDataSourceProperty("characterEncoding", "utf8"); ds.addDataSourceProperty("encoding","UTF-8"); ds.addDataSourceProperty("useUnicode", "true"); - ds.setUsername(this.username); - ds.setPassword(this.password); - ds.setInitializationFailFast(true); // Don't start the plugin if the database is unavailable - ds.setMaxLifetime(180000); // 3 Min - ds.setIdleTimeout(60000); // 1 Min - ds.setMinimumIdle(2); - ds.setMaximumPoolSize((Runtime.getRuntime().availableProcessors() * 2) + 1); + // Random stuff + ds.addDataSourceProperty("rewriteBatchedStatements", "true"); + ds.addDataSourceProperty("jdbcCompliantTruncation", "false"); + + // Caching + ds.addDataSourceProperty("cachePrepStmts", "true"); + ds.addDataSourceProperty("prepStmtCacheSize", "250"); + ds.addDataSourceProperty("prepStmtCacheSqlLimit", "2048"); + ConsoleLogger.info("Connection arguments loaded, Hikari ConnectionPool ready!"); } @Override - public synchronized void reload() throws RuntimeException { + public void reload() throws RuntimeException { if (ds != null) { ds.close(); } @@ -141,11 +143,11 @@ public class MySQL implements DataSource { ConsoleLogger.info("Hikari ConnectionPool arguments reloaded!"); } - private synchronized Connection getConnection() throws SQLException { + private Connection getConnection() throws SQLException { return ds.getConnection(); } - private synchronized void setupConnection() throws SQLException { + private void setupConnection() throws SQLException { try (Connection con = getConnection()) { Statement st = con.createStatement(); DatabaseMetaData md = con.getMetaData(); @@ -258,7 +260,7 @@ public class MySQL implements DataSource { } @Override - public synchronized boolean isAuthAvailable(String user) { + public boolean isAuthAvailable(String user) { String sql = "SELECT " + col.NAME + " FROM " + tableName + " WHERE " + col.NAME + "=?;"; ResultSet rs = null; try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { @@ -294,7 +296,7 @@ public class MySQL implements DataSource { } @Override - public synchronized PlayerAuth getAuth(String user) { + public PlayerAuth getAuth(String user) { String sql = "SELECT * FROM " + tableName + " WHERE " + col.NAME + "=?;"; PlayerAuth auth; try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { @@ -328,7 +330,7 @@ public class MySQL implements DataSource { } @Override - public synchronized boolean saveAuth(PlayerAuth auth) { + public boolean saveAuth(PlayerAuth auth) { try (Connection con = getConnection()) { PreparedStatement pst; PreparedStatement pst2; @@ -531,12 +533,12 @@ public class MySQL implements DataSource { } @Override - public synchronized boolean updatePassword(PlayerAuth auth) { + public boolean updatePassword(PlayerAuth auth) { return updatePassword(auth.getNickname(), auth.getPassword()); } @Override - public synchronized boolean updatePassword(String user, HashedPassword password) { + public boolean updatePassword(String user, HashedPassword password) { user = user.toLowerCase(); try (Connection con = getConnection()) { boolean useSalt = !col.SALT.isEmpty(); @@ -594,7 +596,7 @@ public class MySQL implements DataSource { } @Override - public synchronized boolean updateSession(PlayerAuth auth) { + public boolean updateSession(PlayerAuth auth) { String sql = "UPDATE " + tableName + " SET " + col.IP + "=?, " + col.LAST_LOGIN + "=?, " + col.REAL_NAME + "=? WHERE " + col.NAME + "=?;"; try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { @@ -611,7 +613,7 @@ public class MySQL implements DataSource { } @Override - public synchronized Set autoPurgeDatabase(long until) { + public Set autoPurgeDatabase(long until) { Set list = new HashSet<>(); String select = "SELECT " + col.NAME + " FROM " + tableName + " WHERE " + col.LAST_LOGIN + " getAllAuthsByIp(String ip) { + public List getAllAuthsByIp(String ip) { List result = new ArrayList<>(); String sql = "SELECT " + col.NAME + " FROM " + tableName + " WHERE " + col.IP + "=?;"; try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { @@ -724,7 +726,7 @@ public class MySQL implements DataSource { } @Override - public synchronized int countAuthsByEmail(String email) { + public int countAuthsByEmail(String email) { String sql = "SELECT COUNT(1) FROM " + tableName + " WHERE UPPER(" + col.EMAIL + ") = UPPER(?)"; try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { pst.setString(1, email); @@ -740,7 +742,7 @@ public class MySQL implements DataSource { } @Override - public synchronized void purgeBanned(Set banned) { + public void purgeBanned(Set banned) { String sql = "DELETE FROM " + tableName + " WHERE " + col.NAME + "=?;"; try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { for (String name : banned) { @@ -758,7 +760,7 @@ public class MySQL implements DataSource { } @Override - public synchronized boolean isLogged(String user) { + public boolean isLogged(String user) { String sql = "SELECT " + col.IS_LOGGED + " FROM " + tableName + " WHERE " + col.NAME + "=?;"; try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { pst.setString(1, user); @@ -772,7 +774,7 @@ public class MySQL implements DataSource { } @Override - public synchronized void setLogged(String user) { + public void setLogged(String user) { String sql = "UPDATE " + tableName + " SET " + col.IS_LOGGED + "=? WHERE " + col.NAME + "=?;"; try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { pst.setInt(1, 1); @@ -784,7 +786,7 @@ public class MySQL implements DataSource { } @Override - public synchronized void setUnlogged(String user) { + public void setUnlogged(String user) { String sql = "UPDATE " + tableName + " SET " + col.IS_LOGGED + "=? WHERE " + col.NAME + "=?;"; try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { pst.setInt(1, 0); @@ -824,7 +826,7 @@ public class MySQL implements DataSource { } @Override - public synchronized boolean updateRealName(String user, String realName) { + public boolean updateRealName(String user, String realName) { String sql = "UPDATE " + tableName + " SET " + col.REAL_NAME + "=? WHERE " + col.NAME + "=?;"; try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { pst.setString(1, realName); @@ -838,7 +840,7 @@ public class MySQL implements DataSource { } @Override - public synchronized boolean updateIp(String user, String ip) { + public boolean updateIp(String user, String ip) { String sql = "UPDATE " + tableName + " SET " + col.IP + "=? WHERE " + col.NAME + "=?;"; try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { pst.setString(1, ip); diff --git a/src/main/java/fr/xephi/authme/datasource/SQLite.java b/src/main/java/fr/xephi/authme/datasource/SQLite.java index fa204a6f3..8072d58ff 100644 --- a/src/main/java/fr/xephi/authme/datasource/SQLite.java +++ b/src/main/java/fr/xephi/authme/datasource/SQLite.java @@ -64,13 +64,13 @@ public class SQLite implements DataSource { ConsoleLogger.logException("Error while executing SQL statement:", e); } - private synchronized void connect() throws ClassNotFoundException, SQLException { + private void connect() throws ClassNotFoundException, SQLException { Class.forName("org.sqlite.JDBC"); ConsoleLogger.info("SQLite driver loaded"); this.con = DriverManager.getConnection("jdbc:sqlite:plugins/AuthMe/" + database + ".db"); } - private synchronized void setup() throws SQLException { + private void setup() throws SQLException { Statement st = null; ResultSet rs = null; try { @@ -143,7 +143,7 @@ public class SQLite implements DataSource { } @Override - public synchronized boolean isAuthAvailable(String user) { + public boolean isAuthAvailable(String user) { PreparedStatement pst = null; ResultSet rs = null; try { @@ -181,7 +181,7 @@ public class SQLite implements DataSource { } @Override - public synchronized PlayerAuth getAuth(String user) { + public PlayerAuth getAuth(String user) { PreparedStatement pst = null; ResultSet rs = null; try { @@ -201,7 +201,7 @@ public class SQLite implements DataSource { } @Override - public synchronized boolean saveAuth(PlayerAuth auth) { + public boolean saveAuth(PlayerAuth auth) { PreparedStatement pst = null; try { HashedPassword password = auth.getPassword(); @@ -242,12 +242,12 @@ public class SQLite implements DataSource { } @Override - public synchronized boolean updatePassword(PlayerAuth auth) { + public boolean updatePassword(PlayerAuth auth) { return updatePassword(auth.getNickname(), auth.getPassword()); } @Override - public synchronized boolean updatePassword(String user, HashedPassword password) { + public boolean updatePassword(String user, HashedPassword password) { user = user.toLowerCase(); PreparedStatement pst = null; try { @@ -274,7 +274,7 @@ public class SQLite implements DataSource { } @Override - public synchronized boolean updateSession(PlayerAuth auth) { + public boolean updateSession(PlayerAuth auth) { PreparedStatement pst = null; try { pst = con.prepareStatement("UPDATE " + tableName + " SET " + col.IP + "=?, " + col.LAST_LOGIN + "=?, " + col.REAL_NAME + "=? WHERE " + col.NAME + "=?;"); @@ -315,7 +315,7 @@ public class SQLite implements DataSource { } @Override - public synchronized boolean removeAuth(String user) { + public boolean removeAuth(String user) { PreparedStatement pst = null; try { pst = con.prepareStatement("DELETE FROM " + tableName + " WHERE " + col.NAME + "=?;"); @@ -331,7 +331,7 @@ public class SQLite implements DataSource { } @Override - public synchronized boolean updateQuitLoc(PlayerAuth auth) { + public boolean updateQuitLoc(PlayerAuth auth) { PreparedStatement pst = null; try { pst = con.prepareStatement("UPDATE " + tableName + " SET " + col.LASTLOC_X + "=?, " + col.LASTLOC_Y + "=?, " + col.LASTLOC_Z + "=?, " + col.LASTLOC_WORLD + "=? WHERE " + col.NAME + "=?;"); @@ -351,7 +351,7 @@ public class SQLite implements DataSource { } @Override - public synchronized boolean updateEmail(PlayerAuth auth) { + public boolean updateEmail(PlayerAuth auth) { String sql = "UPDATE " + tableName + " SET " + col.EMAIL + "=? WHERE " + col.NAME + "=?;"; try (PreparedStatement pst = con.prepareStatement(sql)) { pst.setString(1, auth.getEmail()); @@ -365,7 +365,7 @@ public class SQLite implements DataSource { } @Override - public synchronized void close() { + public void close() { try { if (con != null && !con.isClosed()) { con.close(); @@ -462,7 +462,7 @@ public class SQLite implements DataSource { } @Override - public synchronized boolean isLogged(String user) { + public boolean isLogged(String user) { PreparedStatement pst = null; ResultSet rs = null; try { @@ -481,7 +481,7 @@ public class SQLite implements DataSource { } @Override - public synchronized void setLogged(String user) { + public void setLogged(String user) { PreparedStatement pst = null; try { pst = con.prepareStatement("UPDATE " + tableName + " SET " + col.IS_LOGGED + "=? WHERE LOWER(" + col.NAME + ")=?;"); @@ -496,7 +496,7 @@ public class SQLite implements DataSource { } @Override - public synchronized void setUnlogged(String user) { + public void setUnlogged(String user) { PreparedStatement pst = null; if (user != null) try { @@ -540,7 +540,7 @@ public class SQLite implements DataSource { } @Override - public synchronized boolean updateRealName(String user, String realName) { + public boolean updateRealName(String user, String realName) { String sql = "UPDATE " + tableName + " SET " + col.REAL_NAME + "=? WHERE " + col.NAME + "=?;"; try (PreparedStatement pst = con.prepareStatement(sql)) { pst.setString(1, realName); @@ -554,7 +554,7 @@ public class SQLite implements DataSource { } @Override - public synchronized boolean updateIp(String user, String ip) { + public boolean updateIp(String user, String ip) { String sql = "UPDATE " + tableName + " SET " + col.IP + "=? WHERE " + col.NAME + "=?;"; try (PreparedStatement pst = con.prepareStatement(sql)) { pst.setString(1, ip); diff --git a/src/main/java/fr/xephi/authme/initialization/ConstructorInjection.java b/src/main/java/fr/xephi/authme/initialization/ConstructorInjection.java index e80ea128c..74ea28b2e 100644 --- a/src/main/java/fr/xephi/authme/initialization/ConstructorInjection.java +++ b/src/main/java/fr/xephi/authme/initialization/ConstructorInjection.java @@ -11,7 +11,7 @@ import java.lang.reflect.InvocationTargetException; /** * Functionality for constructor injection. */ -class ConstructorInjection implements Injection { +public class ConstructorInjection implements Injection { private final Constructor constructor; diff --git a/src/main/java/fr/xephi/authme/initialization/FieldInjection.java b/src/main/java/fr/xephi/authme/initialization/FieldInjection.java index b8112b876..e9717b334 100644 --- a/src/main/java/fr/xephi/authme/initialization/FieldInjection.java +++ b/src/main/java/fr/xephi/authme/initialization/FieldInjection.java @@ -16,7 +16,7 @@ import java.util.List; /** * Functionality for field injection. */ -class FieldInjection implements Injection { +public class FieldInjection implements Injection { private final Field[] fields; private final Constructor defaultConstructor; diff --git a/src/main/java/fr/xephi/authme/initialization/Injection.java b/src/main/java/fr/xephi/authme/initialization/Injection.java index 6e85b4ce5..65acf7961 100644 --- a/src/main/java/fr/xephi/authme/initialization/Injection.java +++ b/src/main/java/fr/xephi/authme/initialization/Injection.java @@ -5,7 +5,7 @@ package fr.xephi.authme.initialization; * * @param the type of the concerned object */ -interface Injection { +public interface Injection { /** * Returns the dependencies that must be provided to instantiate the given item. diff --git a/src/main/java/fr/xephi/authme/listener/AuthMePlayerListener.java b/src/main/java/fr/xephi/authme/listener/AuthMePlayerListener.java index 5d700e4e0..a65177b43 100644 --- a/src/main/java/fr/xephi/authme/listener/AuthMePlayerListener.java +++ b/src/main/java/fr/xephi/authme/listener/AuthMePlayerListener.java @@ -36,9 +36,9 @@ import org.bukkit.event.block.SignChangeEvent; 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.PlayerChatEvent; import org.bukkit.event.player.PlayerCommandPreprocessEvent; import org.bukkit.event.player.PlayerDropItemEvent; import org.bukkit.event.player.PlayerFishEvent; @@ -55,6 +55,9 @@ import org.bukkit.event.player.PlayerRespawnEvent; import org.bukkit.event.player.PlayerShearEntityEvent; import javax.inject.Inject; + +import java.util.Iterator; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Pattern; @@ -70,6 +73,7 @@ public class AuthMePlayerListener implements Listener { public static final ConcurrentHashMap joinMessage = new ConcurrentHashMap<>(); public static final ConcurrentHashMap causeByAuthMe = new ConcurrentHashMap<>(); + @Inject private AuthMe plugin; @Inject @@ -89,24 +93,6 @@ public class AuthMePlayerListener implements Listener { @Inject private ValidationService validationService; - private void handleChat(AsyncPlayerChatEvent event) { - if (settings.getProperty(RestrictionSettings.ALLOW_CHAT)) { - return; - } - - final Player player = event.getPlayer(); - if (shouldCancelEvent(player)) { - event.setCancelled(true); - sendLoginOrRegisterMessage(player); - } else if (settings.getProperty(RestrictionSettings.HIDE_CHAT)) { - for (Player p : bukkitService.getOnlinePlayers()) { - if (!PlayerCache.getInstance().isAuthenticated(p.getName())) { - event.getRecipients().remove(p); - } - } - } - } - private void sendLoginOrRegisterMessage(final Player player) { bukkitService.runTaskAsynchronously(new Runnable() { @Override @@ -144,29 +130,31 @@ public class AuthMePlayerListener implements Listener { sendLoginOrRegisterMessage(event.getPlayer()); } - @EventHandler(ignoreCancelled = true, priority = EventPriority.NORMAL) - public void onPlayerNormalChat(AsyncPlayerChatEvent event) { - handleChat(event); - } - - @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGH) - public void onPlayerHighChat(AsyncPlayerChatEvent event) { - handleChat(event); - } - - @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) - public void onPlayerHighestChat(AsyncPlayerChatEvent event) { - handleChat(event); - } - - @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) - public void onPlayerEarlyChat(AsyncPlayerChatEvent event) { - handleChat(event); - } - @EventHandler(ignoreCancelled = true, priority = EventPriority.LOW) - public void onPlayerLowChat(AsyncPlayerChatEvent event) { - handleChat(event); + public void onPlayerChat(PlayerChatEvent event) { + if (settings.getProperty(RestrictionSettings.ALLOW_CHAT)) { + return; + } + + final Player player = event.getPlayer(); + if (shouldCancelEvent(player)) { + event.setCancelled(true); + bukkitService.runTaskAsynchronously(new Runnable() { + @Override + public void run() { + m.send(player, MessageKey.DENIED_CHAT_MESSAGE); + } + }); + } else if (settings.getProperty(RestrictionSettings.HIDE_CHAT)) { + Set recipients = event.getRecipients(); + Iterator iter = recipients.iterator(); + while (iter.hasNext()) { + Player p = iter.next(); + if (shouldCancelEvent(p)) { + iter.remove(); + } + } + } } @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) @@ -192,7 +180,6 @@ public class AuthMePlayerListener implements Listener { if (!settings.getProperty(RestrictionSettings.ALLOW_UNAUTHED_MOVEMENT)) { event.setTo(event.getFrom()); - // sgdc3 TODO: remove this, maybe we should set the effect every x ticks, idk! if (settings.getProperty(RestrictionSettings.REMOVE_SPEED)) { player.setFlySpeed(0.0f); player.setWalkSpeed(0.0f); @@ -253,7 +240,7 @@ public class AuthMePlayerListener implements Listener { player.setGameMode(GameMode.SURVIVAL); } - // Shedule login task so works after the prelogin + // Schedule login task so works after the prelogin // (Fix found by Koolaid5000) bukkitService.runTask(new Runnable() { @Override @@ -266,6 +253,23 @@ public class AuthMePlayerListener implements Listener { @EventHandler(priority = EventPriority.HIGHEST) public void onPreLogin(AsyncPlayerPreLoginEvent event) { PlayerAuth auth = dataSource.getAuth(event.getName()); + if (auth == null && antiBot.getAntiBotStatus() == AntiBotStatus.ACTIVE) { + event.setKickMessage(m.retrieveSingle(MessageKey.KICK_ANTIBOT)); + event.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_OTHER); + antiBot.antibotKicked.addIfAbsent(event.getName()); + return; + } + if (auth == null && settings.getProperty(RestrictionSettings.KICK_NON_REGISTERED)) { + event.setKickMessage(m.retrieveSingle(MessageKey.MUST_REGISTER_MESSAGE)); + event.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_OTHER); + return; + } + final String name = event.getName().toLowerCase(); + if (name.length() > settings.getProperty(RestrictionSettings.MAX_NICKNAME_LENGTH) || name.length() < settings.getProperty(RestrictionSettings.MIN_NICKNAME_LENGTH)) { + event.setKickMessage(m.retrieveSingle(MessageKey.INVALID_NAME_LENGTH)); + event.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_OTHER); + return; + } if (settings.getProperty(RegistrationSettings.PREVENT_OTHER_CASE) && auth != null && auth.getRealName() != null) { String realName = auth.getRealName(); if (!realName.isEmpty() && !"Player".equals(realName) && !realName.equals(event.getName())) { @@ -287,7 +291,6 @@ public class AuthMePlayerListener implements Listener { } } - final String name = event.getName().toLowerCase(); final Player player = bukkitService.getPlayerExact(name); // Check if forceSingleSession is set to true, so kick player that has // joined with same nick of online player @@ -345,6 +348,7 @@ public class AuthMePlayerListener implements Listener { if (antiBot.getAntiBotStatus() == AntiBotStatus.ACTIVE && !isAuthAvailable) { event.setKickMessage(m.retrieveSingle(MessageKey.KICK_ANTIBOT)); event.setResult(PlayerLoginEvent.Result.KICK_OTHER); + antiBot.antibotKicked.addIfAbsent(player.getName()); return; } @@ -362,7 +366,7 @@ public class AuthMePlayerListener implements Listener { String nickRegEx = settings.getProperty(RestrictionSettings.ALLOWED_NICKNAME_CHARACTERS); Pattern nickPattern = Pattern.compile(nickRegEx); - if (nickPattern.matcher(player.getName()).matches() || name.equalsIgnoreCase("Player")) { + if (name.equalsIgnoreCase("Player") || !nickPattern.matcher(player.getName()).matches()) { event.setKickMessage(m.retrieveSingle(MessageKey.INVALID_NAME_CHARACTERS).replace("REG_EX", nickRegEx)); event.setResult(PlayerLoginEvent.Result.KICK_OTHER); return; @@ -389,6 +393,10 @@ public class AuthMePlayerListener implements Listener { event.setQuitMessage(null); } + if (antiBot.antibotKicked.contains(player.getName())) { + return; + } + management.performQuit(player, false); } @@ -406,6 +414,10 @@ public class AuthMePlayerListener implements Listener { return; } + if (antiBot.antibotKicked.contains(player.getName())) { + return; + } + plugin.getManagement().performQuit(player, true); } diff --git a/src/main/java/fr/xephi/authme/listener/AuthMePlayerListener19.java b/src/main/java/fr/xephi/authme/listener/AuthMePlayerListener19.java deleted file mode 100644 index f5b073cc9..000000000 --- a/src/main/java/fr/xephi/authme/listener/AuthMePlayerListener19.java +++ /dev/null @@ -1,38 +0,0 @@ -package fr.xephi.authme.listener; - -import fr.xephi.authme.settings.NewSetting; -import fr.xephi.authme.settings.SpawnLoader; -import fr.xephi.authme.settings.properties.RestrictionSettings; - -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.spigotmc.event.player.PlayerSpawnLocationEvent; - -import javax.inject.Inject; - -/** - * Listener of player events for events introduced in Minecraft 1.9. - */ -public class AuthMePlayerListener19 implements Listener { - - @Inject - private SpawnLoader spawnLoader; - - @Inject - private NewSetting settings; - - /* WTF was that? We need to check all the settings before moving the player to the spawn! - * - * TODO: fixme please! - * - @EventHandler(priority = EventPriority.LOWEST) - public void onPlayerSpawn(PlayerSpawnLocationEvent event) { - if(settings.getProperty(RestrictionSettings.NO_TELEPORT)) { - return; - } - event.setSpawnLocation(spawnLoader.getSpawnLocation(event.getPlayer())); - } - */ - -} diff --git a/src/main/java/fr/xephi/authme/output/MessageKey.java b/src/main/java/fr/xephi/authme/output/MessageKey.java index 3fd7c747b..a4ab37364 100644 --- a/src/main/java/fr/xephi/authme/output/MessageKey.java +++ b/src/main/java/fr/xephi/authme/output/MessageKey.java @@ -5,6 +5,8 @@ package fr.xephi.authme.output; */ public enum MessageKey { + DENIED_CHAT_MESSAGE("denied_chat"), + KICK_ANTIBOT("kick_antibot"), UNKNOWN_USER("unknown_user"), diff --git a/src/main/java/fr/xephi/authme/settings/Settings.java b/src/main/java/fr/xephi/authme/settings/Settings.java index 0b6d719f8..e66fa2e05 100644 --- a/src/main/java/fr/xephi/authme/settings/Settings.java +++ b/src/main/java/fr/xephi/authme/settings/Settings.java @@ -61,7 +61,7 @@ public final class Settings { getSessionTimeout = configFile.getInt("settings.sessions.timeout", 10); getMaxNickLength = configFile.getInt("settings.restrictions.maxNicknameLength", 20); getMinNickLength = configFile.getInt("settings.restrictions.minNicknameLength", 3); - getNickRegex = configFile.getString("settings.restrictions.allowedNicknameCharacters", "[a-zA-Z0-9_?]*"); + getNickRegex = load(RestrictionSettings.ALLOWED_NICKNAME_CHARACTERS); nickPattern = Pattern.compile(getNickRegex); isAllowRestrictedIp = load(RestrictionSettings.ENABLE_RESTRICTED_USERS); isRemoveSpeedEnabled = load(RestrictionSettings.REMOVE_SPEED); diff --git a/src/main/java/fr/xephi/authme/settings/properties/ProtectionSettings.java b/src/main/java/fr/xephi/authme/settings/properties/ProtectionSettings.java index c32245c49..312f3f514 100644 --- a/src/main/java/fr/xephi/authme/settings/properties/ProtectionSettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/ProtectionSettings.java @@ -28,11 +28,11 @@ public class ProtectionSettings implements SettingsClass { @Comment("Do we need to enable automatic antibot system?") public static final Property ENABLE_ANTIBOT = - newProperty("Protection.enableAntiBot", false); + newProperty("Protection.enableAntiBot", true); @Comment("Max number of players allowed to login in 5 secs before the AntiBot system is enabled automatically") public static final Property ANTIBOT_SENSIBILITY = - newProperty("Protection.antiBotSensibility", 5); + newProperty("Protection.antiBotSensibility", 10); @Comment("Duration in minutes of the antibot automatic system") public static final Property ANTIBOT_DURATION = diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 8ca9c27fd..3cb5f23fa 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -88,7 +88,7 @@ settings: # due to "Logged in from another Location" # This setting will prevent potetial security exploits. ForceSingleSession: true - ForceSpawnLocOnJoin: + ForceSpawnLocOnJoin: # If enabled, every player will be teleported to the world spawnpoint # after successful authentication. # The quit location of the player will be overwritten. @@ -97,7 +97,7 @@ settings: enabled: false # WorldNames where we need to force the spawn location # Case-sensitive! - worlds: + worlds: - 'world' - 'world_nether' - 'world_the_end' @@ -418,8 +418,8 @@ Protection: countriesBlacklist: - 'A1' # Do we need to enable automatic antibot system? - enableAntiBot: false + enableAntiBot: true # Max number of player allowed to login in 5 secs before enable AntiBot system automatically - antiBotSensibility: 5 + antiBotSensibility: 10 # Duration in minutes of the antibot automatic system antiBotDuration: 10 diff --git a/src/main/resources/messages/messages_en.yml b/src/main/resources/messages/messages_en.yml index 5b474e2dc..203ce720f 100644 --- a/src/main/resources/messages/messages_en.yml +++ b/src/main/resources/messages/messages_en.yml @@ -1,3 +1,4 @@ +denied_chat: '&cIn order to be able to chat you must be authenticated!' kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' unknown_user: '&cCan''t find the requested user in the database!' unsafe_spawn: '&cYour quit location was unsafe, you have been teleported to the world''s spawnpoint.' diff --git a/src/test/java/fr/xephi/authme/command/CommandServiceTest.java b/src/test/java/fr/xephi/authme/command/CommandServiceTest.java index ba7e4d281..c07d44d5d 100644 --- a/src/test/java/fr/xephi/authme/command/CommandServiceTest.java +++ b/src/test/java/fr/xephi/authme/command/CommandServiceTest.java @@ -6,7 +6,6 @@ import fr.xephi.authme.output.Messages; import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.domain.Property; import fr.xephi.authme.settings.properties.SecuritySettings; -import fr.xephi.authme.util.BukkitService; import fr.xephi.authme.util.ValidationService; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -45,8 +44,6 @@ public class CommandServiceTest { private NewSetting settings; @Mock private ValidationService validationService; - @Mock - private BukkitService bukkitService; @Test public void shouldSendMessage() { diff --git a/src/test/java/tools/ToolsRunner.java b/src/test/java/tools/ToolsRunner.java index 9986e9819..f8e74aeaa 100644 --- a/src/test/java/tools/ToolsRunner.java +++ b/src/test/java/tools/ToolsRunner.java @@ -81,7 +81,7 @@ public final class ToolsRunner { private static void collectTasksInDirectory(File dir, Map taskCollection) { File[] files = dir.listFiles(); if (files == null) { - throw new RuntimeException("Cannot read folder '" + dir + "'"); + throw new IllegalStateException("Cannot read folder '" + dir + "'"); } for (File file : files) { if (file.isDirectory()) { @@ -112,7 +112,7 @@ public final class ToolsRunner { return constructor.newInstance(); } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException | InstantiationException e) { - throw new RuntimeException("Cannot instantiate task '" + taskClass + "'"); + throw new IllegalStateException("Cannot instantiate task '" + taskClass + "'"); } } @@ -137,7 +137,7 @@ public final class ToolsRunner { ? (Class) clazz : null; } catch (ClassNotFoundException e) { - throw new RuntimeException(e); + throw new IllegalStateException(e); } } diff --git a/src/test/java/tools/checktestmocks/CheckTestMocks.java b/src/test/java/tools/checktestmocks/CheckTestMocks.java new file mode 100644 index 000000000..2508cdd6f --- /dev/null +++ b/src/test/java/tools/checktestmocks/CheckTestMocks.java @@ -0,0 +1,171 @@ +package tools.checktestmocks; + +import com.google.common.base.Function; +import com.google.common.collect.Collections2; +import com.google.common.collect.Sets; +import fr.xephi.authme.initialization.ConstructorInjection; +import fr.xephi.authme.initialization.FieldInjection; +import fr.xephi.authme.initialization.Injection; +import fr.xephi.authme.util.StringUtils; +import org.mockito.Mock; +import tools.utils.AutoToolTask; +import tools.utils.ToolsConstants; + +import java.io.File; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Scanner; +import java.util.Set; + +/** + * Task checking if all tests' {@code @Mock} fields have a corresponding + * {@code @Inject} field in the class they are testing. + */ +public class CheckTestMocks implements AutoToolTask { + + private List errors = new ArrayList<>(); + + @Override + public String getTaskName() { + return "checkTestMocks"; + } + + @Override + public void execute(Scanner scanner) { + executeDefault(); + } + + @Override + public void executeDefault() { + readAndCheckFiles(new File(ToolsConstants.TEST_SOURCE_ROOT)); + System.out.println(StringUtils.join("\n", errors)); + } + + /** + * Recursively reads directories and checks the contained classes. + * + * @param dir the directory to read + */ + private void readAndCheckFiles(File dir) { + File[] files = dir.listFiles(); + if (files == null) { + throw new IllegalStateException("Cannot read folder '" + dir + "'"); + } + for (File file : files) { + if (file.isDirectory()) { + readAndCheckFiles(file); + } else if (file.isFile()) { + Class clazz = loadTestClass(file); + if (clazz != null) { + checkClass(clazz); + } + // else System.out.format("No @Mock fields found in class of file '%s'%n", file.getName()) + } + } + } + + /** + * Checks the given test class' @Mock fields against the corresponding production class' @Inject fields. + * + * @param testClass the test class to verify + */ + private void checkClass(Class testClass) { + Class realClass = returnRealClass(testClass); + if (realClass != null) { + Set> mockFields = getMocks(testClass); + Set> injectFields = getRealClassDependencies(realClass); + if (!injectFields.containsAll(mockFields)) { + addErrorEntry(testClass, "Error - Found the following mocks absent as @Inject: " + + formatClassList(Sets.difference(mockFields, injectFields))); + } else if (!mockFields.containsAll(injectFields)) { + addErrorEntry(testClass, "Info - Found @Inject fields which are not present as @Mock: " + + formatClassList(Sets.difference(injectFields, mockFields))); + } + } + } + + private void addErrorEntry(Class clazz, String message) { + errors.add(clazz.getSimpleName() + ": " + message); + } + + private static Class loadTestClass(File file) { + String fileName = file.getPath(); + String className = fileName + // Strip source folders and .java ending + .substring("src/test/java/".length(), fileName.length() - 5) + .replace(File.separator, "."); + try { + Class clazz = CheckTestMocks.class.getClassLoader().loadClass(className); + return isTestClassWithMocks(clazz) ? clazz : null; + } catch (ClassNotFoundException e) { + throw new UnsupportedOperationException(e); + } + } + + private static Set> getMocks(Class clazz) { + Set> result = new HashSet<>(); + for (Field field : clazz.getDeclaredFields()) { + if (field.isAnnotationPresent(Mock.class)) { + result.add(field.getType()); + } + } + return result; + } + + /** + * Returns the production class ("real class") corresponding to the given test class. + * Returns null if the production class could not be mapped or loaded. + * + * @param testClass the test class to find the corresponding production class for + * @return production class, or null if not found + */ + private static Class returnRealClass(Class testClass) { + String testClassName = testClass.getName(); + String realClassName = testClassName.replaceAll("(Integration|Consistency)?Test$", ""); + if (realClassName.equals(testClassName)) { + System.out.format("%s doesn't match typical test class naming pattern.%n", testClassName); + return null; + } + try { + return CheckTestMocks.class.getClassLoader().loadClass(realClassName); + } catch (ClassNotFoundException e) { + System.out.format("Real class '%s' not found for test class '%s'%n", realClassName, testClassName); + return null; + } + } + + private static Set> getRealClassDependencies(Class realClass) { + Injection injection = ConstructorInjection.provide(realClass).get(); + if (injection != null) { + return Sets.newHashSet(injection.getDependencies()); + } + injection = FieldInjection.provide(realClass).get(); + return injection == null + ? Collections.>emptySet() + : Sets.newHashSet(injection.getDependencies()); + } + + private static boolean isTestClassWithMocks(Class clazz) { + for (Field field : clazz.getDeclaredFields()) { + if (field.isAnnotationPresent(Mock.class)) { + return true; + } + } + return false; + } + + private static String formatClassList(Collection> coll) { + Collection classNames = Collections2.transform(coll, new Function, String>() { + @Override + public String apply(Class input) { + return input.getSimpleName(); + } + }); + return StringUtils.join(", ", classNames); + } + +} diff --git a/src/test/java/tools/utils/ToolsConstants.java b/src/test/java/tools/utils/ToolsConstants.java index 84ad7fadf..c94a22b1f 100644 --- a/src/test/java/tools/utils/ToolsConstants.java +++ b/src/test/java/tools/utils/ToolsConstants.java @@ -12,6 +12,9 @@ public final class ToolsConstants { public static final String MAIN_RESOURCES_ROOT = "src/main/resources/"; + // Add specific `fr.xephi.authme` package as not to include the tool tasks in the `tools` package + public static final String TEST_SOURCE_ROOT = "src/test/java/fr/xephi/authme"; + public static final String TOOLS_SOURCE_ROOT = "src/test/java/tools/"; public static final String DOCS_FOLDER = "docs/";