Skip to content

Ore Rush Online is a mining MMO built entirely with TypeScript! This monorepo contains the desktop client, api server and marketing website. Created from the ts-online-game-template.

License

Notifications You must be signed in to change notification settings

ASteinheiser/ore-rush-online

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Ore Rush Online

A highly opinionated template for creating real-time, online games using TypeScript! Quickly create mmo-style games using React + Phaser for rendering, Colyseus for websockets and Electron for native builds! Also has support for Progressive Web Apps (PWA). Oh, and lots and lots of Vite for builds and testing!

Try the demo: https://ore-rush.online/download

Overview

Comes with 3 apps:

  • desktop/ore-rush-online: Frontend rendering for the game written in TypeScript using Electron, React, Phaser, Colyseus and GraphQL. When built, compiles an executable that runs a version of Chromium to render the game.
  • game-api: Backend server that handles the game state and data via WebSockets and GraphQL. Written in TypeScript with Colyseus, Express and Apollo GraphQL.
  • web: Static webpage that can serve as a marketing site, devlog, roadmap, wiki etc. Written in Typescript with React and GraphQL. Could also be used to serve the Phaser/Colyseus game (with support for PWA).

And 5 packages:

  • core-game: Main logic for the game. Shareable for use on the server as well as the client. Client-side prediction (CSP) demo included.
  • client-auth: Shared auth forms, hooks, etc. built with the local ui package. Used by both the static webpage and Electron app.
  • ui: Shared Tailwindcss theme and Shadcn/ui components
  • typescript-config: Shared TypeScript configs
  • eslint-config: Shared ESlint configs

Third-party Dependencies

This project relies on Supabase for JWT authentication. They offer a very generous free tier (50k MAU) and a straight-forward developer experience. It's also open source, so you can self-host if the need arises!

You'll need to create a free tier project and add the relevant keys to your local environment. Keys can be found by navigating to your Supabase project, then from the sidebar, "Project Settings" > "API Keys". You can also see some of the strings you'll need via the "Connect" button near the top center of the screen. Use the values from these sections to create the following files based on the .env.example files:

  • apps/desktop/.env
  • apps/game-api/.env
  • apps/web/.env

NOTE: You'll need to add Redirect URLs (under Authentication > URL Configuration) to send email links to the auth redirect route. For example, if your custom domain is https://ore-rush.online, you should add: https://ore-rush.online/auth/redirect*. You can also add http://localhost:4200/auth/redirect* for local development. Configure this here:

https://supabase.com/dashboard/project/<PROJECT_ID>/auth/url-configuration

I recommend also updating the Secure email change setting to false (under Authentication > Providers > Email). By default, Supabase sets this to true, which will require users to click a link in both the old and new emails. While this is a good security measure, it can be annoying if users can't access their old email for some reason.

Env Considerations

  • Turborepo recommends that you define environment variables for each "app" instead of trying to define them globally. This helps prevent sensitive env values from leaking across apps.
    • Although vite has built-in mechanisms for ensuring certain env doesn't get exposed on the frontend, I find it messy to have env from the web app loaded on the backend, for example.
  • To ensure caches miss when updating env values, you may need to update the turbo.json section for tasks.build.env. It should be noted that turborepo will infer any VITE_-prefixed env.
    • This means that if you add more VITE_WHATEVER variables to either apps/desktop/.env or apps/web/.env, then you do NOT need to update the turbo.json.
    • However, if you add another variable to apps/game-api/.env, then you SHOULD update the build tasks' env in the turbo.json.
    • NOTE: the dev task does not need the env field since it's cache setting is set to false.

Auth Email Templates

You can also quickly customize the auth emails using the templates under packages/client-auth/email-templates by navigating to:

https://supabase.com/dashboard/project/<PROJECT_ID>/auth/templates

Developer Quickstart

If you are familiar with pnpm and docker-compose, you can skip to Useful Commands or quickly start development with:

pnpm i
pnpm db:start
pnpm db:sync
pnpm dev

When you run dev, you should see:

Otherwise, you should:

Install the docker-compose cli, which can be installed via Docker Desktop. Make sure you have Docker Desktop running!

Ensure you are using the correct version of Node.js. You can validate this by comparing your local version of node (node -v) with the .nvmrc.

