WebSockets minimal library ( m.net.WebSocket )

For real-time multiplayer games, html5 has something great called WebSockets. It allows to establish bi-directional communication between server and clients, enables messaging in both directions without need of pulling or make request/response for each message.
WebSockets enable efficiency in terms of real-time communication. And it has great applications for multiplayer games. They are supported more than WebGL, so by using playcanvas - we are guaranteed that it will be supported by platform, so there is no need to fallback to any other technology (like socket.io does for example). So library can be very minimal.

I’ve took advantage of hack-ability of java-script, and “extended” pc.net object (playcanvas network library) with WebSocket class, which is small wrapper around native WebSocket.
Module is in namespace: m.net.ws

For server side, node.js been used and shares pretty much most of code. Those who’ve been using socket.io will be very familiar with semantics for this module.

Git: https://github.com/Maksims/m.net.ws

Simple example is described below in git README, so all is required node.js for server side, npm install m.net.ws, and add script to your root node m.net.ws.js, then you are ready to use it (connect and messaging).

Soon there will be simple demos published showcasing simple use of such library.
Regarding extending pc.net.WebSocket if playcanvas devs this that it is not good idea, then I am happy to change it (moka.WebSocket or something less personal).

Pull Requests, as well as suggestions and bug reports are welcome!

3 Likes

Hey Max,

We do actually have an undocumented wrapper around the WebSocket object called pc.net.Socket. Not sure why I never documented, presumably because it’s not used else where in the engine. It looks like this:

pc.extend(pc.net, function () {
    var Socket = function (url) {
        this._ws = new WebSocket(url);

        this._ws.onopen = this._handleOpen.bind(this);
        this._ws.onerror = this._handleError.bind(this);
        this._ws.onmessage = this._handleMessage.bind(this);
        this._ws.onclose = this._handleClose.bind(this);
    };

    Socket.prototype = {
        onopen: null,
        onerror: null,
        onmessage: null,

        get binaryType () {
            return this._ws.binaryType;
        },

        set binaryType (type) {
            this._ws.binaryType = type;
        },

        get readyState () {
            return this._ws.readyState;
        },

        get bufferedAmount () {
            return this._ws.bufferedAmount;
        },

        get extensions () {
            return this._ws.extensions;
        },

        get protocol () {
            return this._ws.protocol;
        },

        _handleOpen: function () {
            if (this.onopen) {
                this.onopen();    
            }
        },

        _handleError: function (error) {
            if (this.onerror) {
                this.onerror(error);
            }
        },

        _handleMessage: function (msg) {
            if (this.onmessage) {
                this.onmessage(msg);
            }
        },

        _handleClose: function () {
            if (this.onclose) {
                this.onclose();
            }
        },

        send: function (msg) {
            this._ws.send(msg);
        }
    };

    return {
        Socket: Socket
    };
}());

It looks like you’re doing some extra event firing? Where as pc.net.Socket is a like for like wrapper around WebSocket, basically there just incase we have to provide support for a platform that doesn’t have native WebSocket and we need to provide some sort of shim without breaking the API.

Regarding namespacing, I’d probably recommend that you don’t plug stuff into the core PlayCanvas namespace. Whilst it’s probably OK in this case, I wouldn’t want it to become a thing. I would suggest, either that you choose your own, or we could reserve a portion of the pc namespace for external libs. Something like: pc.x.*?

Hi @dave.
I have found actually existing WebSockets around there, but the idea is to have wrapper by intention in order to abstract serialisation process for JSON/Binary/String data from developer (let library to take care of that).
As well as to keep socket.io semantics with message names ( emit(name, data), on(name, callback) ) which is much more usefull in longer term and subscribing to different messages, rather than writing by yourself “router” in on('message') event.

Based on: http://caniuse.com/websockets and http://caniuse.com/webgl WebSockets have wider support than WebGL, so that means it is “guaranteed” that playcanvas user will have WebSockets support so fallback to another protocol is not required.

Regarding namespaces - I will defininetelly create own one then, I totally aggree that poluting pc namespace is not a good idea for third-party developers.
Renamed under namespace: m.net.ws

One of example test application with ‘naive’ physics synchronisation (open in 2 tabs): http://boxes.moka.co/

I love that demo!

