diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 407749a..26ef176 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -6,7 +6,7 @@ jobs: build_and_test: strategy: matrix: - jdkversion: [8, 11, 17] + jdkversion: [11, 17] runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 diff --git a/bungee/pom.xml b/bungee/pom.xml new file mode 100644 index 0000000..5c6ee5f --- /dev/null +++ b/bungee/pom.xml @@ -0,0 +1,74 @@ + + + 4.0.0 + + + fr.xephi + authmeproxy-parent + 2.3.0-SNAPSHOT + + + authmeproxy-bungee + AuthMeProxy-Bungee + + + ${project.name}-${project.version}-noshade + + + org.apache.maven.plugins + maven-javadoc-plugin + + + org.apache.maven.plugins + maven-source-plugin + + + org.apache.maven.plugins + maven-shade-plugin + + + + + + + + oss-repo + https://oss.sonatype.org/content/groups/public + + + + + + fr.xephi + authmeproxy-common + 2.3.0-SNAPSHOT + + + + + net.md-5 + bungeecord-api + 1.20-R0.1-SNAPSHOT + jar + provided + + + net.md-5 + bungeecord-api + 1.20-R0.1-SNAPSHOT + javadoc + provided + + + + + org.bstats + bstats-bungeecord + 3.0.2 + compile + + + + diff --git a/bungee/src/main/java/fr/xephi/authmeproxy/bungee/AuthMeBungeeLogger.java b/bungee/src/main/java/fr/xephi/authmeproxy/bungee/AuthMeBungeeLogger.java new file mode 100644 index 0000000..fac7edc --- /dev/null +++ b/bungee/src/main/java/fr/xephi/authmeproxy/bungee/AuthMeBungeeLogger.java @@ -0,0 +1,34 @@ +package fr.xephi.authmeproxy.bungee; + +import fr.xephi.authmeproxy.common.AbstractAuthMeLogger; +import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.chat.TextComponent; + +public class AuthMeBungeeLogger extends AbstractAuthMeLogger { + + public java.util.logging.Logger logger; + + public AuthMeBungeeLogger(java.util.logging.Logger logger) { + this.logger = logger; + } + + @Override + public void send(String message) { + ProxyServer.getInstance().getConsole().sendMessage(TextComponent.fromLegacyText(message)); + } + + @Override + public void info(String... messages) { + this.logger.info(String.join(" ", messages)); + } + + @Override + public void warn(String... messages) { + this.logger.warning(String.join(" ", messages)); + } + + @Override + public void error(String... messages) { + this.logger.severe(String.join(" ", messages)); + } +} diff --git a/bungee/src/main/java/fr/xephi/authmeproxy/bungee/AuthMeProxyBungee.java b/bungee/src/main/java/fr/xephi/authmeproxy/bungee/AuthMeProxyBungee.java new file mode 100644 index 0000000..22de784 --- /dev/null +++ b/bungee/src/main/java/fr/xephi/authmeproxy/bungee/AuthMeProxyBungee.java @@ -0,0 +1,80 @@ +package fr.xephi.authmeproxy.bungee; + +import fr.xephi.authmeproxy.bungee.listeners.MessageListener; +import fr.xephi.authmeproxy.bungee.listeners.PlayerListener; +import fr.xephi.authmeproxy.bungee.platform.BungeePlatform; +import fr.xephi.authmeproxy.bungee.platform.BungeePlayer; +import fr.xephi.authmeproxy.common.AbstractAuthMeProxy; +import fr.xephi.authmeproxy.common.commands.AbstractCommand; +import fr.xephi.authmeproxy.common.commands.ReloadCommand; +import fr.xephi.authmeproxy.common.platform.AbstractPlayer; +import net.md_5.bungee.api.CommandSender; +import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.plugin.Command; +import net.md_5.bungee.api.plugin.Plugin; +import org.bstats.bungeecord.Metrics; + +public class AuthMeProxyBungee extends Plugin { + + private final AuthMeProxyBungeeImpl implementation; + + public AuthMeProxyBungee() { + implementation = new AuthMeProxyBungeeImpl(this); + } + + // Dirty hack as we can't extend multiple classes + public static class AuthMeProxyBungeeImpl extends AbstractAuthMeProxy { + + private final AuthMeProxyBungee plugin; + + public AuthMeProxyBungeeImpl(AuthMeProxyBungee plugin) { + super( + new BungeePlatform(), + new AuthMeBungeeLogger(plugin.getLogger()), + plugin.getDataFolder() + ); + this.plugin = plugin; + } + + @Override + protected void registerCommands() { + plugin.registerCommand(injector.getSingleton(ReloadCommand.class)); + } + + @Override + protected void registerListeners() { + ProxyServer.getInstance().getPluginManager().registerListener(plugin, injector.getSingleton(MessageListener.class)); + ProxyServer.getInstance().getPluginManager().registerListener(plugin, injector.getSingleton(PlayerListener.class)); + } + + @Override + protected void registerMetrics() { + // Send metrics data + new Metrics(plugin, 1880); + } + } + + @Override + public void onEnable() { + implementation.initialize(); + } + + private void registerCommand(AbstractCommand command) { + ProxyServer.getInstance().getPluginManager().registerCommand(this, new Command( + command.getCommand(), + command.getPermission() + ) { + @Override + public void execute(CommandSender sender, String[] args) { + ProxiedPlayer player = null; + if (sender instanceof ProxiedPlayer) { + player = (ProxiedPlayer) sender; + } + AbstractPlayer abstractPlayer = player == null ? null : BungeePlayer.fromPlayer(player); + command.execute(abstractPlayer, args); + } + }); + } + +} diff --git a/bungee/src/main/java/fr/xephi/authmeproxy/bungee/listeners/MessageListener.java b/bungee/src/main/java/fr/xephi/authmeproxy/bungee/listeners/MessageListener.java new file mode 100644 index 0000000..468e3e0 --- /dev/null +++ b/bungee/src/main/java/fr/xephi/authmeproxy/bungee/listeners/MessageListener.java @@ -0,0 +1,26 @@ +package fr.xephi.authmeproxy.bungee.listeners; + +import fr.xephi.authmeproxy.common.services.MessageHandleService; +import net.md_5.bungee.api.connection.Server; +import net.md_5.bungee.api.event.PluginMessageEvent; +import net.md_5.bungee.api.plugin.Listener; +import net.md_5.bungee.event.EventHandler; + +import javax.inject.Inject; + +public class MessageListener implements Listener { + + @Inject + private MessageHandleService messageHandleService; + + @EventHandler + public void onPluginMessage(final PluginMessageEvent event) { + if (event.isCancelled()) { + return; + } + + boolean fromPlayer = !(event.getSender() instanceof Server); + messageHandleService.handleMessage(fromPlayer, event.getTag(), event.getData()); + } + +} diff --git a/bungee/src/main/java/fr/xephi/authmeproxy/bungee/listeners/PlayerListener.java b/bungee/src/main/java/fr/xephi/authmeproxy/bungee/listeners/PlayerListener.java new file mode 100644 index 0000000..105dffe --- /dev/null +++ b/bungee/src/main/java/fr/xephi/authmeproxy/bungee/listeners/PlayerListener.java @@ -0,0 +1,99 @@ +package fr.xephi.authmeproxy.bungee.listeners; + +import fr.xephi.authmeproxy.bungee.platform.BungeePlayer; +import fr.xephi.authmeproxy.common.services.AuthStateService; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.TextComponent; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.connection.Server; +import net.md_5.bungee.api.event.*; +import net.md_5.bungee.api.plugin.Listener; +import net.md_5.bungee.event.EventHandler; +import net.md_5.bungee.event.EventPriority; + +import javax.inject.Inject; + +public class PlayerListener implements Listener { + + @Inject + private AuthStateService authStateService; + + @EventHandler + public void onPlayerJoin(final PostLoginEvent event) { + // Register player in our list + authStateService.trackUser(event.getPlayer().getName()); + } + + @EventHandler + public void onPlayerDisconnect(final PlayerDisconnectEvent event) { + // Remove player from out list + authStateService.untrackUser(event.getPlayer().getName()); + } + + @EventHandler(priority = EventPriority.LOWEST) + public void onCommand(final ChatEvent event) { + if (event.isCancelled()) { + return; + } + if (!event.isCommand() && event.isProxyCommand()) { + return; + } + if (!(event.getSender() instanceof ProxiedPlayer)) { + return; + } + ProxiedPlayer player = (ProxiedPlayer) event.getSender(); + String command = event.getMessage().split(" ")[0].toLowerCase(); + if (!authStateService.shouldAllowCommand(BungeePlayer.fromPlayer(player), command)) { + event.setCancelled(true); + } + } + + // Priority is set to lowest to keep compatibility with some chat plugins + @EventHandler(priority = EventPriority.LOWEST) + public void onPlayerChat(final ChatEvent event) { + if (event.isCancelled()) { + return; + } + if (event.isCommand() || event.isProxyCommand()) { + return; + } + if (!(event.getSender() instanceof ProxiedPlayer)) { + return; + } + ProxiedPlayer player = (ProxiedPlayer) event.getSender(); + + if (!authStateService.shouldAllowChat(BungeePlayer.fromPlayer(player))) { + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.LOWEST) + public void onPlayerConnectedToServer(final ServerConnectedEvent event) { + ProxiedPlayer player = event.getPlayer(); + Server server = event.getServer(); + authStateService.handleServerConnected(BungeePlayer.fromPlayer(player), server.getInfo().getName()); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onPlayerConnectingToServer(final ServerConnectEvent event) { + if (event.isCancelled()) { + return; + } + + ProxiedPlayer player = event.getPlayer(); + String cancelReason = authStateService.shouldAllowServerSwitch( + BungeePlayer.fromPlayer(player), + event.getTarget().getName() + ).orElse(null); + if (cancelReason == null) { + return; + } + event.setCancelled(true); + BaseComponent[] parsedCancelReason = TextComponent.fromLegacyText(cancelReason); + if (event.getPlayer().getServer() == null || !event.getPlayer().getServer().isConnected()) { + player.disconnect(parsedCancelReason); + } else { + player.sendMessage(parsedCancelReason); + } + } +} diff --git a/bungee/src/main/java/fr/xephi/authmeproxy/bungee/platform/BungeePlatform.java b/bungee/src/main/java/fr/xephi/authmeproxy/bungee/platform/BungeePlatform.java new file mode 100644 index 0000000..c69cfe6 --- /dev/null +++ b/bungee/src/main/java/fr/xephi/authmeproxy/bungee/platform/BungeePlatform.java @@ -0,0 +1,31 @@ +package fr.xephi.authmeproxy.bungee.platform; + +import fr.xephi.authmeproxy.common.platform.AbstractPlatform; +import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.config.ServerInfo; + +import java.util.Collection; +import java.util.stream.Collectors; + +public class BungeePlatform extends AbstractPlatform { + + public BungeePlatform() { + } + + @Override + public Collection getAllPlayers() { + return ProxyServer.getInstance().getPlayers().stream() + .map(BungeePlayer::fromPlayer) + .collect(Collectors.toUnmodifiableList()); + } + + @Override + public boolean connect(BungeePlayer player, String serverName) { + ServerInfo serverInfo = ProxyServer.getInstance().getServerInfo(serverName); + if (serverInfo == null) { + return false; + } + player.getHandle().connect(serverInfo); + return true; + } +} diff --git a/bungee/src/main/java/fr/xephi/authmeproxy/bungee/platform/BungeePlayer.java b/bungee/src/main/java/fr/xephi/authmeproxy/bungee/platform/BungeePlayer.java new file mode 100644 index 0000000..c47b6a2 --- /dev/null +++ b/bungee/src/main/java/fr/xephi/authmeproxy/bungee/platform/BungeePlayer.java @@ -0,0 +1,51 @@ +package fr.xephi.authmeproxy.bungee.platform; + +import fr.xephi.authmeproxy.common.platform.AbstractPlayer; +import net.md_5.bungee.api.chat.TextComponent; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.connection.Server; + +import java.util.Optional; + +public class BungeePlayer extends AbstractPlayer { + public BungeePlayer(ProxiedPlayer player) { + super(player); + } + + @Override + public String getUsername() { + return getHandle().getName(); + } + + @Override + public Optional getCurrentServer() { + Server server = getHandle().getServer(); + if (server == null) { + return Optional.empty(); + } + return Optional.of(server.getInfo().getName()); + } + + @Override + public void sendBungeeMessage(byte[] data) { + Server server = getHandle().getServer(); + if (server == null) { + return; + } + server.sendData("BungeeCord", data); + } + + @Override + public void sendMessage(String message) { + getHandle().sendMessage(TextComponent.fromLegacyText(message)); + } + + @Override + public ProxiedPlayer getHandle() { + return (ProxiedPlayer) super.getHandle(); + } + + public static BungeePlayer fromPlayer(ProxiedPlayer player) { + return new BungeePlayer(player); + } +} diff --git a/bungee/src/main/resources/bungee.yml b/bungee/src/main/resources/bungee.yml new file mode 100644 index 0000000..79cbd8b --- /dev/null +++ b/bungee/src/main/resources/bungee.yml @@ -0,0 +1,4 @@ +name: ${project.parent.name} +main: fr.xephi.authmeproxy.bungee.AuthMeProxyBungee +version: ${project.version} +author: AuthMeTeam diff --git a/common/pom.xml b/common/pom.xml new file mode 100644 index 0000000..b450f4c --- /dev/null +++ b/common/pom.xml @@ -0,0 +1,28 @@ + + + 4.0.0 + + fr.xephi + authmeproxy-parent + 2.3.0-SNAPSHOT + + + authmeproxy-common + AuthMeProxy-Common + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + org.apache.maven.plugins + maven-source-plugin + + + + + diff --git a/common/src/main/java/fr/xephi/authmeproxy/common/AbstractAuthMeLogger.java b/common/src/main/java/fr/xephi/authmeproxy/common/AbstractAuthMeLogger.java new file mode 100644 index 0000000..9170b66 --- /dev/null +++ b/common/src/main/java/fr/xephi/authmeproxy/common/AbstractAuthMeLogger.java @@ -0,0 +1,9 @@ +package fr.xephi.authmeproxy.common; + +public abstract class AbstractAuthMeLogger { + + public abstract void send(String message); + public abstract void info(String... messages); + public abstract void warn(String... messages); + public abstract void error(String... messages); +} diff --git a/common/src/main/java/fr/xephi/authmeproxy/common/AbstractAuthMeProxy.java b/common/src/main/java/fr/xephi/authmeproxy/common/AbstractAuthMeProxy.java new file mode 100644 index 0000000..6426984 --- /dev/null +++ b/common/src/main/java/fr/xephi/authmeproxy/common/AbstractAuthMeProxy.java @@ -0,0 +1,82 @@ +package fr.xephi.authmeproxy.common; + +import ch.jalu.configme.SettingsManager; +import ch.jalu.injector.Injector; +import ch.jalu.injector.InjectorBuilder; +import fr.xephi.authmeproxy.common.annotations.DataFolder; +import fr.xephi.authmeproxy.common.config.ProxyConfigProperties; +import fr.xephi.authmeproxy.common.config.ProxySettingsProvider; +import fr.xephi.authmeproxy.common.platform.AbstractPlatform; +import fr.xephi.authmeproxy.common.platform.AbstractPlayer; +import fr.xephi.authmeproxy.common.services.AuthStateService; +import fr.xephi.authmeproxy.common.services.MessageHandleService; +import fr.xephi.authmeproxy.common.services.MessageSenderService; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; + +public abstract class AbstractAuthMeProxy