NOTE: The .nvmrc uses an alias for the node version. I highly recommend managing your local node version with nvm. This will allow you to quickly swap to the correct version with:

nvm use

This project uses pnpm for it's dependency mangement. You can install it with npm:

npm i -g pnpm

This project also uses Turborepo to manage scripts across the monorepo. While this is NOT necessary, it is recommended that you install a local version:

npm i -g turbo

With the turbo cli, you can take a look at the project structure as well as the available commands:

turbo ls
turbo run

Most Used Commands

These commands are available from the root directory whether you decide to install the turbo cli locally or not...

NOTE: Commands should almost always be ran from the root directory. The package manager, pnpm, uses turbo to manage and run scripts. Since code can be shared between repos, turbo helps ensure that scripts run in a certain order when necessary.

Command Description
pnpm db:[start|stop|sync] Uses docker-compose and prisma to manage a local PostgreSQL DB
pnpm dev Run local development servers for each app
pnpm db:test:[start|stop|sync] Uses docker-compose and prisma to manage a test PostgreSQL DB
pnpm test Runs the typecheck, linter and tests for each repo
pnpm test:watch Runs the test suite in each repo and watches for changes
pnpm test:load Builds and runs the game-api then starts the load test
pnpm preview Builds each app and runs a local server using the output
pnpm build:[win|mac|linux] Builds the desktop app via Electron

NOTE: If, for example, your Electron app is throwing an error when starting or building, but was previously working, try:

pnpm install:clean

Local PostgreSQL DB

If this is your first time running the project, you'll need to start the DB with docker-compose and sync the tables with prisma:

pnpm db:start
pnpm db:sync

pnpm generate:db-types will run during dev, build, etc., if you're using the monorepo commands.

However, if you change the DB schema via apps/game-api/prisma/schema.prisma, then you'll need to run db:sync again:

pnpm db:sync

This will generate a SQL migration, migrate your local DB, and update your types.

NOTE: This project uses Turborepo's Terminal UI (tui) and some tasks are interactive, such as test:watch and db:sync. When you want to interact with a window, press "i", then interact as normal. Press "ctrl" + "z" to leave interactive mode.

Testing

When you run pnpm test or pnpm test:watch, it will run the game-api test suite, which requires a local test DB to be running. This can be accomplished by following the instructions for working with the PostgreSQL DB, with the only difference being that you add test: to the db: commands, ie:

pnpm db:test:start
pnpm db:test:sync
pnpm db:test:stop

NOTE: pnpm test:watch offers a similar experience to pnpm dev, in the sense that it will watch for graphql and prisma type changes, and hot-reload as needed. pnpm test acts as a complete CI check as it will run all the type generators, typechecks, linters, then finally tests.


Load Testing

Colyseus has a built-in load testing tool that can be used to test the scalability of the game rooms. The example load test is located in apps/game-api/test/load/test.ts. To start the load test, run:

pnpm test:load

NOTE: This will run the game-api CI checks, start the server in preview mode, then run the load test. The preview server is production-like, but pointed at a local test DB. Please ensure the test DB is running and synced, same as the testing instructions above:

pnpm db:test:start
pnpm db:test:sync

Production Load Testing

WARNING: This is a VERY powerful tool used to test the scalability of your api server/game rooms. Keep in mind that you can impact users if you run this against your production server. At the start, running against prod is fine, however, once you have actual users, you should consider running this against a staging server.

When running the prod load test, you won't be able to seed your DB automatically, unlike the local version. In both local and prod load tests, you'll use the prod JWT_SECRET (present in the local .env) to create test tokens. These tokens will be used by the server to find a user in the DB, using the decoded id. These users will not be created in the prod DB automatically. To quickly solve this issue, I recommend uploading a CSV with test user data via your Supabase Dashboard. A CSV with 200 test users can be found in: apps/game-api/test/load/prod-test-users.csv

Once you have test users in the prod DB, you can run the load test against your production server:

pnpm test:load:prod

NOTE: While running the prod load test, you should monitor the server's CPU and memory usage. I like to watch the DigitalOcean Droplet's graphs to see total server resources used:

cloud.digitalocean.com/droplets/<DROPLET_ID>/graphs

