diff --git a/bukkit/compatibility/build.gradle.kts b/bukkit/compatibility/build.gradle.kts index 0165818bd..8b95c7872 100644 --- a/bukkit/compatibility/build.gradle.kts +++ b/bukkit/compatibility/build.gradle.kts @@ -9,6 +9,7 @@ repositories { maven("https://repo.momirealms.net/releases/") maven("https://mvn.lumine.io/repository/maven-public/") // model engine maven("https://nexus.phoenixdevt.fr/repository/maven-public/") // mmoitems + maven("https://repo.viaversion.com") // via } dependencies { @@ -34,6 +35,8 @@ dependencies { compileOnly("io.lumine:MythicLib-dist:1.6.2-SNAPSHOT") // LuckPerms compileOnly("net.luckperms:api:5.4") + // viaversion + compileOnly("com.viaversion:viaversion-api:5.3.2") } java { diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/viaversion/ViaVersionProtocol.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/viaversion/ViaVersionProtocol.java new file mode 100644 index 000000000..53ef4f5e5 --- /dev/null +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/viaversion/ViaVersionProtocol.java @@ -0,0 +1,27 @@ +package net.momirealms.craftengine.bukkit.compatibility.viaversion; + +import com.viaversion.viaversion.api.Via; +import com.viaversion.viaversion.api.ViaAPI; + +import java.util.UUID; + + +public class ViaVersionProtocol { + private final boolean hasPlugin; + private final ViaAPI viaAPI; + + public ViaVersionProtocol(boolean hasPlugin) { + this.hasPlugin = hasPlugin; + this.viaAPI = hasPlugin ? Via.getAPI() : null; + } + + public int getPlayerProtocolVersion(UUID uuid) { + if (!hasPlugin) return -1; + System.out.println(this.viaAPI.getPlayerProtocolVersion(uuid).getVersion()); + return this.viaAPI.getPlayerProtocolVersion(uuid).getVersion(); + } + + public boolean hasPlugin() { + return hasPlugin; + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/pack/BukkitPackManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/pack/BukkitPackManager.java index 1f93103ca..955264883 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/pack/BukkitPackManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/pack/BukkitPackManager.java @@ -21,7 +21,6 @@ import org.bukkit.event.EventPriority; import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerJoinEvent; -import org.bukkit.event.player.PlayerResourcePackStatusEvent; import java.util.ArrayList; import java.util.List; @@ -55,16 +54,6 @@ public class BukkitPackManager extends AbstractPackManager implements Listener { } } - @EventHandler(priority = EventPriority.LOW) - public void onResourcePackStatus(PlayerResourcePackStatusEvent event) { - // for 1.20.1 servers, not recommended to use - if (Config.sendPackOnJoin() && Config.kickOnDeclined() && !VersionHelper.isVersionNewerThan1_20_2()) { - if (event.getStatus() == PlayerResourcePackStatusEvent.Status.DECLINED || event.getStatus() == PlayerResourcePackStatusEvent.Status.FAILED_DOWNLOAD) { - event.getPlayer().kick(); - } - } - } - @Override public void load() { if (ReloadCommand.RELOAD_PACK_FLAG || CraftEngine.instance().isInitializing()) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java index c5160f6db..b628e6129 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java @@ -1,11 +1,13 @@ package net.momirealms.craftengine.bukkit.plugin.network; +import com.google.gson.JsonObject; import io.netty.buffer.ByteBuf; import io.netty.channel.*; import io.netty.handler.codec.MessageToMessageDecoder; import io.netty.handler.codec.MessageToMessageEncoder; import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; +import net.momirealms.craftengine.bukkit.compatibility.viaversion.ViaVersionProtocol; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.network.id.PacketIds1_20; @@ -16,10 +18,7 @@ import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.network.ConnectionState; import net.momirealms.craftengine.core.plugin.network.NetWorkUser; import net.momirealms.craftengine.core.plugin.network.NetworkManager; -import net.momirealms.craftengine.core.util.FriendlyByteBuf; -import net.momirealms.craftengine.core.util.ListMonitor; -import net.momirealms.craftengine.core.util.TriConsumer; -import net.momirealms.craftengine.core.util.VersionHelper; +import net.momirealms.craftengine.core.util.*; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -57,6 +56,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes private final BiConsumer packetConsumer; private final BiConsumer immediatePacketConsumer; private final BukkitCraftEngine plugin; + private final ViaVersionProtocol viaVersionProtocol; private final Map users = new ConcurrentHashMap<>(); private final Map onlineUsers = new ConcurrentHashMap<>(); @@ -77,6 +77,8 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes instance = this; hasModelEngine = Bukkit.getPluginManager().getPlugin("ModelEngine") != null; this.plugin = plugin; + // hook via + this.viaVersionProtocol = new ViaVersionProtocol(Bukkit.getPluginManager().getPlugin("ViaVersion") != null); // set up packet id this.packetIds = setupPacketIds(); // register packet handlers @@ -101,6 +103,8 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes // set up mod channel this.plugin.bootstrap().getServer().getMessenger().registerIncomingPluginChannel(this.plugin.bootstrap(), MOD_CHANNEL, this); this.plugin.bootstrap().getServer().getMessenger().registerOutgoingPluginChannel(this.plugin.bootstrap(), MOD_CHANNEL); + // 配置via频道 + this.plugin.bootstrap().getServer().getMessenger().registerIncomingPluginChannel(this.plugin.bootstrap(), VIA_CHANNEL, this); // Inject server channel try { Object server = Reflections.method$MinecraftServer$getServer.invoke(null); @@ -147,6 +151,9 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes registerNMSPacketConsumer(PacketConsumers.EDIT_BOOK, Reflections.clazz$ServerboundEditBookPacket); registerNMSPacketConsumer(PacketConsumers.CUSTOM_PAYLOAD, Reflections.clazz$ServerboundCustomPayloadPacket); registerNMSPacketConsumer(PacketConsumers.RESOURCE_PACK_PUSH, Reflections.clazz$ClientboundResourcePackPushPacket); + registerNMSPacketConsumer(PacketConsumers.HANDSHAKE_C2S, Reflections.clazz$ClientIntentionPacket); + registerNMSPacketConsumer(PacketConsumers.LOGIN_ACKNOWLEDGED, Reflections.clazz$ServerboundLoginAcknowledgedPacket); + registerNMSPacketConsumer(PacketConsumers.RESOURCE_PACK_RESPONSE, Reflections.clazz$ServerboundResourcePackPacket); registerByteBufPacketConsumer(PacketConsumers.SECTION_BLOCK_UPDATE, this.packetIds.clientboundSectionBlocksUpdatePacket()); registerByteBufPacketConsumer(PacketConsumers.BLOCK_UPDATE, this.packetIds.clientboundBlockUpdatePacket()); registerByteBufPacketConsumer(VersionHelper.isVersionNewerThan1_21_3() ? PacketConsumers.LEVEL_PARTICLE_1_21_3 : (VersionHelper.isVersionNewerThan1_20_5() ? PacketConsumers.LEVEL_PARTICLE_1_20_5 : PacketConsumers.LEVEL_PARTICLE_1_20), this.packetIds.clientboundLevelParticlesPacket()); @@ -202,9 +209,17 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes return this.onlineUserArray; } - // 保留仅注册入频道用 @Override - public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, byte @NotNull [] message) {} + public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, byte @NotNull [] message) { + if (channel.equals(VIA_CHANNEL)) { + BukkitServerPlayer user = plugin.adapt(player); + if (user != null) { + JsonObject payload = GsonHelper.get().fromJson(new String(message), JsonObject.class); + int version = payload.get("version").getAsInt(); + user.setProtocolVersion(version); + } + } + } @Override public void init() { @@ -620,4 +635,8 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes } return output; } + + public ViaVersionProtocol viaVersionProtocol() { + return this.viaVersionProtocol; + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java index 719667520..1d03f9141 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java @@ -10,6 +10,7 @@ import net.momirealms.craftengine.bukkit.api.event.FurnitureBreakEvent; import net.momirealms.craftengine.bukkit.api.event.FurnitureInteractEvent; import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; import net.momirealms.craftengine.bukkit.compatibility.modelengine.ModelEngineUtils; +import net.momirealms.craftengine.bukkit.compatibility.viaversion.ViaVersionProtocol; import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager; import net.momirealms.craftengine.bukkit.entity.furniture.LoadedFurniture; import net.momirealms.craftengine.bukkit.item.behavior.FurnitureItemBehavior; @@ -1248,6 +1249,10 @@ public class PacketConsumers { player.setConnectionState(ConnectionState.PLAY); Object dimensionKey; if (!VersionHelper.isVersionNewerThan1_20_2()) { + ViaVersionProtocol viaVersionProtocol = BukkitNetworkManager.instance().viaVersionProtocol(); + if (viaVersionProtocol.hasPlugin()) { + user.setProtocolVersion(viaVersionProtocol.getPlayerProtocolVersion(player.uuid())); + } dimensionKey = Reflections.field$ClientboundLoginPacket$dimension.get(packet); } else { Object commonInfo = Reflections.field$ClientboundLoginPacket$commonPlayerSpawnInfo.get(packet); @@ -2178,4 +2183,46 @@ public class PacketConsumers { CraftEngine.instance().logger().warn("Failed to handle ClientboundResourcePackPushPacket", e); } }; + + public static final TriConsumer HANDSHAKE_C2S = (user, event, packet) -> { + try { + if (BukkitNetworkManager.instance().viaVersionProtocol().hasPlugin()) return; + int protocolVersion = Reflections.field$ClientIntentionPacket$protocolVersion.getInt(packet); + user.setProtocolVersion(protocolVersion); + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to handle ClientIntentionPacket", e); + } + }; + + public static final TriConsumer LOGIN_ACKNOWLEDGED = (user, event, packet) -> { + try { + ViaVersionProtocol viaVersionProtocol = BukkitNetworkManager.instance().viaVersionProtocol(); + if (viaVersionProtocol.hasPlugin()) { + user.setProtocolVersion(viaVersionProtocol.getPlayerProtocolVersion(user.uuid())); + } + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to handle ServerboundLoginAcknowledgedPacket", e); + } + }; + + public static final TriConsumer RESOURCE_PACK_RESPONSE = (user, event, packet) -> { + try { + if (user.sentResourcePack() || !Config.sendPackOnJoin() || !Config.kickOnDeclined()) return; + Object action = Reflections.field$ServerboundResourcePackPacket$action.get(packet); + if (action == null) return; + if (action == Reflections.instance$ServerboundResourcePackPacket$Action$DECLINED + || action == Reflections.instance$ServerboundResourcePackPacket$Action$FAILED_DOWNLOAD) { + Object kickPacket = Reflections.constructor$ClientboundDisconnectPacket.newInstance( + ComponentUtils.adventureToMinecraft(Component.translatable("multiplayer.requiredTexturePrompt.disconnect"))); + user.nettyChannel().writeAndFlush(kickPacket); + user.nettyChannel().disconnect(); + return; + } + if (action == Reflections.instance$ServerboundResourcePackPacket$Action$SUCCESSFULLY_LOADED) { + user.setSentResourcePack(true); + } + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to handle ServerboundResourcePackPacket", e); + } + }; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java index bd873f8e8..48a7d88e7 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java @@ -18,6 +18,7 @@ import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.network.ConnectionState; +import net.momirealms.craftengine.core.plugin.network.ProtocolVersion; import net.momirealms.craftengine.core.util.Direction; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.VersionHelper; @@ -39,6 +40,8 @@ import java.util.concurrent.ConcurrentHashMap; public class BukkitServerPlayer extends Player { private final BukkitCraftEngine plugin; + // handshake + private ProtocolVersion protocolVersion = ProtocolVersion.UNKNOWN; // connection state private final Channel channel; private String name; @@ -46,6 +49,7 @@ public class BukkitServerPlayer extends Player { private ConnectionState decoderState; private ConnectionState encoderState; private final Set resourcePackUUID = Collections.synchronizedSet(new HashSet<>()); + private boolean sentResourcePack = !Config.sendPackOnJoin(); // some references private Reference playerRef; private Reference serverPlayerRef; @@ -758,6 +762,26 @@ public class BukkitServerPlayer extends Player { } } + @Override + public ProtocolVersion protocolVersion() { + return this.protocolVersion; + } + + @Override + public void setProtocolVersion(int protocolVersion) { + this.protocolVersion = ProtocolVersion.getById(protocolVersion); + } + + @Override + public boolean sentResourcePack() { + return this.sentResourcePack; + } + + @Override + public void setSentResourcePack(boolean sentResourcePack) { + this.sentResourcePack = sentResourcePack; + } + @Override public void clearView() { this.entityTypeView.clear(); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java index cba4df44f..cc81efc7c 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java @@ -6499,12 +6499,33 @@ public class Reflections { ) ); + public static final Object instance$ServerboundResourcePackPacket$Action$SUCCESSFULLY_LOADED; + public static final Object instance$ServerboundResourcePackPacket$Action$DECLINED; + public static final Object instance$ServerboundResourcePackPacket$Action$FAILED_DOWNLOAD; public static final Object instance$ServerboundResourcePackPacket$Action$ACCEPTED; + public static final Object instance$ServerboundResourcePackPacket$Action$DOWNLOADED; + public static final Object instance$ServerboundResourcePackPacket$Action$INVALID_URL; + public static final Object instance$ServerboundResourcePackPacket$Action$FAILED_RELOAD; + public static final Object instance$ServerboundResourcePackPacket$Action$DISCARDED; static { try { Object[] values = (Object[]) method$ServerboundResourcePackPacket$Action$values.invoke(null); + instance$ServerboundResourcePackPacket$Action$SUCCESSFULLY_LOADED = values[0]; + instance$ServerboundResourcePackPacket$Action$DECLINED = values[1]; + instance$ServerboundResourcePackPacket$Action$FAILED_DOWNLOAD = values[2]; instance$ServerboundResourcePackPacket$Action$ACCEPTED = values[3]; + if (VersionHelper.isVersionNewerThan1_20_3()) { + instance$ServerboundResourcePackPacket$Action$DOWNLOADED = values[4]; + instance$ServerboundResourcePackPacket$Action$INVALID_URL = values[5]; + instance$ServerboundResourcePackPacket$Action$FAILED_RELOAD = values[6]; + instance$ServerboundResourcePackPacket$Action$DISCARDED = values[7]; + } else { + instance$ServerboundResourcePackPacket$Action$DOWNLOADED = null; + instance$ServerboundResourcePackPacket$Action$INVALID_URL = null; + instance$ServerboundResourcePackPacket$Action$FAILED_RELOAD = null; + instance$ServerboundResourcePackPacket$Action$DISCARDED = null; + } } catch (Exception e) { throw new RuntimeException(e); } @@ -6521,4 +6542,29 @@ public class Reflections { "core.component.DataComponentType" ) ); + + public static final Class clazz$ClientIntentionPacket = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("network.protocol.handshake.ClientIntentionPacket"), + BukkitReflectionUtils.assembleMCClass("network.protocol.handshake.PacketHandshakingInSetProtocol") + ) + ); + + public static final Field field$ClientIntentionPacket$protocolVersion = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$ClientIntentionPacket, int.class, VersionHelper.isVersionNewerThan1_20_2() ? 0 : 1 + ) + ); + + // 1.20.2+ + public static final Class clazz$ServerboundLoginAcknowledgedPacket = + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("network.protocol.login.ServerboundLoginAcknowledgedPacket") + ); + + public static final Field field$ServerboundResourcePackPacket$action = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$ServerboundResourcePackPacket, clazz$ServerboundResourcePackPacket$Action, 0 + ) + ); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetWorkUser.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetWorkUser.java index f2a8b8d2d..a88dd7c6a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetWorkUser.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetWorkUser.java @@ -51,4 +51,12 @@ public interface NetWorkUser { void setClientModState(boolean enable); void addResourcePackUUID(UUID uuid); + + ProtocolVersion protocolVersion(); + + void setProtocolVersion(int protocolVersion); + + boolean sentResourcePack(); + + void setSentResourcePack(boolean sentResourcePack); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetworkManager.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetworkManager.java index b6bbae960..facef471a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetworkManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetworkManager.java @@ -9,6 +9,7 @@ import java.util.List; public interface NetworkManager extends Manageable { String MOD_CHANNEL = "craftengine:payload"; + String VIA_CHANNEL = "vv:proxy_details"; void setUser(Channel channel, NetWorkUser user); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/ProtocolVersion.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/ProtocolVersion.java new file mode 100644 index 000000000..c692c6da6 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/ProtocolVersion.java @@ -0,0 +1,52 @@ +package net.momirealms.craftengine.core.plugin.network; + +public enum ProtocolVersion { + UNKNOWN(-1, "Unknown"), + V1_20(763, "1.20"), + V1_20_1(763, "1.20.1"), + V1_20_2(764, "1.20.2"), + V1_20_3(765, "1.20.3"), + V1_20_4(765, "1.20.4"), + V1_20_5(766, "1.20.5"), + V1_20_6(766, "1.20.6"), + V1_21(767, "1.21"), + V1_21_1(767, "1.21.1"), + V1_21_2(768, "1.21.2"), + V1_21_3(768, "1.21.3"), + V1_21_4(769, "1.21.4"), + V1_21_5(770, "1.21.5"); + + private final int id; + private final String name; + + ProtocolVersion(int id, String name) { + this.id = id; + this.name = name; + } + + public int getId() { + return id; + } + + public String getName() { + return name; + } + + public static ProtocolVersion getByName(String name) { + for (ProtocolVersion version : values()) { + if (version.getName().equals(name)) { + return version; + } + } + return UNKNOWN; + } + + public static ProtocolVersion getById(int id) { + for (ProtocolVersion version : values()) { + if (version.getId() == id) { + return version; + } + } + return UNKNOWN; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/ProtocolVersionUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/ProtocolVersionUtils.java new file mode 100644 index 000000000..8372e1117 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/util/ProtocolVersionUtils.java @@ -0,0 +1,10 @@ +package net.momirealms.craftengine.core.util; + +import net.momirealms.craftengine.core.plugin.network.ProtocolVersion; + +public class ProtocolVersionUtils { + + public static boolean isVersionNewerThan(ProtocolVersion version, ProtocolVersion targetVersion) { + return version.getId() >= targetVersion.getId(); + } +}