diff --git a/core/src/main/java/me/xneox/epicguard/core/check/Check.java b/core/src/main/java/me/xneox/epicguard/core/check/Check.java index b026a3f0..6edb929f 100644 --- a/core/src/main/java/me/xneox/epicguard/core/check/Check.java +++ b/core/src/main/java/me/xneox/epicguard/core/check/Check.java @@ -40,7 +40,7 @@ public Check(EpicGuard epicGuard) { * @param expression The expression * @return The return value is based on the check's behaviour. True means positive detection, false means negative. */ - public boolean assertCheck(CheckMode mode, boolean expression) { + public boolean evaluate(CheckMode mode, boolean expression) { if (mode == CheckMode.ALWAYS || mode == CheckMode.ATTACK && this.epicGuard.attackManager().isAttack()) { return expression; } diff --git a/core/src/main/java/me/xneox/epicguard/core/check/impl/AccountLimitCheck.java b/core/src/main/java/me/xneox/epicguard/core/check/impl/AccountLimitCheck.java index 6d10466a..f21208e5 100644 --- a/core/src/main/java/me/xneox/epicguard/core/check/impl/AccountLimitCheck.java +++ b/core/src/main/java/me/xneox/epicguard/core/check/impl/AccountLimitCheck.java @@ -37,7 +37,7 @@ public boolean handle(@Nonnull PendingUser user) { CheckMode mode = CheckMode.valueOf(this.epicGuard.config().accountLimitCheck().checkMode()); List accounts = this.epicGuard.storageManager().accounts(user); - return this.assertCheck(mode, !accounts.contains(user.nickname()) && accounts.size() >= this.epicGuard.config().accountLimitCheck().accountLimit()); + return this.evaluate(mode, !accounts.contains(user.nickname()) && accounts.size() >= this.epicGuard.config().accountLimitCheck().accountLimit()); } @Override diff --git a/core/src/main/java/me/xneox/epicguard/core/check/impl/GeographicalCheck.java b/core/src/main/java/me/xneox/epicguard/core/check/impl/GeographicalCheck.java index ec3165b7..616a027f 100644 --- a/core/src/main/java/me/xneox/epicguard/core/check/impl/GeographicalCheck.java +++ b/core/src/main/java/me/xneox/epicguard/core/check/impl/GeographicalCheck.java @@ -34,7 +34,7 @@ public GeographicalCheck(EpicGuard epicGuard) { @Override public boolean handle(@Nonnull PendingUser user) { CheckMode mode = CheckMode.valueOf(this.epicGuard.config().geographical().checkMode()); - return this.assertCheck(mode, this.isRestricted(user.address())); + return this.evaluate(mode, this.isRestricted(user.address())); } private boolean isRestricted(String address) { @@ -45,10 +45,10 @@ private boolean isRestricted(String address) { return true; } - if (this.epicGuard.config().geographical().checkType().equals("WHITELIST")) { - return !this.epicGuard.config().geographical().countries().contains(country); - } else { + if (this.epicGuard.config().geographical().isBlacklist()) { return this.epicGuard.config().geographical().countries().contains(country); + } else { + return !this.epicGuard.config().geographical().countries().contains(country); } } diff --git a/core/src/main/java/me/xneox/epicguard/core/check/impl/NameSimilarityCheck.java b/core/src/main/java/me/xneox/epicguard/core/check/impl/NameSimilarityCheck.java index 13245580..f6528312 100644 --- a/core/src/main/java/me/xneox/epicguard/core/check/impl/NameSimilarityCheck.java +++ b/core/src/main/java/me/xneox/epicguard/core/check/impl/NameSimilarityCheck.java @@ -3,6 +3,7 @@ import com.google.common.collect.EvictingQueue; import me.xneox.epicguard.core.EpicGuard; import me.xneox.epicguard.core.check.Check; +import me.xneox.epicguard.core.check.CheckMode; import me.xneox.epicguard.core.user.PendingUser; import org.apache.commons.text.similarity.LevenshteinDistance; @@ -21,11 +22,20 @@ public NameSimilarityCheck(EpicGuard epicGuard) { @Override public boolean handle(@Nonnull PendingUser user) { + CheckMode mode = CheckMode.valueOf(this.epicGuard.config().nameSimilarityCheck().checkMode()); + for (String nick : this.nameHistory) { - if (this.distanceAlgorithm.apply(nick, user.nickname()) <= this.epicGuard.config().nameSimilarityCheck().distance()) { - return true; + if (nick.equals(user.nickname())) { + return false; // ignore identical nickname. + } + + int distance = this.distanceAlgorithm.apply(nick, user.nickname()); + if (distance <= this.epicGuard.config().nameSimilarityCheck().distance()) { + return this.evaluate(mode, true); } } + + this.nameHistory.add(user.nickname()); return false; } diff --git a/core/src/main/java/me/xneox/epicguard/core/check/impl/NicknameCheck.java b/core/src/main/java/me/xneox/epicguard/core/check/impl/NicknameCheck.java index 7eea6551..b06bea71 100644 --- a/core/src/main/java/me/xneox/epicguard/core/check/impl/NicknameCheck.java +++ b/core/src/main/java/me/xneox/epicguard/core/check/impl/NicknameCheck.java @@ -20,7 +20,7 @@ public NicknameCheck(EpicGuard epicGuard) { @Override public boolean handle(@Nonnull PendingUser user) { CheckMode mode = CheckMode.valueOf(this.epicGuard.config().nicknameCheck().checkMode()); - return this.assertCheck(mode, user.nickname().matches(this.epicGuard.config().nicknameCheck().expression())); + return this.evaluate(mode, user.nickname().matches(this.epicGuard.config().nicknameCheck().expression())); } @Nonnull diff --git a/core/src/main/java/me/xneox/epicguard/core/check/impl/ProxyCheck.java b/core/src/main/java/me/xneox/epicguard/core/check/impl/ProxyCheck.java index ad730649..fa6286d8 100644 --- a/core/src/main/java/me/xneox/epicguard/core/check/impl/ProxyCheck.java +++ b/core/src/main/java/me/xneox/epicguard/core/check/impl/ProxyCheck.java @@ -34,7 +34,7 @@ public ProxyCheck(EpicGuard epicGuard) { @Override public boolean handle(@Nonnull PendingUser user) { CheckMode mode = CheckMode.valueOf(this.epicGuard.config().proxyCheck().checkMode()); - return this.assertCheck(mode, this.epicGuard.proxyManager().isProxy(user.address())); + return this.evaluate(mode, this.epicGuard.proxyManager().isProxy(user.address())); } @Override diff --git a/core/src/main/java/me/xneox/epicguard/core/check/impl/ReconnectCheck.java b/core/src/main/java/me/xneox/epicguard/core/check/impl/ReconnectCheck.java index be3e911e..f8cf370a 100644 --- a/core/src/main/java/me/xneox/epicguard/core/check/impl/ReconnectCheck.java +++ b/core/src/main/java/me/xneox/epicguard/core/check/impl/ReconnectCheck.java @@ -38,7 +38,7 @@ public ReconnectCheck(EpicGuard epicGuard) { @Override public boolean handle(@Nonnull PendingUser user) { CheckMode mode = CheckMode.valueOf(this.epicGuard.config().misc().reconnectCheckMode()); - return this.assertCheck(mode, needsReconnect(user)); + return this.evaluate(mode, needsReconnect(user)); } private boolean needsReconnect(PendingUser pendingUser) { diff --git a/core/src/main/java/me/xneox/epicguard/core/check/impl/ServerListCheck.java b/core/src/main/java/me/xneox/epicguard/core/check/impl/ServerListCheck.java index 69d72248..574e2d1f 100644 --- a/core/src/main/java/me/xneox/epicguard/core/check/impl/ServerListCheck.java +++ b/core/src/main/java/me/xneox/epicguard/core/check/impl/ServerListCheck.java @@ -34,7 +34,7 @@ public ServerListCheck(EpicGuard epicGuard) { @Override public boolean handle(@Nonnull PendingUser user) { CheckMode mode = CheckMode.valueOf(this.epicGuard.config().misc().serverListCheckMode()); - return this.assertCheck(mode, !this.epicGuard.storageManager().pingCache().contains(user.address())); + return this.evaluate(mode, !this.epicGuard.storageManager().pingCache().contains(user.address())); } @Override diff --git a/core/src/main/java/me/xneox/epicguard/core/config/PluginConfiguration.java b/core/src/main/java/me/xneox/epicguard/core/config/PluginConfiguration.java index 7c478869..5c7c5463 100644 --- a/core/src/main/java/me/xneox/epicguard/core/config/PluginConfiguration.java +++ b/core/src/main/java/me/xneox/epicguard/core/config/PluginConfiguration.java @@ -26,30 +26,45 @@ @ConfigSerializable public class PluginConfiguration { - // Config sections + @Comment("GeographicalCheck will filter countries/cities your players can connect from.") private final Geographical geographical = new Geographical(); + + @Comment("Detect users who are connecting using proxies or VPNs.") private final ProxyCheck proxyCheck = new ProxyCheck(); + + @Comment("This check will limit how many accounts can be registered from single IP address") private final AccountLimitCheck accountLimitCheck = new AccountLimitCheck(); + + @Comment("Every vanilla client sends the Settings packet shortly after joining.\n" + + "Some bots doesn't do this, and this check will try to detect that.") private final SettingsCheck settingsCheck = new SettingsCheck(); + + @Comment("Nickname-check will block players if their nickname matches\n" + + "the regex expression set below.") private final NicknameCheck nicknameCheck = new NicknameCheck(); + + @Comment("NameSimilarityCheck will detect similar nicknames of the connecting users\n" + + "(!) Experimental! https://neox.gitbook.io/epicguard-wiki/configuring/name-similarity-check") private final NameSimilarityCheck nameSimilarityCheck = new NameSimilarityCheck(); - private final ConsoleFilter consoleFilter = new ConsoleFilter(); + @Comment("If a player is online for long enough (see option below)\n" + + "He will be added to the whitelist, and be exempt from every future detections") private final AutoWhitelist autoWhitelist = new AutoWhitelist(); + + private final ConsoleFilter consoleFilter = new ConsoleFilter(); private final Misc misc = new Misc(); @ConfigSerializable public static final class Geographical { - @Comment("Country check will filter countries/cities your players can connect from.\n" + - "NEVER - check is disabled.\n" + + @Comment("NEVER - check is disabled.\n" + "ATTACK - check will be performed only during bot-attack.\n" + "ALWAYS - check will be always performed.") private final String checkMode = "NEVER"; @Comment("This will define if the 'countries' list should be a blacklist or a whitelist.\n" + - "BLACKLIST - countries below are blocked\n" + - "WHITELIST - only countries below are allowed") - private final String checkType = "BLACKLIST"; + "true - configured countries are blocked\n" + + "false - only configured countries are allowed") + private final boolean isBlacklist = false; @Comment("List of country codes: https://dev.maxmind.com/geoip/legacy/codes/iso3166/") private final List countries = Arrays.asList("US", "DE"); @@ -61,8 +76,8 @@ public String checkMode() { return this.checkMode; } - public String checkType() { - return this.checkType; + public boolean isBlacklist() { + return this.isBlacklist; } public List countries() { @@ -76,8 +91,7 @@ public List cityBlacklist() { @ConfigSerializable public static final class ProxyCheck { - @Comment("Detect users who are connecting using proxies or VPNs.\n" + - "NEVER - check is disabled.\n" + + @Comment("NEVER - check is disabled.\n" + "ATTACK - check will be performed only during bot-attack.\n" + "ALWAYS - check will be always performed.") private final String checkMode = "ALWAYS"; @@ -108,8 +122,7 @@ public int cacheDuration() { @ConfigSerializable public static final class AccountLimitCheck { - @Comment("This check will limit how many accounts can be registered from single IP address\n" + - "NEVER - check is disabled.\n" + + @Comment("NEVER - check is disabled.\n" + "ATTACK - check will be performed only during bot-attack.\n" + "ALWAYS - check will be always performed.") private final String checkMode = "ALWAYS"; @@ -128,8 +141,7 @@ public int accountLimit() { @ConfigSerializable public static final class SettingsCheck { - @Comment("Every vanilla client sends the Settings packet shortly after joining.\n" + - "Some bots doesn't do this, and this check will try to detect that.") + @Comment("Enable or disable this check.") private final boolean enabled = true; @Comment("Delay in seconds after which we check if the player has already sent this packet.\n" + @@ -147,9 +159,7 @@ public int delay() { @ConfigSerializable public static final class NicknameCheck { - @Comment("Nickname-check will block players if their nickname matches\n" - + "the regex expression set below.\n" + - "NEVER - check is disabled.\n" + + @Comment("NEVER - check is disabled.\n" + "ATTACK - check will be performed only during bot-attack.\n" + "ALWAYS - check will be always performed.") private final String checkMode = "ALWAYS"; @@ -169,22 +179,21 @@ public String expression() { @ConfigSerializable public static final class NameSimilarityCheck { - @Comment("NameSimilarityCheck will detect similar nicknames\n" - + "of the connecting users.\n" + - "NEVER - check is disabled.\n" + + @Comment("NEVER - check is disabled.\n" + "ATTACK - check will be performed only during bot-attack.\n" + "ALWAYS - check will be always performed.") - private final String checkMode = "ATTACK"; + private final String checkMode = "NEVER"; @Comment("How many nicknames should be keep in the history?\n" + "When an user is connecting to the server, his nickname will be added to the history.\n" + "Then the nickname will be compared with other nicknames stored in the history.\n" + "(!) Requires restart to apply.") - private final int historySize = 10; + private final int historySize = 5; @Comment("The lower the distance, the similar the names.\n" + - "If the distance detected is lower or the same as\n" + - "configured below, it will be considered as a positive detection.") + "If the distance detected is lower or same as configured here,\n" + + "it will be considered as a positive detection.\n" + + "Values below 1 are ignored, as it means identical name.") private final int distance = 1; public String checkMode() { @@ -229,9 +238,7 @@ public List filterMessages() { @ConfigSerializable public static final class AutoWhitelist { - @Comment("If a player is online for long enough (see option below)\n" + - "He will be added to the whitelist, and be exempt from every future detections" + - "ADDRESS - Only whitelist the user's IP address.\n" + + @Comment("ADDRESS - Only whitelist the user's IP address.\n" + "NICKNAME - Only whitelist the user's nickname.\n" + "MIXED - Whitelist the user's IP address AND nickname.\n" + "DISABLED - Disable this feature") diff --git a/core/src/main/java/me/xneox/epicguard/core/handler/JoinHandler.java b/core/src/main/java/me/xneox/epicguard/core/handler/JoinHandler.java index 04cd2bc4..69897ead 100644 --- a/core/src/main/java/me/xneox/epicguard/core/handler/JoinHandler.java +++ b/core/src/main/java/me/xneox/epicguard/core/handler/JoinHandler.java @@ -53,11 +53,11 @@ public void handle(@Nonnull UUID uuid, @Nonnull String address, @Nonnull String if (mode != WhitelistMode.DISABLED) { this.epicGuard.platform().runTaskLater(() -> { if (user != null) { - if (mode == WhitelistMode.MIXED || mode == WhitelistMode.ADDRESS) { + if ((mode == WhitelistMode.MIXED || mode == WhitelistMode.ADDRESS) && !this.epicGuard.storageManager().isWhitelisted(address)) { this.epicGuard.storageManager().whitelistPut(address); } - if (mode == WhitelistMode.MIXED || mode == WhitelistMode.NICKNAME) { + if ((mode == WhitelistMode.MIXED || mode == WhitelistMode.NICKNAME) && !this.epicGuard.storageManager().isWhitelisted(nickname)) { this.epicGuard.storageManager().whitelistPut(nickname); } }