The fastest way to send a chunk

Discussion in 'Resources' started by bergerkiller, Dec 27, 2011.

Thread Status:
Not open for further replies.
  1. Offline

    bergerkiller

    Today implemented the FASTEST POSSIBLE WAY YOU CAN SEND A CHUNK. This monster is even faster than the native chunk sender, which first executes several mathematical formulas.
    Code:
        public static void sendChunk(net.minecraft.server.Chunk c, EntityPlayer player) {
            byte[] data = new byte[81920];
            System.arraycopy(c.b, 0, data, 0, 32768);
            System.arraycopy(c.g.a, 0, data, 32768, 16384);
            System.arraycopy(c.i.a, 0, data, 49152, 16384);
            System.arraycopy(c.h.a, 0, data, 65536, 16384);
    
            player.netServerHandler.sendPacket(new Packet51MapChunk(c.x << 4, 0, c.z << 4, 16, 128, 16, data));
            Packet p;
            for (Object o : c.tileEntities.values()) {
                p = ((TileEntity) o).k();
                if (p != null) player.netServerHandler.sendPacket(p);
            }
        }
    If only it was possible to buffer the data array, but this can cause a lot of strange client glitches. (duplicated chunks, what a laugh)

    I couldn't help myself but to buffer the data array as well. Probably the fastestest possible :)

    Code:
    package com.bergerkiller.bukkit.nolagg;
    
    import java.io.DataOutputStream;
    import java.io.IOException;
    import java.lang.reflect.Field;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Map;
    import java.util.logging.Level;
    
    import net.minecraft.server.Packet;
    import net.minecraft.server.Packet51MapChunk;
    
    public class BufferedPacket51MapChunk extends Packet51MapChunk {
    
        private static List<Integer> freeIndices = new ArrayList<Integer>();
        private static List<byte[]> buffers = new ArrayList<byte[]>();
    
        public final int bufferindex;
        public BufferedPacket51MapChunk(net.minecraft.server.Chunk chunk) {
            if (freeIndices.isEmpty()) {
                this.rawData = new byte[81920];
                this.bufferindex = buffers.size();
                buffers.add(this.rawData);
            } else {
                this.bufferindex = freeIndices.remove(0);
                this.rawData = buffers.get(this.bufferindex);
            }
            System.arraycopy(chunk.b, 0, this.rawData, 0, 32768);
            System.arraycopy(chunk.g.a, 0, this.rawData, 32768, 16384);
            System.arraycopy(chunk.i.a, 0, this.rawData, 49152, 16384);
            System.arraycopy(chunk.h.a, 0, this.rawData, 65536, 16384);
            this.a = chunk.x << 4;
            this.c = chunk.z << 4;
            this.d = 16;
            this.e = 128;
            this.f = 16;
        }
    
        public void a(DataOutputStream dataoutputstream) throws IOException {
            super.a(dataoutputstream);
            freeIndices.add(this.bufferindex);
        }
    
        static {
            try {
                Field a = Packet.class.getDeclaredField("a");
                a.setAccessible(true);
                Map map = (Map) a.get(null);
                map.put(BufferedPacket51MapChunk.class, 51);
            } catch (Exception e) {
                NoLagg.log(Level.SEVERE, "Failed to bind to map packet chunk, total failure imminent!");
                e.printStackTrace();
            }
        }
    }
    
    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: May 22, 2016
    ferrybig likes this.
  2. Offline

    rtcabooservb

    @bergerkiller I assume this is in the newest nolagg build? I hope? :p
     
  3. Offline

    bergerkiller

    @rtcabooservb yup lol, going to upload the updated version today too. Total update times (this INCLUDES chunk sending and unloading) is lower than 1 ms / tick on average, opposed to the 6 it was before. Haven't noticed 'can't keep up' messages ever since, even when exploring vast new area's.

    But, still need to work on the stack forming, it takes 200 ms to update 1000 items. This is way too high, I need to ignore items where possible.
     
  4. Offline

    rtcabooservb

    I would love to know when the build is available to use. :p
     
  5. Mind = Blown
    Nice job, thats pretty damn fast :p
     
  6. Offline

    immac636

    How do you get an EntityPlayer? This would prove extremely useful, if I knew how to use it.

    Also, if I use the top bit of code in my plugin should I credit you? Thx.
     
  7. Offline

    bergerkiller

    @rtcabooservb Yeah, it's in NoLagg 1.54.5 and above. Had several fix attempts for the stack former...therefore 3-4 minor version increments on one day :)

    @immac636 Feel free to use it, it was basic copying from the native source code anyway. Only had to pre-calculate the values I already knew and scratch away the unused values.

    Do note that I now use a complete buffered map chunk implementation for it in Nolagg:
    https://github.com/bergerkiller/NoL...r/bukkit/nolagg/BufferedPacket51MapChunk.java

    It uses a weak reference to the chunk instead of keeping a list of indices, since it would cause a memory leak if the chunk was obtained by another plugin in the sendPacket function.
     
  8. Offline

    immac636

    Is it okay if I do this:
    Code:
    public static void sendChunk(net.minecraft.server.Chunk c, Player t) {
            EntityPlayer player = (EntityPlayer) t;
    so that I can actually define the EntityPlayer? I'm confused about this. (I'm trying to make it so that it doesn't lag when people using my plugin teleport or warp).
     
  9. Offline

    Sir Savary

    If my brain is functioning as it should, I believe taking the method from the first post and leaving it alone will work just fine. Then, when a player teleports, wait until the teleportation has finished then do something along the lines of:

    Code:
    SendChunk(player.getLocation().getChunk(), player)
     
  10. Nope, to get a EntityPlayer you do (Requires CB)
    Code:java
    1.  
    2. EntityPlayer entityplayer = ((CraftPlayer) player).getHandle();
    3.  

    @immac636 @Sir Savary
     
  11. Offline

    immac636

    THANK YOU! :D

    Edit: Should have asked this before, but how do you get a net.minecraft.server.Chunk from Player.getLocation().getChunk() ?

    Edit 2: I'm stupid. Figured it out.

    This is lightning fast! Thanks bergerkiller!
     
  12. Any time :)
     
  13. Offline

    immac636

    You too, tips.
     
  14. Offline

    rymate1234

    Maybe you should ask mojang to implement this in the main minecraft :p
     
  15. Surely those mathematical formulae are there for a reason?
     
  16. Offline

    FalseVacuum

    bergerkiller Does this still work? If it does, that's awesome!
     
  17. Offline

    bergerkiller

    FalseVacuum no, it no longer works, since the chunk packet format got changed, as well as the chunk. The slices are now variable, so they got changed kind of.

    This is the packet gen I use in NoLaggChunks:
    Code:
        public static Packet51MapChunk createPacket(Chunk chunk, byte[] rawData) {
    Packet51MapChunk mapchunk = new Packet51MapChunk();
    mapchunk.a = chunk.x;
    mapchunk.b = chunk.z;
    mapchunk.rawData = rawData;
    fill(chunk, mapchunk);
    return mapchunk;
        }
       
        public static void fill(Chunk chunk, Packet51MapChunk packet) {
    packet.f = true;
            ChunkSection sections[] = chunk.h();
            int j = 0;
            int k = 0;
            int l;
            for(l = 0; l < sections.length; l++)
            {
                if(sections[l] == null || sections[l].a() || (1 << l) == 0) {
                    continue;
                }
                packet.c |= 1 << l;
                j++;
                if(sections[l].h() != null) {
                    packet.d |= 1 << l;
                    k++;
                }
            }
     
            l = 2048 * (5 * j + k) + 256;
            if (packet.rawData == null || packet.rawData.length < l) {
            packet.rawData = new byte[l];
            }
           
            int length = 0;
            for(int j1 = 0; j1 < sections.length; j1++) {
                if(sections[j1] != null && !sections[j1].a())
                {
                    byte abyte1[] = sections[j1].g();
                    System.arraycopy(abyte1, 0, packet.rawData, length, abyte1.length);
                    length += abyte1.length;
                }
            }
            for(int j1 = 0; j1 < sections.length; j1++) {
                if(sections[j1] != null && !sections[j1].a())
                {
                    NibbleArray nibblearray = sections[j1].i();
                    System.arraycopy(nibblearray.a, 0, packet.rawData, length, nibblearray.a.length);
                    length += nibblearray.a.length;
                }
            }
            for(int j1 = 0; j1 < sections.length; j1++) {
                if(sections[j1] != null && !sections[j1].a())
                {
                    NibbleArray nibblearray = sections[j1].j();
                    System.arraycopy(nibblearray.a, 0, packet.rawData, length, nibblearray.a.length);
                    length += nibblearray.a.length;
                }
            }
            for(int j1 = 0; j1 < sections.length; j1++) {
                if(sections[j1] != null && !sections[j1].a())
                {
                    NibbleArray nibblearray = sections[j1].k();
                    System.arraycopy(nibblearray.a, 0, packet.rawData, length, nibblearray.a.length);
                    length += nibblearray.a.length;
                }
            }
            if(k > 0)
            {
                for(int j1 = 0; j1 < sections.length; j1++) {
                    if(sections[j1] != null && !sections[j1].a() && sections[j1].h() != null)
                    {
                        NibbleArray nibblearray = sections[j1].h();
                        System.arraycopy(nibblearray.a, 0, packet.rawData, length, nibblearray.a.length);
                        length += nibblearray.a.length;
                    }
                }
            }
            byte abyte2[] = chunk.l();
            System.arraycopy(abyte2, 0, packet.rawData, length, abyte2.length);
            length += abyte2.length;
        }
     
  18. Offline

    FalseVacuum

    bergerkiller What is the difference between this one and the one in the source code? It looks a lot like the one in Packet51MapChunk.java
     
  19. Offline

    bergerkiller

    FalseVacuum no real difference other than that I removed all checks for 'packet.f' as it was always true anyway, and that it no longer forces the creation of the raw data array. (I had to make that my own buffer array or it wouldn't work)
     
Thread Status:
Not open for further replies.

Share This Page