Path: blob/main/crates/bevy_remote/src/builtin_methods.rs
9296 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, ReflectEvent, ReflectResource},13system::{In, Local},14world::{EntityRef, EntityWorldMut, FilteredEntityRef, Mut, World},15};16use bevy_log::warn_once;17use bevy_platform::collections::HashMap;18use bevy_reflect::{19serde::{ReflectSerializer, TypedReflectDeserializer},20structs::DynamicStruct,21GetPath, PartialReflect, TypeRegistration, TypeRegistry,22};23use serde::{de::DeserializeSeed as _, de::IntoDeserializer, Deserialize, Serialize};24use serde_json::{Map, Value};2526use crate::{27error_codes,28schemas::{29json_schema::{export_type, JsonSchemaBevyType},30open_rpc::OpenRpcDocument,31},32BrpError, BrpResult,33};3435#[cfg(all(feature = "http", not(target_family = "wasm")))]36use {crate::schemas::open_rpc::ServerObject, bevy_utils::default};3738/// The method path for a `world.get_components` request.39pub const BRP_GET_COMPONENTS_METHOD: &str = "world.get_components";4041/// The method path for a `world.query` request.42pub const BRP_QUERY_METHOD: &str = "world.query";4344/// The method path for a `world.spawn_entity` request.45pub const BRP_SPAWN_ENTITY_METHOD: &str = "world.spawn_entity";4647/// The method path for a `world.insert_components` request.48pub const BRP_INSERT_COMPONENTS_METHOD: &str = "world.insert_components";4950/// The method path for a `world.remove_components` request.51pub const BRP_REMOVE_COMPONENTS_METHOD: &str = "world.remove_components";5253/// The method path for a `world.despawn_entity` request.54pub const BRP_DESPAWN_COMPONENTS_METHOD: &str = "world.despawn_entity";5556/// The method path for a `world.reparent_entities` request.57pub const BRP_REPARENT_ENTITIES_METHOD: &str = "world.reparent_entities";5859/// The method path for a `world.list_components` request.60pub const BRP_LIST_COMPONENTS_METHOD: &str = "world.list_components";6162/// The method path for a `world.mutate_components` request.63pub const BRP_MUTATE_COMPONENTS_METHOD: &str = "world.mutate_components";6465/// The method path for a `world.get_components+watch` request.66pub const BRP_GET_COMPONENTS_AND_WATCH_METHOD: &str = "world.get_components+watch";6768/// The method path for a `world.list_components+watch` request.69pub const BRP_LIST_COMPONENTS_AND_WATCH_METHOD: &str = "world.list_components+watch";7071/// The method path for a `world.get_resources` request.72pub const BRP_GET_RESOURCE_METHOD: &str = "world.get_resources";7374/// The method path for a `world.insert_resources` request.75pub const BRP_INSERT_RESOURCE_METHOD: &str = "world.insert_resources";7677/// The method path for a `world.remove_resources` request.78pub const BRP_REMOVE_RESOURCE_METHOD: &str = "world.remove_resources";7980/// The method path for a `world.mutate_resources` request.81pub const BRP_MUTATE_RESOURCE_METHOD: &str = "world.mutate_resources";8283/// The method path for a `world.list_resources` request.84pub const BRP_LIST_RESOURCES_METHOD: &str = "world.list_resources";8586/// The method path for a `world.trigger_event` request.87pub const BRP_TRIGGER_EVENT_METHOD: &str = "world.trigger_event";8889/// The method path for a `registry.schema` request.90pub const BRP_REGISTRY_SCHEMA_METHOD: &str = "registry.schema";9192/// The method path for a `rpc.discover` request.93pub const RPC_DISCOVER_METHOD: &str = "rpc.discover";9495/// `world.get_components`: Retrieves one or more components from the entity with the given96/// ID.97///98/// The server responds with a [`BrpGetComponentsResponse`].99#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]100pub struct BrpGetComponentsParams {101/// The ID of the entity from which components are to be requested.102pub entity: Entity,103104/// The [full paths] of the component types that are to be requested105/// from the entity.106///107/// Note that these strings must consist of the *full* type paths: e.g.108/// `bevy_transform::components::transform::Transform`, not just109/// `Transform`.110///111/// [full paths]: bevy_reflect::TypePath::type_path112pub components: Vec<String>,113114/// An optional flag to fail when encountering an invalid component rather115/// than skipping it. Defaults to false.116#[serde(default)]117pub strict: bool,118}119120/// `world.get_resources`: Retrieves the value of a given resource.121#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]122pub struct BrpGetResourcesParams {123/// The [full path] of the resource type being requested.124///125/// [full path]: bevy_reflect::TypePath::type_path126pub resource: String,127}128129/// `world.query`: Performs a query over components in the ECS, returning entities130/// and component values that match.131///132/// The server responds with a [`BrpQueryResponse`].133#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]134pub struct BrpQueryParams {135/// The components to select.136pub data: BrpQuery,137138/// An optional filter that specifies which entities to include or139/// exclude from the results.140#[serde(default)]141pub filter: BrpQueryFilter,142143/// An optional flag to fail when encountering an invalid component rather144/// than skipping it. Defaults to false.145#[serde(default)]146pub strict: bool,147}148149/// `world.spawn_entity`: Creates a new entity with the given components and responds150/// with its ID.151///152/// The server responds with a [`BrpSpawnEntityResponse`].153#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]154pub struct BrpSpawnEntityParams {155/// A map from each component's full path to its serialized value.156///157/// These components will be added to the entity.158///159/// Note that the keys of the map must be the [full type paths]: e.g.160/// `bevy_transform::components::transform::Transform`, not just161/// `Transform`.162///163/// [full type paths]: bevy_reflect::TypePath::type_path164pub components: HashMap<String, Value>,165}166167/// `world.despawn_entity`: Given an ID, despawns the entity with that ID.168///169/// The server responds with an okay.170#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]171pub struct BrpDespawnEntityParams {172/// The ID of the entity to despawn.173pub entity: Entity,174}175176/// `world.remove_components`: Deletes one or more components from an entity.177///178/// The server responds with a null.179#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]180pub struct BrpRemoveComponentsParams {181/// The ID of the entity from which components are to be removed.182pub entity: Entity,183184/// The full paths of the component types that are to be removed from185/// the entity.186///187/// Note that these strings must consist of the [full type paths]: e.g.188/// `bevy_transform::components::transform::Transform`, not just189/// `Transform`.190///191/// [full type paths]: bevy_reflect::TypePath::type_path192pub components: Vec<String>,193}194195/// `world.remove_resources`: Removes the given resource from the world.196#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]197pub struct BrpRemoveResourcesParams {198/// The [full path] of the resource type to remove.199///200/// [full path]: bevy_reflect::TypePath::type_path201pub resource: String,202}203204/// `world.insert_components`: Adds one or more components to an entity.205///206/// The server responds with a null.207#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]208pub struct BrpInsertComponentsParams {209/// The ID of the entity that components are to be added to.210pub entity: Entity,211212/// A map from each component's full path to its serialized value.213///214/// These components will be added to the entity.215///216/// Note that the keys of the map must be the [full type paths]: e.g.217/// `bevy_transform::components::transform::Transform`, not just218/// `Transform`.219///220/// [full type paths]: bevy_reflect::TypePath::type_path221pub components: HashMap<String, Value>,222}223224/// `world.insert_resources`: Inserts a resource into the world with a given225/// value.226#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]227pub struct BrpInsertResourcesParams {228/// The [full path] of the resource type to insert.229///230/// [full path]: bevy_reflect::TypePath::type_path231pub resource: String,232233/// The serialized value of the resource to be inserted.234pub value: Value,235}236237/// `world.reparent_entities`: Assign a new parent to one or more entities.238///239/// The server responds with a null.240#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]241pub struct BrpReparentEntitiesParams {242/// The IDs of the entities that are to become the new children of the243/// `parent`.244pub entities: Vec<Entity>,245246/// The IDs of the entity that will become the new parent of the247/// `entities`.248///249/// If this is `None`, then the entities are removed from all parents.250#[serde(default)]251pub parent: Option<Entity>,252}253254/// `world.list_components`: Returns a list of all type names of registered components in the255/// system (no params provided), or those on an entity (params provided).256///257/// The server responds with a [`BrpListComponentsResponse`]258#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]259pub struct BrpListComponentsParams {260/// The entity to query.261pub entity: Entity,262}263264/// `world.mutate_components`:265///266/// The server responds with a null.267#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]268pub struct BrpMutateComponentsParams {269/// The entity of the component to mutate.270pub entity: Entity,271272/// The [full path] of the component to mutate.273///274/// [full path]: bevy_reflect::TypePath::type_path275pub component: String,276277/// The [path] of the field within the component.278///279/// [path]: bevy_reflect::GetPath280pub path: String,281282/// The value to insert at `path`.283pub value: Value,284}285286/// `world.mutate_resources`:287///288/// The server responds with a null.289#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]290pub struct BrpMutateResourcesParams {291/// The [full path] of the resource to mutate.292///293/// [full path]: bevy_reflect::TypePath::type_path294pub resource: String,295296/// The [path] of the field within the resource.297///298/// [path]: bevy_reflect::GetPath299pub path: String,300301/// The value to insert at `path`.302pub value: Value,303}304305/// `world.trigger_event`:306///307/// The server responds with a null.308#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]309struct BrpTriggerEventParams {310/// The [full path] of the event to trigger.311///312/// [full path]: bevy_reflect::TypePath::type_path313pub event: String,314/// The serialized value of the event to be triggered, if any.315pub value: Option<Value>,316}317318/// Describes the data that is to be fetched in a query.319#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq)]320pub struct BrpQuery {321/// The [full path] of the type name of each component that is to be322/// fetched.323///324/// [full path]: bevy_reflect::TypePath::type_path325#[serde(default)]326pub components: Vec<String>,327328/// The [full path] of the type name of each component that is to be329/// optionally fetched.330///331/// [full path]: bevy_reflect::TypePath::type_path332#[serde(default)]333pub option: ComponentSelector,334335/// The [full path] of the type name of each component that is to be checked336/// for presence.337///338/// [full path]: bevy_reflect::TypePath::type_path339#[serde(default)]340pub has: Vec<String>,341}342343/// Additional constraints that can be placed on a query to include or exclude344/// certain entities.345#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq)]346pub struct BrpQueryFilter {347/// The [full path] of the type name of each component that must not be348/// present on the entity for it to be included in the results.349///350/// [full path]: bevy_reflect::TypePath::type_path351#[serde(default)]352pub without: Vec<String>,353354/// The [full path] of the type name of each component that must be present355/// on the entity for it to be included in the results.356///357/// [full path]: bevy_reflect::TypePath::type_path358#[serde(default)]359pub with: Vec<String>,360}361362/// Constraints that can be placed on a query to include or exclude363/// certain definitions.364#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq)]365pub struct BrpJsonSchemaQueryFilter {366/// The crate name of the type name of each component that must not be367/// present on the entity for it to be included in the results.368#[serde(skip_serializing_if = "Vec::is_empty", default)]369pub without_crates: Vec<String>,370371/// The crate name of the type name of each component that must be present372/// on the entity for it to be included in the results.373#[serde(skip_serializing_if = "Vec::is_empty", default)]374pub with_crates: Vec<String>,375376/// Constrain resource by type377#[serde(default)]378pub type_limit: JsonSchemaTypeLimit,379}380381/// Additional [`BrpJsonSchemaQueryFilter`] constraints that can be placed on a query to include or exclude382/// certain definitions.383#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq)]384pub struct JsonSchemaTypeLimit {385/// Schema cannot have specified reflect types386#[serde(skip_serializing_if = "Vec::is_empty", default)]387pub without: Vec<String>,388389/// Schema needs to have specified reflect types390#[serde(skip_serializing_if = "Vec::is_empty", default)]391pub with: Vec<String>,392}393394/// A response from the world to the client that specifies a single entity.395///396/// This is sent in response to `world.spawn_entity`.397#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]398pub struct BrpSpawnEntityResponse {399/// The ID of the entity in question.400pub entity: Entity,401}402403/// The response to a `world.get_components` request.404#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]405#[serde(untagged)]406pub enum BrpGetComponentsResponse {407/// The non-strict response that reports errors separately without failing the entire request.408Lenient {409/// A map of successful components with their values.410components: HashMap<String, Value>,411/// A map of unsuccessful components with their errors.412errors: HashMap<String, Value>,413},414/// The strict response that will fail if any components are not present or aren't415/// reflect-able.416Strict(HashMap<String, Value>),417}418419/// The response to a `world.get_resources` request.420#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]421pub struct BrpGetResourcesResponse {422/// The value of the requested resource.423pub value: Value,424}425426/// A single response from a `world.get_components+watch` request.427#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]428#[serde(untagged)]429pub enum BrpGetComponentsWatchingResponse {430/// The non-strict response that reports errors separately without failing the entire request.431Lenient {432/// A map of successful components with their values that were added or changes in the last433/// tick.434components: HashMap<String, Value>,435/// An array of components that were been removed in the last tick.436removed: Vec<String>,437/// A map of unsuccessful components with their errors.438errors: HashMap<String, Value>,439},440/// The strict response that will fail if any components are not present or aren't441/// reflect-able.442Strict {443/// A map of successful components with their values that were added or changes in the last444/// tick.445components: HashMap<String, Value>,446/// An array of components that were been removed in the last tick.447removed: Vec<String>,448},449}450451/// The response to a `world.list_components` request.452pub type BrpListComponentsResponse = Vec<String>;453454/// The response to a `world.list_resources` request.455pub type BrpListResourcesResponse = Vec<String>;456457/// A single response from a `world.list_components+watch` request.458#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]459pub struct BrpListComponentsWatchingResponse {460added: Vec<String>,461removed: Vec<String>,462}463464/// The response to a `world.query` request.465pub type BrpQueryResponse = Vec<BrpQueryRow>;466467/// One query match result: a single entity paired with the requested components.468#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]469pub struct BrpQueryRow {470/// The ID of the entity that matched.471pub entity: Entity,472473/// The serialized values of the requested components.474pub components: HashMap<String, Value>,475476/// The boolean-only containment query results.477#[serde(skip_serializing_if = "HashMap::is_empty", default)]478pub has: HashMap<String, Value>,479}480481/// A helper function used to parse a `serde_json::Value`.482pub fn parse<T: for<'de> Deserialize<'de>>(value: Value) -> Result<T, BrpError> {483serde_json::from_value(value).map_err(|err| BrpError {484code: error_codes::INVALID_PARAMS,485message: err.to_string(),486data: None,487})488}489490/// A helper function used to parse a `serde_json::Value` wrapped in an `Option`.491pub fn parse_some<T: for<'de> Deserialize<'de>>(value: Option<Value>) -> Result<T, BrpError> {492match value {493Some(value) => parse(value),494None => Err(BrpError {495code: error_codes::INVALID_PARAMS,496message: String::from("Params not provided"),497data: None,498}),499}500}501502/// Handles a `world.get_components` request coming from a client.503pub fn process_remote_get_components_request(504In(params): In<Option<Value>>,505world: &World,506) -> BrpResult {507let BrpGetComponentsParams {508entity,509components,510strict,511} = parse_some(params)?;512513let app_type_registry = world.resource::<AppTypeRegistry>();514let type_registry = app_type_registry.read();515let entity_ref = get_entity(world, entity)?;516517let response =518reflect_components_to_response(components, strict, entity, entity_ref, &type_registry)?;519serde_json::to_value(response).map_err(BrpError::internal)520}521522/// Handles a `world.get_resources` request coming from a client.523pub fn process_remote_get_resources_request(524In(params): In<Option<Value>>,525world: &World,526) -> BrpResult {527let BrpGetResourcesParams {528resource: resource_path,529} = parse_some(params)?;530531let app_type_registry = world.resource::<AppTypeRegistry>();532let type_registry = app_type_registry.read();533let reflect_resource =534get_reflect_resource(&type_registry, &resource_path).map_err(BrpError::resource_error)?;535536let Ok(reflected) = reflect_resource.reflect(world) else {537return Err(BrpError::resource_not_present(&resource_path));538};539540// Use the `ReflectSerializer` to serialize the value of the resource;541// this produces a map with a single item.542let reflect_serializer = ReflectSerializer::new(reflected.as_partial_reflect(), &type_registry);543let Value::Object(serialized_object) =544serde_json::to_value(&reflect_serializer).map_err(BrpError::resource_error)?545else {546return Err(BrpError {547code: error_codes::RESOURCE_ERROR,548message: format!("Resource `{resource_path}` could not be serialized"),549data: None,550});551};552553// Get the single value out of the map.554let value = serialized_object.into_values().next().ok_or_else(|| {555BrpError::internal(anyhow!("Unexpected format of serialized resource value"))556})?;557let response = BrpGetResourcesResponse { value };558serde_json::to_value(response).map_err(BrpError::internal)559}560561/// Handles a `world.get_components+watch` request coming from a client.562pub fn process_remote_get_components_watching_request(563In(params): In<Option<Value>>,564world: &World,565mut removal_cursors: Local<HashMap<ComponentId, MessageCursor<RemovedComponentEntity>>>,566) -> BrpResult<Option<Value>> {567let BrpGetComponentsParams {568entity,569components,570strict,571} = parse_some(params)?;572573let app_type_registry = world.resource::<AppTypeRegistry>();574let type_registry = app_type_registry.read();575let entity_ref = get_entity(world, entity)?;576577let mut changed = Vec::new();578let mut removed = Vec::new();579let mut errors = <HashMap<_, _>>::default();580581'component_loop: for component_path in components {582let Ok(type_registration) =583get_component_type_registration(&type_registry, &component_path)584else {585let err =586BrpError::component_error(format!("Unknown component type: `{component_path}`"));587if strict {588return Err(err);589}590errors.insert(591component_path,592serde_json::to_value(err).map_err(BrpError::internal)?,593);594continue;595};596let Some(component_id) = world.components().get_valid_id(type_registration.type_id())597else {598let err = BrpError::component_error(format!("Unknown component: `{component_path}`"));599if strict {600return Err(err);601}602errors.insert(603component_path,604serde_json::to_value(err).map_err(BrpError::internal)?,605);606continue;607};608609if let Some(ticks) = entity_ref.get_change_ticks_by_id(component_id)610&& ticks.is_changed(world.last_change_tick(), world.read_change_tick())611{612changed.push(component_path);613continue;614};615616let Some(events) = world.removed_components().get(component_id) else {617continue;618};619let cursor = removal_cursors620.entry(component_id)621.or_insert_with(|| events.get_cursor());622for event in cursor.read(events) {623if Entity::from(event.clone()) == entity {624removed.push(component_path);625continue 'component_loop;626}627}628}629630if changed.is_empty() && removed.is_empty() {631return Ok(None);632}633634let response =635reflect_components_to_response(changed, strict, entity, entity_ref, &type_registry)?;636637let response = match response {638BrpGetComponentsResponse::Lenient {639components,640errors: mut errs,641} => BrpGetComponentsWatchingResponse::Lenient {642components,643removed,644errors: {645errs.extend(errors);646errs647},648},649BrpGetComponentsResponse::Strict(components) => BrpGetComponentsWatchingResponse::Strict {650components,651removed,652},653};654655Ok(Some(656serde_json::to_value(response).map_err(BrpError::internal)?,657))658}659660/// Reflect a list of components on an entity into a [`BrpGetComponentsResponse`].661fn reflect_components_to_response(662components: Vec<String>,663strict: bool,664entity: Entity,665entity_ref: EntityRef,666type_registry: &TypeRegistry,667) -> BrpResult<BrpGetComponentsResponse> {668let mut response = if strict {669BrpGetComponentsResponse::Strict(Default::default())670} else {671BrpGetComponentsResponse::Lenient {672components: Default::default(),673errors: Default::default(),674}675};676677for component_path in components {678match reflect_component(&component_path, entity, entity_ref, type_registry) {679Ok(serialized_object) => match response {680BrpGetComponentsResponse::Strict(ref mut components)681| BrpGetComponentsResponse::Lenient {682ref mut components, ..683} => {684components.extend(serialized_object.into_iter());685}686},687Err(err) => match response {688BrpGetComponentsResponse::Strict(_) => return Err(err),689BrpGetComponentsResponse::Lenient { ref mut errors, .. } => {690let err_value = serde_json::to_value(err).map_err(BrpError::internal)?;691errors.insert(component_path, err_value);692}693},694}695}696697Ok(response)698}699700/// Reflect a single component on an entity with the given component path.701fn reflect_component(702component_path: &str,703entity: Entity,704entity_ref: EntityRef,705type_registry: &TypeRegistry,706) -> BrpResult<Map<String, Value>> {707let reflect_component =708get_reflect_component(type_registry, component_path).map_err(BrpError::component_error)?;709710// Retrieve the reflected value for the given specified component on the given entity.711let Some(reflected) = reflect_component.reflect(entity_ref) else {712return Err(BrpError::component_not_present(component_path, entity));713};714715// Each component value serializes to a map with a single entry.716let reflect_serializer = ReflectSerializer::new(reflected.as_partial_reflect(), type_registry);717let Value::Object(serialized_object) =718serde_json::to_value(&reflect_serializer).map_err(BrpError::component_error)?719else {720return Err(BrpError {721code: error_codes::COMPONENT_ERROR,722message: format!("Component `{component_path}` could not be serialized"),723data: None,724});725};726727Ok(serialized_object)728}729730/// A selector for components in a query.731///732/// This can either be a list of component paths or an "all" selector that733/// indicates that all components should be selected.734/// The "all" selector is useful when you want to retrieve all components735/// present on an entity without specifying each one individually.736/// The paths in the `Paths` variant must be the [full type paths]: e.g.737/// `bevy_transform::components::transform::Transform`, not just738/// `Transform`.739///740#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]741#[serde(rename_all = "snake_case")]742pub enum ComponentSelector {743/// An "all" selector that indicates all components should be selected.744All,745/// A list of component paths to select as optional components.746#[serde(untagged)]747Paths(Vec<String>),748}749750impl Default for ComponentSelector {751fn default() -> Self {752Self::Paths(Vec::default())753}754}755756/// Handles a `world.query` request coming from a client.757pub fn process_remote_query_request(In(params): In<Option<Value>>, world: &mut World) -> BrpResult {758let BrpQueryParams {759data: BrpQuery {760components,761option,762has,763},764filter,765strict,766} = match params {767Some(params) => parse_some(Some(params))?,768None => BrpQueryParams {769data: BrpQuery {770components: Vec::new(),771option: ComponentSelector::default(),772has: Vec::new(),773},774filter: BrpQueryFilter::default(),775strict: false,776},777};778779let app_type_registry = world.resource::<AppTypeRegistry>().clone();780let type_registry = app_type_registry.read();781782// Required components: must be present783let (required, unregistered_in_required) =784get_component_ids(&type_registry, world, components.clone(), strict)785.map_err(BrpError::component_error)?;786787// Optional components: Option<&T> or all reflectable if "all"788let (optional, _) = match &option {789ComponentSelector::Paths(paths) => {790get_component_ids(&type_registry, world, paths.clone(), strict)791.map_err(BrpError::component_error)?792}793ComponentSelector::All => (Vec::new(), Vec::new()),794};795796// Has components: presence check797let (has_ids, unregistered_in_has) =798get_component_ids(&type_registry, world, has, strict).map_err(BrpError::component_error)?;799800// Filters801let (without, _) = get_component_ids(&type_registry, world, filter.without.clone(), strict)802.map_err(BrpError::component_error)?;803let (with, unregistered_in_with) =804get_component_ids(&type_registry, world, filter.with.clone(), strict)805.map_err(BrpError::component_error)?;806807// When "strict" is false:808// - Unregistered components in "option" and "without" are ignored.809// - Unregistered components in "has" are considered absent from the entity.810// - Unregistered components in "components" and "with" result in an empty811// response since they specify hard requirements.812// If strict, fail if any required or with components are unregistered813if !unregistered_in_required.is_empty() || !unregistered_in_with.is_empty() {814return serde_json::to_value(BrpQueryResponse::default()).map_err(BrpError::internal);815}816817let mut query = QueryBuilder::<FilteredEntityRef>::new(world);818for (_, component) in &required {819query.ref_id(*component);820}821for (_, option) in &optional {822query.optional(|query| {823query.ref_id(*option);824});825}826for (_, has) in &has_ids {827query.optional(|query| {828query.ref_id(*has);829});830}831for (_, without) in without {832query.without_id(without);833}834for (_, with) in with {835query.with_id(with);836}837838// Prepare has reflect info839let has_paths_and_reflect_components: Vec<(&str, &ReflectComponent)> = has_ids840.iter()841.map(|(type_id, _)| reflect_component_from_id(*type_id, &type_registry))842.collect::<AnyhowResult<Vec<(&str, &ReflectComponent)>>>()843.map_err(BrpError::component_error)?;844845let mut response = BrpQueryResponse::default();846let mut query = query.build();847848for row in query.iter(world) {849let entity_id = row.id();850let entity_ref = world.get_entity(entity_id).expect("Entity should exist");851852// Required components853let mut components_map = serialize_components(854entity_ref,855&type_registry,856required857.iter()858.map(|(type_id, component_id)| (*type_id, Some(*component_id))),859);860861// Optional components862match &option {863ComponentSelector::All => {864// Add all reflectable components present on the entity (as Option<&T>)865let all_optionals =866entity_ref867.archetype()868.components()869.iter()870.filter_map(|&component_id| {871let info = world.components().get_info(component_id)?;872let type_id = info.type_id()?;873// Skip required components (already included)874if required.iter().any(|(_, cid)| cid == &component_id) {875return None;876}877Some((type_id, Some(component_id)))878});879components_map.extend(serialize_components(880entity_ref,881&type_registry,882all_optionals,883));884}885ComponentSelector::Paths(_) => {886// Add only the requested optional components (as Option<&T>)887let optionals = optional.iter().filter(|(_, component_id)| {888// Skip required components (already included)889!required.iter().any(|(_, cid)| cid == component_id)890});891components_map.extend(serialize_components(892entity_ref,893&type_registry,894optionals895.clone()896.map(|(type_id, component_id)| (*type_id, Some(*component_id))),897));898}899}900901// The map of boolean-valued component presences:902let has_map = build_has_map(903row,904has_paths_and_reflect_components.iter().copied(),905&unregistered_in_has,906);907908let query_row = BrpQueryRow {909entity: row.id(),910components: components_map,911has: has_map,912};913914response.push(query_row);915}916917serde_json::to_value(response).map_err(BrpError::internal)918}919920/// Serializes the specified components for an entity.921/// The iterator yields ([`TypeId`], Option<[`ComponentId`]>).922fn serialize_components(923entity_ref: EntityRef,924type_registry: &TypeRegistry,925components: impl Iterator<Item = (TypeId, Option<ComponentId>)>,926) -> HashMap<String, Value> {927let mut components_map = HashMap::new();928for (type_id, component_id_opt) in components {929let Some(type_registration) = type_registry.get(type_id) else {930continue;931};932if let Some(reflect_component) = type_registration.data::<ReflectComponent>() {933// If a component_id is provided, check if the entity has it934if let Some(component_id) = component_id_opt935&& !entity_ref.contains_id(component_id)936{937continue;938}939if let Some(reflected) = reflect_component.reflect(entity_ref) {940let reflect_serializer =941ReflectSerializer::new(reflected.as_partial_reflect(), type_registry);942if let Ok(Value::Object(obj)) = serde_json::to_value(&reflect_serializer) {943components_map.extend(obj);944} else {945warn_once!(946"Failed to serialize component `{}` for entity {:?}",947type_registration.type_info().type_path(),948entity_ref.id()949);950}951}952}953}954components_map955}956957/// Handles a `world.spawn_entity` request coming from a client.958pub fn process_remote_spawn_entity_request(959In(params): In<Option<Value>>,960world: &mut World,961) -> BrpResult {962let BrpSpawnEntityParams { components } = parse_some(params)?;963964let app_type_registry = world.resource::<AppTypeRegistry>().clone();965let type_registry = app_type_registry.read();966967let reflect_components =968deserialize_components(&type_registry, components).map_err(BrpError::component_error)?;969970let entity = world.spawn_empty();971let entity_id = entity.id();972insert_reflected_components(entity, reflect_components).map_err(BrpError::component_error)?;973974let response = BrpSpawnEntityResponse { entity: entity_id };975serde_json::to_value(response).map_err(BrpError::internal)976}977978/// Handles a `rpc.discover` request coming from a client.979pub fn process_remote_list_methods_request(980In(_params): In<Option<Value>>,981world: &mut World,982) -> BrpResult {983let remote_methods = world.resource::<crate::RemoteMethods>();984985#[cfg(all(feature = "http", not(target_family = "wasm")))]986let servers = match (987world.get_resource::<crate::http::HostAddress>(),988world.get_resource::<crate::http::HostPort>(),989) {990(Some(url), Some(port)) => Some(vec![ServerObject {991name: "Server".to_owned(),992url: format!("{}:{}", url.0, port.0),993..default()994}]),995(Some(url), None) => Some(vec![ServerObject {996name: "Server".to_owned(),997url: url.0.to_string(),998..default()999}]),1000_ => None,1001};10021003#[cfg(any(not(feature = "http"), target_family = "wasm"))]1004let servers = None;10051006let doc = OpenRpcDocument {1007info: Default::default(),1008methods: remote_methods.into(),1009openrpc: "1.3.2".to_owned(),1010servers,1011};10121013serde_json::to_value(doc).map_err(BrpError::internal)1014}10151016/// Handles a `world.insert_components` request (insert components) coming from a client.1017pub fn process_remote_insert_components_request(1018In(params): In<Option<Value>>,1019world: &mut World,1020) -> BrpResult {1021let BrpInsertComponentsParams { entity, components } = parse_some(params)?;10221023let app_type_registry = world.resource::<AppTypeRegistry>().clone();1024let type_registry = app_type_registry.read();10251026let reflect_components =1027deserialize_components(&type_registry, components).map_err(BrpError::component_error)?;10281029insert_reflected_components(get_entity_mut(world, entity)?, reflect_components)1030.map_err(BrpError::component_error)?;10311032Ok(Value::Null)1033}10341035/// Handles a `world.insert_resources` request coming from a client.1036pub fn process_remote_insert_resources_request(1037In(params): In<Option<Value>>,1038world: &mut World,1039) -> BrpResult {1040let BrpInsertResourcesParams {1041resource: resource_path,1042value,1043} = parse_some(params)?;10441045let app_type_registry = world.resource::<AppTypeRegistry>().clone();1046let type_registry = app_type_registry.read();10471048let reflected_resource = deserialize_resource(&type_registry, &resource_path, value)1049.map_err(BrpError::resource_error)?;10501051let reflect_resource =1052get_reflect_resource(&type_registry, &resource_path).map_err(BrpError::resource_error)?;1053reflect_resource.insert(world, &*reflected_resource, &type_registry);10541055Ok(Value::Null)1056}10571058/// Handles a `world.mutate_components` request coming from a client.1059///1060/// This method allows you to mutate a single field inside an Entity's1061/// component.1062pub fn process_remote_mutate_components_request(1063In(params): In<Option<Value>>,1064world: &mut World,1065) -> BrpResult {1066let BrpMutateComponentsParams {1067entity,1068component,1069path,1070value,1071} = parse_some(params)?;1072let app_type_registry = world.resource::<AppTypeRegistry>().clone();1073let type_registry = app_type_registry.read();10741075// Get the fully-qualified type names of the component to be mutated.1076let component_type: &TypeRegistration = type_registry1077.get_with_type_path(&component)1078.ok_or_else(|| {1079BrpError::component_error(anyhow!("Unknown component type: `{}`", component))1080})?;10811082// Get the reflected representation of the component.1083let mut reflected = component_type1084.data::<ReflectComponent>()1085.ok_or_else(|| {1086BrpError::component_error(anyhow!("Component `{}` isn't registered", component))1087})?1088.reflect_mut(world.entity_mut(entity))1089.ok_or_else(|| {1090BrpError::component_error(anyhow!("Cannot reflect component `{}`", component))1091})?;10921093// Get the type of the field in the component that is to be1094// mutated.1095let value_type: &TypeRegistration = type_registry1096.get_with_type_path(1097reflected1098.reflect_path(path.as_str())1099.map_err(BrpError::component_error)?1100.reflect_type_path(),1101)1102.ok_or_else(|| {1103BrpError::component_error(anyhow!("Unknown component field type: `{}`", component))1104})?;11051106// Get the reflected representation of the value to be inserted1107// into the component.1108let value: Box<dyn PartialReflect> = TypedReflectDeserializer::new(value_type, &type_registry)1109.deserialize(&value)1110.map_err(BrpError::component_error)?;11111112// Apply the mutation.1113reflected1114.reflect_path_mut(path.as_str())1115.map_err(BrpError::component_error)?1116.try_apply(value.as_ref())1117.map_err(BrpError::component_error)?;11181119Ok(Value::Null)1120}11211122/// Handles a `world.mutate_resources` request coming from a client.1123pub fn process_remote_mutate_resources_request(1124In(params): In<Option<Value>>,1125world: &mut World,1126) -> BrpResult {1127let BrpMutateResourcesParams {1128resource: resource_path,1129path: field_path,1130value,1131} = parse_some(params)?;11321133let app_type_registry = world.resource::<AppTypeRegistry>().clone();1134let type_registry = app_type_registry.read();11351136// Get the `ReflectResource` for the given resource path.1137let reflect_resource =1138get_reflect_resource(&type_registry, &resource_path).map_err(BrpError::resource_error)?;11391140// Get the actual resource value from the world as a `dyn Reflect`.1141let mut reflected_resource = reflect_resource1142.reflect_mut(world)1143.map_err(|_| BrpError::resource_not_present(&resource_path))?;11441145// Get the type registration for the field with the given path.1146let value_registration = type_registry1147.get_with_type_path(1148reflected_resource1149.reflect_path(field_path.as_str())1150.map_err(BrpError::resource_error)?1151.reflect_type_path(),1152)1153.ok_or_else(|| {1154BrpError::resource_error(anyhow!("Unknown resource field type: `{}`", resource_path))1155})?;11561157// Use the field's type registration to deserialize the given value.1158let deserialized_value: Box<dyn PartialReflect> =1159TypedReflectDeserializer::new(value_registration, &type_registry)1160.deserialize(&value)1161.map_err(BrpError::resource_error)?;11621163// Apply the value to the resource.1164reflected_resource1165.reflect_path_mut(field_path.as_str())1166.map_err(BrpError::resource_error)?1167.try_apply(&*deserialized_value)1168.map_err(BrpError::resource_error)?;11691170Ok(Value::Null)1171}11721173/// Handles a `world.remove_components` request (remove components) coming from a client.1174pub fn process_remote_remove_components_request(1175In(params): In<Option<Value>>,1176world: &mut World,1177) -> BrpResult {1178let BrpRemoveComponentsParams { entity, components } = parse_some(params)?;11791180let app_type_registry = world.resource::<AppTypeRegistry>().clone();1181let type_registry = app_type_registry.read();11821183let component_ids = get_component_ids(&type_registry, world, components, true)1184.and_then(|(registered, unregistered)| {1185if unregistered.is_empty() {1186Ok(registered)1187} else {1188Err(anyhow!("Unregistered component types: {:?}", unregistered))1189}1190})1191.map_err(BrpError::component_error)?;11921193// Remove the components.1194let mut entity_world_mut = get_entity_mut(world, entity)?;1195for (_, component_id) in component_ids.iter() {1196entity_world_mut.remove_by_id(*component_id);1197}11981199Ok(Value::Null)1200}12011202/// Handles a `world.remove_resources` request coming from a client.1203pub fn process_remote_remove_resources_request(1204In(params): In<Option<Value>>,1205world: &mut World,1206) -> BrpResult {1207let BrpRemoveResourcesParams {1208resource: resource_path,1209} = parse_some(params)?;12101211let app_type_registry = world.resource::<AppTypeRegistry>().clone();1212let type_registry = app_type_registry.read();12131214let reflect_resource =1215get_reflect_resource(&type_registry, &resource_path).map_err(BrpError::resource_error)?;1216reflect_resource.remove(world);12171218Ok(Value::Null)1219}12201221/// Handles a `world.despawn_entity` (despawn entity) request coming from a client.1222pub fn process_remote_despawn_entity_request(1223In(params): In<Option<Value>>,1224world: &mut World,1225) -> BrpResult {1226let BrpDespawnEntityParams { entity } = parse_some(params)?;12271228get_entity_mut(world, entity)?.despawn();12291230Ok(Value::Null)1231}12321233/// Handles a `world.reparent_entities` request coming from a client.1234pub fn process_remote_reparent_entities_request(1235In(params): In<Option<Value>>,1236world: &mut World,1237) -> BrpResult {1238let BrpReparentEntitiesParams {1239entities,1240parent: maybe_parent,1241} = parse_some(params)?;12421243// If `Some`, reparent the entities.1244if let Some(parent) = maybe_parent {1245let mut parent_commands =1246get_entity_mut(world, parent).map_err(|_| BrpError::entity_not_found(parent))?;1247for entity in entities {1248if entity == parent {1249return Err(BrpError::self_reparent(entity));1250}1251parent_commands.add_child(entity);1252}1253}1254// If `None`, remove the entities' parents.1255else {1256for entity in entities {1257get_entity_mut(world, entity)?.remove::<ChildOf>();1258}1259}12601261Ok(Value::Null)1262}12631264/// Handles a `world.list_components` request (list all components) coming from a client.1265pub fn process_remote_list_components_request(1266In(params): In<Option<Value>>,1267world: &World,1268) -> BrpResult {1269let app_type_registry = world.resource::<AppTypeRegistry>();1270let type_registry = app_type_registry.read();12711272let mut response = BrpListComponentsResponse::default();12731274// If `Some`, return all components of the provided entity.1275if let Some(BrpListComponentsParams { entity }) = params.map(parse).transpose()? {1276let entity = get_entity(world, entity)?;1277for &component_id in entity.archetype().components().iter() {1278let Some(component_info) = world.components().get_info(component_id) else {1279continue;1280};1281response.push(component_info.name().to_string());1282}1283}1284// If `None`, list all registered components.1285else {1286for registered_type in type_registry.iter() {1287if registered_type.data::<ReflectComponent>().is_some() {1288response.push(registered_type.type_info().type_path().to_owned());1289}1290}1291}12921293// Sort both for cleanliness and to reduce the risk that clients start1294// accidentally depending on the order.1295response.sort();12961297serde_json::to_value(response).map_err(BrpError::internal)1298}12991300/// Handles a `world.list_resources` request coming from a client.1301pub fn process_remote_list_resources_request(1302In(_params): In<Option<Value>>,1303world: &World,1304) -> BrpResult {1305let mut response = BrpListResourcesResponse::default();13061307let app_type_registry = world.resource::<AppTypeRegistry>();1308let type_registry = app_type_registry.read();13091310for registered_type in type_registry.iter() {1311if registered_type.data::<ReflectResource>().is_some() {1312response.push(registered_type.type_info().type_path().to_owned());1313}1314}13151316response.sort();13171318serde_json::to_value(response).map_err(BrpError::internal)1319}13201321/// Handles a `world.list_components+watch` request coming from a client.1322pub fn process_remote_list_components_watching_request(1323In(params): In<Option<Value>>,1324world: &World,1325mut removal_cursors: Local<HashMap<ComponentId, MessageCursor<RemovedComponentEntity>>>,1326) -> BrpResult<Option<Value>> {1327let BrpListComponentsParams { entity } = parse_some(params)?;1328let entity_ref = get_entity(world, entity)?;1329let mut response = BrpListComponentsWatchingResponse::default();13301331for &component_id in entity_ref.archetype().components().iter() {1332let ticks = entity_ref1333.get_change_ticks_by_id(component_id)1334.ok_or(BrpError::internal("Failed to get ticks"))?;13351336if ticks.is_added(world.last_change_tick(), world.read_change_tick()) {1337let Some(component_info) = world.components().get_info(component_id) else {1338continue;1339};1340response.added.push(component_info.name().to_string());1341}1342}13431344for (component_id, events) in world.removed_components().iter() {1345let cursor = removal_cursors1346.entry(*component_id)1347.or_insert_with(|| events.get_cursor());1348for event in cursor.read(events) {1349if Entity::from(event.clone()) == entity {1350let Some(component_info) = world.components().get_info(*component_id) else {1351continue;1352};1353response.removed.push(component_info.name().to_string());1354}1355}1356}13571358if response.added.is_empty() && response.removed.is_empty() {1359Ok(None)1360} else {1361Ok(Some(1362serde_json::to_value(response).map_err(BrpError::internal)?,1363))1364}1365}13661367/// Handles a `world.trigger_event` request coming from a client.1368pub fn process_remote_trigger_event_request(1369In(params): In<Option<Value>>,1370world: &mut World,1371) -> BrpResult {1372let BrpTriggerEventParams { event, value } = parse_some(params)?;13731374world.resource_scope(|world, registry: Mut<AppTypeRegistry>| {1375let registry = registry.read();13761377let Some(registration) = registry.get_with_type_path(&event) else {1378return Err(BrpError::resource_error(format!(1379"Unknown event type: `{event}`"1380)));1381};1382let Some(reflect_event) = registration.data::<ReflectEvent>() else {1383return Err(BrpError::resource_error(format!(1384"Event `{event}` is not reflectable"1385)));1386};13871388if let Some(payload) = value {1389let payload: Box<dyn PartialReflect> =1390TypedReflectDeserializer::new(registration, ®istry)1391.deserialize(payload.into_deserializer())1392.map_err(|err| {1393BrpError::resource_error(format!("{event} is invalid: {err}"))1394})?;1395reflect_event.trigger(world, &*payload, ®istry);1396} else {1397let payload = DynamicStruct::default();1398reflect_event.trigger(world, &payload, ®istry);1399}14001401Ok(Value::Null)1402})1403}14041405/// Handles a `registry.schema` request (list all registry types in form of schema) coming from a client.1406pub fn export_registry_types(In(params): In<Option<Value>>, world: &World) -> BrpResult {1407let filter: BrpJsonSchemaQueryFilter = match params {1408None => Default::default(),1409Some(params) => parse(params)?,1410};14111412let extra_info = world.resource::<crate::schemas::SchemaTypesMetadata>();1413let types = world.resource::<AppTypeRegistry>();1414let types = types.read();1415let schemas = types1416.iter()1417.filter_map(|type_reg| {1418let path_table = type_reg.type_info().type_path_table();1419if let Some(crate_name) = &path_table.crate_name() {1420if !filter.with_crates.is_empty()1421&& !filter.with_crates.iter().any(|c| crate_name.eq(c))1422{1423return None;1424}1425if !filter.without_crates.is_empty()1426&& filter.without_crates.iter().any(|c| crate_name.eq(c))1427{1428return None;1429}1430}1431let (id, schema) = export_type(type_reg, extra_info);14321433if !filter.type_limit.with.is_empty()1434&& !filter1435.type_limit1436.with1437.iter()1438.any(|c| schema.reflect_types.iter().any(|cc| c.eq(cc)))1439{1440return None;1441}1442if !filter.type_limit.without.is_empty()1443&& filter1444.type_limit1445.without1446.iter()1447.any(|c| schema.reflect_types.iter().any(|cc| c.eq(cc)))1448{1449return None;1450}1451Some((id.to_string(), schema))1452})1453.collect::<HashMap<String, JsonSchemaBevyType>>();14541455serde_json::to_value(schemas).map_err(BrpError::internal)1456}14571458/// Immutably retrieves an entity from the [`World`], returning an error if the1459/// entity isn't present.1460fn get_entity(world: &World, entity: Entity) -> Result<EntityRef<'_>, BrpError> {1461world1462.get_entity(entity)1463.map_err(|_| BrpError::entity_not_found(entity))1464}14651466/// Mutably retrieves an entity from the [`World`], returning an error if the1467/// entity isn't present.1468fn get_entity_mut(world: &mut World, entity: Entity) -> Result<EntityWorldMut<'_>, BrpError> {1469world1470.get_entity_mut(entity)1471.map_err(|_| BrpError::entity_not_found(entity))1472}14731474/// Given components full path, returns a tuple that contains1475/// - A list of corresponding [`TypeId`] and [`ComponentId`] for registered components.1476/// - A list of unregistered component paths.1477///1478/// Note that the supplied path names must be *full* path names: e.g.1479/// `bevy_transform::components::transform::Transform` instead of `Transform`.1480fn get_component_ids(1481type_registry: &TypeRegistry,1482world: &World,1483component_paths: Vec<String>,1484strict: bool,1485) -> AnyhowResult<(Vec<(TypeId, ComponentId)>, Vec<String>)> {1486let mut component_ids = vec![];1487let mut unregistered_components = vec![];14881489for component_path in component_paths {1490let maybe_component_tuple = get_component_type_registration(type_registry, &component_path)1491.ok()1492.and_then(|type_registration| {1493let type_id = type_registration.type_id();1494world1495.components()1496.get_valid_id(type_id)1497.map(|component_id| (type_id, component_id))1498});1499if let Some((type_id, component_id)) = maybe_component_tuple {1500component_ids.push((type_id, component_id));1501} else if strict {1502return Err(anyhow!(1503"Component `{}` isn't registered or used in the world",1504component_path1505));1506} else {1507unregistered_components.push(component_path);1508}1509}15101511Ok((component_ids, unregistered_components))1512}15131514/// Given an entity (`entity_ref`),1515/// a list of reflected component information (`paths_and_reflect_components`)1516/// and a list of unregistered components,1517/// return a map which associates each component to a boolean value indicating1518/// whether or not that component is present on the entity.1519/// Unregistered components are considered absent from the entity.1520fn build_has_map<'a>(1521entity_ref: FilteredEntityRef,1522paths_and_reflect_components: impl Iterator<Item = (&'a str, &'a ReflectComponent)>,1523unregistered_components: &[String],1524) -> HashMap<String, Value> {1525let mut has_map = <HashMap<_, _>>::default();15261527for (type_path, reflect_component) in paths_and_reflect_components {1528let has = reflect_component.contains(entity_ref);1529has_map.insert(type_path.to_owned(), Value::Bool(has));1530}1531unregistered_components.iter().for_each(|component| {1532has_map.insert(component.to_owned(), Value::Bool(false));1533});15341535has_map1536}15371538/// Given a component ID, return the associated [type path] and `ReflectComponent` if possible.1539///1540/// The `ReflectComponent` part is the meat of this; the type path is only used for error messages.1541///1542/// [type path]: bevy_reflect::TypePath::type_path1543fn reflect_component_from_id(1544component_type_id: TypeId,1545type_registry: &TypeRegistry,1546) -> AnyhowResult<(&str, &ReflectComponent)> {1547let Some(type_registration) = type_registry.get(component_type_id) else {1548return Err(anyhow!(1549"Component `{:?}` isn't registered",1550component_type_id1551));1552};15531554let type_path = type_registration.type_info().type_path();15551556let Some(reflect_component) = type_registration.data::<ReflectComponent>() else {1557return Err(anyhow!("Component `{}` isn't reflectable", type_path));1558};15591560Ok((type_path, reflect_component))1561}15621563/// Given a collection of component paths and their associated serialized values (`components`),1564/// return the associated collection of deserialized reflected values.1565fn deserialize_components(1566type_registry: &TypeRegistry,1567components: HashMap<String, Value>,1568) -> AnyhowResult<Vec<Box<dyn PartialReflect>>> {1569let mut reflect_components = vec![];15701571for (component_path, component) in components {1572let Some(component_type) = type_registry.get_with_type_path(&component_path) else {1573return Err(anyhow!("Unknown component type: `{}`", component_path));1574};1575let reflected: Box<dyn PartialReflect> =1576TypedReflectDeserializer::new(component_type, type_registry)1577.deserialize(&component)1578.map_err(|err| anyhow!("{component_path} is invalid: {err}"))?;1579reflect_components.push(reflected);1580}15811582Ok(reflect_components)1583}15841585/// Given a resource path and an associated serialized value (`value`), return the1586/// deserialized value.1587fn deserialize_resource(1588type_registry: &TypeRegistry,1589resource_path: &str,1590value: Value,1591) -> AnyhowResult<Box<dyn PartialReflect>> {1592let Some(resource_type) = type_registry.get_with_type_path(resource_path) else {1593return Err(anyhow!("Unknown resource type: `{}`", resource_path));1594};1595let reflected: Box<dyn PartialReflect> =1596TypedReflectDeserializer::new(resource_type, type_registry)1597.deserialize(&value)1598.map_err(|err| anyhow!("{resource_path} is invalid: {err}"))?;1599Ok(reflected)1600}16011602/// Given a collection `reflect_components` of reflected component values, insert them into1603/// the given entity (`entity_world_mut`).1604fn insert_reflected_components(1605mut entity_world_mut: EntityWorldMut,1606reflect_components: Vec<Box<dyn PartialReflect>>,1607) -> AnyhowResult<()> {1608for reflected in reflect_components {1609entity_world_mut.insert_reflect(reflected);1610}16111612Ok(())1613}16141615/// Given a component's type path, return the associated [`ReflectComponent`] from the given1616/// `type_registry` if possible.1617fn get_reflect_component<'r>(1618type_registry: &'r TypeRegistry,1619component_path: &str,1620) -> AnyhowResult<&'r ReflectComponent> {1621let component_registration = get_component_type_registration(type_registry, component_path)?;16221623component_registration1624.data::<ReflectComponent>()1625.ok_or_else(|| anyhow!("Component `{}` isn't reflectable", component_path))1626}16271628/// Given a component's type path, return the associated [`TypeRegistration`] from the given1629/// `type_registry` if possible.1630fn get_component_type_registration<'r>(1631type_registry: &'r TypeRegistry,1632component_path: &str,1633) -> AnyhowResult<&'r TypeRegistration> {1634type_registry1635.get_with_type_path(component_path)1636.ok_or_else(|| anyhow!("Unknown component type: `{}`", component_path))1637}16381639/// Given a resource's type path, return the associated [`ReflectResource`] from the given1640/// `type_registry` if possible.1641fn get_reflect_resource<'r>(1642type_registry: &'r TypeRegistry,1643resource_path: &str,1644) -> AnyhowResult<&'r ReflectResource> {1645let resource_registration = get_resource_type_registration(type_registry, resource_path)?;16461647resource_registration1648.data::<ReflectResource>()1649.ok_or_else(|| anyhow!("Resource `{}` isn't reflectable", resource_path))1650}16511652/// Given a resource's type path, return the associated [`TypeRegistration`] from the given1653/// `type_registry` if possible.1654fn get_resource_type_registration<'r>(1655type_registry: &'r TypeRegistry,1656resource_path: &str,1657) -> AnyhowResult<&'r TypeRegistration> {1658type_registry1659.get_with_type_path(resource_path)1660.ok_or_else(|| anyhow!("Unknown resource type: `{}`", resource_path))1661}16621663#[cfg(test)]1664mod tests {1665/// A generic function that tests serialization and deserialization of any type1666/// implementing Serialize and Deserialize traits.1667fn test_serialize_deserialize<T>(value: T)1668where1669T: Serialize + for<'a> Deserialize<'a> + PartialEq + core::fmt::Debug,1670{1671// Serialize the value to JSON string1672let serialized = serde_json::to_string(&value).expect("Failed to serialize");16731674// Deserialize the JSON string back into the original type1675let deserialized: T = serde_json::from_str(&serialized).expect("Failed to deserialize");16761677// Assert that the deserialized value is the same as the original1678assert_eq!(1679&value, &deserialized,1680"Deserialized value does not match original"1681);1682}16831684use super::*;1685use bevy_ecs::{1686component::Component, event::Event, observer::On, resource::Resource, system::ResMut,1687};1688use bevy_reflect::Reflect;1689use serde_json::Value::Null;16901691#[test]1692fn insert_reflect_only_component() {1693#[derive(Reflect, Component)]1694#[reflect(Component)]1695struct Player {1696name: String,1697health: u32,1698}1699let components: HashMap<String, Value> = [(1700String::from("bevy_remote::builtin_methods::tests::Player"),1701serde_json::json!({"name": "John", "health": 50}),1702)]1703.into();1704let atr = AppTypeRegistry::default();1705{1706let mut register = atr.write();1707register.register::<Player>();1708}1709let deserialized_components = {1710let type_reg = atr.read();1711deserialize_components(&type_reg, components).expect("FAIL")1712};1713let mut world = World::new();1714world.insert_resource(atr);1715let e = world.spawn_empty();1716insert_reflected_components(e, deserialized_components).expect("FAIL");1717}17181719#[test]1720fn trigger_reflect_only_event() {1721#[derive(Event, Reflect)]1722#[reflect(Event)]1723struct Pass;17241725#[derive(Resource)]1726struct TestResult(pub bool);17271728let atr = AppTypeRegistry::default();1729{1730let mut register = atr.write();1731register.register::<Pass>();1732}1733let mut world = World::new();1734world.add_observer(move |_event: On<Pass>, mut result: ResMut<TestResult>| result.0 = true);1735world.insert_resource(TestResult(false));1736world.insert_resource(atr);17371738let params = serde_json::to_value(&BrpTriggerEventParams {1739event: "bevy_remote::builtin_methods::tests::Pass".to_owned(),1740value: None,1741})1742.expect("FAIL");1743assert_eq!(1744process_remote_trigger_event_request(In(Some(params)), &mut world),1745Ok(Null)1746);1747assert!(world.resource::<TestResult>().0);1748}17491750#[test]1751fn serialization_tests() {1752test_serialize_deserialize(BrpQueryRow {1753components: Default::default(),1754entity: Entity::from_raw_u32(0).unwrap(),1755has: Default::default(),1756});1757test_serialize_deserialize(BrpListComponentsWatchingResponse::default());1758test_serialize_deserialize(BrpQuery::default());1759test_serialize_deserialize(BrpJsonSchemaQueryFilter::default());1760test_serialize_deserialize(BrpJsonSchemaQueryFilter {1761type_limit: JsonSchemaTypeLimit {1762with: vec!["Resource".to_owned()],1763..Default::default()1764},1765..Default::default()1766});1767test_serialize_deserialize(BrpListComponentsParams {1768entity: Entity::from_raw_u32(0).unwrap(),1769});1770}1771}177217731774