diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/FastLoginBukkit.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/FastLoginBukkit.java
index ab4ceed..9c2fa3a 100644
--- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/FastLoginBukkit.java
+++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/FastLoginBukkit.java
@@ -114,9 +114,9 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman
}
if (pluginManager.isPluginEnabled("ProtocolSupport")) {
- pluginManager.registerEvents(new ProtocolSupportListener(this, core.getRateLimiter()), this);
+ pluginManager.registerEvents(new ProtocolSupportListener(this, core.getAntiBot()), this);
} else if (pluginManager.isPluginEnabled("ProtocolLib")) {
- ProtocolLibListener.register(this, core.getRateLimiter());
+ ProtocolLibListener.register(this, core.getAntiBot());
if (isPluginInstalled("floodgate")) {
if (getConfig().getBoolean("floodgatePrefixWorkaround")){
diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java
index 8b3d601..3c161b7 100644
--- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java
+++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java
@@ -31,8 +31,10 @@ import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
-import com.github.games647.fastlogin.core.RateLimiter;
+import com.github.games647.fastlogin.core.antibot.AntiBotService;
+import com.github.games647.fastlogin.core.antibot.AntiBotService.Action;
+import java.net.InetSocketAddress;
import java.security.KeyPair;
import java.security.SecureRandom;
@@ -50,9 +52,9 @@ public class ProtocolLibListener extends PacketAdapter {
//just create a new once on plugin enable. This used for verify token generation
private final SecureRandom random = new SecureRandom();
private final KeyPair keyPair = EncryptionUtil.generateKeyPair();
- private final RateLimiter rateLimiter;
+ private final AntiBotService antiBotService;
- public ProtocolLibListener(FastLoginBukkit plugin, RateLimiter rateLimiter) {
+ public ProtocolLibListener(FastLoginBukkit plugin, AntiBotService antiBotService) {
//run async in order to not block the server, because we are making api calls to Mojang
super(params()
.plugin(plugin)
@@ -60,15 +62,15 @@ public class ProtocolLibListener extends PacketAdapter {
.optionAsync());
this.plugin = plugin;
- this.rateLimiter = rateLimiter;
+ this.antiBotService = antiBotService;
}
- public static void register(FastLoginBukkit plugin, RateLimiter rateLimiter) {
+ public static void register(FastLoginBukkit plugin, AntiBotService antiBotService) {
// they will be created with a static builder, because otherwise it will throw a NoClassDefFoundError
// TODO: make synchronous processing, but do web or database requests async
ProtocolLibrary.getProtocolManager()
.getAsynchronousManager()
- .registerAsyncHandler(new ProtocolLibListener(plugin, rateLimiter))
+ .registerAsyncHandler(new ProtocolLibListener(plugin, antiBotService))
.start();
}
@@ -88,12 +90,26 @@ public class ProtocolLibListener extends PacketAdapter {
Player sender = packetEvent.getPlayer();
PacketType packetType = packetEvent.getPacketType();
if (packetType == START) {
- if (!rateLimiter.tryAcquire()) {
- plugin.getLog().warn("Simple Anti-Bot join limit - Ignoring {}", sender);
- return;
+ PacketContainer packet = packetEvent.getPacket();
+
+ InetSocketAddress address = sender.getAddress();
+ String username = packet.getGameProfiles().read(0).getName();
+
+ Action action = antiBotService.onIncomingConnection(address, username);
+ switch (action) {
+ case Ignore:
+ // just ignore
+ return;
+ case Block:
+ String message = plugin.getCore().getMessage("kick-antibot");
+ sender.kickPlayer(message);
+ break;
+ case Continue:
+ default:
+ //player.getName() won't work at this state
+ onLogin(packetEvent, sender, username);
+ break;
}
-
- onLogin(packetEvent, sender);
} else {
onEncryptionBegin(packetEvent, sender);
}
@@ -113,18 +129,13 @@ public class ProtocolLibListener extends PacketAdapter {
plugin.getScheduler().runAsync(verifyTask);
}
- private void onLogin(PacketEvent packetEvent, Player player) {
+ private void onLogin(PacketEvent packetEvent, Player player, String username) {
//this includes ip:port. Should be unique for an incoming login request with a timeout of 2 minutes
String sessionKey = player.getAddress().toString();
//remove old data every time on a new login in order to keep the session only for one person
plugin.removeSession(player.getAddress());
- //player.getName() won't work at this state
- PacketContainer packet = packetEvent.getPacket();
-
- String username = packet.getGameProfiles().read(0).getName();
-
if (packetEvent.getPacket().getMeta("original_name").isPresent()) {
//username has been injected by ManualNameChange.java
username = (String) packetEvent.getPacket().getMeta("original_name").get();
diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocolsupport/ProtocolSupportListener.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocolsupport/ProtocolSupportListener.java
index 608078c..ce84824 100644
--- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocolsupport/ProtocolSupportListener.java
+++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocolsupport/ProtocolSupportListener.java
@@ -29,8 +29,9 @@ import com.github.games647.craftapi.UUIDAdapter;
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.bukkit.event.BukkitFastLoginPreLoginEvent;
-import com.github.games647.fastlogin.core.RateLimiter;
import com.github.games647.fastlogin.core.StoredProfile;
+import com.github.games647.fastlogin.core.antibot.AntiBotService;
+import com.github.games647.fastlogin.core.antibot.AntiBotService.Action;
import com.github.games647.fastlogin.core.shared.JoinManagement;
import com.github.games647.fastlogin.core.shared.event.FastLoginPreLoginEvent;
@@ -49,13 +50,13 @@ public class ProtocolSupportListener extends JoinManagement<Player, CommandSende
implements Listener {
private final FastLoginBukkit plugin;
- private final RateLimiter rateLimiter;
+ private final AntiBotService antiBotService;
- public ProtocolSupportListener(FastLoginBukkit plugin, RateLimiter rateLimiter) {
+ public ProtocolSupportListener(FastLoginBukkit plugin, AntiBotService antiBotService) {
super(plugin.getCore(), plugin.getCore().getAuthPluginHook(), plugin.getBedrockService());
this.plugin = plugin;
- this.rateLimiter = rateLimiter;
+ this.antiBotService = antiBotService;
}
@EventHandler
@@ -64,19 +65,28 @@ public class ProtocolSupportListener extends JoinManagement<Player, CommandSende
return;
}
- if (!rateLimiter.tryAcquire()) {
- plugin.getLog().warn("Simple Anti-Bot join limit - Ignoring {}", loginStartEvent.getConnection());
- return;
- }
-
String username = loginStartEvent.getConnection().getProfile().getName();
InetSocketAddress address = loginStartEvent.getAddress();
-
- //remove old data every time on a new login in order to keep the session only for one person
- plugin.removeSession(address);
-
- ProtocolLoginSource source = new ProtocolLoginSource(loginStartEvent);
- super.onLogin(username, source);
+ plugin.getLog().info("Incoming login request for {} from {}", username, address);
+
+ Action action = antiBotService.onIncomingConnection(address, username);
+ switch (action) {
+ case Ignore:
+ // just ignore
+ return;
+ case Block:
+ String message = plugin.getCore().getMessage("kick-antibot");
+ loginStartEvent.denyLogin(message);
+ break;
+ case Continue:
+ default:
+ //remove old data every time on a new login in order to keep the session only for one person
+ plugin.removeSession(address);
+
+ ProtocolLoginSource source = new ProtocolLoginSource(loginStartEvent);
+ super.onLogin(username, source);
+ break;
+ }
}
@EventHandler
diff --git a/bungee/src/main/java/com/github/games647/fastlogin/bungee/listener/ConnectListener.java b/bungee/src/main/java/com/github/games647/fastlogin/bungee/listener/ConnectListener.java
index a8c894d..ddd7e97 100644
--- a/bungee/src/main/java/com/github/games647/fastlogin/bungee/listener/ConnectListener.java
+++ b/bungee/src/main/java/com/github/games647/fastlogin/bungee/listener/ConnectListener.java
@@ -32,7 +32,8 @@ import com.github.games647.fastlogin.bungee.task.AsyncPremiumCheck;
import com.github.games647.fastlogin.bungee.task.FloodgateAuthTask;
import com.github.games647.fastlogin.bungee.task.ForceLoginTask;
import com.github.games647.fastlogin.core.StoredProfile;
-import com.github.games647.fastlogin.core.antibot.RateLimiter;
+import com.github.games647.fastlogin.core.antibot.AntiBotService;
+import com.github.games647.fastlogin.core.antibot.AntiBotService.Action;
import com.github.games647.fastlogin.core.hooks.bedrock.FloodgateService;
import com.github.games647.fastlogin.core.shared.LoginSession;
import com.google.common.base.Throwables;
@@ -41,8 +42,10 @@ import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.reflect.Field;
+import java.net.InetSocketAddress;
import java.util.UUID;
+import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.PendingConnection;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.connection.Server;
@@ -92,12 +95,12 @@ public class ConnectListener implements Listener {
}
private final FastLoginBungee plugin;
- private final RateLimiter rateLimiter;
+ private final AntiBotService antiBotService;
private final Property[] emptyProperties = {};
- public ConnectListener(FastLoginBungee plugin, RateLimiter rateLimiter) {
+ public ConnectListener(FastLoginBungee plugin, AntiBotService antiBotService) {
this.plugin = plugin;
- this.rateLimiter = rateLimiter;
+ this.antiBotService = antiBotService;
}
@EventHandler
@@ -107,17 +110,28 @@ public class ConnectListener implements Listener {
return;
}
- if (!rateLimiter.tryAcquire()) {
- plugin.getLog().warn("Simple Anti-Bot join limit - Ignoring {}", connection);
- return;
- }
-
+ InetSocketAddress address = preLoginEvent.getConnection().getAddress();
String username = connection.getName();
+
plugin.getLog().info("Incoming login request for {} from {}", username, connection.getSocketAddress());
- preLoginEvent.registerIntent(plugin);
- Runnable asyncPremiumCheck = new AsyncPremiumCheck(plugin, preLoginEvent, connection, username);
- plugin.getScheduler().runAsync(asyncPremiumCheck);
+ Action action = antiBotService.onIncomingConnection(address, username);
+ switch (action) {
+ case Ignore:
+ // just ignore
+ return;
+ case Block:
+ String message = plugin.getCore().getMessage("kick-antibot");
+ preLoginEvent.setCancelReason(TextComponent.fromLegacyText(message));
+ preLoginEvent.setCancelled(true);
+ break;
+ case Continue:
+ default:
+ preLoginEvent.registerIntent(plugin);
+ Runnable asyncPremiumCheck = new AsyncPremiumCheck(plugin, preLoginEvent, connection, username);
+ plugin.getScheduler().runAsync(asyncPremiumCheck);
+ break;
+ }
}
@EventHandler(priority = EventPriority.LOWEST)
@@ -140,7 +154,7 @@ public class ConnectListener implements Listener {
StoredProfile playerProfile = session.getProfile();
playerProfile.setId(verifiedUUID);
- // bungeecord will do this automatically so override it on disabled option
+ // BungeeCord will do this automatically so override it on disabled option
if (uniqueIdSetter != null) {
InitialHandler initialHandler = (InitialHandler) connection;
diff --git a/core/src/main/java/com/github/games647/fastlogin/core/mojang/MojangApiConnector.java b/core/src/main/java/com/github/games647/fastlogin/core/antibot/AntiBotService.java
similarity index 57%
rename from core/src/main/java/com/github/games647/fastlogin/core/mojang/MojangApiConnector.java
rename to core/src/main/java/com/github/games647/fastlogin/core/antibot/AntiBotService.java
index 4321d6f..2848335 100644
--- a/core/src/main/java/com/github/games647/fastlogin/core/mojang/MojangApiConnector.java
+++ b/core/src/main/java/com/github/games647/fastlogin/core/antibot/AntiBotService.java
@@ -23,3 +23,40 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+package com.github.games647.fastlogin.core.antibot;
+
+import java.net.InetSocketAddress;
+
+import org.slf4j.Logger;
+
+public class AntiBotService {
+
+ private final Logger logger;
+
+ private final RateLimiter rateLimiter;
+ private final Action limitReachedAction;
+
+ public AntiBotService(Logger logger, RateLimiter rateLimiter, Action limitReachedAction) {
+ this.logger = logger;
+
+ this.rateLimiter = rateLimiter;
+ this.limitReachedAction = limitReachedAction;
+ }
+
+ public Action onIncomingConnection(InetSocketAddress clientAddress, String username) {
+ if (!rateLimiter.tryAcquire()) {
+ logger.warn("Anti-Bot join limit - Ignoring {}", clientAddress);
+ return limitReachedAction;
+ }
+
+ return Action.Continue;
+ }
+
+ public enum Action {
+ Ignore,
+
+ Block,
+
+ Continue;
+ }
+}
diff --git a/core/src/main/java/com/github/games647/fastlogin/core/mojang/ProxyAgnosticMojangResolver.java b/core/src/main/java/com/github/games647/fastlogin/core/antibot/ProxyAgnosticMojangResolver.java
similarity index 100%
rename from core/src/main/java/com/github/games647/fastlogin/core/mojang/ProxyAgnosticMojangResolver.java
rename to core/src/main/java/com/github/games647/fastlogin/core/antibot/ProxyAgnosticMojangResolver.java
diff --git a/core/src/main/java/com/github/games647/fastlogin/core/RateLimiter.java b/core/src/main/java/com/github/games647/fastlogin/core/antibot/RateLimiter.java
similarity index 96%
rename from core/src/main/java/com/github/games647/fastlogin/core/RateLimiter.java
rename to core/src/main/java/com/github/games647/fastlogin/core/antibot/RateLimiter.java
index 3ec2f75..b791ca5 100644
--- a/core/src/main/java/com/github/games647/fastlogin/core/RateLimiter.java
+++ b/core/src/main/java/com/github/games647/fastlogin/core/antibot/RateLimiter.java
@@ -23,7 +23,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-package com.github.games647.fastlogin.core;
+package com.github.games647.fastlogin.core.antibot;
@FunctionalInterface
public interface RateLimiter {
diff --git a/core/src/main/java/com/github/games647/fastlogin/core/TickingRateLimiter.java b/core/src/main/java/com/github/games647/fastlogin/core/antibot/TickingRateLimiter.java
similarity index 98%
rename from core/src/main/java/com/github/games647/fastlogin/core/TickingRateLimiter.java
rename to core/src/main/java/com/github/games647/fastlogin/core/antibot/TickingRateLimiter.java
index 6064229..ced7da1 100644
--- a/core/src/main/java/com/github/games647/fastlogin/core/TickingRateLimiter.java
+++ b/core/src/main/java/com/github/games647/fastlogin/core/antibot/TickingRateLimiter.java
@@ -23,7 +23,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-package com.github.games647.fastlogin.core;
+package com.github.games647.fastlogin.core.antibot;
import com.google.common.base.Ticker;
diff --git a/core/src/main/java/com/github/games647/fastlogin/core/mojang/UUIDTypeAdapter.java b/core/src/main/java/com/github/games647/fastlogin/core/mojang/UUIDTypeAdapter.java
deleted file mode 100644
index 4321d6f..0000000
--- a/core/src/main/java/com/github/games647/fastlogin/core/mojang/UUIDTypeAdapter.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * The MIT License (MIT)
- *
- * Copyright (c) 2015-2022 games647 and contributors
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
diff --git a/core/src/main/java/com/github/games647/fastlogin/core/shared/FastLoginCore.java b/core/src/main/java/com/github/games647/fastlogin/core/shared/FastLoginCore.java
index 9e2a49a..474a8e2 100644
--- a/core/src/main/java/com/github/games647/fastlogin/core/shared/FastLoginCore.java
+++ b/core/src/main/java/com/github/games647/fastlogin/core/shared/FastLoginCore.java
@@ -28,8 +28,10 @@ package com.github.games647.fastlogin.core.shared;
import com.github.games647.craftapi.resolver.MojangResolver;
import com.github.games647.craftapi.resolver.http.RotatingProxySelector;
import com.github.games647.fastlogin.core.CommonUtil;
-import com.github.games647.fastlogin.core.RateLimiter;
-import com.github.games647.fastlogin.core.TickingRateLimiter;
+import com.github.games647.fastlogin.core.antibot.AntiBotService;
+import com.github.games647.fastlogin.core.antibot.AntiBotService.Action;
+import com.github.games647.fastlogin.core.antibot.RateLimiter;
+import com.github.games647.fastlogin.core.antibot.TickingRateLimiter;
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import com.github.games647.fastlogin.core.hooks.DefaultPasswordGenerator;
import com.github.games647.fastlogin.core.hooks.PasswordGenerator;
@@ -88,7 +90,7 @@ public class FastLoginCore<P extends C, C, T extends PlatformPlugin<C>> {
private Configuration config;
private SQLStorage storage;
- private RateLimiter rateLimiter;
+ private AntiBotService antiBot;
private PasswordGenerator<P> passwordGenerator = new DefaultPasswordGenerator<>();
private AuthPlugin<P> authPlugin;
@@ -122,7 +124,7 @@ public class FastLoginCore<P extends C, C, T extends PlatformPlugin<C>> {
// Initialize the resolver based on the config parameter
this.resolver = this.config.getBoolean("useProxyAgnosticResolver", false) ? new ProxyAgnosticMojangResolver() : new MojangResolver();
- rateLimiter = createRateLimiter(config.getSection("anti-bot"));
+ antiBot = createAntiBotService(config.getSection("anti-bot"));
Set<Proxy> proxies = config.getStringList("proxies")
.stream()
.map(HostAndPort::fromString)
@@ -144,20 +146,34 @@ public class FastLoginCore<P extends C, C, T extends PlatformPlugin<C>> {
resolver.setOutgoingAddresses(addresses);
}
- private RateLimiter createRateLimiter(Configuration botSection) {
- boolean enabled = botSection.getBoolean("enabled", true);
- if (!enabled) {
+ private AntiBotService createAntiBotService(Configuration botSection) {
+ RateLimiter rateLimiter;
+ if (botSection.getBoolean("enabled", true)) {
+ int maxCon = botSection.getInt("connections", 200);
+ long expireTime = botSection.getLong("expire", 5) * 60 * 1_000L;
+ if (expireTime > MAX_EXPIRE_RATE) {
+ expireTime = MAX_EXPIRE_RATE;
+ }
+
+ rateLimiter = new TickingRateLimiter(Ticker.systemTicker(), maxCon, expireTime);
+ } else {
// no-op rate limiter
- return () -> true;
+ rateLimiter = () -> true;
}
- int maxCon = botSection.getInt("connections", 200);
- long expireTime = botSection.getLong("expire", 5) * 60 * 1_000L;
- if (expireTime > MAX_EXPIRE_RATE) {
- expireTime = MAX_EXPIRE_RATE;
+ Action action = Action.Ignore;
+ switch (botSection.getString("action", "ignore")) {
+ case "ignore":
+ action = Action.Ignore;
+ break;
+ case "block":
+ action = Action.Block;
+ break;
+ default:
+ plugin.getLog().warn("Invalid anti bot action - defaulting to ignore");
}
- return new TickingRateLimiter(Ticker.systemTicker(), maxCon, expireTime);
+ return new AntiBotService(plugin.getLog(), rateLimiter, action);
}
private Configuration loadFile(String fileName) throws IOException {
@@ -285,8 +301,8 @@ public class FastLoginCore<P extends C, C, T extends PlatformPlugin<C>> {
return authPlugin;
}
- public RateLimiter getRateLimiter() {
- return rateLimiter;
+ public AntiBotService getAntiBot() {
+ return antiBot;
}
public void setAuthPluginHook(AuthPlugin<P> authPlugin) {
diff --git a/core/src/main/resources/config.yml b/core/src/main/resources/config.yml
index 1b0905d..3a2ab7a 100644
--- a/core/src/main/resources/config.yml
+++ b/core/src/main/resources/config.yml
@@ -20,6 +20,9 @@ anti-bot:
connections: 600
# Amount of minutes after the first connection got inserted will expire and made available
expire: 10
+ # Action - Which action should be performed when the bucket is full (too many connections)
+ # Allowed values are: 'ignore' (FastLogin drops handling the player) or 'block' (block this incoming connection)
+ action: 'ignore'
# Request a premium login without forcing the player to type a command
#
diff --git a/core/src/main/resources/messages.yml b/core/src/main/resources/messages.yml
index 8b315ba..33cadaa 100644
--- a/core/src/main/resources/messages.yml
+++ b/core/src/main/resources/messages.yml
@@ -48,6 +48,9 @@ not-premium-other: '&4Player is not in the premium list'
# Admin wanted to change the premium of a user that isn't known to the plugin
player-unknown: '&4Player not in the database'
+# Player kicked from anti bot feature
+kick-antibot: '&4Please wait a moment!'
+
# ========= Bukkit/Spigot ================
# The user skipped the authentication, because it was a premium player
diff --git a/core/src/test/java/com/github/games647/fastlogin/core/TickingRateLimiterTest.java b/core/src/test/java/com/github/games647/fastlogin/core/TickingRateLimiterTest.java
index 2c04b6e..3f753cd 100644
--- a/core/src/test/java/com/github/games647/fastlogin/core/TickingRateLimiterTest.java
+++ b/core/src/test/java/com/github/games647/fastlogin/core/TickingRateLimiterTest.java
@@ -25,6 +25,8 @@
*/
package com.github.games647.fastlogin.core;
+import com.github.games647.fastlogin.core.antibot.TickingRateLimiter;
+
import java.time.Duration;
import java.util.concurrent.TimeUnit;
diff --git a/velocity/src/main/java/com/github/games647/fastlogin/velocity/listener/ConnectListener.java b/velocity/src/main/java/com/github/games647/fastlogin/velocity/listener/ConnectListener.java
index 2b51bd2..33c8c31 100644
--- a/velocity/src/main/java/com/github/games647/fastlogin/velocity/listener/ConnectListener.java
+++ b/velocity/src/main/java/com/github/games647/fastlogin/velocity/listener/ConnectListener.java
@@ -27,7 +27,8 @@ package com.github.games647.fastlogin.velocity.listener;
import com.github.games647.craftapi.UUIDAdapter;
import com.github.games647.fastlogin.core.StoredProfile;
-import com.github.games647.fastlogin.core.antibot.RateLimiter;
+import com.github.games647.fastlogin.core.antibot.AntiBotService;
+import com.github.games647.fastlogin.core.antibot.AntiBotService.Action;
import com.github.games647.fastlogin.core.shared.LoginSession;
import com.github.games647.fastlogin.velocity.FastLoginVelocity;
import com.github.games647.fastlogin.velocity.VelocityLoginSession;
@@ -37,6 +38,7 @@ import com.velocitypowered.api.event.Continuation;
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.connection.DisconnectEvent;
import com.velocitypowered.api.event.connection.PreLoginEvent;
+import com.velocitypowered.api.event.connection.PreLoginEvent.PreLoginComponentResult;
import com.velocitypowered.api.event.player.GameProfileRequestEvent;
import com.velocitypowered.api.event.player.ServerConnectedEvent;
import com.velocitypowered.api.proxy.InboundConnection;
@@ -44,19 +46,23 @@ import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.util.GameProfile;
+import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
+import net.kyori.adventure.text.TextComponent;
+import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
+
public class ConnectListener {
private final FastLoginVelocity plugin;
- private final RateLimiter rateLimiter;
+ private final AntiBotService antiBotService;
- public ConnectListener(FastLoginVelocity plugin, RateLimiter rateLimiter) {
+ public ConnectListener(FastLoginVelocity plugin, AntiBotService antiBotService) {
this.plugin = plugin;
- this.rateLimiter = rateLimiter;
+ this.antiBotService = antiBotService;
}
@Subscribe
@@ -66,16 +72,28 @@ public class ConnectListener {
}
InboundConnection connection = preLoginEvent.getConnection();
- if (!rateLimiter.tryAcquire()) {
- plugin.getLog().warn("Simple Anti-Bot join limit - Ignoring {}", connection);
- return;
- }
-
String username = preLoginEvent.getUsername();
- plugin.getLog().info("Incoming login request for {} from {}", username, connection.getRemoteAddress());
-
- Runnable asyncPremiumCheck = new AsyncPremiumCheck(plugin, connection, username, continuation, preLoginEvent);
- plugin.getScheduler().runAsync(asyncPremiumCheck);
+ InetSocketAddress address = connection.getRemoteAddress();
+ plugin.getLog().info("Incoming login request for {} from {}", username, address);
+
+ Action action = antiBotService.onIncomingConnection(address, username);
+ switch (action) {
+ case Ignore:
+ // just ignore
+ return;
+ case Block:
+ String message = plugin.getCore().getMessage("kick-antibot");
+ TextComponent messageParsed = LegacyComponentSerializer.legacyAmpersand().deserialize(message);
+
+ PreLoginComponentResult reason = PreLoginComponentResult.denied(messageParsed);
+ preLoginEvent.setResult(reason);
+ break;
+ case Continue:
+ default:
+ Runnable asyncPremiumCheck = new AsyncPremiumCheck(plugin, connection, username, continuation, preLoginEvent);
+ plugin.getScheduler().runAsync(asyncPremiumCheck);
+ break;
+ }
}
@Subscribe
FastLogin
Checks if a Minecraft player has a paid account (premium). If so, they can skip offline authentication (auth plugins). So they don't need to enter passwords. This is also called auto login (auto-login).
Features
- Detect paid accounts from others
- Automatically login paid accounts (premium)
- Support various of auth plugins
- Cauldron support
- Forge/Sponge message support
- Premium UUID support
- Forward skins
- Detect username changed and will update the existing database record
- BungeeCord support
- Auto register new premium players
- Plugin: ProtocolSupport is supported and can be used as an alternative to ProtocolLib
- No client modifications needed
- Good performance by using async operations
- Locale messages
- Support for Bedrock players proxied through FloodGate
Issues
Please use issues for bug reports, suggestions, questions and more. Please check for existing issues. Existing issues can be voted up by adding up vote to the original post. Closing issues means that they are marked as resolved. Comments are still allowed and it could be re-opened.
Development builds
Development builds contain the latest changes from the Source-Code. They are bleeding edge and could introduce new bugs,
but also include features, enhancements and bug fixes that are not yet in a released version. If you click on the left
side on Changes, you can see iterative change sets leading to a specific build.
You can download them from here: https://ci.codemc.org/job/Games647/job/FastLogin/
Commands
/premium [player] Label the invoker or the argument as paid account
/cracked [player] Label the invoker or the argument as cracked account
Permissions
fastlogin.bukkit.command.premium
fastlogin.bukkit.command.cracked
fastlogin.command.premium.other
fastlogin.command.cracked.other
Placeholder
This plugin supports PlaceholderAPI on Spigot. It exports the following variable
%fastlogin_status%. In BungeeCord environments, the status of a player will be delivered with a delay after the player
already successful joined the server. This takes about a couple of milliseconds. In this case the value
will be Unknown.
Possible values: Premium, Cracked, Unknown
Requirements
- Java 17+
- Server software in offlinemode:
- Spigot (or a fork e.g. Paper) 1.8.8+
- Protocol plugin:
- Latest BungeeCord (or a fork e.g. Waterfall)
- Spigot (or a fork e.g. Paper) 1.8.8+
- An auth plugin.
Supported auth plugins
Spigot/Paper
- AdvancedLogin (Paid)
- AuthMe (5.X)
- CrazyLogin
- LoginSecurity
- LogIt
- SodionAuth (2.0+)
- UltraAuth
- UserLogin
- xAuth
BungeeCord/Waterfall
Network requests
This plugin performs network requests to:
- https://api.mojang.com - retrieving uuid data to decide if we should activate premium login
- https://sessionserver.mojang.com - verify if the player is the owner of that account
How to install
Spigot/Paper
- Download and install ProtocolLib/ProtocolSupport
- Download and install FastLogin (or
FastLoginBukkitfor newer versions) - Set your server in offline mode by setting the value
onlinemodein your server.properties to false
BungeeCord/Waterfall or Velocity
Install the plugin on both platforms, that is proxy (BungeeCord or Velocity) and backend server (Spigot).
- Activate proxy support in the server configuration
- This is often found in
spigot.ymlorpaper.yml
- This is often found in
- Restart the backend server
- Now there is
allowed-proxies.txtfile in the FastLogin folder of the restarted server- BungeeCord: Put your
stats-idfrom the BungeeCord config into this file - Velocity: On plugin startup the plugin generates a
proxyId.txtinside the plugins folder of the proxy
- BungeeCord: Put your
- Activate ip forwarding in your proxy config
- Check your database settings in the config of FastLogin on your proxy
- The proxies only ship with a limited set of drivers where Spigot supports more. Therefore, these are supported:
- BungeeCord:
com.mysql.jdbc.Driverfor MySQL/MariaDB - Velocity:
fastlogin.mariadb.jdbc.Driverfor MySQL/MariaDB - Note the embedded file storage SQLite is not available
- MySQL/MariaDB requires an external database server running. Check your server provider if there is one available or install one.
- Set proxy and Spigot in offline mode by setting the value
onlinemodein yourconfig.ymlto false - You should always configure the firewall for your Spigot server so that it's only accessible through your proxy
- This is also the case without this plugin
- https://www.spigotmc.org/wiki/bungeecord-installation/#post-installation