9
0
mirror of https://github.com/LeavesMC/Leaves.git synced 2025-12-19 14:59:32 +00:00
Files
LeavesMC/patches/server/0135-Bytebuf-API.patch
2024-06-09 17:15:16 +08:00

624 lines
23 KiB
Diff

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 6dcade427f19771b08e04cfa036dedcfac30b5cd..3306d9f5aefe5863cff79374c75cc309c1699c3f 100644
--- a/src/main/java/net/minecraft/network/chat/Component.java
+++ b/src/main/java/net/minecraft/network/chat/Component.java
@@ -291,7 +291,7 @@ public interface Component extends Message, FormattedText, Iterable<Component> {
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 975062e67278614220eab0c301019a235c7953b7..810d92c033b556e8ae3a5e133a4e471e561eef2c 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -470,6 +470,12 @@ public abstract class PlayerList {
return;
}
+ // Leaves start - Bytebuf API
+ if (!(player instanceof ServerBot)) {
+ 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 4ecfa62b8146403b05041418a37d74378bea4b05..7cd56fb6a985075cf6d68a5108329924dd2405fa 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -307,6 +307,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();
@@ -3284,4 +3285,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 264d431ea5d6cbed560fe0f025b1d402877d7d66..909f55976c035e2aa8903e988924bba53cd07e5c 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -3519,4 +3519,16 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
public void setSendViewDistance(final int viewDistance) {
this.getHandle().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 extends Enum<T>> T readEnum(Class<T> 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<PacketListener, Plugin> listenerMap = new HashMap<>();
+ private final BytebufManager manager = new SimpleBytebufManager(this);
+ private final ImmutableMap<PacketType, StreamCodec> type2CodecMap;
+ private final Cache<net.minecraft.network.protocol.PacketType<?>, PacketType> resultCache = CacheBuilder.newBuilder().build();
+
+ public InternalBytebufHandler() {
+ ImmutableMap.Builder<PacketType, StreamCodec> 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<FriendlyByteBuf, net.minecraft.network.protocol.Packet<?>>) 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<FriendlyByteBuf, net.minecraft.network.protocol.Packet<?>> 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<FriendlyByteBuf, net.minecraft.network.protocol.Packet<?>> 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));
+ }
+}