Dojo.godot
Godot Engine is a free, open-source cross-platform game engine renowned for its flexibility, ease of use, and powerful scene system. With its intuitive node-based architecture, GDScript scripting language, and robust 2D and 3D capabilities, Godot empowers developers to create everything from indie platformers to complex multiplayer experiences.
Dojo.godot is the official Godot Engine SDK for building onchain games powered by Dojo. This GDExtension seamlessly integrates blockchain functionality into your Godot projects, enabling you to create fully decentralized games without compromising on performance or developer experience.
Core Concepts
ToriiClient
The ToriiClient
is your gateway to the Dojo world, managing all communication with the blockchain indexer.
Key responsibilities:
- Connection Management: Establish and maintain connections to Torii servers
- Entity Queries: Fetch game entities and their associated models from the blockchain
- Event Subscriptions: Subscribe to real-time blockchain events and entity updates
The ToriiClient operates as a singleton, ensuring consistent state across your entire game.
ControllerAccount
The ControllerAccount
handles all transaction-related operations and wallet management.
Core features:
- Wallet Authentication: Secure connection to Cartridge Controller accounts
- Transaction Execution: Execute smart contract calls with proper signing
- Session Management: Maintain authenticated sessions with configurable policies
DojoCall Resources
DojoCall
resources encapsulate smart contract function calls in a Godot-native format.
Structure:
- Contract Address (
to
): The target smart contract address - Function Selector: The name of the function to call
- Call Data: Array of parameters to pass to the function
These resources can be created and configured directly in the Godot editor, making smart contract integration visual and intuitive.
Cairo Type System
Dojo.godot automatically handles conversions between Cairo types and Godot equivalents:
- Primitives:
u8
,u16
,u32
,u64
,u128
,u256
,felt252
map to Godot integers and strings - Structures: Cairo structs convert to Godot dictionaries with proper field mapping
- Arrays: Cairo arrays become Godot Arrays with automatic element conversion
- Enums: Cairo enums map to Godot integers with enumeration support
- Special Types:
Vec2
structs automatically convert to Godot'sVector2
type
Getting Started
Prerequisites
Before getting started, ensure you have:
- Godot Engine
>= 4.2
- Rust toolchain
- SCons build system
- A C++17 compatible compiler (GCC, Clang, or MSVC)
- A working Dojo project with deployed contracts
- Basic familiarity with GDScript
Build the Extension
Clone and build the Dojo.godot extension:
git clone --recurse-submodules https://github.com/lonewolftechnology/godot-dojo
cd godot-dojo
# Build for your platform
scons platform=linux target=template_release # Linux
scons platform=windows target=template_release # Windows
scons platform=macos target=template_release # macOS
The compiled extension will be output to the bin/
directory.
Create Your Godot Project
- Create a new Godot project or open an existing one
- Copy the
bin/
directory andgodot-dojo.gdextension
file to your project root - Restart Godot to load the extension
Set Up Connection Management
Create an autoload script to manage your blockchain connections:
# autoload/connection.gd
extends Node
signal connected
@onready var client: ToriiClient = $ToriiClient
@onready var controller_account: ControllerAccount = $ControllerAccount
func _ready() -> void:
# Configure the client
client.torii_url = "http://localhost:8080" # or your Torii server
client.world_address = "0x..." # Your world contract address
# Connect signals
client.client_connected.connect(_on_client_connected)
controller_account.controller_connected.connect(_on_controller_connected)
func connect_client() -> void:
client.create_client()
func connect_controller() -> void:
controller_account.setup()
func _on_client_connected(success: bool) -> void:
if success:
connect_controller()
func _on_controller_connected(success: bool) -> void:
if success:
connected.emit()
Basic Usage
Connecting to Dojo
Start by establishing connections to both Torii and the Controller:
# In your main game script
extends Node
func _ready() -> void:
# Wait for connection autoload to be ready
await get_tree().process_frame
Connection.connected.connect(_on_dojo_connected)
Connection.connect_client()
func _on_dojo_connected() -> void:
print("Successfully connected to Dojo!")
# Now you can interact with the blockchain
setup_subscriptions()
query_entities()
Creating Contract Calls
Define your contract interactions using DojoCall resources:
# Create a spawn action
@export var spawn_call: DojoCall
func _ready() -> void:
# Configure the call (or use .tres resource files)
spawn_call = DojoCall.new()
spawn_call.to = "0x..." # Contract address
spawn_call.selector = "spawn"
spawn_call.calldata = [] # Parameters array
func spawn_player() -> void:
Connection.controller_account.execute_from_outside(spawn_call)
Subscribing to Events
Listen for blockchain events in real-time:
func setup_subscriptions() -> void:
# Subscribe to all events
Connection.client.create_event_subscription(_on_events, {})
# Subscribe to entity updates
Connection.client.create_entity_subscription(_on_entities, {})
func _on_events(event_data: Dictionary) -> void:
print("Received event: ", event_data)
# Handle event data - structure depends on your contracts
var data = event_data["data"]
for entry in data:
process_event_entry(entry)
func _on_entities(entity_data: Dictionary) -> void:
print("Entity updated: ", entity_data)
# Handle entity updates - structure depends on your models
var data = entity_data["data"]
for entry in data:
process_entity_update(entry)
Querying Entities
Fetch current blockchain state:
func query_players() -> void:
var query = {
"pagination": {
"limit": 10,
"cursor": "",
"order_by": [],
"direction": ToriiClient.QueryPaginationDirection.FORWARD
},
"clause": null,
"no_hashed_keys": true,
"models": [],
"historical": false
}
var entities = Connection.client.get_entities(query)
for entity in entities:
process_entity(entity)
func process_entity(entity: Dictionary) -> void:
for model in entity.models:
for key in model:
var data = model[key]
# Process based on your model structure
if data.has("Vec2"):
var position = Vector2(data["Vec2"]["x"], data["Vec2"]["y"])
update_player_position(data["player"], position)
Advanced Features
Using Resource Files
Create reusable DojoCall resources in the Godot editor:
- In the FileSystem dock, right-click and select
New Resource
- Choose
DojoCall
as the resource type - Configure the properties in the Inspector:
- To: Contract address
- Selector: Function name
- Calldata: Parameter array
- Save as
.tres
file
Managing Policies
Configure controller permissions using DojoPolicies resources:
# Create policy resource
var policy = DojoPolicy.new()
policy.target = "0x..." # Contract address
policy.method = "move"
var policies = DojoPolicies.new()
policies.name = "game_actions"
policies.contract = "0x..." # Contract address
policies.policies = [policy]
# Use with controller account
controller_account.create(policies)
Type Conversion
Dojo.godot handles type conversion automatically:
# Cairo Vec2 becomes Godot Vector2
func handle_position_update(data: Dictionary) -> void:
if data.has("Vec2"):
var position = Vector2(data["Vec2"]["x"], data["Vec2"]["y"])
player.position = position
# Enums convert to integers
enum Direction { LEFT, RIGHT, UP, DOWN }
func move_player(direction: Direction) -> void:
move_call.calldata = [direction] # Automatically converts to felt252
controller_account.execute_from_outside(move_call)
Building and Deployment
Development Builds
For development, use debug builds to enable logging:
scons platform=linux target=template_debug
Set environment variables in your game for detailed logging:
func _ready() -> void:
OS.set_environment("RUST_BACKTRACE", "full")
OS.set_environment("RUST_LOG", "debug")
Production Builds
Create optimized builds for distribution:
scons platform=linux target=template_release
scons platform=windows target=template_release
scons platform=macos target=template_release
WebAssembly Support
Build for web deployment (experimental):
scons platform=web target=template_release
Note that WebAssembly builds may have limitations compared to native platforms.
Example Project
The Dojo.godot repository includes a complete demo project showcasing:
- Player Movement: Onchain player spawning and movement using arrow keys
- Real-time Updates: Live synchronization between blockchain state and game visuals
- Controller Integration: Seamless wallet authentication and transaction signing
- Event Handling: Processing both events and entity updates from subscriptions
To run the demo:
- Set up a local Dojo Starter environment
- Build the Dojo.godot extension following the instructions above
- Open the
demo
folder in Godot and run the project
The demo connects to a live testnet deployment, demonstrating real blockchain integration in a simple 2D movement game.
Troubleshooting
Enable detailed logging for troubleshooting:
func _ready() -> void:
OS.set_environment("RUST_LOG", "debug,tokio=info,hyper=info")
OS.set_environment("RUST_BACKTRACE", "full")
Check the Godot console for detailed error messages and stack traces.