Building a full-stack Aavegotchi minigame - Part 3: Deploying your game
Tags: websocket, gamedev, web3, node
Published: 2021-08-03 13:20:35

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](https://github.com/ccoyotedev/flappigotchi-tutorial/tree/v1.0.2). 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.

![World wide web](https://media.giphy.com/media/cp3EXAJY259ctiQP9K/giphy.gif)

&nbsp;
## 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](https://aavegotchi.com/baazaar/aavegotchis?sort=latest) or you could remove the `development` mode requisite within `app/src/pages/Home` to allow everyone to be able to select *Use Default Gotchi* without the requirement of owning an Aavegotchi.

&nbsp;
## 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](https://console.cloud.google.com/freetrial?referralId=a19b810972d64524a33e59f79d3c3c24) to get $50 credit, which should be more than enough for this project.

If you have a Google account, go to [Google Clouds Console](https://console.cloud.google.com/projectselector2/home) and in the top right select **CREATE PROJECT**.

![Create project](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5qck4b56pmqymzwyry8d.png)

Then set the name of your project to whatever you like.

![Name project](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5j2aomwlu1zgkds4oqqi.png)

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.

![VM instances in side menu](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e4hafwnn8c7qx0wsyftb.png)

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.

&nbsp;
## 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](https://domains.google.com/registrar/search).

Once you’ve got your domain name. Go back to your VM instances and copy the **External IP** to your clipboard.

![Copy external IP](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wnj3llek5n9rpv5iumch.png)

Now go to the side menu, scroll down, and under Networking select Network services -> Cloud DNS.

![Cloud DNS](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2xottzknau0622jv3bev.png)

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.

![Zone config](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/evamgzu7xmio06gp2gvg.png)

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:
1. Click Add Record Set
2. Select **A** from the **Resource Record Type** menu
3. In DNS name put **server**
4. Under **IPv4 Address**, paste the External IP address from your instance
5. Click create

It will take a few minutes to become active.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pmbpn8ft76bd9wl5o7i4.png)

To update the name servers in Google domains:
1. Go to [Google Domains] (https://domains.google.com/registrar/)
2. Click the domain you set up prior
3. Click **DNS** from the left side menu
4. At the top click **Custom**
5. Copy all 4 NS records from the Zone details page one at a time and paste them into the Name server inputs.
6. Click save
7. 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
```

&nbsp;
## 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.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/29ogqqah9g8o8tsrfs0g.png)

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](https://github.com/nodesource/distributions), 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
```

&nbsp;
## 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
```

&nbsp;
## 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)
```

&nbsp;
## 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 {
    ...
}
```

&nbsp;
## 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:

```ts
// 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]);

  ...
 };

```

&nbsp;
### 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.

&nbsp;
## 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](https://firebase.google.com/docs/cli#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.

&nbsp;
## 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](https://docs.github.com/en/github/importing-your-projects-to-github/importing-source-code-to-github/adding-an-existing-project-to-github-using-the-command-line).

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:

![Url to repo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rt3h81vv6h21edhrep1k.png)

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!

&nbsp;
## 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**:

![Hosting](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7clz68tx36cpzv6ptnjj.png)

1. Click **Add custom domain**
2. Type in the domain name that you used for the server but without the added server subdomain.
3. Go back to the [Cloud DNS](https://console.cloud.google.com/net-services/dns/zones) from before and add in both of the A records

![A records in Firebase](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6p188sqse1xhne0u08ca.png)
> A records in Firebase

![A records inputted into Google Cloud DNS](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xlo4q6vaw4vyve5gl2uv.png)
> 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!

&nbsp;
## 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.

![Deployed](https://media.giphy.com/media/3og0IAQG2BtR13joe4/giphy.gif)

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](https://twitter.com/ccoyotedev) or [@gotchidevs](https://twitter.com/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](https://discord.com/invite/89WEDseCPq) 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](https://flappigotchi.com/).