diff --git a/build-logic/src/main/kotlin/Versions.kt b/build-logic/src/main/kotlin/Versions.kt index 21131e6a..1e5c883f 100644 --- a/build-logic/src/main/kotlin/Versions.kt +++ b/build-logic/src/main/kotlin/Versions.kt @@ -33,8 +33,6 @@ object Versions { const val nettyVersion = "4.1.49.Final" const val snakeyamlVersion = "1.28" const val cloudVersion = "1.5.0" - const val adventureApiVersion = "4.10.0" - const val adventurePlatformVersion = "4.0.1" const val javaWebsocketVersion = "1.5.2" diff --git a/build.gradle.kts b/build.gradle.kts index 8b6c3715..fbcf0e1c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,6 +3,7 @@ plugins { id("floodgate.build-logic") // id("com.github.spotbugs") version "4.8.0" apply false id("io.freefair.lombok") version "6.3.0" apply false +// checkstyle } allprojects { @@ -22,15 +23,22 @@ val platforms = setOf( val api: Project = projects.api.dependencyProject subprojects { -// apply(plugin = "pmd") // apply(plugin = "com.github.spotbugs") apply { plugin("java-library") +// plugin("checkstyle") plugin("io.freefair.lombok") plugin("floodgate.build-logic") } +// checkstyle { +// toolVersion = "9.3" +// configFile = rootProject.projectDir.resolve("checkstyle.xml") +// maxErrors = 0 +// maxWarnings = 0 +// } + val relativePath = projectDir.relativeTo(rootProject.projectDir).path if (relativePath.startsWith("database" + File.separator)) { diff --git a/bungee/build.gradle.kts b/bungee/build.gradle.kts index d98b65e1..b362a0ac 100644 --- a/bungee/build.gradle.kts +++ b/bungee/build.gradle.kts @@ -4,10 +4,7 @@ var guavaVersion = "21.0" dependencies { api(projects.core) - implementation("cloud.commandframework", "cloud-bungee", Versions.cloudVersion) - implementation("net.kyori", "adventure-text-serializer-gson", Versions.adventureApiVersion) - implementation("net.kyori", "adventure-text-serializer-bungeecord", Versions.adventurePlatformVersion) } relocate("com.google.inject") diff --git a/bungee/src/main/java/org/geysermc/floodgate/listener/BungeeListener.java b/bungee/src/main/java/org/geysermc/floodgate/listener/BungeeListener.java index f86a2a32..e62a1776 100644 --- a/bungee/src/main/java/org/geysermc/floodgate/listener/BungeeListener.java +++ b/bungee/src/main/java/org/geysermc/floodgate/listener/BungeeListener.java @@ -49,7 +49,6 @@ import org.geysermc.floodgate.config.ProxyFloodgateConfig; import org.geysermc.floodgate.skin.SkinApplier; import org.geysermc.floodgate.skin.SkinData; -import org.geysermc.floodgate.util.BungeeCommandUtil; import org.geysermc.floodgate.util.LanguageManager; import org.geysermc.floodgate.util.ReflectionUtils; @@ -140,7 +139,5 @@ public void onPostLogin(PostLoginEvent event) { @EventHandler(priority = EventPriority.HIGHEST) public void onPlayerDisconnect(PlayerDisconnectEvent event) { api.playerRemoved(event.getPlayer().getUniqueId()); - - BungeeCommandUtil.AUDIENCE_CACHE.remove(event.getPlayer().getUniqueId()); //todo } } diff --git a/bungee/src/main/java/org/geysermc/floodgate/module/BungeePlatformModule.java b/bungee/src/main/java/org/geysermc/floodgate/module/BungeePlatformModule.java index dccda6b2..7bd1661a 100644 --- a/bungee/src/main/java/org/geysermc/floodgate/module/BungeePlatformModule.java +++ b/bungee/src/main/java/org/geysermc/floodgate/module/BungeePlatformModule.java @@ -83,7 +83,7 @@ public CommandManager commandManager(CommandUtil commandUtil) { CommandManager commandManager = new BungeeCommandManager<>( plugin, CommandExecutionCoordinator.simpleCoordinator(), - commandUtil::getAudience, + commandUtil::getUserAudience, audience -> (CommandSender) audience.source() ); commandManager.registerCommandPreProcessor(new FloodgateCommandPreprocessor<>(commandUtil)); @@ -92,11 +92,8 @@ public CommandManager commandManager(CommandUtil commandUtil) { @Provides @Singleton - public CommandUtil commandUtil( - FloodgateApi api, - FloodgateLogger logger, - LanguageManager languageManager) { - return new BungeeCommandUtil(plugin.getProxy(), api, logger, languageManager); + public CommandUtil commandUtil(FloodgateApi api, LanguageManager languageManager) { + return new BungeeCommandUtil(languageManager, plugin.getProxy(), api); } @Provides diff --git a/bungee/src/main/java/org/geysermc/floodgate/util/BungeeCommandUtil.java b/bungee/src/main/java/org/geysermc/floodgate/util/BungeeCommandUtil.java index e614c351..8d1e7c1c 100644 --- a/bungee/src/main/java/org/geysermc/floodgate/util/BungeeCommandUtil.java +++ b/bungee/src/main/java/org/geysermc/floodgate/util/BungeeCommandUtil.java @@ -25,42 +25,29 @@ package org.geysermc.floodgate.util; -import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; import java.util.UUID; -import lombok.RequiredArgsConstructor; import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.api.ProxyServer; -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 org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.floodgate.api.FloodgateApi; -import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.platform.command.CommandUtil; -import org.geysermc.floodgate.platform.command.TranslatableMessage; import org.geysermc.floodgate.player.UserAudience; -import org.geysermc.floodgate.player.UserAudienceArgument.PlayerType; -import org.geysermc.floodgate.util.BungeeUserAudience.BungeeConsoleAudience; -import org.geysermc.floodgate.util.BungeeUserAudience.BungeePlayerAudience; - -@RequiredArgsConstructor -public final class BungeeCommandUtil implements CommandUtil { - public static final @NonNull Map AUDIENCE_CACHE = new HashMap<>(); - private static UserAudience console; +import org.geysermc.floodgate.player.UserAudience.ConsoleAudience; +import org.geysermc.floodgate.player.UserAudience.PlayerAudience; +public final class BungeeCommandUtil extends CommandUtil { private final ProxyServer server; - private final FloodgateApi api; + private UserAudience console; - private final FloodgateLogger logger; - private final LanguageManager manager; + public BungeeCommandUtil(LanguageManager manager, ProxyServer server, FloodgateApi api) { + super(manager, api); + this.server = server; + } @Override - public @NonNull UserAudience getAudience(@NonNull Object sourceObj) { + public @NonNull UserAudience getUserAudience(@NonNull Object sourceObj) { if (!(sourceObj instanceof CommandSender)) { throw new IllegalArgumentException("Can only work with CommandSource!"); } @@ -70,7 +57,7 @@ public final class BungeeCommandUtil implements CommandUtil { if (console != null) { return console; } - return console = new BungeeConsoleAudience(source, this); + return console = new ConsoleAudience(source, this); } ProxiedPlayer player = (ProxiedPlayer) source; @@ -78,82 +65,39 @@ public final class BungeeCommandUtil implements CommandUtil { String username = player.getName(); String locale = Utils.getLocale(player.getLocale()); - return AUDIENCE_CACHE.computeIfAbsent(uuid, - $ -> new BungeePlayerAudience(uuid, username, locale, source, true, this)); + return new PlayerAudience(uuid, username, locale, source, this, true); } @Override - public @Nullable UserAudience getAudienceByUsername(@NonNull String username) { - ProxiedPlayer player = server.getPlayer(username); - return player != null ? getAudience(player) : null; + protected String getUsernameFromSource(@NonNull Object source) { + return ((ProxiedPlayer) source).getName(); } @Override - public @NonNull UserAudience getOfflineAudienceByUsername(@NonNull String username) { - return new BungeePlayerAudience(null, username, null, null, false, this); + protected UUID getUuidFromSource(@NonNull Object source) { + return ((ProxiedPlayer) source).getUniqueId(); } @Override - public @Nullable UserAudience getAudienceByUuid(@NonNull UUID uuid) { - ProxiedPlayer player = server.getPlayer(uuid); - return player != null ? getAudience(player) : null; + protected Collection getOnlinePlayers() { + return server.getPlayers(); } @Override - public @NonNull UserAudience getOfflineAudienceByUuid(@NonNull UUID uuid) { - return new BungeePlayerAudience(uuid, null, null, null, false, this); + public Object getPlayerByUuid(@NonNull UUID uuid) { + ProxiedPlayer player = server.getPlayer(uuid); + return player != null ? player : uuid; } @Override - public @NonNull Collection getOnlineUsernames(@NonNull PlayerType limitTo) { - Collection players = server.getPlayers(); - - Collection usernames = new ArrayList<>(); - switch (limitTo) { - case ALL_PLAYERS: - for (ProxiedPlayer player : players) { - usernames.add(player.getName()); - } - break; - case ONLY_JAVA: - for (ProxiedPlayer player : players) { - if (!api.isFloodgatePlayer(player.getUniqueId())) { - usernames.add(player.getName()); - } - } - break; - case ONLY_BEDROCK: - for (ProxiedPlayer player : players) { - if (api.isFloodgatePlayer(player.getUniqueId())) { - usernames.add(player.getName()); - } - } - break; - default: - throw new IllegalStateException("Unknown PlayerType"); - } - return usernames; + public Object getPlayerByUsername(@NonNull String username) { + ProxiedPlayer player = server.getPlayer(username); + return player != null ? player : username; } @Override public boolean hasPermission(Object player, String permission) { - return cast(player).hasPermission(permission); - } - - @Override - public Collection getOnlinePlayersWithPermission(String permission) { - List players = new ArrayList<>(); - for (ProxiedPlayer player : ProxyServer.getInstance().getPlayers()) { - if (hasPermission(player, permission)) { - players.add(player); - } - } - return players; - } - - @Override - public void sendMessage(Object target, String locale, TranslatableMessage message, Object... args) { - ((CommandSender) target).sendMessage(translateAndTransform(locale, message, args)); + return ((CommandSender) player).hasPermission(permission); } @Override @@ -162,23 +106,10 @@ public void sendMessage(Object target, String message) { } @Override - public void kickPlayer(Object player, String locale, TranslatableMessage message, Object... args) { - cast(player).disconnect(translateAndTransform(locale, message, args)); - } - - public BaseComponent[] translateAndTransform( - String locale, - TranslatableMessage message, - Object... args) { - return TextComponent.fromLegacyText(message.translateMessage(manager, locale, args)); - } - - protected ProxiedPlayer cast(Object player) { - try { - return (ProxiedPlayer) player; - } catch (ClassCastException exception) { - logger.error("Failed to cast {} to ProxiedPlayer", player.getClass().getName()); - throw exception; + public void kickPlayer(Object player, String message) { + // can also be a console + if (player instanceof ProxiedPlayer) { + ((ProxiedPlayer) player).disconnect(message); } } } diff --git a/bungee/src/main/java/org/geysermc/floodgate/util/BungeeUserAudience.java b/bungee/src/main/java/org/geysermc/floodgate/util/BungeeUserAudience.java deleted file mode 100644 index 3554549d..00000000 --- a/bungee/src/main/java/org/geysermc/floodgate/util/BungeeUserAudience.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Floodgate - */ - -package org.geysermc.floodgate.util; - -import java.util.UUID; -import lombok.RequiredArgsConstructor; -import net.kyori.adventure.audience.Audience; -import net.kyori.adventure.audience.ForwardingAudience; -import net.kyori.adventure.audience.MessageType; -import net.kyori.adventure.identity.Identity; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer; -import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; -import net.md_5.bungee.api.CommandSender; -import net.md_5.bungee.api.connection.ProxiedPlayer; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.geysermc.floodgate.platform.command.CommandUtil; -import org.geysermc.floodgate.platform.command.TranslatableMessage; -import org.geysermc.floodgate.player.UserAudience; - -@RequiredArgsConstructor -public class BungeeUserAudience implements UserAudience, ForwardingAudience.Single { - private final UUID uuid; - private final String locale; - private final CommandSender source; - private final CommandUtil commandUtil; - - @Override - public @NonNull UUID uuid() { - return uuid; - } - - @Override - public @NonNull String username() { - return source.getName(); - } - - @Override - public @NonNull String locale() { - return locale; - } - - @Override - public @NonNull CommandSender source() { - return source; - } - - @Override - public boolean hasPermission(@NonNull String permission) { - return source.hasPermission(permission); - } - - @Override - public void sendMessage( - @NonNull Identity source, - @NonNull Component message, - @NonNull MessageType type) { - this.source.sendMessage(GsonComponentSerializer.gson().serialize(message)); - } - - @Override - public void sendMessage(TranslatableMessage message, Object... args) { - commandUtil.sendMessage(source(), locale(), message, args); - } - - @Override - public void disconnect(@NonNull Component reason) { - if (source instanceof ProxiedPlayer) { - ((ProxiedPlayer) source).disconnect(GsonComponentSerializer.gson().serialize(reason)); - } - } - - @Override - public void disconnect(TranslatableMessage message, Object... args) { - commandUtil.kickPlayer(source(), locale(), message, args); - } - - @Override - public @NonNull Audience audience() { - return this; - } - - public static final class BungeeConsoleAudience extends BungeeUserAudience - implements ConsoleAudience { - - public BungeeConsoleAudience(CommandSender source, CommandUtil commandUtil) { - super(new UUID(0, 0), "en_us", source, commandUtil); - } - - @Override - public void sendMessage( - @NonNull Identity source, - @NonNull Component message, - @NonNull MessageType type) { - source().sendMessage(BungeeComponentSerializer.get().serialize(message)); - } - } - - public static final class BungeePlayerAudience extends BungeeUserAudience - implements PlayerAudience { - - private final String username; - private final boolean online; - - public BungeePlayerAudience( - UUID uuid, - String username, - String locale, - CommandSender source, - boolean online, - CommandUtil commandUtil) { - super(uuid, locale, source, commandUtil); - this.username = username; - this.online = online; - } - - public BungeePlayerAudience( - UUID uuid, - String locale, - CommandSender source, - boolean online, - CommandUtil commandUtil) { - this(uuid, source.getName(), locale, source, online, commandUtil); - } - - @Override - public @NonNull String username() { - return username; - } - - @Override - public boolean online() { - return online; - } - } -} diff --git a/checkstyle.xml b/checkstyle.xml new file mode 100644 index 00000000..572be29a --- /dev/null +++ b/checkstyle.xml @@ -0,0 +1,364 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 36457867..e96167ea 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -11,7 +11,6 @@ dependencies { api("com.nukkitx.fastutil", "fastutil-short-object-maps", Versions.fastutilVersion) api("com.nukkitx.fastutil", "fastutil-int-object-maps", Versions.fastutilVersion) api("org.java-websocket", "Java-WebSocket", Versions.javaWebsocketVersion) - api("net.kyori", "adventure-api", Versions.adventureApiVersion) api("cloud.commandframework", "cloud-core", Versions.cloudVersion) api("org.yaml", "snakeyaml", Versions.snakeyamlVersion) } diff --git a/core/src/main/java/org/geysermc/floodgate/command/LinkAccountCommand.java b/core/src/main/java/org/geysermc/floodgate/command/LinkAccountCommand.java index 7c221538..60392de7 100644 --- a/core/src/main/java/org/geysermc/floodgate/command/LinkAccountCommand.java +++ b/core/src/main/java/org/geysermc/floodgate/command/LinkAccountCommand.java @@ -35,20 +35,19 @@ import com.google.inject.Inject; import lombok.Getter; import lombok.NoArgsConstructor; -import net.kyori.adventure.text.Component; import org.geysermc.floodgate.api.FloodgateApi; import org.geysermc.floodgate.api.link.LinkRequestResult; import org.geysermc.floodgate.api.link.PlayerLink; import org.geysermc.floodgate.api.logger.FloodgateLogger; +import org.geysermc.floodgate.command.util.Permission; import org.geysermc.floodgate.config.FloodgateConfig; import org.geysermc.floodgate.link.GlobalPlayerLinking; import org.geysermc.floodgate.platform.command.FloodgateCommand; import org.geysermc.floodgate.platform.command.TranslatableMessage; import org.geysermc.floodgate.player.UserAudience; import org.geysermc.floodgate.player.UserAudience.PlayerAudience; -import org.geysermc.floodgate.player.UserAudienceArgument; +import org.geysermc.floodgate.player.audience.ProfileAudienceArgument; import org.geysermc.floodgate.util.Constants; -import org.geysermc.floodgate.command.util.Permission; @NoArgsConstructor public final class LinkAccountCommand implements FloodgateCommand { @@ -61,7 +60,7 @@ public Command buildCommand(CommandManager commandMa ArgumentDescription.of("Link your Java account with your Bedrock account")) .senderType(PlayerAudience.class) .permission(Permission.COMMAND_LINK.get()) - .argument(UserAudienceArgument.of("player", true)) + .argument(ProfileAudienceArgument.of("player", true)) .argument(StringArgument.optional("code")) .handler(this::execute) .build(); @@ -125,7 +124,7 @@ public void execute(CommandContext context) { sender.disconnect(Message.LINK_REQUEST_COMPLETED, targetName); break; default: - sender.disconnect(Component.text("Invalid account linking result")); + sender.disconnect("Invalid account linking result"); break; } }); diff --git a/core/src/main/java/org/geysermc/floodgate/command/TestCommand.java b/core/src/main/java/org/geysermc/floodgate/command/TestCommand.java index c5a1de80..f950da04 100644 --- a/core/src/main/java/org/geysermc/floodgate/command/TestCommand.java +++ b/core/src/main/java/org/geysermc/floodgate/command/TestCommand.java @@ -28,7 +28,6 @@ import cloud.commandframework.Command; import cloud.commandframework.CommandManager; import cloud.commandframework.context.CommandContext; -import net.kyori.adventure.text.Component; import org.geysermc.floodgate.api.FloodgateApi; import org.geysermc.floodgate.config.FloodgateConfig; import org.geysermc.floodgate.platform.command.FloodgateCommand; @@ -47,7 +46,7 @@ public Command buildCommand(CommandManager commandMa @Override public void execute(CommandContext context) { int players = FloodgateApi.getInstance().getPlayers().size(); - context.getSender().sendMessage(Component.text(players)); + context.getSender().sendMessage(String.valueOf(players)); } @Override diff --git a/core/src/main/java/org/geysermc/floodgate/command/WhitelistCommand.java b/core/src/main/java/org/geysermc/floodgate/command/WhitelistCommand.java index 21e0bd88..8176c71d 100644 --- a/core/src/main/java/org/geysermc/floodgate/command/WhitelistCommand.java +++ b/core/src/main/java/org/geysermc/floodgate/command/WhitelistCommand.java @@ -38,17 +38,18 @@ import lombok.Getter; import org.geysermc.floodgate.api.FloodgateApi; import org.geysermc.floodgate.api.logger.FloodgateLogger; +import org.geysermc.floodgate.command.util.Permission; import org.geysermc.floodgate.config.FloodgateConfig; import org.geysermc.floodgate.config.ProxyFloodgateConfig; import org.geysermc.floodgate.platform.command.CommandUtil; import org.geysermc.floodgate.platform.command.FloodgateCommand; import org.geysermc.floodgate.platform.command.TranslatableMessage; import org.geysermc.floodgate.player.UserAudience; -import org.geysermc.floodgate.player.UserAudienceArgument; -import org.geysermc.floodgate.player.UserAudienceArgument.PlayerType; +import org.geysermc.floodgate.player.audience.ProfileAudience; +import org.geysermc.floodgate.player.audience.ProfileAudienceArgument; +import org.geysermc.floodgate.player.audience.ProfileAudienceArgument.PlayerType; import org.geysermc.floodgate.util.Constants; import org.geysermc.floodgate.util.HttpUtils; -import org.geysermc.floodgate.command.util.Permission; public class WhitelistCommand implements FloodgateCommand { @Inject private FloodgateConfig config; @@ -62,21 +63,21 @@ public Command buildCommand(CommandManager commandMa commandManager.command(builder .literal("add", "a") - .argument(UserAudienceArgument.of("player", true, true, PlayerType.ONLY_BEDROCK)) + .argument(ProfileAudienceArgument.of("player", true, true, PlayerType.ONLY_BEDROCK)) .handler(context -> performCommand(context, true))); return builder .literal("remove", "r") - .argument(UserAudienceArgument.of("player", true, true, PlayerType.ONLY_BEDROCK)) + .argument(ProfileAudienceArgument.of("player", true, true, PlayerType.ONLY_BEDROCK)) .handler(context -> performCommand(context, false)) .build(); } public void performCommand(CommandContext context, boolean add) { UserAudience sender = context.getSender(); - UserAudience player = context.get("player"); - UUID uuid = player.uuid(); - String name = player.username(); + ProfileAudience profile = context.get("player"); + UUID uuid = profile.uuid(); + String name = profile.username(); if (name == null && uuid == null) { sender.sendMessage(Message.UNEXPECTED_ERROR); diff --git a/core/src/main/java/org/geysermc/floodgate/platform/command/CommandUtil.java b/core/src/main/java/org/geysermc/floodgate/platform/command/CommandUtil.java index ad4c2764..d29f0f75 100644 --- a/core/src/main/java/org/geysermc/floodgate/platform/command/CommandUtil.java +++ b/core/src/main/java/org/geysermc/floodgate/platform/command/CommandUtil.java @@ -25,30 +25,98 @@ package org.geysermc.floodgate.platform.command; +import java.util.ArrayList; import java.util.Collection; +import java.util.List; +import java.util.Objects; import java.util.UUID; +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; +import org.geysermc.floodgate.api.FloodgateApi; import org.geysermc.floodgate.player.UserAudience; -import org.geysermc.floodgate.player.UserAudienceArgument.PlayerType; +import org.geysermc.floodgate.player.audience.ProfileAudience; +import org.geysermc.floodgate.player.audience.ProfileAudienceArgument.PlayerType; +import org.geysermc.floodgate.util.LanguageManager; import org.geysermc.floodgate.util.Utils; /** * An interface used across all Floodgate platforms to simple stuff in commands like kicking players * and sending player messages independent of the Floodgate platform implementation. */ -public interface CommandUtil { - @NonNull UserAudience getAudience(final @NonNull Object source); +@RequiredArgsConstructor(access = AccessLevel.PROTECTED) +public abstract class CommandUtil { + protected final LanguageManager manager; + protected final FloodgateApi api; - @Nullable UserAudience getAudienceByUuid(final @NonNull UUID uuid); + public abstract @NonNull UserAudience getUserAudience(@NonNull Object source); - @NonNull UserAudience getOfflineAudienceByUuid(final @NonNull UUID uuid); + /** + * Get a ProfileAudience from a source. The source should be a platform-specific player instance + * when the player is online, and the username / uuid of the requested player when offline. + * + * @param source source to create a ProfileAudience from + * @param allowOffline if offline players are allowed + * @return a ProfileAudience unless allowOffline is false and the player isn't online + */ + public @Nullable ProfileAudience getProfileAudience( + @NonNull Object source, + boolean allowOffline) { + Objects.requireNonNull(source); + + if (source instanceof UUID) { + return allowOffline ? new ProfileAudience((UUID) source, null) : null; + } else if (source instanceof String) { + return allowOffline ? new ProfileAudience(null, (String) source) : null; + } else { + return new ProfileAudience(getUuidFromSource(source), getUsernameFromSource(source)); + } + } - @Nullable UserAudience getAudienceByUsername(final @NonNull String username); + protected abstract String getUsernameFromSource(@NonNull Object source); + protected abstract UUID getUuidFromSource(@NonNull Object source); + + protected abstract Collection getOnlinePlayers(); + + public @NonNull Collection getOnlineUsernames(@NonNull PlayerType limitTo) { + Collection players = getOnlinePlayers(); + + Collection usernames = new ArrayList<>(); + switch (limitTo) { + case ALL_PLAYERS: + for (Object player : players) { + usernames.add(getUsernameFromSource(player)); + } + break; + case ONLY_JAVA: + for (Object player : players) { + if (!api.isFloodgatePlayer(getUuidFromSource(player))) { + usernames.add(getUsernameFromSource(player)); + } + } + break; + case ONLY_BEDROCK: + for (Object player : players) { + if (api.isFloodgatePlayer(getUuidFromSource(player))) { + usernames.add(getUsernameFromSource(player)); + } + } + break; + default: + throw new IllegalStateException("Unknown PlayerType"); + } + return usernames; + } - @NonNull UserAudience getOfflineAudienceByUsername(final @NonNull String username); + /** + * + * @param uuid + * @return + */ + public abstract Object getPlayerByUuid(@NonNull UUID uuid); - @NonNull Collection getOnlineUsernames(final @NonNull PlayerType limitTo); + public abstract Object getPlayerByUsername(@NonNull String username); /** * Checks if the given player has the given permission. @@ -57,7 +125,7 @@ public interface CommandUtil { * @param permission the permission to check * @return true or false depending on if the player has the permission */ - boolean hasPermission(Object player, String permission); + public abstract boolean hasPermission(Object player, String permission); /** * Get all online players with the given permission. @@ -65,17 +133,15 @@ public interface CommandUtil { * @param permission the permission to check * @return a list of online players that have the given permission */ - Collection getOnlinePlayersWithPermission(String permission); - - /** - * Send a message to the specified target, no matter what platform Floodgate is running on. - * - * @param target the player that should receive the message - * @param message the command message - * @param locale the locale of the player - * @param args the arguments - */ - void sendMessage(Object target, String locale, TranslatableMessage message, Object... args); + public Collection getOnlinePlayersWithPermission(String permission) { + List players = new ArrayList<>(); + for (Object player : getOnlinePlayers()) { + if (hasPermission(player, permission)) { + players.add(player); + } + } + return players; + } /** * Sends a raw message to the specified target, no matter what platform Floodgate is running @@ -84,18 +150,19 @@ public interface CommandUtil { * @param target the player that should receive the message * @param message the message */ - void sendMessage(Object target, String message); + public abstract void sendMessage(Object target, String message); /** - * Same as {@link CommandUtil#sendMessage(Object, String, TranslatableMessage, Object...)} - * except it kicks the player using the given message as the kick reason. + * Kicks the given player using the given message as the kick reason. * * @param player the player that should be kicked * @param message the command message - * @param locale the locale of the player - * @param args the arguments */ - void kickPlayer(Object player, String locale, TranslatableMessage message, Object... args); + public abstract void kickPlayer(Object player, String message); + + public String translateMessage(String locale, TranslatableMessage message, Object... args) { + return message.translateMessage(manager, locale, args); + } /** * Whitelist the given Bedrock player. @@ -105,7 +172,7 @@ public interface CommandUtil { * @return true if the player has been whitelisted, false if the player was already whitelisted. * Defaults to false when this platform doesn't support whitelisting. */ - default boolean whitelistPlayer(String xuid, String username) { + public boolean whitelistPlayer(String xuid, String username) { UUID uuid = Utils.getJavaUuid(xuid); return whitelistPlayer(uuid, username); } @@ -118,7 +185,7 @@ default boolean whitelistPlayer(String xuid, String username) { * @return true if the player has been whitelisted, false if the player was already whitelisted. * Defaults to false when this platform doesn't support whitelisting. */ - default boolean whitelistPlayer(UUID uuid, String username) { + public boolean whitelistPlayer(UUID uuid, String username) { return false; } @@ -130,7 +197,7 @@ default boolean whitelistPlayer(UUID uuid, String username) { * @return true if the player has been removed from the whitelist, false if the player wasn't * whitelisted. Defaults to false when this platform doesn't support whitelisting. */ - default boolean removePlayerFromWhitelist(String xuid, String username) { + public boolean removePlayerFromWhitelist(String xuid, String username) { UUID uuid = Utils.getJavaUuid(xuid); return removePlayerFromWhitelist(uuid, username); } @@ -143,7 +210,7 @@ default boolean removePlayerFromWhitelist(String xuid, String username) { * @return true if the player has been removed from the whitelist, false if the player wasn't * whitelisted. Defaults to false when this platform doesn't support whitelisting. */ - default boolean removePlayerFromWhitelist(UUID uuid, String username) { + public boolean removePlayerFromWhitelist(UUID uuid, String username) { return false; } } diff --git a/core/src/main/java/org/geysermc/floodgate/player/UserAudience.java b/core/src/main/java/org/geysermc/floodgate/player/UserAudience.java index 6a8d998b..9ed5e20d 100644 --- a/core/src/main/java/org/geysermc/floodgate/player/UserAudience.java +++ b/core/src/main/java/org/geysermc/floodgate/player/UserAudience.java @@ -25,52 +25,80 @@ package org.geysermc.floodgate.player; +import java.util.Objects; import java.util.UUID; -import net.kyori.adventure.audience.Audience; -import net.kyori.adventure.audience.MessageType; -import net.kyori.adventure.identity.Identified; -import net.kyori.adventure.identity.Identity; -import net.kyori.adventure.text.Component; +import lombok.Getter; +import lombok.experimental.Accessors; import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.floodgate.platform.command.CommandUtil; import org.geysermc.floodgate.platform.command.TranslatableMessage; -public interface UserAudience extends Identified, Identity, Audience { - @Override - @NonNull UUID uuid(); - - @NonNull String username(); - - @NonNull String locale(); - - @NonNull Object source(); - - boolean hasPermission(@NonNull final String permission); - - @Override - void sendMessage(final @NonNull Identity source, - final @NonNull Component message, - final @NonNull MessageType type); +@Getter @Accessors(fluent = true) +public class UserAudience { + private final @NonNull UUID uuid; + private final @NonNull String username; + private final @NonNull String locale; + private final @NonNull Object source; + private final @NonNull CommandUtil commandUtil; + + public UserAudience( + @NonNull UUID uuid, + @NonNull String username, + @NonNull String locale, + @NonNull Object source, + @NonNull CommandUtil commandUtil) { + this.uuid = Objects.requireNonNull(uuid); + this.username = username; + this.locale = Objects.requireNonNull(locale); + this.source = Objects.requireNonNull(source); + this.commandUtil = Objects.requireNonNull(commandUtil); + } - void sendMessage(TranslatableMessage message, Object... args); + public boolean hasPermission(@NonNull String permission) { + return commandUtil.hasPermission(source(), permission); + } - default void sendMessage(String message) { - sendMessage(Component.text(message)); + public void sendMessage(String message) { + commandUtil.sendMessage(source(), message); } - void disconnect(@NonNull final Component reason); + public void sendMessage(TranslatableMessage message, Object... args) { + sendMessage(translateMessage(message, args)); + } - void disconnect(TranslatableMessage message, Object... args); + public void disconnect(@NonNull String reason) { + commandUtil.kickPlayer(source(), reason); + } - @Override - default @NonNull Identity identity() { - return this; + public void disconnect(TranslatableMessage message, Object... args) { + disconnect(translateMessage(message, args)); } - interface PlayerAudience extends UserAudience { - boolean online(); + public String translateMessage(TranslatableMessage message, Object... args) { + return commandUtil.translateMessage(locale(), message, args); } - interface ConsoleAudience extends UserAudience { + @Getter @Accessors(fluent = true) + public static class PlayerAudience extends UserAudience { + private final boolean online; + + public PlayerAudience( + @NonNull UUID uuid, + @NonNull String username, + @NonNull String locale, + @NonNull Object source, + @NonNull CommandUtil commandUtil, + boolean online) { + super(uuid, username, locale, source, commandUtil); + + this.online = online; + } + } + @Getter @Accessors(fluent = true) + public static class ConsoleAudience extends UserAudience { + public ConsoleAudience(@NonNull Object source, @NonNull CommandUtil commandUtil) { + super(new UUID(0, 0), "CONSOLE", "en_us", source, commandUtil); + } } } diff --git a/core/src/main/java/org/geysermc/floodgate/player/ServerAudience.java b/core/src/main/java/org/geysermc/floodgate/player/audience/ProfileAudience.java similarity index 68% rename from core/src/main/java/org/geysermc/floodgate/player/ServerAudience.java rename to core/src/main/java/org/geysermc/floodgate/player/audience/ProfileAudience.java index 7e4f5521..299f8152 100644 --- a/core/src/main/java/org/geysermc/floodgate/player/ServerAudience.java +++ b/core/src/main/java/org/geysermc/floodgate/player/audience/ProfileAudience.java @@ -23,23 +23,20 @@ * @link https://github.com/GeyserMC/Floodgate */ -package org.geysermc.floodgate.player; +package org.geysermc.floodgate.player.audience; import java.util.UUID; -import net.kyori.adventure.audience.Audience; -import org.checkerframework.checker.index.qual.NonNegative; -import org.checkerframework.checker.nullness.qual.NonNull; +import lombok.Getter; +import lombok.experimental.Accessors; import org.checkerframework.checker.nullness.qual.Nullable; -import org.geysermc.floodgate.player.UserAudience.ConsoleAudience; -public interface ServerAudience extends Audience { - @NonNull Iterable onlineAudiences(); +@Getter @Accessors(fluent = true) +public final class ProfileAudience { + private final @Nullable UUID uuid; + private final @Nullable String username; - @NonNull ConsoleAudience consoleAudience(); - - @NonNegative int onlineCount(); - - @Nullable UserAudience audienceOf(final @NonNull UUID uuid); - - @Nullable UserAudience audienceOf(final @NonNull String username); + public ProfileAudience(@Nullable UUID uuid, @Nullable String username) { + this.uuid = uuid; + this.username = username; + } } diff --git a/core/src/main/java/org/geysermc/floodgate/player/UserAudienceArgument.java b/core/src/main/java/org/geysermc/floodgate/player/audience/ProfileAudienceArgument.java similarity index 69% rename from core/src/main/java/org/geysermc/floodgate/player/UserAudienceArgument.java rename to core/src/main/java/org/geysermc/floodgate/player/audience/ProfileAudienceArgument.java index 10d78a74..c1739f6e 100644 --- a/core/src/main/java/org/geysermc/floodgate/player/UserAudienceArgument.java +++ b/core/src/main/java/org/geysermc/floodgate/player/audience/ProfileAudienceArgument.java @@ -23,7 +23,7 @@ * @link https://github.com/GeyserMC/Floodgate */ -package org.geysermc.floodgate.player; +package org.geysermc.floodgate.player.audience; import cloud.commandframework.arguments.CommandArgument; import cloud.commandframework.arguments.parser.ArgumentParseResult; @@ -37,41 +37,42 @@ import lombok.RequiredArgsConstructor; import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.floodgate.platform.command.CommandUtil; +import org.geysermc.floodgate.player.UserAudience; -public final class UserAudienceArgument extends CommandArgument { - private UserAudienceArgument(final @NonNull String name, final UserAudienceParser parser) { - super(true, name, parser, UserAudience.class); +public class ProfileAudienceArgument extends CommandArgument { + private ProfileAudienceArgument(@NonNull String name, ProfileAudienceParser parser) { + super(true, name, parser, ProfileAudience.class); } - public static UserAudienceArgument of( - final String name, - final boolean allowUuid, - final boolean allowOffline, - final PlayerType limitTo) { - return new UserAudienceArgument(name, - new UserAudienceParser(allowUuid, allowOffline, limitTo)); + public static ProfileAudienceArgument of( + String name, + boolean allowUuid, + boolean allowOffline, + PlayerType limitTo) { + return new ProfileAudienceArgument(name, + new ProfileAudienceParser(allowUuid, allowOffline, limitTo)); } - public static UserAudienceArgument of( - final String name, - final boolean allowOffline, - final PlayerType limitTo) { + public static ProfileAudienceArgument of( + String name, + boolean allowOffline, + PlayerType limitTo) { return of(name, false, allowOffline, limitTo); } - public static UserAudienceArgument ofOnline(final String name, final PlayerType limitTo) { + public static ProfileAudienceArgument ofOnline(String name, PlayerType limitTo) { return of(name, false, false, limitTo); } - public static UserAudienceArgument ofOnline(final String name, final boolean allowUuid) { + public static ProfileAudienceArgument ofOnline(String name, boolean allowUuid) { return of(name, allowUuid, false, PlayerType.ALL_PLAYERS); } - public static CommandArgument ofOnline(final String name) { + public static CommandArgument ofOnline(String name) { return of(name, false, false, PlayerType.ALL_PLAYERS); } - public static UserAudienceArgument of(final String name, final boolean allowOffline) { + public static ProfileAudienceArgument of(String name, boolean allowOffline) { return of(name, false, allowOffline, PlayerType.ALL_PLAYERS); } @@ -82,17 +83,18 @@ public enum PlayerType { } @RequiredArgsConstructor - public static final class UserAudienceParser - implements ArgumentParser { + public static final class ProfileAudienceParser + implements ArgumentParser { private final boolean allowUuid; private final boolean allowOffline; private final PlayerType limitTo; @Override - public @NonNull ArgumentParseResult parse( - final @NonNull CommandContext<@NonNull UserAudience> commandContext, - final @NonNull Queue<@NonNull String> inputQueue) { + public @NonNull ArgumentParseResult parse( + @NonNull CommandContext<@NonNull UserAudience> commandContext, + @NonNull Queue<@NonNull String> inputQueue) { + //todo we don't use the limitTo in the getProfile methods CommandUtil commandUtil = commandContext.get("CommandUtil"); String input = inputQueue.poll(); @@ -129,7 +131,7 @@ public static final class UserAudienceParser } } - UserAudience userAudience; + ProfileAudience profileAudience; if (input.length() > 16) { // This must be a UUID. @@ -146,46 +148,39 @@ public static final class UserAudienceParser try { // We only want to make sure the UUID is valid here. - final UUID uuid = UUID.fromString(input); - userAudience = commandUtil.getAudienceByUuid(uuid); - - if (userAudience == null && allowOffline) { - userAudience = commandUtil.getOfflineAudienceByUuid(uuid); - } + Object player = commandUtil.getPlayerByUuid(UUID.fromString(input)); + profileAudience = commandUtil.getProfileAudience(player, allowOffline); } catch (final IllegalArgumentException ignored) { return ArgumentParseResult.failure( new InvalidPlayerIdentifierException("Invalid UUID '" + input + "'")); } } else { // This is a username. - userAudience = commandUtil.getAudienceByUsername(input); - - if (userAudience == null && allowOffline) { - userAudience = commandUtil.getOfflineAudienceByUsername(input); - } + Object player = commandUtil.getPlayerByUsername(input); + profileAudience = commandUtil.getProfileAudience(player, allowOffline); } - if (userAudience == null) { + if (profileAudience == null) { return ArgumentParseResult.failure( new InvalidPlayerIdentifierException("Invalid player '" + input + "'")); } - return ArgumentParseResult.success(userAudience); + return ArgumentParseResult.success(profileAudience); } @Override public @NonNull List suggestions( - final @NonNull CommandContext commandContext, - final @NonNull String input) { - final CommandUtil commandUtil = commandContext.get("CommandUtil"); - final String trimmedInput = input.trim(); + @NonNull CommandContext commandContext, + @NonNull String input) { + CommandUtil commandUtil = commandContext.get("CommandUtil"); + String trimmedInput = input.trim(); if (trimmedInput.isEmpty()) { return ImmutableList.copyOf(commandUtil.getOnlineUsernames(limitTo)); } - final String lowercaseInput = input.toLowerCase(Locale.ROOT); - final ImmutableList.Builder builder = ImmutableList.builder(); + String lowercaseInput = input.toLowerCase(Locale.ROOT); + ImmutableList.Builder builder = ImmutableList.builder(); for (final String player : commandUtil.getOnlineUsernames(limitTo)) { if (player.toLowerCase(Locale.ROOT).startsWith(lowercaseInput)) { @@ -205,7 +200,7 @@ public boolean isContextFree() { public static final class InvalidPlayerIdentifierException extends IllegalArgumentException { private static final long serialVersionUID = -6500019324607183855L; - public InvalidPlayerIdentifierException(final @NonNull String message) { + public InvalidPlayerIdentifierException(@NonNull String message) { super(message); } diff --git a/spigot/build.gradle.kts b/spigot/build.gradle.kts index d86ba36b..6f53213d 100644 --- a/spigot/build.gradle.kts +++ b/spigot/build.gradle.kts @@ -7,10 +7,7 @@ dependencies { // hack to make pre 1.12 work implementation("com.google.guava", "guava", guavaVersion) - implementation("cloud.commandframework", "cloud-bukkit", Versions.cloudVersion) - implementation("net.kyori", "adventure-text-serializer-legacy", Versions.adventureApiVersion) - implementation("net.kyori", "adventure-text-serializer-gson", Versions.adventureApiVersion) } relocate("com.google.inject") diff --git a/spigot/src/main/java/org/geysermc/floodgate/listener/SpigotListener.java b/spigot/src/main/java/org/geysermc/floodgate/listener/SpigotListener.java index d1337163..3574af44 100644 --- a/spigot/src/main/java/org/geysermc/floodgate/listener/SpigotListener.java +++ b/spigot/src/main/java/org/geysermc/floodgate/listener/SpigotListener.java @@ -35,9 +35,7 @@ import org.geysermc.floodgate.api.SimpleFloodgateApi; import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.api.player.FloodgatePlayer; -import org.geysermc.floodgate.player.FloodgatePlayerImpl; import org.geysermc.floodgate.util.LanguageManager; -import org.geysermc.floodgate.util.SpigotCommandUtil; public final class SpigotListener implements Listener { @Inject private SimpleFloodgateApi api; @@ -68,7 +66,5 @@ public void onPlayerLogin(PlayerLoginEvent event) { @EventHandler(priority = EventPriority.MONITOR) public void onPlayerQuit(PlayerQuitEvent event) { api.playerRemoved(event.getPlayer().getUniqueId()); - - SpigotCommandUtil.AUDIENCE_CACHE.remove(event.getPlayer().getUniqueId()); //todo } } diff --git a/spigot/src/main/java/org/geysermc/floodgate/module/SpigotCommandModule.java b/spigot/src/main/java/org/geysermc/floodgate/module/SpigotCommandModule.java index 66f17752..4db0c9f6 100644 --- a/spigot/src/main/java/org/geysermc/floodgate/module/SpigotCommandModule.java +++ b/spigot/src/main/java/org/geysermc/floodgate/module/SpigotCommandModule.java @@ -59,7 +59,7 @@ public CommandManager commandManager(CommandUtil commandUtil) { CommandManager commandManager = new BukkitCommandManager<>( plugin, CommandExecutionCoordinator.simpleCoordinator(), - commandUtil::getAudience, + commandUtil::getUserAudience, audience -> (CommandSender) audience.source() ); commandManager.registerCommandPreProcessor(new FloodgateCommandPreprocessor<>(commandUtil)); diff --git a/spigot/src/main/java/org/geysermc/floodgate/module/SpigotPlatformModule.java b/spigot/src/main/java/org/geysermc/floodgate/module/SpigotPlatformModule.java index 6ffff89a..94d8f12a 100644 --- a/spigot/src/main/java/org/geysermc/floodgate/module/SpigotPlatformModule.java +++ b/spigot/src/main/java/org/geysermc/floodgate/module/SpigotPlatformModule.java @@ -76,10 +76,9 @@ public FloodgateLogger floodgateLogger(LanguageManager languageManager) { public CommandUtil commandUtil( FloodgateApi api, SpigotVersionSpecificMethods versionSpecificMethods, - FloodgateLogger logger, LanguageManager languageManager) { - return new SpigotCommandUtil(plugin.getServer(), api, versionSpecificMethods, plugin, - logger, languageManager); + return new SpigotCommandUtil( + languageManager, plugin.getServer(), api, versionSpecificMethods, plugin); } @Provides diff --git a/spigot/src/main/java/org/geysermc/floodgate/util/SpigotCommandUtil.java b/spigot/src/main/java/org/geysermc/floodgate/util/SpigotCommandUtil.java index cc182d41..da3d65bc 100644 --- a/spigot/src/main/java/org/geysermc/floodgate/util/SpigotCommandUtil.java +++ b/spigot/src/main/java/org/geysermc/floodgate/util/SpigotCommandUtil.java @@ -25,44 +25,40 @@ package org.geysermc.floodgate.util; -import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; import java.util.UUID; -import lombok.RequiredArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.Server; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.plugin.java.JavaPlugin; import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.floodgate.api.FloodgateApi; -import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.platform.command.CommandUtil; -import org.geysermc.floodgate.platform.command.TranslatableMessage; import org.geysermc.floodgate.player.UserAudience; -import org.geysermc.floodgate.player.UserAudienceArgument.PlayerType; -import org.geysermc.floodgate.util.SpigotUserAudience.SpigotConsoleAudience; -import org.geysermc.floodgate.util.SpigotUserAudience.SpigotPlayerAudience; - -@RequiredArgsConstructor -public final class SpigotCommandUtil implements CommandUtil { - public static final @NonNull Map AUDIENCE_CACHE = new HashMap<>(); - private static UserAudience console; +import org.geysermc.floodgate.player.UserAudience.ConsoleAudience; +import org.geysermc.floodgate.player.UserAudience.PlayerAudience; +public final class SpigotCommandUtil extends CommandUtil { private final Server server; - private final FloodgateApi api; private final SpigotVersionSpecificMethods versionSpecificMethods; - private final JavaPlugin plugin; - private final FloodgateLogger logger; - private final LanguageManager manager; + private UserAudience console; + + public SpigotCommandUtil( + LanguageManager manager, + Server server, + FloodgateApi api, + SpigotVersionSpecificMethods versionSpecificMethods, + JavaPlugin plugin) { + super(manager, api); + this.server = server; + this.versionSpecificMethods = versionSpecificMethods; + this.plugin = plugin; + } @Override - public @NonNull UserAudience getAudience(final @NonNull Object sourceObj) { + public @NonNull UserAudience getUserAudience(final @NonNull Object sourceObj) { if (!(sourceObj instanceof CommandSender)) { throw new IllegalArgumentException("Source has to be a CommandSender!"); } @@ -72,89 +68,47 @@ public final class SpigotCommandUtil implements CommandUtil { if (console != null) { return console; } - return console = new SpigotConsoleAudience(source, this); + return console = new ConsoleAudience(source, this); } Player player = (Player) source; UUID uuid = player.getUniqueId(); + String username = player.getName(); String locale = versionSpecificMethods.getLocale(player); - return AUDIENCE_CACHE.computeIfAbsent(uuid, - $ -> new SpigotPlayerAudience(uuid, locale, source, true, this)); + return new PlayerAudience(uuid, username, locale, source,this, true); } @Override - public @Nullable UserAudience getAudienceByUsername(@NonNull String username) { - Player player = server.getPlayer(username); - return player != null ? getAudience(player) : null; + protected String getUsernameFromSource(@NonNull Object source) { + return ((Player) source).getName(); } @Override - public @NonNull UserAudience getOfflineAudienceByUsername(@NonNull String username) { - return new SpigotPlayerAudience(null, username, null, null, false, this); + protected UUID getUuidFromSource(@NonNull Object source) { + return ((Player) source).getUniqueId(); } @Override - public @Nullable UserAudience getAudienceByUuid(@NonNull UUID uuid) { - Player player = server.getPlayer(uuid); - return player != null ? getAudience(player) : null; + protected Collection getOnlinePlayers() { + return server.getOnlinePlayers(); } @Override - public @NonNull UserAudience getOfflineAudienceByUuid(@NonNull UUID uuid) { - return new SpigotPlayerAudience(uuid, null, null, null, false, this); + public Object getPlayerByUuid(@NonNull UUID uuid) { + Player player = server.getPlayer(uuid); + return player != null ? player : uuid; } @Override - public @NonNull Collection getOnlineUsernames(@NonNull PlayerType limitTo) { - Collection players = server.getOnlinePlayers(); - - Collection usernames = new ArrayList<>(); - switch (limitTo) { - case ALL_PLAYERS: - for (Player player : players) { - usernames.add(player.getName()); - } - break; - case ONLY_JAVA: - for (Player player : players) { - if (!api.isFloodgatePlayer(player.getUniqueId())) { - usernames.add(player.getName()); - } - } - break; - case ONLY_BEDROCK: - for (Player player : players) { - if (api.isFloodgatePlayer(player.getUniqueId())) { - usernames.add(player.getName()); - } - } - break; - default: - throw new IllegalStateException("Unknown PlayerType"); - } - return usernames; + public Object getPlayerByUsername(@NonNull String username) { + Player player = server.getPlayer(username); + return player != null ? player : username; } @Override public boolean hasPermission(Object player, String permission) { - return cast(player).hasPermission(permission); - } - - @Override - public Collection getOnlinePlayersWithPermission(String permission) { - List players = new ArrayList<>(); - for (Player player : Bukkit.getOnlinePlayers()) { - if (hasPermission(player, permission)) { - players.add(player); - } - } - return players; - } - - @Override - public void sendMessage(Object target, String locale, TranslatableMessage message, Object... args) { - sendMessage(target, translateAndTransform(locale, message, args)); + return ((CommandSender) player).hasPermission(permission); } @Override @@ -163,10 +117,11 @@ public void sendMessage(Object target, String message) { } @Override - public void kickPlayer(Object player, String locale, TranslatableMessage message, Object... args) { - // Have to run this in the main thread so we don't get a `Asynchronous player kick!` error - Bukkit.getScheduler().runTask(plugin, - () -> cast(player).kickPlayer(translateAndTransform(locale, message, args))); + public void kickPlayer(Object player, String message) { + // can also be console + if (player instanceof Player) { + Bukkit.getScheduler().runTask(plugin, () -> ((Player) player).kickPlayer(message)); + } } @Override @@ -178,18 +133,4 @@ public boolean whitelistPlayer(UUID uuid, String username) { public boolean removePlayerFromWhitelist(UUID uuid, String username) { return WhitelistUtils.removePlayer(uuid, username); } - - public String translateAndTransform(String locale, TranslatableMessage message, Object... args) { - // unlike others, Bukkit doesn't have to transform a message into another class. - return message.translateMessage(manager, locale, args); - } - - private Player cast(Object instance) { - try { - return (Player) instance; - } catch (ClassCastException exception) { - logger.error("Failed to cast {} to Player", instance.getClass().getName()); - throw exception; - } - } } diff --git a/spigot/src/main/java/org/geysermc/floodgate/util/SpigotUserAudience.java b/spigot/src/main/java/org/geysermc/floodgate/util/SpigotUserAudience.java deleted file mode 100644 index de2ec182..00000000 --- a/spigot/src/main/java/org/geysermc/floodgate/util/SpigotUserAudience.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Floodgate - */ - -package org.geysermc.floodgate.util; - -import java.util.UUID; -import lombok.RequiredArgsConstructor; -import net.kyori.adventure.audience.Audience; -import net.kyori.adventure.audience.ForwardingAudience; -import net.kyori.adventure.audience.MessageType; -import net.kyori.adventure.identity.Identity; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; -import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.geysermc.floodgate.platform.command.CommandUtil; -import org.geysermc.floodgate.platform.command.TranslatableMessage; -import org.geysermc.floodgate.player.UserAudience; - -@RequiredArgsConstructor -public class SpigotUserAudience implements UserAudience, ForwardingAudience.Single { - private final UUID uuid; - private final String locale; - private final CommandSender source; - private final CommandUtil commandUtil; - - @Override - public @NonNull UUID uuid() { - return uuid; - } - - @Override - public @NonNull String username() { - return source.getName(); - } - - @Override - public @NonNull String locale() { - return locale; - } - - @Override - public @NonNull CommandSender source() { - return source; - } - - @Override - public boolean hasPermission(@NonNull String permission) { - return source.hasPermission(permission); - } - - @Override - public void sendMessage( - @NonNull Identity source, - @NonNull Component message, - @NonNull MessageType type) { - this.source.sendMessage(GsonComponentSerializer.gson().serialize(message)); - } - - @Override - public void sendMessage(TranslatableMessage message, Object... args) { - commandUtil.sendMessage(source(), locale(), message, args); - } - - @Override - public void disconnect(@NonNull Component reason) { - if (source instanceof Player) { - ((Player) source).kickPlayer(GsonComponentSerializer.gson().serialize(reason)); - } - } - - @Override - public void disconnect(TranslatableMessage message, Object... args) { - commandUtil.kickPlayer(source(), locale(), message, args); - } - - @Override - public @NonNull Audience audience() { - return this; - } - - public static final class SpigotConsoleAudience extends SpigotUserAudience - implements ConsoleAudience { - public SpigotConsoleAudience(CommandSender source, CommandUtil commandUtil) { - super(new UUID(0, 0), "en_us", source, commandUtil); - } - - @Override - public void sendMessage( - @NonNull Identity source, - @NonNull Component message, - @NonNull MessageType type) { - source().sendMessage(LegacyComponentSerializer.legacySection().serialize(message)); - } - } - - public static final class SpigotPlayerAudience extends SpigotUserAudience - implements PlayerAudience { - - private final String username; - private final boolean online; - - public SpigotPlayerAudience( - UUID uuid, - String username, - String locale, - CommandSender source, - boolean online, - CommandUtil commandUtil) { - super(uuid, locale, source, commandUtil); - this.username = username; - this.online = online; - } - - public SpigotPlayerAudience( - UUID uuid, - String locale, - CommandSender source, - boolean online, - CommandUtil commandUtil) { - this(uuid, source.getName(), locale, source, online, commandUtil); - } - - @Override - public @NonNull String username() { - return username; - } - - @Override - public boolean online() { - return online; - } - } -} diff --git a/velocity/build.gradle.kts b/velocity/build.gradle.kts index 086e2ccc..5fa61663 100644 --- a/velocity/build.gradle.kts +++ b/velocity/build.gradle.kts @@ -14,7 +14,6 @@ relocate("io.leangen.geantyref") // these dependencies are already present on the platform -provided("net.kyori", "adventure-api", Versions.adventureApiVersion, 0b100) provided("com.google.code.gson", "gson", gsonVersion) provided("com.google.guava", "guava", guavaVersion) provided("com.google.inject", "guice", Versions.guiceVersion) diff --git a/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityListener.java b/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityListener.java index 9f2bec92..71741b98 100644 --- a/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityListener.java +++ b/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityListener.java @@ -57,7 +57,6 @@ import org.geysermc.floodgate.api.player.FloodgatePlayer; import org.geysermc.floodgate.config.ProxyFloodgateConfig; import org.geysermc.floodgate.util.LanguageManager; -import org.geysermc.floodgate.util.VelocityCommandUtil; public final class VelocityListener { private static final Field INITIAL_MINECRAFT_CONNECTION; @@ -168,7 +167,5 @@ public void onLogin(LoginEvent event) { @Subscribe(order = PostOrder.LAST) public void onDisconnect(DisconnectEvent event) { api.playerRemoved(event.getPlayer().getUniqueId()); - - VelocityCommandUtil.AUDIENCE_CACHE.remove(event.getPlayer().getUniqueId()); //todo } } diff --git a/velocity/src/main/java/org/geysermc/floodgate/module/VelocityPlatformModule.java b/velocity/src/main/java/org/geysermc/floodgate/module/VelocityPlatformModule.java index 58f694db..209c07c6 100644 --- a/velocity/src/main/java/org/geysermc/floodgate/module/VelocityPlatformModule.java +++ b/velocity/src/main/java/org/geysermc/floodgate/module/VelocityPlatformModule.java @@ -66,25 +66,24 @@ public final class VelocityPlatformModule extends AbstractModule { @Override protected void configure() { - VelocityCommandUtil commandUtil = new VelocityCommandUtil(); - requestInjection(commandUtil); - bind(CommandUtil.class).to(VelocityCommandUtil.class); - bind(VelocityCommandUtil.class).toInstance(commandUtil); + } + @Provides + @Singleton + public CommandManager commandManager(CommandUtil commandUtil) { Injector child = guice.createChildInjector(new CloudInjectionModule<>( UserAudience.class, CommandExecutionCoordinator.simpleCoordinator(), - commandUtil::getAudience, + commandUtil::getUserAudience, audience -> (CommandSource) audience.source() )); CommandManager commandManager = child.getInstance(new Key>() {}); - bind(new Key>() {}).toInstance(commandManager); - commandManager.registerCommandPreProcessor(new FloodgateCommandPreprocessor<>(commandUtil)); + return commandManager; } @Provides @@ -99,8 +98,9 @@ public FloodgateLogger floodgateLogger(Logger logger, LanguageManager languageMa @Provides @Singleton - public ListenerRegistration listenerRegistration(EventManager eventManager, - VelocityPlugin plugin) { + public ListenerRegistration listenerRegistration( + EventManager eventManager, + VelocityPlugin plugin) { return new VelocityListenerRegistration(eventManager, plugin); } diff --git a/velocity/src/main/java/org/geysermc/floodgate/player/VelocityUserAudience.java b/velocity/src/main/java/org/geysermc/floodgate/player/VelocityUserAudience.java deleted file mode 100644 index 46c55dc9..00000000 --- a/velocity/src/main/java/org/geysermc/floodgate/player/VelocityUserAudience.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Floodgate - */ - -package org.geysermc.floodgate.player; - -import com.velocitypowered.api.command.CommandSource; -import com.velocitypowered.api.proxy.Player; -import java.util.UUID; -import lombok.RequiredArgsConstructor; -import net.kyori.adventure.audience.Audience; -import net.kyori.adventure.audience.ForwardingAudience; -import net.kyori.adventure.audience.MessageType; -import net.kyori.adventure.identity.Identity; -import net.kyori.adventure.text.Component; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.geysermc.floodgate.platform.command.CommandUtil; -import org.geysermc.floodgate.platform.command.TranslatableMessage; - -@RequiredArgsConstructor -public abstract class VelocityUserAudience implements UserAudience, ForwardingAudience.Single { - private final UUID uuid; - private final String username; - private final String locale; - private final CommandSource source; - private final CommandUtil commandUtil; - - @Override - public @NonNull UUID uuid() { - return uuid; - } - - @Override - public @NonNull String username() { - return username; - } - - @Override - public @NonNull String locale() { - return locale; - } - - @Override - public @NonNull CommandSource source() { - return source; - } - - @Override - public boolean hasPermission(@NonNull String permission) { - return source.hasPermission(permission); - } - - @Override - public void sendMessage( - @NonNull Identity source, - @NonNull Component message, - @NonNull MessageType type) { - // apparently the console doesn't implement sendMessage with MessageType, - // so we'll just ignore it - this.source.sendMessage(source, message); - } - - @Override - public void sendMessage(TranslatableMessage message, Object... args) { - commandUtil.sendMessage(source(), locale(), message, args); - } - - @Override - public void disconnect(@NonNull Component reason) { - if (source instanceof Player) { - ((Player) source).disconnect(reason); - } - } - - @Override - public void disconnect(TranslatableMessage message, Object... args) { - commandUtil.kickPlayer(source(), locale(), message, args); - } - - @Override - public @NonNull Audience audience() { - return source; - } - - public static final class VelocityConsoleAudience extends VelocityUserAudience - implements ConsoleAudience { - - public VelocityConsoleAudience(CommandSource source, CommandUtil commandUtil) { - super(new UUID(0, 0), "CONSOLE", "en_us", source, commandUtil); - } - } - - public static final class VelocityPlayerAudience extends VelocityUserAudience - implements PlayerAudience { - private final boolean online; - - public VelocityPlayerAudience( - UUID uuid, - String username, - String locale, - CommandSource source, - boolean online, - CommandUtil commandUtil) { - super(uuid, username, locale, source, commandUtil); - this.online = online; - } - - @Override - public boolean online() { - return online; - } - } -} diff --git a/velocity/src/main/java/org/geysermc/floodgate/util/VelocityCommandUtil.java b/velocity/src/main/java/org/geysermc/floodgate/util/VelocityCommandUtil.java index e399d607..8edf09da 100644 --- a/velocity/src/main/java/org/geysermc/floodgate/util/VelocityCommandUtil.java +++ b/velocity/src/main/java/org/geysermc/floodgate/util/VelocityCommandUtil.java @@ -29,35 +29,29 @@ import com.velocitypowered.api.command.CommandSource; import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.ProxyServer; -import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.Optional; import java.util.UUID; import net.kyori.adventure.text.Component; import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.floodgate.api.FloodgateApi; -import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.platform.command.CommandUtil; -import org.geysermc.floodgate.platform.command.TranslatableMessage; import org.geysermc.floodgate.player.UserAudience; -import org.geysermc.floodgate.player.UserAudienceArgument.PlayerType; -import org.geysermc.floodgate.player.VelocityUserAudience.VelocityConsoleAudience; -import org.geysermc.floodgate.player.VelocityUserAudience.VelocityPlayerAudience; +import org.geysermc.floodgate.player.UserAudience.ConsoleAudience; +import org.geysermc.floodgate.player.UserAudience.PlayerAudience; -public final class VelocityCommandUtil implements CommandUtil { - public static final @NonNull Map AUDIENCE_CACHE = new HashMap<>(); +public final class VelocityCommandUtil extends CommandUtil { private static UserAudience console; @Inject private ProxyServer server; - @Inject private FloodgateApi api; - @Inject private FloodgateLogger logger; - @Inject private LanguageManager manager; + + @Inject + public VelocityCommandUtil(LanguageManager manager, FloodgateApi api) { + super(manager, api); + } @Override - public @NonNull UserAudience getAudience(@NonNull Object sourceObj) { + public @NonNull UserAudience getUserAudience(@NonNull Object sourceObj) { if (!(sourceObj instanceof CommandSource)) { throw new IllegalArgumentException("Can only work with CommandSource!"); } @@ -67,7 +61,7 @@ public final class VelocityCommandUtil implements CommandUtil { if (console != null) { return console; } - return console = new VelocityConsoleAudience(source, this); + return console = new ConsoleAudience(source, this); } Player player = (Player) source; @@ -75,84 +69,39 @@ public final class VelocityCommandUtil implements CommandUtil { String username = player.getUsername(); String locale = Utils.getLocale(player.getPlayerSettings().getLocale()); - return AUDIENCE_CACHE.computeIfAbsent(uuid, - $ -> new VelocityPlayerAudience(uuid, username, locale, source, true, this)); + return new PlayerAudience(uuid, username, locale, source, this, true); } @Override - public @Nullable UserAudience getAudienceByUsername(@NonNull String username) { - return server.getPlayer(username) - .map(this::getAudience) - .orElse(null); + protected String getUsernameFromSource(@NonNull Object source) { + return ((Player) source).getUsername(); } @Override - public @NonNull UserAudience getOfflineAudienceByUsername(@NonNull String username) { - return new VelocityPlayerAudience(null, username, null, null, false, this); + protected UUID getUuidFromSource(@NonNull Object source) { + return ((Player) source).getUniqueId(); } @Override - public @Nullable UserAudience getAudienceByUuid(@NonNull UUID uuid) { - return server.getPlayer(uuid) - .map(this::getAudience) - .orElse(null); + protected Collection getOnlinePlayers() { + return server.getAllPlayers(); } @Override - public @NonNull UserAudience getOfflineAudienceByUuid(@NonNull UUID uuid) { - return new VelocityPlayerAudience(uuid, null, null, null, false, this); + public Object getPlayerByUuid(@NonNull UUID uuid) { + Optional player = server.getPlayer(uuid); + return player.isPresent() ? player : uuid; } @Override - public @NonNull Collection getOnlineUsernames(@NonNull PlayerType limitTo) { - Collection players = server.getAllPlayers(); - - Collection usernames = new ArrayList<>(); - switch (limitTo) { - case ALL_PLAYERS: - for (Player player : players) { - usernames.add(player.getUsername()); - } - break; - case ONLY_JAVA: - for (Player player : players) { - if (!api.isFloodgatePlayer(player.getUniqueId())) { - usernames.add(player.getUsername()); - } - } - break; - case ONLY_BEDROCK: - for (Player player : players) { - if (api.isFloodgatePlayer(player.getUniqueId())) { - usernames.add(player.getUsername()); - } - } - break; - default: - throw new IllegalStateException("Unknown PlayerType"); - } - return usernames; + public Object getPlayerByUsername(@NonNull String username) { + Optional player = server.getPlayer(username); + return player.isPresent() ? player : username; } @Override public boolean hasPermission(Object player, String permission) { - return cast(player).hasPermission(permission); - } - - @Override - public Collection getOnlinePlayersWithPermission(String permission) { - List players = new ArrayList<>(); - for (Player player : server.getAllPlayers()) { - if (hasPermission(player, permission)) { - players.add(player); - } - } - return players; - } - - @Override - public void sendMessage(Object target, String locale, TranslatableMessage message, Object... args) { - ((CommandSource) target).sendMessage(translateAndTransform(locale, message, args)); + return ((CommandSource) player).hasPermission(permission); } @Override @@ -161,23 +110,9 @@ public void sendMessage(Object target, String message) { } @Override - public void kickPlayer(Object player, String locale, TranslatableMessage message, Object... args) { - cast(player).disconnect(translateAndTransform(locale, message, args)); - } - - public Component translateAndTransform( - String locale, - TranslatableMessage message, - Object... args) { - return Component.text(message.translateMessage(manager, locale, args)); - } - - protected Player cast(Object instance) { - try { - return (Player) instance; - } catch (ClassCastException exception) { - logger.error("Failed to cast {} to Player", instance.getClass().getName()); - throw exception; + public void kickPlayer(Object player, String message) { + if (player instanceof Player) { + ((Player) player).disconnect(Component.text(message)); } } }