I also like to SSH into the server to see current usage and logs:

ssh deployer@api.ore-rush.online
pm2 monit

Results Snapshot

This snapshot was created on 2026-01-27. All tests were run for 3 minutes against the cheapest DigitalOcean Droplet:

  • 512MB RAM
  • 1 vCPU
  • 10GB SSD
  • Ubuntu 24.04 LTS
  • US West - San Francisco
Room Size Players CPU Peak Memory Peak
- - (idle) < 1% 65% (77 mb)
- - (idle pm2 monit) 3% 75% (77 mb)
4 4 7% 75% (102 mb)
4 40 35% 79% (106 mb)
4 100 61% 85% (114 mb)
4 200 88% 90% (111 mb)
10 10 10% 78% (101 mb)
10 100 51% 84% (109 mb)
10 200 80% 90% (115 mb)
25 25 18% 73% (117 mb)
25 100 49% 77% (118 mb)
25 200 66% 84% (122 mb)
50 50 23% 77% (121 mb)
50 100 38% 81% (127 mb)
50 200 61% 82% (113 mb)
100 100 (overflows at ~80) 39% 78% (130 mb)

NOTE: The CPU and memory usage percentages are relative to the Droplet's total resources (from the DigitalOcean graphs). The memory usage for the game-api server alone is shown in parenthesis (from the pm2 monit output).

Learnings

It appears to be more efficient on the server to have larger rooms, but at a certain size, you will get buffer overflows. This is due to the sheer size of the room state (many players, each with their own state). You should try to strike a balance between room size and state. The smaller you can make the room state, the more players you can support in a single room. Conversely, if you have a lot of state to track, you will need to limit your maximum room size accordingly.

Available Commands

Command Description
pnpm install Installs dependencies for each repo
pnpm install:clean Runs a script to clear builds, caches, deps, etc., then runs install
pnpm test Runs the typecheck, linter and tests for each repo
pnpm test:watch Runs the test suite in each repo and watches for changes
pnpm test:load Builds and runs the game-api then starts the load test
pnpm test:load:prod Runs the load test script pointed at the production server
pnpm lint Runs the code linting check in each repo
pnpm lint:fix Runs the linter and fixes code when possible
pnpm check-types Runs the typescript check in each repo
pnpm generate:gql-types Generates the GraphQL types in each repo
pnpm generate:gql-types:watch Generates the GraphQL types and watches each repo
pnpm generate:db-types Generates DB types via prisma.schema
pnpm generate:db-types:watch Generates DB types via prisma.schema and watches for changes
pnpm db:start Uses docker-compose to start a local PostgreSQL DB
pnpm db:stop Uses docker-compose to stop the local PostgreSQL DB
pnpm db:sync Uses prisma to manage the local DB based on the schema.prisma
pnpm db:test:start Uses docker-compose to start a local PostgreSQL DB for testing
pnpm db:test:stop Uses docker-compose to stop the local PostgreSQL DB for testing
pnpm db:test:sync Uses prisma to manage the testing DB based on the schema.prisma
pnpm db:deploy Uses prisma to deploy migrations to the prod DB (used by CI)
pnpm dev Run local development servers for each app
pnpm generate:app-icons Generates PWA/Electron icons from apps/web/public/logo.svg
pnpm generate:pwa-assets Generates PWA assets from apps/web/public/logo.svg
pnpm build Generates icons and builds each app including sub-repos
pnpm preview Builds each app and runs a local server using the output
pnpm build:win Builds the desktop app (via Electron) for Windows
pnpm build:mac Builds the desktop app (via Electron) for MacOS
pnpm build:linux Builds the desktop app (via Electron) for Linux

Deployment Setup

Architecture overview:

Web Hosting Setup

The web app is built by GitHub Actions and hosted via GitHub Pages. To get started:

Go to your github repository, then Settings > Pages, select Source and choose GitHub Actions.

You can also add a custom domain via the Custom domain field. Make sure you follow the instructions to ensure you have the correct DNS records in place. Also ensure you select Enforce HTTPS and update the /apps/web/public/CNAME file with your custom domain.

Now go to Settings > Environments, then select (or create if needed) the github-pages environment. Fill out the Environment secrets section according to your /apps/web/.env file.

