Skip to content

Changing game data for local testing

Robert Konigsberg edited this page Jun 24, 2021 · 5 revisions

Introduction

An excellent technique for local testing is to change a game's state. The following technique only works when testing locally. It won't work on a cloud-hosted repo such as Heroku.

This technique is also terrific for locally reproduce a bug before sharing it. And you can even send a file of game’s current state as part of a bug report.

Prerequisites

The example uses Linux, and I expect you to know how to use a text editor, and grep. Understanding how JSON works wouldn’t hurt either.

You’ll need your clone of the Git repository ready to run (via node install and npm run build)

Example case

Somebody reported a bug that Giant Space Mirror wasn’t working with Space Station and the assocaited titanium space bonus. In order to recreate this, I will add those two cards to a player’s hand, and make sure the player has enough titanium.

I’m going to do this in five steps:

  1. Switch from sqlite to the local filesystem database
  2. Create a game
  3. Halt the server
  4. Edit the game
  5. Restart the server

Step 1: Switch from sqlite to the local filesystem database

You only need to do this once. And in fact, I recommend this for anyone doing local development -- it makes inspecting game state so much simpler.

The default database for local development is Sqlite. And while Sqlite is shockingly powerful, it falls a bit short for this kind of development workflow. The local database is a simple implementation of one file per game. It's inefficient, terrible for a production instance, but excellent for developers. Read more about it here..

One way this app is controlled is through a set of environment variables. Environment variables are specified in a file called .env, and you can read about it (here)[https://github.com/bafolts/terraforming-mars/wiki/dot-env].

  • Edit the file .env, and add this entry:
LOCAL_FS_DB=on

And that’s it. Save the file. Now whenever the server starts, it will store files locally instead of in the sqlite database. Existing games in the sqlite database won't be accessible (unless you remove the LOCAL_FS_DB environment variable.)

Step 2: Create the game

Go through the steps to create the game exactly as you like. Don't worry about whether it has the right corporation cards available for your test, you can fix those later. The important thing is to set it up the remaining configuration parameters.

Go as far as picking the initial hand. Pick whatever you want, because you're about to change it. (This is necessary simply because the game doesn't get saved until after you choose an initial hand.)

Take careful note of the new game ID.

Step 3: Stop the server.

You have to take this step because the server keeps a lot of data in memory. Once you change data in Step 4, without stopping and restartingr the app, it won't recognize that the data has changed.

Step 4: Edit the game.

Like I said above, the game is stored as a file on disk. Given the game id, the file is stored relative to the repo root directory at db/files/${gameid}.json.

Open the file in your favorite editor, and you'll see the whole file available as a large json file.

Now, this is the whole game as a readable file. And we are going to do three things:

  1. Give the player some titanium.
  2. Add Giant Space Mirror to the player's hand
  3. Add Space Station to the player's played cards

I won't go too much into the JSON file's structure, but it matches the structure in SerializedGame. A top level field is players, which is itself an array. If you have multiple players in your test game, you'll have to find the correct one by searching for the subfield name, color, or id, or by any other bit of data you know about what's in the player's state. You can read more about what goes into a player by reading the interface SerializedPlayer.

  1. Give the player some titanium.

This is the simplest part: within the player's entry is a field called titanium. Set the value to whatever you want.

  1. Add Giant Space Mirror to the player's hand

SerializedPlayer has a field called cardsInHand which represents the player's hand. Its type is CardName, so adding the card to that array is pretty simple:

"cardsInHand": [
  "Giant Space Mirror"
],
  1. Add Space Station to the player's played cards

Within the player object is a field called playedCards. It's possible the array is empty, so while you might think the right thing is to add the cards by name like so:

"playedCards": [
  "Space Station"
],

you would be surprised. Going back to look at SerializedPlayer you can see that playedCards is of type SerializedCard. Here's an example of a set of played cards in the middle of a game:

"playedCards": [
  {
    "name": "Peroxide Power"
  },
  {
    "name": "Power Supply Consortium"
  },
  {
    "name": "Olympus Conference",
    "resourceCount": 1
  },
  {
    "name": "Mining Area",
    "bonusResource": "steel"
  }
],

So if this is a brand new game, you'll create an entry that looks like this:

"playedCards": [
  {
    "name": "Space Station"
  },
],

Save the file.

Step 5: Restart the server

Once you restart the server and refresh the player's page, you will see the new game's state.

Gotchas and Tips

JSON format is more strict than Javascript.

Always use double-quotes around strings. Don't include trailing commas at the end of lists. I'm sure there are more, but take care.

Card name can have a colon after it.

Expansions and promos have replacement cards. So if you mean to use the promo version of Great Dam, you'll see in CardName.ts that it's defined as

GREAT_DAM_PROMO = 'Great Dam:promo',