Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
22 changes: 22 additions & 0 deletions .github/workflows/skill-review.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Tessl Skill Review β€” runs on PRs that change any SKILL.md; posts scores as one PR comment.
# Docs: https://github.com/tesslio/skill-review
name: Tessl Skill Review

on:
pull_request:
branches: [main]
paths:
- "**/SKILL.md"

jobs:
review:
runs-on: ubuntu-latest
permissions:
pull-requests: write
contents: read
steps:
- uses: actions/checkout@v4
- uses: tesslio/skill-review@main
# Optional quality gate (off by default β€” do not enable unless user asked):
# with:
# fail-threshold: 70
159 changes: 37 additions & 122 deletions skills/dojo-model/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,82 +1,20 @@
---
name: dojo-model
description: Create Dojo models for storing game state with proper key definitions, trait derivations, and ECS patterns. Use when defining game entities, components, or state structures.
description: "Create Dojo models in Cairo for storing game state with key definitions, trait derivations, and ECS composition patterns. Define player-owned entities, composite keys, singletons, and nested structs. Use when defining game entities, components, state structures, or data schemas."
allowed-tools: Read, Write, Edit, Glob, Grep
---

# Dojo Model Generation

Create Dojo models that define your game's state using Entity Component System (ECS) patterns.
Create Dojo models that define game state using Entity Component System (ECS) patterns.

## When to Use This Skill

- "Add a Position model"
- "Create a Player entity with health and level"
- "Generate an Inventory model"
- "Define a model for [game concept]"

## What This Skill Does

Generates Cairo model structs with:
- `#[dojo::model]` attribute
- Required trait derivations (`Drop`, `Serde`)
- Key field configuration (`#[key]`)
- Field types appropriate to your data

## Quick Start

**Interactive mode:**
```
"Add a model for player positions"
```

I'll ask about:
- Model name
- Key fields (what makes it unique)
- Data fields and their types

**Direct mode:**
```
"Create a Position model with player as key and x, y coordinates"
```
## Model Structure

## Essential Imports for Models
Models are Cairo structs annotated with `#[dojo::model]`, acting as a key-value store where `#[key]` fields define the lookup key.

**In your model file (e.g., `models.cairo`):**
```cairo
use starknet::ContractAddress;

// For nested structs that aren't models
use dojo::meta::Introspect;
```

**In systems that use models:**
```cairo
// Import your models
use my_project::models::{Player, Position, Inventory};

// Import Dojo storage traits
use dojo::model::{ModelStorage, ModelValueStorage};
```

**Reading/Writing models in a system:**
```cairo
// Get world storage
let mut world = self.world_default();

// Read - provide all #[key] values
let player: Player = world.read_model(player_address);

// Write - model must contain all keys and data
world.write_model(@player);
```

## Model Structure

Models are Cairo structs annotated with `#[dojo::model]`.
They act as a key-value store where `#[key]` fields define the lookup key.

```cairo
#[derive(Drop, Serde)]
#[dojo::model]
struct Moves {
Expand All @@ -86,17 +24,12 @@ struct Moves {
}
```

**Required traits:**
- `Drop` - Cairo ownership system
- `Serde` - Serialization for on-chain storage

**Optional traits:**
- `Copy` - Add when you need to copy values (for primitive types)
**Required traits:** `Drop`, `Serde`
**Optional traits:** `Copy` (for primitive types that need copying)

## Model Patterns

### Player-Owned Model
Models keyed by player address:
```cairo
#[derive(Drop, Serde)]
#[dojo::model]
Expand All @@ -116,7 +49,7 @@ struct Vec2 {
Custom nested structs must derive `Introspect` for Dojo to understand their structure.

### Composite Keys
Multiple keys for relationships (all keys must be provided when reading):
Multiple keys for relationships β€” all keys must be provided when reading:
```cairo
#[derive(Copy, Drop, Serde)]
#[dojo::model]
Expand All @@ -127,10 +60,8 @@ struct GameResource {
location: ContractAddress,
balance: u8,
}
```

Read with tuple of all keys:
```cairo
// Read with tuple of all keys
let resource: GameResource = world.read_model((player, location));
```

Expand All @@ -147,7 +78,6 @@ struct GameSetting {
setting_value: felt252,
}

// Usage
world.write_model(@GameSetting {
setting_id: RESPAWN_DELAY,
setting_value: (10 * 60).into()
Expand All @@ -159,81 +89,66 @@ Small, focused models that can be combined on entities:
```cairo
#[derive(Copy, Drop, Serde)]
#[dojo::model]
struct Position {
#[key]
id: u32,
x: u32,
y: u32,
}
struct Position { #[key] id: u32, x: u32, y: u32 }

#[derive(Copy, Drop, Serde)]
#[dojo::model]
struct Health {
#[key]
id: u32,
health: u8,
}
struct Health { #[key] id: u32, health: u8 }

// Human has Position + Health + Potions
// Orc has Position + Health (no Potions)
```

## Key Rules

1. **At least one key required** - Every model needs a `#[key]` field
2. **Keys must come first** - All key fields before data fields
3. **Keys are not stored** - Used only for indexing/lookup
4. **All keys required for read** - Composite keys must all be provided
1. **At least one key required** β€” every model needs a `#[key]` field
2. **Keys must come first** β€” all key fields before data fields
3. **Keys are not stored** β€” used only for indexing/lookup
4. **All keys required for read** β€” composite keys must all be provided

## Model API

Get the world storage in your system:
```cairo
use dojo::model::{ModelStorage, ModelValueStorage};

let mut world = self.world(@"my_namespace");
```
let mut world = self.world_default();

### Write a Model
```cairo
// Write
world.write_model(@Position { player, vec: Vec2 { x: 0, y: 0 } });
```

### Read a Model
```cairo
// Read
let position: Position = world.read_model(player);
```

### Read with Composite Key
```cairo
// Read with composite key
let resource: GameResource = world.read_model((player, location));
```

### Generate Unique ID
```cairo
// Generate unique ID
let entity_id = world.uuid();
world.write_model(@Health { id: entity_id, health: 100 });
```

## Field Types

- `u8`, `u16`, `u32`, `u64`, `u128`, `u256` - Unsigned integers
- `felt252` - Field elements
- `bool` - Booleans
- `ContractAddress` - Starknet addresses
- Custom structs - Must derive `Introspect`
- Custom enums - Must derive `Introspect`
| Type | Use for |
|------|---------|
| `u8`, `u16`, `u32`, `u64`, `u128`, `u256` | Unsigned integers |
| `felt252` | Field elements, hashes |
| `bool` | Flags, toggles |
| `ContractAddress` | Starknet addresses |
| Custom structs | Must derive `Introspect` |
| Custom enums | Must derive `Introspect` |

## Verification

## Next Steps
After creating models, verify they compile:

After creating models:
1. Use `dojo-system` skill to create systems that use your models
2. Use `dojo-test` skill to test model read/write operations
3. Use `dojo-config` skill to configure permissions
```bash
sozo build
```

## Related Skills

- **dojo-system**: Create systems that use these models
- **dojo-test**: Test your models
- **dojo-init**: Initialize project first
- **dojo-review**: Review model design
- **dojo-system**: Create systems that read and write these models
- **dojo-test**: Test model read/write operations
- **dojo-init**: Initialize project structure first
- **dojo-review**: Review model design patterns
Loading