NOTE: Before hosting the game-api, you won't have a valid VITE_API_URL. So for now, just use the development value: http://localhost:4204

Now that everything is setup, you can either push to main or manually trigger the Deploy Web to GitHub Pages workflow from the Actions tab.

Desktop File Hosting Setup

The desktop app files are built by GitHub Actions, signed (macOS/Windows) and hosted via GitHub Releases. To get started:

Go to your github repository, then Settings > Environments and create the github-releases environment. Fill out the Environment secrets section according to your /apps/desktop/.env file.

NOTE: Before hosting the game-api, you won't have a valid VITE_API_URL or VITE_WEBSOCKET_URL. So for now, just use the development values: http://localhost:4204 and ws://localhost:4204 respectively.

NOTE: You will also need to set the GH_TOKEN environment secret. This is used to authenticate with the GitHub API and update the release with the desktop app files as they are built. Generate a personal access token and make sure to give it permissions to read and write on Contents.

macOS Signing

To sign and notarize builds for macOS, you'll need:

You'll also need to set the following:

Environment Secret Description
APPLE_ID Email address of the Apple Developer account
APPLE_APP_SPECIFIC_PASSWORD Generated "App-Specific Password" (16 characters)
APPLE_TEAM_ID Team ID of the Apple Developer account (10 characters)
MAC_CERT_B64 Base64 encoded "Developer ID Application" certificate
MAC_CERT_PASS Password for the certificate

Setting your APPLE_ID should be easy, just use your email address that you'll use to log in at https://account.apple.com/. Once you log in, under "Sign-In and Security", you should see a section called "App-Specific Passwords". Create a new password and copy the value (this only shows once, 16 characters, ex: asdf-asdf-asdf-asdf). Now use that value to set the APPLE_APP_SPECIFIC_PASSWORD environment secret.

To find your APPLE_TEAM_ID, go to https://developer.apple.com/account and scroll down to "Membership details". You should see your 10 character "Team ID" here (ex: 1234ABCD56).

Getting a valid MAC_CERT_B64 (and MAC_CERT_PASS) is a bit more involved. You'll need a "Developer ID Application" certificate (a .p12 file) that you can base64 encode. Before you can create that certificate, you'll need to create a "Certificate Signing Request" (CSR) file. This can be done with Keychain Access:

  • Open Keychain Access
  • Click "Keychain Access" > "Certificate Assistant" > "Request a Certificate from a Certificate Authority"
  • Create a CSR with:
    • your Apple Developer email as the User Email Address
    • Common Name set to your name or company name
    • empty CA Email Address
    • "Request is" set to "Saved to disk"
  • Save the CSR file (.certSigningRequest file)

Now you can create the "Developer ID Application" certificate (and .p12 file) from https://developer.apple.com/account/resources/certificates/list

  • Create a new Certificate
  • Select "Developer ID Application" and click "Continue"
  • DO NOT CHANGE the default selection for "Previous Sub-CA"
  • Upload the CSR file you just created
  • Create the Certificate
  • Download the certificate (should be a .cer file)
  • Double click the .cer file to add it to your Keychain
  • Ensure that you see a checkmark with "This certificate is valid", otherwise, revisit the steps above
  • Expand the certificate, select the cert and the private key, then click "Export 2 items..."
  • Ensure you save it as a .p12 format
  • As you save this file, you'll be prompted to enter a password for the certificate. This is the MAC_CERT_PASS that you'll need to set as a environment secret. I recommend using a password manager to generate a secure password.

You should now have a valid .p12 file. You can base64 encode it, then copy the value, by running:

base64 < ~/path/to/cert_name.p12 | pbcopy

Finally, set the MAC_CERT_B64 environment secret with the base64 encoded value (you can paste it directly from your clipboard).

Windows Signing

