Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

better floodgate support #135

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 39 additions & 10 deletions src/main/java/net/elytrium/limboauth/LimboAuth.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,15 @@

package net.elytrium.limboauth;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.primitives.Bytes;
import com.google.common.primitives.Longs;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.inject.Inject;
import com.google.zxing.common.StringUtils;
import com.j256.ormlite.dao.Dao;
import com.j256.ormlite.dao.DaoManager;
import com.j256.ormlite.dao.GenericRawResults;
Expand Down Expand Up @@ -51,6 +54,7 @@
import com.velocitypowered.proxy.util.ratelimit.Ratelimiter;
import com.velocitypowered.proxy.util.ratelimit.Ratelimiters;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import io.netty.util.internal.StringUtil;
import io.whitfin.siphash.SipHasher;
import java.io.File;
import java.io.IOException;
Expand All @@ -66,14 +70,7 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.SQLException;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
Expand Down Expand Up @@ -187,6 +184,11 @@ public class LimboAuth {
private Dao<RegisteredPlayer, String> playerDao;
private Pattern nicknameValidationPattern;
private Limbo authServer;
private Cache<String, String> joinHosts = CacheBuilder.newBuilder().expireAfterWrite(10, TimeUnit.MINUTES).build();
public HashMap<String, Boolean> kickedPlayers = new HashMap<>();
public HashMap<String, Boolean> updatedIpPlayers = new HashMap<>();
public boolean checkRuIP = false;
private Cache<String, String> registeredAccounts = CacheBuilder.newBuilder().expireAfterWrite(12, TimeUnit.HOURS).build();

@Inject
public LimboAuth(Logger logger, ProxyServer server, Metrics.Factory metricsFactory, @DataDirectory Path dataDirectory) {
Expand Down Expand Up @@ -595,8 +597,11 @@ public void authPlayer(Player player) {
}

if (nicknameRegisteredPlayer == null && registeredPlayer == null && Settings.IMP.MAIN.SAVE_PREMIUM_ACCOUNTS) {
registeredPlayer = new RegisteredPlayer(player).setPremiumUuid(player.getUniqueId());

if (joinHosts.getIfPresent(player.getUsername()) != null) {
registeredPlayer = new RegisteredPlayer(player, joinHosts.getIfPresent(player.getUsername())).setPremiumUuid(player.getUniqueId());
} else {
registeredPlayer = new RegisteredPlayer(player).setPremiumUuid(player.getUniqueId());
}
try {
this.playerDao.create(registeredPlayer);
} catch (SQLException e) {
Expand Down Expand Up @@ -640,6 +645,15 @@ public void authPlayer(Player player) {
Consumer<TaskEvent> eventConsumer = (event) -> this.sendPlayer(event, null);
eventManager.fire(new PreRegisterEvent(eventConsumer, result, player)).thenAcceptAsync(eventConsumer);
} else {
if (checkRuIP && joinHosts.getIfPresent(player.getUsername()) != null && joinHosts.getIfPresent(player.getUsername()).toLowerCase().endsWith(".ru") && System.currentTimeMillis() - registeredPlayer.getRegDate() > 14 * 24 * 60 * 60 * 1000) {
player.disconnect(SERIALIZER.deserialize("&cПожалуйста, обновите адрес сервера!\n\n" +
"&fДля игры необходимо использовать\n" +
"&fактуальный IP-адрес сервера. Добавьте в список\n" +
"&fсерверов новый с адресом &#f89d57mc.raidmine.com"));
kickedPlayers.put(player.getUsername(), true);
} else if (kickedPlayers.containsKey(player.getUsername())) {
updatedIpPlayers.put(player.getUsername(), true);
}
Consumer<TaskEvent> eventConsumer = (event) -> this.sendPlayer(event, ((PreAuthorizationEvent) event).getPlayerInfo());
eventManager.fire(new PreAuthorizationEvent(eventConsumer, result, player, registeredPlayer)).thenAcceptAsync(eventConsumer);
}
Expand Down Expand Up @@ -683,6 +697,9 @@ public void updateLoginData(Player player) throws SQLException {
updateBuilder.where().eq(RegisteredPlayer.LOWERCASE_NICKNAME_FIELD, lowercaseNickname);
updateBuilder.updateColumnValue(RegisteredPlayer.LOGIN_IP_FIELD, player.getRemoteAddress().getAddress().getHostAddress());
updateBuilder.updateColumnValue(RegisteredPlayer.LOGIN_DATE_FIELD, System.currentTimeMillis());
if (joinHosts.getIfPresent(player.getUsername()) != null) {
updateBuilder.updateColumnValue(RegisteredPlayer.LOGIN_HOST_FIELD, joinHosts.getIfPresent(player.getUsername()));
}
updateBuilder.update();

if (Settings.IMP.MAIN.MOD.ENABLED) {
Expand Down Expand Up @@ -950,6 +967,10 @@ public Dao<RegisteredPlayer, String> getPlayerDao() {
return this.playerDao;
}

public FloodgateApiHolder getFloodgateApi() {
return this.floodgateApi;
}

private static void setLogger(Logger logger) {
LOGGER = logger;
}
Expand All @@ -966,6 +987,14 @@ public Limbo getAuthServer() {
return this.authServer;
}

public Cache<String, String> getJoinHosts() {
return this.joinHosts;
}

public Cache<String, String> getRegisteredAccounts() {
return this.registeredAccounts;
}

public Pattern getNicknameValidationPattern() {
return this.nicknameValidationPattern;
}
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/net/elytrium/limboauth/Settings.java
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ public static class MAIN {
public boolean FORCE_OFFLINE_MODE = false;
@Comment("Forces all players to get offline uuid")
public boolean FORCE_OFFLINE_UUID = false;
@Comment("Does not force floodgate players to get an offline uuid")
public boolean EXEMPT_FLOODGATE_FOR_FORCE_OFFLINE_UUID = true;
@Comment("If enabled, the plugin will firstly check whether the player is premium through the local database, and secondly through Mojang API.")
public boolean CHECK_PREMIUM_PRIORITY_INTERNAL = true;
@Comment("Delay in milliseconds before sending auth-confirming titles and messages to the player. (login-premium-title, login-floodgate, etc.)")
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/net/elytrium/limboauth/command/LimboAuthCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,16 @@ public List<String> suggest(SimpleCommand.Invocation invocation) {
public void execute(CommandSource source, String[] args) {
int argsAmount = args.length;
if (argsAmount > 0) {
if (args[0].equalsIgnoreCase("stats")) {
source.sendMessage(Component.text("kicked " + plugin.kickedPlayers.size()));
source.sendMessage(Component.text("updated ip " + plugin.updatedIpPlayers.size() + " (" + plugin.updatedIpPlayers.size() / plugin.kickedPlayers.size() + ")"));
return;
}
if (args[0].equalsIgnoreCase("switch")) {
plugin.checkRuIP = !plugin.checkRuIP;
source.sendMessage(Component.text( "changed to " + plugin.checkRuIP));
return;
}
try {
Subcommand subcommand = Subcommand.valueOf(args[0].toUpperCase(Locale.ROOT));
if (!subcommand.hasPermission(source)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,29 +136,10 @@ public void onSpawn(Limbo server, LimboPlayer player) {
Serializer serializer = LimboAuth.getSerializer();

if (this.playerInfo == null) {
try {
String ip = this.proxyPlayer.getRemoteAddress().getAddress().getHostAddress();
List<RegisteredPlayer> alreadyRegistered = this.playerDao.queryForEq(RegisteredPlayer.IP_FIELD, ip);
if (alreadyRegistered != null) {
int sizeOfValidRegistrations = alreadyRegistered.size();
if (Settings.IMP.MAIN.IP_LIMIT_VALID_TIME > 0) {
for (RegisteredPlayer registeredPlayer : alreadyRegistered.stream()
.filter(registeredPlayer -> registeredPlayer.getRegDate() < System.currentTimeMillis() - Settings.IMP.MAIN.IP_LIMIT_VALID_TIME)
.collect(Collectors.toList())) {
registeredPlayer.setIP("");
this.playerDao.update(registeredPlayer);
--sizeOfValidRegistrations;
}
}

if (sizeOfValidRegistrations >= Settings.IMP.MAIN.IP_LIMIT_REGISTRATIONS) {
this.proxyPlayer.disconnect(ipLimitKick);
return;
}
}
} catch (SQLException e) {
this.proxyPlayer.disconnect(databaseErrorKick);
throw new SQLRuntimeException(e);
String ip = this.proxyPlayer.getRemoteAddress().getAddress().getHostAddress();
if (plugin.getRegisteredAccounts().asMap().values().stream().filter(i -> i.equalsIgnoreCase(ip)).count() >= Settings.IMP.MAIN.IP_LIMIT_REGISTRATIONS) {
this.proxyPlayer.disconnect(ipLimitKick);
return;
}
} else {
if (!this.proxyPlayer.getUsername().equals(this.playerInfo.getNickname())) {
Expand Down Expand Up @@ -214,7 +195,12 @@ public void onChat(String message) {
String password = args[1];
if (this.checkPasswordsRepeat(args) && this.checkPasswordLength(password) && this.checkPasswordStrength(password)) {
this.saveTempPassword(password);
RegisteredPlayer registeredPlayer = new RegisteredPlayer(this.proxyPlayer).setPassword(password);
RegisteredPlayer registeredPlayer;
if (plugin.getJoinHosts().getIfPresent(this.proxyPlayer.getUsername()) != null) {
registeredPlayer = new RegisteredPlayer(this.proxyPlayer, plugin.getJoinHosts().getIfPresent(this.proxyPlayer.getUsername())).setPassword(password);
} else {
registeredPlayer = new RegisteredPlayer(this.proxyPlayer).setPassword(password);
}

try {
this.playerDao.create(registeredPlayer);
Expand Down Expand Up @@ -447,6 +433,7 @@ private void finishAuth() {

this.plugin.cacheAuthUser(this.proxyPlayer);
this.player.disconnect();
plugin.getRegisteredAccounts().put(this.proxyPlayer.getUsername(), this.proxyPlayer.getRemoteAddress().getAddress().getHostAddress());
}

public static void reload() {
Expand Down
17 changes: 16 additions & 1 deletion src/main/java/net/elytrium/limboauth/listener/AuthListener.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,13 @@

package net.elytrium.limboauth.listener;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.j256.ormlite.dao.Dao;
import com.j256.ormlite.stmt.UpdateBuilder;
import com.velocitypowered.api.event.PostOrder;
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.connection.DisconnectEvent;
import com.velocitypowered.api.event.connection.PostLoginEvent;
import com.velocitypowered.api.event.connection.PreLoginEvent;
import com.velocitypowered.api.event.player.GameProfileRequestEvent;
Expand All @@ -31,6 +34,8 @@
import com.velocitypowered.proxy.connection.client.LoginInboundConnection;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;

import java.net.InetSocketAddress;
import java.sql.SQLException;
import java.util.Locale;
import java.util.UUID;
Expand Down Expand Up @@ -107,6 +112,11 @@ public void onPreLoginEvent(PreLoginEvent event) {
throw new IllegalStateException("failed to track client disconnection", throwable);
}
}
InboundConnection connection = event.getConnection();
String host = connection.getVirtualHost().map(InetSocketAddress::getHostString).orElse("").toLowerCase();
if (!host.isEmpty()) {
plugin.getJoinHosts().put(event.getUsername(), host);
}
}

private MinecraftConnection getConnection(InboundConnection inbound) throws Throwable {
Expand Down Expand Up @@ -137,6 +147,11 @@ private boolean isPremiumByIdentifiedKey(InboundConnection inbound) throws Throw
}
*/

@Subscribe
public void onProxyDisconnect(DisconnectEvent event) {
this.plugin.unsetForcedPreviously(event.getPlayer().getUsername());
}

@Subscribe
public void onPostLogin(PostLoginEvent event) {
UUID uuid = event.getPlayer().getUniqueId();
Expand Down Expand Up @@ -203,7 +218,7 @@ public void onGameProfileRequest(GameProfileRequestEvent event) {
}
}

if (Settings.IMP.MAIN.FORCE_OFFLINE_UUID) {
if (Settings.IMP.MAIN.FORCE_OFFLINE_UUID && !(Settings.IMP.MAIN.EXEMPT_FLOODGATE_FOR_FORCE_OFFLINE_UUID && plugin.getFloodgateApi().isFloodgatePlayer(event.getOriginalProfile().getId()))) {
event.setGameProfile(event.getOriginalProfile().withId(UuidUtils.generateOfflinePlayerUuid(event.getUsername())));
}

Expand Down
52 changes: 51 additions & 1 deletion src/main/java/net/elytrium/limboauth/model/RegisteredPlayer.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
import java.net.InetSocketAddress;
import java.util.Locale;
import java.util.UUID;

import net.elytrium.limboauth.LimboAuth;
import net.elytrium.limboauth.Settings;

@DatabaseTable(tableName = "AUTH")
Expand All @@ -40,6 +42,8 @@ public class RegisteredPlayer {
public static final String UUID_FIELD = "UUID";
public static final String PREMIUM_UUID_FIELD = "PREMIUMUUID";
public static final String TOKEN_ISSUED_AT_FIELD = "ISSUEDTIME";
public static final String REG_HOST_FIELD = "REGHOST";
public static final String LOGIN_HOST_FIELD = "LOGINHOST";

private static final BCrypt.Hasher HASHER = BCrypt.withDefaults();

Expand Down Expand Up @@ -76,9 +80,16 @@ public class RegisteredPlayer {
@DatabaseField(columnName = TOKEN_ISSUED_AT_FIELD)
private Long tokenIssuedAt = System.currentTimeMillis();

@DatabaseField(columnName = REG_HOST_FIELD)
private String regHost;

@DatabaseField(columnName = LOGIN_HOST_FIELD)
private String loginHost;

@Deprecated
public RegisteredPlayer(String nickname, String lowercaseNickname,
String hash, String ip, String totpToken, Long regDate, String uuid, String premiumUuid, String loginIp, Long loginDate) {
String hash, String ip, String totpToken, Long regDate, String uuid, String premiumUuid, String loginIp,
Long loginDate, String regHost, String loginHost) {
this.nickname = nickname;
this.lowercaseNickname = lowercaseNickname;
this.hash = hash;
Expand All @@ -89,16 +100,26 @@ public RegisteredPlayer(String nickname, String lowercaseNickname,
this.premiumUuid = premiumUuid;
this.loginIp = loginIp;
this.loginDate = loginDate;
this.regHost = regHost;
this.loginHost = loginHost;
}

public RegisteredPlayer(Player player) {
this(player.getUsername(), player.getUniqueId(), player.getRemoteAddress());
}

public RegisteredPlayer(Player player, String regHost) {
this(player.getUsername(), player.getUniqueId(), player.getRemoteAddress(), regHost);
}

public RegisteredPlayer(String nickname, UUID uuid, InetSocketAddress ip) {
this(nickname, uuid.toString(), ip.getAddress().getHostAddress());
}

public RegisteredPlayer(String nickname, UUID uuid, InetSocketAddress ip, String regHost) {
this(nickname, uuid.toString(), ip.getAddress().getHostAddress(), regHost);
}

public RegisteredPlayer(String nickname, String uuid, String ip) {
this.nickname = nickname;
this.lowercaseNickname = nickname.toLowerCase(Locale.ROOT);
Expand All @@ -107,6 +128,15 @@ public RegisteredPlayer(String nickname, String uuid, String ip) {
this.loginIp = ip;
}

public RegisteredPlayer(String nickname, String uuid, String ip, String regHost) {
this.nickname = nickname;
this.lowercaseNickname = nickname.toLowerCase(Locale.ROOT);
this.uuid = uuid;
this.ip = ip;
this.loginIp = ip;
this.regHost = regHost;
}

public RegisteredPlayer() {

}
Expand Down Expand Up @@ -233,4 +263,24 @@ public RegisteredPlayer setTokenIssuedAt(Long tokenIssuedAt) {

return this;
}

public String getRegHost() {
return this.regHost == null ? "" : this.regHost;
}

public RegisteredPlayer setRegHost(String regHost) {
this.regHost = regHost;

return this;
}

public String getLoginHost() {
return this.loginHost == null ? "" : this.loginHost;
}

public RegisteredPlayer setLoginHost(String loginHost) {
this.loginHost = loginHost;

return this;
}
}
Loading