Skip to content

Commit

Permalink
Improve game bundle caching, add metadata field to INft
Browse files Browse the repository at this point in the history
  • Loading branch information
DogLooksGood committed Sep 29, 2023
1 parent 064dd79 commit 8eb3fb6
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 6 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,16 @@ Race Protocol: A multi-chain infrastructure for asymmetric competitive games

# Master(Unreleased)

# 0.2.1

## Features
- CLI: Add `claim` command to claim tokens from a recipient account.

## Enhancements
- SDK: Add caching for token/NFT fetching.
- SDK: Add caching for game bundle fetching. (Caching for wasm module is TBD)
- SDK: Add `data` and `dataLen` to `AppHelper.info`.
- SDK: Add original metadata to NFT structure.

## Breaking changes
- CONTRACT: Remove `claim_amount_cap`.
Expand Down
2 changes: 2 additions & 0 deletions js/sdk-core/src/accounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ export interface INft {
readonly name: string;
readonly symbol: string;
readonly collection: string | undefined;
readonly metadata: any;
}

export interface IRecipientAccount {
Expand Down Expand Up @@ -242,6 +243,7 @@ export class Nft implements INft {
readonly symbol!: string;
@field(option('string'))
readonly collection: string | undefined;
readonly metadata: any;
constructor(fields: INft) {
Object.assign(this, fields);
}
Expand Down
41 changes: 35 additions & 6 deletions js/sdk-core/src/app-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,16 @@ import { IWallet } from './wallet';
import { Handler, InitAccount } from './handler';
import { Encryptor, IEncryptor } from './encryptor';
import { SdkError } from './error';
import { EntryType, EntryTypeCash, GameAccount, IToken, PlayerProfile } from './accounts';
import { EntryType, EntryTypeCash, GameAccount, GameBundle, IToken, PlayerProfile } from './accounts';
import { TxState } from './tx-state';
import { Client } from './client';
import { Custom, GameEvent, ICustomEvent } from './events';
import { ProfileCache } from './profile-cache';
import { IStorage } from './storage';
import { IStorage, getTtlCache, setTtlCache } from './storage';
import { DecryptionCache } from './decryption-cache';

const BUNDLE_CACHE_TTL = 3600 * 365;

export type EventCallbackFunction = (
context: GameContextSnapshot,
state: Uint8Array,
Expand Down Expand Up @@ -60,6 +62,8 @@ export type GameInfo = {
token: IToken;
tokenAddr: string;
bundleAddr: string;
data: Uint8Array;
dataLen: number;
};

export class AppClient {
Expand All @@ -79,6 +83,7 @@ export class AppClient {
#profileCaches: ProfileCache;
#info: GameInfo;
#decryptionCache: DecryptionCache;
#storage?: IStorage;

constructor(
gameAddr: string,
Expand All @@ -95,7 +100,8 @@ export class AppClient {
onConnectionState: OnConnectionStateCallbackFunction | undefined,
encryptor: IEncryptor,
info: GameInfo,
decryptionCache: DecryptionCache
decryptionCache: DecryptionCache,
storage?: IStorage,
) {
this.#gameAddr = gameAddr;
this.#handler = handler;
Expand All @@ -113,6 +119,7 @@ export class AppClient {
this.#profileCaches = new ProfileCache(transport);
this.#info = info;
this.#decryptionCache = decryptionCache;
this.#storage = storage;
}

static async initialize(opts: AppClientInitOpts): Promise<AppClient> {
Expand All @@ -126,11 +133,30 @@ export class AppClient {
throw SdkError.gameAccountNotFound(gameAddr);
}
console.log('Game account:', gameAccount);
const gameBundle = await transport.getGameBundle(gameAccount.bundleAddr);

// Fetch game bundle
// The bundle can be considered as immutable, so we use cache whenever possible
const bundleCacheKey = `BUNDLE__${transport.chain}_${gameAccount.bundleAddr}`;

let gameBundle: GameBundle | undefined;
if (storage !== undefined) {
gameBundle = getTtlCache(storage, bundleCacheKey);
console.log('Use game bundle from cache:', gameBundle);
if (gameBundle !== undefined) {
Object.assign(gameBundle, { data: Uint8Array.of() })
}
}
if (gameBundle === undefined) {
gameBundle = await transport.getGameBundle(gameAccount.bundleAddr);
console.log('Game bundle:', gameBundle);
if (gameBundle !== undefined && storage !== undefined && gameBundle.data.length === 0) {
setTtlCache(storage, bundleCacheKey, gameBundle, BUNDLE_CACHE_TTL);
}
}
if (gameBundle === undefined) {
throw SdkError.gameBundleNotFound(gameAccount.bundleAddr);
}
console.log('Game bundle:', gameBundle);

const transactorAddr = gameAccount.transactorAddr;
if (transactorAddr === undefined) {
throw SdkError.gameNotServed(gameAddr);
Expand All @@ -156,6 +182,8 @@ export class AppClient {
maxPlayers: gameAccount.maxPlayers,
tokenAddr: gameAccount.tokenAddr,
bundleAddr: gameAccount.bundleAddr,
data: gameAccount.data,
dataLen: gameAccount.dataLen,
token,
};

Expand All @@ -179,7 +207,8 @@ export class AppClient {
onConnectionState,
encryptor,
info,
decryptionCache
decryptionCache,
storage,
);
} finally {
console.groupEnd();
Expand Down
1 change: 1 addition & 0 deletions js/sdk-solana/src/solana-transport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,7 @@ export class SolanaTransport implements ITransport {
symbol: trimString(metadataState.data.symbol),
image,
collection: metadataState?.collection?.key.toBase58(),
metadata: metadataState,
};

if (storage !== undefined) {
Expand Down

0 comments on commit 8eb3fb6

Please sign in to comment.