From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Lumine1909 <133463833+Lumine1909@users.noreply.github.com> Date: Wed, 22 May 2024 10:12:22 +0800 Subject: [PATCH] Bytebuf API diff --git a/src/main/java/net/minecraft/network/chat/Component.java b/src/main/java/net/minecraft/network/chat/Component.java index 2de8da4dbe2f7b9da740a90829f18bff0b3d5b8c..a8af45393b4733ea3b6639ad7d890896752185fd 100644 --- a/src/main/java/net/minecraft/network/chat/Component.java +++ b/src/main/java/net/minecraft/network/chat/Component.java @@ -296,7 +296,7 @@ public interface Component extends Message, FormattedText, Iterable { return (MutableComponent) ComponentSerialization.CODEC.parse(registries.createSerializationContext(JsonOps.INSTANCE), json).getOrThrow(JsonParseException::new); } - static JsonElement serialize(Component text, HolderLookup.Provider registries) { + public static JsonElement serialize(Component text, HolderLookup.Provider registries) { // Leaves - package -> public return (JsonElement) ComponentSerialization.CODEC.encodeStart(registries.createSerializationContext(JsonOps.INSTANCE), text).getOrThrow(JsonParseException::new); } diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java index d04ebc381ba3f52954b647bc76ccc9cf628052eb..bcd2aa65470caee707209d717c2e50f53923f297 100644 --- a/src/main/java/net/minecraft/server/players/PlayerList.java +++ b/src/main/java/net/minecraft/server/players/PlayerList.java @@ -468,6 +468,12 @@ public abstract class PlayerList { return; } + // Leaves start - Bytebuf API + if (!(player instanceof ServerBot) && !(player instanceof ServerPhotographer)) { + this.cserver.getBytebufHandler().injectPlayer(player); + } + // Leaves end - Bytebuf API + org.leavesmc.leaves.protocol.core.LeavesProtocolManager.handlePlayerJoin(player); // Leaves - protocol // Leaves start - bot support diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java index eadb36fc12822886f73d15b22a50f9f1f87a7214..277d4b896378894bdd5fe33c2e005801a878e6bd 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java @@ -311,6 +311,7 @@ public final class CraftServer implements Server { private final io.papermc.paper.potion.PaperPotionBrewer potionBrewer; // Paper - Custom Potion Mixes private final org.leavesmc.leaves.entity.CraftBotManager botManager = new org.leavesmc.leaves.entity.CraftBotManager(); // Leaves private final org.leavesmc.leaves.entity.CraftPhotographerManager photographerManager = new org.leavesmc.leaves.entity.CraftPhotographerManager(); // Leaves + private final org.leavesmc.leaves.bytebuf.internal.InternalBytebufHandler internalBytebufHandler = new org.leavesmc.leaves.bytebuf.internal.InternalBytebufHandler(); // Leaves // Paper start - Folia region threading API private final io.papermc.paper.threadedregions.scheduler.FallbackRegionScheduler regionizedScheduler = new io.papermc.paper.threadedregions.scheduler.FallbackRegionScheduler(); @@ -3241,4 +3242,15 @@ public final class CraftServer implements Server { return photographerManager; } // Leaves end - replay mod api + + // Leaves start - Bytebuf API + @Override + public org.leavesmc.leaves.bytebuf.BytebufManager getBytebufManager() { + return internalBytebufHandler.getManager(); + } + + public org.leavesmc.leaves.bytebuf.internal.InternalBytebufHandler getBytebufHandler() { + return internalBytebufHandler; + } + // Leaves end - Bytebuf API } diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java index cc2bd9fd33070464bac8aeab9ff26543e7b57801..32f246acbdca8836b8e75e31e26a5407fd394714 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java @@ -3554,4 +3554,16 @@ public class CraftPlayer extends CraftHumanEntity implements Player { ((ca.spottedleaf.moonrise.patches.chunk_system.player.ChunkSystemServerPlayer)this.getHandle()) .moonrise$getViewDistanceHolder().setSendViewDistance(viewDistance); } + + // Leaves start - Bytebuf API + @Override + public void sendPacket(org.leavesmc.leaves.bytebuf.packet.Packet packet) { + this.server.getBytebufHandler().applyPacketToPlayer(this.getHandle(), packet); + } + + @Override + public void sendPacket(org.leavesmc.leaves.bytebuf.Bytebuf bytebuf, org.leavesmc.leaves.bytebuf.packet.PacketType type) { + this.server.getBytebufHandler().applyPacketToPlayer(this.getHandle(), new org.leavesmc.leaves.bytebuf.packet.Packet(type, bytebuf)); + } + // Leaves end - Bytebuf API } diff --git a/src/main/java/org/leavesmc/leaves/bytebuf/SimpleBytebufManager.java b/src/main/java/org/leavesmc/leaves/bytebuf/SimpleBytebufManager.java new file mode 100644 index 0000000000000000000000000000000000000000..443f7f6e0b8d40eaafb8009b3b7e405c6ec78d02 --- /dev/null +++ b/src/main/java/org/leavesmc/leaves/bytebuf/SimpleBytebufManager.java @@ -0,0 +1,35 @@ +package org.leavesmc.leaves.bytebuf; + +import io.netty.buffer.Unpooled; +import org.bukkit.plugin.Plugin; +import org.leavesmc.leaves.bytebuf.internal.InternalBytebufHandler; +import org.leavesmc.leaves.bytebuf.packet.PacketListener; + +public class SimpleBytebufManager implements BytebufManager { + + private final InternalBytebufHandler internal; + + public SimpleBytebufManager(InternalBytebufHandler internal) { + this.internal = internal; + } + + @Override + public void registerListener(Plugin plugin, PacketListener listener) { + internal.listenerMap.put(listener, plugin); + } + + @Override + public void unregisterListener(Plugin plugin, PacketListener listener) { + internal.listenerMap.remove(listener); + } + + @Override + public Bytebuf newBytebuf(int size) { + return new WrappedBytebuf(Unpooled.buffer(size)); + } + + @Override + public Bytebuf toBytebuf(byte[] bytes) { + return new WrappedBytebuf(Unpooled.wrappedBuffer(bytes)); + } +} diff --git a/src/main/java/org/leavesmc/leaves/bytebuf/WrappedBytebuf.java b/src/main/java/org/leavesmc/leaves/bytebuf/WrappedBytebuf.java new file mode 100644 index 0000000000000000000000000000000000000000..707d784cf17926e869481c39b0da908eab501708 --- /dev/null +++ b/src/main/java/org/leavesmc/leaves/bytebuf/WrappedBytebuf.java @@ -0,0 +1,260 @@ +package org.leavesmc.leaves.bytebuf; + +import com.google.gson.JsonElement; +import io.netty.buffer.ByteBuf; +import net.minecraft.core.RegistryAccess; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.ComponentSerialization; +import net.minecraft.server.MinecraftServer; +import org.bukkit.inventory.ItemStack; +import org.bukkit.craftbukkit.inventory.CraftItemStack; + +import java.util.UUID; + +public class WrappedBytebuf implements Bytebuf { + + private final FriendlyByteBuf buf; + private final RegistryFriendlyByteBuf registryBuf; + + public WrappedBytebuf(ByteBuf buf) { + if (buf instanceof RegistryFriendlyByteBuf) { + this.buf = (FriendlyByteBuf) buf; + this.registryBuf = (RegistryFriendlyByteBuf) buf; + } else { + this.buf = new FriendlyByteBuf(buf); + this.registryBuf = new RegistryFriendlyByteBuf(this.buf, MinecraftServer.getServer().registryAccess()); + } + } + + public RegistryFriendlyByteBuf getRegistryBuf() { + return registryBuf; + } + + @Override + public byte[] toArray() { + return buf.array(); + } + + @Override + public Bytebuf skipBytes(int i) { + buf.skipBytes(i); + return this; + } + + @Override + public int readerIndex() { + return buf.readerIndex(); + } + + @Override + public Bytebuf readerIndex(int i) { + buf.readerIndex(i); + return this; + } + + @Override + public int writerIndex() { + return buf.writerIndex(); + } + + @Override + public Bytebuf writerIndex(int i) { + buf.writerIndex(i); + return this; + } + + @Override + public Bytebuf resetReaderIndex() { + buf.resetReaderIndex(); + return this; + } + + @Override + public Bytebuf resetWriterIndex() { + buf.resetWriterIndex(); + return this; + } + + @Override + public Bytebuf writeByte(int i) { + buf.writeByte(i); + return this; + } + + @Override + public byte readByte() { + return buf.readByte(); + } + + @Override + public Bytebuf writeBoolean(boolean b) { + buf.writeBoolean(b); + return this; + } + + @Override + public boolean readBoolean() { + return buf.readBoolean(); + } + + @Override + public Bytebuf writeFloat(float f) { + buf.writeFloat(f); + return this; + } + + @Override + public float readFloat() { + return buf.readFloat(); + } + + @Override + public Bytebuf writeDouble(double d) { + buf.writeDouble(d); + return this; + } + + @Override + public double readDouble() { + return buf.readDouble(); + } + + @Override + public Bytebuf writeShort(int i) { + buf.writeShort(i); + return this; + } + + @Override + public short readShort() { + return buf.readShort(); + } + + @Override + public Bytebuf writeInt(int i) { + buf.writeShort(i); + return this; + } + + @Override + public int readInt() { + return buf.readInt(); + } + + @Override + public Bytebuf writeLong(long i) { + buf.writeLong(i); + return this; + } + + @Override + public long readLong() { + return buf.readLong(); + } + + @Override + public Bytebuf writeVarInt(int i) { + this.buf.writeVarInt(i); + return this; + } + + @Override + public int readVarInt() { + return this.buf.readVarInt(); + } + + @Override + public Bytebuf writeVarLong(long i) { + this.buf.writeVarLong(i); + return this; + } + + @Override + public long readVarLong() { + return this.buf.readVarLong(); + } + + @Override + public Bytebuf writeUUID(UUID uuid) { + this.buf.writeUUID(uuid); + return this; + } + + @Override + public UUID readUUID() { + return this.buf.readUUID(); + } + + @Override + public Bytebuf writeEnum(Enum instance) { + this.buf.writeEnum(instance); + return this; + } + + @Override + public > T readEnum(Class enumClass) { + return this.buf.readEnum(enumClass); + } + + @Override + public Bytebuf writeUTFString(String utf) { + buf.writeUtf(utf); + return this; + } + + @Override + public String readUTFString() { + return buf.readUtf(); + } + + @Override + public Bytebuf writeComponentPlain(String str) { + ComponentSerialization.STREAM_CODEC.encode(new RegistryFriendlyByteBuf(this.buf, RegistryAccess.EMPTY), Component.literal(str)); + return this; + } + + @Override + public String readComponentPlain() { + return ComponentSerialization.STREAM_CODEC.decode(new RegistryFriendlyByteBuf(buf, RegistryAccess.EMPTY)).getString(); + } + + @Override + public Bytebuf writeComponentJson(JsonElement json) { + Component component = Component.Serializer.fromJson(json, RegistryAccess.EMPTY); + if (component == null) { + throw new IllegalArgumentException("Null can not be serialize to Minecraft chat component"); + } + ComponentSerialization.STREAM_CODEC.encode(new RegistryFriendlyByteBuf(buf, RegistryAccess.EMPTY), component); + return this; + } + + @Override + public JsonElement readComponentJson() { + return Component.Serializer.serialize(ComponentSerialization.STREAM_CODEC.decode(new RegistryFriendlyByteBuf(buf, RegistryAccess.EMPTY)), RegistryAccess.EMPTY); + } + + @Override + public Bytebuf writeItemStack(ItemStack itemStack) { + net.minecraft.world.item.ItemStack nmsItem = CraftItemStack.unwrap(itemStack); + net.minecraft.world.item.ItemStack.OPTIONAL_STREAM_CODEC.encode(this.registryBuf, nmsItem); + return this; + } + + @Override + public ItemStack readItemStack() { + net.minecraft.world.item.ItemStack nmsItem = net.minecraft.world.item.ItemStack.OPTIONAL_STREAM_CODEC.decode(this.registryBuf); + return nmsItem.asBukkitMirror(); + } + + @Override + public Bytebuf copy() { + return new WrappedBytebuf(this.buf.copy()); + } + + @Override + public boolean release() { + return this.buf.release(); + } +} diff --git a/src/main/java/org/leavesmc/leaves/bytebuf/internal/InternalBytebufHandler.java b/src/main/java/org/leavesmc/leaves/bytebuf/internal/InternalBytebufHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..2c446d1657834324f015b1637221164ff61f04e1 --- /dev/null +++ b/src/main/java/org/leavesmc/leaves/bytebuf/internal/InternalBytebufHandler.java @@ -0,0 +1,225 @@ +package org.leavesmc.leaves.bytebuf.internal; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.collect.ImmutableMap; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelDuplexHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelPromise; +import net.minecraft.network.Connection; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.BundleDelimiterPacket; +import net.minecraft.network.protocol.BundlePacket; +import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket; +import net.minecraft.network.protocol.game.*; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerPlayer; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.Nullable; +import org.leavesmc.leaves.LeavesConfig; +import org.leavesmc.leaves.bytebuf.BytebufManager; +import org.leavesmc.leaves.bytebuf.SimpleBytebufManager; +import org.leavesmc.leaves.bytebuf.WrappedBytebuf; +import org.leavesmc.leaves.bytebuf.packet.Packet; +import org.leavesmc.leaves.bytebuf.packet.PacketListener; +import org.leavesmc.leaves.bytebuf.packet.PacketType; + +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ExecutionException; + +import static org.leavesmc.leaves.bytebuf.packet.PacketType.*; + +public class InternalBytebufHandler { + + private class PacketHandler extends ChannelDuplexHandler { + + private final static String handlerName = "leaves-bytebuf-handler"; + private final Player player; + + public PacketHandler(Player player) { + this.player = player; + } + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + if (msg instanceof BundlePacket || msg instanceof BundleDelimiterPacket) { + super.channelRead(ctx, msg); + return; + } + + if (msg instanceof net.minecraft.network.protocol.Packet nmsPacket) { + PacketType type = toEnumType(nmsPacket.type()); + if (type != null) { + try { + msg = callPacketInEvent(player, createBytebufPacket(type, nmsPacket)); + } catch (Exception e) { + MinecraftServer.LOGGER.error("Error on PacketInEvent.", e); + } + } + } + + if (msg != null) { + super.channelRead(ctx, msg); + } + } + + @Override + public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { + if (msg instanceof BundlePacket || msg instanceof BundleDelimiterPacket) { + super.write(ctx, msg, promise); + return; + } + + if (msg instanceof net.minecraft.network.protocol.Packet nmsPacket) { + PacketType type = toEnumType(nmsPacket.type()); + if (type != null) { + try { + msg = callPacketOutEvent(player, createBytebufPacket(type, nmsPacket)); + } catch (Exception e) { + MinecraftServer.LOGGER.error("Error on PacketInEvent.", e); + } + } + } + + if (msg != null) { + super.write(ctx, msg, promise); + } + } + } + + public final Map listenerMap = new HashMap<>(); + private final BytebufManager manager = new SimpleBytebufManager(this); + private final ImmutableMap type2CodecMap; + private final Cache, PacketType> resultCache = CacheBuilder.newBuilder().build(); + + public InternalBytebufHandler() { + ImmutableMap.Builder builder = ImmutableMap.builder(); + for (PacketType packet : PacketType.values()) { + Class packetClass; + try { + packetClass = Class.forName("net.minecraft.network.protocol.game." + packet.name() + "Packet"); + } catch (ClassNotFoundException e) { + try { + packetClass = Class.forName("net.minecraft.network.protocol.common." + packet.name() + "Packet"); + } catch (ClassNotFoundException e2) { + try { + packetClass = Class.forName("net.minecraft.network.protocol.ping." + packet.name() + "Packet"); + } catch (ClassNotFoundException ignored) { + continue; + } + } + } + try { + Field field = packetClass.getDeclaredField("STREAM_CODEC"); + builder.put(packet, (StreamCodec>) field.get(null)); + } catch (Exception ignored) { + } + } + + builder.put(ClientboundMoveEntityPos, ClientboundMoveEntityPacket.Pos.STREAM_CODEC); + builder.put(ClientboundMoveEntityPosRot, ClientboundMoveEntityPacket.PosRot.STREAM_CODEC); + builder.put(ClientboundMoveEntityRot, ClientboundMoveEntityPacket.Rot.STREAM_CODEC); + builder.put(ServerboundMovePlayerPos, ServerboundMovePlayerPacket.Pos.STREAM_CODEC); + builder.put(ServerboundMovePlayerPosRot, ServerboundMovePlayerPacket.PosRot.STREAM_CODEC); + builder.put(ServerboundMovePlayerRot, ServerboundMovePlayerPacket.Rot.STREAM_CODEC); + builder.put(ServerboundMovePlayerStatusOnly, ServerboundMovePlayerPacket.StatusOnly.STREAM_CODEC); + builder.put(ClientboundCustomPayload, ClientboundCustomPayloadPacket.GAMEPLAY_STREAM_CODEC); + + type2CodecMap = builder.build(); + } + + public void injectPlayer(ServerPlayer player) { + if (LeavesConfig.leavesPacketEvent) { + player.connection.connection.channel.pipeline().addBefore("packet_handler", PacketHandler.handlerName, new PacketHandler(player.getBukkitEntity())); + } + } + + public BytebufManager getManager() { + return manager; + } + + public net.minecraft.network.protocol.Packet callPacketInEvent(Player player, Packet packet) { + for (PacketListener listener : listenerMap.keySet()) { + if (listenerMap.get(listener).isEnabled()) { + packet = listener.onPacketIn(player, packet); + packet.bytebuf().resetReaderIndex(); + } else { + listenerMap.remove(listener); + } + } + return createNMSPacket(packet); + } + + public net.minecraft.network.protocol.Packet callPacketOutEvent(Player player, Packet packet) { + for (PacketListener listener : listenerMap.keySet()) { + if (listenerMap.get(listener).isEnabled()) { + packet = listener.onPacketOut(player, packet); + packet.bytebuf().resetReaderIndex(); + } else { + listenerMap.remove(listener); + } + } + return createNMSPacket(packet); + } + + public void applyPacketToPlayer(ServerPlayer player, Packet packet) { + Connection sp = player.connection.connection; + sp.send(createNMSPacket(packet)); + } + + public net.minecraft.network.protocol.Packet createNMSPacket(Packet packet) { + StreamCodec> codec = type2CodecMap.get(packet.type()); + if (codec == null) { + throw new UnsupportedOperationException("This feature is not completely finished yet, packet type " + packet.type() + " is not supported temporary."); + } + return codec.decode(((WrappedBytebuf) packet.bytebuf()).getRegistryBuf()); + } + + @Nullable + private PacketType toEnumType(net.minecraft.network.protocol.PacketType type) { + try { + return this.resultCache.get(type, () -> { + StringBuilder builder = new StringBuilder(); + String bound = type.toString().split("/")[0]; + String name = type.toString().split(":")[1]; + builder.append(bound.substring(0, 1).toUpperCase()).append(bound.substring(1)); + boolean flag = true; + for (int i = 0; i < name.length(); i++) { + if (flag) { + builder.append(name.substring(i, i + 1).toUpperCase()); + flag = false; + continue; + } + if (name.charAt(i) == '_') { + flag = true; + } else { + builder.append(name.charAt(i)); + } + } + try { + return PacketType.valueOf(builder.toString()); + } catch (IllegalArgumentException ignored) { + return null; + } + }); + } catch (ExecutionException ignore) { + return null; + } + } + + public Packet createBytebufPacket(PacketType type, net.minecraft.network.protocol.Packet nmsPacket) { + RegistryFriendlyByteBuf buf = new RegistryFriendlyByteBuf(Unpooled.buffer(8192), MinecraftServer.getServer().registryAccess()); + StreamCodec> codec = type2CodecMap.get(type); + if (codec == null) { + throw new UnsupportedOperationException("This feature is not completely finished yet, packet type " + type + " is not supported temporary."); + } + codec.encode(buf, nmsPacket); + return new Packet(type, new WrappedBytebuf(buf)); + } +}