To sign builds for Windows, you'll need an Azure "Artifact Signing Account", which is a hosted Certificate Authority service. This works similarly to the Apple Developer program and has similar pricing as well. To get started:

  • Create/log in to your Azure account
  • From the Azure Dashboard, search for "Artifact Signing Accounts"
  • Create a new Artifact Signing Account
  • After creating the account, you'll need to give the Artifact Signing Account permissions to perform Identity Verification. This can be done by selecting the Account, then going to "Access Control (IAM)" and adding the role assigment for "Artifact Signing Identity Verifier" to your user.
  • Now go back to the Artifact Signing Account, and click on "Identity validation", select "Individual" (instead of "Organization"), then "New Identity" > "Public"
  • Fill out the form with your information, create your new identity and wait for processing
  • You will most likely see the status update to "Action Required". If you do, click on the Identity and you should see a link (ex: "Please complete your verification here")
  • Complete the verification process, which will likely include uploading your ID and a selfie through a verification portal, as well as downloading the Microsoft Authenticator app
  • You should now see your Individual Identity with a status of "Completed"
  • Now go back to the Artifact Signing Account, and click on "Certificate profile"
  • Click "Create" > "Public Trust"
  • Fill out the form, selecting your new Individual Identity from the dropdown for "Verified CN and O", then create the profile

With a valid "Certificate Profile", you can now fill out your Azure signing information in the /apps/desktop/electron-builder.yml file:

win:
  azureSignOptions:
    endpoint: "<YOUR_ARTIFACT_SIGNING_ACCOUNT_URI>"
    codeSigningAccountName: "<YOUR_ARTIFACT_SIGNING_ACCOUNT_NAME>"
    certificateProfileName: "<YOUR_CERTIFICATE_PROFILE_NAME>"
    publisherName: "<YOUR_CERTIFICATE_SUBJECT>"

Now you need to create the "App registration" in Azure:

  • From the Azure Dashboard, search for "App registrations"
  • Click "New registration"
  • Give it a name like gh-electron-signer and create
  • From your new App Registration, you should be able to see two fields:
    • "Application (client) ID" > set this as the AZURE_CLIENT_ID environment secret
    • "Directory (tenant) ID" > set this as the AZURE_TENANT_ID environment secret
  • Now click on "Manage" > "Certificates & secrets"
  • Click "New client secret" and create a new secret
  • Ensure you copy the Value, as it will only show once!
  • Set the AZURE_CLIENT_SECRET environment secret with the value you just copied

This final step is very important... You need to assign "Artifact Signing Certificate Profile Signer" permissions to your "App Registration":

  • From your "Artifact Signing Account", click on "Access Control (IAM)"
  • Click "Add" > "Add role assignment"
  • Find and select the "Artifact Signing Certificate Profile Signer" role
  • For "Members", you need to assign a "service principal" for your "App Registration"
  • Click "Select members" and search for your "App Registration" by name
  • Create the permission
  • Double check the new permission in "Artifact Signing Account" > "Access Control (IAM)" > "Role assignments" tab

Deployment Trigger

The desktop app files can now be built, signed (macOS/Windows) and hosted in a GitHub Release! Simply create a Release with a new Tag that looks like v*.*.*, and the GitHub Action will kick off the build process for each OS. As each OS build completes, the Release will be updated with the desktop app files.

Automatic Updates

On all platforms (macOS/Windows/Linux), the app will automatically check for updates and download them in the background. This is possible thanks to electron-builder's publish feature combined with electron-updater. When an update is found, the app will display a modal and toast to the user, then restart the app once the download completes. Session rejoin logic is implemented, so this setup should result in all users being on the latest version of the app at all times, while experiencing minimal interruption.

Auth Setup

You should already have a Supabase project setup with JWT auth. So that piece is covered!

Database Setup

Supabase offers a hosted PostgreSQL DB at no cost (limited storage/CPU). You can choose any other provider you'd like, and depending on your needs, you may want to compare pricing with DigitalOcean or AWS RDS at actual scale. But for a small project, Supabase is a great option.

Follow these instructions to setup a hosted PostgreSQL DB with Supabase for Prisma. You'll need to:

  • Turn off the Supabase Data API setting
    • this is a security measure since you will connect to the DB directly via connection string
  • Create your Prisma user in Supabase
  • Obtain your connection string
    • Example: postgres://prisma.[PROJECT-REF]:[PRISMA-PASSWORD]@[DB-REGION].pooler.supabase.com:5432/postgres
  • Go to your GitHub repository, then Settings > Environments, and create the game-server environment
  • Add a new environment secret called DATABASE_URL and set it to your connection string

