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 c6e31c55..f1ba3e17 100644 --- a/api/src/main/java/org/geysermc/floodgate/api/FloodgateApi.java +++ b/api/src/main/java/org/geysermc/floodgate/api/FloodgateApi.java @@ -95,6 +95,8 @@ public interface FloodgateApi { boolean sendForm(UUID uuid, FormBuilder formBuilder); + boolean transferPlayer(UUID uuid, String address, int port); + /** * Get the xuid of the user that has the given gamertag. * 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 07290cff..51e806f2 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 @@ -119,6 +119,10 @@ public interface FloodgatePlayer { return sendForm(formBuilder.build()); } + default boolean transfer(String address, int port) { + return FloodgateApi.getInstance().transferPlayer(getCorrectUniqueId(), address, port); + } + boolean hasProperty(PropertyKey key); boolean hasProperty(String key); diff --git a/bungee/src/main/java/org/geysermc/floodgate/pluginmessage/BungeePluginMessageUtils.java b/bungee/src/main/java/org/geysermc/floodgate/pluginmessage/BungeePluginMessageUtils.java index 6e72b89f..693a318f 100644 --- a/bungee/src/main/java/org/geysermc/floodgate/pluginmessage/BungeePluginMessageUtils.java +++ b/bungee/src/main/java/org/geysermc/floodgate/pluginmessage/BungeePluginMessageUtils.java @@ -28,6 +28,7 @@ package org.geysermc.floodgate.pluginmessage; import java.util.UUID; import lombok.RequiredArgsConstructor; import net.md_5.bungee.ServerConnection; +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; @@ -96,4 +97,19 @@ public final class BungeePluginMessageUtils extends PluginMessageUtils implement logger.error(reason + " Closing connection"); source.disconnect(new TextComponent(reason)); } + + @Override + public boolean sendMessage(UUID player, boolean toServer, String channel, byte[] data) { + ProxiedPlayer proxiedPlayer = ProxyServer.getInstance().getPlayer(player); + if (proxiedPlayer == null) { + return false; + } + + if (toServer) { + proxiedPlayer.getServer().sendData(channel, data); + } else { + proxiedPlayer.sendData(channel, data); + } + 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 33e930cb..4ef41101 100644 --- a/common/src/main/java/org/geysermc/floodgate/api/SimpleFloodgateApi.java +++ b/common/src/main/java/org/geysermc/floodgate/api/SimpleFloodgateApi.java @@ -41,6 +41,7 @@ import org.geysermc.floodgate.config.FloodgateConfigHolder; import org.geysermc.floodgate.player.FloodgatePlayerImpl; import org.geysermc.floodgate.pluginmessage.PluginMessageManager; import org.geysermc.floodgate.pluginmessage.channel.FormChannel; +import org.geysermc.floodgate.pluginmessage.channel.TransferChannel; import org.geysermc.floodgate.util.Constants; import org.geysermc.floodgate.util.HttpUtils; import org.geysermc.floodgate.util.Utils; @@ -109,6 +110,13 @@ public class SimpleFloodgateApi implements FloodgateApi { return sendForm(uuid, formBuilder.build()); } + @Override + public boolean transferPlayer(UUID uuid, String address, int port) { + return pluginMessageManager + .getChannel(TransferChannel.class) + .sendTransfer(uuid, address, port); + } + @Override public CompletableFuture getXuidFor(String gamertag) { if (gamertag == null || gamertag.isEmpty() || gamertag.length() > 16) { diff --git a/common/src/main/java/org/geysermc/floodgate/module/PluginMessageModule.java b/common/src/main/java/org/geysermc/floodgate/module/PluginMessageModule.java index a9a613f2..282667d8 100644 --- a/common/src/main/java/org/geysermc/floodgate/module/PluginMessageModule.java +++ b/common/src/main/java/org/geysermc/floodgate/module/PluginMessageModule.java @@ -31,6 +31,7 @@ import com.google.inject.multibindings.ProvidesIntoSet; import org.geysermc.floodgate.pluginmessage.PluginMessageChannel; import org.geysermc.floodgate.pluginmessage.channel.FormChannel; import org.geysermc.floodgate.pluginmessage.channel.SkinChannel; +import org.geysermc.floodgate.pluginmessage.channel.TransferChannel; import org.geysermc.floodgate.register.PluginMessageRegister; public final class PluginMessageModule extends AbstractModule { @@ -50,4 +51,10 @@ public final class PluginMessageModule extends AbstractModule { public PluginMessageChannel skinChannel() { return new SkinChannel(); } + + @Singleton + @ProvidesIntoSet + public PluginMessageChannel transferChannel() { + return new TransferChannel(); + } } diff --git a/common/src/main/java/org/geysermc/floodgate/pluginmessage/channel/TransferChannel.java b/common/src/main/java/org/geysermc/floodgate/pluginmessage/channel/TransferChannel.java new file mode 100644 index 00000000..9051bcde --- /dev/null +++ b/common/src/main/java/org/geysermc/floodgate/pluginmessage/channel/TransferChannel.java @@ -0,0 +1,81 @@ +/* + * 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.pluginmessage.channel; + +import com.google.inject.Inject; +import java.nio.charset.StandardCharsets; +import java.util.UUID; +import org.geysermc.floodgate.platform.pluginmessage.PluginMessageUtils; +import org.geysermc.floodgate.pluginmessage.PluginMessageChannel; + +public class TransferChannel implements PluginMessageChannel { + @Inject private PluginMessageUtils pluginMessageUtils; + + @Override + public String getIdentifier() { + return "floodgate:transfer"; + } + + @Override + public Result handleProxyCall( + byte[] data, + UUID targetUuid, + String targetUsername, + Identity targetIdentity, + UUID sourceUuid, + String sourceUsername, + Identity sourceIdentity) { + + if (sourceIdentity == Identity.SERVER) { + // send it to the client + return Result.forward(); + } + + if (sourceIdentity == Identity.PLAYER) { + handleServerCall(data, targetUuid, targetUsername); + } + + return Result.handled(); + } + + @Override + public Result handleServerCall(byte[] data, UUID targetUuid, String targetUsername) { + return Result.kick("I'm sorry, I'm unable to transfer a server :("); + } + + public boolean sendTransfer(UUID player, String address, int port) { + byte[] addressBytes = address.getBytes(StandardCharsets.UTF_8); + byte[] data = new byte[addressBytes.length + 4]; + + data[0] = (byte) (port >> 24); + data[1] = (byte) (port >> 16); + data[2] = (byte) (port >> 8); + data[3] = (byte) (port); + System.arraycopy(addressBytes, 0, data, 4, addressBytes.length); + + return pluginMessageUtils.sendMessage(player, false, getIdentifier(), data); + } +}