1
0
mirror of https://github.com/GeyserMC/Floodgate.git synced 2025-12-19 14:59:20 +00:00

No longer use Adventure internally. Started working on adding Checkstyle

This commit is contained in:
Tim203
2022-03-05 20:02:06 +01:00
parent 0e055d106d
commit 0e1e1b823c
27 changed files with 711 additions and 921 deletions

View File

@@ -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"

View File

@@ -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)) {

View File

@@ -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")

View File

@@ -49,7 +49,6 @@ import org.geysermc.floodgate.api.player.FloodgatePlayer;
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 final class BungeeListener implements Listener {
@EventHandler(priority = EventPriority.HIGHEST)
public void onPlayerDisconnect(PlayerDisconnectEvent event) {
api.playerRemoved(event.getPlayer().getUniqueId());
BungeeCommandUtil.AUDIENCE_CACHE.remove(event.getPlayer().getUniqueId()); //todo
}
}

View File

@@ -83,7 +83,7 @@ public final class BungeePlatformModule extends AbstractModule {
CommandManager<UserAudience> commandManager = new BungeeCommandManager<>(
plugin,
CommandExecutionCoordinator.simpleCoordinator(),
commandUtil::getAudience,
commandUtil::getUserAudience,
audience -> (CommandSender) audience.source()
);
commandManager.registerCommandPreProcessor(new FloodgateCommandPreprocessor<>(commandUtil));
@@ -92,11 +92,8 @@ public final class BungeePlatformModule extends AbstractModule {
@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

View File

@@ -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<UUID, UserAudience> 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) {
protected Collection<?> getOnlinePlayers() {
return server.getPlayers();
}
@Override
public Object getPlayerByUuid(@NonNull UUID uuid) {
ProxiedPlayer player = server.getPlayer(uuid);
return player != null ? getAudience(player) : null;
return player != null ? player : uuid;
}
@Override
public @NonNull UserAudience getOfflineAudienceByUuid(@NonNull UUID uuid) {
return new BungeePlayerAudience(uuid, null, null, null, false, this);
}
@Override
public @NonNull Collection<String> getOnlineUsernames(@NonNull PlayerType limitTo) {
Collection<ProxiedPlayer> players = server.getPlayers();
Collection<String> 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<Object> getOnlinePlayersWithPermission(String permission) {
List<Object> 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 final class BungeeCommandUtil implements CommandUtil {
}
@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);
}
}
}

View File

@@ -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;
}
}
}

364
checkstyle.xml Normal file
View File

