diff --git a/.github/workflows/pullrequest.yml b/.github/workflows/pullrequest.yml index 7d3232c4e..852330799 100644 --- a/.github/workflows/pullrequest.yml +++ b/.github/workflows/pullrequest.yml @@ -13,33 +13,33 @@ jobs: with: submodules: recursive - - name: Set up JDK 1.8 + - name: Set up JDK 17 uses: actions/setup-java@v2 with: distribution: 'temurin' - java-version: '8' + java-version: '17' cache: 'gradle' - name: Build with Maven run: ./gradlew build - name: Archive artifacts (Floodgate Bungee) - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 if: success() with: name: Floodgate Bungee path: bungee/build/libs/connect-bungee.jar - name: Archive artifacts (Floodgate Spigot) - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 if: success() with: name: Floodgate Spigot path: spigot/build/libs/connect-spigot.jar - name: Archive artifacts (Floodgate Velocity) - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 if: success() with: name: Floodgate Velocity - path: velocity/build/libs/connect-velocity.jar \ No newline at end of file + path: velocity/build/libs/connect-velocity.jar diff --git a/bungee/build.gradle.kts b/bungee/build.gradle.kts index 781e32d94..a5cc23e9b 100644 --- a/bungee/build.gradle.kts +++ b/bungee/build.gradle.kts @@ -1,4 +1,4 @@ -var bungeeVersion = "1.20-R0.3-SNAPSHOT" +var bungeeVersion = "1.21-R0.1-SNAPSHOT" var gsonVersion = "2.8.0" var guavaVersion = "21.0" diff --git a/bungee/src/main/java/com/minekube/connect/inject/bungee/BungeeInjector.java b/bungee/src/main/java/com/minekube/connect/inject/bungee/BungeeInjector.java index f9123f42e..0faf7a5a6 100644 --- a/bungee/src/main/java/com/minekube/connect/inject/bungee/BungeeInjector.java +++ b/bungee/src/main/java/com/minekube/connect/inject/bungee/BungeeInjector.java @@ -32,11 +32,7 @@ import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelOutboundHandlerAdapter; -import io.netty.channel.ChannelPromise; import io.netty.channel.EventLoopGroup; import io.netty.channel.local.LocalAddress; import io.netty.util.AttributeKey; @@ -46,18 +42,17 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.ProxyServer.Unsafe; import net.md_5.bungee.api.config.ListenerInfo; import net.md_5.bungee.api.event.ProxyReloadEvent; import net.md_5.bungee.api.plugin.Listener; import net.md_5.bungee.api.plugin.Plugin; import net.md_5.bungee.event.EventHandler; import net.md_5.bungee.netty.PipelineUtils; -import net.md_5.bungee.protocol.MinecraftEncoder; -import net.md_5.bungee.protocol.Varint21LengthFieldPrepender; +import net.md_5.bungee.protocol.channel.BungeeChannelInitializer; @RequiredArgsConstructor public final class BungeeInjector extends CommonPlatformInjector implements Listener { - private static final String BUNGEE_INIT = "connect-bungee-init"; private final ConnectLogger logger; private final ProxyServer proxy; @@ -69,17 +64,27 @@ public final class BungeeInjector extends CommonPlatformInjector implements List public boolean inject() { try { // Can everyone just switch to Velocity please :) + // :( ~BungeeCord Collaborator + + Unsafe unsafe = ProxyServer.getInstance().unsafe(); + BungeeChannelInitializer frontend = unsafe.getFrontendChannelInitializer(); + unsafe.setFrontendChannelInitializer(BungeeChannelInitializer.create(channel -> { + if (!frontend.getChannelAcceptor().accept(channel)) { + return false; + } + injectClient(channel, true); + return true; + })); + + BungeeChannelInitializer backend = unsafe.getBackendChannelInitializer(); + unsafe.setBackendChannelInitializer(BungeeChannelInitializer.create(channel -> { + if (!backend.getChannelAcceptor().accept(channel)) { + return false; + } + injectClient(channel, false); + return true; + })); -// Field framePrepender = ReflectionUtils.getField(PipelineUtils.class, "framePrepender"); -// -// // Required in order to inject into both Geyser <-> proxy AND proxy <-> server -// // (Instead of just replacing the ChannelInitializer which is only called for -// // player <-> proxy) -// BungeeCustomPrepender customPrepender = new BungeeCustomPrepender( -// this, ReflectionUtils.castedStaticValue(framePrepender) -// ); -// -// BungeeReflectionUtils.setFieldValue(null, framePrepender, customPrepender); initializeLocalChannel0(); injected = true; return true; @@ -129,6 +134,13 @@ private void initializeLocalChannel0() throws Exception { "Connect does not currently support multiple listeners with injection! " + "Please reach out to us on our Discord at https://minekube.com/discord so we can hear feedback on your setup."); } + + try { + ProxyServer.class.getMethod("unsafe"); + } catch (NoSuchMethodException e) { + throw new UnsupportedOperationException("You're using an outdated version of BungeeCord - please update. Thank you!"); + } + ListenerInfo listenerInfo = proxy.getConfig().getListeners().stream().findFirst().orElseThrow( IllegalStateException::new); @@ -202,7 +214,7 @@ protected void initChannel(Channel ch) throws Exception { if (channelInitializer == null) { // Proxy has finished initializing; we can safely grab this variable without fear of plugins modifying it // (Older versions of ViaVersion replace this to inject) - channelInitializer = PipelineUtils.SERVER_CHILD; + channelInitializer = proxy.unsafe().getFrontendChannelInitializer().getChannelInitializer(); } initChannel.invoke(channelInitializer, ch); } @@ -258,75 +270,7 @@ public void onProxyReload(ProxyReloadEvent event) { // End of logic from GeyserMC void injectClient(Channel channel, boolean clientToProxy) { - if (!channel.isOpen()) { - return; - } - - if (channel.pipeline().get(MinecraftEncoder.class) == null) { - logger.debug( - "Minecraft encoder not found while injecting! {}", - String.join(", ", channel.pipeline().names()) - ); - return; - } - injectAddonsCall(channel, !clientToProxy); addInjectedClient(channel); } - - @RequiredArgsConstructor - private static final class BungeeCustomPrepender extends Varint21LengthFieldPrepender { - private final BungeeInjector injector; - private final Varint21LengthFieldPrepender original; - - @Override - public void handlerAdded(ChannelHandlerContext ctx) throws Exception { - original.handlerAdded(ctx); - // The Minecraft encoder being in the pipeline isn't present until later - - if (ctx.channel().parent() != null) { - // Client <-> Proxy - ctx.pipeline().addBefore( - PipelineUtils.FRAME_DECODER, BUNGEE_INIT, - new BungeeClientToProxyInjectInitializer(injector) - ); - } else { - // Proxy <-> Server - ctx.pipeline().addLast( - BUNGEE_INIT, new BungeeProxyToServerInjectInitializer(injector) - ); - } - } - } - - @RequiredArgsConstructor - private static final class BungeeClientToProxyInjectInitializer - extends ChannelInboundHandlerAdapter { - - private final BungeeInjector injector; - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - injector.injectClient(ctx.channel(), true); - - ctx.pipeline().remove(this); - super.channelRead(ctx, msg); - } - } - - @RequiredArgsConstructor - private static final class BungeeProxyToServerInjectInitializer - extends ChannelOutboundHandlerAdapter { - - private final BungeeInjector injector; - - @Override - public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) - throws Exception { - injector.injectClient(ctx.channel(), false); - - ctx.pipeline().remove(this); - super.write(ctx, msg, promise); - } - } -} +} \ No newline at end of file diff --git a/bungee/src/main/java/com/minekube/connect/util/BungeeReflectionUtils.java b/bungee/src/main/java/com/minekube/connect/util/BungeeReflectionUtils.java deleted file mode 100644 index 77deac14b..000000000 --- a/bungee/src/main/java/com/minekube/connect/util/BungeeReflectionUtils.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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 com.minekube.connect.util; - -import static com.minekube.connect.util.MessageFormatter.format; - -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; - -// Reflection just for Bungee because Bungee is special :) -public class BungeeReflectionUtils { - private static final sun.misc.Unsafe UNSAFE; - - static { - try { - Field unsafeField = sun.misc.Unsafe.class.getDeclaredField("theUnsafe"); - unsafeField.setAccessible(true); - UNSAFE = (sun.misc.Unsafe) unsafeField.get(null); - } catch (Exception exception) { - throw new RuntimeException(format( - "Cannot initialize required reflection setup :/\nJava version: {}\nVendor: {} ({})", - System.getProperty("java.version"), - System.getProperty("java.vendor"), - System.getProperty("java.vendor.url") - ), exception); - } - } - - public static void setFieldValue(Object object, Field field, Object result) { - try { - boolean isStatic = Modifier.isStatic(field.getModifiers()); - - long offset = isStatic ? - UNSAFE.staticFieldOffset(field) : - UNSAFE.objectFieldOffset(field); - - if (isStatic) { - UNSAFE.putObject(UNSAFE.staticFieldBase(field), offset, result); - } else { - UNSAFE.putObject(object, offset, result); - } - } catch (Exception e) { - throw new RuntimeException(format( - "Java version: {}\nVendor: {} ({})", - System.getProperty("java.version"), - System.getProperty("java.vendor"), - System.getProperty("java.vendor.url"), e)); - } - } -} diff --git a/spigot/src/main/java/com/minekube/connect/module/SpigotPlatformModule.java b/spigot/src/main/java/com/minekube/connect/module/SpigotPlatformModule.java index 9f38b6410..343b113f6 100644 --- a/spigot/src/main/java/com/minekube/connect/module/SpigotPlatformModule.java +++ b/spigot/src/main/java/com/minekube/connect/module/SpigotPlatformModule.java @@ -41,8 +41,6 @@ import com.minekube.connect.platform.command.CommandUtil; import com.minekube.connect.platform.listener.ListenerRegistration; import com.minekube.connect.platform.util.PlatformUtils; -import com.minekube.connect.pluginmessage.SpigotSkinApplier; -import com.minekube.connect.skin.SkinApplier; import com.minekube.connect.util.LanguageManager; import com.minekube.connect.util.SpigotCommandUtil; import com.minekube.connect.util.SpigotPlatformUtils; @@ -159,12 +157,6 @@ public String platformName() { // return new SpigotPluginMessageRegistration(plugin); // } - @Provides - @Singleton - public SkinApplier skinApplier(SpigotVersionSpecificMethods versionSpecificMethods) { - return new SpigotSkinApplier(versionSpecificMethods, plugin); - } - @Provides @Singleton public SpigotVersionSpecificMethods versionSpecificMethods() { diff --git a/spigot/src/main/java/com/minekube/connect/pluginmessage/SpigotSkinApplier.java b/spigot/src/main/java/com/minekube/connect/pluginmessage/SpigotSkinApplier.java deleted file mode 100644 index 5deb98542..000000000 --- a/spigot/src/main/java/com/minekube/connect/pluginmessage/SpigotSkinApplier.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * 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 com.minekube.connect.pluginmessage; - -import com.minekube.connect.SpigotPlugin; -import com.minekube.connect.api.player.ConnectPlayer; -import com.minekube.connect.skin.SkinApplier; -import com.minekube.connect.skin.SkinData; -import com.minekube.connect.util.ClassNames; -import com.minekube.connect.util.ReflectionUtils; -import com.minekube.connect.util.SpigotVersionSpecificMethods; -import com.mojang.authlib.GameProfile; -import com.mojang.authlib.properties.Property; -import com.mojang.authlib.properties.PropertyMap; -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; - -public final class SpigotSkinApplier implements SkinApplier { - private final SpigotVersionSpecificMethods versionSpecificMethods; - private final SpigotPlugin plugin; - - public SpigotSkinApplier( - SpigotVersionSpecificMethods versionSpecificMethods, - SpigotPlugin plugin) { - this.versionSpecificMethods = versionSpecificMethods; - this.plugin = plugin; - } - - @Override - public void applySkin(ConnectPlayer connectPlayer, SkinData skinData) { - applySkin0(connectPlayer, skinData, true); - } - - private void applySkin0(ConnectPlayer connectPlayer, SkinData skinData, boolean firstTry) { - Player player = Bukkit.getPlayer(connectPlayer.getUniqueId()); - - // player is probably not logged in yet - if (player == null) { - if (firstTry) { - Bukkit.getScheduler().runTaskLater(plugin, - () -> applySkin0(connectPlayer, skinData, false), - 10 * 1000); - } - return; - } - - GameProfile profile = ReflectionUtils.castedInvoke(player, ClassNames.GET_PROFILE_METHOD); - - if (profile == null) { - throw new IllegalStateException("The GameProfile cannot be null! " + player.getName()); - } - - PropertyMap properties = profile.getProperties(); - - properties.removeAll("textures"); - Property property = new Property("textures", skinData.getValue(), skinData.getSignature()); - properties.put("textures", property); - - // By running as a task, we don't run into async issues - plugin.getServer().getScheduler().runTask(plugin, () -> { - for (Player p : Bukkit.getOnlinePlayers()) { - if (!p.equals(player) && p.canSee(player)) { - versionSpecificMethods.hideAndShowPlayer(p, player); - versionSpecificMethods.hideAndShowPlayer(p, player); - } - } - }); - } -}