Merge pull request #494 from Smart123s/main

Floodgate support for Bukkit
This commit is contained in:
games647
2021-05-16 20:50:53 +02:00
committed by GitHub
9 changed files with 258 additions and 7 deletions

3
.gitignore vendored
View File

@ -13,6 +13,9 @@ nb-configuration.xml
*.iws
.idea/
# VSCode
.vscode/
# Maven
target/
pom.xml.versionsBackup

View File

@ -125,6 +125,12 @@
<enabled>false</enabled>
</snapshots>
</repository>
<!-- Floodgate -->
<repository>
<id>nukkitx-snapshot</id>
<url>https://repo.nukkitx.com/maven-snapshots/</url>
</repository>
</repositories>
<dependencies>
@ -183,6 +189,14 @@
</exclusions>
</dependency>
<!--Floodgate for Xbox Live Authentication-->
<dependency>
<groupId>org.geysermc.floodgate</groupId>
<artifactId>api</artifactId>
<version>2.0-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<!--Login Plugins-->
<dependency>
<groupId>fr.xephi</groupId>

View File

@ -47,10 +47,13 @@ import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.bukkit.Bukkit;
import org.bukkit.Server.Spigot;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
import org.geysermc.floodgate.api.FloodgateApi;
import org.slf4j.Logger;
/**
@ -86,6 +89,13 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman
setEnabled(false);
return;
}
// Check Floodgate config values
if (!isValidFloodgateConfigString("autoLoginFloodgate")
|| !isValidFloodgateConfigString("allowFloodgateNameConflict")) {
setEnabled(false);
return;
}
bungeeManager = new BungeeManager(this);
bungeeManager.initialize();
@ -131,6 +141,8 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman
premiumPlaceholder = new PremiumPlaceholder(this);
premiumPlaceholder.register();
}
dependencyWarnings();
}
@Override
@ -237,4 +249,59 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman
public void sendMessage(CommandSender receiver, String message) {
receiver.sendMessage(message);
}
/**
* Checks if a config entry (related to Floodgate) is valid. <br>
* Writes to Log if the value is invalid.
* <p>
* This should be used for:
* <ul>
* <li>allowFloodgateNameConflict
* <li>autoLoginFloodgate
* </ul>
* </p>
*
* @param key the key of the entry in config.yml
* @return <b>true</b> if the entry's value is "true", "false", or "linked"
*/
private boolean isValidFloodgateConfigString(String key) {
String value = core.getConfig().get(key).toString().toLowerCase();
if (!value.equals("true") && !value.equals("linked") && !value.equals("false")) {
logger.error("Invalid value detected for {} in FastLogin/config.yml.", key);
return false;
}
return true;
}
/**
* Checks if a plugin is installed on the server
* @param name the name of the plugin
* @return true if the plugin is installed
*/
private boolean isPluginInstalled(String name) {
//the plugin may be enabled after FastLogin, so isPluginEnabled()
//won't work here
return Bukkit.getServer().getPluginManager().getPlugin(name) != null;
}
/**
* Send warning messages to log if incompatible plugins are used
*/
private void dependencyWarnings() {
if (isPluginInstalled("floodgate-bukkit")) {
logger.warn("We have detected that you are runnging Floodgate 1.0 which is not supported by the Bukkit "
+ "version of FastLogin.");
logger.warn("If you would like to use FastLogin with Floodgate, you can download developement builds of "
+ "Floodgate 2.0 from https://ci.opencollab.dev/job/GeyserMC/job/Floodgate/job/dev%252F2.0/");
logger.warn("Don't forget to update Geyser to a supported version as well from "
+ "https://ci.opencollab.dev/job/GeyserMC/job/Geyser/job/floodgate-2.0/");
} else if (isPluginInstalled("floodgate") && isPluginInstalled("ProtocolLib")) {
logger.warn("We have detected that you are runnging FastLogin alongside Floodgate and ProtocolLib.");
logger.warn("Currently there is an issue with FastLogin that prevents Floodgate name prefixes from showing up "
+ "when it is together used with ProtocolLib.");
logger.warn("If you would like to use Floodgate name prefixes, you can replace ProtocolLib with ProtocolSupport "
+ "which does not have this issue.");
logger.warn("For more information visit https://github.com/games647/FastLogin/issues/493");
}
}
}

View File

