1
0
mirror of https://github.com/GeyserMC/Floodgate.git synced 2025-12-31 04:36:47 +00:00

Added support for Forms and changed Commands + the LanguageManager a bit

This commit is contained in:
Tim203
2020-10-29 20:40:15 +01:00
parent c4971d5bf3
commit d8d0917ffb
29 changed files with 662 additions and 100 deletions

View File

@@ -111,7 +111,7 @@ public class FloodgatePlatform {
return false;
}
guice.createChildInjector(new PostInitializeModule(postInitializeModules));
this.guice = guice.createChildInjector(new PostInitializeModule(postInitializeModules));
return true;
}

View File

@@ -30,12 +30,15 @@ import java.util.Map;
import java.util.UUID;
import javax.annotation.Nullable;
import lombok.RequiredArgsConstructor;
import org.geysermc.common.form.Form;
import org.geysermc.floodgate.FloodgatePlayerImpl;
import org.geysermc.floodgate.api.player.FloodgatePlayer;
import org.geysermc.floodgate.platform.pluginmessage.PluginMessageHandler;
@RequiredArgsConstructor
public class SimpleFloodgateApi implements FloodgateApi {
private final Map<UUID, FloodgatePlayer> players = new HashMap<>();
private final PluginMessageHandler pluginMessageHandler;
@Override
public boolean isBedrockPlayer(UUID uuid) {
@@ -70,6 +73,13 @@ public class SimpleFloodgateApi implements FloodgateApi {
return uuid.getMostSignificantBits() == 0;
}
@Override
public boolean sendForm(UUID uuid, Form form) {
// the most easy way is to add the sendForm method to something that has to be implemented
// to every platform anyway, not the most elegant solution though.
return pluginMessageHandler.sendForm(uuid, form);
}
public FloodgatePlayer addPlayer(UUID uuid, FloodgatePlayer player) {
return players.put(uuid, player);
}

View File

@@ -33,6 +33,7 @@ import org.geysermc.floodgate.platform.command.CommandMessage;
*/
public enum CommonCommandMessage implements CommandMessage {
NOT_A_PLAYER("floodgate.commands.not_a_player"),
NO_PERMISSION("floodgate.commands.no_permission"),
CHECK_CONSOLE("floodgate.commands.check_console"),
// TODO used to also have console check
IS_LINKED_ERROR("floodgate.commands.is_linked_error");

View File

@@ -47,6 +47,17 @@ public final class LinkAccountCommand implements Command {
@Inject private FloodgateApi api;
@Inject private CommandUtil commandUtil;
// @Override todo impl this
// public <T> LiteralCommandNode<T> commandNode(T source, CommandUtil commandUtil) {
// return literal(getName())
// .then(
// argument("gamertag", word())
// .executes(cmd -> {
// return 0;
// })
// ).build();
// }
@Override
public void execute(Object player, UUID uuid, String username, String locale, String[] args) {
PlayerLink link = api.getPlayerLink();
@@ -134,6 +145,11 @@ public final class LinkAccountCommand implements Command {
return "linkaccount";
}
@Override
public String getDescription() {
return "Link your Java account with your Bedrock account";
}
@Override
public String getPermission() {
return "floodgate.linkaccount";

View File

@@ -77,6 +77,11 @@ public final class UnlinkAccountCommand implements Command {
return "unlinkaccount";
}
@Override
public String getDescription() {
return "Unlink your Java account from your Bedrock account";
}
@Override
public String getPermission() {
return "floodgate.unlinkaccount";

View File

@@ -101,8 +101,8 @@ public final class CommonModule extends AbstractModule {
@Provides
@Singleton
public LanguageManager languageLoader(FloodgateLogger logger) {
return new LanguageManager(logger);
public LanguageManager languageLoader() {
return new LanguageManager();
}
@Provides
@@ -123,4 +123,18 @@ public final class CommonModule extends AbstractModule {
public AttributeKey<FloodgatePlayer> playerAttribute() {
return AttributeKey.newInstance("floodgate-player");
}
@Provides
@Singleton
@Named("formChannel")
public String formChannel() {
return "floodgate:form";
}
@Provides
@Singleton
@Named("skinChannel")
public String skinChannel() {
return "floodgate:skin";
}
}

View File

@@ -31,6 +31,8 @@ import java.util.UUID;
* The base class for every Floodgate command.
*/
public interface Command {
// <T> LiteralCommandNode<T> commandNode(T source, CommandUtil commandUtil);
/**
* Should be implemented when {@link #isRequirePlayer()} is true or when the source is a
* player.
@@ -68,6 +70,12 @@ public interface Command {
*/
String getName();
/**
* Description of the command. Used as description in commands like /help (Spigot) and when you
* run the command without any arguments
*/
String getDescription();
/**
* The permission that is required to execute the specific command. Should return null when
* there is no permission required.

View File

@@ -0,0 +1,78 @@
/*
* Copyright (c) 2019-2020 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.platform.pluginmessage;
import com.google.common.base.Charsets;
import it.unimi.dsi.fastutil.shorts.Short2ObjectMap;
import it.unimi.dsi.fastutil.shorts.Short2ObjectOpenHashMap;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import org.geysermc.common.form.Form;
public abstract class PluginMessageHandler {
protected final Short2ObjectMap<Form> storedForms = new Short2ObjectOpenHashMap<>();
private final AtomicInteger nextFormId = new AtomicInteger(0);
protected boolean proxy = false;
public abstract boolean sendForm(UUID player, Form form);
protected byte[] createFormData(Form form) {
short formId = getNextFormId();
if (proxy) {
formId |= 0x8000;
}
storedForms.put(formId, form);
byte[] jsonData = form.getJsonData().getBytes(Charsets.UTF_8);
byte[] data = new byte[jsonData.length + 3];
data[0] = (byte) form.getType().ordinal();
data[1] = (byte) (formId >> 8 & 0xFF);
data[2] = (byte) (formId & 0xFF);
System.arraycopy(jsonData, 0, data, 3, jsonData.length);
return data;
}
protected boolean callResponseConsumer(byte[] data) {
Form storedForm = storedForms.remove(getFormId(data));
if (storedForm != null) {
storedForm.getResponseHandler().accept(
new String(data, 2, data.length - 2, Charsets.UTF_8));
return true;
}
return false;
}
protected short getFormId(byte[] data) {
return (short) ((data[0] & 0xFF) << 8 | data[1] & 0xFF);
}
protected short getNextFormId() {
// signed bit is used to check if the form is from a proxy or a server
return (short) nextFormId.getAndUpdate(
(number) -> number == Short.MAX_VALUE ? 0 : number + 1);
}
}

View File

@@ -25,6 +25,7 @@
package org.geysermc.floodgate.util;
import com.google.common.base.Joiner;
import com.google.inject.Inject;
import java.io.InputStream;
import java.io.InputStreamReader;
@@ -47,13 +48,17 @@ import org.geysermc.floodgate.config.FloodgateConfig;
public final class LanguageManager {
private final Map<String, Properties> LOCALE_MAPPINGS = new HashMap<>();
private final FloodgateLogger logger;
/**
* The locale used in console and as a fallback
*/
@Getter private String defaultLocale;
@Inject private FloodgateLogger logger;
public boolean isLoaded() {
return logger != null && defaultLocale != null;
}
/**
* Cleans up and formats a locale string
*
@@ -76,13 +81,17 @@ public final class LanguageManager {
*/
@Inject
public void init(FloodgateConfig config) {
loadLocale("en_US"); // Fallback
if (!loadLocale("en_US")) {// Fallback
logger.error("Failed to load the fallback language. This will likely cause errors!");
}
defaultLocale = formatLocale(config.getDefaultLocale());
if (isValidLanguage(defaultLocale)) {
loadLocale(defaultLocale);
return;
if (loadLocale(defaultLocale)) {
return;
}
logger.warn("Language provided in the config wasn't found. Will use system locale.");
}
String systemLocale = formatLocale(
@@ -90,9 +99,11 @@ public final class LanguageManager {
);
if (isValidLanguage(systemLocale)) {
loadLocale(systemLocale);
defaultLocale = systemLocale;
return;
if (loadLocale(systemLocale)) {
defaultLocale = systemLocale;
return;
}
logger.warn("Language file for system locale wasn't found. Falling back to en_US");
}
defaultLocale = "en_US";
@@ -102,19 +113,20 @@ public final class LanguageManager {
* Loads a Floodgate locale from resources; if the file doesn't exist it just logs a warning
*
* @param locale locale to load
* @return true if the locale has been found
*/
public void loadLocale(String locale) {
public boolean loadLocale(String locale) {
locale = formatLocale(locale);
// just return if the locale has been loaded already
if (LOCALE_MAPPINGS.containsKey(locale)) {
return;
return true;
}
InputStream localeStream = LanguageManager.class.getClassLoader().getResourceAsStream(
"languages/texts/" + locale + ".properties");
// Load the locale
// load the locale
if (localeStream != null) {
Properties localeProp = new Properties();
try {
@@ -123,11 +135,13 @@ public final class LanguageManager {
throw new AssertionError("Failed to load Floodgate locale", e);
}
// Insert the locale into the mappings
// insert the locale into the mappings
LOCALE_MAPPINGS.put(locale, localeProp);
} else {
logger.warn("Missing locale file: " + locale);
return true;
}
logger.warn("Missing locale file: " + locale);
return false;
}
/**
@@ -135,7 +149,7 @@ public final class LanguageManager {
*
* @param key language string to translate
* @param values values to put into the string
* @return translated string or the original message if it was not found in the given locale
* @return translated string or "key arg1, arg2 (etc.)" if it was not found in the given locale
*/
public String getLogString(String key, Object... values) {
return getString(key, defaultLocale, values);
@@ -147,31 +161,33 @@ public final class LanguageManager {
* @param key language string to translate
* @param locale locale to translate to
* @param values values to put into the string
* @return translated string or the original message if it was not found in the given locale
* @return translated string or "key arg1, arg2 (etc.)" if it was not found in the given locale
*/
public String getString(String key, String locale, Object... values) {
locale = formatLocale(locale);
// we can skip everything if the LanguageManager isn't loaded yet
if (!isLoaded()) {
return formatNotFound(key, values);
}
Properties properties = LOCALE_MAPPINGS.get(locale);
String formatString = properties.getProperty(key);
String formatString = null;
// Try and get the key from the default locale
if (properties != null) {
formatString = properties.getProperty(key);
}
// try and get the key from the default locale
if (formatString == null) {
properties = LOCALE_MAPPINGS.get(defaultLocale);
formatString = properties.getProperty(key);
}
// Try and get the key from en_US (this should only ever happen in development)
// key wasn't found
if (formatString == null) {
properties = LOCALE_MAPPINGS.get("en_US");
formatString = properties.getProperty(key);
}
// Final fallback
if (formatString == null) {
formatString = key;
return formatNotFound(key, values);
}
//todo don't use color codes in the strings
return MessageFormat.format(formatString.replace("'", "''").replace("&", "\u00a7"), values);
}
@@ -195,4 +211,8 @@ public final class LanguageManager {
}
return true;
}
private String formatNotFound(String key, Object... args) {
return key + " " + Joiner.on(", ").join(args);
}
}

View File

@@ -211,6 +211,18 @@ public final class ReflectionUtils {
}
}
/**
* Get the value of the given field by finding the field and then get the value of it.
*
* @param instance the instance of the object
* @param fieldName the name of the field to get the value from
* @return the value of the field when succeeded, otheriwse null
*/
@Nullable
public static Object getValue(Object instance, String fieldName) {
return getValue(instance, getField(instance.getClass(), fieldName));
}
/**
* Get the value of a field and cast it to <T>.
*
@@ -226,15 +238,17 @@ public final class ReflectionUtils {
}
/**
* Get the value of the given field by finding the field and then get the value of it.
* Get the value of a field and cast it to <T>.
*
* @param instance the instance of the object
* @param fieldName the name of the field to get the value from
* @return the value of the field when succeeded, otheriwse null
* @param instance the instance to get the value from
* @param fieldName the field to get the value from
* @param <T> the type to cast the value to
* @return the casted value when succeeded, otherwise null
*/
@SuppressWarnings("unchecked")
@Nullable
public static Object getValue(Object instance, String fieldName) {
return getValue(instance, getField(instance.getClass(), fieldName));
public static <T> T getCastedValue(Object instance, String fieldName) {
return (T) getValue(instance, getField(instance.getClass(), fieldName));
}
/**