@@ -0,0 +1,364 @@
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
"https://checkstyle.org/dtds/configuration_1_3.dtd">
<!--
Checkstyle configuration that checks the Google coding conventions from Google Java Style
that can be found at https://google.github.io/styleguide/javaguide.html
Checkstyle is very configurable. Be sure to read the documentation at
http://checkstyle.org (or in your downloaded distribution).
To completely disable a check, just comment it out or delete it from the file.
To suppress certain violations please review suppression filters.
Authors: Max Vetrenko, Ruslan Diachenko, Roman Ivanov.
-->
<module name = "Checker">
<property name="charset" value="UTF-8"/>
<property name="severity" value="warning"/>
<property name="fileExtensions" value="java, properties, xml"/>
<!-- Excludes all 'module-info.java' files -->
<!-- See https://checkstyle.org/config_filefilters.html -->
<module name="BeforeExecutionExclusionFileFilter">
<property name="fileNamePattern" value="module\-info\.java$"/>
</module>
<!-- https://checkstyle.org/config_filters.html#SuppressionFilter -->
<module name="SuppressionFilter">
<property name="file" value="${org.checkstyle.google.suppressionfilter.config}"
default="checkstyle-suppressions.xml" />
<property name="optional" value="true"/>
</module>
<!-- Checks for whitespace -->
<!-- See http://checkstyle.org/config_whitespace.html -->
<module name="FileTabCharacter">
<property name="eachLine" value="true"/>
</module>
<module name="LineLength">
<property name="fileExtensions" value="java"/>
<property name="max" value="100"/>
<property name="ignorePattern" value="^package.*|^import.*|a href|href|http://|https://|ftp://"/>
</module>
<module name="TreeWalker">
<module name="OuterTypeFilename"/>
<module name="IllegalTokenText">
<property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/>
<property name="format"
value="\\u00(09|0(a|A)|0(c|C)|0(d|D)|22|27|5(C|c))|\\(0(10|11|12|14|15|42|47)|134)"/>
<property name="message"
value="Consider using special escape sequence instead of octal value or Unicode escaped value."/>
</module>
<module name="AvoidEscapedUnicodeCharacters">
<property name="allowEscapesForControlCharacters" value="true"/>
<property name="allowByTailComment" value="true"/>
<property name="allowNonPrintableEscapes" value="true"/>
</module>
<module name="AvoidStarImport"/>
<module name="OneTopLevelClass"/>
<module name="NoLineWrap">
<property name="tokens" value="PACKAGE_DEF, IMPORT, STATIC_IMPORT"/>
</module>
<module name="EmptyBlock">
<property name="option" value="TEXT"/>
<property name="tokens"
value="LITERAL_TRY, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH"/>
</module>
<module name="NeedBraces">
<property name="tokens"
value="LITERAL_DO, LITERAL_ELSE, LITERAL_FOR, LITERAL_IF, LITERAL_WHILE"/>
</module>
<module name="LeftCurly">
<property name="tokens"
value="ANNOTATION_DEF, CLASS_DEF, CTOR_DEF, ENUM_CONSTANT_DEF, ENUM_DEF,
INTERFACE_DEF, LAMBDA, LITERAL_CASE, LITERAL_CATCH, LITERAL_DEFAULT,
LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY, LITERAL_FOR, LITERAL_IF,
LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE, METHOD_DEF,
OBJBLOCK, STATIC_INIT, RECORD_DEF, COMPACT_CTOR_DEF"/>
</module>
<module name="RightCurly">
<property name="id" value="RightCurlySame"/>
<property name="tokens"
value="LITERAL_TRY, LITERAL_CATCH, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE,
LITERAL_DO"/>
</module>
<module name="RightCurly">
<property name="id" value="RightCurlyAlone"/>
<property name="option" value="alone"/>
<property name="tokens"
value="CLASS_DEF, METHOD_DEF, CTOR_DEF, LITERAL_FOR, LITERAL_WHILE, STATIC_INIT,
INSTANCE_INIT, ANNOTATION_DEF, ENUM_DEF, INTERFACE_DEF, RECORD_DEF,
COMPACT_CTOR_DEF"/>
</module>
<module name="SuppressionXpathSingleFilter">
<!-- suppresion is required till https://github.com/checkstyle/checkstyle/issues/7541 -->
<property name="id" value="RightCurlyAlone"/>
<property name="query" value="//RCURLY[parent::SLIST[count(./*)=1]
or preceding-sibling::*[last()][self::LCURLY]]"/>
</module>
<module name="WhitespaceAfter">
<property name="tokens"
value="COMMA, SEMI, TYPECAST, LITERAL_IF, LITERAL_ELSE,
LITERAL_WHILE, LITERAL_DO, LITERAL_FOR, DO_WHILE"/>
</module>
<module name="WhitespaceAround">
<property name="allowEmptyConstructors" value="true"/>
<property name="allowEmptyLambdas" value="true"/>
<property name="allowEmptyMethods" value="true"/>
<property name="allowEmptyTypes" value="true"/>
<property name="allowEmptyLoops" value="true"/>
<property name="ignoreEnhancedForColon" value="false"/>
<property name="tokens"
value="ASSIGN, BAND, BAND_ASSIGN, BOR, BOR_ASSIGN, BSR, BSR_ASSIGN, BXOR,
BXOR_ASSIGN, COLON, DIV, DIV_ASSIGN, DO_WHILE, EQUAL, GE, GT, LAMBDA, LAND,
LCURLY, LE, LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY,
LITERAL_FOR, LITERAL_IF, LITERAL_RETURN, LITERAL_SWITCH, LITERAL_SYNCHRONIZED,
LITERAL_TRY, LITERAL_WHILE, LOR, LT, MINUS, MINUS_ASSIGN, MOD, MOD_ASSIGN,
NOT_EQUAL, PLUS, PLUS_ASSIGN, QUESTION, RCURLY, SL, SLIST, SL_ASSIGN, SR,
SR_ASSIGN, STAR, STAR_ASSIGN, LITERAL_ASSERT, TYPE_EXTENSION_AND"/>
<message key="ws.notFollowed"
value="WhitespaceAround: ''{0}'' is not followed by whitespace. Empty blocks may only be represented as '{}' when not part of a multi-block statement (4.1.3)"/>
<message key="ws.notPreceded"
value="WhitespaceAround: ''{0}'' is not preceded with whitespace."/>
</module>
<module name="OneStatementPerLine"/>
<module name="MultipleVariableDeclarations"/>
<module name="ArrayTypeStyle"/>
<module name="MissingSwitchDefault"/>
<module name="FallThrough"/>
<module name="UpperEll"/>
<module name="ModifierOrder"/>
<module name="EmptyLineSeparator">
<property name="tokens"
value="PACKAGE_DEF, IMPORT, STATIC_IMPORT, CLASS_DEF, INTERFACE_DEF, ENUM_DEF,
STATIC_INIT, INSTANCE_INIT, METHOD_DEF, CTOR_DEF, VARIABLE_DEF, RECORD_DEF,
COMPACT_CTOR_DEF"/>
<property name="allowNoEmptyLineBetweenFields" value="true"/>
</module>
<module name="SeparatorWrap">
<property name="id" value="SeparatorWrapDot"/>
<property name="tokens" value="DOT"/>
<property name="option" value="nl"/>
</module>
<module name="SeparatorWrap">
<property name="id" value="SeparatorWrapComma"/>
<property name="tokens" value="COMMA"/>
<property name="option" value="EOL"/>
</module>
<module name="SeparatorWrap">
<!-- ELLIPSIS is EOL until https://github.com/google/styleguide/issues/259 -->
<property name="id" value="SeparatorWrapEllipsis"/>
<property name="tokens" value="ELLIPSIS"/>
<property name="option" value="EOL"/>
</module>
<module name="SeparatorWrap">
<!-- ARRAY_DECLARATOR is EOL until https://github.com/google/styleguide/issues/258 -->
<property name="id" value="SeparatorWrapArrayDeclarator"/>
<property name="tokens" value="ARRAY_DECLARATOR"/>
<property name="option" value="EOL"/>
</module>
<module name="SeparatorWrap">
<property name="id" value="SeparatorWrapMethodRef"/>
<property name="tokens" value="METHOD_REF"/>
<property name="option" value="nl"/>
</module>
<module name="PackageName">
<property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/>
<message key="name.invalidPattern"
value="Package name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="TypeName">
<property name="tokens" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF,
ANNOTATION_DEF, RECORD_DEF"/>
<message key="name.invalidPattern"
value="Type name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="MemberName">
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/>
<message key="name.invalidPattern"
value="Member name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="ParameterName">
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
<message key="name.invalidPattern"
value="Parameter name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="LambdaParameterName">
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
<message key="name.invalidPattern"
value="Lambda parameter name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="CatchParameterName">
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
<message key="name.invalidPattern"
value="Catch parameter name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="LocalVariableName">
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
<message key="name.invalidPattern"
value="Local variable name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="PatternVariableName">
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
<message key="name.invalidPattern"
value="Pattern variable name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="ClassTypeParameterName">
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
<message key="name.invalidPattern"
value="Class type name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="RecordComponentName">
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
<message key="name.invalidPattern"
value="Record component name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="RecordTypeParameterName">
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
<message key="name.invalidPattern"
value="Record type name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="MethodTypeParameterName">
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
<message key="name.invalidPattern"
value="Method type name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="InterfaceTypeParameterName">
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
<message key="name.invalidPattern"
value="Interface type name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="NoFinalizer"/>
<module name="GenericWhitespace">
<message key="ws.followed"
value="GenericWhitespace ''{0}'' is followed by whitespace."/>
<message key="ws.preceded"
value="GenericWhitespace ''{0}'' is preceded with whitespace."/>
<message key="ws.illegalFollow"
value="GenericWhitespace ''{0}'' should followed by whitespace."/>
<message key="ws.notPreceded"
value="GenericWhitespace ''{0}'' is not preceded with whitespace."/>
</module>
<module name="Indentation">
<property name="basicOffset" value="2"/>
<property name="braceAdjustment" value="2"/>
<property name="caseIndent" value="2"/>
<property name="throwsIndent" value="4"/>
<property name="lineWrappingIndentation" value="4"/>
<property name="arrayInitIndent" value="2"/>
</module>
<module name="AbbreviationAsWordInName">
<property name="ignoreFinal" value="false"/>
<property name="allowedAbbreviationLength" value="0"/>
<property name="tokens"
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, ANNOTATION_DEF, ANNOTATION_FIELD_DEF,
PARAMETER_DEF, VARIABLE_DEF, METHOD_DEF, PATTERN_VARIABLE_DEF, RECORD_DEF,
RECORD_COMPONENT_DEF"/>
</module>
<module name="NoWhitespaceBeforeCaseDefaultColon"/>
<module name="OverloadMethodsDeclarationOrder"/>
<module name="VariableDeclarationUsageDistance"/>
<module name="CustomImportOrder">
<property name="sortImportsInGroupAlphabetically" value="true"/>
<property name="separateLineBetweenGroups" value="true"/>
<property name="customImportOrderRules" value="STATIC###THIRD_PARTY_PACKAGE"/>
<property name="tokens" value="IMPORT, STATIC_IMPORT, PACKAGE_DEF"/>
</module>
<module name="MethodParamPad">
<property name="tokens"
value="CTOR_DEF, LITERAL_NEW, METHOD_CALL, METHOD_DEF,
SUPER_CTOR_CALL, ENUM_CONSTANT_DEF, RECORD_DEF"/>
</module>
<module name="NoWhitespaceBefore">
<property name="tokens"
value="COMMA, SEMI, POST_INC, POST_DEC, DOT,
LABELED_STAT, METHOD_REF"/>
<property name="allowLineBreaks" value="true"/>
</module>
<module name="ParenPad">
<property name="tokens"
value="ANNOTATION, ANNOTATION_FIELD_DEF, CTOR_CALL, CTOR_DEF, DOT, ENUM_CONSTANT_DEF,
EXPR, LITERAL_CATCH, LITERAL_DO, LITERAL_FOR, LITERAL_IF, LITERAL_NEW,
LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_WHILE, METHOD_CALL,
METHOD_DEF, QUESTION, RESOURCE_SPECIFICATION, SUPER_CTOR_CALL, LAMBDA,
RECORD_DEF"/>
</module>
<module name="OperatorWrap">
<property name="option" value="NL"/>
<property name="tokens"
value="BAND, BOR, BSR, BXOR, DIV, EQUAL, GE, GT, LAND, LE, LITERAL_INSTANCEOF, LOR,
LT, MINUS, MOD, NOT_EQUAL, PLUS, QUESTION, SL, SR, STAR, METHOD_REF,
TYPE_EXTENSION_AND "/>
</module>
<module name="AnnotationLocation">
<property name="id" value="AnnotationLocationMostCases"/>
<property name="tokens"
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF,
RECORD_DEF, COMPACT_CTOR_DEF"/>
</module>
<module name="AnnotationLocation">
<property name="id" value="AnnotationLocationVariables"/>
<property name="tokens" value="VARIABLE_DEF"/>
<property name="allowSamelineMultipleAnnotations" value="true"/>
</module>
<module name="NonEmptyAtclauseDescription"/>
<module name="InvalidJavadocPosition"/>
<module name="JavadocTagContinuationIndentation"/>
<module name="SummaryJavadoc">
<property name="forbiddenSummaryFragments"
value="^@return the *|^This method returns |^A [{]@code [a-zA-Z0-9]+[}]( is a )"/>
</module>
<module name="JavadocParagraph"/>
<module name="RequireEmptyLineBeforeBlockTagGroup"/>
<module name="AtclauseOrder">
<property name="tagOrder" value="@param, @return, @throws, @deprecated"/>
<property name="target"
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/>
</module>
<module name="JavadocMethod">
<property name="accessModifiers" value="public"/>
<property name="allowMissingParamTags" value="true"/>
<property name="allowMissingReturnTag" value="true"/>
<property name="allowedAnnotations" value="Override, Test"/>
<property name="tokens" value="METHOD_DEF, CTOR_DEF, ANNOTATION_FIELD_DEF, COMPACT_CTOR_DEF"/>
</module>
<module name="MissingJavadocMethod">
<property name="scope" value="public"/>
<property name="minLineCount" value="2"/>
<property name="allowedAnnotations" value="Override, Test"/>
<property name="tokens" value="METHOD_DEF, CTOR_DEF, ANNOTATION_FIELD_DEF,
COMPACT_CTOR_DEF"/>
</module>
<module name="MissingJavadocType">
<property name="scope" value="protected"/>
<property name="tokens"
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF,
RECORD_DEF, ANNOTATION_DEF"/>
<property name="excludeScope" value="nothing"/>
</module>
<module name="MethodName">
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9_]*$"/>
<message key="name.invalidPattern"
value="Method name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="SingleLineJavadoc"/>
<module name="EmptyCatchBlock">
<property name="exceptionVariableName" value="expected"/>
</module>
<module name="CommentsIndentation">
<property name="tokens" value="SINGLE_LINE_COMMENT, BLOCK_COMMENT_BEGIN"/>
</module>
<!-- https://checkstyle.org/config_filters.html#SuppressionXpathFilter -->
<module name="SuppressionXpathFilter">
<property name="file" value="${org.checkstyle.google.suppressionxpathfilter.config}"
default="checkstyle-xpath-suppressions.xml" />
<property name="optional" value="true"/>
</module>
</module>
</module>

