voxel / voxel-clientmc

Minecraft client using WebSockets and voxel-engine (voxel.js plugin)
57 stars 13 forks source link

Optimize chunk processing from mineflayer blockAt #14

Closed deathcap closed 9 years ago

deathcap commented 9 years ago

Each chunk takes about 350 ms to process (improved by GH-9 mfworker, GH-5 use mineflayer), using blockAt on each block, which performs unnecessary work:

 function blockAt(absolutePoint) {
    var loc = new Location(absolutePoint);
    var key = columnKeyXZ(loc.chunkCorner.x, loc.chunkCorner.z);
    var column = columns[key];
    // null column means chunk not loaded
    if (! column) return null;
    var blockType = bite(column.blockType);
    var nibbleIndex = loc.blockIndex >> 1;
    var lowNibble = loc.blockIndex % 2 === 1;

    var biomeId = column.biome.readUInt8(loc.biomeBlockIndex);

    var block = new Block(blockType >> 4, biomeId);
    block.metadata = blockType & 0x0f;
    block.light = nib(column.light);
    block.skyLight = nib(column.skyLight);
    block.add = nib(column.add);
    block.position = loc.floored;
    block.signText = signs[loc.floored];
    block.painting = paintingsByPos[loc.floored];

    return block;

See if this can be optimized, possibly by enhancing the mineflayer API, maybe with something like forEachBlock https://github.com/deathcap/voxel-clientmc/issues/5#issuecomment-85180048

Note: the raw block data is stored in columns.blockType, as a Buffer, each block 16 bits, but it is a private variable (although would be very convenient to access as a Uint16Array, converting Buffer to ArrayBuffer then taking the 16-bit data view). maybe direct access is not needed for acceptable performance; would have to investigate forEachBlock first

deathcap commented 9 years ago

Testing with https://github.com/deathcap/mineflayer/commits/chunkarray

Extremely faster, before took 300-400 ms, now:

chunk added in 38.17100008018315
Chunk load (-224,0,240)
Created new chunk -7|0|7
chunk added in 18.40999978594482
Chunk load (-224,0,256)
Created new chunk -7|0|8
chunk added in 15.504000009968877
Chunk load (-240,0,256)
Created new chunk -8|0|8
chunk added in 16.607000026851892
Chunk load (-256,0,256)
chunk added in 17.327000154182315
Chunk load (-256,0,240)
chunk added in 16.90100017003715
Chunk load (-256,0,224)
chunk added in 16.116000013425946
Chunk load (-240,0,224)
chunk added in 15.004999935626984
Chunk load (-224,0,224)
chunk added in 17.11999997496605
Chunk load (-208,0,224)
chunk added in 15.481000067666173

20x speedup!

deathcap commented 9 years ago

@rom1504 @roblabla would a PR to add something like this to mineflayer be acceptable?

// lib/plugins/blocks.js

   bot.blockAt = blockAt;
+  bot._chunkColumn = function(x, z) {
+    var key = columnKeyXZ(x, z);
+    return columns[key];
+  }

this is what I'm using in this project to access the chunk data array, instead of blockAt, for better performance. Granted it does expose internal implementation details, but the leading underscore signifies this is not part of the public, stable, API of mineflayer (the chunk data format has changed between MC versions, and clients wanting to support other versions (after https://github.com/andrewrk/mineflayer/issues/199 multi-protocol support) will have to change accordingly). The tradeoff is extremely fast (no iteration) chunk data access for clients that need it.

A new forEachBlockAt(start,size,function(block){}) public API call in mineflayer may be possible, to provide stability across versions, not necessarily a bad idea, but it would be more complex to implement, only fastest when start==a chunk corner, size==chunk size, still have the overhead of Block object creation and function calls and iteration, likely won't be as fast (but I haven't quantified the difference, _chunkColumn vs forEachBlock vs blockAt), is more complicated, and it hasn't been written yet. So I think a _chunkColumn private call is most expedient. Note there's currently no way for the columns variable to be accessed outside of mineflayer because it is in a closure (a true "private member variable" in JavaScript http://www.crockford.com/javascript/private.html).

roblabla commented 9 years ago

If you expose it like this, I guess I'm OK with it. Related to andrewrk/mineflayer#246

deathcap commented 9 years ago

Sweet. Updated wsmc back to mineflayer master branch in https://github.com/deathcap/wsmc/commit/7e5a9045b9ecf85faefd4ea71fcf3f8f5d752509; _chunkColumn access in voxel-clientmc in https://github.com/deathcap/voxel-clientmc/commit/189472f47a3d05186989d05cd71db400cadf1214