> { + + protected P platform; + protected AbstractAuthMeLogger logger; + protected File dataFolder; + + protected Injector injector; + protected SettingsManager settings; + + protected AuthStateService authStateService; + protected MessageHandleService messageHandleService; + protected MessageSenderService messageSenderService; + + public AbstractAuthMeProxy(P platform, AbstractAuthMeLogger logger, File dataFolder) { + this.platform = platform; + this.logger = logger; + this.dataFolder = dataFolder; + + injector = new InjectorBuilder().addDefaultHandlers("").create(); + + injector.register(AbstractAuthMeProxy.class, this); + injector.register(AbstractAuthMeLogger.class, logger); + injector.provide(DataFolder.class, dataFolder); + + injector.registerProvider(SettingsManager.class, ProxySettingsProvider.class); + } + + protected abstract void registerCommands(); + + protected abstract void registerListeners(); + + protected abstract void registerMetrics(); + + public final void initialize() { + try { + Files.createDirectories(dataFolder.toPath()); + } catch (IOException e) { + throw new RuntimeException("Unable to create the plugin folder", e); + } + + // Get singletons from the injector + settings = injector.getSingleton(SettingsManager.class); + + // Initialize services + authStateService = injector.getSingleton(AuthStateService.class); + messageHandleService = injector.getSingleton(MessageHandleService.class); + messageSenderService = injector.getSingleton(MessageSenderService.class); + + // Print some config information + logger.info("Current auth servers:"); + for (String authServer : settings.getProperty(ProxyConfigProperties.AUTH_SERVERS)) { + logger.info("> " + authServer.toLowerCase()); + } + + // Add online players (plugin hotswap, just in case) + for (AbstractPlayer player : platform.getAllPlayers()) { + authStateService.trackUser(player.getUsername()); + } + + registerCommands(); + registerListeners(); + registerMetrics(); + } +} diff --git a/src/main/java/fr/xephi/authmebungee/annotations/DataFolder.java b/common/src/main/java/fr/xephi/authmeproxy/common/annotations/DataFolder.java similarity index 87% rename from src/main/java/fr/xephi/authmebungee/annotations/DataFolder.java rename to common/src/main/java/fr/xephi/authmeproxy/common/annotations/DataFolder.java index 69fb6b2..93ad89a 100644 --- a/src/main/java/fr/xephi/authmebungee/annotations/DataFolder.java +++ b/common/src/main/java/fr/xephi/authmeproxy/common/annotations/DataFolder.java @@ -1,4 +1,4 @@ -package fr.xephi.authmebungee.annotations; +package fr.xephi.authmeproxy.common.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/common/src/main/java/fr/xephi/authmeproxy/common/commands/AbstractCommand.java b/common/src/main/java/fr/xephi/authmeproxy/common/commands/AbstractCommand.java new file mode 100644 index 0000000..be88ed7 --- /dev/null +++ b/common/src/main/java/fr/xephi/authmeproxy/common/commands/AbstractCommand.java @@ -0,0 +1,24 @@ +package fr.xephi.authmeproxy.common.commands; + +import fr.xephi.authmeproxy.common.platform.AbstractPlayer; + +public abstract class AbstractCommand { + + protected final String command; + protected final String permission; + + public AbstractCommand(String command, String permission) { + this.command = command; + this.permission = permission; + } + + public abstract void execute(AbstractPlayer sender, String[] strings); + + public String getCommand() { + return command; + } + + public String getPermission() { + return permission; + } +} diff --git a/common/src/main/java/fr/xephi/authmeproxy/common/commands/ReloadCommand.java b/common/src/main/java/fr/xephi/authmeproxy/common/commands/ReloadCommand.java new file mode 100644 index 0000000..b4098ee --- /dev/null +++ b/common/src/main/java/fr/xephi/authmeproxy/common/commands/ReloadCommand.java @@ -0,0 +1,36 @@ +package fr.xephi.authmeproxy.common.commands; + +import ch.jalu.configme.SettingsManager; +import ch.jalu.injector.factory.SingletonStore; +import fr.xephi.authmeproxy.common.AbstractAuthMeLogger; +import fr.xephi.authmeproxy.common.config.SettingsDependent; +import fr.xephi.authmeproxy.common.platform.AbstractPlayer; + +import javax.inject.Inject; + +public class ReloadCommand extends AbstractCommand { + + private final AbstractAuthMeLogger logger; + private final SettingsManager settings; + private final SingletonStore settingsDependentStore; + + @Inject + public ReloadCommand(AbstractAuthMeLogger logger, SettingsManager settings, SingletonStore settingsDependentStore) { + super("authmeproxyreload", "authmeproxy.reload"); + this.logger = logger; + this.settings = settings; + this.settingsDependentStore = settingsDependentStore; + } + + @Override + public void execute(AbstractPlayer sender, String[] strings) { + settings.reload(); + settingsDependentStore.retrieveAllOfType().forEach(settingsDependent -> settingsDependent.reload(settings)); + String message = "&2AuthMeBungee configuration reloaded!"; + if (sender != null) { + sender.sendMessage(message); + } else { + logger.send(message); + } + } +} diff --git a/src/main/java/fr/xephi/authmebungee/config/BungeeConfigProperties.java b/common/src/main/java/fr/xephi/authmeproxy/common/config/ProxyConfigProperties.java similarity index 90% rename from src/main/java/fr/xephi/authmebungee/config/BungeeConfigProperties.java rename to common/src/main/java/fr/xephi/authmeproxy/common/config/ProxyConfigProperties.java index 600d447..10cf492 100644 --- a/src/main/java/fr/xephi/authmebungee/config/BungeeConfigProperties.java +++ b/common/src/main/java/fr/xephi/authmeproxy/common/config/ProxyConfigProperties.java @@ -1,4 +1,4 @@ -package fr.xephi.authmebungee.config; +package fr.xephi.authmeproxy.common.config; import ch.jalu.configme.Comment; import ch.jalu.configme.SettingsHolder; @@ -9,7 +9,7 @@ import static ch.jalu.configme.properties.PropertyInitializer.newListProperty; import static ch.jalu.configme.properties.PropertyInitializer.newProperty; -public class BungeeConfigProperties implements SettingsHolder { +public class ProxyConfigProperties implements SettingsHolder { @Comment("List of servers in the network where authme is installed") public static final Property> AUTH_SERVERS = @@ -30,7 +30,7 @@ public class BungeeConfigProperties implements SettingsHolder { public static final Property SERVER_SWITCH_REQUIRES_AUTH = newProperty("serverSwitch.requiresAuth", true); public static final Property SERVER_SWITCH_KICK_MESSAGE = - newProperty("serverSwitch.kickMessage", "Authentication required."); + newProperty("serverSwitch.kickMessage", "&4Authentication required."); @Comment("Enable auto-login between servers") public static final Property AUTOLOGIN = newProperty("autoLogin", false); @@ -41,7 +41,7 @@ public class BungeeConfigProperties implements SettingsHolder { public static final Property SEND_ON_LOGOUT_TARGET = newProperty("unloggedUserServer", ""); - private BungeeConfigProperties() { + private ProxyConfigProperties() { } } diff --git a/common/src/main/java/fr/xephi/authmeproxy/common/config/ProxySettingsProvider.java b/common/src/main/java/fr/xephi/authmeproxy/common/config/ProxySettingsProvider.java new file mode 100644 index 0000000..de8e54f --- /dev/null +++ b/common/src/main/java/fr/xephi/authmeproxy/common/config/ProxySettingsProvider.java @@ -0,0 +1,15 @@ +package fr.xephi.authmeproxy.common.config; + +import fr.xephi.authmeproxy.common.annotations.DataFolder; + +import javax.inject.Inject; +import java.io.File; + +public class ProxySettingsProvider extends SettingsProvider { + + @Inject + public ProxySettingsProvider(@DataFolder File dataFolder) { + super(dataFolder, ProxyConfigProperties.class); + } + +} diff --git a/src/main/java/fr/xephi/authmebungee/config/SettingsDependent.java b/common/src/main/java/fr/xephi/authmeproxy/common/config/SettingsDependent.java similarity index 88% rename from src/main/java/fr/xephi/authmebungee/config/SettingsDependent.java rename to common/src/main/java/fr/xephi/authmeproxy/common/config/SettingsDependent.java index a038f7a..4b9c4da 100644 --- a/src/main/java/fr/xephi/authmebungee/config/SettingsDependent.java +++ b/common/src/main/java/fr/xephi/authmeproxy/common/config/SettingsDependent.java @@ -1,4 +1,4 @@ -package fr.xephi.authmebungee.config; +package fr.xephi.authmeproxy.common.config; import ch.jalu.configme.SettingsManager; diff --git a/src/main/java/fr/xephi/authmebungee/config/SettingsProvider.java b/common/src/main/java/fr/xephi/authmeproxy/common/config/SettingsProvider.java similarity index 86% rename from src/main/java/fr/xephi/authmebungee/config/SettingsProvider.java rename to common/src/main/java/fr/xephi/authmeproxy/common/config/SettingsProvider.java index f9b5dee..f3a5e50 100644 --- a/src/main/java/fr/xephi/authmebungee/config/SettingsProvider.java +++ b/common/src/main/java/fr/xephi/authmeproxy/common/config/SettingsProvider.java @@ -1,4 +1,4 @@ -package fr.xephi.authmebungee.config; +package fr.xephi.authmeproxy.common.config; import ch.jalu.configme.SettingsHolder; import ch.jalu.configme.SettingsManager; @@ -12,9 +12,9 @@ */ public abstract class SettingsProvider implements Provider { - private File dataFolder; + private final File dataFolder; - private Class properties; + private final Class properties; protected SettingsProvider(File dataFolder, Class properties) { this.dataFolder = dataFolder; diff --git a/common/src/main/java/fr/xephi/authmeproxy/common/data/AuthUserState.java b/common/src/main/java/fr/xephi/authmeproxy/common/data/AuthUserState.java new file mode 100644 index 0000000..7d58495 --- /dev/null +++ b/common/src/main/java/fr/xephi/authmeproxy/common/data/AuthUserState.java @@ -0,0 +1,29 @@ +package fr.xephi.authmeproxy.common.data; + +public class AuthUserState { + + private final String name; + private boolean isLogged; + + public AuthUserState(String name, boolean isLogged) { + this.name = name.toLowerCase(); + this.isLogged = isLogged; + } + + public AuthUserState(String name) { + this(name, false); + } + + public String getName() { + return name; + } + + public boolean isLogged() { + return isLogged; + } + + public void setLogged(boolean isLogged) { + this.isLogged = isLogged; + } + +} diff --git a/common/src/main/java/fr/xephi/authmeproxy/common/platform/AbstractPlatform.java b/common/src/main/java/fr/xephi/authmeproxy/common/platform/AbstractPlatform.java new file mode 100644 index 0000000..92923ee --- /dev/null +++ b/common/src/main/java/fr/xephi/authmeproxy/common/platform/AbstractPlatform.java @@ -0,0 +1,9 @@ +package fr.xephi.authmeproxy.common.platform; + +import java.util.Collection; + +public abstract class AbstractPlatform

{ + + public abstract Collection

getAllPlayers(); + public abstract boolean connect(P player, String server); +} diff --git a/common/src/main/java/fr/xephi/authmeproxy/common/platform/AbstractPlayer.java b/common/src/main/java/fr/xephi/authmeproxy/common/platform/AbstractPlayer.java new file mode 100644 index 0000000..cfd1e6e --- /dev/null +++ b/common/src/main/java/fr/xephi/authmeproxy/common/platform/AbstractPlayer.java @@ -0,0 +1,24 @@ +package fr.xephi.authmeproxy.common.platform; + +import java.util.Optional; + +public abstract class AbstractPlayer { + protected final Object handle; + + public AbstractPlayer(Object handle) { + this.handle = handle; + } + + public abstract String getUsername(); + + public abstract Optional getCurrentServer(); + + public Object getHandle() { + return handle; + } + + public abstract void sendBungeeMessage(byte[] data); + + public abstract void sendMessage(String message); + +} diff --git a/common/src/main/java/fr/xephi/authmeproxy/common/services/AuthStateService.java b/common/src/main/java/fr/xephi/authmeproxy/common/services/AuthStateService.java new file mode 100644 index 0000000..75ef1ca --- /dev/null +++ b/common/src/main/java/fr/xephi/authmeproxy/common/services/AuthStateService.java @@ -0,0 +1,176 @@ +package fr.xephi.authmeproxy.common.services; + +import ch.jalu.configme.SettingsManager; +import fr.xephi.authmeproxy.common.AbstractAuthMeLogger; +import fr.xephi.authmeproxy.common.config.ProxyConfigProperties; +import fr.xephi.authmeproxy.common.config.SettingsDependent; +import fr.xephi.authmeproxy.common.data.AuthUserState; +import fr.xephi.authmeproxy.common.platform.AbstractPlatform; +import fr.xephi.authmeproxy.common.platform.AbstractPlayer; + +import javax.inject.Inject; +import java.util.*; + +public class AuthStateService implements SettingsDependent { + + private final AbstractPlatform platform; + private final AbstractAuthMeLogger logger; + private final MessageSenderService messageSenderService; + + private final Map players; + + // Settings + private boolean isAutoLoginEnabled; + private boolean isServerSwitchRequiresAuth; + private String requiresAuthKickMessage; + private List authServers; + private boolean allServersAreAuthServers; + private boolean isCommandsRequireAuth; + private List commandWhitelist; + private boolean chatRequiresAuth; + private boolean isSendOnLogoutEnabled; + private String sendOnLogoutTarget; + + @Inject + public AuthStateService( + AbstractPlatform platform, + AbstractAuthMeLogger logger, + MessageSenderService messageSenderService, + SettingsManager settings + ) { + this.platform = platform; + this.logger = logger; + this.messageSenderService = messageSenderService; + players = new HashMap<>(); + reload(settings); + } + + @Override + public void reload(SettingsManager settings) { + isAutoLoginEnabled = settings.getProperty(ProxyConfigProperties.AUTOLOGIN); + isServerSwitchRequiresAuth = settings.getProperty(ProxyConfigProperties.SERVER_SWITCH_REQUIRES_AUTH); + requiresAuthKickMessage = settings.getProperty(ProxyConfigProperties.SERVER_SWITCH_KICK_MESSAGE); + authServers = new ArrayList<>(); + for (String server : settings.getProperty(ProxyConfigProperties.AUTH_SERVERS)) { + authServers.add(server.toLowerCase()); + } + allServersAreAuthServers = settings.getProperty(ProxyConfigProperties.ALL_SERVERS_ARE_AUTH_SERVERS); + isCommandsRequireAuth = settings.getProperty(ProxyConfigProperties.COMMANDS_REQUIRE_AUTH); + commandWhitelist = new ArrayList<>(); + for (String command : settings.getProperty(ProxyConfigProperties.COMMANDS_WHITELIST)) { + commandWhitelist.add(command.toLowerCase()); + } + chatRequiresAuth = settings.getProperty(ProxyConfigProperties.CHAT_REQUIRES_AUTH); + isSendOnLogoutEnabled = settings.getProperty(ProxyConfigProperties.ENABLE_SEND_ON_LOGOUT); + sendOnLogoutTarget = settings.getProperty(ProxyConfigProperties.SEND_ON_LOGOUT_TARGET); + } + + private boolean isAuthServer(String serverName) { + return allServersAreAuthServers || authServers.contains(serverName.toLowerCase()); + } + + public AuthUserState trackUser(String name) { + name = name.toLowerCase(); + if (players.containsKey(name)) { + logger.warn("Player " + name + " was already being tracked! Removing previous instance from the memory store..."); + } + AuthUserState authUserState = new AuthUserState(name); + players.put(name, authUserState); + return authUserState; + } + + public void untrackUser(String name) { + name = name.toLowerCase(); + players.remove(name); + } + + public Optional getAuthUserState(String name) { + name = name.toLowerCase(); + return Optional.ofNullable(players.get(name)); + } + + public Optional toPlayer(AuthUserState authUserState) { + for (AbstractPlayer current : platform.getAllPlayers()) { + if (current.getUsername().equalsIgnoreCase(authUserState.getName())) { + return Optional.of(current); + } + } + return Optional.empty(); + } + + public void login(String name) { + getAuthUserState(name) + .ifPresent(authUserState -> authUserState.setLogged(true)); + } + + public void logout(String name) { + getAuthUserState(name) + .ifPresent(authUserState -> { + authUserState.setLogged(false); + if (!isSendOnLogoutEnabled) { + return; + } + toPlayer(authUserState) + .ifPresent(player -> platform.connect(player, sendOnLogoutTarget)); + }); + } + + private boolean shouldBeRestricted(AbstractPlayer player) { + // Filter only unauthenticated players + AuthUserState authUserState = getAuthUserState(player.getUsername()).orElse(null); + if (authUserState != null && authUserState.isLogged()) { + return false; + } + String currentServer = player.getCurrentServer().orElse(null); + if (currentServer == null) { + return true; + } + // Only in auth servers + return isAuthServer(currentServer); + } + + public boolean shouldAllowChat(AbstractPlayer player) { + return !chatRequiresAuth || !shouldBeRestricted(player); + } + + public boolean shouldAllowCommand(AbstractPlayer player, String command) { + if (!isCommandsRequireAuth) { + return true; + } + return !shouldBeRestricted(player) || commandWhitelist.contains(command.split(" ")[0].toLowerCase()); + } + + public void handleServerConnected(AbstractPlayer player, String server) { + AuthUserState authUserState = getAuthUserState(player.getUsername()).orElse(null); + boolean isAuthenticated = authUserState != null && authUserState.isLogged(); + + if (isAuthenticated && isAuthServer(server)) { + // If AutoLogin enabled, notify the server + if (isAutoLoginEnabled) { + messageSenderService.sendAutoLoginMessage(player); + } + } + } + + public Optional shouldAllowServerSwitch(AbstractPlayer player, String server) { + if (!isServerSwitchRequiresAuth) { + return Optional.empty(); + } + + AuthUserState authUserState = getAuthUserState(player.getUsername()).orElse(null); + boolean isAuthenticated = authUserState != null && authUserState.isLogged(); + + // Skip logged users + if (isAuthenticated) { + return Optional.empty(); + } + + // Only check non auth servers + if (isAuthServer(server)) { + return Optional.empty(); + } + + // If the player is not logged in and serverSwitchRequiresAuth is enabled, cancel the connection + return Optional.of(requiresAuthKickMessage); + } +} diff --git a/common/src/main/java/fr/xephi/authmeproxy/common/services/MessageHandleService.java b/common/src/main/java/fr/xephi/authmeproxy/common/services/MessageHandleService.java new file mode 100644 index 0000000..6eec3ae --- /dev/null +++ b/common/src/main/java/fr/xephi/authmeproxy/common/services/MessageHandleService.java @@ -0,0 +1,65 @@ +package fr.xephi.authmeproxy.common.services; + +import com.google.common.io.ByteArrayDataInput; +import com.google.common.io.ByteStreams; + +import javax.inject.Inject; + +public class MessageHandleService { + + @Inject + private AuthStateService authStateService; + + public void handleMessage(boolean fromPlayer, String channel, byte[] data) { + // Check if a player is not trying to send us a fake message + if (fromPlayer) { + return; + } + + // Check if the message is for a server (ignore client messages) + if (!channel.equals("bungeecord:main") && !channel.equals("BungeeCord")) { + return; + } + + // Read the plugin message + ByteArrayDataInput in = ByteStreams.newDataInput(data); + + // Accept only broadcasts + if (!in.readUTF().equals("Forward")) { + return; + } + in.readUTF(); // Skip ONLINE/ALL parameter + + // Let's check the subchannel + if (!in.readUTF().equals("AuthMe.v2.Broadcast")) { + return; + } + + // Read data byte array + short dataLength = in.readShort(); + byte[] dataBytes = new byte[dataLength]; + in.readFully(dataBytes); + ByteArrayDataInput dataIn = ByteStreams.newDataInput(dataBytes); + + // For now that's the only type of message the server is able to receive + String type = dataIn.readUTF(); + switch (type) { + case "login": + handleOnLogin(dataIn); + break; + case "logout": + handleOnLogout(dataIn); + break; + } + } + + private void handleOnLogin(ByteArrayDataInput in) { + String name = in.readUTF(); + authStateService.login(name); + } + + private void handleOnLogout(ByteArrayDataInput in) { + String name = in.readUTF(); + authStateService.logout(name); + } +} diff --git a/common/src/main/java/fr/xephi/authmeproxy/common/services/MessageSenderService.java b/common/src/main/java/fr/xephi/authmeproxy/common/services/MessageSenderService.java new file mode 100644 index 0000000..83e1510 --- /dev/null +++ b/common/src/main/java/fr/xephi/authmeproxy/common/services/MessageSenderService.java @@ -0,0 +1,22 @@ +package fr.xephi.authmeproxy.common.services; + +import com.google.common.io.ByteArrayDataOutput; +import com.google.common.io.ByteStreams; +import fr.xephi.authmeproxy.common.platform.AbstractPlayer; + +import javax.inject.Inject; + +public class MessageSenderService { + + @Inject + public MessageSenderService() { + } + + public void sendAutoLoginMessage(AbstractPlayer player) { + ByteArrayDataOutput out = ByteStreams.newDataOutput(); + out.writeUTF("AuthMe.v2"); + out.writeUTF("perform.login"); + out.writeUTF(player.getUsername()); + player.sendBungeeMessage(out.toByteArray()); + } +} diff --git a/pom.xml b/pom.xml index d120ea1..458ac06 100644 --- a/pom.xml +++ b/pom.xml @@ -5,10 +5,18 @@ 4.0.0 fr.xephi - authmebungee - 2.2.0-SNAPSHOT + authmeproxy-parent + 2.3.0-SNAPSHOT + pom - AuthMeBungee + + common + bungee + velocity + universal + + + AuthMeProxy Bungeecord addon for AuthMe! 2017 https://www.spigotmc.org/resources/authmebungee.50219/ @@ -54,13 +62,13 @@ + 11 UTF-8 UTF-8 clean install - ${project.name}-${project.version}-noshade false @@ -74,161 +82,171 @@ src/main/resources - - - org.apache.maven.plugins - maven-clean-plugin - 3.2.0 - - - org.apache.maven.plugins - maven-resources-plugin - 3.3.1 - - - org.apache.maven.plugins - maven-compiler-plugin - 3.11.0 - - 1.8 - 1.8 - - - - org.apache.maven.plugins - maven-surefire-plugin - 3.1.0 - - false - - - - org.apache.maven.plugins - maven-jar-plugin - 3.3.0 - - - org.apache.maven.plugins - maven-javadoc-plugin - 3.5.0 - - - attach-javadoc - - jar - - - - - ${project.name}-${project.version} - public - false - - - - org.apache.maven.plugins - maven-source-plugin - 3.2.1 - - - attach-sources - - jar - - - - - ${project.name}-${project.version} - - - - org.apache.maven.plugins - maven-shade-plugin - 3.4.1 - - - package - - shade - - - - - false - true - ${project.name}-${project.version} - - - javax.annotation - fr.xephi.authmebungee.libs.javax.annotation - - - javax.inject - fr.xephi.authmebungee.libs.javax.inject - - - ch.jalu.injector - fr.xephi.authmebungee.libs.jalu.injector - - - org.yaml.snakeyaml - fr.xephi.authmebungee.libs.yaml.snakeyaml - - - ch.jalu.configme - fr.xephi.authmebungee.libs.jalu.configme - - - org.bstats - fr.xephi.authmebungee.libs.org.bstats - - + + + + org.apache.maven.plugins + maven-clean-plugin + 3.2.0 + + + org.apache.maven.plugins + maven-resources-plugin + 3.3.1 + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + ${java.version} + ${java.version} + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.1.0 + + false + + + + org.apache.maven.plugins + maven-jar-plugin + 3.3.0 + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.5.0 + + + attach-javadoc + + jar + + + + + ${project.name}-${project.version} + public + false + + + + org.apache.maven.plugins + maven-source-plugin + 3.2.1 + + + attach-sources + + jar + + + + + ${project.name}-${project.version} + + + + org.apache.maven.plugins + maven-shade-plugin + 3.4.1 + + + package + + shade + + + + + false + true + ${project.name}-${project.version} + + + javax.annotation + fr.xephi.authmeproxy.libs.javax.annotation + + + javax.inject + fr.xephi.authmeproxy.libs.javax.inject + + + ch.jalu.injector + fr.xephi.authmeproxy.libs.jalu.injector + + + org.yaml.snakeyaml + fr.xephi.authmeproxy.libs.yaml.snakeyaml + + + ch.jalu.configme + fr.xephi.authmeproxy.libs.jalu.configme + + + org.bstats + fr.xephi.authmeproxy.libs.org.bstats + + - - - - - *:* - - META-INF/*.SF - META-INF/*.DSA - META-INF/*.RSA - META-INF/*.RSA - META-INF/*.MF - META-INF/DEPENDENCIES - META-INF/**/module-info.class - - - + + + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + META-INF/*.RSA + META-INF/*.MF + META-INF/DEPENDENCIES + META-INF/**/module-info.class + + + - - - - false - - - - - - - org.apache.maven.plugins - maven-install-plugin - 3.1.1 - - - org.apache.maven.plugins - maven-deploy-plugin - 3.1.1 - - + + + + false + + + + + + + org.apache.maven.plugins + maven-install-plugin + 3.1.1 + + + org.apache.maven.plugins + maven-deploy-plugin + 3.1.1 + + + org.codehaus.mojo + templating-maven-plugin + 1.0.0 + + + filter-src + + filter-sources + + + + + + - - - oss-repo - https://oss.sonatype.org/content/groups/public - codemc-repo @@ -253,28 +271,12 @@ compile - - - net.md-5 - bungeecord-api - 1.19-R0.1-SNAPSHOT - jar - provided - + - net.md-5 - bungeecord-api - 1.19-R0.1-SNAPSHOT - javadoc + com.google.guava + guava + 25.1-jre provided - - - - org.bstats - bstats-bungeecord - 3.0.2 - compile - diff --git a/src/main/java/fr/xephi/authmebungee/AuthMeBungee.java b/src/main/java/fr/xephi/authmebungee/AuthMeBungee.java deleted file mode 100644 index 9b7ed47..0000000 --- a/src/main/java/fr/xephi/authmebungee/AuthMeBungee.java +++ /dev/null @@ -1,75 +0,0 @@ -package fr.xephi.authmebungee; - -import ch.jalu.configme.SettingsManager; -import ch.jalu.injector.Injector; -import ch.jalu.injector.InjectorBuilder; -import fr.xephi.authmebungee.annotations.DataFolder; -import fr.xephi.authmebungee.commands.BungeeReloadCommand; -import fr.xephi.authmebungee.config.BungeeConfigProperties; -import fr.xephi.authmebungee.config.BungeeSettingsProvider; -import fr.xephi.authmebungee.listeners.BungeeMessageListener; -import fr.xephi.authmebungee.listeners.BungeePlayerListener; -import fr.xephi.authmebungee.services.AuthPlayerManager; -import net.md_5.bungee.api.ProxyServer; -import net.md_5.bungee.api.connection.ProxiedPlayer; -import net.md_5.bungee.api.plugin.Plugin; -import net.md_5.bungee.api.plugin.PluginManager; -import net.md_5.bungee.api.scheduler.TaskScheduler; -import org.bstats.bungeecord.Metrics; - -import java.util.logging.Logger; - -public class AuthMeBungee extends Plugin { - - // Instances - private Injector injector; - private SettingsManager settings; - private AuthPlayerManager authPlayerManager; - - public AuthMeBungee() { - } - - @Override - public void onEnable() { - // Prepare the injector and register stuff - setupInjector(); - - // Get singletons from the injector - settings = injector.getSingleton(SettingsManager.class); - authPlayerManager = injector.getSingleton(AuthPlayerManager.class); - - // Print some config information - getLogger().info("Current auth servers:"); - for (String authServer : settings.getProperty(BungeeConfigProperties.AUTH_SERVERS)) { - getLogger().info("> " + authServer.toLowerCase()); - } - - // Add online players (plugin hotswap, just in case) - for (ProxiedPlayer player : getProxy().getPlayers()) { - authPlayerManager.addAuthPlayer(player); - } - - // Register commands - getProxy().getPluginManager().registerCommand(this, injector.getSingleton(BungeeReloadCommand.class)); - - // Registering event listeners - getProxy().getPluginManager().registerListener(this, injector.getSingleton(BungeeMessageListener.class)); - getProxy().getPluginManager().registerListener(this, injector.getSingleton(BungeePlayerListener.class)); - - // Send metrics data - new Metrics(this, 1880); - } - - private void setupInjector() { - // Setup injector - injector = new InjectorBuilder().addDefaultHandlers("").create(); - injector.register(Logger.class, getLogger()); - injector.register(AuthMeBungee.class, this); - injector.register(ProxyServer.class, getProxy()); - injector.register(PluginManager.class, getProxy().getPluginManager()); - injector.register(TaskScheduler.class, getProxy().getScheduler()); - injector.provide(DataFolder.class, getDataFolder()); - injector.registerProvider(SettingsManager.class, BungeeSettingsProvider.class); - } - -} diff --git a/src/main/java/fr/xephi/authmebungee/commands/BungeeReloadCommand.java b/src/main/java/fr/xephi/authmebungee/commands/BungeeReloadCommand.java deleted file mode 100644 index bfd42a7..0000000 --- a/src/main/java/fr/xephi/authmebungee/commands/BungeeReloadCommand.java +++ /dev/null @@ -1,33 +0,0 @@ -package fr.xephi.authmebungee.commands; - -import ch.jalu.configme.SettingsManager; -import ch.jalu.injector.factory.SingletonStore; -import fr.xephi.authmebungee.config.SettingsDependent; -import net.md_5.bungee.api.ChatColor; -import net.md_5.bungee.api.CommandSender; -import net.md_5.bungee.api.chat.ComponentBuilder; -import net.md_5.bungee.api.plugin.Command; - -import javax.inject.Inject; - -public class BungeeReloadCommand extends Command { - - private SettingsManager settings; - private SingletonStore settingsDependentStore; - - @Inject - public BungeeReloadCommand(SettingsManager settings, SingletonStore settingsDependentStore) { - super("abreloadproxy", "authmebungee.reload"); - this.settings = settings; - this.settingsDependentStore = settingsDependentStore; - } - - @Override - public void execute(CommandSender commandSender, String[] strings) { - settings.reload(); - settingsDependentStore.retrieveAllOfType().forEach(settingsDependent -> settingsDependent.reload(settings)); - commandSender.sendMessage( - new ComponentBuilder("AuthMeBungee configuration reloaded!").color(ChatColor.GREEN).create() - ); - } -} diff --git a/src/main/java/fr/xephi/authmebungee/config/BungeeSettingsProvider.java b/src/main/java/fr/xephi/authmebungee/config/BungeeSettingsProvider.java deleted file mode 100644 index 03d5931..0000000 --- a/src/main/java/fr/xephi/authmebungee/config/BungeeSettingsProvider.java +++ /dev/null @@ -1,15 +0,0 @@ -package fr.xephi.authmebungee.config; - -import fr.xephi.authmebungee.annotations.DataFolder; - -import javax.inject.Inject; -import java.io.File; - -public class BungeeSettingsProvider extends SettingsProvider { - - @Inject - public BungeeSettingsProvider(@DataFolder File dataFolder) { - super(dataFolder, BungeeConfigProperties.class); - } - -} diff --git a/src/main/java/fr/xephi/authmebungee/data/AuthPlayer.java b/src/main/java/fr/xephi/authmebungee/data/AuthPlayer.java deleted file mode 100644 index 1eb6680..0000000 --- a/src/main/java/fr/xephi/authmebungee/data/AuthPlayer.java +++ /dev/null @@ -1,45 +0,0 @@ -package fr.xephi.authmebungee.data; - -import net.md_5.bungee.api.ProxyServer; -import net.md_5.bungee.api.connection.ProxiedPlayer; - -public class AuthPlayer { - - private String name; - private boolean isLogged; - - public AuthPlayer(String name, boolean isLogged) { - this.name = name.toLowerCase(); - this.isLogged = isLogged; - } - - public AuthPlayer(String name) { - this(name, false); - } - - public String getName() { - return name; - } - - public boolean isLogged() { - return isLogged; - } - - public void setLogged(boolean isLogged) { - this.isLogged = isLogged; - } - - public ProxiedPlayer getPlayer() { - for (ProxiedPlayer current : ProxyServer.getInstance().getPlayers()) { - if (current.getName().equalsIgnoreCase(name)) { - return current; - } - } - return null; - } - - public boolean isOnline() { - return getPlayer() != null; - } - -} diff --git a/src/main/java/fr/xephi/authmebungee/listeners/BungeeMessageListener.java b/src/main/java/fr/xephi/authmebungee/listeners/BungeeMessageListener.java deleted file mode 100644 index a0322cf..0000000 --- a/src/main/java/fr/xephi/authmebungee/listeners/BungeeMessageListener.java +++ /dev/null @@ -1,110 +0,0 @@ -package fr.xephi.authmebungee.listeners; - -import ch.jalu.configme.SettingsManager; -import com.google.common.io.ByteArrayDataInput; -import com.google.common.io.ByteStreams; -import fr.xephi.authmebungee.config.BungeeConfigProperties; -import fr.xephi.authmebungee.config.SettingsDependent; -import fr.xephi.authmebungee.data.AuthPlayer; -import fr.xephi.authmebungee.services.AuthPlayerManager; -import net.md_5.bungee.api.ProxyServer; -import net.md_5.bungee.api.connection.ProxiedPlayer; -import net.md_5.bungee.api.connection.Server; -import net.md_5.bungee.api.event.PluginMessageEvent; -import net.md_5.bungee.api.plugin.Listener; -import net.md_5.bungee.event.EventHandler; - -import javax.inject.Inject; - -public class BungeeMessageListener implements Listener, SettingsDependent { - - // Services - private final AuthPlayerManager authPlayerManager; - - // Settings - private boolean isSendOnLogoutEnabled; - private String sendOnLogoutTarget; - - @Inject - public BungeeMessageListener(final SettingsManager settings, final AuthPlayerManager authPlayerManager) { - this.authPlayerManager = authPlayerManager; - reload(settings); - } - - @Override - public void reload(final SettingsManager settings) { - isSendOnLogoutEnabled = settings.getProperty(BungeeConfigProperties.ENABLE_SEND_ON_LOGOUT); - sendOnLogoutTarget = settings.getProperty(BungeeConfigProperties.SEND_ON_LOGOUT_TARGET); - } - - @EventHandler - public void onPluginMessage(final PluginMessageEvent event) { - if (event.isCancelled()) { - return; - } - - // Check if the message is for a server (ignore client messages) - if (!event.getTag().equals("BungeeCord")) { - return; - } - - // Check if a player is not trying to send us a fake message - if (!(event.getSender() instanceof Server)) { - return; - } - - // Read the plugin message - final ByteArrayDataInput in = ByteStreams.newDataInput(event.getData()); - - // Accept only broadcasts - if(!in.readUTF().equals("Forward")) { - return; - } - in.readUTF(); // Skip ONLINE/ALL parameter - - // Let's check the subchannel - if (!in.readUTF().equals("AuthMe.v2.Broadcast")) { - return; - } - - // Read data byte array - final short dataLength = in.readShort(); - final byte[] dataBytes = new byte[dataLength]; - in.readFully(dataBytes); - final ByteArrayDataInput dataIn = ByteStreams.newDataInput(dataBytes); - - // For now that's the only type of message the server is able to receive - final String type = dataIn.readUTF(); - switch (type) { - case "login": - handleOnLogin(dataIn); - break; - case "logout": - handleOnLogout(dataIn); - break; - } - } - - private void handleOnLogin(final ByteArrayDataInput in) { - final String name = in.readUTF(); - final AuthPlayer authPlayer = authPlayerManager.getAuthPlayer(name); - if (authPlayer != null) { - authPlayer.setLogged(true); - } - } - - private void handleOnLogout(final ByteArrayDataInput in) { - final String name = in.readUTF(); - final AuthPlayer authPlayer = authPlayerManager.getAuthPlayer(name); - if (authPlayer != null) { - authPlayer.setLogged(false); - if (isSendOnLogoutEnabled) { - final ProxiedPlayer player = authPlayer.getPlayer(); - if (player != null) { - player.connect(ProxyServer.getInstance().getServerInfo(sendOnLogoutTarget)); - } - } - } - } - -} diff --git a/src/main/java/fr/xephi/authmebungee/listeners/BungeePlayerListener.java b/src/main/java/fr/xephi/authmebungee/listeners/BungeePlayerListener.java deleted file mode 100644 index b2aae2e..0000000 --- a/src/main/java/fr/xephi/authmebungee/listeners/BungeePlayerListener.java +++ /dev/null @@ -1,189 +0,0 @@ -package fr.xephi.authmebungee.listeners; - -import ch.jalu.configme.SettingsManager; -import com.google.common.io.ByteArrayDataOutput; -import com.google.common.io.ByteStreams; -import fr.xephi.authmebungee.config.BungeeConfigProperties; -import fr.xephi.authmebungee.config.SettingsDependent; -import fr.xephi.authmebungee.data.AuthPlayer; -import fr.xephi.authmebungee.services.AuthPlayerManager; -import net.md_5.bungee.api.ChatColor; -import net.md_5.bungee.api.chat.TextComponent; -import net.md_5.bungee.api.config.ServerInfo; -import net.md_5.bungee.api.connection.ProxiedPlayer; -import net.md_5.bungee.api.event.*; -import net.md_5.bungee.api.plugin.Listener; -import net.md_5.bungee.event.EventHandler; -import net.md_5.bungee.event.EventPriority; - -import javax.inject.Inject; -import java.util.ArrayList; -import java.util.List; - -public class BungeePlayerListener implements Listener, SettingsDependent { - - // Services - private final AuthPlayerManager authPlayerManager; - - // Settings - private boolean isAutoLoginEnabled; - private boolean isServerSwitchRequiresAuth; - private String requiresAuthKickMessage; - private List authServers; - private boolean allServersAreAuthServers; - private boolean isCommandsRequireAuth; - private List commandWhitelist; - private boolean chatRequiresAuth; - - @Inject - public BungeePlayerListener(final SettingsManager settings, final AuthPlayerManager authPlayerManager) { - this.authPlayerManager = authPlayerManager; - reload(settings); - } - - @Override - public void reload(final SettingsManager settings) { - isAutoLoginEnabled = settings.getProperty(BungeeConfigProperties.AUTOLOGIN); - isServerSwitchRequiresAuth = settings.getProperty(BungeeConfigProperties.SERVER_SWITCH_REQUIRES_AUTH); - requiresAuthKickMessage = settings.getProperty(BungeeConfigProperties.SERVER_SWITCH_KICK_MESSAGE); - authServers = new ArrayList<>(); - for (final String server : settings.getProperty(BungeeConfigProperties.AUTH_SERVERS)) { - authServers.add(server.toLowerCase()); - } - allServersAreAuthServers = settings.getProperty(BungeeConfigProperties.ALL_SERVERS_ARE_AUTH_SERVERS); - isCommandsRequireAuth = settings.getProperty(BungeeConfigProperties.COMMANDS_REQUIRE_AUTH); - commandWhitelist = new ArrayList<>(); - for (final String command : settings.getProperty(BungeeConfigProperties.COMMANDS_WHITELIST)) { - commandWhitelist.add(command.toLowerCase()); - } - chatRequiresAuth = settings.getProperty(BungeeConfigProperties.CHAT_REQUIRES_AUTH); - } - - @EventHandler - public void onPlayerJoin(final PostLoginEvent event) { - // Register player in our list - authPlayerManager.addAuthPlayer(event.getPlayer()); - } - - @EventHandler - public void onPlayerDisconnect(final PlayerDisconnectEvent event) { - // Remove player from out list - authPlayerManager.removeAuthPlayer(event.getPlayer()); - } - - @EventHandler(priority = EventPriority.LOWEST) - public void onCommand(final ChatEvent event) { - if (event.isCancelled() || !event.isCommand() || !isCommandsRequireAuth) { - return; - } - - // Check if it's a player - if (!(event.getSender() instanceof ProxiedPlayer)) { - return; - } - final ProxiedPlayer player = (ProxiedPlayer) event.getSender(); - - // Filter only unauthenticated players - final AuthPlayer authPlayer = authPlayerManager.getAuthPlayer(player); - if (authPlayer != null && authPlayer.isLogged()) { - return; - } - // Only in auth servers - if (!isAuthServer(player.getServer().getInfo())) { - return; - } - // Check if command is whitelisted command - if (commandWhitelist.contains(event.getMessage().split(" ")[0].toLowerCase())) { - return; - } - event.setCancelled(true); - } - - // Priority is set to lowest to keep compatibility with some chat plugins - @EventHandler(priority = EventPriority.LOWEST) - public void onPlayerChat(final ChatEvent event) { - if (event.isCancelled() || event.isCommand()) { - return; - } - - // Check if it's a player - if (!(event.getSender() instanceof ProxiedPlayer)) { - return; - } - final ProxiedPlayer player = (ProxiedPlayer) event.getSender(); - - // Filter only unauthenticated players - final AuthPlayer authPlayer = authPlayerManager.getAuthPlayer(player); - if (authPlayer != null && authPlayer.isLogged()) { - return; - } - // Only in auth servers - if (!isAuthServer(player.getServer().getInfo())) { - return; - } - - if (!chatRequiresAuth) { - return; - } - event.setCancelled(true); - } - - private boolean isAuthServer(ServerInfo serverInfo) { - return allServersAreAuthServers || authServers.contains(serverInfo.getName().toLowerCase()); - } - - @EventHandler(priority = EventPriority.LOWEST) - public void onPlayerConnectedToServer(final ServerSwitchEvent event) { - final ProxiedPlayer player = event.getPlayer(); - final ServerInfo server = player.getServer().getInfo(); - final AuthPlayer authPlayer = authPlayerManager.getAuthPlayer(player); - final boolean isAuthenticated = authPlayer != null && authPlayer.isLogged(); - - if (isAuthenticated && isAuthServer(server)) { - // If AutoLogin enabled, notify the server - if (isAutoLoginEnabled) { - final ByteArrayDataOutput out = ByteStreams.newDataOutput(); - out.writeUTF("AuthMe.v2"); - out.writeUTF("perform.login"); - out.writeUTF(event.getPlayer().getName()); - server.sendData("BungeeCord", out.toByteArray(), false); - } - } - } - - @EventHandler(priority = EventPriority.HIGHEST) - public void onPlayerConnectingToServer(final ServerConnectEvent event) { - if (event.isCancelled()) { - return; - } - - final ProxiedPlayer player = event.getPlayer(); - final AuthPlayer authPlayer = authPlayerManager.getAuthPlayer(player); - final boolean isAuthenticated = authPlayer != null && authPlayer.isLogged(); - - // Skip logged users - if (isAuthenticated) { - return; - } - - // Only check non auth servers - if (isAuthServer(event.getTarget())) { - return; - } - - // If the player is not logged in and serverSwitchRequiresAuth is enabled, cancel the connection - if (isServerSwitchRequiresAuth) { - event.setCancelled(true); - - final TextComponent reasonMessage = new TextComponent(requiresAuthKickMessage); - reasonMessage.setColor(ChatColor.RED); - - // Handle race condition on player join on a misconfigured network - if (player.getServer() == null) { - player.disconnect(reasonMessage); - } else { - player.sendMessage(reasonMessage); - } - } - } -} diff --git a/src/main/java/fr/xephi/authmebungee/services/AuthPlayerManager.java b/src/main/java/fr/xephi/authmebungee/services/AuthPlayerManager.java deleted file mode 100644 index 9988a33..0000000 --- a/src/main/java/fr/xephi/authmebungee/services/AuthPlayerManager.java +++ /dev/null @@ -1,43 +0,0 @@ -package fr.xephi.authmebungee.services; - -import fr.xephi.authmebungee.data.AuthPlayer; -import net.md_5.bungee.api.connection.ProxiedPlayer; - -import java.util.HashMap; -import java.util.Map; - -/* - * Players manager - store all references to AuthPlayer objects through an HashMap - */ -public class AuthPlayerManager { - - private Map players; - - public AuthPlayerManager() { - players = new HashMap<>(); - } - - public void addAuthPlayer(AuthPlayer player) { - players.put(player.getName(), player); - } - - public void addAuthPlayer(ProxiedPlayer player) { - addAuthPlayer(new AuthPlayer(player.getName().toLowerCase())); - } - - public void removeAuthPlayer(String name) { - players.remove(name.toLowerCase()); - } - - public void removeAuthPlayer(ProxiedPlayer player) { - removeAuthPlayer(player.getName()); - } - - public AuthPlayer getAuthPlayer(String name) { - return players.get(name.toLowerCase()); - } - - public AuthPlayer getAuthPlayer(ProxiedPlayer player) { - return getAuthPlayer(player.getName()); - } -} diff --git a/src/main/java/fr/xephi/authmebungee/utils/FileUtils.java b/src/main/java/fr/xephi/authmebungee/utils/FileUtils.java deleted file mode 100644 index 51f31c3..0000000 --- a/src/main/java/fr/xephi/authmebungee/utils/FileUtils.java +++ /dev/null @@ -1,31 +0,0 @@ -package fr.xephi.authmebungee.utils; - -import java.io.File; -import java.io.IOException; - -/** - * File utilities. - */ -public final class FileUtils { - - // Utility class - private FileUtils() { - } - - /** - * Creates the given file or throws an exception. - * - * @param file the file to create - */ - public static void create(File file) { - try { - file.getParentFile().mkdirs(); - boolean result = file.createNewFile(); - if (!result) { - throw new IllegalStateException("Could not create file '" + file + "'"); - } - } catch (IOException e) { - throw new IllegalStateException("Error while creating file '" + file + "'", e); - } - } -} diff --git a/src/main/resources/bungee.yml b/src/main/resources/bungee.yml deleted file mode 100644 index 3ad3771..0000000 --- a/src/main/resources/bungee.yml +++ /dev/null @@ -1,4 +0,0 @@ -name: ${project.name} -main: fr.xephi.authmebungee.AuthMeBungee -version: ${project.version} -author: AuthMeTeam diff --git a/universal/pom.xml b/universal/pom.xml new file mode 100644 index 0000000..ecd4a15 --- /dev/null +++ b/universal/pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + + fr.xephi + authmeproxy-parent + 2.3.0-SNAPSHOT + + + authmeproxy-universal + AuthMeProxy-Universal + + + ${project.name}-${project.version}-noshade + + + org.apache.maven.plugins + maven-shade-plugin + + + + + + + fr.xephi + authmeproxy-bungee + 2.3.0-SNAPSHOT + compile + + + fr.xephi + authmeproxy-velocity + 2.3.0-SNAPSHOT + compile + + + + diff --git a/velocity/pom.xml b/velocity/pom.xml new file mode 100644 index 0000000..275eceb --- /dev/null +++ b/velocity/pom.xml @@ -0,0 +1,68 @@ + + + 4.0.0 + + + fr.xephi + authmeproxy-parent + 2.3.0-SNAPSHOT + + + authmeproxy-velocity + AuthMeProxy-Velocity + + + ${project.name}-${project.version}-noshade + + + org.apache.maven.plugins + maven-javadoc-plugin + + + org.apache.maven.plugins + maven-source-plugin + + + org.apache.maven.plugins + maven-shade-plugin + + + org.codehaus.mojo + templating-maven-plugin + + + + + + + papermc-repo + https://repo.papermc.io/repository/maven-public/ + + + + + + fr.xephi + authmeproxy-common + 2.3.0-SNAPSHOT + + + + + com.velocitypowered + velocity-api + 3.2.0-SNAPSHOT + provided + + + + + org.bstats + bstats-velocity + 3.0.2 + compile + + + diff --git a/velocity/src/main/java-templates/fr/xephi/authmeproxy/velocity/PluginDescriptor.java b/velocity/src/main/java-templates/fr/xephi/authmeproxy/velocity/PluginDescriptor.java new file mode 100644 index 0000000..53259c7 --- /dev/null +++ b/velocity/src/main/java-templates/fr/xephi/authmeproxy/velocity/PluginDescriptor.java @@ -0,0 +1,10 @@ +package fr.xephi.authmeproxy.velocity; + +public final class PluginDescriptor { + private PluginDescriptor() { + } + + public static final String ID = "authmeproxy"; + public static final String NAME = "${project.parent.name}"; + public static final String VERSION = "${project.version}"; +} diff --git a/velocity/src/main/java/fr/xephi/authmeproxy/velocity/AuthMeProxyVelocity.java b/velocity/src/main/java/fr/xephi/authmeproxy/velocity/AuthMeProxyVelocity.java new file mode 100644 index 0000000..18e1ea1 --- /dev/null +++ b/velocity/src/main/java/fr/xephi/authmeproxy/velocity/AuthMeProxyVelocity.java @@ -0,0 +1,88 @@ +package fr.xephi.authmeproxy.velocity; + +import com.velocitypowered.api.command.SimpleCommand; +import com.velocitypowered.api.event.proxy.ProxyInitializeEvent; +import com.velocitypowered.api.event.Subscribe; +import com.velocitypowered.api.plugin.Plugin; +import com.velocitypowered.api.plugin.annotation.DataDirectory; +import com.velocitypowered.api.proxy.Player; +import com.velocitypowered.api.proxy.ProxyServer; +import fr.xephi.authmeproxy.common.AbstractAuthMeProxy; +import fr.xephi.authmeproxy.common.commands.AbstractCommand; +import fr.xephi.authmeproxy.common.commands.ReloadCommand; +import fr.xephi.authmeproxy.common.platform.AbstractPlayer; +import fr.xephi.authmeproxy.velocity.listeners.PlayerListener; +import fr.xephi.authmeproxy.velocity.listeners.MessageListener; +import fr.xephi.authmeproxy.velocity.platform.VelocityPlatform; +import fr.xephi.authmeproxy.velocity.platform.VelocityPlayer; +import org.bstats.velocity.Metrics; + +import javax.inject.Inject; +import java.nio.file.Path; + +@Plugin( + id = PluginDescriptor.ID, + name = PluginDescriptor.NAME, + version = PluginDescriptor.VERSION +) +public class AuthMeProxyVelocity extends AbstractAuthMeProxy { + + private final ProxyServer proxyServer; + private final Metrics.Factory metricsFactory; + + @Inject + public AuthMeProxyVelocity( + com.google.inject.Injector proxyInjector, + ProxyServer proxyServer, + Metrics.Factory metricsFactory, + @DataDirectory Path dataDirectory + ) { + super( + proxyInjector.getInstance(VelocityPlatform.class), + proxyInjector.getInstance(AuthMeVelocityLogger.class), + dataDirectory.toFile() + ); + this.proxyServer = proxyServer; + this.metricsFactory = metricsFactory; + } + + @Subscribe + public void onProxyInitialization(ProxyInitializeEvent event) { + initialize(); + } + + @Override + protected void registerCommands() { + registerCommand(injector.getSingleton(ReloadCommand.class)); + } + + @Override + protected void registerListeners() { + proxyServer.getEventManager().register(this, injector.getSingleton(MessageListener.class)); + proxyServer.getEventManager().register(this, injector.getSingleton(PlayerListener.class)); + } + + @Override + protected void registerMetrics() { + //metricsFactory.make(this, 1880); TODO: generate a new project id on bstats + } + + private void registerCommand(AbstractCommand command) { + proxyServer.getCommandManager().register(command.getCommand(), new SimpleCommand() { + @Override + public void execute(Invocation invocation) { + Player player = null; + if (invocation.source() instanceof Player) { + player = (Player) invocation.source(); + } + AbstractPlayer abstractPlayer = player == null ? null : VelocityPlayer.fromPlayer(player); + command.execute(abstractPlayer, invocation.arguments()); + } + + @Override + public boolean hasPermission(Invocation invocation) { + return invocation.source().hasPermission(command.getPermission()); + } + }); + } +} diff --git a/velocity/src/main/java/fr/xephi/authmeproxy/velocity/AuthMeVelocityLogger.java b/velocity/src/main/java/fr/xephi/authmeproxy/velocity/AuthMeVelocityLogger.java new file mode 100644 index 0000000..a9b0c2c --- /dev/null +++ b/velocity/src/main/java/fr/xephi/authmeproxy/velocity/AuthMeVelocityLogger.java @@ -0,0 +1,40 @@ +package fr.xephi.authmeproxy.velocity; + +import com.velocitypowered.api.proxy.ProxyServer; +import fr.xephi.authmeproxy.common.AbstractAuthMeLogger; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import org.slf4j.Logger; + +import javax.inject.Inject; + +public class AuthMeVelocityLogger extends AbstractAuthMeLogger { + + private final org.slf4j.Logger logger; + private final ProxyServer proxyServer; + + @Inject + public AuthMeVelocityLogger(Logger logger, ProxyServer proxyServer) { + this.logger = logger; + this.proxyServer = proxyServer; + } + + @Override + public void send(String message) { + proxyServer.getConsoleCommandSource().sendMessage(LegacyComponentSerializer.legacyAmpersand().deserialize(message)); + } + + @Override + public void info(String... messages) { + this.logger.info(String.join(" ", messages)); + } + + @Override + public void warn(String... messages) { + this.logger.warn(String.join(" ", messages)); + } + + @Override + public void error(String... messages) { + this.logger.error(String.join(" ", messages)); + } +} diff --git a/velocity/src/main/java/fr/xephi/authmeproxy/velocity/listeners/MessageListener.java b/velocity/src/main/java/fr/xephi/authmeproxy/velocity/listeners/MessageListener.java new file mode 100644 index 0000000..3b73b27 --- /dev/null +++ b/velocity/src/main/java/fr/xephi/authmeproxy/velocity/listeners/MessageListener.java @@ -0,0 +1,26 @@ +package fr.xephi.authmeproxy.velocity.listeners; + +import com.velocitypowered.api.event.Subscribe; +import com.velocitypowered.api.event.connection.PluginMessageEvent; +import com.velocitypowered.api.proxy.ServerConnection; +import fr.xephi.authmeproxy.common.services.MessageHandleService; + +import javax.inject.Inject; + +public class MessageListener { + + @Inject + private MessageHandleService messageHandleService; + + @Subscribe + public void onPluginMessage(PluginMessageEvent event) { + if (!event.getResult().isAllowed()) { + // Ignore cancelled messages + return; + } + + boolean fromPlayer = !(event.getSource() instanceof ServerConnection); + messageHandleService.handleMessage(fromPlayer, event.getIdentifier().getId(), event.getData()); + } + +} diff --git a/velocity/src/main/java/fr/xephi/authmeproxy/velocity/listeners/PlayerListener.java b/velocity/src/main/java/fr/xephi/authmeproxy/velocity/listeners/PlayerListener.java new file mode 100644 index 0000000..1cf5810 --- /dev/null +++ b/velocity/src/main/java/fr/xephi/authmeproxy/velocity/listeners/PlayerListener.java @@ -0,0 +1,101 @@ +package fr.xephi.authmeproxy.velocity.listeners; + +import com.velocitypowered.api.event.PostOrder; +import com.velocitypowered.api.event.Subscribe; +import com.velocitypowered.api.event.command.CommandExecuteEvent; +import com.velocitypowered.api.event.connection.DisconnectEvent; +import com.velocitypowered.api.event.connection.PostLoginEvent; +import com.velocitypowered.api.event.player.PlayerChatEvent; +import com.velocitypowered.api.event.player.ServerConnectedEvent; +import com.velocitypowered.api.event.player.ServerPreConnectEvent; +import com.velocitypowered.api.proxy.Player; +import com.velocitypowered.api.proxy.server.RegisteredServer; +import fr.xephi.authmeproxy.common.services.AuthStateService; +import fr.xephi.authmeproxy.velocity.platform.VelocityPlayer; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; + +import javax.inject.Inject; + +public class PlayerListener { + + @Inject + private AuthStateService authStateService; + + @Subscribe + public void onPlayerJoin(PostLoginEvent event) { + authStateService.trackUser(event.getPlayer().getUsername()); + } + + @Subscribe + public void onPlayerDisconnect(DisconnectEvent event) { + authStateService.untrackUser(event.getPlayer().getUsername()); + } + + @Subscribe(order = PostOrder.EARLY) + public void onCommand(CommandExecuteEvent event) { + if (!event.getResult().isAllowed()) { + return; + } + if (!(event.getCommandSource() instanceof Player)) { + return; + } + Player player = (Player) event.getCommandSource(); + if (!authStateService.shouldAllowCommand(VelocityPlayer.fromPlayer(player), "/" + event.getCommand())) { + event.setResult(CommandExecuteEvent.CommandResult.denied()); + } + } + + @Subscribe(order = PostOrder.EARLY) + public void onDownstreamCommand(PlayerChatEvent event) { + if (!event.getResult().isAllowed() || !event.getMessage().startsWith("/")) { + return; + } + String command = event.getMessage().split(" ")[0].toLowerCase(); + if (!authStateService.shouldAllowCommand(VelocityPlayer.fromPlayer(event.getPlayer()), command)) { + event.setResult(PlayerChatEvent.ChatResult.denied()); + } + } + + // Priority is set to lowest to keep compatibility with some chat plugins + @Subscribe(order = PostOrder.EARLY) + public void onPlayerChat(PlayerChatEvent event) { + if (!event.getResult().isAllowed() || event.getMessage().startsWith("/")) { + return; + } + + if (!authStateService.shouldAllowChat(VelocityPlayer.fromPlayer(event.getPlayer()))) { + event.setResult(PlayerChatEvent.ChatResult.denied()); + } + } + + @Subscribe(order = PostOrder.LAST) + public void onPlayerConnectedToServer(ServerConnectedEvent event) { + Player player = event.getPlayer(); + RegisteredServer server = event.getServer(); + authStateService.handleServerConnected(VelocityPlayer.fromPlayer(player), server.getServerInfo().getName()); + } + + @Subscribe(order = PostOrder.EARLY) + public void onPlayerConnectingToServer(ServerPreConnectEvent event) { + if (!event.getResult().isAllowed()) { + return; + } + + Player player = event.getPlayer(); + String cancelReason = authStateService.shouldAllowServerSwitch( + VelocityPlayer.fromPlayer(player), + event.getOriginalServer().getServerInfo().getName() + ).orElse(null); + if (cancelReason == null) { + return; + } + event.setResult(ServerPreConnectEvent.ServerResult.denied()); + Component parsedCancelReason = LegacyComponentSerializer.legacyAmpersand().deserialize(cancelReason); + if (event.getPreviousServer() == null) { + player.disconnect(parsedCancelReason); + } else { + player.sendMessage(parsedCancelReason); + } + } +} diff --git a/velocity/src/main/java/fr/xephi/authmeproxy/velocity/platform/VelocityPlatform.java b/velocity/src/main/java/fr/xephi/authmeproxy/velocity/platform/VelocityPlatform.java new file mode 100644 index 0000000..8719589 --- /dev/null +++ b/velocity/src/main/java/fr/xephi/authmeproxy/velocity/platform/VelocityPlatform.java @@ -0,0 +1,32 @@ +package fr.xephi.authmeproxy.velocity.platform; + +import com.velocitypowered.api.proxy.ProxyServer; +import com.velocitypowered.api.proxy.server.RegisteredServer; +import fr.xephi.authmeproxy.common.platform.AbstractPlatform; + +import javax.inject.Inject; +import java.util.Collection; +import java.util.stream.Collectors; + +public class VelocityPlatform extends AbstractPlatform { + + @Inject + private ProxyServer proxyServer; + + @Override + public Collection getAllPlayers() { + return proxyServer.getAllPlayers().stream() + .map(VelocityPlayer::fromPlayer) + .collect(Collectors.toUnmodifiableList()); + } + + @Override + public boolean connect(VelocityPlayer player, String serverName) { + RegisteredServer registeredServer = proxyServer.getServer(serverName).orElse(null); + if (registeredServer == null) { + return false; + } + player.getHandle().createConnectionRequest(registeredServer).connect(); + return true; + } +} diff --git a/velocity/src/main/java/fr/xephi/authmeproxy/velocity/platform/VelocityPlayer.java b/velocity/src/main/java/fr/xephi/authmeproxy/velocity/platform/VelocityPlayer.java new file mode 100644 index 0000000..6753b53 --- /dev/null +++ b/velocity/src/main/java/fr/xephi/authmeproxy/velocity/platform/VelocityPlayer.java @@ -0,0 +1,44 @@ +package fr.xephi.authmeproxy.velocity.platform; + +import com.velocitypowered.api.proxy.Player; +import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier; +import fr.xephi.authmeproxy.common.platform.AbstractPlayer; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; + +import java.util.Optional; + +public class VelocityPlayer extends AbstractPlayer { + public VelocityPlayer(Player player) { + super(player); + } + + @Override + public String getUsername() { + return getHandle().getUsername(); + } + + @Override + public Optional getCurrentServer() { + return getHandle().getCurrentServer().map(server -> server.getServerInfo().getName()); + } + + @Override + public void sendBungeeMessage(byte[] data) { + getHandle().getCurrentServer() + .ifPresent(connection -> connection.sendPluginMessage(MinecraftChannelIdentifier.create("bungeecord", "main"), data)); + } + + @Override + public void sendMessage(String message) { + getHandle().sendMessage(LegacyComponentSerializer.legacyAmpersand().deserialize(message)); + } + + @Override + public Player getHandle() { + return (Player) super.getHandle(); + } + + public static VelocityPlayer fromPlayer(Player player) { + return new VelocityPlayer(player); + } +}