View File

@@ -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)
}

View File

@@ -35,20 +35,19 @@ import cloud.commandframework.context.CommandContext;
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 final class LinkAccountCommand implements FloodgateCommand {
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 final class LinkAccountCommand implements FloodgateCommand {
sender.disconnect(Message.LINK_REQUEST_COMPLETED, targetName);
break;
default:
sender.disconnect(Component.text("Invalid account linking result"));
sender.disconnect("Invalid account linking result");
break;
}
});

View File

@@ -28,7 +28,6 @@ package org.geysermc.floodgate.command;
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 class TestCommand implements FloodgateCommand {
@Override
public void execute(CommandContext<UserAudience> context) {
int players = FloodgateApi.getInstance().getPlayers().size();
context.getSender().sendMessage(Component.text(players));
context.getSender().sendMessage(String.valueOf(players));
}
@Override

View File

@@ -38,17 +38,18 @@ import java.util.UUID;
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 class WhitelistCommand implements FloodgateCommand {
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<UserAudience> 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);

View File

@@ -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);
@Nullable UserAudience getAudienceByUsername(final @NonNull String username);
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));
}
}
@NonNull UserAudience getOfflineAudienceByUsername(final @NonNull String username);
protected abstract String getUsernameFromSource(@NonNull Object source);
protected abstract UUID getUuidFromSource(@NonNull Object source);
@NonNull Collection<String> getOnlineUsernames(final @NonNull PlayerType limitTo);
protected abstract Collection<?> getOnlinePlayers();
public @NonNull Collection<String> getOnlineUsernames(@NonNull PlayerType limitTo) {
Collection<?> players = getOnlinePlayers();
Collection<String> 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;
}
/**
*
* @param uuid
* @return
*/
public abstract Object getPlayerByUuid(@NonNull UUID uuid);
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<Object> 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<Object> getOnlinePlayersWithPermission(String permission) {
List<Object> 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 @@ 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(UUID uuid, String username) {
public boolean whitelistPlayer(UUID uuid, String username) {
return false;
}
@@ -130,7 +197,7 @@ public interface CommandUtil {
* @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 @@ public interface CommandUtil {
* @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;
}
}

View File

@@ -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();
@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;
@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);
void sendMessage(TranslatableMessage message, Object... args);
default void sendMessage(String message) {
sendMessage(Component.text(message));
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 disconnect(@NonNull final Component reason);
void disconnect(TranslatableMessage message, Object... args);
@Override
default @NonNull Identity identity() {
return this;
public boolean hasPermission(@NonNull String permission) {
return commandUtil.hasPermission(source(), permission);
}
interface PlayerAudience extends UserAudience {
boolean online();
public void sendMessage(String message) {
commandUtil.sendMessage(source(), message);
}
interface ConsoleAudience extends UserAudience {
public void sendMessage(TranslatableMessage message, Object... args) {
sendMessage(translateMessage(message, args));
}
public void disconnect(@NonNull String reason) {
commandUtil.kickPlayer(source(), reason);
}
public void disconnect(TranslatableMessage message, Object... args) {
disconnect(translateMessage(message, args));
}
public String translateMessage(TranslatableMessage message, Object... args) {
return commandUtil.translateMessage(locale(), message, args);
}
@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);
}
}
}

View File

@@ -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<? extends UserAudience> 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;
}
}

