robotman3000 / DragonProxy

A proxy for Minecraft Pocket Edition connecting to Minecraft PC servers, such as Mineplex.
http://dragonet.org
GNU General Public License v3.0
8 stars 1 forks source link

Chunks aren't appearing on the client #5

Open robotman3000 opened 7 years ago

robotman3000 commented 7 years ago

No idea with this one. MiNet documentation suggests something to look into

MrPowerGamerBR commented 7 years ago

Four hours later and I still couldn't figure out how to handle those chunks. Will keep trying...

It doesn't help that you don't receive any feedback from MCPE, because only three things can happen when dealing with packets:

  1. Nothing happens and you can't figure out why
  2. The client crashes and you can't figure out why but at least you can try changing it until it stops crashing
  3. It works

I'm trying to create my own minimal server to debug the issue, already got the client joined the server (really, it was easy), but of course I can't figure out those chunks.

robotman3000 commented 7 years ago

Perhaps we should enlist the help of one of the PocketMine-MP developers.

MrPowerGamerBR commented 7 years ago

@robotman3000 the problem is figuring out what fields are required for a chunk packet

I think it is like that:

Chunk X - VarInt Chunk Z - VarInt Number of sections in this chunk - Byte Sections ??? - Byte (Nukkit always use 0 for this) Blocks ID - Byte Array Blocks Data - Byte Array Blocks Sky Light - Byte Array Blocks Light (? Probably light emitted from blocks like Glowstone) - Byte Array End of Sections Block Height (?) - Byte Array ? - Byte Array (Always 256 in Nukkit) Biome ID Array - Byte Array ? - Byte (Always 0 in Nukkit) Extra Data - Unsigned VarInt (Can be 0 if no extra data) Block Entities - Byte Array

If you send everything, it should work.

