Guide for the backend code

This is a simple guide for the backend code structure, therefore we will not discuss every line of this codebase. Its structure looks like this:

.
├── Cargo.lock
├── Cargo.toml
├── Makefile
├── crates
│   ├── bin
│   │   ├── Cargo.toml
│   │   └── src
│   │       └── bin.rs
│   └── core
│       ├── Cargo.toml
│       └── src
│           ├── config.rs
│           ├── engine
│           │   ├── cards
│           │   │   ├── defend.rs
│           │   │   ├── mod.rs
│           │   │   └── strike.rs
│           │   ├── combat.rs
│           │   ├── mod.rs
│           │   ├── roles
│           │   │   ├── enemy
│           │   │   │   ├── mod.rs
│           │   │   │   └── monster
│           │   │   │       ├── mod.rs
│           │   │   │       ├── robot.rs
│           │   │   │       └── turkey.rs
│           │   │   ├── mod.rs
│           │   │   └── player.rs
│           │   └── state.rs
│           ├── lib.rs
│           ├── test.rs
│           ├── utils
│           │   ├── mod.rs
│           │   └── state.rs
│           └── wasm
│               └── mod.rs
├── rust-toolchain
└── test_cli.sh

The main logic body of the game is defined in the `engine` subdirectory. As you can see, it is a relatively simple turn-based game, which defines cards, roles, combat, etc.

The player and monsters both have `HP` representing their left life. They use handy cards to attack the opponent or defend himself.

There are two monsters: a robot and a turkey.

In `engine/mod.rs`, there is a definition of Engine:

pub struct Engine {
    pub(crate) player: Player,
    pub floor: usize,
}

It seems just preliminary right now, but enough for a demo.

In `utils/state.rs`, it defines the global game state singleton:

#[derive(Serialize)]
pub struct GameState {
    floor: usize,
    turn: usize,

    hero_hp: i32,
    hero_power: i32,
    hero_block: i32,
    hand_of_card: Vec<Card>,

    enemy_name: &'static str,
    enemy_hp: i32,
    enemy_block: i32,
    enemy_action: SkillEffect,
}

The game will use this GameState to manage the state of the instance of this roguelike card game.

Another important component is the `wasm/mod.rs`, it bridges the game and the underneath zkwasm SDK. Let's look into it:

use wasm_bindgen::prelude::wasm_bindgen;
use zkwasm_rust_sdk::require;
use zkwasm_rust_sdk::jubjub::BabyJubjubPoint;
use zkwasm_rust_sdk::jubjub::JubjubSignature;
use zkwasm_rust_sdk::wasm_input;
use primitive_types::U256;
use sha2::{Sha256, Digest};

As you can see, we import some functionalities from zkwasm_rust_sdk.

We will compile this Rust project into wasm target, and the target wasm file will contain some facilities provided by zkwasm.

In the same file, we find:

#[wasm_bindgen]
pub fn zkmain() {
}

The zkmain() is the entry of this game project.

Compile method

Given the output of this backend implementation are some wasm files, how to compile it, and how to export it to frontend is a challege.

We use a Makefile to overcome it. In the `Makefile`, we can see:

wasm:
	wasm-pack build --release crates/core --out-name gameplay --out-dir pkg
	cp crates/core/pkg/gameplay.d.ts ../src/games/roguelike/js/gameplay.d.ts
	cp crates/core/pkg/gameplay_bg.wasm ../src/games/roguelike/js/gameplay_bg.wasm
	cp crates/core/pkg/gameplay_bg.wasm.d.ts ../src/games/roguelike/js/gameplay_bg.wasm.d.ts
	cp crates/core/pkg/gameplay_bg.js ../src/games/roguelike/js/gameplay_bg.js

While we run `make wasm` in the game backend directory, it actually uses wasm-pack to execute the process of compilation. This process will compile wasm files to the pkg dir, and copy those facilities to the frontend directory. Then the frontend code can import these wasm files and call the functions written by Rust.

Last updated