diff --git a/.gitmodules b/.gitmodules index 074c6a2c..e69de29b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +0,0 @@ -[submodule "core/src/main/resources/languages"] - path = core/src/main/resources/languages - url = https://github.com/GeyserMC/languages - branch = l10n_floodgate diff --git a/core/src/main/java/org/geysermc/floodgate/FloodgatePlatform.java b/core/src/main/java/org/geysermc/floodgate/FloodgatePlatform.java index bce37078..8da53815 100644 --- a/core/src/main/java/org/geysermc/floodgate/FloodgatePlatform.java +++ b/core/src/main/java/org/geysermc/floodgate/FloodgatePlatform.java @@ -139,6 +139,7 @@ public class FloodgatePlatform { } } + guice.getInstance(NewsChecker.class).shutdown(); api.getPlayerLink().stop(); return true; } diff --git a/core/src/main/java/org/geysermc/floodgate/player/FloodgateHandshakeHandler.java b/core/src/main/java/org/geysermc/floodgate/player/FloodgateHandshakeHandler.java index f112c26e..b551f82e 100644 --- a/core/src/main/java/org/geysermc/floodgate/player/FloodgateHandshakeHandler.java +++ b/core/src/main/java/org/geysermc/floodgate/player/FloodgateHandshakeHandler.java @@ -35,7 +35,6 @@ import io.netty.util.AttributeKey; import it.unimi.dsi.fastutil.Pair; import it.unimi.dsi.fastutil.objects.ObjectObjectImmutablePair; import java.net.InetSocketAddress; -import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import lombok.AccessLevel; @@ -218,8 +217,6 @@ public final class FloodgateHandshakeHandler { bedrockData.getVerifyCode()); } - correctHostname(handshakeData); - FloodgatePlayer player = FloodgatePlayerImpl.from(bedrockData, handshakeData); api.addPlayer(player); @@ -247,30 +244,9 @@ public final class FloodgateHandshakeHandler { bedrockData, configHolder.get(), null, hostname); handshakeHandlers.callHandshakeHandlers(handshakeData); - if (bedrockData != null) { - correctHostname(handshakeData); - } - return new HandshakeResult(resultType, handshakeData, bedrockData, null); } - private void correctHostname(HandshakeData handshakeData) { - BedrockData bedrockData = handshakeData.getBedrockData(); - UUID correctUuid = handshakeData.getCorrectUniqueId(); - - // replace the ip and uuid with the Bedrock client IP and an uuid based of the xuid - String[] split = handshakeData.getHostname().split("\0"); - if (split.length >= 3) { - if (logger.isDebug()) { - logger.info("Replacing hostname arg1 '{}' with '{}' and arg2 '{}' with '{}'", - split[1], bedrockData.getIp(), split[2], correctUuid.toString()); - } - split[1] = bedrockData.getIp(); - split[2] = correctUuid.toString(); - } - handshakeData.setHostname(String.join("\0", split)); - } - private CompletableFuture> fetchLinkedPlayer(BedrockData data) { if (!api.getPlayerLink().isEnabled()) { return CompletableFuture.completedFuture(new ObjectObjectImmutablePair<>(data, null)); diff --git a/core/src/main/resources/languages b/core/src/main/resources/languages deleted file mode 160000 index d08abbfa..00000000 --- a/core/src/main/resources/languages +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d08abbfab6e15bee15ba356ba4aa2a7ac0e329ce diff --git a/spigot/src/main/java/org/geysermc/floodgate/SpigotPlugin.java b/spigot/src/main/java/org/geysermc/floodgate/SpigotPlugin.java index 8fe74af9..677e2f86 100644 --- a/spigot/src/main/java/org/geysermc/floodgate/SpigotPlugin.java +++ b/spigot/src/main/java/org/geysermc/floodgate/SpigotPlugin.java @@ -28,6 +28,7 @@ package org.geysermc.floodgate; import com.google.inject.Guice; import com.google.inject.Injector; import org.bukkit.plugin.java.JavaPlugin; +import org.geysermc.floodgate.api.handshake.HandshakeHandlers; import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.module.PluginMessageModule; import org.geysermc.floodgate.module.ServerCommonModule; @@ -35,6 +36,7 @@ import org.geysermc.floodgate.module.SpigotAddonModule; import org.geysermc.floodgate.module.SpigotCommandModule; import org.geysermc.floodgate.module.SpigotListenerModule; import org.geysermc.floodgate.module.SpigotPlatformModule; +import org.geysermc.floodgate.util.SpigotHandshakeHandler; import org.geysermc.floodgate.util.SpigotProtocolSupportHandler; import org.geysermc.floodgate.util.SpigotProtocolSupportListener; @@ -66,6 +68,10 @@ public final class SpigotPlugin extends JavaPlugin { new PluginMessageModule() ); + //todo add proper support for disabling things on shutdown and enabling this on enable + injector.getInstance(HandshakeHandlers.class) + .addHandshakeHandler(injector.getInstance(SpigotHandshakeHandler.class)); + // add ProtocolSupport support (hack) if (getServer().getPluginManager().getPlugin("ProtocolSupport") != null) { injector.getInstance(SpigotProtocolSupportHandler.class); diff --git a/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java b/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java index 25eb6d24..bc5742db 100644 --- a/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java +++ b/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java @@ -42,6 +42,7 @@ import org.geysermc.floodgate.util.ProxyUtils; public final class SpigotDataHandler extends CommonDataHandler { private Object networkManager; private FloodgatePlayer player; + private boolean proxyData; public SpigotDataHandler( FloodgateHandshakeHandler handshakeHandler, @@ -52,7 +53,6 @@ public final class SpigotDataHandler extends CommonDataHandler { @Override protected void setNewIp(Channel channel, InetSocketAddress newIp) { - //todo the socket address will be overridden when bungeeData is true setValue(networkManager, ClassNames.SOCKET_ADDRESS, newIp); } @@ -76,20 +76,22 @@ public final class SpigotDataHandler extends CommonDataHandler { // the server will do all the work if BungeeCord mode is enabled, // otherwise we have to help the server. - boolean bungeeData = ProxyUtils.isProxyData(); + proxyData = ProxyUtils.isProxyData(); - if (!bungeeData) { + if (!proxyData) { // Use a spoofedUUID for initUUID (just like Bungeecord) setValue(networkManager, "spoofedUUID", player.getCorrectUniqueId()); } - return bungeeData; + // we can only remove the handler if the data is proxy data and username validation doesn't + // exist. Otherwise, we need to wait and disable it. + return proxyData && ClassNames.PAPER_DISABLE_USERNAME_VALIDATION == null; } @Override protected boolean shouldCallFireRead(Object queuedPacket) { - // we have to ignore the 'login start' packet if BungeeCord mode is disabled, - // otherwise the server might ask the user to login + // we have to ignore the 'login start' packet, + // otherwise the server will ask the user to login try { if (checkAndHandleLogin(queuedPacket)) { return false; @@ -136,6 +138,16 @@ public final class SpigotDataHandler extends CommonDataHandler { return true; } + if (ClassNames.PAPER_DISABLE_USERNAME_VALIDATION != null) { + // ensure that Paper will not be checking + setValue(packetListener, ClassNames.PAPER_DISABLE_USERNAME_VALIDATION, true); + if (proxyData) { + // the server will handle the rest if we have proxy data + ctx.pipeline().remove(this); + return false; + } + } + // set the player his GameProfile, we can't change the username without this GameProfile gameProfile = new GameProfile( player.getCorrectUniqueId(), player.getCorrectUsername() diff --git a/spigot/src/main/java/org/geysermc/floodgate/util/ClassNames.java b/spigot/src/main/java/org/geysermc/floodgate/util/ClassNames.java index 4479ea66..60d38a38 100644 --- a/spigot/src/main/java/org/geysermc/floodgate/util/ClassNames.java +++ b/spigot/src/main/java/org/geysermc/floodgate/util/ClassNames.java @@ -25,6 +25,7 @@ package org.geysermc.floodgate.util; +import static org.geysermc.floodgate.util.ReflectionUtils.getField; import static org.geysermc.floodgate.util.ReflectionUtils.getFieldOfType; import static org.geysermc.floodgate.util.ReflectionUtils.getMethod; @@ -37,6 +38,7 @@ import java.lang.reflect.Method; import java.net.SocketAddress; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; +import org.checkerframework.checker.nullness.qual.Nullable; @SuppressWarnings("PMD.SystemPrintln") public class ClassNames { @@ -56,6 +58,8 @@ public class ClassNames { public static final Field HANDSHAKE_HOST; public static final Field LOGIN_PROFILE; public static final Field PACKET_LISTENER; + @Nullable + public static final Field PAPER_DISABLE_USERNAME_VALIDATION; public static final Method GET_PROFILE_METHOD; public static final Method LOGIN_DISCONNECT; @@ -157,6 +161,13 @@ public class ClassNames { FIRE_LOGIN_EVENTS = getMethod(LOGIN_HANDLER, "fireEvents"); checkNotNull(FIRE_LOGIN_EVENTS, "fireEvents from LoginHandler"); + + PAPER_DISABLE_USERNAME_VALIDATION = getField(LOGIN_LISTENER, + "iKnowThisMayNotBeTheBestIdeaButPleaseDisableUsernameValidation"); + if (Constants.DEBUG_MODE) { + System.out.println("Paper disable username validation field exists? " + + (PAPER_DISABLE_USERNAME_VALIDATION != null)); + } } private static Class getClassOrFallBack(String className, String fallbackName) { diff --git a/spigot/src/main/java/org/geysermc/floodgate/util/ProxyUtils.java b/spigot/src/main/java/org/geysermc/floodgate/util/ProxyUtils.java index b788d518..55d2bb3d 100644 --- a/spigot/src/main/java/org/geysermc/floodgate/util/ProxyUtils.java +++ b/spigot/src/main/java/org/geysermc/floodgate/util/ProxyUtils.java @@ -66,4 +66,4 @@ public final class ProxyUtils { return ReflectionUtils.getCastedValue(null, IS_MODERN_FORWARDING); } -} +} \ No newline at end of file diff --git a/spigot/src/main/java/org/geysermc/floodgate/util/SpigotHandshakeHandler.java b/spigot/src/main/java/org/geysermc/floodgate/util/SpigotHandshakeHandler.java new file mode 100644 index 00000000..b425fb67 --- /dev/null +++ b/spigot/src/main/java/org/geysermc/floodgate/util/SpigotHandshakeHandler.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Floodgate + */ + +package org.geysermc.floodgate.util; + +import com.google.inject.Inject; +import java.util.UUID; +import org.geysermc.floodgate.api.handshake.HandshakeData; +import org.geysermc.floodgate.api.handshake.HandshakeHandler; +import org.geysermc.floodgate.api.logger.FloodgateLogger; + +public class SpigotHandshakeHandler implements HandshakeHandler { + @Inject + private FloodgateLogger logger; + + @Override + public void handle(HandshakeData data) { + // we never have to do anything when BedrockData is null. + // BedrockData is null when something went wrong (e.g. invalid key / exception) + if (data.getBedrockData() == null) { + return; + } + + BedrockData bedrockData = data.getBedrockData(); + UUID correctUuid = data.getCorrectUniqueId(); + + // replace the ip and uuid with the Bedrock client IP and an uuid based of the xuid + String[] split = data.getHostname().split("\0"); + if (split.length >= 3) { + if (logger.isDebug()) { + logger.info("Replacing hostname arg1 '{}' with '{}' and arg2 '{}' with '{}'", + split[1], bedrockData.getIp(), split[2], correctUuid.toString() + ); + } + split[1] = bedrockData.getIp(); + split[2] = correctUuid.toString(); + } + data.setHostname(String.join("\0", split)); + } +}