Building a full-stack Aavegotchi minigame - Part 3: Deploying your game
Caleb Coyote
Posted on August 3, 2021
In part 1 of the tutorial we created gameplay using Phaser 3, then in part 2 we created a server to verify the scores submitted to the leaderboard.
All we got to do now is deploy it! However, due to the nature of our app, it isn't as simple as uploading a HTML file to a web hosting service.
Instead, our backend server and front end application have different requirements to be hosted and accessed on the World Wide Web, and therefore need different solutions.
You can follow along from the previous lesson by cloning the end result of the previous lesson. Bare in mind you will need to set up a firebase project and assign the project keys in the repo to get it to work.
End result
By the end of this tutorial your game will be accessible to the Aavegotchi masses to play and compete!
We will be using Google Clouds Compute Engine to host and deploy our web-socket server onto a Virtual Machine. We will then use Firebase to host our React app and ensure that both the Server and Front end can communicate with each other.
Unless you have coded it to work otherwise, you wont be able to play the game without owning an Aavegotchi. You can buy one here or you could remove the
development
mode requisite withinapp/src/pages/Home
to allow everyone to be able to select Use Default Gotchi without the requirement of owning an Aavegotchi.
Step 1) Setting up a virtual machine
First you will need to get set up on Google Cloud Platform, you can use my referral code to get $50 credit, which should be more than enough for this project.
If you have a Google account, go to Google Clouds Console and in the top right select CREATE PROJECT.
Then set the name of your project to whatever you like.
It will take a couple of seconds to create the project, but once it does you will be presented with your Projects dashboard.
In the side menu, navigate to Compute Engine -> VM Instances.
Enable the Compute Engine API.
Then select Create Instance.
Give it a name, select your region.
For machine configuration, select what works best for you depending on your app. Flappigotchis server is very simple so I will be selecting the smallest machine of the E2 series.
Then for the Firewall select allow HTTP and HTTPS traffic so that the browser is able to connect.
Then click create. It will take a minute or so to set up.
Step 2) Set up domain
Later on we will be setting up TLS (Transport Layer Security) for our server. For that we are going to need a domain name.
If you already have one to hand then great, otherwise you can get one from Google Domains.
Once you’ve got your domain name. Go back to your VM instances and copy the External IP to your clipboard.
Now go to the side menu, scroll down, and under Networking select Network services -> Cloud DNS.
Enable the API if you haven’t done so already, then create Zone.
Set your name and your DNS name to the domain. Ensure that DNSSEC is off.
After clicking create, a zone will be populated with NS and SOA records.
To point your registered domain name to the IP address of the hosting server, you must set an A record to your zone.
To do this:
- Click Add Record Set
- Select A from the Resource Record Type menu
- In DNS name put server
- Under IPv4 Address, paste the External IP address from your instance
- Click create
It will take a few minutes to become active.
To update the name servers in Google domains:
- Go to Google Domains
- Click the domain you set up prior
- Click DNS from the left side menu
- At the top click Custom
- Copy all 4 NS records from the Zone details page one at a time and paste them into the Name server inputs.
- Click save
- Click switch to these settings
To verify your changes were successful in your terminal run
dig +trace server.example.com
where example.com is the domain you registered.
You should see at the end of the output the IP_Address of your virtual machine
server.example.com. 300 IN A 34.105.146.34
;; Received 68 bytes from 216.239.36.109#53(ns-cloud-d3.googledomains.com) in 51 ms
Step 3) Installing dependencies on Virtual Machine
Now we need to prepare our Virtual machine. To do this we use the Cloud Shell to access the machines terminal.
To log into the virtual machine, in the Cloud shell terminal run (replacing zone and name with the zone and name of your VM instance):
gcloud compute ssh --zone [zone] [name]
The rest of the tutorial assumes that the distribution of of linux the virtual machine uses is Debian. To check what distribution you are running, in your cloud shell invoke
lsb_release -a
. If it is different, the concepts will remain the same, it's just some syntax may or may not be different.
At the moment our virtual machine is very vanilla. We are going to have to install a bunch of dependencies to get it to be able to clone and run our application.
These will be
- Git - So we can clone our project
- NGINX - To proxy the web requests to the node server
- Certbot - To provision a TLS certificate
- UFW - user-friendly front-end for managing iptables firewall rules.
To install them, in your cloud shell run:
sudo apt-get install git nginx certbot python-certbot-nginx ufw
Next we will install Node. For this we need to install the latest Node repository which you can find here, for me the latest stable release is Node.js v16.x so I will run:
curl -fsSL https://deb.nodesource.com/setup_16.x | sudo -E bash -
Once its installed run:
sudo apt-get install -y nodejs
To install NodeJS and npm.
The final thing we will need to install is ts-node
due to the fact our server is written in typescript, because we have downloaded npm. This command is as simple as running:
sudo npm install -g ts-node
Step 4) Config NGINX
First we got to config the NGINX so that it can proxy requests to the Node server. To do this, in the Cloud Shell terminal create a new file sudo nano /etc/nginx/sites-available/server.[domainname].com
and inside it paste (replacing domainname with the domain you set up earlier):
server {
listen 80;
listen [::]:80;
root /var/www/html;
server_name server.[domainname].com;
}
Press
CONTROL + X
to save the changes then Y to confirm, then ENTER to exit.
Enable the config file by running:
sudo ln -s /etc/nginx/sites-available/server.[domainname].com /etc/nginx/sites-enabled/
Then because you edited the config, restart NGINX by running:
sudo systemctl restart nginx
You can then check NGINX status by running:
sudo systemctl status nginx
Step 5) Set up Firewall
As we have installed UFW (Uncomplicated firewall) this step is as simple as running
sudo ufw allow ssh
sudo ufw allow http
sudo ufw allow https
and then enabling it using
sudo ufw enable
Finally, we can check that the rules have configured properly by running:
sudo ufw status
You should hopefully get a response that looks like this:
Status: active
To Action From
-- ------ ----
22/tcp ALLOW Anywhere
80/tcp ALLOW Anywhere
443/tcp ALLOW Anywhere
22/tcp (v6) ALLOW Anywhere (v6)
80/tcp (v6) ALLOW Anywhere (v6)
443/tcp (v6) ALLOW Anywhere (v6)
Step 6) Provisioning the TLS certificate
Now we have our NGINX and Firewall setup, we can run certbot to create our HTTPS certificates and config the rest of our NGINX.
In your cloud shell terminal run
sudo certbot --nginx
Enter your email, agree to the terms of service, choose whether you want to share information or not.
Enter domain of the app including the sub domain.
Last choice is whether we want to redirect HTTP traffic to HTTPS, which we do so select option 2.
Your private certificate and chain will now be saved onto your virtual machine.
Now all we need to do is add a reverse proxy into our
nginx/sites-available/server.[domainname].com
file, so open it up again using:
sudo nano /etc/nginx/sites-available/server.[domainname].com
at the end of the top server object add location
:
server {
...
location / {
# we're actually going to proxy all requests to
# a Nodejs backend
proxy_pass http://localhost:8080/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
server {
...
}
Step 7) Preparing app for deployment
We have everything we need, we can now prepare our app to deploy both front end and the backend.
First thing we want to do is let our app know which url to target when initiating socket.io.
For this we can make use of .env and processes to target the create variables depending on if the app is run in development or production.
If you are carrying on from the previous tutorial, in the app directory you should already have a .env.development
file in the app
directory. What you should do is create a new file in the app
directory called .env.production
and copy all the firebase variables over as they will be the same in both development and production.
In .env.development
create a new variable called REACT_APP_SERVER
and assign it a value of the url of your server when ran on local host. It should look like this.
// app/.env.development
...
REACT_APP_COLLECTION_NAME="test"
REACT_APP_SERVER_PORT="http://localhost:8080"
Now in .env.production
we add in two different variables for the COLLECTION_NAME and the SERVER_PORT.
The collection name should be the name of the Database we want our app to target on production mode, therefore it should be different to the one used in development.
The SERVER_PORT should be the url of our servers domain. This by default will connect to port 443 which points to our NGINX setup, which in turns encrypts the request and connects to port 8080 on our virtual machine where our web-socket server will be listening.
// app/.env.production
...
REACT_APP_COLLECTION_NAME="highscores"
REACT_APP_SERVER_PORT="https://server.[domainname].com"
In app/src/game/main.tsx
you should see our .env variable is already being used to initiate the socket, so no more needs to be done here with the socket.
However our ServerProvider
in app/src/server-store
is still only fetching data from the "test" collection, so we need to update it to use the .env variables like so:
// app/src/server-store
...
export const ServerProvider = ({
children,
}: {
children: React.ReactNode;
}) => {
...
const snapshotListener = (
...
) => {
return database
.collection(process.env.REACT_APP_COLLECTION_NAME || "test")
...
};
useEffect(() => {
const getHighscores = async (_firebase: fb.app.App) => {
...
const highscoreRef = db
.collection(process.env.REACT_APP_COLLECTION_NAME || "test")
...
}
}, [firebase]);
...
};
Preparing server
For the server we are also going to want to create some .env variables for the names of the Collections we set earlier. So create another .env.development
and .env.production
and inside put the variables for the collection name:
// server/.env.development
DB_COLLECTION="test"
// server/.env.production
DB_COLLECTION="highscores"
REACT_APP is not necessary as the server isn't a React app.
Then in server.ts
replace the collection name with process.env.DB_COLLECTION
// server/server.ts
...
const submitScore = async ({tokenId, score, name}: ScoreSubmission) => {
const collection = db.collection(process.env.DB_COLLECTION);
const ref = collection.doc(tokenId);
...
Now run the app locally and ensure everything is still working correctly.
Step 8) Deploying the front end
To host the front end up your app feel free to use any hosting service. Im going to be using firebase hosting purely due to the fact my database is on firebase and my backend is hosted on Google Cloud.
To get started visit the Firebase CLI documentation to learn how to install the Firebase CLI.
Once you have it installed, initiate hosting for the app by going into the app
directory in your terminal and running:
firebase init hosting
Select Use an existing project (as you should already have a project set up for your leaderboard in the previous tutorial), and select your project.
For your public directory, type in "build" as thats the folder react constructs after building the app.
For configure as single-page app select Yes as it is a React app and therefore is a single-page app.
And finally for set up automatic build, choose what ever you fancy. Il select no, but if you want to do an automatic redeploy every time you push your code to your main directory in GitHub then select yes.
Your app will now configure itself automatically.
Now to deploy the site, first you have to build your application by running:
npm run build
in your app directory. This will compile your app into an optimised production build in the build folder. Then after it's compiled run:
firebase deploy --only hosting
Once it's deployed the terminal will return the URL to the deployed web app.
If you go to your deployed site, you should have an empty leaderboard, abd when you try running the game the loading bar should get stuck on connecting to server. Thats because we haven't deployed our server app on the virtual machine yet.
Step 9) Deploying the back end
We are going to be using git
to clone our app into the virtual machine. Therefore we need to ensure our code is up to date on GitHub.
If you were following from the start you should have your own GitHub rep setup for your app. So just run:
git add .
git commit -m "Ready for deployment"
git push
If you haven't got a GitHub repository setup yet, then do so by following these instructions to import project to Github here.
Once that is done, go back to the cloud shell of your app, and log back into your virtual machine.
Press
up ↑
on your keyboard to get previously used commands.
Now get the url to your GitHub project:
And clone your Github project into your virtual machine by running:
git clone https://github.com/[username]/[example].git`
You may notice that we have cloned both the app and the server into the virtual machine with this method which is not ideal. You can just clone the server directory, but in terms of ease of managing different versions of your app, this way saves a lot of headache.
If you find your project ends up taking up too much space in your machine, then opt to remove the app from the virtual machine. Or create a separate repo for your server and app directories.
You can now in your virtual machine go to your server directory and install its dependencies:
cd flappigotchi/server
npm install
Now before we can run the app there are a few things we need to add to the project. Those with keen eyes may notice that both our service-account.json
and our .env
variables are in the .gitignore
file. Therefore when we pushed and pulled our code from github, these files wouldn't of been included. Therefore we need to rewrite them within the cloud shell.
Because we only run our server in production mode with the virtual machine, we will just add the .env.production
file. To do this, run sudo nano .env.production
within the server directory to create and open up a .env file. Then inside copy and paste your variable from before:
DB_COLLECTION="highscores"
Now do the same for the service-account JSON file. Run sudo nano service-account.json
and paste in your service account key.
If you are to run sudo npm run start:prod
now, then your server should hopefully output a message saying its listening on port:8080!
Hopefully now, when you open your deployed app, it should connect to your server without a hitch and you will be able to play and submit a score to the leaderboard!
10) Set domain name for front end
Your game is now live and ready to be tested by the Aavegotchi community! Now we just want to set up our domain name from earlier to point to the front end of our application.
To do this, go to your firebase console, and in the side menu select Hosting:
- Click Add custom domain
- Type in the domain name that you used for the server but without the added server subdomain.
- Go back to the Cloud DNS from before and add in both of the A records
A records in Firebase
A records inputted into Google Cloud DNS
The status should now go to pending as it verifies ownership of the domain. This process can take anywhere from a couple of minutes to 24 hours.
Once its verified you should now be able to play your game at your given domain!
Conclusion
That's it! Your game is live and ready to be played!
In this lesson you learnt how to securely set up a web-socket server on Google Cloud, as well as deploy your front end application.
This 3 part series has been a high level overview of the concepts that go into building a game. There is still more that can be done, so if there is any concepts that you want to see tackled in a future tutorial, comment them down below.
If you have just deployed your own game. Message me on Twitter or Discord to see about running an Aavegotchi EXP event!
Part 4 of the series will likely be how to add a Pay2Play/Play2Earn system into your game. So make sure to follow me @ccoyotedev or @gotchidevs on Twitter for updates on future tutorials.
If you have any questions about Aavegotchi or want to work with others to build Aavegotchi minigames, then join the Aavegotchi discord community where you can chat and collaborate with other Aavegotchi Aarchitects!
If you own an Aavegotchi, you can play the end result of this tutorial series at flappigotchi.com.
Posted on August 3, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.