diff --git a/docs/pages/framework/world/api.md b/docs/pages/framework/world/api.md index 4fc230c9..b5704d82 100644 --- a/docs/pages/framework/world/api.md +++ b/docs/pages/framework/world/api.md @@ -33,7 +33,7 @@ struct Moves { ## `read_model` -The `read_model` command is used to retrieve models from the world state: +The `read_model` command is used to retrieve a model from the world state: ```rust let player = get_caller_address(); @@ -44,7 +44,7 @@ let mut position: Position = world.read_model(player); Here we are retrieving the `Position` model from the world state. We are also using the `caller` to retrieve the models for the current entity. :::warning -As a recall, a model must have at least one [key](/framework/models#the-key-attribute) to be retrievable. In this example, we assume that `Position` model has exactly one key, `caller`. +As a recall, a model must have at least one [key](/framework/models#the-key-attribute) to be retrievable. In this example, the model `Position` has exactly one key, `caller`. ::: You can then use `position` as you would as any other Cairo struct. @@ -62,7 +62,7 @@ If you use the `read_model` command on a model that has never been set before, a ## `write_model` -The `write_model` command is used to update models state. +The `write_model` command is used to update a model's state. ```rust let player = get_caller_address(); @@ -81,6 +81,36 @@ world.write_model(@position); Here we are updating the `Position` model in the world state using the `player` as the entity id. +## `read_member` and `read_member_of_models` + +The `read_member` and `read_member_of_models` commands are used to read a member from model(s). + +```rust +// The `ptr_from_keys` function is used to get the pointer to the model in the storage, for the given keys. +// Then the `selector!` macro is used to get the member to target. +let vec: Vec2 = world.read_member(Model::::ptr_from_keys(player), selector!("vec")); + +// In this case, several entities are trageted, which will return an array of the member values. +// This function call could be read in english by: "Read the member `vec` from the models `Position` for the entities `player1` and `player2`." +let vecs: Array = world.read_member_of_models(Model::::ptr_from_keys([player1, player2].span()), selector!("vec")); +``` + +## `write_member` and `write_member_of_models` + +The `write_member` and `write_member_of_models` commands are used to write a member to a model. + +```rust +let vec = Vec2{x: 1, y: 2}; +let vec_b = Vec2{x: 3, y: 4} +// Same logic as the `read_member` command. +world.write_member(Model::::ptr_from_keys(player), selector!("vec"), vec); + +// In this case, several entities are trageted, which will write the member to the models for the given entities. +// This function call could be read in english by: "Write the member `vec` to the model `Position` for the entities `player1` and `player2`." +let vecs = [vec, vec_b].span(); +world.write_member_of_models(Model::::ptr_from_keys([player1, player2].span()), selector!("vec"), vecs); +``` + ## `emit_event` The `emit_event` command is used to emit [custom events](/framework/world/events.md#custom-events). These events are indexed by [Torii](/toolchain/torii). @@ -105,6 +135,52 @@ let moves = world.read_model(player); world.erase_model(@moves); ``` +## `read_schema` and `read_schemas` + +The `read_schema` command allows for effient reading of parts of models. To define a schema the members name and type need to match the model, it needs to have `Serde` and `Introspect` derived and the model cannot be packed. If you plan to only read/write one member, `read_member` and `write_member` are more efficient. When two or more members are read, `read_schema` is more efficient. + +```rust +#[derive(Copy, Drop, Serde, Debug, Introspect)] +struct AStruct { + a: u8, + b: u8, + c: u8, + d: u8, +} + +#[dojo::model] +#[derive(Copy, Drop, Serde, Debug)] +struct Foo4 { + #[key] + id: felt252, + v0: u256, + v1: felt252, + v2: u128, + v3: AStruct, +} + +#[derive(Copy, Drop, Serde, Debug, Introspect)] +struct MySchema { + // Name and type must match the model. + v0: u256, + // Name and type must match the model. + v3: AStruct, +} + +fn something(){ + let id: felt252 = 12; + let schema: MySchema = world.read_schema(Model::::ptr_from_keys(id)); + + // If an other struct has `v0` and `v3` as members (with the same types), you can read it like this: + // (The id could be something different, it is just an example) + let schema: MySchema = world.read_schema(Model::::ptr_from_keys(id)); + + // If you want to read multiple schemas, you can use the `read_schemas` command. + let ids: Span = [12, 13].span(); + let schemas: Array = world.read_schemas(Model::::ptrs_from_keys(ids)); +} +``` + ## World Interface The world exposes an interface which can be interacted with by any client. It is worth noting here that as a developer you don't deploy this world, it is deployed when you [migrate](/toolchain/sozo) your project as it is part of the `dojo-core` library. @@ -115,41 +191,278 @@ However, you can interact with the world contract directly if you need to for in let game_id = world.uuid(); ``` -Here's the full [world's API](https://github.com/dojoengine/dojo/blob/main/crates/dojo/core/src/world/iworld.cairo): +Here's the full [world's API](https://github.com/dojoengine/dojo/blob/main/crates/dojo/core/src/world/iworld.cairo) for low level access: ```rust #[starknet::interface] -trait IWorld { - fn metadata(self: @T, resource_id: felt252) -> ResourceMetadata; +pub trait IWorld { + /// Returns the resource from its selector. + /// + /// # Arguments + /// * `selector` - the resource selector + /// + /// # Returns + /// * `Resource` - the resource data associated with the selector. + fn resource(self: @T, selector: felt252) -> Resource; + + /// Issues an autoincremented id to the caller. + /// This functionalities is useful to generate unique, but sequential ids. + /// + /// Note: This functionalities may impact performances since transaction paralellisation can't + /// be achieved since the same storage slot is being written. + fn uuid(ref self: T) -> usize; + + /// Returns the metadata of the resource. + /// + /// # Arguments + /// + /// `resource_selector` - The resource selector. + fn metadata(self: @T, resource_selector: felt252) -> ResourceMetadata; + + /// Sets the metadata of the resource. + /// + /// # Arguments + /// + /// `metadata` - The metadata content for the resource. fn set_metadata(ref self: T, metadata: ResourceMetadata); - fn model(self: @T, selector: felt252) -> (ClassHash, ContractAddress); - fn register_model(ref self: T, class_hash: ClassHash); - fn deploy_contract( - ref self: T, salt: felt252, class_hash: ClassHash, init_calldata: Span + + /// Registers a namespace in the world. + /// + /// # Arguments + /// + /// * `namespace` - The name of the namespace to be registered. + fn register_namespace(ref self: T, namespace: ByteArray); + + /// Registers an event in the world. + /// + /// # Arguments + /// + /// * `namespace` - The namespace of the event to be registered. + /// * `class_hash` - The class hash of the event to be registered. + fn register_event(ref self: T, namespace: ByteArray, class_hash: ClassHash); + + /// Registers a model in the world. + /// + /// # Arguments + /// + /// * `namespace` - The namespace of the model to be registered. + /// * `class_hash` - The class hash of the model to be registered. + fn register_model(ref self: T, namespace: ByteArray, class_hash: ClassHash); + + /// Registers and deploys a contract associated with the world and returns the address of newly + /// deployed contract. + /// + /// # Arguments + /// + /// * `salt` - The salt use for contract deployment. + /// * `namespace` - The namespace of the contract to be registered. + /// * `class_hash` - The class hash of the contract. + fn register_contract( + ref self: T, salt: felt252, namespace: ByteArray, class_hash: ClassHash, ) -> ContractAddress; - fn upgrade_contract(ref self: T, address: ContractAddress, class_hash: ClassHash) -> ClassHash; - fn uuid(ref self: T) -> usize; - fn emit(self: @T, keys: Array, values: Span); + + /// Initializes a contract associated registered in the world. + /// + /// As a constructor call, the initialization function can be called only once, and only + /// callable by the world itself. + /// + /// Also, the caller of this function must have the writer owner permission for the contract + /// resource. + fn init_contract(ref self: T, selector: felt252, init_calldata: Span); + + /// Upgrades an event in the world. + /// + /// # Arguments + /// + /// * `namespace` - The namespace of the event to be upgraded. + /// * `class_hash` - The class hash of the event to be upgraded. + fn upgrade_event(ref self: T, namespace: ByteArray, class_hash: ClassHash); + + /// Upgrades a model in the world. + /// + /// # Arguments + /// + /// * `namespace` - The namespace of the model to be upgraded. + /// * `class_hash` - The class hash of the model to be upgraded. + fn upgrade_model(ref self: T, namespace: ByteArray, class_hash: ClassHash); + + /// Upgrades an already deployed contract associated with the world and returns the new class + /// hash. + /// + /// # Arguments + /// + /// * `namespace` - The namespace of the contract to be upgraded. + /// * `class_hash` - The class hash of the contract. + fn upgrade_contract(ref self: T, namespace: ByteArray, class_hash: ClassHash) -> ClassHash; + + /// Emits a custom event that was previously registered in the world. + /// The dojo event emission is permissioned, since data are collected by + /// Torii and served to clients. + /// + /// # Arguments + /// + /// * `event_selector` - The selector of the event. + /// * `keys` - The keys of the event. + /// * `values` - The data to be logged by the event. + fn emit_event(ref self: T, event_selector: felt252, keys: Span, values: Span); + + /// Emits multiple events. + /// Permissions are only checked once, then the events are batched. + /// + /// # Arguments + /// + /// * `event_selector` - The selector of the event. + /// * `keys` - The keys of the event. + /// * `values` - The data to be logged by the event. + fn emit_events( + ref self: T, + event_selector: felt252, + keys: Span>, + values: Span>, + ); + + /// Gets the values of a model entity/member. + /// Returns a zero initialized model value if the entity/member has not been set. + /// + /// # Arguments + /// + /// * `model_selector` - The selector of the model to be retrieved. + /// * `index` - The index of the entity/member to read. + /// * `layout` - The memory layout of the model. + /// + /// # Returns + /// + /// * `Span` - The serialized value of the model, zero initialized if not set. fn entity( - self: @T, model: felt252, keys: Span, layout: dojo::database::introspect::Layout + self: @T, model_selector: felt252, index: ModelIndex, layout: Layout, ) -> Span; + + /// Gets the model values for the given entities. + /// + /// # Arguments + /// + /// * `model_selector` - The selector of the model to be retrieved. + /// * `indices` - The indexes of the entities/members to read. + /// * `layout` - The memory layout of the model. + fn entities( + self: @T, model_selector: felt252, indexes: Span, layout: Layout, + ) -> Span>; + + /// Sets the model value for the given entity/member. + /// + /// # Arguments + /// + /// * `model_selector` - The selector of the model to be set. + /// * `index` - The index of the entity/member to write. + /// * `values` - The value to be set, serialized using the model layout format. + /// * `layout` - The memory layout of the model. fn set_entity( ref self: T, - model: felt252, - keys: Span, + model_selector: felt252, + index: ModelIndex, values: Span, - layout: dojo::database::introspect::Layout + layout: Layout, ); - fn delete_entity( - ref self: T, model: felt252, keys: Span, layout: dojo::database::introspect::Layout + + /// Sets the model values for the given entities. + /// The permissions are only checked once, then the writes are batched. + /// + /// # Arguments + /// + /// * `model_selector` - The selector of the model to be set. + /// * `indexes` - The indexes of the entities/members to write. + /// * `values` - The values to be set, serialized using the model layout format. + /// * `layout` - The memory layout of the model. + fn set_entities( + ref self: T, + model_selector: felt252, + indexes: Span, + values: Span>, + layout: Layout, ); - fn base(self: @T) -> ClassHash; - fn is_owner(self: @T, address: ContractAddress, resource: felt252) -> bool; - fn grant_owner(ref self: T, address: ContractAddress, resource: felt252); - fn revoke_owner(ref self: T, address: ContractAddress, resource: felt252); - - fn is_writer(self: @T, model: felt252, contract: ContractAddress) -> bool; - fn grant_writer(ref self: T, model: felt252, contract: ContractAddress); - fn revoke_writer(ref self: T, model: felt252, contract: ContractAddress); + + /// Deletes a model value for the given entity/member. + /// Deleting is setting all the values to 0 in the given layout. + /// + /// # Arguments + /// + /// * `model_selector` - The selector of the model to be deleted. + /// * `index` - The index of the entity/member to delete. + /// * `layout` - The memory layout of the model. + fn delete_entity(ref self: T, model_selector: felt252, index: ModelIndex, layout: Layout); + + /// Deletes the model values for the given entities. + /// The permissions are only checked once, then the deletes are batched. + /// + /// # Arguments + /// + /// * `model_selector` - The selector of the model to be deleted. + /// * `indexes` - The indexes of the entities/members to delete. + /// * `layout` - The memory layout of the model. + fn delete_entities( + ref self: T, model_selector: felt252, indexes: Span, layout: Layout, + ); + + /// Returns true if the provided account has owner permission for the resource, false otherwise. + /// + /// # Arguments + /// + /// * `resource` - The selector of the resource. + /// * `address` - The address of the contract. + fn is_owner(self: @T, resource: felt252, address: ContractAddress) -> bool; + + /// Grants owner permission to the address. + /// Can only be called by an existing owner or the world admin. + /// + /// Note that this resource must have been registered to the world first. + /// + /// # Arguments + /// + /// * `resource` - The selector of the resource. + /// * `address` - The address of the contract to grant owner permission to. + fn grant_owner(ref self: T, resource: felt252, address: ContractAddress); + + /// Revokes owner permission to the contract for the resource. + /// Can only be called by an existing owner or the world admin. + /// + /// Note that this resource must have been registered to the world first. + /// + /// # Arguments + /// + /// * `resource` - The selector of the resource. + /// * `address` - The address of the contract to revoke owner permission from. + fn revoke_owner(ref self: T, resource: felt252, address: ContractAddress); + + + /// Returns true if the provided contract has writer permission for the resource, false + /// otherwise. + /// + /// # Arguments + /// + /// * `resource` - The selector of the resource. + /// * `contract` - The address of the contract. + fn is_writer(self: @T, resource: felt252, contract: ContractAddress) -> bool; + + /// Grants writer permission to the contract for the resource. + /// Can only be called by an existing resource owner or the world admin. + /// + /// Note that this resource must have been registered to the world first. + /// + /// # Arguments + /// + /// * `resource` - The selector of the resource. + /// * `contract` - The address of the contract to grant writer permission to. + fn grant_writer(ref self: T, resource: felt252, contract: ContractAddress); + + /// Revokes writer permission to the contract for the resource. + /// Can only be called by an existing resource owner or the world admin. + /// + /// Note that this resource must have been registered to the world first. + /// + /// # Arguments + /// + /// * `resource` - The selector of the resource. + /// * `contract` - The address of the contract to revoke writer permission from. + fn revoke_writer(ref self: T, resource: felt252, contract: ContractAddress); } ```