This is part 2 in my article series about how I created "Pizza Peers". If you haven't read the previous article, make sure to do so as it explains the basic idea behind everything. Here's the link: How I Created "Pizza Peers"
So, first things first, we need:
A server that hosts our game (and serves the game files)
A server that receives connections from both the players and the computer, so that it can connect them directly. (Once connected, the whole game goes via peer-to-peer.)
Obviously, we'll use the same server for both these things.
IMPORTANT REMARK: In these articles, I will not show the full code (as it's too complicated and specific, thus not so good for teaching or explaining). Instead, I give a template and some pseudo-code where needed. If you want to implement these things yourself, check out the source code for all the details and exceptions.
Below is the template for this server. It simply sets up a server that servers both static files (which are the game files) and accepts websocket connections (which are needed to connect players with the computer).
Of course, all the magic is going to happen in that "on(message)" block.
Peer to Peer
Which messages do we need to send? For that, we need to understand how peer-to-peer works.
Instead of creating a connection with the server, a peer connection is a direct connection between two devices. So, once established, your smartphone has a direct link with the computer (that hosts the game), and vice versa. This is what makes it so incredibly quick and easy.
However, we cannot allow devices to just establish connections as they please. That would not be very secure.
Instead, there's a handshake protocol we need to follow:
Device A wants to connect with device B
A creates a peer (on their side) and sends out an offer
B receives the offer, creates its own peer, and formulates a response.
Once A has received and validated the response, both are officially connected.
For generating the offer and response signals, I use the simple-peer library. I don't need to understand what it's doing with those signals or what all the information means, and neither do you. I just pass the signals along.
Speaking about that: we've encountered an issue. A needs to send an offer to B. But ... they are not connected yet. How is A going to find B?
That's where our WebSockets come in!
In order to exchange signals, we turn our server into a so-called "signaling server".
The idea is very simple:
A generates a signal and sends it to the server.
The server determines the intended recipient and relays the signal.
B receives it, creates a response, sends it back.
The server determines that the response must return to A.
And voila, peer to peer connection!
So, at our server, we need a way to receive and pass on messages.
In my case, this is even more simplified:
The player (with the smartphone) is always the initiator of the connection.
The computer (that hosts the game) is always the one responding.
See the code below for the basic structure of this system. (It will make even more sense once we've created the client side.)
In fact, this is almost all the code on the server. The only thing I added in the real game is more robust error handling and handling some extra cases/exceptions.
Client side - Websockets
So far, we've created the server only. It accepts web sockets and passes along signals ... but we still need a client side to actually create those web sockets and signals.
I assume you know how to set up a basic HTML page. If not, check out index.html in my source code.
This bit of code opens the web socket connection, then listens for responses from the server. When necessary, it creates a new peer.
The "peer.signal(...)" bit will be explained soon. Essentially, we just pass the "signal message" directly into the peer and let it formulate its response. (Remember when I told you that I don't know what's inside those signals? Those things are put in here.)
NOTE: All messages are JSON. However, we cannot (and don't want to) send objects over the internet. So, before sending, we must always stringify the object. At the receiving end, we always parse it, so it returns to the original JSON object.
Almost done ...
All the code above still does not complete our system for connecting -- so don't try to run out -- but we're very close.
This is what we've achieved so far:
A server that servers our game files.
A server that accepts socket connections, gets messages, and then relays them to the right connection.
A client side that connects with the server, and also sends/receives the right messages, and creates the proper peers when needed.
All that's left to do, is actually create the peers. For that, see you at part 3!