View File

@@ -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 java.util.UUID;
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<UserAudience, UserAudience> {
private UserAudienceArgument(final @NonNull String name, final UserAudienceParser parser) {
super(true, name, parser, UserAudience.class);
public class ProfileAudienceArgument extends CommandArgument<UserAudience, ProfileAudience> {
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<UserAudience, UserAudience> ofOnline(final String name) {
public static CommandArgument<UserAudience, ProfileAudience> 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 final class UserAudienceArgument extends CommandArgument<UserAudience, Us
}
@RequiredArgsConstructor
public static final class UserAudienceParser
implements ArgumentParser<UserAudience, UserAudience> {
public static final class ProfileAudienceParser
implements ArgumentParser<UserAudience, ProfileAudience> {
private final boolean allowUuid;
private final boolean allowOffline;
private final PlayerType limitTo;
@Override
public @NonNull ArgumentParseResult<UserAudience> parse(
final @NonNull CommandContext<@NonNull UserAudience> commandContext,
final @NonNull Queue<@NonNull String> inputQueue) {
public @NonNull ArgumentParseResult<ProfileAudience> 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 final class UserAudienceArgument extends CommandArgument<UserAudience, Us
}
}
UserAudience userAudience;
ProfileAudience profileAudience;
if (input.length() > 16) {
// This must be a UUID.
@@ -146,46 +148,39 @@ public final class UserAudienceArgument extends CommandArgument<UserAudience, Us
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<String> suggestions(
final @NonNull CommandContext<UserAudience> commandContext,
final @NonNull String input) {
final CommandUtil commandUtil = commandContext.get("CommandUtil");
final String trimmedInput = input.trim();
@NonNull CommandContext<UserAudience> 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<String> builder = ImmutableList.builder();
String lowercaseInput = input.toLowerCase(Locale.ROOT);
ImmutableList.Builder<String> builder = ImmutableList.builder();
for (final String player : commandUtil.getOnlineUsernames(limitTo)) {
if (player.toLowerCase(Locale.ROOT).startsWith(lowercaseInput)) {
@@ -205,7 +200,7 @@ public final class UserAudienceArgument extends CommandArgument<UserAudience, Us
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);
}

View File

@@ -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")

View File

@@ -35,9 +35,7 @@ import org.bukkit.event.player.PlayerQuitEvent;
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 final class SpigotListener implements Listener {
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerQuit(PlayerQuitEvent event) {
api.playerRemoved(event.getPlayer().getUniqueId());
SpigotCommandUtil.AUDIENCE_CACHE.remove(event.getPlayer().getUniqueId()); //todo
}
}

View File

@@ -59,7 +59,7 @@ public final class SpigotCommandModule extends CommandModule {
CommandManager<UserAudience> commandManager = new BukkitCommandManager<>(
plugin,
CommandExecutionCoordinator.simpleCoordinator(),
commandUtil::getAudience,
commandUtil::getUserAudience,
audience -> (CommandSender) audience.source()
);
commandManager.registerCommandPreProcessor(new FloodgateCommandPreprocessor<>(commandUtil));

View File

@@ -76,10 +76,9 @@ public final class SpigotPlatformModule extends AbstractModule {
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

View File

@@ -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<UUID, UserAudience> 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) {
protected Collection<?> getOnlinePlayers() {
return server.getOnlinePlayers();
}
@Override
public Object getPlayerByUuid(@NonNull UUID uuid) {
Player player = server.getPlayer(uuid);
return player != null ? getAudience(player) : null;
return player != null ? player : uuid;
}
@Override
public @NonNull UserAudience getOfflineAudienceByUuid(@NonNull UUID uuid) {
return new SpigotPlayerAudience(uuid, null, null, null, false, this);
}
@Override
public @NonNull Collection<String> getOnlineUsernames(@NonNull PlayerType limitTo) {
Collection<? extends Player> players = server.getOnlinePlayers();
Collection<String> 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<Object> getOnlinePlayersWithPermission(String permission) {
List<Object> 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 final class SpigotCommandUtil implements CommandUtil {
}
@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 final class SpigotCommandUtil implements CommandUtil {
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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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)

View File

@@ -57,7 +57,6 @@ import org.geysermc.floodgate.api.logger.FloodgateLogger;
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 final class VelocityListener {
@Subscribe(order = PostOrder.LAST)
public void onDisconnect(DisconnectEvent event) {
api.playerRemoved(event.getPlayer().getUniqueId());
VelocityCommandUtil.AUDIENCE_CACHE.remove(event.getPlayer().getUniqueId()); //todo
}
}

View File

@@ -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<UserAudience> commandManager(CommandUtil commandUtil) {
Injector child = guice.createChildInjector(new CloudInjectionModule<>(
UserAudience.class,
CommandExecutionCoordinator.simpleCoordinator(),
commandUtil::getAudience,
commandUtil::getUserAudience,
audience -> (CommandSource) audience.source()
));
CommandManager<UserAudience> commandManager =
child.getInstance(new Key<VelocityCommandManager<UserAudience>>() {});
bind(new Key<CommandManager<UserAudience>>() {}).toInstance(commandManager);
commandManager.registerCommandPreProcessor(new FloodgateCommandPreprocessor<>(commandUtil));
return commandManager;
}
@Provides
@@ -99,7 +98,8 @@ public final class VelocityPlatformModule extends AbstractModule {
@Provides
@Singleton
public ListenerRegistration<Object> listenerRegistration(EventManager eventManager,
public ListenerRegistration<Object> listenerRegistration(
EventManager eventManager,
VelocityPlugin plugin) {
return new VelocityListenerRegistration(eventManager, plugin);
}

View File

@@ -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;
}
}
}

View File

@@ -29,35 +29,29 @@ import com.google.inject.Inject;
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<UUID, UserAudience> 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> player = server.getPlayer(uuid);
return player.isPresent() ? player : uuid;
}
@Override
public @NonNull Collection<String> getOnlineUsernames(@NonNull PlayerType limitTo) {
Collection<Player> players = server.getAllPlayers();
Collection<String> 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> 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<Object> getOnlinePlayersWithPermission(String permission) {
List<Object> 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 final class VelocityCommandUtil implements CommandUtil {
}
@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));
}
}
}