Path: blob/main/crates/bevy_remote/src/builtin_methods.rs
6849 views
//! Built-in verbs for the Bevy Remote Protocol.12use core::any::TypeId;34use anyhow::{anyhow, Result as AnyhowResult};5use bevy_ecs::{6component::ComponentId,7entity::Entity,8hierarchy::ChildOf,9lifecycle::RemovedComponentEntity,10message::MessageCursor,11query::QueryBuilder,12reflect::{AppTypeRegistry, ReflectComponent, ReflectResource},13system::{In, Local},14world::{EntityRef, EntityWorldMut, FilteredEntityRef, World},15};16use bevy_log::warn_once;17use bevy_platform::collections::HashMap;18use bevy_reflect::{19serde::{ReflectSerializer, TypedReflectDeserializer},20GetPath, PartialReflect, TypeRegistration, TypeRegistry,21};22use serde::{de::DeserializeSeed as _, Deserialize, Serialize};23use serde_json::{Map, Value};2425use crate::{26error_codes,27schemas::{28json_schema::{export_type, JsonSchemaBevyType},29open_rpc::OpenRpcDocument,30},31BrpError, BrpResult,32};3334#[cfg(all(feature = "http", not(target_family = "wasm")))]35use {crate::schemas::open_rpc::ServerObject, bevy_utils::default};3637/// The method path for a `world.get_components` request.38pub const BRP_GET_COMPONENTS_METHOD: &str = "world.get_components";3940/// The method path for a `world.query` request.41pub const BRP_QUERY_METHOD: &str = "world.query";4243/// The method path for a `world.spawn_entity` request.44pub const BRP_SPAWN_ENTITY_METHOD: &str = "world.spawn_entity";4546/// The method path for a `world.insert_components` request.47pub const BRP_INSERT_COMPONENTS_METHOD: &str = "world.insert_components";4849/// The method path for a `world.remove_components` request.50pub const BRP_REMOVE_COMPONENTS_METHOD: &str = "world.remove_components";5152/// The method path for a `world.despawn_entity` request.53pub const BRP_DESPAWN_COMPONENTS_METHOD: &str = "world.despawn_entity";5455/// The method path for a `world.reparent_entities` request.56pub const BRP_REPARENT_ENTITIES_METHOD: &str = "world.reparent_entities";5758/// The method path for a `world.list_components` request.59pub const BRP_LIST_COMPONENTS_METHOD: &str = "world.list_components";6061/// The method path for a `world.mutate_components` request.62pub const BRP_MUTATE_COMPONENTS_METHOD: &str = "world.mutate_components";6364/// The method path for a `world.get_components+watch` request.65pub const BRP_GET_COMPONENTS_AND_WATCH_METHOD: &str = "world.get_components+watch";6667/// The method path for a `world.list_components+watch` request.68pub const BRP_LIST_COMPONENTS_AND_WATCH_METHOD: &str = "world.list_components+watch";6970/// The method path for a `world.get_resources` request.71pub const BRP_GET_RESOURCE_METHOD: &str = "world.get_resources";7273/// The method path for a `world.insert_resources` request.74pub const BRP_INSERT_RESOURCE_METHOD: &str = "world.insert_resources";7576/// The method path for a `world.remove_resources` request.77pub const BRP_REMOVE_RESOURCE_METHOD: &str = "world.remove_resources";7879/// The method path for a `world.mutate_resources` request.80pub const BRP_MUTATE_RESOURCE_METHOD: &str = "world.mutate_resources";8182/// The method path for a `world.list_resources` request.83pub const BRP_LIST_RESOURCES_METHOD: &str = "world.list_resources";8485/// The method path for a `registry.schema` request.86pub const BRP_REGISTRY_SCHEMA_METHOD: &str = "registry.schema";8788/// The method path for a `rpc.discover` request.89pub const RPC_DISCOVER_METHOD: &str = "rpc.discover";9091/// `world.get_components`: Retrieves one or more components from the entity with the given92/// ID.93///94/// The server responds with a [`BrpGetComponentsResponse`].95#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]96pub struct BrpGetComponentsParams {97/// The ID of the entity from which components are to be requested.98pub entity: Entity,99100/// The [full paths] of the component types that are to be requested101/// from the entity.102///103/// Note that these strings must consist of the *full* type paths: e.g.104/// `bevy_transform::components::transform::Transform`, not just105/// `Transform`.106///107/// [full paths]: bevy_reflect::TypePath::type_path108pub components: Vec<String>,109110/// An optional flag to fail when encountering an invalid component rather111/// than skipping it. Defaults to false.112#[serde(default)]113pub strict: bool,114}115116/// `world.get_resources`: Retrieves the value of a given resource.117#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]118pub struct BrpGetResourcesParams {119/// The [full path] of the resource type being requested.120///121/// [full path]: bevy_reflect::TypePath::type_path122pub resource: String,123}124125/// `world.query`: Performs a query over components in the ECS, returning entities126/// and component values that match.127///128/// The server responds with a [`BrpQueryResponse`].129#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]130pub struct BrpQueryParams {131/// The components to select.132pub data: BrpQuery,133134/// An optional filter that specifies which entities to include or135/// exclude from the results.136#[serde(default)]137pub filter: BrpQueryFilter,138139/// An optional flag to fail when encountering an invalid component rather140/// than skipping it. Defaults to false.141#[serde(default)]142pub strict: bool,143}144145/// `world.spawn_entity`: Creates a new entity with the given components and responds146/// with its ID.147///148/// The server responds with a [`BrpSpawnEntityResponse`].149#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]150pub struct BrpSpawnEntityParams {151/// A map from each component's full path to its serialized value.152///153/// These components will be added to the entity.154///155/// Note that the keys of the map must be the [full type paths]: e.g.156/// `bevy_transform::components::transform::Transform`, not just157/// `Transform`.158///159/// [full type paths]: bevy_reflect::TypePath::type_path160pub components: HashMap<String, Value>,161}162163/// `world.despawn_entity`: Given an ID, despawns the entity with that ID.164///165/// The server responds with an okay.166#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]167pub struct BrpDespawnEntityParams {168/// The ID of the entity to despawn.169pub entity: Entity,170}171172/// `world.remove_components`: Deletes one or more components from an entity.173///174/// The server responds with a null.175#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]176pub struct BrpRemoveComponentsParams {177/// The ID of the entity from which components are to be removed.178pub entity: Entity,179180/// The full paths of the component types that are to be removed from181/// the entity.182///183/// Note that these strings must consist of the [full type paths]: e.g.184/// `bevy_transform::components::transform::Transform`, not just185/// `Transform`.186///187/// [full type paths]: bevy_reflect::TypePath::type_path188pub components: Vec<String>,189}190191/// `world.remove_resources`: Removes the given resource from the world.192#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]193pub struct BrpRemoveResourcesParams {194/// The [full path] of the resource type to remove.195///196/// [full path]: bevy_reflect::TypePath::type_path197pub resource: String,198}199200/// `world.insert_components`: Adds one or more components to an entity.201///202/// The server responds with a null.203#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]204pub struct BrpInsertComponentsParams {205/// The ID of the entity that components are to be added to.206pub entity: Entity,207208/// A map from each component's full path to its serialized value.209///210/// These components will be added to the entity.211///212/// Note that the keys of the map must be the [full type paths]: e.g.213/// `bevy_transform::components::transform::Transform`, not just214/// `Transform`.215///216/// [full type paths]: bevy_reflect::TypePath::type_path217pub components: HashMap<String, Value>,218}219220/// `world.insert_resources`: Inserts a resource into the world with a given221/// value.222#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]223pub struct BrpInsertResourcesParams {224/// The [full path] of the resource type to insert.225///226/// [full path]: bevy_reflect::TypePath::type_path227pub resource: String,228229/// The serialized value of the resource to be inserted.230pub value: Value,231}232233/// `world.reparent_entities`: Assign a new parent to one or more entities.234///235/// The server responds with a null.236#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]237pub struct BrpReparentEntitiesParams {238/// The IDs of the entities that are to become the new children of the239/// `parent`.240pub entities: Vec<Entity>,241242/// The IDs of the entity that will become the new parent of the243/// `entities`.244///245/// If this is `None`, then the entities are removed from all parents.246#[serde(default)]247pub parent: Option<Entity>,248}249250/// `world.list_components`: Returns a list of all type names of registered components in the251/// system (no params provided), or those on an entity (params provided).252///253/// The server responds with a [`BrpListComponentsResponse`]254#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]255pub struct BrpListComponentsParams {256/// The entity to query.257pub entity: Entity,258}259260/// `world.mutate_components`:261///262/// The server responds with a null.263#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]264pub struct BrpMutateComponentsParams {265/// The entity of the component to mutate.266pub entity: Entity,267268/// The [full path] of the component to mutate.269///270/// [full path]: bevy_reflect::TypePath::type_path271pub component: String,272273/// The [path] of the field within the component.274///275/// [path]: bevy_reflect::GetPath276pub path: String,277278/// The value to insert at `path`.279pub value: Value,280}281282/// `world.mutate_resources`:283///284/// The server responds with a null.285#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]286pub struct BrpMutateResourcesParams {287/// The [full path] of the resource to mutate.288///289/// [full path]: bevy_reflect::TypePath::type_path290pub resource: String,291292/// The [path] of the field within the resource.293///294/// [path]: bevy_reflect::GetPath295pub path: String,296297/// The value to insert at `path`.298pub value: Value,299}300301/// Describes the data that is to be fetched in a query.302#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq)]303pub struct BrpQuery {304/// The [full path] of the type name of each component that is to be305/// fetched.306///307/// [full path]: bevy_reflect::TypePath::type_path308#[serde(default)]309pub components: Vec<String>,310311/// The [full path] of the type name of each component that is to be312/// optionally fetched.313///314/// [full path]: bevy_reflect::TypePath::type_path315#[serde(default)]316pub option: ComponentSelector,317318/// The [full path] of the type name of each component that is to be checked319/// for presence.320///321/// [full path]: bevy_reflect::TypePath::type_path322#[serde(default)]323pub has: Vec<String>,324}325326/// Additional constraints that can be placed on a query to include or exclude327/// certain entities.328#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq)]329pub struct BrpQueryFilter {330/// The [full path] of the type name of each component that must not be331/// present on the entity for it to be included in the results.332///333/// [full path]: bevy_reflect::TypePath::type_path334#[serde(default)]335pub without: Vec<String>,336337/// The [full path] of the type name of each component that must be present338/// on the entity for it to be included in the results.339///340/// [full path]: bevy_reflect::TypePath::type_path341#[serde(default)]342pub with: Vec<String>,343}344345/// Constraints that can be placed on a query to include or exclude346/// certain definitions.347#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq)]348pub struct BrpJsonSchemaQueryFilter {349/// The crate name of the type name of each component that must not be350/// present on the entity for it to be included in the results.351#[serde(skip_serializing_if = "Vec::is_empty", default)]352pub without_crates: Vec<String>,353354/// The crate name of the type name of each component that must be present355/// on the entity for it to be included in the results.356#[serde(skip_serializing_if = "Vec::is_empty", default)]357pub with_crates: Vec<String>,358359/// Constrain resource by type360#[serde(default)]361pub type_limit: JsonSchemaTypeLimit,362}363364/// Additional [`BrpJsonSchemaQueryFilter`] constraints that can be placed on a query to include or exclude365/// certain definitions.366#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq)]367pub struct JsonSchemaTypeLimit {368/// Schema cannot have specified reflect types369#[serde(skip_serializing_if = "Vec::is_empty", default)]370pub without: Vec<String>,371372/// Schema needs to have specified reflect types373#[serde(skip_serializing_if = "Vec::is_empty", default)]374pub with: Vec<String>,375}376377/// A response from the world to the client that specifies a single entity.378///379/// This is sent in response to `world.spawn_entity`.380#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]381pub struct BrpSpawnEntityResponse {382/// The ID of the entity in question.383pub entity: Entity,384}385386/// The response to a `world.get_components` request.387#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]388#[serde(untagged)]389pub enum BrpGetComponentsResponse {390/// The non-strict response that reports errors separately without failing the entire request.391Lenient {392/// A map of successful components with their values.393components: HashMap<String, Value>,394/// A map of unsuccessful components with their errors.395errors: HashMap<String, Value>,396},397/// The strict response that will fail if any components are not present or aren't398/// reflect-able.399Strict(HashMap<String, Value>),400}401402/// The response to a `world.get_resources` request.403#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]404pub struct BrpGetResourcesResponse {405/// The value of the requested resource.406pub value: Value,407}408409/// A single response from a `world.get_components+watch` request.410#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]411#[serde(untagged)]412pub enum BrpGetComponentsWatchingResponse {413/// The non-strict response that reports errors separately without failing the entire request.414Lenient {415/// A map of successful components with their values that were added or changes in the last416/// tick.417components: HashMap<String, Value>,418/// An array of components that were been removed in the last tick.419removed: Vec<String>,420/// A map of unsuccessful components with their errors.421errors: HashMap<String, Value>,422},423/// The strict response that will fail if any components are not present or aren't424/// reflect-able.425Strict {426/// A map of successful components with their values that were added or changes in the last427/// tick.428components: HashMap<String, Value>,429/// An array of components that were been removed in the last tick.430removed: Vec<String>,431},432}433434/// The response to a `world.list_components` request.435pub type BrpListComponentsResponse = Vec<String>;436437/// The response to a `world.list_resources` request.438pub type BrpListResourcesResponse = Vec<String>;439440/// A single response from a `world.list_components+watch` request.441#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]442pub struct BrpListComponentsWatchingResponse {443added: Vec<String>,444removed: Vec<String>,445}446447/// The response to a `world.query` request.448pub type BrpQueryResponse = Vec<BrpQueryRow>;449450/// One query match result: a single entity paired with the requested components.451#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]452pub struct BrpQueryRow {453/// The ID of the entity that matched.454pub entity: Entity,455456/// The serialized values of the requested components.457pub components: HashMap<String, Value>,458459/// The boolean-only containment query results.460#[serde(skip_serializing_if = "HashMap::is_empty", default)]461pub has: HashMap<String, Value>,462}463464/// A helper function used to parse a `serde_json::Value`.465fn parse<T: for<'de> Deserialize<'de>>(value: Value) -> Result<T, BrpError> {466serde_json::from_value(value).map_err(|err| BrpError {467code: error_codes::INVALID_PARAMS,468message: err.to_string(),469data: None,470})471}472473/// A helper function used to parse a `serde_json::Value` wrapped in an `Option`.474fn parse_some<T: for<'de> Deserialize<'de>>(value: Option<Value>) -> Result<T, BrpError> {475match value {476Some(value) => parse(value),477None => Err(BrpError {478code: error_codes::INVALID_PARAMS,479message: String::from("Params not provided"),480data: None,481}),482}483}484485/// Handles a `world.get_components` request coming from a client.486pub fn process_remote_get_components_request(487In(params): In<Option<Value>>,488world: &World,489) -> BrpResult {490let BrpGetComponentsParams {491entity,492components,493strict,494} = parse_some(params)?;495496let app_type_registry = world.resource::<AppTypeRegistry>();497let type_registry = app_type_registry.read();498let entity_ref = get_entity(world, entity)?;499500let response =501reflect_components_to_response(components, strict, entity, entity_ref, &type_registry)?;502serde_json::to_value(response).map_err(BrpError::internal)503}504505/// Handles a `world.get_resources` request coming from a client.506pub fn process_remote_get_resources_request(507In(params): In<Option<Value>>,508world: &World,509) -> BrpResult {510let BrpGetResourcesParams {511resource: resource_path,512} = parse_some(params)?;513514let app_type_registry = world.resource::<AppTypeRegistry>();515let type_registry = app_type_registry.read();516let reflect_resource =517get_reflect_resource(&type_registry, &resource_path).map_err(BrpError::resource_error)?;518519let Ok(reflected) = reflect_resource.reflect(world) else {520return Err(BrpError::resource_not_present(&resource_path));521};522523// Use the `ReflectSerializer` to serialize the value of the resource;524// this produces a map with a single item.525let reflect_serializer = ReflectSerializer::new(reflected.as_partial_reflect(), &type_registry);526let Value::Object(serialized_object) =527serde_json::to_value(&reflect_serializer).map_err(BrpError::resource_error)?528else {529return Err(BrpError {530code: error_codes::RESOURCE_ERROR,531message: format!("Resource `{resource_path}` could not be serialized"),532data: None,533});534};535536// Get the single value out of the map.537let value = serialized_object.into_values().next().ok_or_else(|| {538BrpError::internal(anyhow!("Unexpected format of serialized resource value"))539})?;540let response = BrpGetResourcesResponse { value };541serde_json::to_value(response).map_err(BrpError::internal)542}543544/// Handles a `world.get_components+watch` request coming from a client.545pub fn process_remote_get_components_watching_request(546In(params): In<Option<Value>>,547world: &World,548mut removal_cursors: Local<HashMap<ComponentId, MessageCursor<RemovedComponentEntity>>>,549) -> BrpResult<Option<Value>> {550let BrpGetComponentsParams {551entity,552components,553strict,554} = parse_some(params)?;555556let app_type_registry = world.resource::<AppTypeRegistry>();557let type_registry = app_type_registry.read();558let entity_ref = get_entity(world, entity)?;559560let mut changed = Vec::new();561let mut removed = Vec::new();562let mut errors = <HashMap<_, _>>::default();563564'component_loop: for component_path in components {565let Ok(type_registration) =566get_component_type_registration(&type_registry, &component_path)567else {568let err =569BrpError::component_error(format!("Unknown component type: `{component_path}`"));570if strict {571return Err(err);572}573errors.insert(574component_path,575serde_json::to_value(err).map_err(BrpError::internal)?,576);577continue;578};579let Some(component_id) = world.components().get_valid_id(type_registration.type_id())580else {581let err = BrpError::component_error(format!("Unknown component: `{component_path}`"));582if strict {583return Err(err);584}585errors.insert(586component_path,587serde_json::to_value(err).map_err(BrpError::internal)?,588);589continue;590};591592if let Some(ticks) = entity_ref.get_change_ticks_by_id(component_id)593&& ticks.is_changed(world.last_change_tick(), world.read_change_tick())594{595changed.push(component_path);596continue;597};598599let Some(events) = world.removed_components().get(component_id) else {600continue;601};602let cursor = removal_cursors603.entry(component_id)604.or_insert_with(|| events.get_cursor());605for event in cursor.read(events) {606if Entity::from(event.clone()) == entity {607removed.push(component_path);608continue 'component_loop;609}610}611}612613if changed.is_empty() && removed.is_empty() {614return Ok(None);615}616617let response =618reflect_components_to_response(changed, strict, entity, entity_ref, &type_registry)?;619620let response = match response {621BrpGetComponentsResponse::Lenient {622components,623errors: mut errs,624} => BrpGetComponentsWatchingResponse::Lenient {625components,626removed,627errors: {628errs.extend(errors);629errs630},631},632BrpGetComponentsResponse::Strict(components) => BrpGetComponentsWatchingResponse::Strict {633components,634removed,635},636};637638Ok(Some(639serde_json::to_value(response).map_err(BrpError::internal)?,640))641}642643/// Reflect a list of components on an entity into a [`BrpGetComponentsResponse`].644fn reflect_components_to_response(645components: Vec<String>,646strict: bool,647entity: Entity,648entity_ref: EntityRef,649type_registry: &TypeRegistry,650) -> BrpResult<BrpGetComponentsResponse> {651let mut response = if strict {652BrpGetComponentsResponse::Strict(Default::default())653} else {654BrpGetComponentsResponse::Lenient {655components: Default::default(),656errors: Default::default(),657}658};659660for component_path in components {661match reflect_component(&component_path, entity, entity_ref, type_registry) {662Ok(serialized_object) => match response {663BrpGetComponentsResponse::Strict(ref mut components)664| BrpGetComponentsResponse::Lenient {665ref mut components, ..666} => {667components.extend(serialized_object.into_iter());668}669},670Err(err) => match response {671BrpGetComponentsResponse::Strict(_) => return Err(err),672BrpGetComponentsResponse::Lenient { ref mut errors, .. } => {673let err_value = serde_json::to_value(err).map_err(BrpError::internal)?;674errors.insert(component_path, err_value);675}676},677}678}679680Ok(response)681}682683/// Reflect a single component on an entity with the given component path.684fn reflect_component(685component_path: &str,686entity: Entity,687entity_ref: EntityRef,688type_registry: &TypeRegistry,689) -> BrpResult<Map<String, Value>> {690let reflect_component =691get_reflect_component(type_registry, component_path).map_err(BrpError::component_error)?;692693// Retrieve the reflected value for the given specified component on the given entity.694let Some(reflected) = reflect_component.reflect(entity_ref) else {695return Err(BrpError::component_not_present(component_path, entity));696};697698// Each component value serializes to a map with a single entry.699let reflect_serializer = ReflectSerializer::new(reflected.as_partial_reflect(), type_registry);700let Value::Object(serialized_object) =701serde_json::to_value(&reflect_serializer).map_err(BrpError::component_error)?702else {703return Err(BrpError {704code: error_codes::COMPONENT_ERROR,705message: format!("Component `{component_path}` could not be serialized"),706data: None,707});708};709710Ok(serialized_object)711}712713/// A selector for components in a query.714///715/// This can either be a list of component paths or an "all" selector that716/// indicates that all components should be selected.717/// The "all" selector is useful when you want to retrieve all components718/// present on an entity without specifying each one individually.719/// The paths in the `Paths` variant must be the [full type paths]: e.g.720/// `bevy_transform::components::transform::Transform`, not just721/// `Transform`.722///723#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]724#[serde(rename_all = "snake_case")]725pub enum ComponentSelector {726/// An "all" selector that indicates all components should be selected.727All,728/// A list of component paths to select as optional components.729#[serde(untagged)]730Paths(Vec<String>),731}732733impl Default for ComponentSelector {734fn default() -> Self {735Self::Paths(Vec::default())736}737}738739/// Handles a `world.query` request coming from a client.740pub fn process_remote_query_request(In(params): In<Option<Value>>, world: &mut World) -> BrpResult {741let BrpQueryParams {742data: BrpQuery {743components,744option,745has,746},747filter,748strict,749} = match params {750Some(params) => parse_some(Some(params))?,751None => BrpQueryParams {752data: BrpQuery {753components: Vec::new(),754option: ComponentSelector::default(),755has: Vec::new(),756},757filter: BrpQueryFilter::default(),758strict: false,759},760};761762let app_type_registry = world.resource::<AppTypeRegistry>().clone();763let type_registry = app_type_registry.read();764765// Required components: must be present766let (required, unregistered_in_required) =767get_component_ids(&type_registry, world, components.clone(), strict)768.map_err(BrpError::component_error)?;769770// Optional components: Option<&T> or all reflectable if "all"771let (optional, _) = match &option {772ComponentSelector::Paths(paths) => {773get_component_ids(&type_registry, world, paths.clone(), strict)774.map_err(BrpError::component_error)?775}776ComponentSelector::All => (Vec::new(), Vec::new()),777};778779// Has components: presence check780let (has_ids, unregistered_in_has) =781get_component_ids(&type_registry, world, has, strict).map_err(BrpError::component_error)?;782783// Filters784let (without, _) = get_component_ids(&type_registry, world, filter.without.clone(), strict)785.map_err(BrpError::component_error)?;786let (with, unregistered_in_with) =787get_component_ids(&type_registry, world, filter.with.clone(), strict)788.map_err(BrpError::component_error)?;789790// When "strict" is false:791// - Unregistered components in "option" and "without" are ignored.792// - Unregistered components in "has" are considered absent from the entity.793// - Unregistered components in "components" and "with" result in an empty794// response since they specify hard requirements.795// If strict, fail if any required or with components are unregistered796if !unregistered_in_required.is_empty() || !unregistered_in_with.is_empty() {797return serde_json::to_value(BrpQueryResponse::default()).map_err(BrpError::internal);798}799800let mut query = QueryBuilder::<FilteredEntityRef>::new(world);801for (_, component) in &required {802query.ref_id(*component);803}804for (_, option) in &optional {805query.optional(|query| {806query.ref_id(*option);807});808}809for (_, has) in &has_ids {810query.optional(|query| {811query.ref_id(*has);812});813}814for (_, without) in without {815query.without_id(without);816}817for (_, with) in with {818query.with_id(with);819}820821// Prepare has reflect info822let has_paths_and_reflect_components: Vec<(&str, &ReflectComponent)> = has_ids823.iter()824.map(|(type_id, _)| reflect_component_from_id(*type_id, &type_registry))825.collect::<AnyhowResult<Vec<(&str, &ReflectComponent)>>>()826.map_err(BrpError::component_error)?;827828let mut response = BrpQueryResponse::default();829let mut query = query.build();830831for row in query.iter(world) {832let entity_id = row.id();833let entity_ref = world.get_entity(entity_id).expect("Entity should exist");834835// Required components836let mut components_map = serialize_components(837entity_ref,838&type_registry,839required840.iter()841.map(|(type_id, component_id)| (*type_id, Some(*component_id))),842);843844// Optional components845match &option {846ComponentSelector::All => {847// Add all reflectable components present on the entity (as Option<&T>)848let all_optionals =849entity_ref850.archetype()851.iter_components()852.filter_map(|component_id| {853let info = world.components().get_info(component_id)?;854let type_id = info.type_id()?;855// Skip required components (already included)856if required.iter().any(|(_, cid)| cid == &component_id) {857return None;858}859Some((type_id, Some(component_id)))860});861components_map.extend(serialize_components(862entity_ref,863&type_registry,864all_optionals,865));866}867ComponentSelector::Paths(_) => {868// Add only the requested optional components (as Option<&T>)869let optionals = optional.iter().filter(|(_, component_id)| {870// Skip required components (already included)871!required.iter().any(|(_, cid)| cid == component_id)872});873components_map.extend(serialize_components(874entity_ref,875&type_registry,876optionals877.clone()878.map(|(type_id, component_id)| (*type_id, Some(*component_id))),879));880}881}882883// The map of boolean-valued component presences:884let has_map = build_has_map(885row,886has_paths_and_reflect_components.iter().copied(),887&unregistered_in_has,888);889890let query_row = BrpQueryRow {891entity: row.id(),892components: components_map,893has: has_map,894};895896response.push(query_row);897}898899serde_json::to_value(response).map_err(BrpError::internal)900}901902/// Serializes the specified components for an entity.903/// The iterator yields ([`TypeId`], Option<[`ComponentId`]>).904fn serialize_components(905entity_ref: EntityRef,906type_registry: &TypeRegistry,907components: impl Iterator<Item = (TypeId, Option<ComponentId>)>,908) -> HashMap<String, Value> {909let mut components_map = HashMap::new();910for (type_id, component_id_opt) in components {911let Some(type_registration) = type_registry.get(type_id) else {912continue;913};914if let Some(reflect_component) = type_registration.data::<ReflectComponent>() {915// If a component_id is provided, check if the entity has it916if let Some(component_id) = component_id_opt917&& !entity_ref.contains_id(component_id)918{919continue;920}921if let Some(reflected) = reflect_component.reflect(entity_ref) {922let reflect_serializer =923ReflectSerializer::new(reflected.as_partial_reflect(), type_registry);924if let Ok(Value::Object(obj)) = serde_json::to_value(&reflect_serializer) {925components_map.extend(obj);926} else {927warn_once!(928"Failed to serialize component `{}` for entity {:?}",929type_registration.type_info().type_path(),930entity_ref.id()931);932}933}934}935}936components_map937}938939/// Handles a `world.spawn_entity` request coming from a client.940pub fn process_remote_spawn_entity_request(941In(params): In<Option<Value>>,942world: &mut World,943) -> BrpResult {944let BrpSpawnEntityParams { components } = parse_some(params)?;945946let app_type_registry = world.resource::<AppTypeRegistry>().clone();947let type_registry = app_type_registry.read();948949let reflect_components =950deserialize_components(&type_registry, components).map_err(BrpError::component_error)?;951952let entity = world.spawn_empty();953let entity_id = entity.id();954insert_reflected_components(entity, reflect_components).map_err(BrpError::component_error)?;955956let response = BrpSpawnEntityResponse { entity: entity_id };957serde_json::to_value(response).map_err(BrpError::internal)958}959960/// Handles a `rpc.discover` request coming from a client.961pub fn process_remote_list_methods_request(962In(_params): In<Option<Value>>,963world: &mut World,964) -> BrpResult {965let remote_methods = world.resource::<crate::RemoteMethods>();966967#[cfg(all(feature = "http", not(target_family = "wasm")))]968let servers = match (969world.get_resource::<crate::http::HostAddress>(),970world.get_resource::<crate::http::HostPort>(),971) {972(Some(url), Some(port)) => Some(vec![ServerObject {973name: "Server".to_owned(),974url: format!("{}:{}", url.0, port.0),975..default()976}]),977(Some(url), None) => Some(vec![ServerObject {978name: "Server".to_owned(),979url: url.0.to_string(),980..default()981}]),982_ => None,983};984985#[cfg(any(not(feature = "http"), target_family = "wasm"))]986let servers = None;987988let doc = OpenRpcDocument {989info: Default::default(),990methods: remote_methods.into(),991openrpc: "1.3.2".to_owned(),992servers,993};994995serde_json::to_value(doc).map_err(BrpError::internal)996}997998/// Handles a `world.insert_components` request (insert components) coming from a client.999pub fn process_remote_insert_components_request(1000In(params): In<Option<Value>>,1001world: &mut World,1002) -> BrpResult {1003let BrpInsertComponentsParams { entity, components } = parse_some(params)?;10041005let app_type_registry = world.resource::<AppTypeRegistry>().clone();1006let type_registry = app_type_registry.read();10071008let reflect_components =1009deserialize_components(&type_registry, components).map_err(BrpError::component_error)?;10101011insert_reflected_components(get_entity_mut(world, entity)?, reflect_components)1012.map_err(BrpError::component_error)?;10131014Ok(Value::Null)1015}10161017/// Handles a `world.insert_resources` request coming from a client.1018pub fn process_remote_insert_resources_request(1019In(params): In<Option<Value>>,1020world: &mut World,1021) -> BrpResult {1022let BrpInsertResourcesParams {1023resource: resource_path,1024value,1025} = parse_some(params)?;10261027let app_type_registry = world.resource::<AppTypeRegistry>().clone();1028let type_registry = app_type_registry.read();10291030let reflected_resource = deserialize_resource(&type_registry, &resource_path, value)1031.map_err(BrpError::resource_error)?;10321033let reflect_resource =1034get_reflect_resource(&type_registry, &resource_path).map_err(BrpError::resource_error)?;1035reflect_resource.insert(world, &*reflected_resource, &type_registry);10361037Ok(Value::Null)1038}10391040/// Handles a `world.mutate_components` request coming from a client.1041///1042/// This method allows you to mutate a single field inside an Entity's1043/// component.1044pub fn process_remote_mutate_components_request(1045In(params): In<Option<Value>>,1046world: &mut World,1047) -> BrpResult {1048let BrpMutateComponentsParams {1049entity,1050component,1051path,1052value,1053} = parse_some(params)?;1054let app_type_registry = world.resource::<AppTypeRegistry>().clone();1055let type_registry = app_type_registry.read();10561057// Get the fully-qualified type names of the component to be mutated.1058let component_type: &TypeRegistration = type_registry1059.get_with_type_path(&component)1060.ok_or_else(|| {1061BrpError::component_error(anyhow!("Unknown component type: `{}`", component))1062})?;10631064// Get the reflected representation of the component.1065let mut reflected = component_type1066.data::<ReflectComponent>()1067.ok_or_else(|| {1068BrpError::component_error(anyhow!("Component `{}` isn't registered", component))1069})?1070.reflect_mut(world.entity_mut(entity))1071.ok_or_else(|| {1072BrpError::component_error(anyhow!("Cannot reflect component `{}`", component))1073})?;10741075// Get the type of the field in the component that is to be1076// mutated.1077let value_type: &TypeRegistration = type_registry1078.get_with_type_path(1079reflected1080.reflect_path(path.as_str())1081.map_err(BrpError::component_error)?1082.reflect_type_path(),1083)1084.ok_or_else(|| {1085BrpError::component_error(anyhow!("Unknown component field type: `{}`", component))1086})?;10871088// Get the reflected representation of the value to be inserted1089// into the component.1090let value: Box<dyn PartialReflect> = TypedReflectDeserializer::new(value_type, &type_registry)1091.deserialize(&value)1092.map_err(BrpError::component_error)?;10931094// Apply the mutation.1095reflected1096.reflect_path_mut(path.as_str())1097.map_err(BrpError::component_error)?1098.try_apply(value.as_ref())1099.map_err(BrpError::component_error)?;11001101Ok(Value::Null)1102}11031104/// Handles a `world.mutate_resources` request coming from a client.1105pub fn process_remote_mutate_resources_request(1106In(params): In<Option<Value>>,1107world: &mut World,1108) -> BrpResult {1109let BrpMutateResourcesParams {1110resource: resource_path,1111path: field_path,1112value,1113} = parse_some(params)?;11141115let app_type_registry = world.resource::<AppTypeRegistry>().clone();1116let type_registry = app_type_registry.read();11171118// Get the `ReflectResource` for the given resource path.1119let reflect_resource =1120get_reflect_resource(&type_registry, &resource_path).map_err(BrpError::resource_error)?;11211122// Get the actual resource value from the world as a `dyn Reflect`.1123let mut reflected_resource = reflect_resource1124.reflect_mut(world)1125.map_err(|_| BrpError::resource_not_present(&resource_path))?;11261127// Get the type registration for the field with the given path.1128let value_registration = type_registry1129.get_with_type_path(1130reflected_resource1131.reflect_path(field_path.as_str())1132.map_err(BrpError::resource_error)?1133.reflect_type_path(),1134)1135.ok_or_else(|| {1136BrpError::resource_error(anyhow!("Unknown resource field type: `{}`", resource_path))1137})?;11381139// Use the field's type registration to deserialize the given value.1140let deserialized_value: Box<dyn PartialReflect> =1141TypedReflectDeserializer::new(value_registration, &type_registry)1142.deserialize(&value)1143.map_err(BrpError::resource_error)?;11441145// Apply the value to the resource.1146reflected_resource1147.reflect_path_mut(field_path.as_str())1148.map_err(BrpError::resource_error)?1149.try_apply(&*deserialized_value)1150.map_err(BrpError::resource_error)?;11511152Ok(Value::Null)1153}11541155/// Handles a `world.remove_components` request (remove components) coming from a client.1156pub fn process_remote_remove_components_request(1157In(params): In<Option<Value>>,1158world: &mut World,1159) -> BrpResult {1160let BrpRemoveComponentsParams { entity, components } = parse_some(params)?;11611162let app_type_registry = world.resource::<AppTypeRegistry>().clone();1163let type_registry = app_type_registry.read();11641165let component_ids = get_component_ids(&type_registry, world, components, true)1166.and_then(|(registered, unregistered)| {1167if unregistered.is_empty() {1168Ok(registered)1169} else {1170Err(anyhow!("Unregistered component types: {:?}", unregistered))1171}1172})1173.map_err(BrpError::component_error)?;11741175// Remove the components.1176let mut entity_world_mut = get_entity_mut(world, entity)?;1177for (_, component_id) in component_ids.iter() {1178entity_world_mut.remove_by_id(*component_id);1179}11801181Ok(Value::Null)1182}11831184/// Handles a `world.remove_resources` request coming from a client.1185pub fn process_remote_remove_resources_request(1186In(params): In<Option<Value>>,1187world: &mut World,1188) -> BrpResult {1189let BrpRemoveResourcesParams {1190resource: resource_path,1191} = parse_some(params)?;11921193let app_type_registry = world.resource::<AppTypeRegistry>().clone();1194let type_registry = app_type_registry.read();11951196let reflect_resource =1197get_reflect_resource(&type_registry, &resource_path).map_err(BrpError::resource_error)?;1198reflect_resource.remove(world);11991200Ok(Value::Null)1201}12021203/// Handles a `world.despawn_entity` (despawn entity) request coming from a client.1204pub fn process_remote_despawn_entity_request(1205In(params): In<Option<Value>>,1206world: &mut World,1207) -> BrpResult {1208let BrpDespawnEntityParams { entity } = parse_some(params)?;12091210get_entity_mut(world, entity)?.despawn();12111212Ok(Value::Null)1213}12141215/// Handles a `world.reparent_entities` request coming from a client.1216pub fn process_remote_reparent_entities_request(1217In(params): In<Option<Value>>,1218world: &mut World,1219) -> BrpResult {1220let BrpReparentEntitiesParams {1221entities,1222parent: maybe_parent,1223} = parse_some(params)?;12241225// If `Some`, reparent the entities.1226if let Some(parent) = maybe_parent {1227let mut parent_commands =1228get_entity_mut(world, parent).map_err(|_| BrpError::entity_not_found(parent))?;1229for entity in entities {1230if entity == parent {1231return Err(BrpError::self_reparent(entity));1232}1233parent_commands.add_child(entity);1234}1235}1236// If `None`, remove the entities' parents.1237else {1238for entity in entities {1239get_entity_mut(world, entity)?.remove::<ChildOf>();1240}1241}12421243Ok(Value::Null)1244}12451246/// Handles a `world.list_components` request (list all components) coming from a client.1247pub fn process_remote_list_components_request(1248In(params): In<Option<Value>>,1249world: &World,1250) -> BrpResult {1251let app_type_registry = world.resource::<AppTypeRegistry>();1252let type_registry = app_type_registry.read();12531254let mut response = BrpListComponentsResponse::default();12551256// If `Some`, return all components of the provided entity.1257if let Some(BrpListComponentsParams { entity }) = params.map(parse).transpose()? {1258let entity = get_entity(world, entity)?;1259for component_id in entity.archetype().iter_components() {1260let Some(component_info) = world.components().get_info(component_id) else {1261continue;1262};1263response.push(component_info.name().to_string());1264}1265}1266// If `None`, list all registered components.1267else {1268for registered_type in type_registry.iter() {1269if registered_type.data::<ReflectComponent>().is_some() {1270response.push(registered_type.type_info().type_path().to_owned());1271}1272}1273}12741275// Sort both for cleanliness and to reduce the risk that clients start1276// accidentally depending on the order.1277response.sort();12781279serde_json::to_value(response).map_err(BrpError::internal)1280}12811282/// Handles a `world.list_resources` request coming from a client.1283pub fn process_remote_list_resources_request(1284In(_params): In<Option<Value>>,1285world: &World,1286) -> BrpResult {1287let mut response = BrpListResourcesResponse::default();12881289let app_type_registry = world.resource::<AppTypeRegistry>();1290let type_registry = app_type_registry.read();12911292for registered_type in type_registry.iter() {1293if registered_type.data::<ReflectResource>().is_some() {1294response.push(registered_type.type_info().type_path().to_owned());1295}1296}12971298response.sort();12991300serde_json::to_value(response).map_err(BrpError::internal)1301}13021303/// Handles a `world.list_components+watch` request coming from a client.1304pub fn process_remote_list_components_watching_request(1305In(params): In<Option<Value>>,1306world: &World,1307mut removal_cursors: Local<HashMap<ComponentId, MessageCursor<RemovedComponentEntity>>>,1308) -> BrpResult<Option<Value>> {1309let BrpListComponentsParams { entity } = parse_some(params)?;1310let entity_ref = get_entity(world, entity)?;1311let mut response = BrpListComponentsWatchingResponse::default();13121313for component_id in entity_ref.archetype().iter_components() {1314let ticks = entity_ref1315.get_change_ticks_by_id(component_id)1316.ok_or(BrpError::internal("Failed to get ticks"))?;13171318if ticks.is_added(world.last_change_tick(), world.read_change_tick()) {1319let Some(component_info) = world.components().get_info(component_id) else {1320continue;1321};1322response.added.push(component_info.name().to_string());1323}1324}13251326for (component_id, events) in world.removed_components().iter() {1327let cursor = removal_cursors1328.entry(*component_id)1329.or_insert_with(|| events.get_cursor());1330for event in cursor.read(events) {1331if Entity::from(event.clone()) == entity {1332let Some(component_info) = world.components().get_info(*component_id) else {1333continue;1334};1335response.removed.push(component_info.name().to_string());1336}1337}1338}13391340if response.added.is_empty() && response.removed.is_empty() {1341Ok(None)1342} else {1343Ok(Some(1344serde_json::to_value(response).map_err(BrpError::internal)?,1345))1346}1347}13481349/// Handles a `registry.schema` request (list all registry types in form of schema) coming from a client.1350pub fn export_registry_types(In(params): In<Option<Value>>, world: &World) -> BrpResult {1351let filter: BrpJsonSchemaQueryFilter = match params {1352None => Default::default(),1353Some(params) => parse(params)?,1354};13551356let extra_info = world.resource::<crate::schemas::SchemaTypesMetadata>();1357let types = world.resource::<AppTypeRegistry>();1358let types = types.read();1359let schemas = types1360.iter()1361.filter_map(|type_reg| {1362let path_table = type_reg.type_info().type_path_table();1363if let Some(crate_name) = &path_table.crate_name() {1364if !filter.with_crates.is_empty()1365&& !filter.with_crates.iter().any(|c| crate_name.eq(c))1366{1367return None;1368}1369if !filter.without_crates.is_empty()1370&& filter.without_crates.iter().any(|c| crate_name.eq(c))1371{1372return None;1373}1374}1375let (id, schema) = export_type(type_reg, extra_info);13761377if !filter.type_limit.with.is_empty()1378&& !filter1379.type_limit1380.with1381.iter()1382.any(|c| schema.reflect_types.iter().any(|cc| c.eq(cc)))1383{1384return None;1385}1386if !filter.type_limit.without.is_empty()1387&& filter1388.type_limit1389.without1390.iter()1391.any(|c| schema.reflect_types.iter().any(|cc| c.eq(cc)))1392{1393return None;1394}1395Some((id.to_string(), schema))1396})1397.collect::<HashMap<String, JsonSchemaBevyType>>();13981399serde_json::to_value(schemas).map_err(BrpError::internal)1400}14011402/// Immutably retrieves an entity from the [`World`], returning an error if the1403/// entity isn't present.1404fn get_entity(world: &World, entity: Entity) -> Result<EntityRef<'_>, BrpError> {1405world1406.get_entity(entity)1407.map_err(|_| BrpError::entity_not_found(entity))1408}14091410/// Mutably retrieves an entity from the [`World`], returning an error if the1411/// entity isn't present.1412fn get_entity_mut(world: &mut World, entity: Entity) -> Result<EntityWorldMut<'_>, BrpError> {1413world1414.get_entity_mut(entity)1415.map_err(|_| BrpError::entity_not_found(entity))1416}14171418/// Given components full path, returns a tuple that contains1419/// - A list of corresponding [`TypeId`] and [`ComponentId`] for registered components.1420/// - A list of unregistered component paths.1421///1422/// Note that the supplied path names must be *full* path names: e.g.1423/// `bevy_transform::components::transform::Transform` instead of `Transform`.1424fn get_component_ids(1425type_registry: &TypeRegistry,1426world: &World,1427component_paths: Vec<String>,1428strict: bool,1429) -> AnyhowResult<(Vec<(TypeId, ComponentId)>, Vec<String>)> {1430let mut component_ids = vec![];1431let mut unregistered_components = vec![];14321433for component_path in component_paths {1434let maybe_component_tuple = get_component_type_registration(type_registry, &component_path)1435.ok()1436.and_then(|type_registration| {1437let type_id = type_registration.type_id();1438world1439.components()1440.get_valid_id(type_id)1441.map(|component_id| (type_id, component_id))1442});1443if let Some((type_id, component_id)) = maybe_component_tuple {1444component_ids.push((type_id, component_id));1445} else if strict {1446return Err(anyhow!(1447"Component `{}` isn't registered or used in the world",1448component_path1449));1450} else {1451unregistered_components.push(component_path);1452}1453}14541455Ok((component_ids, unregistered_components))1456}14571458/// Given an entity (`entity_ref`),1459/// a list of reflected component information (`paths_and_reflect_components`)1460/// and a list of unregistered components,1461/// return a map which associates each component to a boolean value indicating1462/// whether or not that component is present on the entity.1463/// Unregistered components are considered absent from the entity.1464fn build_has_map<'a>(1465entity_ref: FilteredEntityRef,1466paths_and_reflect_components: impl Iterator<Item = (&'a str, &'a ReflectComponent)>,1467unregistered_components: &[String],1468) -> HashMap<String, Value> {1469let mut has_map = <HashMap<_, _>>::default();14701471for (type_path, reflect_component) in paths_and_reflect_components {1472let has = reflect_component.contains(entity_ref);1473has_map.insert(type_path.to_owned(), Value::Bool(has));1474}1475unregistered_components.iter().for_each(|component| {1476has_map.insert(component.to_owned(), Value::Bool(false));1477});14781479has_map1480}14811482/// Given a component ID, return the associated [type path] and `ReflectComponent` if possible.1483///1484/// The `ReflectComponent` part is the meat of this; the type path is only used for error messages.1485///1486/// [type path]: bevy_reflect::TypePath::type_path1487fn reflect_component_from_id(1488component_type_id: TypeId,1489type_registry: &TypeRegistry,1490) -> AnyhowResult<(&str, &ReflectComponent)> {1491let Some(type_registration) = type_registry.get(component_type_id) else {1492return Err(anyhow!(1493"Component `{:?}` isn't registered",1494component_type_id1495));1496};14971498let type_path = type_registration.type_info().type_path();14991500let Some(reflect_component) = type_registration.data::<ReflectComponent>() else {1501return Err(anyhow!("Component `{}` isn't reflectable", type_path));1502};15031504Ok((type_path, reflect_component))1505}15061507/// Given a collection of component paths and their associated serialized values (`components`),1508/// return the associated collection of deserialized reflected values.1509fn deserialize_components(1510type_registry: &TypeRegistry,1511components: HashMap<String, Value>,1512) -> AnyhowResult<Vec<Box<dyn PartialReflect>>> {1513let mut reflect_components = vec![];15141515for (component_path, component) in components {1516let Some(component_type) = type_registry.get_with_type_path(&component_path) else {1517return Err(anyhow!("Unknown component type: `{}`", component_path));1518};1519let reflected: Box<dyn PartialReflect> =1520TypedReflectDeserializer::new(component_type, type_registry)1521.deserialize(&component)1522.map_err(|err| anyhow!("{component_path} is invalid: {err}"))?;1523reflect_components.push(reflected);1524}15251526Ok(reflect_components)1527}15281529/// Given a resource path and an associated serialized value (`value`), return the1530/// deserialized value.1531fn deserialize_resource(1532type_registry: &TypeRegistry,1533resource_path: &str,1534value: Value,1535) -> AnyhowResult<Box<dyn PartialReflect>> {1536let Some(resource_type) = type_registry.get_with_type_path(resource_path) else {1537return Err(anyhow!("Unknown resource type: `{}`", resource_path));1538};1539let reflected: Box<dyn PartialReflect> =1540TypedReflectDeserializer::new(resource_type, type_registry)1541.deserialize(&value)1542.map_err(|err| anyhow!("{resource_path} is invalid: {err}"))?;1543Ok(reflected)1544}15451546/// Given a collection `reflect_components` of reflected component values, insert them into1547/// the given entity (`entity_world_mut`).1548fn insert_reflected_components(1549mut entity_world_mut: EntityWorldMut,1550reflect_components: Vec<Box<dyn PartialReflect>>,1551) -> AnyhowResult<()> {1552for reflected in reflect_components {1553entity_world_mut.insert_reflect(reflected);1554}15551556Ok(())1557}15581559/// Given a component's type path, return the associated [`ReflectComponent`] from the given1560/// `type_registry` if possible.1561fn get_reflect_component<'r>(1562type_registry: &'r TypeRegistry,1563component_path: &str,1564) -> AnyhowResult<&'r ReflectComponent> {1565let component_registration = get_component_type_registration(type_registry, component_path)?;15661567component_registration1568.data::<ReflectComponent>()1569.ok_or_else(|| anyhow!("Component `{}` isn't reflectable", component_path))1570}15711572/// Given a component's type path, return the associated [`TypeRegistration`] from the given1573/// `type_registry` if possible.1574fn get_component_type_registration<'r>(1575type_registry: &'r TypeRegistry,1576component_path: &str,1577) -> AnyhowResult<&'r TypeRegistration> {1578type_registry1579.get_with_type_path(component_path)1580.ok_or_else(|| anyhow!("Unknown component type: `{}`", component_path))1581}15821583/// Given a resource's type path, return the associated [`ReflectResource`] from the given1584/// `type_registry` if possible.1585fn get_reflect_resource<'r>(1586type_registry: &'r TypeRegistry,1587resource_path: &str,1588) -> AnyhowResult<&'r ReflectResource> {1589let resource_registration = get_resource_type_registration(type_registry, resource_path)?;15901591resource_registration1592.data::<ReflectResource>()1593.ok_or_else(|| anyhow!("Resource `{}` isn't reflectable", resource_path))1594}15951596/// Given a resource's type path, return the associated [`TypeRegistration`] from the given1597/// `type_registry` if possible.1598fn get_resource_type_registration<'r>(1599type_registry: &'r TypeRegistry,1600resource_path: &str,1601) -> AnyhowResult<&'r TypeRegistration> {1602type_registry1603.get_with_type_path(resource_path)1604.ok_or_else(|| anyhow!("Unknown resource type: `{}`", resource_path))1605}16061607#[cfg(test)]1608mod tests {1609/// A generic function that tests serialization and deserialization of any type1610/// implementing Serialize and Deserialize traits.1611fn test_serialize_deserialize<T>(value: T)1612where1613T: Serialize + for<'a> Deserialize<'a> + PartialEq + core::fmt::Debug,1614{1615// Serialize the value to JSON string1616let serialized = serde_json::to_string(&value).expect("Failed to serialize");16171618// Deserialize the JSON string back into the original type1619let deserialized: T = serde_json::from_str(&serialized).expect("Failed to deserialize");16201621// Assert that the deserialized value is the same as the original1622assert_eq!(1623&value, &deserialized,1624"Deserialized value does not match original"1625);1626}16271628use super::*;16291630#[test]1631fn insert_reflect_only_component() {1632use bevy_ecs::prelude::Component;1633use bevy_reflect::Reflect;1634#[derive(Reflect, Component)]1635#[reflect(Component)]1636struct Player {1637name: String,1638health: u32,1639}1640let components: HashMap<String, Value> = [(1641String::from("bevy_remote::builtin_methods::tests::Player"),1642serde_json::json!({"name": "John", "health": 50}),1643)]1644.into();1645let atr = AppTypeRegistry::default();1646{1647let mut register = atr.write();1648register.register::<Player>();1649}1650let deserialized_components = {1651let type_reg = atr.read();1652deserialize_components(&type_reg, components).expect("FAIL")1653};1654let mut world = World::new();1655world.insert_resource(atr);1656let e = world.spawn_empty();1657insert_reflected_components(e, deserialized_components).expect("FAIL");1658}16591660#[test]1661fn serialization_tests() {1662test_serialize_deserialize(BrpQueryRow {1663components: Default::default(),1664entity: Entity::from_raw_u32(0).unwrap(),1665has: Default::default(),1666});1667test_serialize_deserialize(BrpListComponentsWatchingResponse::default());1668test_serialize_deserialize(BrpQuery::default());1669test_serialize_deserialize(BrpJsonSchemaQueryFilter::default());1670test_serialize_deserialize(BrpJsonSchemaQueryFilter {1671type_limit: JsonSchemaTypeLimit {1672with: vec!["Resource".to_owned()],1673..Default::default()1674},1675..Default::default()1676});1677test_serialize_deserialize(BrpListComponentsParams {1678entity: Entity::from_raw_u32(0).unwrap(),1679});1680}1681}168216831684