diff --git a/api/src/main/java/org/geysermc/floodgate/api/FloodgateApi.java b/api/src/main/java/org/geysermc/floodgate/api/FloodgateApi.java index 7df084ab..7cc38c55 100644 --- a/api/src/main/java/org/geysermc/floodgate/api/FloodgateApi.java +++ b/api/src/main/java/org/geysermc/floodgate/api/FloodgateApi.java @@ -26,6 +26,7 @@ package org.geysermc.floodgate.api; import java.util.UUID; +import org.geysermc.common.form.Form; import org.geysermc.floodgate.api.link.PlayerLink; import org.geysermc.floodgate.api.player.FloodgatePlayer; @@ -71,6 +72,8 @@ public interface FloodgateApi { */ boolean isFloodgateId(UUID uuid); + boolean sendForm(UUID uuid, Form form); + /** * Returns the instance that manages all the linking. */ diff --git a/api/src/main/java/org/geysermc/floodgate/api/player/FloodgatePlayer.java b/api/src/main/java/org/geysermc/floodgate/api/player/FloodgatePlayer.java index 41796821..73bb823e 100644 --- a/api/src/main/java/org/geysermc/floodgate/api/player/FloodgatePlayer.java +++ b/api/src/main/java/org/geysermc/floodgate/api/player/FloodgatePlayer.java @@ -26,6 +26,8 @@ package org.geysermc.floodgate.api.player; import java.util.UUID; +import org.geysermc.common.form.Form; +import org.geysermc.floodgate.api.FloodgateApi; import org.geysermc.floodgate.util.DeviceOs; import org.geysermc.floodgate.util.InputMode; import org.geysermc.floodgate.util.LinkedPlayer; @@ -104,6 +106,10 @@ public interface FloodgatePlayer { */ RawSkin getRawSkin(); + default boolean sendForm(Form form) { + return FloodgateApi.getInstance().sendForm(getCorrectUniqueId(), form); + } + /** * Casts the FloodgatePlayer instance to a class that extends FloodgatePlayer. * diff --git a/bungee/src/main/java/org/geysermc/floodgate/handler/BungeeDataHandler.java b/bungee/src/main/java/org/geysermc/floodgate/handler/BungeeDataHandler.java index 8bc136ce..eabb4acb 100644 --- a/bungee/src/main/java/org/geysermc/floodgate/handler/BungeeDataHandler.java +++ b/bungee/src/main/java/org/geysermc/floodgate/handler/BungeeDataHandler.java @@ -35,6 +35,7 @@ import io.netty.util.AttributeKey; import java.lang.reflect.Field; import java.net.InetSocketAddress; import java.net.SocketAddress; +import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.event.PreLoginEvent; import net.md_5.bungee.api.plugin.Plugin; @@ -102,10 +103,10 @@ public final class BungeeDataHandler { event.setCancelReason(config.getMessages().getInvalidKey()); break; case INVALID_DATA_LENGTH: - event.setCancelReason(String.format( + event.setCancelReason(TextComponent.fromLegacyText(String.format( config.getMessages().getInvalidArgumentsLength(), BedrockData.EXPECTED_LENGTH, result.getBedrockData().getDataLength() - )); + ))); break; } @@ -131,9 +132,6 @@ public final class BungeeDataHandler { SocketAddress remoteAddress = ReflectionUtils.getCastedValue(channelWrapper, PLAYER_REMOTE_ADDRESS); - Channel channel = ReflectionUtils.getCastedValue(channelWrapper, PLAYER_CHANNEL); - channel.attr(playerAttribute).set(player); - if (!(remoteAddress instanceof InetSocketAddress)) { logger.info("Player {} doesn't use an InetSocketAddress, it uses {}. " + "Ignoring the player, I guess.", @@ -146,6 +144,11 @@ public final class BungeeDataHandler { new InetSocketAddress(result.getBedrockData().getIp(), port) ); } + + Channel channel = ReflectionUtils.getCastedValue(channelWrapper, PLAYER_CHANNEL); + + channel.attr(playerAttribute).set(player); + event.completeIntent(plugin); }); } diff --git a/bungee/src/main/java/org/geysermc/floodgate/module/BungeeListenerModule.java b/bungee/src/main/java/org/geysermc/floodgate/module/BungeeListenerModule.java index 370479de..6f57739e 100644 --- a/bungee/src/main/java/org/geysermc/floodgate/module/BungeeListenerModule.java +++ b/bungee/src/main/java/org/geysermc/floodgate/module/BungeeListenerModule.java @@ -31,6 +31,7 @@ import com.google.inject.TypeLiteral; import com.google.inject.multibindings.ProvidesIntoSet; import net.md_5.bungee.api.plugin.Listener; import org.geysermc.floodgate.listener.BungeeListener; +import org.geysermc.floodgate.platform.pluginmessage.PluginMessageHandler; import org.geysermc.floodgate.register.ListenerRegister; public final class BungeeListenerModule extends AbstractModule { @@ -44,4 +45,10 @@ public final class BungeeListenerModule extends AbstractModule { public Listener bungeeListener() { return new BungeeListener(); } + + @Singleton + @ProvidesIntoSet + public Listener pluginMessageListener(PluginMessageHandler handler) { + return (Listener) handler; // Plugin message handler is also the listener + } } diff --git a/bungee/src/main/java/org/geysermc/floodgate/module/BungeePlatformModule.java b/bungee/src/main/java/org/geysermc/floodgate/module/BungeePlatformModule.java index e80fbd71..88a59916 100644 --- a/bungee/src/main/java/org/geysermc/floodgate/module/BungeePlatformModule.java +++ b/bungee/src/main/java/org/geysermc/floodgate/module/BungeePlatformModule.java @@ -47,6 +47,8 @@ import org.geysermc.floodgate.logger.JavaUtilFloodgateLogger; import org.geysermc.floodgate.platform.command.CommandRegistration; import org.geysermc.floodgate.platform.command.CommandUtil; import org.geysermc.floodgate.platform.listener.ListenerRegistration; +import org.geysermc.floodgate.platform.pluginmessage.PluginMessageHandler; +import org.geysermc.floodgate.pluginmessage.BungeePluginMessageHandler; import org.geysermc.floodgate.util.BungeeCommandUtil; import org.geysermc.floodgate.util.LanguageManager; @@ -74,8 +76,9 @@ public final class BungeePlatformModule extends AbstractModule { @Provides @Singleton - public ProxyFloodgateApi proxyFloodgateApi(FloodgateCipher cipher) { - return new ProxyFloodgateApi(cipher); + public ProxyFloodgateApi proxyFloodgateApi(PluginMessageHandler pluginMessageHandler, + FloodgateCipher cipher) { + return new ProxyFloodgateApi(pluginMessageHandler, cipher); } @Provides @@ -107,6 +110,12 @@ public final class BungeePlatformModule extends AbstractModule { return new BungeeListenerRegistration(plugin); } + @Provides + @Singleton + public PluginMessageHandler pluginMessageHandler() { + return new BungeePluginMessageHandler(); + } + /* DebugAddon / PlatformInjector */ diff --git a/bungee/src/main/java/org/geysermc/floodgate/pluginmessage/BungeePluginMessageHandler.java b/bungee/src/main/java/org/geysermc/floodgate/pluginmessage/BungeePluginMessageHandler.java new file mode 100644 index 00000000..fe734e52 --- /dev/null +++ b/bungee/src/main/java/org/geysermc/floodgate/pluginmessage/BungeePluginMessageHandler.java @@ -0,0 +1,110 @@ +/* + * 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.pluginmessage; + +import static org.geysermc.floodgate.util.MessageFormatter.format; + +import com.google.inject.Inject; +import com.google.inject.name.Named; +import java.util.UUID; +import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.chat.TextComponent; +import net.md_5.bungee.api.connection.Connection; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.connection.Server; +import net.md_5.bungee.api.event.PluginMessageEvent; +import net.md_5.bungee.api.plugin.Listener; +import net.md_5.bungee.api.plugin.Plugin; +import net.md_5.bungee.event.EventHandler; +import org.geysermc.common.form.Form; +import org.geysermc.floodgate.api.logger.FloodgateLogger; +import org.geysermc.floodgate.platform.pluginmessage.PluginMessageHandler; + +public class BungeePluginMessageHandler extends PluginMessageHandler implements Listener { + private ProxyServer proxy; + private FloodgateLogger logger; + private String formChannel; + + @Inject // called because this is a listener as well + public void init(Plugin plugin, FloodgateLogger logger, + @Named("formChannel") String formChannel, + @Named("skinChannel") String skinChannel) { + super.proxy = true; + this.proxy = plugin.getProxy(); + this.logger = logger; + this.formChannel = formChannel; + + proxy.registerChannel(formChannel); + proxy.registerChannel(skinChannel); + } + + @EventHandler + public void onPluginMessage(PluginMessageEvent event) { + Connection source = event.getSender(); + if (event.getTag().equals(formChannel)) { + if (source instanceof Server) { + // send it to the client + event.setCancelled(false); + return; + } + + if (source instanceof ProxiedPlayer) { + byte[] data = event.getData(); + if (data.length < 2) { + logger.error("Invalid form response! Closing connection"); + source.disconnect(new TextComponent("Invalid form response!")); + return; + } + + short formId = getFormId(data); + + // if the bit is not set, it's for the connected server + if ((formId & 0x8000) == 0) { + event.setCancelled(false); + return; + } + + event.setCancelled(true); + + if (!callResponseConsumer(data)) { + logger.error(format( + "Couldn't find stored form with id {} for player {}", + formId, ((ProxiedPlayer) source).getName())); + } + } + } + } + + @Override + public boolean sendForm(UUID uuid, Form form) { + ProxiedPlayer player = proxy.getPlayer(uuid); + if (player != null) { + player.sendData(formChannel, createFormData(form)); + return true; + } + return false; + } +} diff --git a/common/pom.xml b/common/pom.xml index b803f004..0b92f849 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -53,6 +53,17 @@ jackson-dataformat-yaml 2.9.9 + + com.nukkitx.fastutil + fastutil-short-object-maps + 8.3.1 + compile + + + + + + 4.0.0 @@ -84,5 +95,10 @@ true + + minecraft-libraries + Minecraft Libraries + https://libraries.minecraft.net + \ No newline at end of file diff --git a/common/src/main/java/org/geysermc/floodgate/FloodgatePlatform.java b/common/src/main/java/org/geysermc/floodgate/FloodgatePlatform.java index b778dcf0..3e86f14b 100644 --- a/common/src/main/java/org/geysermc/floodgate/FloodgatePlatform.java +++ b/common/src/main/java/org/geysermc/floodgate/FloodgatePlatform.java @@ -111,7 +111,7 @@ public class FloodgatePlatform { return false; } - guice.createChildInjector(new PostInitializeModule(postInitializeModules)); + this.guice = guice.createChildInjector(new PostInitializeModule(postInitializeModules)); return true; } diff --git a/common/src/main/java/org/geysermc/floodgate/api/SimpleFloodgateApi.java b/common/src/main/java/org/geysermc/floodgate/api/SimpleFloodgateApi.java index 7b9b9a83..f11094bb 100644 --- a/common/src/main/java/org/geysermc/floodgate/api/SimpleFloodgateApi.java +++ b/common/src/main/java/org/geysermc/floodgate/api/SimpleFloodgateApi.java @@ -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 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); } diff --git a/common/src/main/java/org/geysermc/floodgate/command/CommonCommandMessage.java b/common/src/main/java/org/geysermc/floodgate/command/CommonCommandMessage.java index 26b0f499..545047b4 100644 --- a/common/src/main/java/org/geysermc/floodgate/command/CommonCommandMessage.java +++ b/common/src/main/java/org/geysermc/floodgate/command/CommonCommandMessage.java @@ -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"); diff --git a/common/src/main/java/org/geysermc/floodgate/command/LinkAccountCommand.java b/common/src/main/java/org/geysermc/floodgate/command/LinkAccountCommand.java index 0daf65a0..be201811 100644 --- a/common/src/main/java/org/geysermc/floodgate/command/LinkAccountCommand.java +++ b/common/src/main/java/org/geysermc/floodgate/command/LinkAccountCommand.java @@ -47,6 +47,17 @@ public final class LinkAccountCommand implements Command { @Inject private FloodgateApi api; @Inject private CommandUtil commandUtil; +// @Override todo impl this +// public LiteralCommandNode 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"; diff --git a/common/src/main/java/org/geysermc/floodgate/command/UnlinkAccountCommand.java b/common/src/main/java/org/geysermc/floodgate/command/UnlinkAccountCommand.java index 86edb15d..eda3c621 100644 --- a/common/src/main/java/org/geysermc/floodgate/command/UnlinkAccountCommand.java +++ b/common/src/main/java/org/geysermc/floodgate/command/UnlinkAccountCommand.java @@ -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"; diff --git a/common/src/main/java/org/geysermc/floodgate/module/CommonModule.java b/common/src/main/java/org/geysermc/floodgate/module/CommonModule.java index de575dcc..6bcf3edc 100644 --- a/common/src/main/java/org/geysermc/floodgate/module/CommonModule.java +++ b/common/src/main/java/org/geysermc/floodgate/module/CommonModule.java @@ -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 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"; + } } diff --git a/common/src/main/java/org/geysermc/floodgate/platform/command/Command.java b/common/src/main/java/org/geysermc/floodgate/platform/command/Command.java index 338c5134..c3b0e4b7 100644 --- a/common/src/main/java/org/geysermc/floodgate/platform/command/Command.java +++ b/common/src/main/java/org/geysermc/floodgate/platform/command/Command.java @@ -31,6 +31,8 @@ import java.util.UUID; * The base class for every Floodgate command. */ public interface Command { +// LiteralCommandNode 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. diff --git a/common/src/main/java/org/geysermc/floodgate/platform/pluginmessage/PluginMessageHandler.java b/common/src/main/java/org/geysermc/floodgate/platform/pluginmessage/PluginMessageHandler.java new file mode 100644 index 00000000..0b1e448b --- /dev/null +++ b/common/src/main/java/org/geysermc/floodgate/platform/pluginmessage/PluginMessageHandler.java @@ -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
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); + } +} diff --git a/common/src/main/java/org/geysermc/floodgate/util/LanguageManager.java b/common/src/main/java/org/geysermc/floodgate/util/LanguageManager.java index 9850b59e..15062c79 100644 --- a/common/src/main/java/org/geysermc/floodgate/util/LanguageManager.java +++ b/common/src/main/java/org/geysermc/floodgate/util/LanguageManager.java @@ -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 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); + } } diff --git a/common/src/main/java/org/geysermc/floodgate/util/ReflectionUtils.java b/common/src/main/java/org/geysermc/floodgate/util/ReflectionUtils.java index 4184c585..b5f20184 100644 --- a/common/src/main/java/org/geysermc/floodgate/util/ReflectionUtils.java +++ b/common/src/main/java/org/geysermc/floodgate/util/ReflectionUtils.java @@ -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 . * @@ -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 . * - * @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 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 getCastedValue(Object instance, String fieldName) { + return (T) getValue(instance, getField(instance.getClass(), fieldName)); } /** diff --git a/pom.xml b/pom.xml index 97efc64d..3f6193aa 100644 --- a/pom.xml +++ b/pom.xml @@ -47,7 +47,7 @@ 1.1.0 1.8.8-R0.1-SNAPSHOT 1.15-SNAPSHOT - 1.1.0-SNAPSHOT + 1.1.0 floodgate-${project.name} UTF-8 diff --git a/spigot/src/main/java/org/geysermc/floodgate/command/SpigotCommandRegistration.java b/spigot/src/main/java/org/geysermc/floodgate/command/SpigotCommandRegistration.java index 3158b89a..902e806f 100644 --- a/spigot/src/main/java/org/geysermc/floodgate/command/SpigotCommandRegistration.java +++ b/spigot/src/main/java/org/geysermc/floodgate/command/SpigotCommandRegistration.java @@ -25,56 +25,74 @@ package org.geysermc.floodgate.command; -import lombok.RequiredArgsConstructor; -import org.bukkit.command.CommandExecutor; +import java.util.Collections; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandException; +import org.bukkit.command.CommandMap; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.plugin.java.JavaPlugin; import org.geysermc.floodgate.platform.command.Command; import org.geysermc.floodgate.platform.command.CommandRegistration; import org.geysermc.floodgate.platform.command.CommandUtil; -import org.geysermc.floodgate.util.LanguageManager; +import org.geysermc.floodgate.util.ReflectionUtils; -@RequiredArgsConstructor public final class SpigotCommandRegistration implements CommandRegistration { private final JavaPlugin plugin; private final CommandUtil commandUtil; - private final LanguageManager languageManager; + private final CommandMap commandMap; + + public SpigotCommandRegistration(JavaPlugin plugin, CommandUtil commandUtil) { + this.plugin = plugin; + this.commandUtil = commandUtil; + this.commandMap = ReflectionUtils.getCastedValue(Bukkit.getPluginManager(), "commandMap"); + } @Override public void register(Command command) { - String defaultLocale = languageManager.getDefaultLocale(); - - plugin.getCommand(command.getName()) - .setExecutor(new SpigotCommandWrapper(commandUtil, command, defaultLocale)); + commandMap.register("floodgate", new SpigotCommand(plugin, commandUtil, command)); } - @RequiredArgsConstructor - protected static class SpigotCommandWrapper implements CommandExecutor { + protected static class SpigotCommand extends org.bukkit.command.Command { + private final JavaPlugin plugin; private final CommandUtil commandUtil; private final Command command; - private final String defaultLocale; + + protected SpigotCommand(JavaPlugin plugin, CommandUtil commandUtil, Command command) { + super(command.getName(), command.getDescription(), "", Collections.emptyList()); + this.plugin = plugin; + this.commandUtil = commandUtil; + this.command = command; + } @Override - public boolean onCommand(CommandSender source, org.bukkit.command.Command cmd, - String label, String[] args) { - if (!(source instanceof Player)) { - if (command.isRequirePlayer()) { - commandUtil.sendMessage( - source, defaultLocale, - CommonCommandMessage.NOT_A_PLAYER - ); - return true; - } - command.execute(source, defaultLocale, args); + public boolean execute(CommandSender sender, String commandLabel, String[] args) { + if (!plugin.isEnabled()) { + throw new CommandException("Cannot execute command '" + commandLabel + + "' while the plugin is disabled"); + } + if (!sender.hasPermission(command.getPermission())) { + commandUtil.sendMessage(sender, null, CommonCommandMessage.NO_PERMISSION); return true; } + executeCommand(sender, args); + return true; + } - Player player = (Player) source; + public void executeCommand(CommandSender sender, String[] args) { + if (!(sender instanceof Player)) { + if (command.isRequirePlayer()) { + commandUtil.sendMessage(sender, null, CommonCommandMessage.NOT_A_PLAYER); + return; + } + command.execute(sender, null, args); + return; + } + + Player player = (Player) sender; String locale = player.spigot().getLocale(); - command.execute(source, player.getUniqueId(), source.getName(), locale, args); - return true; + command.execute(sender, player.getUniqueId(), sender.getName(), locale, args); } } } diff --git a/spigot/src/main/java/org/geysermc/floodgate/module/SpigotPlatformModule.java b/spigot/src/main/java/org/geysermc/floodgate/module/SpigotPlatformModule.java index cbd6d918..6318ce86 100644 --- a/spigot/src/main/java/org/geysermc/floodgate/module/SpigotPlatformModule.java +++ b/spigot/src/main/java/org/geysermc/floodgate/module/SpigotPlatformModule.java @@ -42,8 +42,10 @@ import org.geysermc.floodgate.inject.spigot.SpigotInjector; import org.geysermc.floodgate.listener.SpigotListenerRegistration; import org.geysermc.floodgate.logger.JavaUtilFloodgateLogger; import org.geysermc.floodgate.platform.command.CommandRegistration; -import org.geysermc.floodgate.platform.listener.ListenerRegistration; import org.geysermc.floodgate.platform.command.CommandUtil; +import org.geysermc.floodgate.platform.listener.ListenerRegistration; +import org.geysermc.floodgate.platform.pluginmessage.PluginMessageHandler; +import org.geysermc.floodgate.pluginmessage.BukkitPluginMessageHandler; import org.geysermc.floodgate.util.LanguageManager; import org.geysermc.floodgate.util.SpigotCommandUtil; @@ -66,8 +68,8 @@ public final class SpigotPlatformModule extends AbstractModule { @Provides @Singleton - public SimpleFloodgateApi floodgateApi() { - return new SimpleFloodgateApi(); + public SimpleFloodgateApi floodgateApi(PluginMessageHandler pluginMessageHandler) { + return new SimpleFloodgateApi(pluginMessageHandler); } @Provides @@ -82,9 +84,8 @@ public final class SpigotPlatformModule extends AbstractModule { @Provides @Singleton - public CommandRegistration commandRegistration(CommandUtil commandUtil, - LanguageManager languageManager) { - return new SpigotCommandRegistration(plugin, commandUtil, languageManager); + public CommandRegistration commandRegistration(CommandUtil commandUtil) { + return new SpigotCommandRegistration(plugin, commandUtil); } @Provides @@ -99,6 +100,13 @@ public final class SpigotPlatformModule extends AbstractModule { return new SpigotListenerRegistration(plugin); } + @Provides + @Singleton + public PluginMessageHandler pluginMessageHandler(@Named("formChannel") String formChannel, + @Named("skinChannel") String skinChannel) { + return new BukkitPluginMessageHandler(plugin, formChannel, skinChannel); + } + /* DebugAddon / PlatformInjector */ diff --git a/spigot/src/main/java/org/geysermc/floodgate/pluginmessage/BukkitPluginMessageHandler.java b/spigot/src/main/java/org/geysermc/floodgate/pluginmessage/BukkitPluginMessageHandler.java new file mode 100644 index 00000000..7c88fab0 --- /dev/null +++ b/spigot/src/main/java/org/geysermc/floodgate/pluginmessage/BukkitPluginMessageHandler.java @@ -0,0 +1,85 @@ +/* + * 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.pluginmessage; + +import java.util.Set; +import java.util.UUID; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.plugin.messaging.Messenger; +import org.geysermc.common.form.Form; +import org.geysermc.floodgate.api.FloodgateApi; +import org.geysermc.floodgate.platform.pluginmessage.PluginMessageHandler; +import org.geysermc.floodgate.util.ReflectionUtils; + +public class BukkitPluginMessageHandler extends PluginMessageHandler { + private final JavaPlugin plugin; + private final String formChannel; + private final String skinChannel; + + public BukkitPluginMessageHandler(JavaPlugin plugin, String formChannel, String skinChannel) { + this.plugin = plugin; + this.formChannel = formChannel; + this.skinChannel = skinChannel; + + Messenger messenger = plugin.getServer().getMessenger(); + + // form + messenger.registerIncomingPluginChannel( + plugin, formChannel, + (channel, player, message) -> callResponseConsumer(message)); + messenger.registerOutgoingPluginChannel(plugin, formChannel); + + // skin + messenger.registerIncomingPluginChannel( + plugin, skinChannel, + (channel, player, message) -> { + String origin = + FloodgateApi.getInstance().getPlayer(player.getUniqueId()) != null + ? "Geyser" : "player"; + System.out.println("Got skin from " + origin + "!"); + } + ); + } + + @Override + public boolean sendForm(UUID playerId, Form form) { + try { + Player player = Bukkit.getPlayer(playerId); + //todo improve + Set channels = ReflectionUtils.getCastedValue(player, "channels"); + if (channels != null) { + channels.add("floodgate:form"); + } + player.sendPluginMessage(plugin, formChannel, createFormData(form)); + } catch (Exception exception) { + exception.printStackTrace(); + return false; + } + return true; + } +} diff --git a/spigot/src/main/resources/plugin.yml b/spigot/src/main/resources/plugin.yml index aa216a9d..972691db 100644 --- a/spigot/src/main/resources/plugin.yml +++ b/spigot/src/main/resources/plugin.yml @@ -4,7 +4,4 @@ version: ${project.version} author: ${project.organization.name} website: ${project.url} main: org.geysermc.floodgate.SpigotPlugin -api-version: 1.13 -commands: #todo maybe add the commands directly to the commandmap? - linkaccount: - unlinkaccount: \ No newline at end of file +api-version: 1.13 \ No newline at end of file diff --git a/velocity/src/main/java/org/geysermc/floodgate/command/VelocityCommandRegistration.java b/velocity/src/main/java/org/geysermc/floodgate/command/VelocityCommandRegistration.java index 01f5ba79..0d6400ea 100644 --- a/velocity/src/main/java/org/geysermc/floodgate/command/VelocityCommandRegistration.java +++ b/velocity/src/main/java/org/geysermc/floodgate/command/VelocityCommandRegistration.java @@ -25,12 +25,14 @@ package org.geysermc.floodgate.command; +import static org.geysermc.floodgate.command.CommonCommandMessage.NOT_A_PLAYER; + import com.velocitypowered.api.command.CommandManager; import com.velocitypowered.api.command.CommandSource; +import com.velocitypowered.api.command.RawCommand; import com.velocitypowered.api.proxy.Player; import java.util.Locale; import lombok.RequiredArgsConstructor; -import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.floodgate.platform.command.Command; import org.geysermc.floodgate.platform.command.CommandRegistration; import org.geysermc.floodgate.platform.command.CommandUtil; @@ -48,26 +50,26 @@ public final class VelocityCommandRegistration implements CommandRegistration { commandManager.register(command.getName(), new VelocityCommandWrapper(commandUtil, command, defaultLocale)); + +// BrigadierCommand cmd = new BrigadierCommand(); } @RequiredArgsConstructor - protected static final class VelocityCommandWrapper - implements com.velocitypowered.api.command.Command { + protected static final class VelocityCommandWrapper implements RawCommand { private final CommandUtil commandUtil; private final Command command; private final String defaultLocale; @Override - public void execute(CommandSource source, @NonNull String[] args) { + public void execute(Invocation invocation) { + CommandSource source = invocation.source(); + if (!(source instanceof Player)) { if (command.isRequirePlayer()) { - commandUtil.sendMessage( - source, defaultLocale, - CommonCommandMessage.NOT_A_PLAYER - ); + commandUtil.sendMessage(source, defaultLocale, NOT_A_PLAYER); return; } - command.execute(source, defaultLocale, args); + command.execute(source, defaultLocale, invocation.arguments().split(" ")); return; } @@ -75,7 +77,10 @@ public final class VelocityCommandRegistration implements CommandRegistration { Locale locale = player.getPlayerSettings().getLocale(); String localeString = locale.getLanguage() + "_" + locale.getCountry(); - command.execute(source, player.getUniqueId(), player.getUsername(), localeString, args); + command.execute( + source, player.getUniqueId(), player.getUsername(), + localeString, invocation.arguments().split(" ") + ); } } } diff --git a/velocity/src/main/java/org/geysermc/floodgate/inject/velocity/VelocityInjector.java b/velocity/src/main/java/org/geysermc/floodgate/inject/velocity/VelocityInjector.java index 56c74cc8..ebc85394 100644 --- a/velocity/src/main/java/org/geysermc/floodgate/inject/velocity/VelocityInjector.java +++ b/velocity/src/main/java/org/geysermc/floodgate/inject/velocity/VelocityInjector.java @@ -60,7 +60,7 @@ public final class VelocityInjector extends CommonPlatformInjector { Method serverSetter = getMethod(serverInitializerHolder, "set", ChannelInitializer.class); invoke(serverInitializerHolder, serverSetter, - new VelocityChannelInitializer(this, serverInitializer, false)); + new VelocityChannelInitializer(this, serverInitializer, false)); // Proxy <-> Server @@ -69,7 +69,7 @@ public final class VelocityInjector extends CommonPlatformInjector { Method backendSetter = getMethod(backendInitializerHolder, "set", ChannelInitializer.class); invoke(backendInitializerHolder, backendSetter, - new VelocityChannelInitializer(this, backendInitializer, true)); + new VelocityChannelInitializer(this, backendInitializer, true)); return injected = true; } diff --git a/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityListener.java b/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityListener.java index ea146254..518e0325 100644 --- a/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityListener.java +++ b/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityListener.java @@ -48,7 +48,7 @@ import io.netty.util.AttributeKey; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.concurrent.TimeUnit; -import net.kyori.adventure.text.TextComponent; +import net.kyori.adventure.text.Component; import org.geysermc.floodgate.api.ProxyFloodgateApi; import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.api.player.FloodgatePlayer; @@ -104,7 +104,7 @@ public final class VelocityListener { if (kickMessage != null) { event.setResult( - PreLoginEvent.PreLoginComponentResult.denied(TextComponent.of(kickMessage)) + PreLoginEvent.PreLoginComponentResult.denied(Component.text(kickMessage)) ); return; } diff --git a/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityPluginMessageListener.java b/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityPluginMessageListener.java new file mode 100644 index 00000000..2e1f2888 --- /dev/null +++ b/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityPluginMessageListener.java @@ -0,0 +1,113 @@ +/* + * 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.listener; + +import static org.geysermc.floodgate.util.MessageFormatter.format; + +import com.google.inject.Inject; +import com.google.inject.name.Named; +import com.velocitypowered.api.event.Subscribe; +import com.velocitypowered.api.event.connection.PluginMessageEvent; +import com.velocitypowered.api.event.connection.PluginMessageEvent.ForwardResult; +import com.velocitypowered.api.proxy.Player; +import com.velocitypowered.api.proxy.ProxyServer; +import com.velocitypowered.api.proxy.ServerConnection; +import com.velocitypowered.api.proxy.messages.ChannelIdentifier; +import com.velocitypowered.api.proxy.messages.ChannelMessageSource; +import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier; +import java.util.UUID; +import net.kyori.adventure.text.Component; +import org.geysermc.common.form.Form; +import org.geysermc.floodgate.api.logger.FloodgateLogger; +import org.geysermc.floodgate.platform.pluginmessage.PluginMessageHandler; + +//todo naming of this class is different between platforms +public class VelocityPluginMessageListener extends PluginMessageHandler { + private ProxyServer proxy; + private FloodgateLogger logger; + private ChannelIdentifier formChannel; + + @Inject // called because this is a listener as well + public void init(ProxyServer proxy, FloodgateLogger logger, + @Named("formChannel") String formChannelName, + @Named("skinChannel") String skinChannelName) { + super.proxy = true; + this.proxy = proxy; + this.logger = logger; + + formChannel = MinecraftChannelIdentifier.from(formChannelName); + + proxy.getChannelRegistrar().register(formChannel); + proxy.getChannelRegistrar().register(MinecraftChannelIdentifier.from(skinChannelName)); + } + + @Subscribe + public void onPluginMessage(PluginMessageEvent event) { + ChannelMessageSource source = event.getSource(); + + if (event.getIdentifier().equals(formChannel)) { + if (source instanceof ServerConnection) { + // send it to the client + event.setResult(ForwardResult.forward()); + return; + } + + if (source instanceof Player) { + byte[] data = event.getData(); + if (data.length < 2) { + logger.error("Invalid form response! Closing connection"); + ((Player) source).disconnect(Component.text("Invalid form response!")); + return; + } + + short formId = getFormId(data); + + // if the bit is not set, it's for the connected server + if ((formId & 0x8000) == 0) { + event.setResult(ForwardResult.forward()); + return; + } + + event.setResult(ForwardResult.handled()); + + if (!callResponseConsumer(data)) { + logger.error(format( + "Couldn't find stored form with id {} for player {}", + formId, ((Player) source).getUsername())); + } + } + } + + // proxies don't have to receive anything from the skins channel, they only have to send + } + + @Override + public boolean sendForm(UUID uuid, Form form) { + return proxy.getPlayer(uuid) + .map(value -> value.sendPluginMessage(formChannel, createFormData(form))) + .orElse(false); + } +} diff --git a/velocity/src/main/java/org/geysermc/floodgate/module/VelocityListenerModule.java b/velocity/src/main/java/org/geysermc/floodgate/module/VelocityListenerModule.java index 49409681..ae110a24 100644 --- a/velocity/src/main/java/org/geysermc/floodgate/module/VelocityListenerModule.java +++ b/velocity/src/main/java/org/geysermc/floodgate/module/VelocityListenerModule.java @@ -30,6 +30,7 @@ import com.google.inject.Singleton; import com.google.inject.TypeLiteral; import com.google.inject.multibindings.ProvidesIntoSet; import org.geysermc.floodgate.listener.VelocityListener; +import org.geysermc.floodgate.platform.pluginmessage.PluginMessageHandler; import org.geysermc.floodgate.register.ListenerRegister; public final class VelocityListenerModule extends AbstractModule { @@ -43,4 +44,10 @@ public final class VelocityListenerModule extends AbstractModule { public Object velocityListener() { return new VelocityListener(); } + + @Singleton + @ProvidesIntoSet + public Object pluginMessageListener(PluginMessageHandler handler) { + return handler; // Plugin message handler is also the listener + } } diff --git a/velocity/src/main/java/org/geysermc/floodgate/module/VelocityPlatformModule.java b/velocity/src/main/java/org/geysermc/floodgate/module/VelocityPlatformModule.java index 9ee6fc27..04e99dbd 100644 --- a/velocity/src/main/java/org/geysermc/floodgate/module/VelocityPlatformModule.java +++ b/velocity/src/main/java/org/geysermc/floodgate/module/VelocityPlatformModule.java @@ -44,10 +44,12 @@ import org.geysermc.floodgate.crypto.FloodgateCipher; import org.geysermc.floodgate.inject.CommonPlatformInjector; import org.geysermc.floodgate.inject.velocity.VelocityInjector; import org.geysermc.floodgate.listener.VelocityListenerRegistration; +import org.geysermc.floodgate.listener.VelocityPluginMessageListener; import org.geysermc.floodgate.logger.Slf4jFloodgateLogger; import org.geysermc.floodgate.platform.command.CommandRegistration; import org.geysermc.floodgate.platform.command.CommandUtil; import org.geysermc.floodgate.platform.listener.ListenerRegistration; +import org.geysermc.floodgate.platform.pluginmessage.PluginMessageHandler; import org.geysermc.floodgate.util.LanguageManager; import org.geysermc.floodgate.util.VelocityCommandUtil; import org.slf4j.Logger; @@ -68,8 +70,9 @@ public final class VelocityPlatformModule extends AbstractModule { @Provides @Singleton - public ProxyFloodgateApi proxyFloodgateApi(FloodgateCipher cipher) { - return new ProxyFloodgateApi(cipher); + public ProxyFloodgateApi proxyFloodgateApi(PluginMessageHandler pluginMessageHandler, + FloodgateCipher cipher) { + return new ProxyFloodgateApi(pluginMessageHandler, cipher); } @Provides @@ -104,6 +107,12 @@ public final class VelocityPlatformModule extends AbstractModule { return new VelocityListenerRegistration(eventManager, plugin); } + @Provides + @Singleton + public PluginMessageHandler pluginMessageHandler() { + return new VelocityPluginMessageListener(); + } + /* DebugAddon / PlatformInjector */ diff --git a/velocity/src/main/java/org/geysermc/floodgate/util/VelocityCommandUtil.java b/velocity/src/main/java/org/geysermc/floodgate/util/VelocityCommandUtil.java index 9d392179..4dc2be0e 100644 --- a/velocity/src/main/java/org/geysermc/floodgate/util/VelocityCommandUtil.java +++ b/velocity/src/main/java/org/geysermc/floodgate/util/VelocityCommandUtil.java @@ -27,7 +27,7 @@ package org.geysermc.floodgate.util; import com.velocitypowered.api.proxy.Player; import lombok.RequiredArgsConstructor; -import net.kyori.adventure.text.TextComponent; +import net.kyori.adventure.text.Component; import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.platform.command.CommandMessage; import org.geysermc.floodgate.platform.command.CommandUtil; @@ -47,9 +47,9 @@ public final class VelocityCommandUtil implements CommandUtil { cast(player).disconnect(translateAndTransform(locale, message, args)); } - public TextComponent translateAndTransform(String locale, CommandMessage message, - Object... args) { - return TextComponent.of(manager.getString(message.getMessage(), locale, args)); + public Component translateAndTransform(String locale, CommandMessage message, + Object... args) { + return Component.text(manager.getString(message.getMessage(), locale, args)); } protected Player cast(Object instance) {