BRP resource methods (#17423)
# Objective So far, built-in BRP methods allow users to interact with entities' components, but global resources have remained beyond its reach. The goal of this PR is to take the first steps in rectifying this shortfall. ## Solution Added five new default methods to BRP: - `bevy/get_resource`: Extracts the value of a given resource from the world. - `bevy/insert_resource`: Serializes an input value to a given resource type and inserts it into the world. - `bevy/remove_resource`: Removes the given resource from the world. - `bevy/mutate_resource`: Replaces the value of a field in a given resource with the result of serializing a given input value. - `bevy/list_resources`: Lists all resources in the type registry with an available `ReflectResource`. ## Testing Added a test resource to the `server` example scene that you can use to mess around with the new BRP methods. ## Showcase Resources can now be retrieved and manipulated remotely using a handful of new BRP methods. For example, a resource that looks like this: ```rust #[derive(Resource, Reflect, Serialize, Deserialize)] #[reflect(Resource, Serialize, Deserialize)] pub struct PlayerSpawnSettings { pub location: Vec2, pub lives: u8, } ``` can be manipulated remotely as follows. Retrieving the value of the resource: ```json { "jsonrpc": "2.0", "id": 1, "method": "bevy/get_resource", "params": { "resource": "path::to::my::module::PlayerSpawnSettings" } } ``` Inserting a resource value into the world: ```json { "jsonrpc": "2.0", "id": 2, "method": "bevy/insert_resource", "params": { "resource": "path::to::my::module::PlayerSpawnSettings", "value": { "location": [ 2.5, 2.5 ], "lives": 25 } } } ``` Removing the resource from the world: ```json { "jsonrpc": "2.0", "id": 3, "method": "bevy/remove_resource", "params": { "resource": "path::to::my::module::PlayerSpawnSettings" } } ``` Mutating a field of the resource specified by a path: ```json { "jsonrpc": "2.0", "id": 4, "method": "bevy/mutate_resource", "params": { "resource": "path::to::my::module::PlayerSpawnSettings", "path": ".location.x", "value": -3.0 } } ``` Listing all manipulable resources in the type registry: ```json { "jsonrpc": "2.0", "id": 5, "method": "bevy/list_resources" } ```
This commit is contained in:
parent
11db71727d
commit
4b1745f813
@ -18,7 +18,7 @@ use bevy_platform_support::collections::HashMap;
|
||||
use bevy_reflect::{
|
||||
prelude::ReflectDefault,
|
||||
serde::{ReflectSerializer, TypedReflectDeserializer},
|
||||
GetPath as _, NamedField, OpaqueInfo, PartialReflect, ReflectDeserialize, ReflectSerialize,
|
||||
GetPath, NamedField, OpaqueInfo, PartialReflect, ReflectDeserialize, ReflectSerialize,
|
||||
TypeInfo, TypeRegistration, TypeRegistry, VariantInfo,
|
||||
};
|
||||
use serde::{de::DeserializeSeed as _, Deserialize, Serialize};
|
||||
@ -59,6 +59,21 @@ pub const BRP_GET_AND_WATCH_METHOD: &str = "bevy/get+watch";
|
||||
/// The method path for a `bevy/list+watch` request.
|
||||
pub const BRP_LIST_AND_WATCH_METHOD: &str = "bevy/list+watch";
|
||||
|
||||
/// The method path for a `bevy/get_resource` request.
|
||||
pub const BRP_GET_RESOURCE_METHOD: &str = "bevy/get_resource";
|
||||
|
||||
/// The method path for a `bevy/insert_resource` request.
|
||||
pub const BRP_INSERT_RESOURCE_METHOD: &str = "bevy/insert_resource";
|
||||
|
||||
/// The method path for a `bevy/remove_resource` request.
|
||||
pub const BRP_REMOVE_RESOURCE_METHOD: &str = "bevy/remove_resource";
|
||||
|
||||
/// The method path for a `bevy/mutate_resource` request.
|
||||
pub const BRP_MUTATE_RESOURCE_METHOD: &str = "bevy/mutate_resource";
|
||||
|
||||
/// The method path for a `bevy/list_resources` request.
|
||||
pub const BRP_LIST_RESOURCES_METHOD: &str = "bevy/list_resources";
|
||||
|
||||
/// The method path for a `bevy/registry/schema` request.
|
||||
pub const BRP_REGISTRY_SCHEMA_METHOD: &str = "bevy/registry/schema";
|
||||
|
||||
@ -87,6 +102,15 @@ pub struct BrpGetParams {
|
||||
pub strict: bool,
|
||||
}
|
||||
|
||||
/// `bevy/get_resource`: Retrieves the value of a given resource.
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
||||
pub struct BrpGetResourceParams {
|
||||
/// The [full path] of the resource type being requested.
|
||||
///
|
||||
/// [full path]: bevy_reflect::TypePath::type_path
|
||||
pub resource: String,
|
||||
}
|
||||
|
||||
/// `bevy/query`: Performs a query over components in the ECS, returning entities
|
||||
/// and component values that match.
|
||||
///
|
||||
@ -153,6 +177,15 @@ pub struct BrpRemoveParams {
|
||||
pub components: Vec<String>,
|
||||
}
|
||||
|
||||
/// `bevy/remove_resource`: Removes the given resource from the world.
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
||||
pub struct BrpRemoveResourceParams {
|
||||
/// The [full path] of the resource type to remove.
|
||||
///
|
||||
/// [full path]: bevy_reflect::TypePath::type_path
|
||||
pub resource: String,
|
||||
}
|
||||
|
||||
/// `bevy/insert`: Adds one or more components to an entity.
|
||||
///
|
||||
/// The server responds with a null.
|
||||
@ -173,6 +206,19 @@ pub struct BrpInsertParams {
|
||||
pub components: HashMap<String, Value>,
|
||||
}
|
||||
|
||||
/// `bevy/insert_resource`: Inserts a resource into the world with a given
|
||||
/// value.
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
||||
pub struct BrpInsertResourceParams {
|
||||
/// The [full path] of the resource type to insert.
|
||||
///
|
||||
/// [full path]: bevy_reflect::TypePath::type_path
|
||||
pub resource: String,
|
||||
|
||||
/// The serialized value of the resource to be inserted.
|
||||
pub value: Value,
|
||||
}
|
||||
|
||||
/// `bevy/reparent`: Assign a new parent to one or more entities.
|
||||
///
|
||||
/// The server responds with a null.
|
||||
@ -204,7 +250,7 @@ pub struct BrpListParams {
|
||||
///
|
||||
/// The server responds with a null.
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
||||
pub struct BrpMutateParams {
|
||||
pub struct BrpMutateComponentParams {
|
||||
/// The entity of the component to mutate.
|
||||
pub entity: Entity,
|
||||
|
||||
@ -222,6 +268,25 @@ pub struct BrpMutateParams {
|
||||
pub value: Value,
|
||||
}
|
||||
|
||||
/// `bevy/mutate_resource`:
|
||||
///
|
||||
/// The server responds with a null.
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
||||
pub struct BrpMutateResourceParams {
|
||||
/// The [full path] of the resource to mutate.
|
||||
///
|
||||
/// [full path]: bevy_reflect::TypePath::type_path
|
||||
pub resource: String,
|
||||
|
||||
/// The [path] of the field within the resource.
|
||||
///
|
||||
/// [path]: bevy_reflect::GetPath
|
||||
pub path: String,
|
||||
|
||||
/// The value to insert at `path`.
|
||||
pub value: Value,
|
||||
}
|
||||
|
||||
/// Describes the data that is to be fetched in a query.
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq)]
|
||||
pub struct BrpQuery {
|
||||
@ -323,6 +388,13 @@ pub enum BrpGetResponse {
|
||||
Strict(HashMap<String, Value>),
|
||||
}
|
||||
|
||||
/// The response to a `bevy/get_resource` request.
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
||||
pub struct BrpGetResourceResponse {
|
||||
/// The value of the requested resource.
|
||||
pub value: Value,
|
||||
}
|
||||
|
||||
/// A single response from a `bevy/get+watch` request.
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
||||
#[serde(untagged)]
|
||||
@ -351,6 +423,9 @@ pub enum BrpGetWatchingResponse {
|
||||
/// The response to a `bevy/list` request.
|
||||
pub type BrpListResponse = Vec<String>;
|
||||
|
||||
/// The response to a `bevy/list_resources` request.
|
||||
pub type BrpListResourcesResponse = Vec<String>;
|
||||
|
||||
/// A single response from a `bevy/list+watch` request.
|
||||
#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
|
||||
pub struct BrpListWatchingResponse {
|
||||
@ -413,6 +488,45 @@ pub fn process_remote_get_request(In(params): In<Option<Value>>, world: &World)
|
||||
serde_json::to_value(response).map_err(BrpError::internal)
|
||||
}
|
||||
|
||||
/// Handles a `bevy/get_resource` request coming from a client.
|
||||
pub fn process_remote_get_resource_request(
|
||||
In(params): In<Option<Value>>,
|
||||
world: &World,
|
||||
) -> BrpResult {
|
||||
let BrpGetResourceParams {
|
||||
resource: resource_path,
|
||||
} = parse_some(params)?;
|
||||
|
||||
let app_type_registry = world.resource::<AppTypeRegistry>();
|
||||
let type_registry = app_type_registry.read();
|
||||
let reflect_resource =
|
||||
get_reflect_resource(&type_registry, &resource_path).map_err(BrpError::resource_error)?;
|
||||
|
||||
let Some(reflected) = reflect_resource.reflect(world) else {
|
||||
return Err(BrpError::resource_not_present(&resource_path));
|
||||
};
|
||||
|
||||
// Use the `ReflectSerializer` to serialize the value of the resource;
|
||||
// this produces a map with a single item.
|
||||
let reflect_serializer = ReflectSerializer::new(reflected.as_partial_reflect(), &type_registry);
|
||||
let Value::Object(serialized_object) =
|
||||
serde_json::to_value(&reflect_serializer).map_err(BrpError::resource_error)?
|
||||
else {
|
||||
return Err(BrpError {
|
||||
code: error_codes::RESOURCE_ERROR,
|
||||
message: format!("Resource `{}` could not be serialized", resource_path),
|
||||
data: None,
|
||||
});
|
||||
};
|
||||
|
||||
// Get the single value out of the map.
|
||||
let value = serialized_object.into_values().next().ok_or_else(|| {
|
||||
BrpError::internal(anyhow!("Unexpected format of serialized resource value"))
|
||||
})?;
|
||||
let response = BrpGetResourceResponse { value };
|
||||
serde_json::to_value(response).map_err(BrpError::internal)
|
||||
}
|
||||
|
||||
/// Handles a `bevy/get+watch` request coming from a client.
|
||||
pub fn process_remote_get_watching_request(
|
||||
In(params): In<Option<Value>>,
|
||||
@ -569,11 +683,7 @@ fn reflect_component(
|
||||
// Each component value serializes to a map with a single entry.
|
||||
let reflect_serializer = ReflectSerializer::new(reflected.as_partial_reflect(), type_registry);
|
||||
let Value::Object(serialized_object) =
|
||||
serde_json::to_value(&reflect_serializer).map_err(|err| BrpError {
|
||||
code: error_codes::COMPONENT_ERROR,
|
||||
message: err.to_string(),
|
||||
data: None,
|
||||
})?
|
||||
serde_json::to_value(&reflect_serializer).map_err(BrpError::component_error)?
|
||||
else {
|
||||
return Err(BrpError {
|
||||
code: error_codes::COMPONENT_ERROR,
|
||||
@ -719,6 +829,29 @@ pub fn process_remote_insert_request(
|
||||
Ok(Value::Null)
|
||||
}
|
||||
|
||||
/// Handles a `bevy/insert_resource` request coming from a client.
|
||||
pub fn process_remote_insert_resource_request(
|
||||
In(params): In<Option<Value>>,
|
||||
world: &mut World,
|
||||
) -> BrpResult {
|
||||
let BrpInsertResourceParams {
|
||||
resource: resource_path,
|
||||
value,
|
||||
} = parse_some(params)?;
|
||||
|
||||
let app_type_registry = world.resource::<AppTypeRegistry>().clone();
|
||||
let type_registry = app_type_registry.read();
|
||||
|
||||
let reflected_resource = deserialize_resource(&type_registry, &resource_path, value)
|
||||
.map_err(BrpError::resource_error)?;
|
||||
|
||||
let reflect_resource =
|
||||
get_reflect_resource(&type_registry, &resource_path).map_err(BrpError::resource_error)?;
|
||||
reflect_resource.insert(world, &*reflected_resource, &type_registry);
|
||||
|
||||
Ok(Value::Null)
|
||||
}
|
||||
|
||||
/// Handles a `bevy/mutate_component` request coming from a client.
|
||||
///
|
||||
/// This method allows you to mutate a single field inside an Entity's
|
||||
@ -727,7 +860,7 @@ pub fn process_remote_mutate_component_request(
|
||||
In(params): In<Option<Value>>,
|
||||
world: &mut World,
|
||||
) -> BrpResult {
|
||||
let BrpMutateParams {
|
||||
let BrpMutateComponentParams {
|
||||
entity,
|
||||
component,
|
||||
path,
|
||||
@ -747,7 +880,7 @@ pub fn process_remote_mutate_component_request(
|
||||
let mut reflected = component_type
|
||||
.data::<ReflectComponent>()
|
||||
.ok_or_else(|| {
|
||||
BrpError::component_error(anyhow!("Component `{}` isn't registered.", component))
|
||||
BrpError::component_error(anyhow!("Component `{}` isn't registered", component))
|
||||
})?
|
||||
.reflect_mut(world.entity_mut(entity))
|
||||
.ok_or_else(|| {
|
||||
@ -783,6 +916,57 @@ pub fn process_remote_mutate_component_request(
|
||||
Ok(Value::Null)
|
||||
}
|
||||
|
||||
/// Handles a `bevy/mutate_resource` request coming from a client.
|
||||
pub fn process_remote_mutate_resource_request(
|
||||
In(params): In<Option<Value>>,
|
||||
world: &mut World,
|
||||
) -> BrpResult {
|
||||
let BrpMutateResourceParams {
|
||||
resource: resource_path,
|
||||
path: field_path,
|
||||
value,
|
||||
} = parse_some(params)?;
|
||||
|
||||
let app_type_registry = world.resource::<AppTypeRegistry>().clone();
|
||||
let type_registry = app_type_registry.read();
|
||||
|
||||
// Get the `ReflectResource` for the given resource path.
|
||||
let reflect_resource =
|
||||
get_reflect_resource(&type_registry, &resource_path).map_err(BrpError::resource_error)?;
|
||||
|
||||
// Get the actual resource value from the world as a `dyn Reflect`.
|
||||
let mut reflected_resource = reflect_resource
|
||||
.reflect_mut(world)
|
||||
.ok_or_else(|| BrpError::resource_not_present(&resource_path))?;
|
||||
|
||||
// Get the type registration for the field with the given path.
|
||||
let value_registration = type_registry
|
||||
.get_with_type_path(
|
||||
reflected_resource
|
||||
.reflect_path(field_path.as_str())
|
||||
.map_err(BrpError::resource_error)?
|
||||
.reflect_type_path(),
|
||||
)
|
||||
.ok_or_else(|| {
|
||||
BrpError::resource_error(anyhow!("Unknown resource field type: `{}`", resource_path))
|
||||
})?;
|
||||
|
||||
// Use the field's type registration to deserialize the given value.
|
||||
let deserialized_value: Box<dyn PartialReflect> =
|
||||
TypedReflectDeserializer::new(value_registration, &type_registry)
|
||||
.deserialize(&value)
|
||||
.map_err(BrpError::resource_error)?;
|
||||
|
||||
// Apply the value to the resource.
|
||||
reflected_resource
|
||||
.reflect_path_mut(field_path.as_str())
|
||||
.map_err(BrpError::resource_error)?
|
||||
.try_apply(&*deserialized_value)
|
||||
.map_err(BrpError::resource_error)?;
|
||||
|
||||
Ok(Value::Null)
|
||||
}
|
||||
|
||||
/// Handles a `bevy/remove` request (remove components) coming from a client.
|
||||
pub fn process_remote_remove_request(
|
||||
In(params): In<Option<Value>>,
|
||||
@ -805,6 +989,25 @@ pub fn process_remote_remove_request(
|
||||
Ok(Value::Null)
|
||||
}
|
||||
|
||||
/// Handles a `bevy/remove_resource` request coming from a client.
|
||||
pub fn process_remote_remove_resource_request(
|
||||
In(params): In<Option<Value>>,
|
||||
world: &mut World,
|
||||
) -> BrpResult {
|
||||
let BrpRemoveResourceParams {
|
||||
resource: resource_path,
|
||||
} = parse_some(params)?;
|
||||
|
||||
let app_type_registry = world.resource::<AppTypeRegistry>().clone();
|
||||
let type_registry = app_type_registry.read();
|
||||
|
||||
let reflect_resource =
|
||||
get_reflect_resource(&type_registry, &resource_path).map_err(BrpError::resource_error)?;
|
||||
reflect_resource.remove(world);
|
||||
|
||||
Ok(Value::Null)
|
||||
}
|
||||
|
||||
/// Handles a `bevy/destroy` (despawn entity) request coming from a client.
|
||||
pub fn process_remote_destroy_request(
|
||||
In(params): In<Option<Value>>,
|
||||
@ -881,7 +1084,28 @@ pub fn process_remote_list_request(In(params): In<Option<Value>>, world: &World)
|
||||
serde_json::to_value(response).map_err(BrpError::internal)
|
||||
}
|
||||
|
||||
/// Handles a `bevy/list` request (list all components) coming from a client.
|
||||
/// Handles a `bevy/list_resources` request coming from a client.
|
||||
pub fn process_remote_list_resources_request(
|
||||
In(_params): In<Option<Value>>,
|
||||
world: &World,
|
||||
) -> BrpResult {
|
||||
let mut response = BrpListResourcesResponse::default();
|
||||
|
||||
let app_type_registry = world.resource::<AppTypeRegistry>();
|
||||
let type_registry = app_type_registry.read();
|
||||
|
||||
for registered_type in type_registry.iter() {
|
||||
if registered_type.data::<ReflectResource>().is_some() {
|
||||
response.push(registered_type.type_info().type_path().to_owned());
|
||||
}
|
||||
}
|
||||
|
||||
response.sort();
|
||||
|
||||
serde_json::to_value(response).map_err(BrpError::internal)
|
||||
}
|
||||
|
||||
/// Handles a `bevy/list+watch` request coming from a client.
|
||||
pub fn process_remote_list_watching_request(
|
||||
In(params): In<Option<Value>>,
|
||||
world: &World,
|
||||
@ -1232,6 +1456,7 @@ pub enum SchemaKind {
|
||||
pub enum SchemaType {
|
||||
/// Represents a string value.
|
||||
String,
|
||||
|
||||
/// Represents a floating-point number.
|
||||
Float,
|
||||
|
||||
@ -1451,6 +1676,23 @@ fn deserialize_components(
|
||||
Ok(reflect_components)
|
||||
}
|
||||
|
||||
/// Given a resource path and an associated serialized value (`value`), return the
|
||||
/// deserialized value.
|
||||
fn deserialize_resource(
|
||||
type_registry: &TypeRegistry,
|
||||
resource_path: &str,
|
||||
value: Value,
|
||||
) -> AnyhowResult<Box<dyn PartialReflect>> {
|
||||
let Some(resource_type) = type_registry.get_with_type_path(resource_path) else {
|
||||
return Err(anyhow!("Unknown resource type: `{}`", resource_path));
|
||||
};
|
||||
let reflected: Box<dyn PartialReflect> =
|
||||
TypedReflectDeserializer::new(resource_type, type_registry)
|
||||
.deserialize(&value)
|
||||
.map_err(|err| anyhow!("{resource_path} is invalid: {err}"))?;
|
||||
Ok(reflected)
|
||||
}
|
||||
|
||||
/// Given a collection `reflect_components` of reflected component values, insert them into
|
||||
/// the given entity (`entity_world_mut`).
|
||||
fn insert_reflected_components(
|
||||
@ -1491,6 +1733,30 @@ fn get_component_type_registration<'r>(
|
||||
.ok_or_else(|| anyhow!("Unknown component type: `{}`", component_path))
|
||||
}
|
||||
|
||||
/// Given a resource's type path, return the associated [`ReflectResource`] from the given
|
||||
/// `type_registry` if possible.
|
||||
fn get_reflect_resource<'r>(
|
||||
type_registry: &'r TypeRegistry,
|
||||
resource_path: &str,
|
||||
) -> AnyhowResult<&'r ReflectResource> {
|
||||
let resource_registration = get_resource_type_registration(type_registry, resource_path)?;
|
||||
|
||||
resource_registration
|
||||
.data::<ReflectResource>()
|
||||
.ok_or_else(|| anyhow!("Resource `{}` isn't reflectable", resource_path))
|
||||
}
|
||||
|
||||
/// Given a resource's type path, return the associated [`TypeRegistration`] from the given
|
||||
/// `type_registry` if possible.
|
||||
fn get_resource_type_registration<'r>(
|
||||
type_registry: &'r TypeRegistry,
|
||||
resource_path: &str,
|
||||
) -> AnyhowResult<&'r TypeRegistration> {
|
||||
type_registry
|
||||
.get_with_type_path(resource_path)
|
||||
.ok_or_else(|| anyhow!("Unknown resource type: `{}`", resource_path))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
/// A generic function that tests serialization and deserialization of any type
|
||||
|
@ -102,7 +102,7 @@
|
||||
//! in the ECS. Each of these methods uses the `bevy/` prefix, which is a namespace reserved for
|
||||
//! BRP built-in methods.
|
||||
//!
|
||||
//! ### bevy/get
|
||||
//! ### `bevy/get`
|
||||
//!
|
||||
//! Retrieve the values of one or more components from an entity.
|
||||
//!
|
||||
@ -123,7 +123,7 @@
|
||||
//!
|
||||
//! `result`: A map associating each type name to its value on the requested entity.
|
||||
//!
|
||||
//! ### bevy/query
|
||||
//! ### `bevy/query`
|
||||
//!
|
||||
//! Perform a query over components in the ECS, returning all matching entities and their associated
|
||||
//! component values.
|
||||
@ -155,7 +155,7 @@
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
//! ### bevy/spawn
|
||||
//! ### `bevy/spawn`
|
||||
//!
|
||||
//! Create a new entity with the provided components and return the resulting entity ID.
|
||||
//!
|
||||
@ -165,7 +165,7 @@
|
||||
//! `result`:
|
||||
//! - `entity`: The ID of the newly spawned entity.
|
||||
//!
|
||||
//! ### bevy/destroy
|
||||
//! ### `bevy/destroy`
|
||||
//!
|
||||
//! Despawn the entity with the given ID.
|
||||
//!
|
||||
@ -174,7 +174,7 @@
|
||||
//!
|
||||
//! `result`: null.
|
||||
//!
|
||||
//! ### bevy/remove
|
||||
//! ### `bevy/remove`
|
||||
//!
|
||||
//! Delete one or more components from an entity.
|
||||
//!
|
||||
@ -184,7 +184,7 @@
|
||||
//!
|
||||
//! `result`: null.
|
||||
//!
|
||||
//! ### bevy/insert
|
||||
//! ### `bevy/insert`
|
||||
//!
|
||||
//! Insert one or more components into an entity.
|
||||
//!
|
||||
@ -207,7 +207,7 @@
|
||||
//!
|
||||
//! `result`: null.
|
||||
//!
|
||||
//! ### bevy/reparent
|
||||
//! ### `bevy/reparent`
|
||||
//!
|
||||
//! Assign a new parent to one or more entities.
|
||||
//!
|
||||
@ -218,7 +218,7 @@
|
||||
//!
|
||||
//! `result`: null.
|
||||
//!
|
||||
//! ### bevy/list
|
||||
//! ### `bevy/list`
|
||||
//!
|
||||
//! List all registered components or all components present on an entity.
|
||||
//!
|
||||
@ -230,7 +230,7 @@
|
||||
//!
|
||||
//! `result`: An array of fully-qualified type names of components.
|
||||
//!
|
||||
//! ### bevy/get+watch
|
||||
//! ### `bevy/get+watch`
|
||||
//!
|
||||
//! Watch the values of one or more components from an entity.
|
||||
//!
|
||||
@ -258,7 +258,7 @@
|
||||
//! - `removed`: An array of fully-qualified type names of components removed from the entity
|
||||
//! in the last tick.
|
||||
//!
|
||||
//! ### bevy/list+watch
|
||||
//! ### `bevy/list+watch`
|
||||
//!
|
||||
//! Watch all components present on an entity.
|
||||
//!
|
||||
@ -274,6 +274,52 @@
|
||||
//! - `removed`: An array of fully-qualified type names of components removed from the entity
|
||||
//! in the last tick.
|
||||
//!
|
||||
//! ### `bevy/get_resource`
|
||||
//!
|
||||
//! Extract the value of a given resource from the world.
|
||||
//!
|
||||
//! `params`:
|
||||
//! - `resource`: The [fully-qualified type name] of the resource to get.
|
||||
//!
|
||||
//! `result`:
|
||||
//! - `value`: The value of the resource in the world.
|
||||
//!
|
||||
//! ### `bevy/insert_resource`
|
||||
//!
|
||||
//! Insert the given resource into the world with the given value.
|
||||
//!
|
||||
//! `params`:
|
||||
//! - `resource`: The [fully-qualified type name] of the resource to insert.
|
||||
//! - `value`: The value of the resource to be inserted.
|
||||
//!
|
||||
//! `result`: null.
|
||||
//!
|
||||
//! ### `bevy/remove_resource`
|
||||
//!
|
||||
//! Remove the given resource from the world.
|
||||
//!
|
||||
//! `params`
|
||||
//! - `resource`: The [fully-qualified type name] of the resource to remove.
|
||||
//!
|
||||
//! `result`: null.
|
||||
//!
|
||||
//! ### `bevy/mutate_resource`
|
||||
//!
|
||||
//! Mutate a field in a resource.
|
||||
//!
|
||||
//! `params`:
|
||||
//! - `resource`: The [fully-qualified type name] of the resource to mutate.
|
||||
//! - `path`: The path of the field within the resource. See
|
||||
//! [`GetPath`](bevy_reflect::GetPath#syntax) for more information on formatting this string.
|
||||
//! - `value`: The value to be inserted at `path`.
|
||||
//!
|
||||
//! `result`: null.
|
||||
//!
|
||||
//! ### `bevy/list_resources`
|
||||
//!
|
||||
//! List all reflectable registered resource types. This method has no parameters.
|
||||
//!
|
||||
//! `result`: An array of [fully-qualified type names] of registered resource types.
|
||||
//!
|
||||
//! ## Custom methods
|
||||
//!
|
||||
@ -424,10 +470,6 @@ impl Default for RemotePlugin {
|
||||
builtin_methods::BRP_LIST_METHOD,
|
||||
builtin_methods::process_remote_list_request,
|
||||
)
|
||||
.with_method(
|
||||
builtin_methods::BRP_REGISTRY_SCHEMA_METHOD,
|
||||
builtin_methods::export_registry_types,
|
||||
)
|
||||
.with_method(
|
||||
builtin_methods::BRP_MUTATE_COMPONENT_METHOD,
|
||||
builtin_methods::process_remote_mutate_component_request,
|
||||
@ -440,6 +482,30 @@ impl Default for RemotePlugin {
|
||||
builtin_methods::BRP_LIST_AND_WATCH_METHOD,
|
||||
builtin_methods::process_remote_list_watching_request,
|
||||
)
|
||||
.with_method(
|
||||
builtin_methods::BRP_GET_RESOURCE_METHOD,
|
||||
builtin_methods::process_remote_get_resource_request,
|
||||
)
|
||||
.with_method(
|
||||
builtin_methods::BRP_INSERT_RESOURCE_METHOD,
|
||||
builtin_methods::process_remote_insert_resource_request,
|
||||
)
|
||||
.with_method(
|
||||
builtin_methods::BRP_REMOVE_RESOURCE_METHOD,
|
||||
builtin_methods::process_remote_remove_resource_request,
|
||||
)
|
||||
.with_method(
|
||||
builtin_methods::BRP_MUTATE_RESOURCE_METHOD,
|
||||
builtin_methods::process_remote_mutate_resource_request,
|
||||
)
|
||||
.with_method(
|
||||
builtin_methods::BRP_LIST_RESOURCES_METHOD,
|
||||
builtin_methods::process_remote_list_resources_request,
|
||||
)
|
||||
.with_method(
|
||||
builtin_methods::BRP_REGISTRY_SCHEMA_METHOD,
|
||||
builtin_methods::export_registry_types,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -718,6 +784,26 @@ impl BrpError {
|
||||
}
|
||||
}
|
||||
|
||||
/// Resource was not present in the world.
|
||||
#[must_use]
|
||||
pub fn resource_not_present(resource: &str) -> Self {
|
||||
Self {
|
||||
code: error_codes::RESOURCE_NOT_PRESENT,
|
||||
message: format!("Resource `{resource}` not present in the world"),
|
||||
data: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// An arbitrary resource error. Possibly related to reflection.
|
||||
#[must_use]
|
||||
pub fn resource_error<E: ToString>(error: E) -> Self {
|
||||
Self {
|
||||
code: error_codes::RESOURCE_ERROR,
|
||||
message: error.to_string(),
|
||||
data: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// An arbitrary internal error.
|
||||
#[must_use]
|
||||
pub fn internal<E: ToString>(error: E) -> Self {
|
||||
@ -772,6 +858,12 @@ pub mod error_codes {
|
||||
|
||||
/// Cannot reparent an entity to itself.
|
||||
pub const SELF_REPARENT: i16 = -23404;
|
||||
|
||||
/// Could not reflect or find resource.
|
||||
pub const RESOURCE_ERROR: i16 = -23501;
|
||||
|
||||
/// Could not find resource in the world.
|
||||
pub const RESOURCE_NOT_PRESENT: i16 = -23502;
|
||||
}
|
||||
|
||||
/// The result of a request.
|
||||
|
@ -16,7 +16,9 @@ fn main() {
|
||||
.add_systems(Startup, setup)
|
||||
.add_systems(Update, remove.run_if(input_just_pressed(KeyCode::Space)))
|
||||
.add_systems(Update, move_cube)
|
||||
// New types must be registered in order to be usable with reflection.
|
||||
.register_type::<Cube>()
|
||||
.register_type::<TestResource>()
|
||||
.run();
|
||||
}
|
||||
|
||||
@ -40,6 +42,12 @@ fn setup(
|
||||
Cube(1.0),
|
||||
));
|
||||
|
||||
// test resource
|
||||
commands.insert_resource(TestResource {
|
||||
foo: Vec2::new(1.0, -1.0),
|
||||
bar: false,
|
||||
});
|
||||
|
||||
// light
|
||||
commands.spawn((
|
||||
PointLight {
|
||||
@ -56,6 +64,17 @@ fn setup(
|
||||
));
|
||||
}
|
||||
|
||||
/// An arbitrary resource that can be inspected and manipulated with remote methods.
|
||||
#[derive(Resource, Reflect, Serialize, Deserialize)]
|
||||
#[reflect(Resource, Serialize, Deserialize)]
|
||||
pub struct TestResource {
|
||||
/// An arbitrary field of the test resource.
|
||||
pub foo: Vec2,
|
||||
|
||||
/// Another arbitrary field.
|
||||
pub bar: bool,
|
||||
}
|
||||
|
||||
fn move_cube(mut query: Query<&mut Transform, With<Cube>>, time: Res<Time>) {
|
||||
for mut transform in &mut query {
|
||||
transform.translation.y = -cos(time.elapsed_secs()) + 1.5;
|
||||
|
Loading…
Reference in New Issue
Block a user