Now your production DB is set up. Each time you create a new Tag that looks like api-v*.*.*, the GitHub Action will kick off a backend deployment. One step of this deployment will handle migrating the DB according to your Prisma migrations.

NOTE: When creating a new Tag for a backend deployment, DO NOT create a Release! Releases are reserved for hosting the desktop app files only...

Game Server Setup

To host your game server as quickly and cheaply as possible, I recommend using DigitalOcean Droplets. Their cheapest server is ~$4/month and deployment to it can be easily automated with GitHub Actions (after one-time setup). As you scale, you may want to evaluate pricing against other hosting providers, such as AWS EC2 or GCP Compute Engine.

To get started, first go to your github repository, then Settings > Environments and select the game-server environment. This env should already have the DATABASE_URL secret from the DB setup steps above. Fill out the remaining Environment secrets based on your /apps/game-api/.env file.

Now you need to create a DigitalOcean Droplet:

  • Create/log in to your DigitalOcean account
  • Create a new Droplet
    • Select a region near you/your users
    • Select "Ubuntu 24.04 LTS"
    • Choose the cheapest server (should be ~$4/mo)
    • Select "SSH Key for Authentication Method"
      • Follow the instructions to generate and add an SSH key
    • Select "Add improved metrics monitoring and alerting (free)"
      • This will give you some helpful insight as you scale up
    • Create the Droplet
  • Copy the Droplet's public IP address (IPv4)
    • If you want to use your custom domain for your API server, you can use this value to create a DNS record in your domain registrar
      • Create an "A record" for api.domain.com pointed to the Droplet's IPv4
    • Either the Droplet's IP or your custom domain can be used to create production VITE_API_URL and VITE_WEBSOCKET_URL environment secrets
      • Make sure you use secure protocols for production URLs
      • ex: wss://api.domain.com or https://123.123.123.123

Now that you have a Droplet online, you can SSH into it for first time setup:

ssh root@<DROPLET_IP>

Once you are logged in to the Droplet, you should setup the deployer user and create firewall rules:

adduser deployer --disabled-password
usermod -aG sudo deployer

ufw allow OpenSSH
ufw allow 80,443/tcp
ufw enable

Copy the SSH key from the root user to the deployer, then switch to the deployer user:

sudo -u deployer mkdir -p /home/deployer/.ssh
sudo chown deployer:deployer /home/deployer/.ssh
sudo chmod 700 /home/deployer/.ssh

sudo cat /root/.ssh/authorized_keys \
  | sudo -u deployer tee -a /home/deployer/.ssh/authorized_keys >/dev/null

sudo chown deployer:deployer /home/deployer/.ssh/authorized_keys
sudo chmod 600 /home/deployer/.ssh/authorized_keys

su - deployer

IMPORTANT: On Ubuntu 24.04, you need to comment out the lines in your ~/.bashrc file following the comment: # If not running interactively, don't do anything. These lines will prevent you from calling pm2 from the GitHub Action (SSH non-interactive mode). Since you will only be using deployer to SSH during the deployment process, you do not need to update root's ~/.bashrc file. Your deployer user's ~/.bashrc file should look like this:

...
# If not running interactively, don't do anything
#case $- in
#    *i*) ;;
#      *) return;;
#esac
...

Install nvm, node and pm2:

curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash
source ~/.nvm/nvm.sh
# Make sure this matches the version in the .nvmrc file
nvm install 24
npm install -g pm2

Ensure pm2 resurrects on boot:

pm2 startup systemd -u deployer --hp /home/deployer

NOTE: This will output a command which you will need to run as sudo. Switch back to the root user (exit) and run the command. Once the command completes, you need to switch back to the deployer user.

Setup pm2-logrotate, which will help with the limited server resources by rotating and cleaning up pm2 logs:

pm2 install pm2-logrotate
pm2 set pm2-logrotate:max_size 10M
pm2 set pm2-logrotate:retain 30
pm2 set pm2-logrotate:compress true
pm2 save --force

The final step for setting up the Droplet is to install caddy, which will be used as a reverse proxy (with automatic TLS) for the game API server. Before continuing, you will need to switch back to the root user:

