Skip to content

pixelaw/core

Repository files navigation

PixeLAW Core

A game built on top of Dojo. See live example at <SECRET_URL: please reach out us on discord>.

This repository includes core components and systems. For more details, please check PixeLAW Book.

Concepts

  • World : A Cartesian plane (2d grid), where every position represents a "Pixel"
  • Pixel : One x/y Position, that has 6 primitive properties and one behavior template (App)
  • App : A Pixel can have only one App that defines its behavior
  • App2App : Interactions between Apps, where allowed
  • Action : A specific behavior in the context of an App
  • Queued Action : Action to be executed in the future, can be scheduled during an Action

App Core Behavior (for owner)

  • register : Register the App in the World
  • unregister : Remove the App from the World
  • allow_app
  • disallow_app

App Properties

  • name
  • permissions (bool hashmap of appname+property)

Core Pixel Behavior

  • update_all
  • update_app
  • update_color
  • update_owner
  • update_text
  • update_alert
  • update_timestamp

Pixel Properties (every Pixel has these)

  • position (cannot be changed)
  • app
  • color
  • owner
  • text
  • alert
  • timestamp

Default App

  • paint (put_color , remove_color)

Checking Permissions

  • Is calling Player the owner of the pixel -> they can do anything
  • Is calling App allowed to update the Property?
  • Problem
    • If scheduled, the calling App is CoreActions
    • How can we reliably check

Scheduling Actions

  • Can schedule anything through the Core.ScheduleAction function (?)
  • This stores a hash onchain that will be removed once executed
  • Core.ProcessScheduleAction takes the calldata and
    • checks if the hash exists
    • checks if it's not too early
  • Upside
    • Onchain storage is minimized
  • Problem

Snake

  • properties
    • direction
    • head_position
    • length?
  • behavior
    • spawn
      • position
      • color
      • text
      • direction
    • move

      • handle_next_pixel
        • normal:
          • head moves to next
          • rollback last
        • die
          • iterate all pixels and rollback
        • longer
          • head moves to next
        • shorter
          • head moves to next
          • rollback last 2
      • change_direction

What if..

Future actions are 1 per Pixel?

Todo

  • handle unregistered apps
  • research feasibility of "hooks"
  • Properly hook up process_queue so it is allowed to do player_id, but normal calls are not.

Snake moves onto a non-owned Paint Pixel

  • action: snake_move
  • check pixel that will be occupied
    • call update_color on that pixel
    • is PaintApp allowing update_color from Snake?

Prerequisites

  • Rust - install here
  • Cairo language server - install here
  • Dojo - install here
  • Scarb - install here
  • NodeJS - install here

Developing Locally

Step 1: Build the contracts

make build

This command compiles your project and prepares it for execution.

Step 2: Start Keiko

The Keiko is a container that has the Katana RPC, the Torii World Indexer, and a Dashboard. Once the container starts, it starts running Katana, deploys the World Container from the repo via the contracts volume (See the docker-compose.yml for more details), runs the post_deploy script from the repo's Scarb.toml, and starts up Torii. Keiko Dashboard is accesible via http://localhost:3000/fork.

make start_keiko

Step 3: Get the React frontend ready

make prep_web
cd web
yarn

Step 4: Run the frontend locally

cd web
yarn dev

Step 5: Run the queue bot

cd bots
yarn install
yarn dev
brew install pkg-config cairo pango libpng jpeg giflib librsvg pixman

NOTE

To change accounts, add an account query to the frontend url. For example: http://localhost:3000/?account=1. Add as many accounts as desired by following the pattern set in the env.example.

The following would be example players:

# for player 1
http://localhost:5173/?account=1
# for player 2
http://localhost:5173/?account=2

Project Structure

This is an overview of the most important folders/files:

  • Makefile : A collection of helpful commands, mainly for Dojo
  • contracts : The Dojo Cairo smart contract code
    • src/components.cairo : Dojo component definitions
    • src/systems.cairo : Dojo component definitions
    • src/Scarb.toml : The scarb config file used for katana
  • web : A Vite React project
    • .env : (copied from env.example) Contains the hardcoded developer addresses used for Dojo
    • src/dojo/contractComponents.ts : Client-side definitions of the components
    • src/dojo/createClientComponents.ts : Client-side setup of the components
    • src/dojo/createSystemCalls.ts : Client-side definitions of the systems

Typical development activities

Add a DOJO system

  • Edit src/systems.cairo
  • Edit src/dojo/createSystemCalls.ts

Add a DOJO component

  • Edit src/components.cairo
  • Edit src/dojo/contractComponents.ts
  • Edit src/dojo/createClientComponents.ts

