node-minecraft-protocol icon indicating copy to clipboard operation
node-minecraft-protocol copied to clipboard

WebSocket support

Open rom1504 opened this issue 10 years ago • 15 comments

How hard would it be to use a websocket connection instead of tcp in the server ? (and both ?)

That would allow anybody to connect through a web browser to connect to flying-squid using https://github.com/voxel/voxel-clientmc

rom1504 avatar Jan 06 '16 13:01 rom1504

relevant https://github.com/deathcap/wsmc

Gjum avatar Jan 07 '16 18:01 Gjum

Yeah sure I know about wsmc, but it would be cool to have native support for that in nmp.

rom1504 avatar Jan 07 '16 18:01 rom1504

:+1: for native ws in nmp. Currently wsmc is broken, I think the relevant bits could be easier to maintain in sync with this repository if they were a part of it.

deathcap avatar Jan 09 '16 05:01 deathcap

Or maybe, refactor node-minecraft-protocol to operate on node.js streams instead of TCP sockets? References:

TCP: https://github.com/PrismarineJS/node-minecraft-protocol/blob/master/src/createClient.js Stream: https://github.com/deathcap/wsmc/blob/master/minecraft-protocol-stream.js

Since streams are an abstract interface, using them with websockets is as simple as piping to websocket-stream. Although some options may still be needed to control various other tweaks (e.g. for websockets you likely want to disable MC protocol encryption, since it is slow in JavaScript and wss:// URLs (analogous to https://) can be used instead for encryption). Useful handbook on streams:

https://github.com/substack/stream-handbook

edit: also, for web sockets you probably don't want Minecraft's packet framing, since the ws protocol https://html.spec.whatwg.org/multipage/comms.html#server-sent-events-intro already has its own framing

deathcap avatar Jan 09 '16 06:01 deathcap

NPm already operates on streams. All you have to do is call setConnection with your stream?

On Sat, Jan 9, 2016, 7:06 AM deathcap [email protected] wrote:

Or maybe, refactor node-minecraft-protocol to operate on node.js streams https://nodejs.org/api/stream.html#stream_stream instead of TCP sockets? References:

TCP: https://github.com/PrismarineJS/node-minecraft-protocol/blob/master/src/createClient.js Stream: https://github.com/deathcap/wsmc/blob/master/minecraft-protocol-stream.js

Since streams are an abstract interface, using them with websockets is as simple as piping to websocket-stream https://www.npmjs.com/package/websocket-stream. Although some options may still be needed to control various other tweaks (e.g. for websockets you likely want to disable MC protocol encryption, since it is slow in JavaScript and wss:// URLs (analogous to https://) can be used instead for encryption).

https://github.com/substack/stream-handbook

— Reply to this email directly or view it on GitHub https://github.com/PrismarineJS/node-minecraft-protocol/issues/312#issuecomment-170200699 .

roblabla avatar Jan 09 '16 13:01 roblabla

But the createClient() API accepts host and port options, which it passes to net.connect() to get a stream for setSocket() — I'd like to pass createClient a stream directly.

deathcap avatar Jan 12 '16 05:01 deathcap

Being able to pass a stream would be useful for https://github.com/PrismarineJS/node-minecraft-protocol/issues/185 too.

rom1504 avatar Jan 12 '16 10:01 rom1504

Actually, one of the point of the old modularization work (https://github.com/PrismarineJS/node-minecraft-protocol/pull/210) was to help mitigate this. You'd use the raw Client API and then attach the modulese to it, so you could pass whatever stream you want to Client. I never finished that PR, but the idea's there.

roblabla avatar Jan 12 '16 12:01 roblabla

It's a good idea, the changes in modularizing NMP I think would be useful for websocket support:

  • [ ] Protocol stream access: allow connecting NMP directly to a Stream, which could be either a TCP socket or WS socket or something else, update: like in voxel-clientmc, I use a duplexer stream and connect NMP to a webworker, instead of a websocket directly (currently I have my own createClient taking a stream option, calls client.setSocket(stream))
  • [ ] Handshaking: allow skipping the HANDSHAKING phase, not necessary/feasible for WS — this also disables packet encryption, can use WSS (≈ HTTPS) instead (currently I set client.state = states.LOGIN upon receiving the connect event)
  • [ ] Framing: allow disabling MC packet framing (length prepending), as WS has its own, and multiple writes cause packet fragmentation with WS unlike TCP with Nagle's algorithm https://github.com/PrismarineJS/node-minecraft-protocol/pull/316 (currently I set client.framer to an empty transform stream)
  • [ ] Compression: allow disabling compression, since there is a websocket extension with per-frame compression (currently I'm not using this, very new only standardized last month, but would like to https://github.com/deathcap/wsmc/issues/23)

If NMP is sufficiently modularized, it should be possible to build all of the above from modular components, taking only what is needed from this module.


There's also the question of where websocket support should live exactly, e.g. should node-minecraft-protocol have websocket-stream as a dependency? On one hand, there is an argument for keeping NMP small and minimal, allowing other modules to build on top of it. On the other hand, having built-in WS support could greatly help ease WS/MC adoption - meaning that, for example, someone writing a client/server/proxy with NMP could trivially interoperate with WS client/server/proxies.

https://github.com/feross/webtorrent faces a similar problem: they have implemented bittorrent in the browser, but cannot interoperate with peers only supporting the original native TCP/UDP BT protocol. So they are advocating other bittorrent software (even native apps) adopt browser support so they can talk to each other (WebRTC in their case, but it is analogous to WebSockets in this project).

deathcap avatar Jan 17 '16 21:01 deathcap

Prototyping sort of what I had in mind in: https://github.com/PrismarineJS/node-minecraft-protocol/pull/320 Client stream - factoring out the common code (keep alive, login state change, compression request), which both WS and TCP sockets can use.

deathcap avatar Jan 18 '16 21:01 deathcap

BTW, it's worth noting that we disable Nagle's algorithm. So we actually send two TCP frames, one with the ID alone, one with the content. I consciously chose this on-the-wire overhead over recreating a buffer for the sole purpose of doing only a single, clean write.

That being said, with WS being message-based, (vs stream-based), having a framer makes absolutely no sense there ^^.

roblabla avatar Jan 18 '16 21:01 roblabla

Oh and concerning websocket support directly IN NMP, I think it would be great if NMP worked easily with WS, but I'm a bit iffy with having it completely built-in. That is to say : patches to make websocket usage work better are great, but having a dependency on websocket is meh.

What would totally make sense, however, is having a server implementation based on NMP (E.G. : Flying-Squid) support WS natively (for instance, by running two instances of NMP, one with a websocket server stream, one with the usual tcp server stream, and synching the two).

roblabla avatar Jan 18 '16 21:01 roblabla

For the multiple writes wrt Nagle, I think that's worth discussing further, in https://github.com/PrismarineJS/node-minecraft-protocol/pull/316

Not having a direct dependency on websocket-stream in NMP, looking into this some more, I agree. The most generic API that NMP could expose is a plain Node.js Stream, which is compatible with websocket-stream, duplexer, websocket-stream, etc. (I use all these for various purposes, so a built-in ws stream would only be marginally useful). I first passed this in through options.stream, but @rom1504 has a point, callers can just use client.setSocket() - updated https://github.com/PrismarineJS/node-minecraft-protocol/pull/320 accordingly.

#320 factors out all of the TCP-specific stuff into a separate file (DNS SRV lookup, net.connect, encryption/authentication/yggdrasil), this way WS clients (or other non-TCP clients — WebRTC P2P?) can use NMP without this baggage.

The remaining problem is disabling framing — cleanly —, #320 has a hack by setting the framer to an empty stream, not sure of the best way to do this.

deathcap avatar Jan 18 '16 22:01 deathcap

To fix that problem, we really, really need the pipeline module outlined in #176

roblabla avatar Jan 19 '16 00:01 roblabla

Have we gotten any updates regarding this issue?

ariesclark avatar Jan 10 '20 19:01 ariesclark