sudo apt update && sudo apt upgrade -y
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl caddy
# Make sure to replace the two placeholders with actual values
sudo tee /etc/caddy/Caddyfile >/dev/null <<'EOF'
<YOUR_CUSTOM_DOMAIN_OR_DROPLET_IP> {
    reverse_proxy localhost:<PORT_ENV_SECRET>
    encode gzip
    log {
      output file /var/log/caddy/game-api_access.log
    }
}
EOF
# Reload Caddy to apply the new configuration
sudo systemctl reload caddy

NOTE: If you need to see production logs, you can always SSH into the Droplet and run:

# List all running PM2 processes
pm2 list
# View the logs for the game-api server
pm2 logs game-api --lines 200
# Clear out logs manually
pm2 flush
# Show verbose info about the game-api process
pm2 show game-api
# Monitor the game-api process (CPU/Memory usage and logs)
pm2 monit

For the GitHub Action to properly deploy the backend, you'll need to create three more environment secrets in game-server:

Environment Secret Description
DROPLET_IP The IPv4 address of the Droplet
DROPLET_SSH_KEY The private SSH key string (pbcopy < ~/.ssh/key)
DROPLET_SSH_PASS The password to use with the SSH key

Now your Droplet is ready to host the game API server!

Deployment Trigger

Whenever you create a new Tag that looks like api-v*.*.*, the GitHub Action will kick off a backend deployment. First, this will handle migrating the DB according to your Prisma migrations, then it will update and restart the game API server in DigitalOcean.

NOTE: When creating a new API Tag, DO NOT create a Release! Releases are reserved for hosting the desktop app files only...

Deploys, Releases and Tags

Each app in the monorepo is deployed using GitHub Actions, however, they all have different trigger mechanisms.

NOTE: If you just finished the "Deployment Setup" section above, you might have your web and desktop apps deployed, but they'll be missing the proper environment secrets for the production game server. You can resolve this by first updating the environment secrets for both apps (VITE_API_URL and VITE_WEBSOCKET_URL), then triggering new deployments.

Web Deploy

Simply push to the main branch or manually trigger the Deploy Web to GitHub Pages workflow from the Actions tab in GitHub. The automated workflow is looking for any changes to apps/web/, as well as packages/.

Desktop Deploy

To build and host the desktop app files, create a Release with a new Tag (or just push a new Tag), that looks like v*.*.*. You can do this through the GitHub UI, by clicking on "Releases" > "Draft a new release". The GitHub Release will be updated with the desktop app files as they are built.

NOTE: It's very important that the "Releases" are reserved for hosting the desktop app files only. The native app updates and download button on web depend on this.

If you want to delete a Release and Tag, you can do that! I recommend doing this through the GitHub UI. Once you delete the Release and Tag, you'll most likely encounter a git error the next time you try to push. If you do, run this command to force pull the tags:

git pull --tags origin main --force

Game Server Deploy

To deploy the game server, create a new Tag that looks like api-v*.*.*. You can do this via the git CLI:

git tag api-v0.0.1
git push origin api-v0.0.1

Once you push the Tag, the GitHub Action will kick off the backend deployment. This will migrate the DB and update the running game API server.

NOTE: It's very important that you only create a Tag for backend deploys, and NOT a "Release". Releases are reserved for hosting the desktop app files only...

If you want to delete an API Tag, you can do that! I recommend doing this through the GitHub UI. Once you delete the Tag, you'll most likely encounter a git error the next time you try to push. If you do, run this command to remove the local Tag:

git tag -d api-v0.0.1

Cost Breakdown

A focal point of this project is to be as cost-effective as possible at the start. Here's a breakdown of the costs associated with running everything:

Provider Service Cost
GitHub Actions CI/CD Free
GitHub Pages Web Hosting Free
GitHub Releases Desktop App Hosting Free
Supabase Auth and DB Free
DigitalOcean Persistent Server ~$4/month
Apple macOS signing cert ~$8/month ($100/year)
Microsoft Azure Windows signing cert $10/month

TOTAL: ~$22/month or ~$264/year

About

Ore Rush Online is a mining MMO built entirely with TypeScript! This monorepo contains the desktop client, api server and marketing website. Created from the ts-online-game-template.

Resources

License

Stars

Watchers

Forks