Creating a multiplayer online card game with Node.js and Phaser 3
As the world is locked down due to the COVID-19 Corona Virus, we are quarantined at home. We miss the company of our families and friends, so online games are the only option to play together. We are going to create an online multiplayer game that can be used for any tabletop gameplay.
The frontend is going to be JavaScript, Node.js, Phaser3, the backend is Express and Socket.IO.
The framework for this game came from the great tutorial at How to Build a Multiplayer Card Game with Phaser 3, Express, and Socket.IO
Install a web server for development
I have installed XAMPP from https://www.apachefriends.org/index.html
The home directory where the index.html should be is at C:\xampp\htdocs
Install Node.js
Install the latest version of Node.js from https://nodejs.org/en/download/
Install Phaser3
Install Phaser 3 based on http://phaser.io/download/stable as of writing with
npm install phaser@3.22.0
The getting started guide on Phaser 3 is at Getting Started with Phaser 3
A great game tutorial is at Making your first Phaser 3 game
The multiplayer online game development
Client
To test the client on your workstation, start the Node.js client from a terminal window to display the web page for the players.
cd client
npm install
npm start
The default browser opens with the http://localhost:8080/ address.
Server
To test the server on your workstation start the multiplayer server in another terminal window
cd into the root directory above the client. The next command will ask questions and create a new package.json file
cd server
npm init
Install Express, Socket.IO, and Nodemon
npm install --save express socket.io nodemon
Start the server
npm run start
or
node server.js
Build the application
Stop the client development server, otherwise you will get the error message
Error: EPERM: operation not permitted, lstat ‘…\client\dist\src\assets’
Build the client application
cd client
npm update
npm run build
Deploy the application on the workstation
Copy the assets into the dist directory
cd client
mkdir -p dist/src/assets
cp src/assets/* dist/src/assets
Copy the contents of the client\dist directory to the webserver
mkdir -p C:/xampp/htdocs/rummy
cp -r dist/* C:/xampp/htdocs/rummy
Start the Express server
To provide the Socket.IO functionality in the root of the game development directory execute
cd server
npm run start
Test the multiplayer application in the local network
Use your workstation as the test server and connect to it from another computer.
Set the Socket.IO server URL
To be able to connect to the same Express server from the workstation and from another computer on the same network change the Socket.IO URL in the client/src/scenes/game.js file.
this.socket = io('http://MY_COMPUTER_IP:3000');
Expose the Express server on your workstation to the local network
Open port 3000 in the Windows firewall.
Start the webserver
Open the XAMPP Control Panel and click the Apache Start button
Open the website
In a web browser navigate to http://MY_COMPUTER_IP/rummy/
Build the Docker image
Based on the great post at Dockerizing a Node.js web app
Create a Dockerfile in the server directory
FROM node:12
# Create app directory
WORKDIR /usr/src/app
# Install app dependencies
# The wildcard will copy both the package.json AND package-lock.json files (npm@5+)
COPY package*.json ./
RUN npm install
# If you are building your code for production
# RUN npm ci --only=production
# Copy all files
COPY . .
# The server listens on port 3000 by default
EXPOSE 3000
CMD [ "node", "server.js" ]
Build the server Docker image
cd server
docker build -t robbers-rummy-server .
Create a Dockerfile in the client directory. We will use a two-stage build process to make the final image as lean as possible. We build the application in a Node.js container and run it in an Nginx container.
FROM node:12 as builder
# Create app directory
WORKDIR /usr/src/app
# Install app dependencies
# A wildcard is used to ensure both package.json AND package-lock.json are copied
# where available (npm@5+)
COPY package*.json ./
RUN npm install
# If you are building your code for production
# RUN npm ci --only=production
# Bundle app source
COPY . .
RUN npm update
RUN npm run build
# Create asset directory
RUN mkdir -p dist/src/assets
# Copy the images to the dist folder
COPY src/assets/* dist/src/assets/
FROM nginx:alpine as runner
WORKDIR /usr/share/nginx/html
COPY --from=builder usr/src/app/dist/* ./
# Copy the images from the source
RUN mkdir -p src/assets
COPY --from=builder /usr/src/app/src/assets/* src/assets/
EXPOSE 80
Build the client Docker image
cd client
docker build -t robbers-rummy .
Launch the Docker containers
Start the server container from any directory
docker run -p 3000:3000 -d robbers-rummy-server
Start the client website container from any directory
docker run -p 80:80 -d robbers-rummy
Stop the Docker conainers
Get the container IDs
docker ps
Stop and remove the containers using the container IDs
docker stop MY_CONTAINER_ID
docker rm MY_CONTAINER_ID
Use Docker Compose
To launch the server and the client website together we will create a docker-compose.yml file in the root of the application
version: '3'
services:
api:
image: robbers-rummy-server
build: .
networks:
- backend
ports:
- "3000:3000"
web-cli:
image: robbers-rummy
networks:
- backend
ports:
- "80:80"
networks:
backend:
driver: bridge
Start the containers with Docker Compose from the application root directory
docker-compose up -d
To stop the containers launched with Docker Compose from the application root directory
docker-compose down
Host the application in AWS
The application consists of two parts: a static website with the game Javascript files, and the Express server to run Socket.IO
We will host the static website in AWS S3, and the server Docker container in an AWS ECS Fargate cluster.
Get the public IP address from the command line
To get the public IP address of the server from the command line, execute
curl ifconfig.me
Migrating from Chef Client version 13 to 15
Syntax changes
There are breaking changes between Chef Client version 13 and the newer versions, make sure you update your Chef cookbooks to make them work with the new version of the Chef Client.
Resource | Since version | Old syntax | New syntax | Notes |
logger | Client 14.0 | keyword, cannot use it as a variable or parameter name | ||
windows_firewall_rule resource | Client 14.7 | localport | local_port | Moved from the Windows cookbook to the Chef cookbook |
Debugging Go programs in Visual Studio Code
Install Visual Studio Code
Install Delve
For up-to date info visit https://github.com/derekparker/delve/tree/master/Documentation/installation
go get -u github.com/go-delve/delve/cmd/dlv
Computer failed to join domain from its current workgroup ‘WORKGROUP’ with following error message: The system cannot open the device or file specified.
When you use PowerShell to join a Windows server to the domain make sure the -OUPath is correct.
Computer failed to join domain from its current workgroup ‘WORKGROUP’ with following error message: The system cannot open the device or file specified.
In this case we wanted to place the joined computers in the Computers folder, so no OUPath specification is necessary. The solution was to omit the -OUPath option from the command:
Add-Computer -DomainName $domain -Credential $credential -Server $domaincontroller -ComputerName $old_computername -NewName $new_computername
Computer ‘…’ failed to join domain. The value provided as the current password is incorrect.
Computer ‘…’ failed to join domain ‘…’ from its current workgroup ‘WORKGROUP’ with following error message: Unable to update the password. The value provided as the current password is incorrect.
If you can join the computer to a domain using the UI, but the PowerShell script fails with the error message above, make sure you add the name of the domain to the username that joins the computer to the domain.
domain\\username
When you use the UI, Windows knows the user belongs to the new domain. The configuration tools run as a local admin, so you need to specify the name of the domain with the user name.
Form value count limit 1024 exceeded
By default dotnet core limits the form element count to 1024. To submit forms with more elements, increase the limit in the ConfigureServices() method of the Startup.cs file:
services.Configure<FormOptions>(x => x.ValueCountLimit = 4096);
Set the environment name in Chef Test Kitchen
To specify the environment name in the .kitchen.yml file
Create a JSON environment file. Chef Zero used by Test Kitchen does not understand YAML or Ruby, we need to use JSON.
- In your cookbook’s root directory create a directory for environment files
mkdir environments
- Create the myenv.json environment file in the environments directory.
{
"name": "myenv",
"description": "Test environment",
"chef_type": "environment"
}
- Specify the environment name in the .kitchen.yml file
provisioner:
name: chef_zero
client_rb:
environment: "myenv"
- Set the environment file location in the suite
suites:
- name: default_envtest
environments_path: "environments"
The node.chef_environment value will be “myenv” during the Test Kitchen run.
Using Makefiles
Makefiles are great to self-document frequently executed commands in a repository. They provide easy access to the bash code by easy to remember tags.
Formatting
Only tabs are allowed to indent lines. The indentation groups the commands under the rules.
Set Bash as the shell
On some systems Bash is not the default shell when Makefiles run. To set the shell add the line to the top of the Makefile:
SHELL := /bin/bash
Using pushd
Make executes every line in a new context. If you need to use pushd, popd you need to write all commands between them in one line separated with semicolons.
cluster-create:
pushd ../terraform; terraform apply; popd