diff --git a/crates/dojo-types/src/schema.rs b/crates/dojo-types/src/schema.rs index 57b3298af7..46cce85435 100644 --- a/crates/dojo-types/src/schema.rs +++ b/crates/dojo-types/src/schema.rs @@ -20,13 +20,62 @@ impl Member { } } -/// Represents a model of an entity -#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)] -pub struct EntityModel { +#[derive(Debug, Serialize, Deserialize, PartialEq, Hash, Eq, Clone)] +pub struct EntityQuery { pub model: String, + pub clause: Clause, +} + +#[derive(Debug, Serialize, Deserialize, PartialEq, Hash, Eq, Clone)] +pub enum Clause { + Keys(KeysClause), + Attribute(AttributeClause), + Composite(CompositeClause), +} + +#[derive(Debug, Serialize, Deserialize, PartialEq, Hash, Eq, Clone)] +pub struct KeysClause { pub keys: Vec, } +#[derive(Debug, Serialize, Deserialize, PartialEq, Hash, Eq, Clone)] +pub struct AttributeClause { + pub attribute: String, + pub operator: ComparisonOperator, + pub value: Value, +} + +#[derive(Debug, Serialize, Deserialize, PartialEq, Hash, Eq, Clone)] +pub struct CompositeClause { + pub operator: LogicalOperator, + pub clauses: Vec, +} + +#[derive(Debug, Serialize, Deserialize, PartialEq, Hash, Eq, Clone)] +pub enum LogicalOperator { + And, + Or, +} + +#[derive(Debug, Serialize, Deserialize, PartialEq, Hash, Eq, Clone)] +pub enum ComparisonOperator { + Eq, + Neq, + Gt, + Gte, + Lt, + Lte, +} + +#[derive(Debug, Serialize, Deserialize, PartialEq, Hash, Eq, Clone)] +pub enum Value { + String(String), + Int(i64), + UInt(u64), + Bool(bool), + Bytes(Vec), +} + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ModelMetadata { pub schema: Ty, diff --git a/crates/torii/client/src/client/error.rs b/crates/torii/client/src/client/error.rs index ea248e7b89..9fdaa17666 100644 --- a/crates/torii/client/src/client/error.rs +++ b/crates/torii/client/src/client/error.rs @@ -18,6 +18,8 @@ pub enum Error { GrpcClient(#[from] torii_grpc::client::Error), #[error(transparent)] Model(#[from] ModelError), + #[error("Unsupported query")] + UnsupportedQuery, } #[derive(Debug, thiserror::Error)] diff --git a/crates/torii/client/src/client/mod.rs b/crates/torii/client/src/client/mod.rs index f6a4f392a4..6c58f2c559 100644 --- a/crates/torii/client/src/client/mod.rs +++ b/crates/torii/client/src/client/mod.rs @@ -7,7 +7,7 @@ use std::collections::HashSet; use std::sync::Arc; use dojo_types::packing::unpack; -use dojo_types::schema::{EntityModel, Ty}; +use dojo_types::schema::{Clause, EntityQuery, Ty}; use dojo_types::WorldMetadata; use dojo_world::contracts::WorldContractReader; use parking_lot::{RwLock, RwLockReadGuard}; @@ -46,7 +46,7 @@ impl Client { torii_url: String, rpc_url: String, world: FieldElement, - entities: Option>, + queries: Option>, ) -> Result { let mut grpc_client = torii_grpc::client::WorldClient::new(torii_url, world).await?; @@ -61,13 +61,18 @@ impl Client { let provider = JsonRpcClient::new(HttpTransport::new(rpc_url)); let world_reader = WorldContractReader::new(world, provider); - if let Some(entities_to_sync) = entities { - subbed_entities.add_entities(entities_to_sync)?; + if let Some(queries) = queries { + subbed_entities.add_entities(queries)?; // TODO: change this to querying the gRPC url instead let subbed_entities = subbed_entities.entities.read().clone(); - for EntityModel { model, keys } in subbed_entities { + for EntityQuery { model, clause } in subbed_entities { let model_reader = world_reader.model(&model).await?; + let keys = if let Clause::Keys(clause) = clause { + clause.keys + } else { + return Err(Error::UnsupportedQuery); + }; let values = model_reader.entity_storage(&keys).await?; client_storage.set_entity_storage( @@ -93,7 +98,7 @@ impl Client { self.metadata.read() } - pub fn subscribed_entities(&self) -> RwLockReadGuard<'_, HashSet> { + pub fn subscribed_entities(&self) -> RwLockReadGuard<'_, HashSet> { self.subscribed_entities.entities.read() } @@ -104,21 +109,27 @@ impl Client { /// /// If the requested entity is not among the synced entities, it will attempt to fetch it from /// the RPC. - pub async fn entity(&self, entity: &EntityModel) -> Result, Error> { + pub async fn entity(&self, entity: &EntityQuery) -> Result, Error> { let Some(mut schema) = self.metadata.read().model(&entity.model).map(|m| m.schema.clone()) else { return Ok(None); }; + let keys = if let Clause::Keys(clause) = entity.clone().clause { + clause.keys + } else { + return Err(Error::UnsupportedQuery); + }; + if !self.subscribed_entities.is_synced(entity) { let model = self.world_reader.model(&entity.model).await?; - return Ok(Some(model.entity(&entity.keys).await?)); + return Ok(Some(model.entity(&keys).await?)); } let Ok(Some(raw_values)) = self.storage.get_entity_storage( cairo_short_string_to_felt(&entity.model) .map_err(ParseError::CairoShortStringToFelt)?, - &entity.keys, + &keys, ) else { return Ok(Some(schema)); }; @@ -131,7 +142,7 @@ impl Client { .expect("qed; layout should exist"); let unpacked = unpack(raw_values, layout).unwrap(); - let mut keys_and_unpacked = [entity.keys.to_vec(), unpacked].concat(); + let mut keys_and_unpacked = [keys.to_vec(), unpacked].concat(); schema.deserialize(&mut keys_and_unpacked).unwrap(); @@ -158,9 +169,15 @@ impl Client { /// Adds entities to the list of entities to be synced. /// /// NOTE: This will establish a new subscription stream with the server. - pub async fn add_entities_to_sync(&self, entities: Vec) -> Result<(), Error> { + pub async fn add_entities_to_sync(&self, entities: Vec) -> Result<(), Error> { for entity in &entities { - self.initiate_entity(&entity.model, entity.keys.clone()).await?; + let keys = if let Clause::Keys(clause) = entity.clone().clause { + clause.keys + } else { + return Err(Error::UnsupportedQuery); + }; + + self.initiate_entity(&entity.model, keys.clone()).await?; } self.subscribed_entities.add_entities(entities)?; @@ -179,7 +196,7 @@ impl Client { /// Removes entities from the list of entities to be synced. /// /// NOTE: This will establish a new subscription stream with the server. - pub async fn remove_entities_to_sync(&self, entities: Vec) -> Result<(), Error> { + pub async fn remove_entities_to_sync(&self, entities: Vec) -> Result<(), Error> { self.subscribed_entities.remove_entities(entities)?; let updated_entities = @@ -199,7 +216,7 @@ impl Client { async fn initiate_subscription( &self, - entities: Vec, + entities: Vec, ) -> Result { let mut grpc_client = self.inner.write().await; let stream = grpc_client.subscribe_entities(entities).await?; diff --git a/crates/torii/client/src/client/storage.rs b/crates/torii/client/src/client/storage.rs index 61d0b00517..2f486825b2 100644 --- a/crates/torii/client/src/client/storage.rs +++ b/crates/torii/client/src/client/storage.rs @@ -168,7 +168,7 @@ mod tests { use std::collections::HashMap; use std::sync::Arc; - use dojo_types::schema::Ty; + use dojo_types::schema::{KeysClause, Ty}; use dojo_types::WorldMetadata; use parking_lot::RwLock; use starknet::core::utils::cairo_short_string_to_felt; @@ -201,14 +201,15 @@ mod tests { #[test] fn err_if_set_values_too_many() { let storage = create_dummy_storage(); - let entity = dojo_types::schema::EntityModel { + let keys = vec![felt!("0x12345")]; + let entity = dojo_types::schema::EntityQuery { model: "Position".into(), - keys: vec![felt!("0x12345")], + clause: dojo_types::schema::Clause::Keys(KeysClause { keys: keys.clone() }), }; let values = vec![felt!("1"), felt!("2"), felt!("3"), felt!("4"), felt!("5")]; let model = cairo_short_string_to_felt(&entity.model).unwrap(); - let result = storage.set_entity_storage(model, entity.keys, values); + let result = storage.set_entity_storage(model, keys, values); assert!(storage.storage.read().is_empty()); matches!( @@ -220,14 +221,15 @@ mod tests { #[test] fn err_if_set_values_too_few() { let storage = create_dummy_storage(); - let entity = dojo_types::schema::EntityModel { + let keys = vec![felt!("0x12345")]; + let entity = dojo_types::schema::EntityQuery { model: "Position".into(), - keys: vec![felt!("0x12345")], + clause: dojo_types::schema::Clause::Keys(KeysClause { keys: keys.clone() }), }; let values = vec![felt!("1"), felt!("2")]; let model = cairo_short_string_to_felt(&entity.model).unwrap(); - let result = storage.set_entity_storage(model, entity.keys, values); + let result = storage.set_entity_storage(model, keys, values); assert!(storage.storage.read().is_empty()); matches!( @@ -239,9 +241,10 @@ mod tests { #[test] fn set_and_get_entity_value() { let storage = create_dummy_storage(); - let entity = dojo_types::schema::EntityModel { + let keys = vec![felt!("0x12345")]; + let entity = dojo_types::schema::EntityQuery { model: "Position".into(), - keys: vec![felt!("0x12345")], + clause: dojo_types::schema::Clause::Keys(KeysClause { keys: keys.clone() }), }; assert!(storage.storage.read().is_empty(), "storage must be empty initially"); @@ -250,7 +253,7 @@ mod tests { let expected_storage_addresses = compute_all_storage_addresses( cairo_short_string_to_felt(&model.name).unwrap(), - &entity.keys, + &keys, model.packed_size, ); @@ -258,11 +261,11 @@ mod tests { let model_name_in_felt = cairo_short_string_to_felt(&entity.model).unwrap(); storage - .set_entity_storage(model_name_in_felt, entity.keys.clone(), expected_values.clone()) + .set_entity_storage(model_name_in_felt, keys.clone(), expected_values.clone()) .expect("set storage values"); let actual_values = storage - .get_entity_storage(model_name_in_felt, &entity.keys) + .get_entity_storage(model_name_in_felt, &keys) .expect("model exist") .expect("values are set"); @@ -270,11 +273,7 @@ mod tests { storage.storage.read().clone().into_keys().collect::>(); assert!( - storage - .model_index - .read() - .get(&model_name_in_felt) - .is_some_and(|e| e.contains(&entity.keys)), + storage.model_index.read().get(&model_name_in_felt).is_some_and(|e| e.contains(&keys)), "entity keys must be indexed" ); assert!(actual_values == expected_values); diff --git a/crates/torii/client/src/client/subscription.rs b/crates/torii/client/src/client/subscription.rs index f5a78d1471..8bd82346f1 100644 --- a/crates/torii/client/src/client/subscription.rs +++ b/crates/torii/client/src/client/subscription.rs @@ -4,7 +4,7 @@ use std::future::Future; use std::sync::Arc; use std::task::Poll; -use dojo_types::schema::EntityModel; +use dojo_types::schema::{Clause, EntityQuery}; use dojo_types::WorldMetadata; use futures::channel::mpsc::{self, Receiver, Sender}; use futures_util::StreamExt; @@ -24,13 +24,13 @@ pub enum SubscriptionEvent { pub struct SubscribedEntities { metadata: Arc>, - pub(super) entities: RwLock>, + pub(super) entities: RwLock>, /// All the relevant storage addresses derived from the subscribed entities pub(super) subscribed_storage_addresses: RwLock>, } impl SubscribedEntities { - pub(super) fn is_synced(&self, entity: &EntityModel) -> bool { + pub(super) fn is_synced(&self, entity: &EntityQuery) -> bool { self.entities.read().contains(entity) } @@ -42,25 +42,31 @@ impl SubscribedEntities { } } - pub(super) fn add_entities(&self, entities: Vec) -> Result<(), Error> { + pub(super) fn add_entities(&self, entities: Vec) -> Result<(), Error> { for entity in entities { Self::add_entity(self, entity)?; } Ok(()) } - pub(super) fn remove_entities(&self, entities: Vec) -> Result<(), Error> { + pub(super) fn remove_entities(&self, entities: Vec) -> Result<(), Error> { for entity in entities { Self::remove_entity(self, entity)?; } Ok(()) } - pub(super) fn add_entity(&self, entity: EntityModel) -> Result<(), Error> { + pub(super) fn add_entity(&self, entity: EntityQuery) -> Result<(), Error> { if !self.entities.write().insert(entity.clone()) { return Ok(()); } + let keys = if let Clause::Keys(clause) = entity.clause { + clause.keys + } else { + return Err(Error::UnsupportedQuery); + }; + let model_packed_size = self .metadata .read() @@ -72,7 +78,7 @@ impl SubscribedEntities { let storage_addresses = compute_all_storage_addresses( cairo_short_string_to_felt(&entity.model) .map_err(ParseError::CairoShortStringToFelt)?, - &entity.keys, + &keys, model_packed_size, ); @@ -84,11 +90,17 @@ impl SubscribedEntities { Ok(()) } - pub(super) fn remove_entity(&self, entity: EntityModel) -> Result<(), Error> { + pub(super) fn remove_entity(&self, entity: EntityQuery) -> Result<(), Error> { if !self.entities.write().remove(&entity) { return Ok(()); } + let keys = if let Clause::Keys(clause) = entity.clause { + clause.keys + } else { + return Err(Error::UnsupportedQuery); + }; + let model_packed_size = self .metadata .read() @@ -100,7 +112,7 @@ impl SubscribedEntities { let storage_addresses = compute_all_storage_addresses( cairo_short_string_to_felt(&entity.model) .map_err(ParseError::CairoShortStringToFelt)?, - &entity.keys, + &keys, model_packed_size, ); @@ -244,7 +256,7 @@ mod tests { use std::collections::HashMap; use std::sync::Arc; - use dojo_types::schema::{EntityModel, Ty}; + use dojo_types::schema::{KeysClause, Ty}; use dojo_types::WorldMetadata; use parking_lot::RwLock; use starknet::core::utils::cairo_short_string_to_felt; @@ -282,7 +294,11 @@ mod tests { .into_iter(); let metadata = self::create_dummy_metadata(); - let entity = EntityModel { model: model_name, keys }; + + let entity = dojo_types::schema::EntityQuery { + model: model_name, + clause: dojo_types::schema::Clause::Keys(KeysClause { keys }), + }; let subscribed_entities = super::SubscribedEntities::new(Arc::new(RwLock::new(metadata))); subscribed_entities.add_entities(vec![entity.clone()]).expect("able to add entity"); diff --git a/crates/torii/core/src/error.rs b/crates/torii/core/src/error.rs index 5384512428..8ccccdc601 100644 --- a/crates/torii/core/src/error.rs +++ b/crates/torii/core/src/error.rs @@ -1,4 +1,4 @@ -use starknet::core::types::FromStrError; +use starknet::core::types::{FromByteSliceError, FromStrError}; use starknet::core::utils::CairoShortStringToFeltError; #[derive(Debug, thiserror::Error)] @@ -7,6 +7,8 @@ pub enum Error { Parse(#[from] ParseError), #[error(transparent)] Sql(#[from] sqlx::Error), + #[error("unsupported query clause")] + UnsupportedQuery, } #[derive(Debug, thiserror::Error)] @@ -15,4 +17,6 @@ pub enum ParseError { FromStr(#[from] FromStrError), #[error(transparent)] CairoShortStringToFelt(#[from] CairoShortStringToFeltError), + #[error(transparent)] + FromByteSliceError(#[from] FromByteSliceError), } diff --git a/crates/torii/grpc/proto/types.proto b/crates/torii/grpc/proto/types.proto index 5d3e8e9367..eb2e6e9fad 100644 --- a/crates/torii/grpc/proto/types.proto +++ b/crates/torii/grpc/proto/types.proto @@ -29,14 +29,6 @@ message ModelMetadata { bytes schema = 6; } -/// Represents a component for a given entity. -message EntityModel { - /// Model name - string model = 1; - /// Entity keys - repeated string keys = 2; -} - message StorageEntry { // The key of the changed value string key = 1; @@ -61,3 +53,54 @@ message EntityUpdate { EntityDiff entity_diff = 2; } +message EntityQuery { + string model = 1; + Clause clause = 2; +} + +message Clause { + oneof clause_type { + KeysClause keys = 1; + AttributeClause attribute = 2; + CompositeClause composite = 3; + } +} + +message KeysClause { + repeated bytes keys = 1; +} + +message AttributeClause { + string attribute = 1; + ComparisonOperator operator = 2; + Value value = 3; +} + +message CompositeClause { + LogicalOperator operator = 1; + repeated Clause clauses = 2; +} + +enum LogicalOperator { + AND = 0; + OR = 1; +} + +enum ComparisonOperator { + EQ = 0; + NEQ = 1; + GT = 2; + GTE = 3; + LT = 4; + LTE = 5; +} + +message Value { + oneof value_type { + string string_value = 1; + int64 int_value = 2; + uint64 uint_value = 3; + bool bool_value = 4; + bytes byte_value = 5; + } +} diff --git a/crates/torii/grpc/proto/world.proto b/crates/torii/grpc/proto/world.proto index 3117a3ad56..10734e7e8e 100644 --- a/crates/torii/grpc/proto/world.proto +++ b/crates/torii/grpc/proto/world.proto @@ -25,8 +25,8 @@ message MetadataResponse { } message SubscribeEntitiesRequest { - // The list of entities to subscribe to. - repeated types.EntityModel entities = 1; + // The list of entity queries to subscribe to. + repeated types.EntityQuery queries = 1; } message SubscribeEntitiesResponse { diff --git a/crates/torii/grpc/src/client.rs b/crates/torii/grpc/src/client.rs index 89717d7274..a143a6788c 100644 --- a/crates/torii/grpc/src/client.rs +++ b/crates/torii/grpc/src/client.rs @@ -67,18 +67,12 @@ impl WorldClient { /// Subscribe to the state diff for a set of entities of a World. pub async fn subscribe_entities( &mut self, - entities: Vec, + queries: Vec, ) -> Result { let stream = self .inner .subscribe_entities(SubscribeEntitiesRequest { - entities: entities - .into_iter() - .map(|e| protos::types::EntityModel { - model: e.model, - keys: e.keys.into_iter().map(|felt| format!("{felt:#x}")).collect(), - }) - .collect(), + queries: queries.into_iter().map(|e| e.into()).collect(), }) .await .map_err(Error::Grpc) diff --git a/crates/torii/grpc/src/conversion.rs b/crates/torii/grpc/src/conversion.rs index 6d4cb3dc89..0ddbeadb78 100644 --- a/crates/torii/grpc/src/conversion.rs +++ b/crates/torii/grpc/src/conversion.rs @@ -1,9 +1,11 @@ use std::collections::HashMap; use std::str::FromStr; -use dojo_types::schema::Ty; +use dojo_types::schema::{ + AttributeClause, Clause, CompositeClause, EntityQuery, KeysClause, Ty, Value, +}; use starknet::core::types::{ - ContractStorageDiffItem, FromStrError, StateDiff, StateUpdate, StorageEntry, + ContractStorageDiffItem, FromByteSliceError, FromStrError, StateDiff, StateUpdate, StorageEntry, }; use starknet_crypto::FieldElement; @@ -44,11 +46,85 @@ impl TryFrom for dojo_types::WorldMetadata { } } -impl From for protos::types::EntityModel { - fn from(value: dojo_types::schema::EntityModel) -> Self { +impl From for protos::types::EntityQuery { + fn from(value: EntityQuery) -> Self { + Self { model: value.model, clause: Some(value.clause.into()) } + } +} + +impl From for protos::types::Clause { + fn from(value: Clause) -> Self { + match value { + Clause::Keys(clause) => { + Self { clause_type: Some(protos::types::clause::ClauseType::Keys(clause.into())) } + } + Clause::Attribute(clause) => Self { + clause_type: Some(protos::types::clause::ClauseType::Attribute(clause.into())), + }, + Clause::Composite(clause) => Self { + clause_type: Some(protos::types::clause::ClauseType::Composite(clause.into())), + }, + } + } +} + +impl From for protos::types::KeysClause { + fn from(value: KeysClause) -> Self { + Self { keys: value.keys.iter().map(|k| k.to_bytes_be().into()).collect() } + } +} + +impl TryFrom for KeysClause { + type Error = FromByteSliceError; + + fn try_from(value: protos::types::KeysClause) -> Result { + let keys = value + .keys + .into_iter() + .map(|k| FieldElement::from_byte_slice_be(&k)) + .collect::, _>>()?; + + Ok(Self { keys }) + } +} + +impl From for protos::types::AttributeClause { + fn from(value: AttributeClause) -> Self { Self { - model: value.model, - keys: value.keys.into_iter().map(|key| format!("{key:#}")).collect(), + attribute: value.attribute, + operator: value.operator as i32, + value: Some(value.value.into()), + } + } +} + +impl From for protos::types::CompositeClause { + fn from(value: CompositeClause) -> Self { + Self { + operator: value.operator as i32, + clauses: value.clauses.into_iter().map(|clause| clause.into()).collect(), + } + } +} + +impl From for protos::types::Value { + fn from(value: Value) -> Self { + match value { + Value::String(val) => { + Self { value_type: Some(protos::types::value::ValueType::StringValue(val)) } + } + Value::Int(val) => { + Self { value_type: Some(protos::types::value::ValueType::IntValue(val)) } + } + Value::UInt(val) => { + Self { value_type: Some(protos::types::value::ValueType::UintValue(val)) } + } + Value::Bool(val) => { + Self { value_type: Some(protos::types::value::ValueType::BoolValue(val)) } + } + Value::Bytes(val) => { + Self { value_type: Some(protos::types::value::ValueType::ByteValue(val)) } + } } } } diff --git a/crates/torii/grpc/src/server/mod.rs b/crates/torii/grpc/src/server/mod.rs index 0fb13a61a7..a9ca671f3a 100644 --- a/crates/torii/grpc/src/server/mod.rs +++ b/crates/torii/grpc/src/server/mod.rs @@ -3,15 +3,14 @@ pub mod logger; pub mod subscription; use std::pin::Pin; -use std::str::FromStr; use std::sync::Arc; +use dojo_types::schema::KeysClause; use futures::Stream; use protos::world::{ MetadataRequest, MetadataResponse, SubscribeEntitiesRequest, SubscribeEntitiesResponse, }; use sqlx::{Pool, Sqlite}; -use starknet::core::types::FromStrError; use starknet::core::utils::cairo_short_string_to_felt; use starknet::providers::jsonrpc::HttpTransport; use starknet::providers::JsonRpcClient; @@ -23,6 +22,7 @@ use torii_core::error::{Error, ParseError}; use torii_core::model::{parse_sql_model_members, SqlModelMember}; use self::subscription::SubscribeRequest; +use crate::protos::types::clause::ClauseType; use crate::protos::{self}; #[derive(Clone)] @@ -136,38 +136,38 @@ impl DojoWorld { async fn subscribe_entities( &self, - raw_entities: Vec, + queries: Vec, ) -> Result>, Error> { - let mut entities = Vec::with_capacity(raw_entities.len()); - - // in order to be able to compute all the storage address for all the requested entities, we - // need to know the size of the entity component. we can get this information from the - // sql database by querying the component metadata. - for entity in raw_entities { - let keys = entity - .keys - .into_iter() - .map(|v| FieldElement::from_str(&v)) - .collect::, FromStrError>>() - .map_err(ParseError::FromStr)?; - - let model = cairo_short_string_to_felt(&entity.model) + let mut subs = Vec::with_capacity(queries.len()); + for query in queries { + let clause: KeysClause = query + .clause + .ok_or(Error::UnsupportedQuery) + .and_then(|clause| clause.clause_type.ok_or(Error::UnsupportedQuery)) + .and_then(|clause_type| match clause_type { + ClauseType::Keys(clause) => Ok(clause), + _ => Err(Error::UnsupportedQuery), + })? + .try_into() + .map_err(ParseError::FromByteSliceError)?; + + let model = cairo_short_string_to_felt(&query.model) .map_err(ParseError::CairoShortStringToFelt)?; let protos::types::ModelMetadata { packed_size, .. } = - self.model_metadata(&entity.model).await?; + self.model_metadata(&query.model).await?; - entities.push(SubscribeRequest { - keys, + subs.push(SubscribeRequest { + keys: clause.keys, model: subscription::ModelMetadata { name: model, packed_size: packed_size as usize, }, - }) + }); } - let res = self.subscriber_manager.add_subscriber(entities).await; + let res = self.subscriber_manager.add_subscriber(subs).await; Ok(res) } @@ -197,9 +197,9 @@ impl protos::world::world_server::World for DojoWorld { &self, request: Request, ) -> ServiceResult { - let SubscribeEntitiesRequest { entities } = request.into_inner(); + let SubscribeEntitiesRequest { queries } = request.into_inner(); let rx = - self.subscribe_entities(entities).await.map_err(|e| Status::internal(e.to_string()))?; + self.subscribe_entities(queries).await.map_err(|e| Status::internal(e.to_string()))?; Ok(Response::new(Box::pin(ReceiverStream::new(rx)) as Self::SubscribeEntitiesStream)) } }