mirror of
https://github.com/GeyserMC/Floodgate.git
synced 2026-01-06 15:42:03 +00:00
Added initial version of whitelist command and fixed a Java 16 issue
This commit is contained in:
@@ -43,12 +43,12 @@
|
||||
<dependency>
|
||||
<groupId>net.kyori</groupId>
|
||||
<artifactId>adventure-text-serializer-gson</artifactId>
|
||||
<version>${adventure-serializer.version}</version>
|
||||
<version>${adventure-platform.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.kyori</groupId>
|
||||
<artifactId>adventure-text-serializer-bungeecord</artifactId>
|
||||
<version>${adventure-serializer.version}</version>
|
||||
<version>${adventure-platform.version}</version>
|
||||
</dependency>
|
||||
<!-- the following common dependencies are already present on the platform -->
|
||||
<dependency>
|
||||
|
||||
@@ -144,6 +144,16 @@ public final class BungeeCommandUtil implements CommandUtil {
|
||||
cast(player).disconnect(translateAndTransform(locale, message, args));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean whitelistPlayer(String xuid, String username) {
|
||||
return false; // todo
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removePlayerFromWhitelist(String xuid, String username) {
|
||||
return false; // todo
|
||||
}
|
||||
|
||||
public BaseComponent[] translateAndTransform(String locale, CommandMessage message,
|
||||
Object... args) {
|
||||
return TextComponent.fromLegacyText(message.translateMessage(manager, locale, args));
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<dependency>
|
||||
<groupId>com.google.inject</groupId>
|
||||
<artifactId>guice</artifactId>
|
||||
<version>4.2.3</version>
|
||||
<version>5.0.0-BETA-1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2021 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.command;
|
||||
|
||||
import cloud.commandframework.Command;
|
||||
import cloud.commandframework.CommandManager;
|
||||
import cloud.commandframework.Description;
|
||||
import cloud.commandframework.context.CommandContext;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.inject.Inject;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.config.FloodgateConfig;
|
||||
import org.geysermc.floodgate.platform.command.CommandUtil;
|
||||
import org.geysermc.floodgate.platform.command.FloodgateCommand;
|
||||
import org.geysermc.floodgate.player.UserAudience;
|
||||
import org.geysermc.floodgate.player.UserAudienceArgument;
|
||||
import org.geysermc.floodgate.util.Constants;
|
||||
import org.geysermc.floodgate.util.HttpUtils;
|
||||
|
||||
public class WhitelistCommand implements FloodgateCommand {
|
||||
@Inject private FloodgateConfig config;
|
||||
@Inject private FloodgateLogger logger;
|
||||
|
||||
@Override
|
||||
public Command<UserAudience> buildCommand(CommandManager<UserAudience> commandManager) {
|
||||
Command.Builder<UserAudience> builder = commandManager.commandBuilder("fwhitelist",
|
||||
Description.of("Easy way to whitelist Bedrock players"))
|
||||
.permission("floodgate.command.fwhitelist");
|
||||
|
||||
commandManager.command(builder
|
||||
.literal("add", "a")
|
||||
.argument(UserAudienceArgument.of("player", true))
|
||||
.handler(context -> performCommand(context, true)));
|
||||
|
||||
return builder
|
||||
.literal("remove", "r")
|
||||
.argument(UserAudienceArgument.of("player", true))
|
||||
.handler(context -> performCommand(context, false))
|
||||
.build();
|
||||
}
|
||||
|
||||
public void performCommand(CommandContext<UserAudience> context, boolean add) {
|
||||
UserAudience sender = context.getSender();
|
||||
UserAudience player = context.get("player");
|
||||
String name = player.username();
|
||||
|
||||
if (name.startsWith(config.getUsernamePrefix())) {
|
||||
name = name.substring(config.getUsernamePrefix().length());
|
||||
}
|
||||
|
||||
if (name.length() < 1 || name.length() > 16) {
|
||||
sender.sendMessage(Component.text(
|
||||
"The given username '" + name + "' is not a valid username."));
|
||||
return;
|
||||
}
|
||||
|
||||
// todo let it use translations
|
||||
|
||||
final String tempName = name;
|
||||
|
||||
HttpUtils.asyncGet(Constants.GET_XUID_URL + name)
|
||||
.whenComplete((result, error) -> {
|
||||
if (error != null) {
|
||||
sender.sendMessage(Component.text(
|
||||
"An error occurred. See the console for more info"));
|
||||
error.printStackTrace();
|
||||
return;
|
||||
}
|
||||
|
||||
JsonObject response = result.getResponse();
|
||||
boolean success = response.get("success").getAsBoolean();
|
||||
|
||||
if (!success) {
|
||||
sender.sendMessage(Component.text(
|
||||
"An error occurred. See the console for more info"));
|
||||
logger.error(
|
||||
"Got an error from requesting the xuid of a Bedrock player: {}",
|
||||
response.get("message").getAsString());
|
||||
return;
|
||||
}
|
||||
|
||||
JsonObject data = response.getAsJsonObject("data");
|
||||
if (data.size() == 0) {
|
||||
sender.sendMessage(Component.text(
|
||||
"Couldn't find the user '" + tempName + "'"));
|
||||
return;
|
||||
}
|
||||
|
||||
String xuid = data.get("xuid").getAsString();
|
||||
CommandUtil commandUtil = context.get("CommandUtil");
|
||||
|
||||
try {
|
||||
if (add && commandUtil.whitelistPlayer(xuid, tempName)) {
|
||||
sender.sendMessage(Component.text("Player has been whitelisted :)"));
|
||||
} else if (!add && commandUtil.removePlayerFromWhitelist(xuid, tempName)) {
|
||||
sender.sendMessage(Component.text("Player has been removed :o"));
|
||||
} else {
|
||||
sender.sendMessage(Component.text("Player was already whitelisted :o"));
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
logger.error(
|
||||
"An unexpected error happened while executing the whitelist command",
|
||||
exception);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandContext<UserAudience> context) {
|
||||
// ignored, all the logic is in the other method
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,7 @@ import com.google.inject.multibindings.ProvidesIntoSet;
|
||||
import org.geysermc.floodgate.command.LinkAccountCommand;
|
||||
import org.geysermc.floodgate.command.TestCommand;
|
||||
import org.geysermc.floodgate.command.UnlinkAccountCommand;
|
||||
import org.geysermc.floodgate.command.WhitelistCommand;
|
||||
import org.geysermc.floodgate.platform.command.FloodgateCommand;
|
||||
import org.geysermc.floodgate.register.CommandRegister;
|
||||
|
||||
@@ -52,6 +53,12 @@ public class CommandModule extends AbstractModule {
|
||||
return new UnlinkAccountCommand();
|
||||
}
|
||||
|
||||
@Singleton
|
||||
@ProvidesIntoSet
|
||||
public FloodgateCommand whitelistCommand() {
|
||||
return new WhitelistCommand();
|
||||
}
|
||||
|
||||
@Singleton
|
||||
@ProvidesIntoSet
|
||||
public FloodgateCommand testCommand() {
|
||||
|
||||
@@ -69,4 +69,23 @@ public interface CommandUtil {
|
||||
* @param args the arguments
|
||||
*/
|
||||
void kickPlayer(Object player, String locale, CommandMessage message, Object... args);
|
||||
|
||||
/**
|
||||
* Whitelist the given Bedrock player.
|
||||
*
|
||||
* @param xuid the xuid of the username to be whitelisted
|
||||
* @param username the username to be whitelisted
|
||||
* @return true if the player has been whitelisted, false if the player was already whitelisted
|
||||
*/
|
||||
boolean whitelistPlayer(String xuid, String username);
|
||||
|
||||
/**
|
||||
* Removes the given Bedrock player from the whitelist.
|
||||
*
|
||||
* @param xuid the xuid of the username to be removed from the whitelist
|
||||
* @param username the username to be removed from the whitelist
|
||||
* @return true if the player has been removed from the whitelist, false if the player wasn't
|
||||
* whitelisted
|
||||
*/
|
||||
boolean removePlayerFromWhitelist(String xuid, String username);
|
||||
}
|
||||
|
||||
@@ -28,7 +28,10 @@ package org.geysermc.floodgate.util;
|
||||
public final class Constants {
|
||||
public static final String DATABASE_NAME_FORMAT = "^floodgate-[a-zA-Z0-9_]{0,16}-database.jar$";
|
||||
public static final int LOGIN_SUCCESS_PACKET_ID = 2;
|
||||
public static final String WEBSOCKET_URL = "wss://api.geysermc.org/ws";
|
||||
|
||||
private static final String API_BASE_URL = "s://api.geysermc.org";
|
||||
public static final String WEBSOCKET_URL = "ws" + API_BASE_URL + "/ws";
|
||||
public static final String GET_XUID_URL = "http" + API_BASE_URL + "/v1/xbox/xuid/";
|
||||
|
||||
public static final boolean DEBUG_MODE = true;
|
||||
|
||||
|
||||
@@ -27,27 +27,33 @@ package org.geysermc.floodgate.util;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import javax.imageio.ImageIO;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public class HttpUtils {
|
||||
private static final ExecutorService EXECUTOR_SERVICE = Executors.newSingleThreadExecutor();
|
||||
|
||||
private static final Gson GSON = new Gson();
|
||||
private static final String USER_AGENT = "GeyserMC/Floodgate";
|
||||
private static final String CONNECTION_STRING = "--";
|
||||
private static final String BOUNDARY = "******";
|
||||
private static final String END = "\r\n";
|
||||
|
||||
public static CompletableFuture<HttpResponse> asyncGet(String urlString) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
return get(urlString);
|
||||
}, EXECUTOR_SERVICE);
|
||||
}
|
||||
|
||||
public static HttpResponse get(String urlString) {
|
||||
HttpURLConnection connection;
|
||||
|
||||
@@ -70,42 +76,6 @@ public class HttpUtils {
|
||||
return readResponse(connection);
|
||||
}
|
||||
|
||||
public static HttpResponse post(String urlString, BufferedImage... images) {
|
||||
HttpURLConnection connection;
|
||||
|
||||
try {
|
||||
URL url = new URL(urlString);
|
||||
connection = (HttpURLConnection) url.openConnection();
|
||||
} catch (Exception exception) {
|
||||
throw new RuntimeException("Failed to create connection", exception);
|
||||
}
|
||||
|
||||
DataOutputStream outputStream = null;
|
||||
|
||||
try {
|
||||
connection.setRequestMethod("POST");
|
||||
connection.setDoOutput(true);
|
||||
connection.setUseCaches(false);
|
||||
connection.setRequestProperty("User-Agent", USER_AGENT);
|
||||
connection.setRequestProperty(
|
||||
"Content-Type",
|
||||
"multipart/form-data;boundary=" + BOUNDARY
|
||||
);
|
||||
|
||||
outputStream = new DataOutputStream(connection.getOutputStream());
|
||||
writeDataFor(outputStream, images);
|
||||
} catch (Exception exception) {
|
||||
throw new RuntimeException("Failed to create request", exception);
|
||||
} finally {
|
||||
try {
|
||||
outputStream.close();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
return readResponse(connection);
|
||||
}
|
||||
|
||||
private static HttpResponse readResponse(HttpURLConnection connection) {
|
||||
InputStream stream = null;
|
||||
try {
|
||||
@@ -136,31 +106,6 @@ public class HttpUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static void writeDataFor(DataOutputStream outputStream, BufferedImage... images) {
|
||||
try {
|
||||
for (int i = 0; i < images.length; i++) {
|
||||
outputStream.writeBytes(CONNECTION_STRING + BOUNDARY + END);
|
||||
outputStream.writeBytes(
|
||||
"Content-Disposition:form-data;name=file;filename=image" + i + ".png");
|
||||
outputStream.writeBytes(END);
|
||||
outputStream.writeBytes(END);
|
||||
fileDataForImage(outputStream, images[i]);
|
||||
outputStream.writeBytes(END);
|
||||
}
|
||||
outputStream.writeBytes(CONNECTION_STRING + BOUNDARY + CONNECTION_STRING + END);
|
||||
} catch (Exception exception) {
|
||||
throw new RuntimeException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
public static void fileDataForImage(OutputStream outputStream, BufferedImage image) {
|
||||
try {
|
||||
ImageIO.write(image, "png", outputStream);
|
||||
} catch (IOException exception) {
|
||||
throw new RuntimeException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public static final class HttpResponse {
|
||||
|
||||
@@ -25,21 +25,16 @@
|
||||
|
||||
package org.geysermc.floodgate.util;
|
||||
|
||||
import static org.geysermc.floodgate.util.MessageFormatter.format;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import java.lang.reflect.AccessibleObject;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import javax.annotation.Nullable;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
public final class ReflectionUtils {
|
||||
private static final Field MODIFIERS_FIELD;
|
||||
|
||||
/**
|
||||
* The package name that is shared between all the {@link #getPrefixedClass(String)} calls so
|
||||
* that the className will be a lot shorter. Example net.minecraft.server.v1_8R3.PacketHandshakingInSetProtocol
|
||||
@@ -50,43 +45,6 @@ public final class ReflectionUtils {
|
||||
@Setter
|
||||
private static String prefix;
|
||||
|
||||
static {
|
||||
Field modifiersField = null;
|
||||
try {
|
||||
modifiersField = Field.class.getDeclaredField("modifiers");
|
||||
} catch (NoSuchFieldException ignored) {
|
||||
// Java 12 compatibility, thanks to https://github.com/powermock/powermock/pull/1010
|
||||
try {
|
||||
Method declaredFields = getMethod(Class.class, "getDeclaredFields0", boolean.class);
|
||||
if (declaredFields == null) {
|
||||
throw new NoSuchMethodException("Cannot find method getDeclaredFields0");
|
||||
}
|
||||
|
||||
Field[] fields = castedInvoke(Field.class, declaredFields, false);
|
||||
if (fields == null) {
|
||||
throw new RuntimeException("The Field class cannot have null fields");
|
||||
}
|
||||
|
||||
for (Field field : fields) {
|
||||
if ("modifiers".equals(field.getName())) {
|
||||
modifiersField = field;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
throw new RuntimeException(format(
|
||||
"Cannot find the modifiers field :/\nJava version: {}\nVendor: {} ({})",
|
||||
System.getProperty("java.version"),
|
||||
System.getProperty("java.vendor"),
|
||||
System.getProperty("java.vendor.url")
|
||||
), exception);
|
||||
}
|
||||
}
|
||||
|
||||
Preconditions.checkNotNull(modifiersField, "Modifiers field cannot be null!");
|
||||
MODIFIERS_FIELD = modifiersField;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a class that is prefixed with the prefix provided in {@link #setPrefix(String)}. Calling
|
||||
* this method is equal to calling {@link #getClass(String)} with <i>prefix</i>.<i>classname</i>
|
||||
@@ -119,6 +77,24 @@ public final class ReflectionUtils {
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Constructor<?> getConstructor(Class<?> clazz, Class<?>... parameters) {
|
||||
try {
|
||||
return clazz.getConstructor(parameters);
|
||||
} catch (NoSuchMethodException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Object newInstance(Constructor<?> constructor, Object... parameters) {
|
||||
try {
|
||||
return constructor.newInstance(parameters);
|
||||
} catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a field of a class. Calling this method is equal to calling {@link
|
||||
* Class#getField(String)} where String is the fieldName when isPublic is true and calling this
|
||||
@@ -288,33 +264,6 @@ public final class ReflectionUtils {
|
||||
return field != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of a <b>final</b> field. This method first makes the field accessible, then
|
||||
* removes the final modifier and then sets the value.<br> This method will not throw exceptions
|
||||
* when failed, but it'll log the error to the console.
|
||||
*
|
||||
* @param instance the instance to set the value to
|
||||
* @param field the field to set the value to
|
||||
* @param value the value to set
|
||||
* @return true if succeeded
|
||||
*/
|
||||
public static boolean setFinalValue(Object instance, Field field, Object value) {
|
||||
try {
|
||||
makeAccessible(field);
|
||||
|
||||
int modifiers = field.getModifiers();
|
||||
if (Modifier.isFinal(modifiers)) {
|
||||
makeAccessible(MODIFIERS_FIELD);
|
||||
MODIFIERS_FIELD.setInt(field, modifiers & ~Modifier.FINAL);
|
||||
}
|
||||
setValue(instance, field, value);
|
||||
return true;
|
||||
} catch (Exception exception) {
|
||||
exception.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a method from a class, it doesn't matter if the field is public or not. This method will
|
||||
* first try to get a declared field and if that failed it'll try to get a public field.<br>
|
||||
@@ -328,8 +277,10 @@ public final class ReflectionUtils {
|
||||
* @return the requested method if it has been found, otherwise null
|
||||
*/
|
||||
@Nullable
|
||||
public static Method getMethod(Class<?> clazz, String method, boolean declared,
|
||||
Class<?>... arguments) {
|
||||
public static Method getMethod(
|
||||
Class<?> clazz, String method,
|
||||
boolean declared,
|
||||
Class<?>... arguments) {
|
||||
try {
|
||||
if (declared) {
|
||||
return clazz.getMethod(method, arguments);
|
||||
@@ -372,6 +323,46 @@ public final class ReflectionUtils {
|
||||
return getMethod(instance.getClass(), methodName, arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a method from a class by using the name of the method.
|
||||
*
|
||||
* @param clazz the class to search the method in
|
||||
* @param methodName the name of the method
|
||||
* @param declared if the method is declared or public
|
||||
* @return the method if it has been found, otherwise null
|
||||
*/
|
||||
@Nullable
|
||||
public static Method getMethodByName(Class<?> clazz, String methodName, boolean declared) {
|
||||
Method[] methods = declared ? clazz.getDeclaredMethods() : clazz.getMethods();
|
||||
for (Method method : methods) {
|
||||
if (method.getName().equals(methodName)) {
|
||||
return method;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a method from a class without having to provide a method name.
|
||||
*
|
||||
* @param clazz the class to search the method in
|
||||
* @param paramType the type of one of the method parameters
|
||||
* @param declared if the method is declared or public
|
||||
* @return the method if it has been found, otherwise null
|
||||
*/
|
||||
@Nullable
|
||||
public static Method getMethodFromParam(Class<?> clazz, Class<?> paramType, boolean declared) {
|
||||
Method[] methods = declared ? clazz.getDeclaredMethods() : clazz.getMethods();
|
||||
for (Method method : methods) {
|
||||
for (Class<?> parameter : method.getParameterTypes()) {
|
||||
if (parameter == paramType) {
|
||||
return method;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke the given method of the given instance with the given arguments.
|
||||
*
|
||||
|
||||
5
pom.xml
5
pom.xml
@@ -62,8 +62,9 @@
|
||||
<spigot.version>1.13-R0.1-SNAPSHOT</spigot.version>
|
||||
<bungee.version>1.15-SNAPSHOT</bungee.version>
|
||||
<velocity.version>1.1.0</velocity.version>
|
||||
<cloud.version>1.3.0</cloud.version>
|
||||
<adventure-serializer.version>4.0.0-SNAPSHOT</adventure-serializer.version>
|
||||
<cloud.version>1.4.0</cloud.version>
|
||||
<adventure-api.version>4.5.0</adventure-api.version>
|
||||
<adventure-platform.version>4.0.0-SNAPSHOT</adventure-platform.version>
|
||||
|
||||
<outputName>floodgate-${project.name}</outputName>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
|
||||
@@ -57,8 +57,13 @@
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.kyori</groupId>
|
||||
<artifactId>adventure-text-serializer-craftbukkit</artifactId>
|
||||
<version>${adventure-serializer.version}</version>
|
||||
<artifactId>adventure-text-serializer-legacy</artifactId>
|
||||
<version>${adventure-api.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.kyori</groupId>
|
||||
<artifactId>adventure-text-serializer-gson</artifactId>
|
||||
<version>${adventure-api.version}</version>
|
||||
</dependency>
|
||||
<!-- the following common dependencies are already present on the platform -->
|
||||
<dependency>
|
||||
|
||||
@@ -147,6 +147,16 @@ public final class SpigotCommandUtil implements CommandUtil {
|
||||
() -> cast(player).kickPlayer(translateAndTransform(locale, message, args)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean whitelistPlayer(String xuid, String username) {
|
||||
return WhitelistUtils.addPlayer(xuid, username);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removePlayerFromWhitelist(String xuid, String username) {
|
||||
return WhitelistUtils.removePlayer(xuid, username);
|
||||
}
|
||||
|
||||
public String translateAndTransform(String locale, CommandMessage message, Object... args) {
|
||||
// unlike others, Bukkit doesn't have to transform a message into another class.
|
||||
return message.translateMessage(manager, locale, args);
|
||||
|
||||
@@ -25,8 +25,6 @@
|
||||
|
||||
package org.geysermc.floodgate.util;
|
||||
|
||||
import static net.kyori.adventure.text.serializer.craftbukkit.BukkitComponentSerializer.legacy;
|
||||
|
||||
import java.util.UUID;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
@@ -35,6 +33,7 @@ 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;
|
||||
@@ -106,7 +105,6 @@ public class SpigotUserAudience implements UserAudience, ForwardingAudience.Sing
|
||||
|
||||
public static final class SpigotConsoleAudience extends SpigotUserAudience
|
||||
implements ConsoleAudience {
|
||||
|
||||
public SpigotConsoleAudience(CommandSender source, CommandUtil commandUtil) {
|
||||
super(new UUID(0, 0), "en_us", source, commandUtil);
|
||||
}
|
||||
@@ -116,7 +114,7 @@ public class SpigotUserAudience implements UserAudience, ForwardingAudience.Sing
|
||||
@NonNull Identity source,
|
||||
@NonNull Component message,
|
||||
@NonNull MessageType type) {
|
||||
source().sendMessage(legacy().serialize(message));
|
||||
source().sendMessage(LegacyComponentSerializer.legacySection().serialize(message));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2021 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 static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public final class WhitelistUtils {
|
||||
private static final Method GET_SERVER;
|
||||
private static final Method GET_PLAYER_LIST;
|
||||
private static final Method GET_WHITELIST;
|
||||
private static final Method IS_WHITELISTED;
|
||||
private static final Constructor<?> WHITELIST_ENTRY;
|
||||
private static final Method ADD_ENTRY;
|
||||
private static final Method REMOVE_ENTRY;
|
||||
|
||||
static {
|
||||
Class<?> bukkitServerClass = Bukkit.getServer().getClass();
|
||||
GET_SERVER = ReflectionUtils.getMethod(bukkitServerClass, "getServer");
|
||||
checkNotNull(GET_SERVER, bukkitServerClass.getName() + " doesn't have a getServer method?");
|
||||
|
||||
Class<?> minecraftServer = ReflectionUtils.getPrefixedClass("MinecraftServer");
|
||||
GET_PLAYER_LIST = ReflectionUtils.getMethod(minecraftServer, "getPlayerList");
|
||||
checkNotNull(GET_PLAYER_LIST, "Cannot find getPlayerList");
|
||||
|
||||
Class<?> playerList = ReflectionUtils.getPrefixedClass("PlayerList");
|
||||
GET_WHITELIST = ReflectionUtils.getMethod(playerList, "getWhitelist");
|
||||
checkNotNull(GET_WHITELIST, "Cannot find getWhitelist");
|
||||
|
||||
Class<?> whitelist = ReflectionUtils.getPrefixedClass("WhiteList");
|
||||
IS_WHITELISTED = ReflectionUtils.getMethod(whitelist, "isWhitelisted", GameProfile.class);
|
||||
checkNotNull(IS_WHITELISTED, "Couldn't find the isWhitelisted method!");
|
||||
|
||||
Class<?> whitelistEntry = ReflectionUtils.getPrefixedClass("WhiteListEntry");
|
||||
WHITELIST_ENTRY = ReflectionUtils.getConstructor(whitelistEntry, GameProfile.class);
|
||||
checkNotNull(WHITELIST_ENTRY, "Could not find required WhiteListEntry constructor");
|
||||
|
||||
Class<?> jsonList = ReflectionUtils.getPrefixedClass("JsonList");
|
||||
ADD_ENTRY = ReflectionUtils.getMethodByName(jsonList, "add", false);
|
||||
checkNotNull(ADD_ENTRY, "Cannot find add method");
|
||||
|
||||
Class<?> jsonListEntry = ReflectionUtils.getPrefixedClass("JsonListEntry");
|
||||
REMOVE_ENTRY = ReflectionUtils.getMethodFromParam(jsonList, jsonListEntry, false);
|
||||
checkNotNull(REMOVE_ENTRY, "Cannot find remove method");
|
||||
}
|
||||
|
||||
/**
|
||||
* Whitelist the given Bedrock player.
|
||||
*
|
||||
* @param xuid the xuid of the Bedrock player to be whitelisted
|
||||
* @param username the username of the Bedrock player to be whitelisted
|
||||
* @return true if the player has been whitelisted, false if the player is already whitelisted
|
||||
*/
|
||||
public static boolean addPlayer(String xuid, String username) {
|
||||
Object whitelist = getWhitelist();
|
||||
|
||||
GameProfile profile = new GameProfile(Utils.getJavaUuid(xuid), username);
|
||||
|
||||
if (ReflectionUtils.castedInvoke(whitelist, IS_WHITELISTED, profile)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Object entry = ReflectionUtils.newInstance(WHITELIST_ENTRY, profile);
|
||||
|
||||
ReflectionUtils.invoke(whitelist, ADD_ENTRY, entry);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given Bedrock player from the whitelist.
|
||||
*
|
||||
* @param xuid the xuid of the Bedrock player to be removed
|
||||
* @param username the username of the Bedrock player to be removed
|
||||
* @return true if the player has been removed from the whitelist, false if the player wasn't
|
||||
* whitelisted
|
||||
*/
|
||||
public static boolean removePlayer(String xuid, String username) {
|
||||
Object whitelist = getWhitelist();
|
||||
|
||||
GameProfile profile = new GameProfile(Utils.getJavaUuid(xuid), username);
|
||||
|
||||
if (!(boolean) ReflectionUtils.castedInvoke(whitelist, IS_WHITELISTED, profile)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Object entry = ReflectionUtils.newInstance(WHITELIST_ENTRY, profile);
|
||||
|
||||
ReflectionUtils.invoke(whitelist, REMOVE_ENTRY, entry);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static Object getWhitelist() {
|
||||
Object minecraftServer = ReflectionUtils.invoke(Bukkit.getServer(), GET_SERVER);
|
||||
Object playerList = ReflectionUtils.invoke(minecraftServer, GET_PLAYER_LIST);
|
||||
return ReflectionUtils.invoke(playerList, GET_WHITELIST);
|
||||
}
|
||||
}
|
||||
@@ -143,6 +143,16 @@ public final class VelocityCommandUtil implements CommandUtil {
|
||||
cast(player).disconnect(translateAndTransform(locale, message, args));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean whitelistPlayer(String xuid, String username) {
|
||||
return false; // todo
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removePlayerFromWhitelist(String xuid, String username) {
|
||||
return false; // todo
|
||||
}
|
||||
|
||||
public Component translateAndTransform(String locale, CommandMessage message,
|
||||
Object... args) {
|
||||
return Component.text(message.translateMessage(manager, locale, args));
|
||||
|
||||
Reference in New Issue
Block a user