Just looking through the code, it looks like you are running the simulation on the server and updating the transforms from the local updates, but also running the simulation on each client, is that right? Have you tried running without the simulation on the client?

And you’re right to add additional features to your socket, pc.net.Socket was intentionally just a raw WebSocket.

Actually in this demo, as it was just a very quick (2 hours) I’ve decided not to do any simulation on the server.
So clients do all physics. Server is only exchange point.

Each physical box has an owner, usually it is person who created the box. The owner (client) will periodically send data to server of that object, which immediately will be published to everyone, and every other client will rudely snap object to data been provided.
While they all simulate it as well, so it is “extrapolation” out of the box.

There are few cos and pros in such approach:

cos:

no intensive calculations on server so computation is spread between clients.
pros:

cheating - in order to prevent one owner of deciding things, there should be “shared ownership” and server should validate their data for consistency.
latency implications - as there is no centralised computation node, different latency might result in inconsistent positioning of objects. To prevent this “rude snapping” should be prevented, and data from owners should be used only to smoothly correct simulation.
This was just a proof of concept.
In fact server code in this example is about 80 lines of code.

For most games where players are kinetic, and physics has only visual purpose, this is perfect solution.
Event WebRTC can be used in order to sync the physics in p2p manner (as optional feature of game engine), so clients without WebRTC will simulate physics but not in sync mode.
To keep things simple, I do not want to implement full physics on server side… :slight_smile: just simple bullet collision (which on flat world is line intersections), and simple players physics (circle intersections), and path finding (own algorithm based on line intersections again).
For the core of top-down shooter, that should be enough/

Interesting, in which case, I’m amazed at how smooth it seemed. I saw no snapping at all.

Unless you will have 3G mobile :wink: with inconsistent latency changes.

Heh :no_mobile_phones:

I don’t know, but it works out of the box on Nexus 5 :slight_smile: (except of some shadowmaps PCF artefacts)

I haven’t pulled your code yet, but I’m anxious to do so. What I’d like to do is gather requirements for a multiplayer pack. I have created a topic here: Requirements for a multiplayer pack

Hi @moka,
For the server side of m.net.WebSocket, how can I create a broadcast?

Thanks!

Hey @nguyenshane
You can have your own list of clients, like so:

var clients = { };
server.on('connection', function(client) {
    clients[client.id] = client;

    client.on('disconnect', function() {
        delete clients[client.id];
    });
});

And then send messages to all clients like so:

for(var id in clients) {
    clients[id].send('message', { some: 'data' });
}

Feel free to experiment will all this before actually asking question, as this question feels like you haven’t tried to solve your challenge first. It is better to try, that way you will learn more. And then if after trying different ways, you feel a problem there, feel free to ask.

Hi moka, Im a newbie here so I need a bit of help:
I want to create something with playcanvas & websockets so I was looking at your Tanx project that uses the m.net.ws thing.
Where is the server side code?
I mean the client sends information (shoot, move, etc…) through the socket but I dont see the create server part…

var server = new WebSocketServer({ port: 8080 });

…anywhere so I’m asking if maybe it is not needed or something.

Cheers and congratulations for the great game and code!

You might be interested in the server code I am creating with socket.io. See https://github.com/coderextreme/pc.multiplayer It uses a new api, not one based on the library mentioned above. Suggestions and contributions are welcome!

Hey @sugarmaster, yes there is some server side code, you can download it from here.
In case of tanx, server side does all game logic, which is called authoritative server, so to prevent client-side decisions that could be exploited (cheating).

I’ve got some fairly standalone node.js or io.js code which will run a socket.io server. It’s a script, and I’d like to run it in a PlayCanvas environment. I don’t know what to do to pull the PlayCanvas API in though. I need pc, pc.script and pc.script.create.

Hey just think about what use playcanvas for multiplayer games, for example the tanx game use

nodeJs + sockjs you can use it in private the way between player and server or public “broadcast” the way between all players and server in real time.

if you want an example send me a message i will replyed asap !

WebSockets do not provide peer-to-peer functionality, it is purely for a server-client communication model.
For peer-to-peer you need to use WebRTC.

TANX used to use sockjs, but we’ve switched to engine.io

hey Max thank you for the

engine.io

i didn’t used yet but i will soon.

I assume you mean this: https://github.com/socketio/engine.io not this: http://engine.io?

1 Like