Socket.io Server
Overview
In this section we will work on making the game online and realtime. Player's will be able to connect and play together remotely over the internet.
For this, we will create a server that is going to connect the players and share events such as player moves between them. Our server will be making real-time communication with the connected players' browsers through the Websockets protocol
To get started, let's create a new folder outside of our current chess-client
directory (or whatever your name for this project is), save that folder as chess-server
.
Open your terminal, navigate to the chess-server
folder and run
This initialized a new npm project and created a package.json
file to manage our dependencies. Let's install the packages we need. We need express.js for creating a web server and socket.io to easily make use of web sockets for instant messaging.
Install this dependencies by running:
The cors package helps us connect to our server from our client and allow cross origin resource sharing. Learn more
Make sure to install the given version of socket.io @2.3.0
Server scaffold
In chess-server
, create a new folder src
. In chess-server/src
create two files, index.js
and game.js
Let's create our server in index.js
by adding this code.
To import packages in Node, we use the require
function. First, we require http
, socketio
and express
. http
is a builtin Node package that we can use alongside express to create a web server.
We create a new app
by calling express
and then create the web server, server
by calling http.createServer
and passing in our express instance app
. We define the port PORT
where the server will be running on. Next we create an io
object by calling socketio
and providing our server instance. io
is used to listen for connections and to emit events to connected users.
We also call app.use(cors())
to add the cors middleware to our express server, to allow cross origin resource sharing with our client.
Finally we set up the server server
to listen on port PORT
. Through that port, we can listen for incoming events.
To run the server, open the package.json
and add the following in the scripts: {}
section
This creates a command we can use to start our server. In your terminal, navigate to the folder for this project and run npm start
. This starts our server on localhost:5000
. You should see this message printed in your terminal once the server starts
Managing the games
Before we start working on real-time messaging, let's first create some functions to manage our players and games in src/game.js
.
All of our games will be stored in an object where the key is the id of the game gameID
and the value is an array of the two players in that game. Each game(key) will have only two players connected. We can also have multiple games happening concurrently.
Example
In src/game.js
let's create this games
object to hold our games and a class to represent a Player.
To create a player, we will need the player's name
, color
w or b, the playerID
for this player and gameID
for the game they are playing.
Next, let's define the addPlayer
function we will use to add a player
The addPlayer
function receives an object with the name
, playerID
and gameID
. To add a player, we first check to see if the game they are trying to join exists or has been created
games[gameID]
, if not we create this new player providing the required parameters and a randomly assigned color.
new Player(name, color, playerID, gameID)
We then create this game and add the player in our array of players. games[gameID] = [player]
.
We return an object with a message
, an opponent
of null, since they are the first to join and we don't have any opponent yet, and the player
we just created.
Next, we check to see if the game already has 2 players. If that is true, we return an object with an error
property.
The last part of this function executes when this game has already been created but the game is not full, i.e when we are adding a second player. For this, we get first player games[gameID][0]
who is the opponent. We create a new player and add that to the array of players. games[gameID].push(player)
we return an object with three properties message
, opponent
and the player
created.
Next we create a function to access a game by it's id
from our games.
Let's create another function to remove a player in case a player leaves the game. This function also returns the player who left the game
Finally, we export this functions from our module
Find the complete code snippet for src/game.js
in this Github gist.
Socket events
In our index.js
file, let's set up our web sockets connection
We first listen for new connections io.on('connection')
. For every connection to our web server, we get a socket
object which represents that particular connection. We register a listener for the join
event. This will be emitted from our client in order to join a game, and it receives an object with the name
and gameID
and a callback function from the client. We use the addPlayer
function to join this game. If we get an error, we pass this to the callback function to be received by the client, if it was successful, we return the color assigned to the player via the callback.
We also add this player to the game socket.join(gameID)
. (In socket.io terms, it's called a room)
We emit a welcome
event to this socket/connection through socket.emit()
and provide a message
and the opponent
for this player. The opponent might be null if this socket is the first player.
Next, we emit the opponentJoin
event to the other player in the game, socket.broadcast.to(player.gameID)
(in case one had joined before), and send them their opponent data.
socket.broadcast
sends an event to the other connected sockets/clients in the same game/room.
Next, we check if game is full game(gameID).length >= 2
, if true, we emit a message event to all players using io.to(gameID).emit
to inform them to start the game.
When a client makes a move, they will emit a move event. We listen for this event in
socket.on('move')
and inform the other client of their opponents move by emiting the opponentMove event and passing some data about the move socket.broadcast.to(gameID).emit('opponentMove', { from, to })
Finally, we set up a listener for disconnect socket.on('disconnect')
which is emitted when a socket is disconnected e.g by closing the app on their browser.
We use the removePlayer
function to remove this player from our games object, and then emit a message event to the other player to inform them. We also emit an additional opponentLeft event using socket.broadcast.to(player.game).emit('opponentLeft')
.
That's all we need to setup our server. Find the complete code snippet for the server, src/index.js
here
Get the complete server-side code on GitHub