Redeploy to Katana

  • Restart Katana
  • Redeploy the contracts with cd contracts && scarb run deploy

Troubleshooting / Tricks

When using vscode, the cairo language server panics with thread 'main' panicked at 'internal error: entered unreachable code:

Resolution: None, this is a know issue, can ignore

When deploying/migrating, consistent exceptions even though the contract compiles.

Resolution: Delete the contracts/target dir

How do I use different accounts while testing?

Register 2 accounts (example from https://github.com/coostendorp/dojo-rps):

let player1 = starknet::contract_address_const::<0x1337>();
let player2 = starknet::contract_address_const::<0x1338>();

And then switch accounts like this:

starknet::testing::set_contract_address(player1);

Deploying Contracts Remotely

Step 1 Follow slot deployment

Replace the rpc_url in Scarb.toml, as well as the account_address, and private_key with the slot katana url, account_address, and private_key. Read this to familiarize yourself with slot deployments. NOTE: set the invoke-max-steps to a sufficiently high number to allow ml-based games (4_000_000_000 is a good amount). Also, take note of copying the SEED, TOTAL_ACCOUNTS, and WORLD_ADDRESS

Deploying Katana

slot deployments create <world-name> katana --invoke-max-steps 4000000000

Get the account information and seed using the logs

slot deployments logs <world-name> katana > output.txt

Replace the account_address, private_key, and rpc_url, then deploy your world

cd contracts
sozo migrate --name <world-name>

Deploy Torii

slot deployments create <world-name> torii --rpc <katana-rpc-url> --world <world_address> --start-block 0

Step 2 Run post slot deployment

This will initialize the deployed world

cd contracts
scarb run slot_post_deploy

Step 3 Set environment variables

Set the following environment variables in the Docker Container that holds the PixeLAW Core image:

  1. PUBLIC_NODE_URL - the slot katana url provided
  2. PUBLIC_TORII - the slot torii url provided
  3. SLOT_KATANA - same value as the PUBLIC_NODE_URL
  4. SLOT_TORII - same value as the PUBLIC_TORII
  5. SEED - the seed provided when first deploying with Slot
  6. TOTAL_ACCOUNTS - number of accounts prefunded
  7. WORLD_ADDRESS - the address of the deployed world

Step 4 Upload the manifest

Wait till the Docker Container is up and running, then execute this command:

cd contracts
scarb run upload_manifest <replace-with-webapp-url-followed-by-a-/manifests>

Deployed Worlds

ID Address Core Version Dojo Branch
pixelaw 0x47864d08a1b86cf0dd52ea2785f8bc93b5f48edca8380bbb3057d158521e12d v0.0.45 v0.4.1 main
pixelaw1 0x662b50ea51bf4b9b4a72e48d189d11d4224456c06256f0d57d1756d2d909c47 v0.0.30 v0.3.15 demo1
pixelaw-test 0x2d409910fb90d701633d9e09139a2b37a13e1cbf1288fc4a9ae6ac09a59ff16 v0.0.45 v0.4.1 test
pixelaw-demo 0x51ee3a738cbe090c95eb8102427ffb31b796be7395968cdda70cbfdd675e65a v0.0.61 v0.4.1 main

How to create new Demo

  • Create a new demo branch
  • Add new workflow .github/workflows/demo{x}.yaml
  • Copy the content of demo1.yaml and only change below lines
        env:
          ARGOCD_SERVER: ${{ secrets.ARGOCD_SERVER }}
          ARGOCD_AUTH_TOKEN: ${{ secrets.ARGOCD_AUTH_TOKEN }}   
        run: |
            argocd app create $PROJECTNAME-{demo1} \        <-- Change demo1 to preferred demo number
                --repo https://github.com/pixelaw/core.git \
                --path chart/pixelaw-core  \
                --revision {demo1} \                        <--- Revision = BranchName, change it
                --dest-namespace $PROJECTNAME-{demo1} \     <-- Change demo1 to preferred demo number
                --dest-server https://kubernetes.default.svc \
                --helm-set-string dockerImage=$REGISTRY/$PROJECTNAME:${VERSION} \
                --upsert \
                --server $ARGOCD_SERVER \
                --auth-token $ARGOCD_AUTH_TOKEN 
    
    
    
  • Edit chart/pixelaw-core/values.yaml
  •       frontend: webapp-demo1  <-- Change demo1 to preferred demo number
    
        subDomainName:            <-- Change subdomains to preferred ones
          pixelaw: demo                 
          katana: katana.demo           
          torii: torii.demo             
          grpcTorii: grpc.demo