Race API

This section provides a comprehensive guide for developers on how to use the Race Protocol API to write WASM bundles for their games, covering the process from scratch to publishing.

Prerequisites

Before diving into the API, ensure you have the following set up:

  • Rust development environment: Install Rust and the necessary tools (e.g., Cargo) to compile your game logic into WASM.

  • Race Protocol dependencies: Include the race-api and race-proc-macro crates as dependencies in your project's Cargo.toml file.

  • Basic understanding of Rust: Familiarity with Rust syntax and concepts is essential for writing game handlers.

1. Defining Game State and Events

Start by defining the core data structures that represent your game's state and events:

1. Game State:

  • Create a struct to represent your game's state. This struct should hold all the necessary information to track the game's progress, such as player data, game stage, randomness identifiers, and any game-specific data.

  • Derive the BorshSerialize and BorshDeserialize traits for your game state struct. This allows it to be serialized and deserialized for communication between the WASM bundle and the runtime environment.

Example:

// Rust code
use race_api::prelude::*;

#[derive(BorshSerialize, BorshDeserialize)]
struct GameState {
    players: Vec<Player>,
    current_stage: GameStage,
    random_id: RandomId,
    // ... other game-specific data
}

2. Custom Events:

  • Define an enum to represent the custom events that can occur in your game. These events should correspond to player actions, decisions, and other game-specific occurrences.

  • Implement the CustomEvent trait for your event enum. This provides the necessary functionality for serializing and deserializing events for communication with the Transactor.

Example:

// Rust code
use race_api::prelude::*;

#[derive(BorshSerialize, BorshDeserialize)]
enum GameEvent {
    PlayerJoined(PlayerJoin),
    PlayerMadeDecision(DecisionId, String),
    // ... other game-specific events
}

impl CustomEvent for GameEvent {}

2. Implementing the GameHandler Trait

The GameHandler trait is the core interface between your game logic and the Race Protocol runtime. Implement this trait for your game state struct:

1. init_state Function:

  • This function is called when the game is loaded by the Transactor or validator servers.

  • It takes an Effect object and an InitAccount object as arguments.

  • Use the InitAccount to access the initial game data from the on-chain game account.

  • Initialize your game state struct based on the initial data and perform any necessary validation.

  • Return a HandleResult containing either your initialized game state or an error if validation fails.

Example:

// Rust code
impl GameHandler for GameState {
    fn init_state(_effect: &mut Effect, init_account: InitAccount) -> HandleResult<Self> {
        // Validate and extract game data from init_account
        let players: Vec<Player> = init_account.players.into_iter().map(Into::into).collect();
        // ... initialize other game state fields

        Ok(Self {
            players,
            // ... other fields
        })
    }

    // ... other functions
}

2. handle_event Function:

  • This function is called whenever the game handler receives an event.

  • It takes a mutable Effect object and an Event object as arguments.

  • Use the Event to determine the type of event and perform the corresponding game logic.

  • Update the game state based on the event and use the Effect object to trigger actions like generating randomness, assigning secrets, or making settlements.

  • Return a HandleResult indicating success or an error if event handling fails.

Example:

// Rust code
impl GameHandler for GameState {
    // ... other functions

    fn handle_event(&mut self, effect: &mut Effect, event: Event) -> HandleResult<()> {
        match event {
            Event::GameStart { .. } => {
                // Initialize game logic, generate randomness, etc.
            }
            Event::Custom { sender, raw } => {
                let game_event: GameEvent = GameEvent::try_parse(&raw)?;
                // Handle custom game event logic
            }
            // ... handle other built-in events
            _ => {}
        }

        Ok(())
    }
}

3. into_checkpoint Function:

  • This function is called when the Transactor creates a checkpoint of the game state.

  • It takes your game state struct as an argument.

  • Convert your game state into a checkpoint struct that can be serialized and stored on-chain.

  • Return a HandleResult containing either your checkpoint struct or an error if conversion fails.

Example:

// Rust code
impl GameHandler for GameState {
    // ... other functions

    fn into_checkpoint(self) -> HandleResult<Checkpoint> {
        // Convert game state into checkpoint data
        Ok(Checkpoint {
            // ... checkpoint fields
        })
    }
}

3. Building and Publishing the WASM Bundle

Once you have implemented the game handler, you can build and publish your WASM bundle:

1. Building the WASM Bundle:

  • Use the wasm-pack tool to compile your Rust code into a WASM bundle.

  • You can specify the target platform (e.g., web) and optimization level (e.g., -Oz) during the build process.

Example:

wasm-pack build --target web --release -Oz

2. Publishing the WASM Bundle:

Example:

race-cli publish MyGame solana https://arweave.net/...

4. Testing and Debugging

Testing and debugging are crucial steps in ensuring your game logic functions correctly and fairly. Race Protocol provides a comprehensive test kit (race-test crate) to facilitate both unit and integration testing. Refer to the Testing section for detailed instructions on how to use the test kit effectively.

Conclusion

By following these steps and utilizing the Race Protocol API, developers can create secure, fair, and extensible WASM bundles for their web3 games. The API provides a clear separation between game logic and blockchain interactions, simplifying development and ensuring a consistent and reliable gameplay experience for players.

Last updated