Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Achievements page to Game Node API section #40

Merged
merged 3 commits into from
May 14, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions docs/home/350-game-node-api/2-achievements.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Achievements

[PRC-1] defines a standard API for Paima Engine game nodes to serve achievement
metadata and progress. Paima Engine itself includes an implementation of this
API that only requires your game to export an achivement list and submit SQL
updates to set player progress.

## Achievement metadata

When serving the achievements API, Paima Engine will attempt to use the `achievements` export with type `Promise<AchievementMetadata>` from your `packaged/endpoints.cjs`, if it exists.

```ts
// api/src/index.ts
const achievements: Promise<AchievementMetadata> = Promise.resolve({
game: {
id: "example",
},
list: [
{
name: "finish-chapter-1",
displayName: "Over The River",
description: "Finish Chapter 1.",
},
{
name: "finish-chapter-2",
displayName: "Through The Woods",
description: "Finish Chapter 2.",
},
],
});

export default RegisterRoutes; // The usual default export.
export { achievements }; // Include this to enable the achievement API.
```

Achievement metadata uses types defined in [PRC-1] as well as the following:
```ts
/** The type of the `achievements` export of `endpoints.cjs`. */
export interface AchievementMetadata {
/** Game ID, name, and version. */
game: Game;
/** Achievement types. */
list: Achievement[];
/**
* Per-language overrides for achievement display names and descriptions.
* Falls back to base definition whenever absent.
*/
languages?: {
[language: string]: {
[name: string]: {
displayName?: string;
description?: string;
};
};
};
}
```

TypeScript definitions for `AchievementMetadata` and [PRC-1] can be imported from `@paima/utils-backend`:
```ts
import type { AchievementMetadata, Game, Achievement } from '@paima/utils-backend';
```

## Achievement progress

Paima SDK exports `pgtyped` queries to store and retrieve achievement progress.
They can be imported and used in your API or a state transition function:

```js
import { getAchievementProgress, setAchievementProgress } from '@paima/db';
// ... other imports ...

async function wonBattle(wallet: number, blockTime: Date, dbConn: Pool): Promise<SQLUpdate[]> {
// Get user's current achievement progress.
const row = (await getAchievementProgress.run({ wallet, names: ['win-10-battles'] }, dbConn))[0];
if (!row?.completed_date) {
// Not complete yet. Add one, mark completed if needed, and store it back.
const newProgress = (row?.progress ?? 0) + 1;
return [
[setAchievementProgress, {
name: 'win-10-battles',
wallet,
completed_date: newProgress >= 10 ? blockTime : null,
progress: newProgress,
total: 10,
} satisfies ISetAchievementProgressParams],
];
} else {
// Already complete. Nothing to update.
return [];
}
}
```

Achievement progress stored this way will be served by Paima Engine's built-in
PRC-1 implementation.

[PRC-1]: ../20000-PRCs/prc-1.md
6 changes: 5 additions & 1 deletion src/css/custom.css
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ img {
@apply mb-3;
}

code {
vertical-align: unset;
}

.img-full {
@apply w-full max-w-full;
}
Expand Down Expand Up @@ -220,4 +224,4 @@ img {

.code-block-alternate-color-line:nth-child(odd) {
background-color: rgba(0, 0, 0, 0.05);
}
}
Loading