deathcap / wsmc

WebSocket proxy to Minecraft
34 stars 10 forks source link

Binary websockets #1

Closed deathcap closed 10 years ago

deathcap commented 10 years ago

ArrayBuffer on browser side and Buffer on server is supported with: https://github.com/maxogden/websocket-stream

deathcap commented 10 years ago

Testing using websocket-stream but there is an impedance mismatch with the binary data types, Node's native binary data is stored as Buffers, but browsers store as typed arrays - ArrayBuffers, including views Uint8Array, etc. node-minecraft-protocol parsePacket() accepts a Buffer. It is possible to convert between the two, but:

http://nodejs.org/api/buffer.html#buffer_buffer

A Buffer object can also be used with typed arrays. The buffer object is cloned to an ArrayBuffer that is used as the backing store for the typed array. The memory of the buffer and the ArrayBuffer is not shared.

NOTE: Node.js v0.8 simply retained a reference to the buffer in array.buffer instead of cloning it.

While more efficient, it introduces subtle incompatibilities with the typed arrays specification. ArrayBuffer#slice() makes a copy of the slice while Buffer#slice() creates a view.

Testing with:

var buffer = new Buffer(data); // where data is a Uint8Array from an ArrayBuffer from the websocket-stream

is drastically inefficient, so much that mcwebchat is practically unusable - it works, but there is massive lag as the packets are copied, it is even slower than decoding packets on the server, sending the decoded fields as JSON, and parsing the JSON on the client(!)

http://stackoverflow.com/questions/8609289/convert-a-binary-nodejs-buffer-to-javascript-arraybuffer confirms this is slow

Found this: https://npmjs.org/package/memcpy

node-memcpy: Copies data between node Buffers and/or ArrayBuffers up to ~75 times faster than in pure JS.

node.js utilizes a non-standard concept of I/O buffers and thus has both its Buffer as well as ArrayBuffer support. While Buffers are nice because they are a lot faster than V8's ArrayBuffers, transferring data between those two types can be ridiculously slow. This is where a node module like memcpy comes into play.

But it is a native module (C++ bindings), cannot be used in the browser (and not suitable for asm.js). Probably the best solution right now is, as node-memcpy's author says:

Please keep in mind that - besides the nice numbers - this is still to be considered experimental. I'd love if you'd review the C++ code to validate that it's safe. I can't yet think of a sane use case, though, as just sticking with Buffers on node.js and ArrayBuffers in the browser should be best practice.

which means porting node-minecraft-protocol to use ArrayBuffers instead of Buffers, for use in the browser :/

deathcap commented 10 years ago

or this? https://npmjs.org/package/view-buffer "Using the magic of Proxies a ViewBuffer is a polymorphous buffer interface that allows each instance to encompass all the features of DataView, Buffer, and the Typed Arrays"

even better: https://npmjs.org/package/native-buffer-browserify "buffer module compatibility for browserify (backed by ArrayBuffer so its fast!)"

edit: turns out native-buffer-browserify is already included in Browserify 3 (I was using 2.35.1), time to update.. (3.19.0)

deathcap commented 10 years ago

Solved - the unbearable lag was actually caused by a different unrelated type conversion bug.. fixed in: https://github.com/deathcap/node-minecraft-protocol/commit/c1d410acf965f768eb28feb06fe95b23b8f644a2