Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_remote/src/builtin_methods.rs
9296 views
1
//! Built-in verbs for the Bevy Remote Protocol.
2
3
use core::any::TypeId;
4
5
use anyhow::{anyhow, Result as AnyhowResult};
6
use bevy_ecs::{
7
component::ComponentId,
8
entity::Entity,
9
hierarchy::ChildOf,
10
lifecycle::RemovedComponentEntity,
11
message::MessageCursor,
12
query::QueryBuilder,
13
reflect::{AppTypeRegistry, ReflectComponent, ReflectEvent, ReflectResource},
14
system::{In, Local},
15
world::{EntityRef, EntityWorldMut, FilteredEntityRef, Mut, World},
16
};
17
use bevy_log::warn_once;
18
use bevy_platform::collections::HashMap;
19
use bevy_reflect::{
20
serde::{ReflectSerializer, TypedReflectDeserializer},
21
structs::DynamicStruct,
22
GetPath, PartialReflect, TypeRegistration, TypeRegistry,
23
};
24
use serde::{de::DeserializeSeed as _, de::IntoDeserializer, Deserialize, Serialize};
25
use serde_json::{Map, Value};
26
27
use crate::{
28
error_codes,
29
schemas::{
30
json_schema::{export_type, JsonSchemaBevyType},
31
open_rpc::OpenRpcDocument,
32
},
33
BrpError, BrpResult,
34
};
35
36
#[cfg(all(feature = "http", not(target_family = "wasm")))]
37
use {crate::schemas::open_rpc::ServerObject, bevy_utils::default};
38
39
/// The method path for a `world.get_components` request.
40
pub const BRP_GET_COMPONENTS_METHOD: &str = "world.get_components";
41
42
/// The method path for a `world.query` request.
43
pub const BRP_QUERY_METHOD: &str = "world.query";
44
45
/// The method path for a `world.spawn_entity` request.
46
pub const BRP_SPAWN_ENTITY_METHOD: &str = "world.spawn_entity";
47
48
/// The method path for a `world.insert_components` request.
49
pub const BRP_INSERT_COMPONENTS_METHOD: &str = "world.insert_components";
50
51
/// The method path for a `world.remove_components` request.
52
pub const BRP_REMOVE_COMPONENTS_METHOD: &str = "world.remove_components";
53
54
/// The method path for a `world.despawn_entity` request.
55
pub const BRP_DESPAWN_COMPONENTS_METHOD: &str = "world.despawn_entity";
56
57
/// The method path for a `world.reparent_entities` request.
58
pub const BRP_REPARENT_ENTITIES_METHOD: &str = "world.reparent_entities";
59
60
/// The method path for a `world.list_components` request.
61
pub const BRP_LIST_COMPONENTS_METHOD: &str = "world.list_components";
62
63
/// The method path for a `world.mutate_components` request.
64
pub const BRP_MUTATE_COMPONENTS_METHOD: &str = "world.mutate_components";
65
66
/// The method path for a `world.get_components+watch` request.
67
pub const BRP_GET_COMPONENTS_AND_WATCH_METHOD: &str = "world.get_components+watch";
68
69
/// The method path for a `world.list_components+watch` request.
70
pub const BRP_LIST_COMPONENTS_AND_WATCH_METHOD: &str = "world.list_components+watch";
71
72
/// The method path for a `world.get_resources` request.
73
pub const BRP_GET_RESOURCE_METHOD: &str = "world.get_resources";
74
75
/// The method path for a `world.insert_resources` request.
76
pub const BRP_INSERT_RESOURCE_METHOD: &str = "world.insert_resources";
77
78
/// The method path for a `world.remove_resources` request.
79
pub const BRP_REMOVE_RESOURCE_METHOD: &str = "world.remove_resources";
80
81
/// The method path for a `world.mutate_resources` request.
82
pub const BRP_MUTATE_RESOURCE_METHOD: &str = "world.mutate_resources";
83
84
/// The method path for a `world.list_resources` request.
85
pub const BRP_LIST_RESOURCES_METHOD: &str = "world.list_resources";
86
87
/// The method path for a `world.trigger_event` request.
88
pub const BRP_TRIGGER_EVENT_METHOD: &str = "world.trigger_event";
89
90
/// The method path for a `registry.schema` request.
91
pub const BRP_REGISTRY_SCHEMA_METHOD: &str = "registry.schema";
92
93
/// The method path for a `rpc.discover` request.
94
pub const RPC_DISCOVER_METHOD: &str = "rpc.discover";
95
96
/// `world.get_components`: Retrieves one or more components from the entity with the given
97
/// ID.
98
///
99
/// The server responds with a [`BrpGetComponentsResponse`].
100
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
101
pub struct BrpGetComponentsParams {
102
/// The ID of the entity from which components are to be requested.
103
pub entity: Entity,
104
105
/// The [full paths] of the component types that are to be requested
106
/// from the entity.
107
///
108
/// Note that these strings must consist of the *full* type paths: e.g.
109
/// `bevy_transform::components::transform::Transform`, not just
110
/// `Transform`.
111
///
112
/// [full paths]: bevy_reflect::TypePath::type_path
113
pub components: Vec<String>,
114
115
/// An optional flag to fail when encountering an invalid component rather
116
/// than skipping it. Defaults to false.
117
#[serde(default)]
118
pub strict: bool,
119
}
120
121
/// `world.get_resources`: Retrieves the value of a given resource.
122
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
123
pub struct BrpGetResourcesParams {
124
/// The [full path] of the resource type being requested.
125
///
126
/// [full path]: bevy_reflect::TypePath::type_path
127
pub resource: String,
128
}
129
130
/// `world.query`: Performs a query over components in the ECS, returning entities
131
/// and component values that match.
132
///
133
/// The server responds with a [`BrpQueryResponse`].
134
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
135
pub struct BrpQueryParams {
136
/// The components to select.
137
pub data: BrpQuery,
138
139
/// An optional filter that specifies which entities to include or
140
/// exclude from the results.
141
#[serde(default)]
142
pub filter: BrpQueryFilter,
143
144
/// An optional flag to fail when encountering an invalid component rather
145
/// than skipping it. Defaults to false.
146
#[serde(default)]
147
pub strict: bool,
148
}
149
150
/// `world.spawn_entity`: Creates a new entity with the given components and responds
151
/// with its ID.
152
///
153
/// The server responds with a [`BrpSpawnEntityResponse`].
154
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
155
pub struct BrpSpawnEntityParams {
156
/// A map from each component's full path to its serialized value.
157
///
158
/// These components will be added to the entity.
159
///
160
/// Note that the keys of the map must be the [full type paths]: e.g.
161
/// `bevy_transform::components::transform::Transform`, not just
162
/// `Transform`.
163
///
164
/// [full type paths]: bevy_reflect::TypePath::type_path
165
pub components: HashMap<String, Value>,
166
}
167
168
/// `world.despawn_entity`: Given an ID, despawns the entity with that ID.
169
///
170
/// The server responds with an okay.
171
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
172
pub struct BrpDespawnEntityParams {
173
/// The ID of the entity to despawn.
174
pub entity: Entity,
175
}
176
177
/// `world.remove_components`: Deletes one or more components from an entity.
178
///
179
/// The server responds with a null.
180
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
181
pub struct BrpRemoveComponentsParams {
182
/// The ID of the entity from which components are to be removed.
183
pub entity: Entity,
184
185
/// The full paths of the component types that are to be removed from
186
/// the entity.
187
///
188
/// Note that these strings must consist of the [full type paths]: e.g.
189
/// `bevy_transform::components::transform::Transform`, not just
190
/// `Transform`.
191
///
192
/// [full type paths]: bevy_reflect::TypePath::type_path
193
pub components: Vec<String>,
194
}
195
196
/// `world.remove_resources`: Removes the given resource from the world.
197
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
198
pub struct BrpRemoveResourcesParams {
199
/// The [full path] of the resource type to remove.
200
///
201
/// [full path]: bevy_reflect::TypePath::type_path
202
pub resource: String,
203
}
204
205
/// `world.insert_components`: Adds one or more components to an entity.
206
///
207
/// The server responds with a null.
208
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
209
pub struct BrpInsertComponentsParams {
210
/// The ID of the entity that components are to be added to.
211
pub entity: Entity,
212
213
/// A map from each component's full path to its serialized value.
214
///
215
/// These components will be added to the entity.
216
///
217
/// Note that the keys of the map must be the [full type paths]: e.g.
218
/// `bevy_transform::components::transform::Transform`, not just
219
/// `Transform`.
220
///
221
/// [full type paths]: bevy_reflect::TypePath::type_path
222
pub components: HashMap<String, Value>,
223
}
224
225
/// `world.insert_resources`: Inserts a resource into the world with a given
226
/// value.
227
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
228
pub struct BrpInsertResourcesParams {
229
/// The [full path] of the resource type to insert.
230
///
231
/// [full path]: bevy_reflect::TypePath::type_path
232
pub resource: String,
233
234
/// The serialized value of the resource to be inserted.
235
pub value: Value,
236
}
237
238
/// `world.reparent_entities`: Assign a new parent to one or more entities.
239
///
240
/// The server responds with a null.
241
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
242
pub struct BrpReparentEntitiesParams {
243
/// The IDs of the entities that are to become the new children of the
244
/// `parent`.
245
pub entities: Vec<Entity>,
246
247
/// The IDs of the entity that will become the new parent of the
248
/// `entities`.
249
///
250
/// If this is `None`, then the entities are removed from all parents.
251
#[serde(default)]
252
pub parent: Option<Entity>,
253
}
254
255
/// `world.list_components`: Returns a list of all type names of registered components in the
256
/// system (no params provided), or those on an entity (params provided).
257
///
258
/// The server responds with a [`BrpListComponentsResponse`]
259
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
260
pub struct BrpListComponentsParams {
261
/// The entity to query.
262
pub entity: Entity,
263
}
264
265
/// `world.mutate_components`:
266
///
267
/// The server responds with a null.
268
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
269
pub struct BrpMutateComponentsParams {
270
/// The entity of the component to mutate.
271
pub entity: Entity,
272
273
/// The [full path] of the component to mutate.
274
///
275
/// [full path]: bevy_reflect::TypePath::type_path
276
pub component: String,
277
278
/// The [path] of the field within the component.
279
///
280
/// [path]: bevy_reflect::GetPath
281
pub path: String,
282
283
/// The value to insert at `path`.
284
pub value: Value,
285
}
286
287
/// `world.mutate_resources`:
288
///
289
/// The server responds with a null.
290
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
291
pub struct BrpMutateResourcesParams {
292
/// The [full path] of the resource to mutate.
293
///
294
/// [full path]: bevy_reflect::TypePath::type_path
295
pub resource: String,
296
297
/// The [path] of the field within the resource.
298
///
299
/// [path]: bevy_reflect::GetPath
300
pub path: String,
301
302
/// The value to insert at `path`.
303
pub value: Value,
304
}
305
306
/// `world.trigger_event`:
307
///
308
/// The server responds with a null.
309
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
310
struct BrpTriggerEventParams {
311
/// The [full path] of the event to trigger.
312
///
313
/// [full path]: bevy_reflect::TypePath::type_path
314
pub event: String,
315
/// The serialized value of the event to be triggered, if any.
316
pub value: Option<Value>,
317
}
318
319
/// Describes the data that is to be fetched in a query.
320
#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq)]
321
pub struct BrpQuery {
322
/// The [full path] of the type name of each component that is to be
323
/// fetched.
324
///
325
/// [full path]: bevy_reflect::TypePath::type_path
326
#[serde(default)]
327
pub components: Vec<String>,
328
329
/// The [full path] of the type name of each component that is to be
330
/// optionally fetched.
331
///
332
/// [full path]: bevy_reflect::TypePath::type_path
333
#[serde(default)]
334
pub option: ComponentSelector,
335
336
/// The [full path] of the type name of each component that is to be checked
337
/// for presence.
338
///
339
/// [full path]: bevy_reflect::TypePath::type_path
340
#[serde(default)]
341
pub has: Vec<String>,
342
}
343
344
/// Additional constraints that can be placed on a query to include or exclude
345
/// certain entities.
346
#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq)]
347
pub struct BrpQueryFilter {
348
/// The [full path] of the type name of each component that must not be
349
/// present on the entity for it to be included in the results.
350
///
351
/// [full path]: bevy_reflect::TypePath::type_path
352
#[serde(default)]
353
pub without: Vec<String>,
354
355
/// The [full path] of the type name of each component that must be present
356
/// on the entity for it to be included in the results.
357
///
358
/// [full path]: bevy_reflect::TypePath::type_path
359
#[serde(default)]
360
pub with: Vec<String>,
361
}
362
363
/// Constraints that can be placed on a query to include or exclude
364
/// certain definitions.
365
#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq)]
366
pub struct BrpJsonSchemaQueryFilter {
367
/// The crate name of the type name of each component that must not be
368
/// present on the entity for it to be included in the results.
369
#[serde(skip_serializing_if = "Vec::is_empty", default)]
370
pub without_crates: Vec<String>,
371
372
/// The crate name of the type name of each component that must be present
373
/// on the entity for it to be included in the results.
374
#[serde(skip_serializing_if = "Vec::is_empty", default)]
375
pub with_crates: Vec<String>,
376
377
/// Constrain resource by type
378
#[serde(default)]
379
pub type_limit: JsonSchemaTypeLimit,
380
}
381
382
/// Additional [`BrpJsonSchemaQueryFilter`] constraints that can be placed on a query to include or exclude
383
/// certain definitions.
384
#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq)]
385
pub struct JsonSchemaTypeLimit {
386
/// Schema cannot have specified reflect types
387
#[serde(skip_serializing_if = "Vec::is_empty", default)]
388
pub without: Vec<String>,
389
390
/// Schema needs to have specified reflect types
391
#[serde(skip_serializing_if = "Vec::is_empty", default)]
392
pub with: Vec<String>,
393
}
394
395
/// A response from the world to the client that specifies a single entity.
396
///
397
/// This is sent in response to `world.spawn_entity`.
398
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
399
pub struct BrpSpawnEntityResponse {
400
/// The ID of the entity in question.
401
pub entity: Entity,
402
}
403
404
/// The response to a `world.get_components` request.
405
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
406
#[serde(untagged)]
407
pub enum BrpGetComponentsResponse {
408
/// The non-strict response that reports errors separately without failing the entire request.
409
Lenient {
410
/// A map of successful components with their values.
411
components: HashMap<String, Value>,
412
/// A map of unsuccessful components with their errors.
413
errors: HashMap<String, Value>,
414
},
415
/// The strict response that will fail if any components are not present or aren't
416
/// reflect-able.
417
Strict(HashMap<String, Value>),
418
}
419
420
/// The response to a `world.get_resources` request.
421
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
422
pub struct BrpGetResourcesResponse {
423
/// The value of the requested resource.
424
pub value: Value,
425
}
426
427
/// A single response from a `world.get_components+watch` request.
428
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
429
#[serde(untagged)]
430
pub enum BrpGetComponentsWatchingResponse {
431
/// The non-strict response that reports errors separately without failing the entire request.
432
Lenient {
433
/// A map of successful components with their values that were added or changes in the last
434
/// tick.
435
components: HashMap<String, Value>,
436
/// An array of components that were been removed in the last tick.
437
removed: Vec<String>,
438
/// A map of unsuccessful components with their errors.
439
errors: HashMap<String, Value>,
440
},
441
/// The strict response that will fail if any components are not present or aren't
442
/// reflect-able.
443
Strict {
444
/// A map of successful components with their values that were added or changes in the last
445
/// tick.
446
components: HashMap<String, Value>,
447
/// An array of components that were been removed in the last tick.
448
removed: Vec<String>,
449
},
450
}
451
452
/// The response to a `world.list_components` request.
453
pub type BrpListComponentsResponse = Vec<String>;
454
455
/// The response to a `world.list_resources` request.
456
pub type BrpListResourcesResponse = Vec<String>;
457
458
/// A single response from a `world.list_components+watch` request.
459
#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
460
pub struct BrpListComponentsWatchingResponse {
461
added: Vec<String>,
462
removed: Vec<String>,
463
}
464
465
/// The response to a `world.query` request.
466
pub type BrpQueryResponse = Vec<BrpQueryRow>;
467
468
/// One query match result: a single entity paired with the requested components.
469
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
470
pub struct BrpQueryRow {
471
/// The ID of the entity that matched.
472
pub entity: Entity,
473
474
/// The serialized values of the requested components.
475
pub components: HashMap<String, Value>,
476
477
/// The boolean-only containment query results.
478
#[serde(skip_serializing_if = "HashMap::is_empty", default)]
479
pub has: HashMap<String, Value>,
480
}
481
482
/// A helper function used to parse a `serde_json::Value`.
483
pub fn parse<T: for<'de> Deserialize<'de>>(value: Value) -> Result<T, BrpError> {
484
serde_json::from_value(value).map_err(|err| BrpError {
485
code: error_codes::INVALID_PARAMS,
486
message: err.to_string(),
487
data: None,
488
})
489
}
490
491
/// A helper function used to parse a `serde_json::Value` wrapped in an `Option`.
492
pub fn parse_some<T: for<'de> Deserialize<'de>>(value: Option<Value>) -> Result<T, BrpError> {
493
match value {
494
Some(value) => parse(value),
495
None => Err(BrpError {
496
code: error_codes::INVALID_PARAMS,
497
message: String::from("Params not provided"),
498
data: None,
499
}),
500
}
501
}
502
503
/// Handles a `world.get_components` request coming from a client.
504
pub fn process_remote_get_components_request(
505
In(params): In<Option<Value>>,
506
world: &World,
507
) -> BrpResult {
508
let BrpGetComponentsParams {
509
entity,
510
components,
511
strict,
512
} = parse_some(params)?;
513
514
let app_type_registry = world.resource::<AppTypeRegistry>();
515
let type_registry = app_type_registry.read();
516
let entity_ref = get_entity(world, entity)?;
517
518
let response =
519
reflect_components_to_response(components, strict, entity, entity_ref, &type_registry)?;
520
serde_json::to_value(response).map_err(BrpError::internal)
521
}
522
523
/// Handles a `world.get_resources` request coming from a client.
524
pub fn process_remote_get_resources_request(
525
In(params): In<Option<Value>>,
526
world: &World,
527
) -> BrpResult {
528
let BrpGetResourcesParams {
529
resource: resource_path,
530
} = parse_some(params)?;
531
532
let app_type_registry = world.resource::<AppTypeRegistry>();
533
let type_registry = app_type_registry.read();
534
let reflect_resource =
535
get_reflect_resource(&type_registry, &resource_path).map_err(BrpError::resource_error)?;
536
537
let Ok(reflected) = reflect_resource.reflect(world) else {
538
return Err(BrpError::resource_not_present(&resource_path));
539
};
540
541
// Use the `ReflectSerializer` to serialize the value of the resource;
542
// this produces a map with a single item.
543
let reflect_serializer = ReflectSerializer::new(reflected.as_partial_reflect(), &type_registry);
544
let Value::Object(serialized_object) =
545
serde_json::to_value(&reflect_serializer).map_err(BrpError::resource_error)?
546
else {
547
return Err(BrpError {
548
code: error_codes::RESOURCE_ERROR,
549
message: format!("Resource `{resource_path}` could not be serialized"),
550
data: None,
551
});
552
};
553
554
// Get the single value out of the map.
555
let value = serialized_object.into_values().next().ok_or_else(|| {
556
BrpError::internal(anyhow!("Unexpected format of serialized resource value"))
557
})?;
558
let response = BrpGetResourcesResponse { value };
559
serde_json::to_value(response).map_err(BrpError::internal)
560
}
561
562
/// Handles a `world.get_components+watch` request coming from a client.
563
pub fn process_remote_get_components_watching_request(
564
In(params): In<Option<Value>>,
565
world: &World,
566
mut removal_cursors: Local<HashMap<ComponentId, MessageCursor<RemovedComponentEntity>>>,
567
) -> BrpResult<Option<Value>> {
568
let BrpGetComponentsParams {
569
entity,
570
components,
571
strict,
572
} = parse_some(params)?;
573
574
let app_type_registry = world.resource::<AppTypeRegistry>();
575
let type_registry = app_type_registry.read();
576
let entity_ref = get_entity(world, entity)?;
577
578
let mut changed = Vec::new();
579
let mut removed = Vec::new();
580
let mut errors = <HashMap<_, _>>::default();
581
582
'component_loop: for component_path in components {
583
let Ok(type_registration) =
584
get_component_type_registration(&type_registry, &component_path)
585
else {
586
let err =
587
BrpError::component_error(format!("Unknown component type: `{component_path}`"));
588
if strict {
589
return Err(err);
590
}
591
errors.insert(
592
component_path,
593
serde_json::to_value(err).map_err(BrpError::internal)?,
594
);
595
continue;
596
};
597
let Some(component_id) = world.components().get_valid_id(type_registration.type_id())
598
else {
599
let err = BrpError::component_error(format!("Unknown component: `{component_path}`"));
600
if strict {
601
return Err(err);
602
}
603
errors.insert(
604
component_path,
605
serde_json::to_value(err).map_err(BrpError::internal)?,
606
);
607
continue;
608
};
609
610
if let Some(ticks) = entity_ref.get_change_ticks_by_id(component_id)
611
&& ticks.is_changed(world.last_change_tick(), world.read_change_tick())
612
{
613
changed.push(component_path);
614
continue;
615
};
616
617
let Some(events) = world.removed_components().get(component_id) else {
618
continue;
619
};
620
let cursor = removal_cursors
621
.entry(component_id)
622
.or_insert_with(|| events.get_cursor());
623
for event in cursor.read(events) {
624
if Entity::from(event.clone()) == entity {
625
removed.push(component_path);
626
continue 'component_loop;
627
}
628
}
629
}
630
631
if changed.is_empty() && removed.is_empty() {
632
return Ok(None);
633
}
634
635
let response =
636
reflect_components_to_response(changed, strict, entity, entity_ref, &type_registry)?;
637
638
let response = match response {
639
BrpGetComponentsResponse::Lenient {
640
components,
641
errors: mut errs,
642
} => BrpGetComponentsWatchingResponse::Lenient {
643
components,
644
removed,
645
errors: {
646
errs.extend(errors);
647
errs
648
},
649
},
650
BrpGetComponentsResponse::Strict(components) => BrpGetComponentsWatchingResponse::Strict {
651
components,
652
removed,
653
},
654
};
655
656
Ok(Some(
657
serde_json::to_value(response).map_err(BrpError::internal)?,
658
))
659
}
660
661
/// Reflect a list of components on an entity into a [`BrpGetComponentsResponse`].
662
fn reflect_components_to_response(
663
components: Vec<String>,
664
strict: bool,
665
entity: Entity,
666
entity_ref: EntityRef,
667
type_registry: &TypeRegistry,
668
) -> BrpResult<BrpGetComponentsResponse> {
669
let mut response = if strict {
670
BrpGetComponentsResponse::Strict(Default::default())
671
} else {
672
BrpGetComponentsResponse::Lenient {
673
components: Default::default(),
674
errors: Default::default(),
675
}
676
};
677
678
for component_path in components {
679
match reflect_component(&component_path, entity, entity_ref, type_registry) {
680
Ok(serialized_object) => match response {
681
BrpGetComponentsResponse::Strict(ref mut components)
682
| BrpGetComponentsResponse::Lenient {
683
ref mut components, ..
684
} => {
685
components.extend(serialized_object.into_iter());
686
}
687
},
688
Err(err) => match response {
689
BrpGetComponentsResponse::Strict(_) => return Err(err),
690
BrpGetComponentsResponse::Lenient { ref mut errors, .. } => {
691
let err_value = serde_json::to_value(err).map_err(BrpError::internal)?;
692
errors.insert(component_path, err_value);
693
}
694
},
695
}
696
}
697
698
Ok(response)
699
}
700
701
/// Reflect a single component on an entity with the given component path.
702
fn reflect_component(
703
component_path: &str,
704
entity: Entity,
705
entity_ref: EntityRef,
706
type_registry: &TypeRegistry,
707
) -> BrpResult<Map<String, Value>> {
708
let reflect_component =
709
get_reflect_component(type_registry, component_path).map_err(BrpError::component_error)?;
710
711
// Retrieve the reflected value for the given specified component on the given entity.
712
let Some(reflected) = reflect_component.reflect(entity_ref) else {
713
return Err(BrpError::component_not_present(component_path, entity));
714
};
715
716
// Each component value serializes to a map with a single entry.
717
let reflect_serializer = ReflectSerializer::new(reflected.as_partial_reflect(), type_registry);
718
let Value::Object(serialized_object) =
719
serde_json::to_value(&reflect_serializer).map_err(BrpError::component_error)?
720
else {
721
return Err(BrpError {
722
code: error_codes::COMPONENT_ERROR,
723
message: format!("Component `{component_path}` could not be serialized"),
724
data: None,
725
});
726
};
727
728
Ok(serialized_object)
729
}
730
731
/// A selector for components in a query.
732
///
733
/// This can either be a list of component paths or an "all" selector that
734
/// indicates that all components should be selected.
735
/// The "all" selector is useful when you want to retrieve all components
736
/// present on an entity without specifying each one individually.
737
/// The paths in the `Paths` variant must be the [full type paths]: e.g.
738
/// `bevy_transform::components::transform::Transform`, not just
739
/// `Transform`.
740
///
741
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
742
#[serde(rename_all = "snake_case")]
743
pub enum ComponentSelector {
744
/// An "all" selector that indicates all components should be selected.
745
All,
746
/// A list of component paths to select as optional components.
747
#[serde(untagged)]
748
Paths(Vec<String>),
749
}
750
751
impl Default for ComponentSelector {
752
fn default() -> Self {
753
Self::Paths(Vec::default())
754
}
755
}
756
757
/// Handles a `world.query` request coming from a client.
758
pub fn process_remote_query_request(In(params): In<Option<Value>>, world: &mut World) -> BrpResult {
759
let BrpQueryParams {
760
data: BrpQuery {
761
components,
762
option,
763
has,
764
},
765
filter,
766
strict,
767
} = match params {
768
Some(params) => parse_some(Some(params))?,
769
None => BrpQueryParams {
770
data: BrpQuery {
771
components: Vec::new(),
772
option: ComponentSelector::default(),
773
has: Vec::new(),
774
},
775
filter: BrpQueryFilter::default(),
776
strict: false,
777
},
778
};
779
780
let app_type_registry = world.resource::<AppTypeRegistry>().clone();
781
let type_registry = app_type_registry.read();
782
783
// Required components: must be present
784
let (required, unregistered_in_required) =
785
get_component_ids(&type_registry, world, components.clone(), strict)
786
.map_err(BrpError::component_error)?;
787
788
// Optional components: Option<&T> or all reflectable if "all"
789
let (optional, _) = match &option {
790
ComponentSelector::Paths(paths) => {
791
get_component_ids(&type_registry, world, paths.clone(), strict)
792
.map_err(BrpError::component_error)?
793
}
794
ComponentSelector::All => (Vec::new(), Vec::new()),
795
};
796
797
// Has components: presence check
798
let (has_ids, unregistered_in_has) =
799
get_component_ids(&type_registry, world, has, strict).map_err(BrpError::component_error)?;
800
801
// Filters
802
let (without, _) = get_component_ids(&type_registry, world, filter.without.clone(), strict)
803
.map_err(BrpError::component_error)?;
804
let (with, unregistered_in_with) =
805
get_component_ids(&type_registry, world, filter.with.clone(), strict)
806
.map_err(BrpError::component_error)?;
807
808
// When "strict" is false:
809
// - Unregistered components in "option" and "without" are ignored.
810
// - Unregistered components in "has" are considered absent from the entity.
811
// - Unregistered components in "components" and "with" result in an empty
812
// response since they specify hard requirements.
813
// If strict, fail if any required or with components are unregistered
814
if !unregistered_in_required.is_empty() || !unregistered_in_with.is_empty() {
815
return serde_json::to_value(BrpQueryResponse::default()).map_err(BrpError::internal);
816
}
817
818
let mut query = QueryBuilder::<FilteredEntityRef>::new(world);
819
for (_, component) in &required {
820
query.ref_id(*component);
821
}
822
for (_, option) in &optional {
823
query.optional(|query| {
824
query.ref_id(*option);
825
});
826
}
827
for (_, has) in &has_ids {
828
query.optional(|query| {
829
query.ref_id(*has);
830
});
831
}
832
for (_, without) in without {
833
query.without_id(without);
834
}
835
for (_, with) in with {
836
query.with_id(with);
837
}
838
839
// Prepare has reflect info
840
let has_paths_and_reflect_components: Vec<(&str, &ReflectComponent)> = has_ids
841
.iter()
842
.map(|(type_id, _)| reflect_component_from_id(*type_id, &type_registry))
843
.collect::<AnyhowResult<Vec<(&str, &ReflectComponent)>>>()
844
.map_err(BrpError::component_error)?;
845
846
let mut response = BrpQueryResponse::default();
847
let mut query = query.build();
848
849
for row in query.iter(world) {
850
let entity_id = row.id();
851
let entity_ref = world.get_entity(entity_id).expect("Entity should exist");
852
853
// Required components
854
let mut components_map = serialize_components(
855
entity_ref,
856
&type_registry,
857
required
858
.iter()
859
.map(|(type_id, component_id)| (*type_id, Some(*component_id))),
860
);
861
862
// Optional components
863
match &option {
864
ComponentSelector::All => {
865
// Add all reflectable components present on the entity (as Option<&T>)
866
let all_optionals =
867
entity_ref
868
.archetype()
869
.components()
870
.iter()
871
.filter_map(|&component_id| {
872
let info = world.components().get_info(component_id)?;
873
let type_id = info.type_id()?;
874
// Skip required components (already included)
875
if required.iter().any(|(_, cid)| cid == &component_id) {
876
return None;
877
}
878
Some((type_id, Some(component_id)))
879
});
880
components_map.extend(serialize_components(
881
entity_ref,
882
&type_registry,
883
all_optionals,
884
));
885
}
886
ComponentSelector::Paths(_) => {
887
// Add only the requested optional components (as Option<&T>)
888
let optionals = optional.iter().filter(|(_, component_id)| {
889
// Skip required components (already included)
890
!required.iter().any(|(_, cid)| cid == component_id)
891
});
892
components_map.extend(serialize_components(
893
entity_ref,
894
&type_registry,
895
optionals
896
.clone()
897
.map(|(type_id, component_id)| (*type_id, Some(*component_id))),
898
));
899
}
900
}
901
902
// The map of boolean-valued component presences:
903
let has_map = build_has_map(
904
row,
905
has_paths_and_reflect_components.iter().copied(),
906
&unregistered_in_has,
907
);
908
909
let query_row = BrpQueryRow {
910
entity: row.id(),
911
components: components_map,
912
has: has_map,
913
};
914
915
response.push(query_row);
916
}
917
918
serde_json::to_value(response).map_err(BrpError::internal)
919
}
920
921
/// Serializes the specified components for an entity.
922
/// The iterator yields ([`TypeId`], Option<[`ComponentId`]>).
923
fn serialize_components(
924
entity_ref: EntityRef,
925
type_registry: &TypeRegistry,
926
components: impl Iterator<Item = (TypeId, Option<ComponentId>)>,
927
) -> HashMap<String, Value> {
928
let mut components_map = HashMap::new();
929
for (type_id, component_id_opt) in components {
930
let Some(type_registration) = type_registry.get(type_id) else {
931
continue;
932
};
933
if let Some(reflect_component) = type_registration.data::<ReflectComponent>() {
934
// If a component_id is provided, check if the entity has it
935
if let Some(component_id) = component_id_opt
936
&& !entity_ref.contains_id(component_id)
937
{
938
continue;
939
}
940
if let Some(reflected) = reflect_component.reflect(entity_ref) {
941
let reflect_serializer =
942
ReflectSerializer::new(reflected.as_partial_reflect(), type_registry);
943
if let Ok(Value::Object(obj)) = serde_json::to_value(&reflect_serializer) {
944
components_map.extend(obj);
945
} else {
946
warn_once!(
947
"Failed to serialize component `{}` for entity {:?}",
948
type_registration.type_info().type_path(),
949
entity_ref.id()
950
);
951
}
952
}
953
}
954
}
955
components_map
956
}
957
958
/// Handles a `world.spawn_entity` request coming from a client.
959
pub fn process_remote_spawn_entity_request(
960
In(params): In<Option<Value>>,
961
world: &mut World,
962
) -> BrpResult {
963
let BrpSpawnEntityParams { components } = parse_some(params)?;
964
965
let app_type_registry = world.resource::<AppTypeRegistry>().clone();
966
let type_registry = app_type_registry.read();
967
968
let reflect_components =
969
deserialize_components(&type_registry, components).map_err(BrpError::component_error)?;
970
971
let entity = world.spawn_empty();
972
let entity_id = entity.id();
973
insert_reflected_components(entity, reflect_components).map_err(BrpError::component_error)?;
974
975
let response = BrpSpawnEntityResponse { entity: entity_id };
976
serde_json::to_value(response).map_err(BrpError::internal)
977
}
978
979
/// Handles a `rpc.discover` request coming from a client.
980
pub fn process_remote_list_methods_request(
981
In(_params): In<Option<Value>>,
982
world: &mut World,
983
) -> BrpResult {
984
let remote_methods = world.resource::<crate::RemoteMethods>();
985
986
#[cfg(all(feature = "http", not(target_family = "wasm")))]
987
let servers = match (
988
world.get_resource::<crate::http::HostAddress>(),
989
world.get_resource::<crate::http::HostPort>(),
990
) {
991
(Some(url), Some(port)) => Some(vec![ServerObject {
992
name: "Server".to_owned(),
993
url: format!("{}:{}", url.0, port.0),
994
..default()
995
}]),
996
(Some(url), None) => Some(vec![ServerObject {
997
name: "Server".to_owned(),
998
url: url.0.to_string(),
999
..default()
1000
}]),
1001
_ => None,
1002
};
1003
1004
#[cfg(any(not(feature = "http"), target_family = "wasm"))]
1005
let servers = None;
1006
1007
let doc = OpenRpcDocument {
1008
info: Default::default(),
1009
methods: remote_methods.into(),
1010
openrpc: "1.3.2".to_owned(),
1011
servers,
1012
};
1013
1014
serde_json::to_value(doc).map_err(BrpError::internal)
1015
}
1016
1017
/// Handles a `world.insert_components` request (insert components) coming from a client.
1018
pub fn process_remote_insert_components_request(
1019
In(params): In<Option<Value>>,
1020
world: &mut World,
1021
) -> BrpResult {
1022
let BrpInsertComponentsParams { entity, components } = parse_some(params)?;
1023
1024
let app_type_registry = world.resource::<AppTypeRegistry>().clone();
1025
let type_registry = app_type_registry.read();
1026
1027
let reflect_components =
1028
deserialize_components(&type_registry, components).map_err(BrpError::component_error)?;
1029
1030
insert_reflected_components(get_entity_mut(world, entity)?, reflect_components)
1031
.map_err(BrpError::component_error)?;
1032
1033
Ok(Value::Null)
1034
}
1035
1036
/// Handles a `world.insert_resources` request coming from a client.
1037
pub fn process_remote_insert_resources_request(
1038
In(params): In<Option<Value>>,
1039
world: &mut World,
1040
) -> BrpResult {
1041
let BrpInsertResourcesParams {
1042
resource: resource_path,
1043
value,
1044
} = parse_some(params)?;
1045
1046
let app_type_registry = world.resource::<AppTypeRegistry>().clone();
1047
let type_registry = app_type_registry.read();
1048
1049
let reflected_resource = deserialize_resource(&type_registry, &resource_path, value)
1050
.map_err(BrpError::resource_error)?;
1051
1052
let reflect_resource =
1053
get_reflect_resource(&type_registry, &resource_path).map_err(BrpError::resource_error)?;
1054
reflect_resource.insert(world, &*reflected_resource, &type_registry);
1055
1056
Ok(Value::Null)
1057
}
1058
1059
/// Handles a `world.mutate_components` request coming from a client.
1060
///
1061
/// This method allows you to mutate a single field inside an Entity's
1062
/// component.
1063
pub fn process_remote_mutate_components_request(
1064
In(params): In<Option<Value>>,
1065
world: &mut World,
1066
) -> BrpResult {
1067
let BrpMutateComponentsParams {
1068
entity,
1069
component,
1070
path,
1071
value,
1072
} = parse_some(params)?;
1073
let app_type_registry = world.resource::<AppTypeRegistry>().clone();
1074
let type_registry = app_type_registry.read();
1075
1076
// Get the fully-qualified type names of the component to be mutated.
1077
let component_type: &TypeRegistration = type_registry
1078
.get_with_type_path(&component)
1079
.ok_or_else(|| {
1080
BrpError::component_error(anyhow!("Unknown component type: `{}`", component))
1081
})?;
1082
1083
// Get the reflected representation of the component.
1084
let mut reflected = component_type
1085
.data::<ReflectComponent>()
1086
.ok_or_else(|| {
1087
BrpError::component_error(anyhow!("Component `{}` isn't registered", component))
1088
})?
1089
.reflect_mut(world.entity_mut(entity))
1090
.ok_or_else(|| {
1091
BrpError::component_error(anyhow!("Cannot reflect component `{}`", component))
1092
})?;
1093
1094
// Get the type of the field in the component that is to be
1095
// mutated.
1096
let value_type: &TypeRegistration = type_registry
1097
.get_with_type_path(
1098
reflected
1099
.reflect_path(path.as_str())
1100
.map_err(BrpError::component_error)?
1101
.reflect_type_path(),
1102
)
1103
.ok_or_else(|| {
1104
BrpError::component_error(anyhow!("Unknown component field type: `{}`", component))
1105
})?;
1106
1107
// Get the reflected representation of the value to be inserted
1108
// into the component.
1109
let value: Box<dyn PartialReflect> = TypedReflectDeserializer::new(value_type, &type_registry)
1110
.deserialize(&value)
1111
.map_err(BrpError::component_error)?;
1112
1113
// Apply the mutation.
1114
reflected
1115
.reflect_path_mut(path.as_str())
1116
.map_err(BrpError::component_error)?
1117
.try_apply(value.as_ref())
1118
.map_err(BrpError::component_error)?;
1119
1120
Ok(Value::Null)
1121
}
1122
1123
/// Handles a `world.mutate_resources` request coming from a client.
1124
pub fn process_remote_mutate_resources_request(
1125
In(params): In<Option<Value>>,
1126
world: &mut World,
1127
) -> BrpResult {
1128
let BrpMutateResourcesParams {
1129
resource: resource_path,
1130
path: field_path,
1131
value,
1132
} = parse_some(params)?;
1133
1134
let app_type_registry = world.resource::<AppTypeRegistry>().clone();
1135
let type_registry = app_type_registry.read();
1136
1137
// Get the `ReflectResource` for the given resource path.
1138
let reflect_resource =
1139
get_reflect_resource(&type_registry, &resource_path).map_err(BrpError::resource_error)?;
1140
1141
// Get the actual resource value from the world as a `dyn Reflect`.
1142
let mut reflected_resource = reflect_resource
1143
.reflect_mut(world)
1144
.map_err(|_| BrpError::resource_not_present(&resource_path))?;
1145
1146
// Get the type registration for the field with the given path.
1147
let value_registration = type_registry
1148
.get_with_type_path(
1149
reflected_resource
1150
.reflect_path(field_path.as_str())
1151
.map_err(BrpError::resource_error)?
1152
.reflect_type_path(),
1153
)
1154
.ok_or_else(|| {
1155
BrpError::resource_error(anyhow!("Unknown resource field type: `{}`", resource_path))
1156
})?;
1157
1158
// Use the field's type registration to deserialize the given value.
1159
let deserialized_value: Box<dyn PartialReflect> =
1160
TypedReflectDeserializer::new(value_registration, &type_registry)
1161
.deserialize(&value)
1162
.map_err(BrpError::resource_error)?;
1163
1164
// Apply the value to the resource.
1165
reflected_resource
1166
.reflect_path_mut(field_path.as_str())
1167
.map_err(BrpError::resource_error)?
1168
.try_apply(&*deserialized_value)
1169
.map_err(BrpError::resource_error)?;
1170
1171
Ok(Value::Null)
1172
}
1173
1174
/// Handles a `world.remove_components` request (remove components) coming from a client.
1175
pub fn process_remote_remove_components_request(
1176
In(params): In<Option<Value>>,
1177
world: &mut World,
1178
) -> BrpResult {
1179
let BrpRemoveComponentsParams { entity, components } = parse_some(params)?;
1180
1181
let app_type_registry = world.resource::<AppTypeRegistry>().clone();
1182
let type_registry = app_type_registry.read();
1183
1184
let component_ids = get_component_ids(&type_registry, world, components, true)
1185
.and_then(|(registered, unregistered)| {
1186
if unregistered.is_empty() {
1187
Ok(registered)
1188
} else {
1189
Err(anyhow!("Unregistered component types: {:?}", unregistered))
1190
}
1191
})
1192
.map_err(BrpError::component_error)?;
1193
1194
// Remove the components.
1195
let mut entity_world_mut = get_entity_mut(world, entity)?;
1196
for (_, component_id) in component_ids.iter() {
1197
entity_world_mut.remove_by_id(*component_id);
1198
}
1199
1200
Ok(Value::Null)
1201
}
1202
1203
/// Handles a `world.remove_resources` request coming from a client.
1204
pub fn process_remote_remove_resources_request(
1205
In(params): In<Option<Value>>,
1206
world: &mut World,
1207
) -> BrpResult {
1208
let BrpRemoveResourcesParams {
1209
resource: resource_path,
1210
} = parse_some(params)?;
1211
1212
let app_type_registry = world.resource::<AppTypeRegistry>().clone();
1213
let type_registry = app_type_registry.read();
1214
1215
let reflect_resource =
1216
get_reflect_resource(&type_registry, &resource_path).map_err(BrpError::resource_error)?;
1217
reflect_resource.remove(world);
1218
1219
Ok(Value::Null)
1220
}
1221
1222
/// Handles a `world.despawn_entity` (despawn entity) request coming from a client.
1223
pub fn process_remote_despawn_entity_request(
1224
In(params): In<Option<Value>>,
1225
world: &mut World,
1226
) -> BrpResult {
1227
let BrpDespawnEntityParams { entity } = parse_some(params)?;
1228
1229
get_entity_mut(world, entity)?.despawn();
1230
1231
Ok(Value::Null)
1232
}
1233
1234
/// Handles a `world.reparent_entities` request coming from a client.
1235
pub fn process_remote_reparent_entities_request(
1236
In(params): In<Option<Value>>,
1237
world: &mut World,
1238
) -> BrpResult {
1239
let BrpReparentEntitiesParams {
1240
entities,
1241
parent: maybe_parent,
1242
} = parse_some(params)?;
1243
1244
// If `Some`, reparent the entities.
1245
if let Some(parent) = maybe_parent {
1246
let mut parent_commands =
1247
get_entity_mut(world, parent).map_err(|_| BrpError::entity_not_found(parent))?;
1248
for entity in entities {
1249
if entity == parent {
1250
return Err(BrpError::self_reparent(entity));
1251
}
1252
parent_commands.add_child(entity);
1253
}
1254
}
1255
// If `None`, remove the entities' parents.
1256
else {
1257
for entity in entities {
1258
get_entity_mut(world, entity)?.remove::<ChildOf>();
1259
}
1260
}
1261
1262
Ok(Value::Null)
1263
}
1264
1265
/// Handles a `world.list_components` request (list all components) coming from a client.
1266
pub fn process_remote_list_components_request(
1267
In(params): In<Option<Value>>,
1268
world: &World,
1269
) -> BrpResult {
1270
let app_type_registry = world.resource::<AppTypeRegistry>();
1271
let type_registry = app_type_registry.read();
1272
1273
let mut response = BrpListComponentsResponse::default();
1274
1275
// If `Some`, return all components of the provided entity.
1276
if let Some(BrpListComponentsParams { entity }) = params.map(parse).transpose()? {
1277
let entity = get_entity(world, entity)?;
1278
for &component_id in entity.archetype().components().iter() {
1279
let Some(component_info) = world.components().get_info(component_id) else {
1280
continue;
1281
};
1282
response.push(component_info.name().to_string());
1283
}
1284
}
1285
// If `None`, list all registered components.
1286
else {
1287
for registered_type in type_registry.iter() {
1288
if registered_type.data::<ReflectComponent>().is_some() {
1289
response.push(registered_type.type_info().type_path().to_owned());
1290
}
1291
}
1292
}
1293
1294
// Sort both for cleanliness and to reduce the risk that clients start
1295
// accidentally depending on the order.
1296
response.sort();
1297
1298
serde_json::to_value(response).map_err(BrpError::internal)
1299
}
1300
1301
/// Handles a `world.list_resources` request coming from a client.
1302
pub fn process_remote_list_resources_request(
1303
In(_params): In<Option<Value>>,
1304
world: &World,
1305
) -> BrpResult {
1306
let mut response = BrpListResourcesResponse::default();
1307
1308
let app_type_registry = world.resource::<AppTypeRegistry>();
1309
let type_registry = app_type_registry.read();
1310
1311
for registered_type in type_registry.iter() {
1312
if registered_type.data::<ReflectResource>().is_some() {
1313
response.push(registered_type.type_info().type_path().to_owned());
1314
}
1315
}
1316
1317
response.sort();
1318
1319
serde_json::to_value(response).map_err(BrpError::internal)
1320
}
1321
1322
/// Handles a `world.list_components+watch` request coming from a client.
1323
pub fn process_remote_list_components_watching_request(
1324
In(params): In<Option<Value>>,
1325
world: &World,
1326
mut removal_cursors: Local<HashMap<ComponentId, MessageCursor<RemovedComponentEntity>>>,
1327
) -> BrpResult<Option<Value>> {
1328
let BrpListComponentsParams { entity } = parse_some(params)?;
1329
let entity_ref = get_entity(world, entity)?;
1330
let mut response = BrpListComponentsWatchingResponse::default();
1331
1332
for &component_id in entity_ref.archetype().components().iter() {
1333
let ticks = entity_ref
1334
.get_change_ticks_by_id(component_id)
1335
.ok_or(BrpError::internal("Failed to get ticks"))?;
1336
1337
if ticks.is_added(world.last_change_tick(), world.read_change_tick()) {
1338
let Some(component_info) = world.components().get_info(component_id) else {
1339
continue;
1340
};
1341
response.added.push(component_info.name().to_string());
1342
}
1343
}
1344
1345
for (component_id, events) in world.removed_components().iter() {
1346
let cursor = removal_cursors
1347
.entry(*component_id)
1348
.or_insert_with(|| events.get_cursor());
1349
for event in cursor.read(events) {
1350
if Entity::from(event.clone()) == entity {
1351
let Some(component_info) = world.components().get_info(*component_id) else {
1352
continue;
1353
};
1354
response.removed.push(component_info.name().to_string());
1355
}
1356
}
1357
}
1358
1359
if response.added.is_empty() && response.removed.is_empty() {
1360
Ok(None)
1361
} else {
1362
Ok(Some(
1363
serde_json::to_value(response).map_err(BrpError::internal)?,
1364
))
1365
}
1366
}
1367
1368
/// Handles a `world.trigger_event` request coming from a client.
1369
pub fn process_remote_trigger_event_request(
1370
In(params): In<Option<Value>>,
1371
world: &mut World,
1372
) -> BrpResult {
1373
let BrpTriggerEventParams { event, value } = parse_some(params)?;
1374
1375
world.resource_scope(|world, registry: Mut<AppTypeRegistry>| {
1376
let registry = registry.read();
1377
1378
let Some(registration) = registry.get_with_type_path(&event) else {
1379
return Err(BrpError::resource_error(format!(
1380
"Unknown event type: `{event}`"
1381
)));
1382
};
1383
let Some(reflect_event) = registration.data::<ReflectEvent>() else {
1384
return Err(BrpError::resource_error(format!(
1385
"Event `{event}` is not reflectable"
1386
)));
1387
};
1388
1389
if let Some(payload) = value {
1390
let payload: Box<dyn PartialReflect> =
1391
TypedReflectDeserializer::new(registration, &registry)
1392
.deserialize(payload.into_deserializer())
1393
.map_err(|err| {
1394
BrpError::resource_error(format!("{event} is invalid: {err}"))
1395
})?;
1396
reflect_event.trigger(world, &*payload, &registry);
1397
} else {
1398
let payload = DynamicStruct::default();
1399
reflect_event.trigger(world, &payload, &registry);
1400
}
1401
1402
Ok(Value::Null)
1403
})
1404
}
1405
1406
/// Handles a `registry.schema` request (list all registry types in form of schema) coming from a client.
1407
pub fn export_registry_types(In(params): In<Option<Value>>, world: &World) -> BrpResult {
1408
let filter: BrpJsonSchemaQueryFilter = match params {
1409
None => Default::default(),
1410
Some(params) => parse(params)?,
1411
};
1412
1413
let extra_info = world.resource::<crate::schemas::SchemaTypesMetadata>();
1414
let types = world.resource::<AppTypeRegistry>();
1415
let types = types.read();
1416
let schemas = types
1417
.iter()
1418
.filter_map(|type_reg| {
1419
let path_table = type_reg.type_info().type_path_table();
1420
if let Some(crate_name) = &path_table.crate_name() {
1421
if !filter.with_crates.is_empty()
1422
&& !filter.with_crates.iter().any(|c| crate_name.eq(c))
1423
{
1424
return None;
1425
}
1426
if !filter.without_crates.is_empty()
1427
&& filter.without_crates.iter().any(|c| crate_name.eq(c))
1428
{
1429
return None;
1430
}
1431
}
1432
let (id, schema) = export_type(type_reg, extra_info);
1433
1434
if !filter.type_limit.with.is_empty()
1435
&& !filter
1436
.type_limit
1437
.with
1438
.iter()
1439
.any(|c| schema.reflect_types.iter().any(|cc| c.eq(cc)))
1440
{
1441
return None;
1442
}
1443
if !filter.type_limit.without.is_empty()
1444
&& filter
1445
.type_limit
1446
.without
1447
.iter()
1448
.any(|c| schema.reflect_types.iter().any(|cc| c.eq(cc)))
1449
{
1450
return None;
1451
}
1452
Some((id.to_string(), schema))
1453
})
1454
.collect::<HashMap<String, JsonSchemaBevyType>>();
1455
1456
serde_json::to_value(schemas).map_err(BrpError::internal)
1457
}
1458
1459
/// Immutably retrieves an entity from the [`World`], returning an error if the
1460
/// entity isn't present.
1461
fn get_entity(world: &World, entity: Entity) -> Result<EntityRef<'_>, BrpError> {
1462
world
1463
.get_entity(entity)
1464
.map_err(|_| BrpError::entity_not_found(entity))
1465
}
1466
1467
/// Mutably retrieves an entity from the [`World`], returning an error if the
1468
/// entity isn't present.
1469
fn get_entity_mut(world: &mut World, entity: Entity) -> Result<EntityWorldMut<'_>, BrpError> {
1470
world
1471
.get_entity_mut(entity)
1472
.map_err(|_| BrpError::entity_not_found(entity))
1473
}
1474
1475
/// Given components full path, returns a tuple that contains
1476
/// - A list of corresponding [`TypeId`] and [`ComponentId`] for registered components.
1477
/// - A list of unregistered component paths.
1478
///
1479
/// Note that the supplied path names must be *full* path names: e.g.
1480
/// `bevy_transform::components::transform::Transform` instead of `Transform`.
1481
fn get_component_ids(
1482
type_registry: &TypeRegistry,
1483
world: &World,
1484
component_paths: Vec<String>,
1485
strict: bool,
1486
) -> AnyhowResult<(Vec<(TypeId, ComponentId)>, Vec<String>)> {
1487
let mut component_ids = vec![];
1488
let mut unregistered_components = vec![];
1489
1490
for component_path in component_paths {
1491
let maybe_component_tuple = get_component_type_registration(type_registry, &component_path)
1492
.ok()
1493
.and_then(|type_registration| {
1494
let type_id = type_registration.type_id();
1495
world
1496
.components()
1497
.get_valid_id(type_id)
1498
.map(|component_id| (type_id, component_id))
1499
});
1500
if let Some((type_id, component_id)) = maybe_component_tuple {
1501
component_ids.push((type_id, component_id));
1502
} else if strict {
1503
return Err(anyhow!(
1504
"Component `{}` isn't registered or used in the world",
1505
component_path
1506
));
1507
} else {
1508
unregistered_components.push(component_path);
1509
}
1510
}
1511
1512
Ok((component_ids, unregistered_components))
1513
}
1514
1515
/// Given an entity (`entity_ref`),
1516
/// a list of reflected component information (`paths_and_reflect_components`)
1517
/// and a list of unregistered components,
1518
/// return a map which associates each component to a boolean value indicating
1519
/// whether or not that component is present on the entity.
1520
/// Unregistered components are considered absent from the entity.
1521
fn build_has_map<'a>(
1522
entity_ref: FilteredEntityRef,
1523
paths_and_reflect_components: impl Iterator<Item = (&'a str, &'a ReflectComponent)>,
1524
unregistered_components: &[String],
1525
) -> HashMap<String, Value> {
1526
let mut has_map = <HashMap<_, _>>::default();
1527
1528
for (type_path, reflect_component) in paths_and_reflect_components {
1529
let has = reflect_component.contains(entity_ref);
1530
has_map.insert(type_path.to_owned(), Value::Bool(has));
1531
}
1532
unregistered_components.iter().for_each(|component| {
1533
has_map.insert(component.to_owned(), Value::Bool(false));
1534
});
1535
1536
has_map
1537
}
1538
1539
/// Given a component ID, return the associated [type path] and `ReflectComponent` if possible.
1540
///
1541
/// The `ReflectComponent` part is the meat of this; the type path is only used for error messages.
1542
///
1543
/// [type path]: bevy_reflect::TypePath::type_path
1544
fn reflect_component_from_id(
1545
component_type_id: TypeId,
1546
type_registry: &TypeRegistry,
1547
) -> AnyhowResult<(&str, &ReflectComponent)> {
1548
let Some(type_registration) = type_registry.get(component_type_id) else {
1549
return Err(anyhow!(
1550
"Component `{:?}` isn't registered",
1551
component_type_id
1552
));
1553
};
1554
1555
let type_path = type_registration.type_info().type_path();
1556
1557
let Some(reflect_component) = type_registration.data::<ReflectComponent>() else {
1558
return Err(anyhow!("Component `{}` isn't reflectable", type_path));
1559
};
1560
1561
Ok((type_path, reflect_component))
1562
}
1563
1564
/// Given a collection of component paths and their associated serialized values (`components`),
1565
/// return the associated collection of deserialized reflected values.
1566
fn deserialize_components(
1567
type_registry: &TypeRegistry,
1568
components: HashMap<String, Value>,
1569
) -> AnyhowResult<Vec<Box<dyn PartialReflect>>> {
1570
let mut reflect_components = vec![];
1571
1572
for (component_path, component) in components {
1573
let Some(component_type) = type_registry.get_with_type_path(&component_path) else {
1574
return Err(anyhow!("Unknown component type: `{}`", component_path));
1575
};
1576
let reflected: Box<dyn PartialReflect> =
1577
TypedReflectDeserializer::new(component_type, type_registry)
1578
.deserialize(&component)
1579
.map_err(|err| anyhow!("{component_path} is invalid: {err}"))?;
1580
reflect_components.push(reflected);
1581
}
1582
1583
Ok(reflect_components)
1584
}
1585
1586
/// Given a resource path and an associated serialized value (`value`), return the
1587
/// deserialized value.
1588
fn deserialize_resource(
1589
type_registry: &TypeRegistry,
1590
resource_path: &str,
1591
value: Value,
1592
) -> AnyhowResult<Box<dyn PartialReflect>> {
1593
let Some(resource_type) = type_registry.get_with_type_path(resource_path) else {
1594
return Err(anyhow!("Unknown resource type: `{}`", resource_path));
1595
};
1596
let reflected: Box<dyn PartialReflect> =
1597
TypedReflectDeserializer::new(resource_type, type_registry)
1598
.deserialize(&value)
1599
.map_err(|err| anyhow!("{resource_path} is invalid: {err}"))?;
1600
Ok(reflected)
1601
}
1602
1603
/// Given a collection `reflect_components` of reflected component values, insert them into
1604
/// the given entity (`entity_world_mut`).
1605
fn insert_reflected_components(
1606
mut entity_world_mut: EntityWorldMut,
1607
reflect_components: Vec<Box<dyn PartialReflect>>,
1608
) -> AnyhowResult<()> {
1609
for reflected in reflect_components {
1610
entity_world_mut.insert_reflect(reflected);
1611
}
1612
1613
Ok(())
1614
}
1615
1616
/// Given a component's type path, return the associated [`ReflectComponent`] from the given
1617
/// `type_registry` if possible.
1618
fn get_reflect_component<'r>(
1619
type_registry: &'r TypeRegistry,
1620
component_path: &str,
1621
) -> AnyhowResult<&'r ReflectComponent> {
1622
let component_registration = get_component_type_registration(type_registry, component_path)?;
1623
1624
component_registration
1625
.data::<ReflectComponent>()
1626
.ok_or_else(|| anyhow!("Component `{}` isn't reflectable", component_path))
1627
}
1628
1629
/// Given a component's type path, return the associated [`TypeRegistration`] from the given
1630
/// `type_registry` if possible.
1631
fn get_component_type_registration<'r>(
1632
type_registry: &'r TypeRegistry,
1633
component_path: &str,
1634
) -> AnyhowResult<&'r TypeRegistration> {
1635
type_registry
1636
.get_with_type_path(component_path)
1637
.ok_or_else(|| anyhow!("Unknown component type: `{}`", component_path))
1638
}
1639
1640
/// Given a resource's type path, return the associated [`ReflectResource`] from the given
1641
/// `type_registry` if possible.
1642
fn get_reflect_resource<'r>(
1643
type_registry: &'r TypeRegistry,
1644
resource_path: &str,
1645
) -> AnyhowResult<&'r ReflectResource> {
1646
let resource_registration = get_resource_type_registration(type_registry, resource_path)?;
1647
1648
resource_registration
1649
.data::<ReflectResource>()
1650
.ok_or_else(|| anyhow!("Resource `{}` isn't reflectable", resource_path))
1651
}
1652
1653
/// Given a resource's type path, return the associated [`TypeRegistration`] from the given
1654
/// `type_registry` if possible.
1655
fn get_resource_type_registration<'r>(
1656
type_registry: &'r TypeRegistry,
1657
resource_path: &str,
1658
) -> AnyhowResult<&'r TypeRegistration> {
1659
type_registry
1660
.get_with_type_path(resource_path)
1661
.ok_or_else(|| anyhow!("Unknown resource type: `{}`", resource_path))
1662
}
1663
1664
#[cfg(test)]
1665
mod tests {
1666
/// A generic function that tests serialization and deserialization of any type
1667
/// implementing Serialize and Deserialize traits.
1668
fn test_serialize_deserialize<T>(value: T)
1669
where
1670
T: Serialize + for<'a> Deserialize<'a> + PartialEq + core::fmt::Debug,
1671
{
1672
// Serialize the value to JSON string
1673
let serialized = serde_json::to_string(&value).expect("Failed to serialize");
1674
1675
// Deserialize the JSON string back into the original type
1676
let deserialized: T = serde_json::from_str(&serialized).expect("Failed to deserialize");
1677
1678
// Assert that the deserialized value is the same as the original
1679
assert_eq!(
1680
&value, &deserialized,
1681
"Deserialized value does not match original"
1682
);
1683
}
1684
1685
use super::*;
1686
use bevy_ecs::{
1687
component::Component, event::Event, observer::On, resource::Resource, system::ResMut,
1688
};
1689
use bevy_reflect::Reflect;
1690
use serde_json::Value::Null;
1691
1692
#[test]
1693
fn insert_reflect_only_component() {
1694
#[derive(Reflect, Component)]
1695
#[reflect(Component)]
1696
struct Player {
1697
name: String,
1698
health: u32,
1699
}
1700
let components: HashMap<String, Value> = [(
1701
String::from("bevy_remote::builtin_methods::tests::Player"),
1702
serde_json::json!({"name": "John", "health": 50}),
1703
)]
1704
.into();
1705
let atr = AppTypeRegistry::default();
1706
{
1707
let mut register = atr.write();
1708
register.register::<Player>();
1709
}
1710
let deserialized_components = {
1711
let type_reg = atr.read();
1712
deserialize_components(&type_reg, components).expect("FAIL")
1713
};
1714
let mut world = World::new();
1715
world.insert_resource(atr);
1716
let e = world.spawn_empty();
1717
insert_reflected_components(e, deserialized_components).expect("FAIL");
1718
}
1719
1720
#[test]
1721
fn trigger_reflect_only_event() {
1722
#[derive(Event, Reflect)]
1723
#[reflect(Event)]
1724
struct Pass;
1725
1726
#[derive(Resource)]
1727
struct TestResult(pub bool);
1728
1729
let atr = AppTypeRegistry::default();
1730
{
1731
let mut register = atr.write();
1732
register.register::<Pass>();
1733
}
1734
let mut world = World::new();
1735
world.add_observer(move |_event: On<Pass>, mut result: ResMut<TestResult>| result.0 = true);
1736
world.insert_resource(TestResult(false));
1737
world.insert_resource(atr);
1738
1739
let params = serde_json::to_value(&BrpTriggerEventParams {
1740
event: "bevy_remote::builtin_methods::tests::Pass".to_owned(),
1741
value: None,
1742
})
1743
.expect("FAIL");
1744
assert_eq!(
1745
process_remote_trigger_event_request(In(Some(params)), &mut world),
1746
Ok(Null)
1747
);
1748
assert!(world.resource::<TestResult>().0);
1749
}
1750
1751
#[test]
1752
fn serialization_tests() {
1753
test_serialize_deserialize(BrpQueryRow {
1754
components: Default::default(),
1755
entity: Entity::from_raw_u32(0).unwrap(),
1756
has: Default::default(),
1757
});
1758
test_serialize_deserialize(BrpListComponentsWatchingResponse::default());
1759
test_serialize_deserialize(BrpQuery::default());
1760
test_serialize_deserialize(BrpJsonSchemaQueryFilter::default());
1761
test_serialize_deserialize(BrpJsonSchemaQueryFilter {
1762
type_limit: JsonSchemaTypeLimit {
1763
with: vec!["Resource".to_owned()],
1764
..Default::default()
1765
},
1766
..Default::default()
1767
});
1768
test_serialize_deserialize(BrpListComponentsParams {
1769
entity: Entity::from_raw_u32(0).unwrap(),
1770
});
1771
}
1772
}
1773
1774