Here is what I done (but didn't work)

        RakNetPacket rnp = new RakNetPacket(0xFE);
        rnp.writeByte(0x3a);
        try {
            VarInt.writeVarInt(rnp, 0); // Chunk X
            VarInt.writeVarInt(rnp, 0); // Chunk Z
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        rnp.writeByte((byte) 16); // Send all chunk sections
        int section = 0;
        while (16 > section) {
            rnp.writeByte((byte) 0); // Always zero
            byte[] array = new byte[4096];

            Arrays.fill(array, (byte) 1);
            String build = "";
            for (byte b : array) {
                build += b;
            }
            byte[] blocks = new byte[4096];
            byte[] data = new byte[2048];
            byte[] skyLight = new byte[2048];
            byte[] blockLight = new byte[2048];
            for (int x = 0; x < 16; x++) {
                for (int z = 0; z < 16; z++) {
                    int i = (x << 7) | (z << 3);
                    for (int y = 0; y < 16; y += 2) {
                        blocks[(i << 1) | y] = (byte) 1;
                        blocks[(i << 1) | (y + 1)] = (byte) 1;
                        int b1 = 0;
                        int b2 = 0;
                        data[i | (y >> 1)] = (byte) ((b2 << 4) | b1);
                        b1 = 1;
                        b2 = 1;
                        skyLight[i | (y >> 1)] = (byte) ((b2 << 4) | b1);
                        b1 = 1;
                        b2 = 1;
                        blockLight[i | (y >> 1)] = (byte) ((b2 << 4) | b1);
                    }
                }
            }
            section++;
            ByteBuf bbx = Unpooled.buffer();
            bbx.writeBytes(blocks);
            bbx.writeBytes(data);
            bbx.writeBytes(skyLight);
            bbx.writeBytes(blockLight);
            rnp.write(bbx.array());
        }

        int height = 0;
        while (256 > height) {
            rnp.writeByte((byte) height);
            height++;
        }

        rnp.write(new byte[256]);
        rnp.write(new byte[1]);
        rnp.writeByte((byte) 0);
        try {
            VarInt.writeUnsignedVarInt(rnp, 0);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        rnp.write(new byte[0]);
        packet = rnp;

The problem with MCPE development is the lack of a good, explained, protocol documentation, the PC version has a very extensive and good protocol documentation, the only similar thing that exists is yawk.at's MCPE documentation, but that's outdated since 0.15.

MrPowerGamerBR commented 7 years ago

6 days later and I still couldn't figure out how to send chunks

I tried doing this

package com.magicalpocket.network.packets;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Map;

import com.magicalpocket.utils.VarInt;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import net.marfgamer.jraknet.RakNetPacket;

public class FullChunkDataPacket extends DataPacket {
    public int x;
    public int z;

    @Override
    public void decode() {

    }

    @Override
    public void encode() {
        System.out.println("Chunk data...");

        RakNetPacket rnp = new RakNetPacket(0xFE);
        rnp.writeByte(0x3a);
        try {
            VarInt.writeVarInt(rnp, x); // Chunk X
            VarInt.writeVarInt(rnp, z); // Chunk Z
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        rnp.writeByte((byte) 16); // Chunk sections (?)
        for (int i = 0; i < 16; i++) {
            rnp.writeByte((byte) 0);

            ByteBuffer buffer = ByteBuffer.allocate(10240);
            byte[] blocks = new byte[4096];
            byte[] data = new byte[2048];
            byte[] skyLight = new byte[2048];
            byte[] blockLight = new byte[2048];
            for (int x = 0; x < 16; x++) {
                for (int z = 0; z < 16; z++) {
                    int temporary = (x << 7) | (z << 3);
                    for (int y = 0; y < 16; y += 2) {
                        blocks[(temporary << 1) | y] = (byte) 1;
                        blocks[(temporary << 1) | (y + 1)] = 1;
                        int b1 = 1;
                        int b2 = 1;
                        data[temporary | (y >> 1)] = (byte) ((b2 << 4) | b1);
                        b1 = 1;
                        b2 = 1;
                        skyLight[temporary | (y >> 1)] = (byte) ((b2 << 4) | b1);
                        b1 = 1;
                        b2 = 1;
                        blockLight[temporary | (y >> 1)] = (byte) ((b2 << 4) | b1);
                    }
                }
            }

            rnp.write(buffer
                    .put(blocks)
                    .put(data)
                    .put(skyLight)
                    .put(blockLight)
                    .array());

        }
        int[] heightMap = new int[256];
        Arrays.fill(heightMap, 1);
        for (int height : heightMap) {
            rnp.writeByte((byte) height);
        }
        rnp.write(new byte[256]);
        rnp.write(new byte[256]);
        rnp.writeByte((byte) 0);
        try {
            VarInt.writeUnsignedVarInt(rnp, 0);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        rnp.write(new byte[0]);

        packet = rnp;
    }
}

But nope, didn't work.

MrPowerGamerBR commented 7 years ago

Tried replaying the data sent by Nukkit when sending a chunk and...

Also didn't work.

robotman3000 commented 7 years ago

Try replaying an exact packet stream from Nukkit and slowly remove packets, to see what is required to get chunks to appear

MrPowerGamerBR commented 7 years ago

@robotman3000 I will try doing that later.

In theory, my code should work, but it doesn't.

package com.magicalpocket.network.packets;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Map;

import com.google.gson.Gson;
import com.google.gson.stream.JsonReader;
import com.magicalpocket.utils.VarInt;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import net.marfgamer.jraknet.RakNetPacket;

public class FullChunkDataPacket extends DataPacket {
    public int x;
    public int z;

    @Override
    public void decode() {

    }

    @Override
    public void encode() {
        System.out.println("Chunk data...");

        RakNetPacket rnp = new RakNetPacket(0xFE);
        rnp.writeByte(0x3a);
        try {
            VarInt.writeVarInt(rnp, 0); // Chunk X
            VarInt.writeVarInt(rnp, 0); // Chunk Z
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        Gson gson = new Gson();
        JsonReader reader;
        try {
            reader = new JsonReader(new FileReader("D:\\Minecraft Servers\\Nukkit\\nukkitdata.txt"));
            byte[] wow = gson.fromJson(reader, byte[].class);
            try {
                VarInt.writeUnsignedVarInt(rnp, wow.length);
                rnp.write(wow);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        packet = rnp;
    }
}

But nope, NOTHING happens.