@ -27,6 +27,7 @@ package com.github.games647.fastlogin.bukkit.listener;
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.bukkit.task.FloodgateAuthTask;
import com.github.games647.fastlogin.bukkit.task.ForceLoginTask;
import org.bukkit.Bukkit;
@ -37,6 +38,8 @@ import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.event.player.PlayerLoginEvent.Result;
import org.geysermc.floodgate.api.FloodgateApi;
import org.geysermc.floodgate.api.player.FloodgatePlayer;
import org.bukkit.event.player.PlayerQuitEvent;
/**
@ -70,13 +73,26 @@ public class ConnectionListener implements Listener {
// cases: Paper (firing BungeeCord message before PlayerJoinEvent) or not running BungeeCord and already
// having the login session from the login process
BukkitLoginSession session = plugin.getSession(player.getAddress());
if (session == null) {
String sessionId = plugin.getSessionId(player.getAddress());
plugin.getLog().info("No on-going login session for player: {} with ID {}", player, sessionId);
} else {
Runnable forceLoginTask = new ForceLoginTask(plugin.getCore(), player, session);
Bukkit.getScheduler().runTaskAsynchronously(plugin, forceLoginTask);
}
boolean isFloodgateLogin = false;
if (Bukkit.getServer().getPluginManager().isPluginEnabled("floodgate")) {
FloodgatePlayer floodgatePlayer = FloodgateApi.getInstance().getPlayer(player.getUniqueId());
if (floodgatePlayer != null) {
isFloodgateLogin = true;
Runnable floodgateAuthTask = new FloodgateAuthTask(plugin, player, floodgatePlayer);
Bukkit.getScheduler().runTaskAsynchronously(plugin, floodgateAuthTask);
}
}
if (!isFloodgateLogin) {
if (session == null) {
String sessionId = plugin.getSessionId(player.getAddress());
plugin.getLog().info("No on-going login session for player: {} with ID {}", player, sessionId);
} else {
Runnable forceLoginTask = new ForceLoginTask(plugin.getCore(), player, session);
Bukkit.getScheduler().runTaskAsynchronously(plugin, forceLoginTask);
}
}
plugin.getBungeeManager().markJoinEventFired(player);
// delay the login process to let auth plugins initialize the player

View File

@ -37,8 +37,11 @@ import com.github.games647.fastlogin.core.shared.event.FastLoginPreLoginEvent;
import java.security.PublicKey;
import java.util.Random;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.geysermc.floodgate.api.FloodgateApi;
import org.geysermc.floodgate.api.player.FloodgatePlayer;
public class NameCheckTask extends JoinManagement<Player, CommandSender, ProtocolLibLoginSource>
implements Runnable {
@ -67,6 +70,12 @@ public class NameCheckTask extends JoinManagement<Player, CommandSender, Protoco
@Override
public void run() {
try {
// check if the player is connecting through Geyser
if (!plugin.getCore().getConfig().get("allowFloodgateNameConflict").toString().equalsIgnoreCase("false")
&& getFloodgatePlayer(username) != null) {
plugin.getLog().info("Skipping name conflict checking for player {}", username);
return;
}
super.onLogin(username, new ProtocolLibLoginSource(packetEvent, player, random, publicKey));
} finally {
ProtocolLibrary.getProtocolManager().getAsynchronousManager().signalPacketTransmission(packetEvent);
@ -111,4 +120,16 @@ public class NameCheckTask extends JoinManagement<Player, CommandSender, Protoco
BukkitLoginSession loginSession = new BukkitLoginSession(username, profile);
plugin.putSession(player.getAddress(), loginSession);
}
private static FloodgatePlayer getFloodgatePlayer(String username) {
if (Bukkit.getServer().getPluginManager().isPluginEnabled("floodgate")) {
// the Floodgate API requires UUID, which is inaccessible at NameCheckTask.java
for (FloodgatePlayer floodgatePlayer : FloodgateApi.getInstance().getPlayers()) {
if (floodgatePlayer.getUsername().equals(username)) {
return floodgatePlayer;
}
}
}
return null;
}
}

View File

@ -37,6 +37,7 @@ import com.github.games647.fastlogin.core.shared.event.FastLoginPreLoginEvent;
import java.net.InetSocketAddress;
import java.util.Optional;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;

View File

@ -0,0 +1,86 @@
package com.github.games647.fastlogin.bukkit.task;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.geysermc.floodgate.api.player.FloodgatePlayer;
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.core.StoredProfile;
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
public class FloodgateAuthTask implements Runnable {
private final FastLoginBukkit plugin;
private final Player player;
private final FloodgatePlayer floodgatePlayer;
public FloodgateAuthTask(FastLoginBukkit plugin, Player player, FloodgatePlayer floodgatePlayer) {
this.plugin = plugin;
this.player = player;
this.floodgatePlayer = floodgatePlayer;
}
@Override
public void run() {
plugin.getLog().info(
"Player {} is connecting through Geyser Floodgate.",
player.getName());
String allowNameConflict = plugin.getCore().getConfig().get("allowFloodgateNameConflict").toString().toLowerCase();
// check if the Bedrock player is linked to a Java account
boolean isLinked = floodgatePlayer.getLinkedPlayer() != null;
if (allowNameConflict.equals("linked") && !isLinked) {
plugin.getLog().info(
"Bedrock Player {}'s name conflits an existing Java Premium Player's name",
player.getName());
// kicking must be synchronous
// https://www.spigotmc.org/threads/asynchronous-player-kick-problem.168580/
Bukkit.getScheduler().runTask(plugin, new Runnable() {
public void run() {
player.kickPlayer("This name is allready in use by a Premium Java Player");
}
});
return;
}
AuthPlugin<Player> authPlugin = plugin.getCore().getAuthPluginHook();
String autoLoginFloodgate = plugin.getCore().getConfig().get("autoLoginFloodgate").toString().toLowerCase();
boolean autoRegisterFloodgate = plugin.getCore().getConfig().getBoolean("autoRegisterFloodgate");
boolean isRegistered;
try {
isRegistered = authPlugin.isRegistered(player.getName());
} catch (Exception e) {
plugin.getLog().error(
"An error has occured while checking if player {} is registered",
player.getName());
return;
}
if (!isRegistered && !autoRegisterFloodgate) {
plugin.getLog().info(
"Auto registration is disabled for Floodgate players in config.yml");
return;
}
// logging in from bedrock for a second time threw an error with UUID
StoredProfile profile = plugin.getCore().getStorage().loadProfile(player.getName());
if (profile == null) {
profile = new StoredProfile(player.getUniqueId(), player.getName(), true, player.getAddress().toString());
}
BukkitLoginSession session = new BukkitLoginSession(player.getName(), isRegistered, profile);
// enable auto login based on the value of 'autoLoginFloodgate' in config.yml
session.setVerified(autoLoginFloodgate.equals("true")
|| (autoLoginFloodgate.equals("linked") && isLinked));
// run login task
Runnable forceLoginTask = new ForceLoginTask(plugin.getCore(), player, session);
Bukkit.getScheduler().runTaskAsynchronously(plugin, forceLoginTask);
}
}

View File

@ -20,6 +20,7 @@ softdepend:
- ProtocolLib
# Premium variable
- PlaceholderAPI
- floodgate
# Auth plugins
- AuthMe
- LoginSecurity

View File

@ -187,6 +187,48 @@ auto-register-unknown: false
# The password of your Minecraft and the password to login in with your auth plugin
autoLogin: true
# Floodgate configuration
# Connecing through Floodgate requires player's to sign in via their Xbox Live account
# Requires Floodgate 2.0 https://github.com/GeyserMC/Floodgate/tree/dev/2.0
# These settings only work in Bukkit/Spigot/Paper mode
# !!!!!!!! WARNING: FLOODGATE SUPPORT IS AN EXPERIMENTAL FEATURE !!!!!!!!
# Enabling any of these settings might lead to people gaining unauthorized access to other's accounts!
# This enables auto login for every player connecting through Floodgate.
# Possible values: false, true, linked
# Linked means that only Bedrock accounts linked to a Java account will be logged in automatically
# !!!!!!!! WARNING: FLOODGATE SUPPORT IS AN EXPERIMENTAL FEATURE !!!!!!!!
# Enabling this might lead to people gaining unauthorized access to other's accounts!
autoLoginFloodgate: false
# This enables Floodgate players to join the server, even if autoRegister is true and there's an existing
# Java **PREMIUM** account with the same name
#
# Java and Bedrock players will get different UUIDs, so their inventories, location, etc. will be different.
# However, some plugins (such as AuthMe) rely on names instead of UUIDs to identify a player which might cause issues.
# In the case of AuthMe (and other auth plugins), both the Java and the Bedrock player will have the same password.
#
# To prevent conflits from two different players having the same name, it is highly recommended to use a 'username-prefix'
# in floodgate/config.yml
# Note: 'username-prefix' is currently broken when used with FastLogin and ProtocolLib. For more information visit:
# https://github.com/games647/FastLogin/issues/493
# A solution to this is to replace ProtocolLib with ProtocolSupport
#
# Possible values:
# false: Check for Premium Java name conflicts as described in 'autoRegister'
# 'autoRegister' must be 'true' for this to work
# true: Bypass 'autoRegister's name conflict checking
# linked: Bedrock accounts linked to a Java account will be allowed to join with conflicting names
# !!!!!!!! WARNING: FLOODGATE SUPPORT IS AN EXPERIMENTAL FEATURE !!!!!!!!
# Enabling this might lead to people gaining unauthorized access to other's accounts!
allowFloodgateNameConflict: false
# This enables auto registering every player connecting through Floodgate.
# autoLoginFloodgate must be 'true' for this to work
# !!!!!!!! WARNING: FLOODGATE SUPPORT IS AN EXPERIMENTAL FEATURE !!!!!!!!
# Enabling this might lead to people gaining unauthorized access to other's accounts!
autoRegisterFloodgate: false
# Database configuration
# Recommended is the use of MariaDB (a better version of MySQL)