What you’ll learn
We’re going to build a simple leaderboard module from scratch. Users will be able to authenticate, submit leaderboard scores, and retrieve the top scores. This will teach you how to build a simple leaderboard module from scratch & all related Open Game Backend concepts including:- Create a module
- Setup your database
- Write scripts
- Write a test
Prerequisites
Step 1: Create project
Create a new directory. Open a terminal in this directory. Run the following command:What did this do?
What did this do?
This will create a
backend.json file in your directory with some default
settings.Step 2: Create module
In the same terminal, run:What did this do?
What did this do?
This will create a directory
modules/my_leaderboard and updated
backend.json to include the module. The important files we’ll look at are
modules/my_leaderboard/db/schema.prisma, modules/my_leaderboard/scripts/,
and modules/my_leaderboard/tests/.Step 3: Write database schema
Edit yourmodules/my_leaderboard/db/schema.prisma file to look like this:
modules/my_leaderboard/db/schema.prisma
What's going on here?
What's going on here?
model Scorescreates a new table that will store all of the scores on the leadboardidis the unique identifier for each score.Stringis the type of the column@idindicates that this is the primary key of the table@default(uuid())indicates that this column will be automatically set to a UUID when a new row is created@db.Uuidindicates that this is stored as UUID type. UUIDs are universally unique (unlike integers) and take a small amount of storage (unlike strings).
createdAtis the time the score was createdDateTimeis the type of the column@default(now())indicates that this column will be automatically set to the current time when a new row is created@db.Timestampindicates that this is stored as a timestamp type. This is a good way to store dates and times in a database.
userIdis the user who submitted the scoreStringis the type of the column@db.Uuidindicates that this is stored as UUID type.
scoreis the score itselfIntis the type of the column
@@index([score])creates an index on thescorecolumn.- This means the database will keep track of the scores in order so we can query them efficiently.
Open Game Backend is powered by PostgreSQL.
Learn about why we chose PostgreSQL here.
Step 4: Write submit_score script
We’re going to create a submit_score script that will:
- Throttle requests
- Validate user token
- Insert score
- Query score rank
1
Create script
In the same terminal, run:
What did this do?
What did this do?
This will create a new file This command also udpated
modules/my_leaderboard/scripts/submit_score.ts:modules/my_leaderboard/module.json to include the new script:What's going on here?
What's going on here?
import { ScriptContext } from "../module.gen.ts";imports theScriptContext.- The
module.gen.tsfile is generated by Open Game Backend to provide a type-safe interface for the database (seeschema.prisma), calling other modules, and reading the module config.
- The
export interface Request {}andexport interface Response {}define the input and output of the script.- Open Game Backend will automatically generate strict schema validation for these types. This reduces unintentional errors and potential exploits/crashes.
export async function run(...)is the main function of the script.- This is where you write the code that will be executed when the script is called.
2
Add dependencies & make public
Update the
modules/my_leaderboard/module.json file to look like this:modules/my_leaderboard/module.json
What did this do?
What did this do?
- Added
userandrate_limitas dependencies. This will allow us to authenticate the user and rate limit calls to the script. - Set
public: truefor thesubmit_scorescript so that anyone can call it.
3
Update request & response
Open
modules/my_leaderboard/scripts/submit_score.ts and update Request and Response to look like this:modules/my_leaderboard/scripts/submit_score.ts
What did this do?
What did this do?
This will allow the user to authenticate using
userToken and submit a score using score.4
Throttle requests
At the top the
run function in modules/my_leaderboard/scripts/submit_score.ts file, add code to throttle requests:models/my_leaderboard/scripts/submit_score.ts
What did this do?
What did this do?
This will rate limit the script to 1 request every 15 seconds. If the rate limit is exceeded, a
rate_limit_exceeded error will be thrown. See the rate limit docs for more details.5
Validate user token
Then authenticate the user:
models/my_leaderboard/scripts/submit_score.ts
What did this do?
What did this do?
This will authenticate the user and throw an error if the token is invalid. See the token docs for more details.
6
Insert score
Then insert the score in to the database:
models/my_leaderboard/scripts/submit_score.ts
What did this do?
What did this do?
This inserts the score into the database.
id and createdAt will be automatically set by the database because of the @default annotations in the schema.7
Query rank
Finally, query the score’s rank:
models/my_leaderboard/scripts/submit_score.ts
What did this do?
What did this do?
This will return the number of scores that are greater than (
gt) the user’s score. This is the user’s rank on the leaderboard.We add 1 to the rank, since the 1st place will have 0 scores greater than it.Full submit_score.ts source code
Full submit_score.ts source code
models/my_leaderboard/scripts/submit_score.ts
Step 5: Create get_top_scores script
1
Create script
In the same terminal, run:
2
Make public
Open
modules/my_leaderboard/module.json and update the get_top_scores script to be public:modules/my_leaderboard/module.json
3
Update request & response
Open
modules/my_leaderboard/scripts/get_top_scores.ts and update Request and Response to look like this:modules/my_leaderboard/scripts/get_top_scores.ts
What did this do?
What did this do?
This will allow the user to request the top
count scores and return an array of Score objects.4
Throttle requests
At the top the
run function in modules/my_leaderboard/scripts/get_top_scores.ts file, add code to throttle the requests:models/my_leaderboard/scripts/get_top_scores.ts
5
Query scores
Then query the top scores:
models/my_leaderboard/scripts/get_top_scores.ts
What did this do?
What did this do?
takelimits the number of scores returned toreq.count.orderByorders the scores byscorein descending order.selectlimits the columns returned touserId,createdAt, andscore.
6
Convert rows
Finally, convert the database rows in to
Score objects:models/my_leaderboard/scripts/get_top_scores.ts
What did this do?
What did this do?
This converts the database rows into the
Score objects we defined in the Response.We use toISOString to convert the createdAt of type Date into a string.Full get_top_scores.ts source code
Full get_top_scores.ts source code
models/my_leaderboard/scripts/get_top_scores.ts
Step 6: Start development server
1
Start development server
In the same terminal, run:
2
Migrate database
You will be prompted to apply your schema changes to the database. Name the migration init (this name doesn’t matter):
3
Success
You’ve now written a full module from scratch. You can now generate an SDK to use in your game or publish it to a registry.
Step 7 (optional): Test module
Tests are helpful for validating your module works as expected before running in to the issue down the road. Testing is optional, but strongly encouraged.All modules provided in the default registry are thoroughly tested.
1
Create test
2
Write test
Update
modules/my_leaderboard/tests/e2e.ts to look like this:modules/my_leaderboard/tests/e2e.ts
3
Run test
In the same terminal, run:You should see this output once complete: