bevy/examples/remote/server.rs
Matty Weatherley 4b1745f813
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"
}
```
2025-02-26 20:29:47 +00:00

91 lines
2.5 KiB
Rust

//! A Bevy app that you can connect to with the BRP and edit.
use bevy::math::ops::cos;
use bevy::{
input::common_conditions::input_just_pressed,
prelude::*,
remote::{http::RemoteHttpPlugin, RemotePlugin},
};
use serde::{Deserialize, Serialize};
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_plugins(RemotePlugin::default())
.add_plugins(RemoteHttpPlugin::default())
.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();
}
fn setup(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
// circular base
commands.spawn((
Mesh3d(meshes.add(Circle::new(4.0))),
MeshMaterial3d(materials.add(Color::WHITE)),
Transform::from_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2)),
));
// cube
commands.spawn((
Mesh3d(meshes.add(Cuboid::new(1.0, 1.0, 1.0))),
MeshMaterial3d(materials.add(Color::srgb_u8(124, 144, 255))),
Transform::from_xyz(0.0, 0.5, 0.0),
Cube(1.0),
));
// test resource
commands.insert_resource(TestResource {
foo: Vec2::new(1.0, -1.0),
bar: false,
});
// light
commands.spawn((
PointLight {
shadows_enabled: true,
..default()
},
Transform::from_xyz(4.0, 8.0, 4.0),
));
// camera
commands.spawn((
Camera3d::default(),
Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),
));
}
/// 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;
}
}
fn remove(mut commands: Commands, cube_entity: Single<Entity, With<Cube>>) {
commands.entity(*cube_entity).remove::<Cube>();
}
#[derive(Component, Reflect, Serialize, Deserialize)]
#[reflect(Component, Serialize, Deserialize)]
struct Cube(f32);