Can't read ByteArray from ChannelBuf

I’m trying to send a byte array from forge client and then read it in channel listener.

    channel.get("screenshot").addListener(Platform.Type.SERVER, (buf, con, side) -> {   
            byte[] chunkByteArray = buf.readByteArray();
    });

However, there’s a fatal error - “ByteArray with size 10249 is bigger than allowed 4096”.
4096 is the length of byte array that the client is sending. Code from client if needed (forge):

                    ByteBuf buf = Unpooled.buffer(0);
                    buf.writeBytes(chunk); //it's my byte array

                    PacketBuffer packetBuffer = new PacketBuffer(buf);
                    CPacketCustomPayload packet = new CPacketCustomPayload("channelName", packetBuffer);
                    mc.player.connection.sendPacket(packet);

Full stack trace: New stacktrace - Pastebin.com

So if I recall there is a limit on a forge custom packet size… I didn’t think that the size was so small, but might be an area to look in to.

What you can do instead is beak your packet into a series of packets, with a little extra data like the order ID and small uuid, and rebuild the big packet on the other end.

I may be wrong about this but I’m pretty sure it’s what I had to do for another mod/plugin.

It’s funny - I’m rewriting my plugin from bukkit and the chunking was already done, since I needed to send big bytearrays. Each chunk was 10240 bytes length and there were no uuids or something.
I’m sending a really big thing and chunking it to fewer bytes will make too many packets. If 4096 is really beyond the limit - how many bytes should every chunk have?

The maximum size a forge packet can be is 1048496 * 255 = 267366480 (Forge packets are split into a maximum of 255 parts, multiplexed over the FML|MP channel), so forge packet sizing is not the issue.

The PacketBuffer implementation of readByteArray() is:

    public byte[] readByteArray()
    {
        return this.readByteArray(this.readableBytes());
    }

    public byte[] readByteArray(int maxLength)
    {
        int i = this.readVarInt();

        if (i > maxLength)
        {
            throw new DecoderException("ByteArray with size " + i + " is bigger than allowed " + maxLength);
        }
        else
        {
            byte[] abyte = new byte[i];
            this.readBytes(abyte);
            return abyte;
        }
    }

so I guess the problem is that readableBytes() is only 4096.
Just re-read your post, 4096 is expected. The problem is that you’re mixing writeBytes() and readByteArray() The latter uses a prefixed varint array length.

So either use writeByteArray / readByteArray or writeBytes / readBytes, they cannot be mixed.

1 Like

I’ll try, thanks.

Not 4096 exactly, because it was saying about 10240 when the array length was 10240. So what it;s saying depends on array size.

Still can’t read it. Maybe I use readBytes wrong? My code: (I’ve also added a boolean to the buffer)

    if (buf.readBoolean()) {
            // stuff
    } else {
            byte[] chunkByteArray = buf.readBytes(4096);
    }

Stacktrace: Another one - Pastebin.com

Interesting, I can’t reproduce.

Here’s what I tried:

Mod on the client:

@Mod(modid = "bugtest_forge", version = "1.0.0")
public class BugTestForge {

    @Mod.EventHandler
    public void onInit(FMLInitializationEvent event) {
        MinecraftForge.EVENT_BUS.register(this);
    }

    @SubscribeEvent
    public void onConnect(ClientConnectedToServerEvent event) {
        ByteBuf buf = Unpooled.buffer(0);
        byte[] chunk = new byte[10240];
        buf.writeBytes(chunk);
        PacketBuffer packetBuffer = new PacketBuffer(buf);
        CPacketCustomPayload packet = new CPacketCustomPayload("bugtest", packetBuffer);
        ((NetHandlerPlayClient) event.getHandler()).sendPacket(packet);
    }
}

Plugin on the server:

@Plugin(id = "bugtest_sponge", name = "bugtest")
public class BugTestPlugin {

    @Listener
    public void onPreInit(GamePreInitializationEvent event) {
        Sponge.getChannelRegistrar().createRawChannel(this, "bugtest").addListener((buf, con, side) -> {
            byte[] chunkByteArray = buf.readBytes(4096);
            System.out.println(Arrays.toString(chunkByteArray));
        });
    }
}

No errors occurred when I connected to the server.

My problem is that the last byte array is smaller than 4096 bytes. (almost sure about this)
How to read byte array that’s smaller than expected?

If I understand correctly, you are splitting up a 10240 byte array into multiple packets - up to 4096 per packet.
When reading the last packet, it does not fill up 4096 bytes (10240 / 4096 = 2.5), so the problem is that readBytes(4096) ends up trying to read past the end of the array.

There are a few ways to fix this:

  1. buf.available() returns the number of unread bytes left in the packet. You could read up to this value (or min(4096, buf.available()))
  2. use writeByteArray / readByteArray. This transmits the array length over the wire so you do not need to know ahead of time how large the array is.
  3. Transmit the entire array in one packet.
  4. Implement a custom solution for determining how much data needs to be read into an array.
  1. That should definitely help me!
  2. I could’n find method “writeByteArray” in ByteBuf

Oh these methods on PacketBuffer which is a Minecraft class that adds various helpful read/write methods. Sponge’s ChannelBuf also has most of these methods too. I recommend using PacketBuffer instead.

1 Like