Skip to content

Commit 2165c43

Browse files
committed
get something working
1 parent 6bfdbeb commit 2165c43

File tree

7 files changed

+149
-34
lines changed

7 files changed

+149
-34
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
local NewComponent = world.register_new_component("NewComponent")
2+
assert(NewComponent ~= nil, "Failed to register new component")
3+
assert(NewComponent:short_name() == "ScriptComponent", "Unexpected component type")
4+
5+
6+
local new_entity = world.spawn()
7+
8+
world.add_default_component(new_entity, NewComponent)
9+
10+
local component_intance = world.get_component(new_entity, NewComponent)
11+
12+
assert(component_intance ~= nil, "Failed to get component instance")
Lines changed: 67 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,23 @@
11
//! Everything necessary to support scripts registering their own components
22
3-
use std::{alloc::Layout, mem::needs_drop, sync::Arc};
4-
3+
use super::{ScriptComponentRegistration, ScriptTypeRegistration, ScriptValue, WorldAccessGuard};
4+
use crate::error::InteropError;
55
use bevy::{
6+
app::{App, Plugin},
67
ecs::{
78
component::{Component, ComponentDescriptor, StorageType},
89
reflect::ReflectComponent,
10+
system::Resource,
911
},
10-
reflect::{GetTypeRegistration, Reflect, TypeRegistration},
12+
reflect::{prelude::ReflectDefault, GetTypeRegistration, Reflect},
1113
utils::HashMap,
1214
};
1315
use parking_lot::RwLock;
14-
15-
use crate::error::InteropError;
16-
17-
use super::{ScriptComponentRegistration, ScriptTypeRegistration, ScriptValue, WorldGuard};
16+
use std::{alloc::Layout, mem::needs_drop, sync::Arc};
1817

1918
/// A dynamic script component, with script set
20-
#[derive(Reflect, Clone)]
21-
#[reflect(Component)]
19+
#[derive(Reflect, Clone, Default)]
20+
#[reflect(Component, Default)]
2221
pub struct ScriptComponent {
2322
data: ScriptValue,
2423
}
@@ -36,20 +35,40 @@ impl Component for ScriptComponent {
3635
}
3736

3837
/// A registry of dynamically registered script components
38+
#[derive(Clone, Resource, Default)]
3939
pub struct AppScriptComponentRegistry(pub Arc<RwLock<ScriptComponentRegistry>>);
4040

41+
impl AppScriptComponentRegistry {
42+
/// Reads the underlying registry
43+
pub fn read(&self) -> parking_lot::RwLockReadGuard<ScriptComponentRegistry> {
44+
self.0.read()
45+
}
46+
47+
/// Writes to the underlying registry
48+
pub fn write(&self) -> parking_lot::RwLockWriteGuard<ScriptComponentRegistry> {
49+
self.0.write()
50+
}
51+
}
52+
53+
#[derive(Default)]
54+
/// A registry of dynamically registered script components
4155
pub struct ScriptComponentRegistry {
4256
components: HashMap<String, ScriptComponentInfo>,
4357
}
4458

45-
impl WorldGuard<'_> {
46-
pub fn script_component_registry(&self) -> AppScriptComponentRegistry {
47-
let component_registry =
48-
self.with_resource(|app_component_registry: &AppScriptComponentRegistry| {
49-
app_component_registry.clone()
50-
})?;
59+
impl ScriptComponentRegistry {
60+
/// Registers a dynamic script component, possibly overwriting an existing one
61+
pub fn register(&mut self, info: ScriptComponentInfo) {
62+
self.components.insert(info.name.clone(), info);
63+
}
64+
65+
/// Gets a dynamic script component by name
66+
pub fn get(&self, name: &str) -> Option<&ScriptComponentInfo> {
67+
self.components.get(name)
5168
}
69+
}
5270

71+
impl WorldAccessGuard<'_> {
5372
/// Registers a dynamic script component, and returns a reference to its registration
5473
pub fn register_script_component(
5574
&self,
@@ -59,8 +78,9 @@ impl WorldGuard<'_> {
5978
let descriptor = unsafe {
6079
// Safety: same safety guarantees as ComponentDescriptor::new
6180
// we know the type in advance
81+
// we only use this method to name the component
6282
ComponentDescriptor::new_with_layout(
63-
component_name,
83+
component_name.clone(),
6484
ScriptComponent::STORAGE_TYPE,
6585
Layout::new::<ScriptComponent>(),
6686
needs_drop::<ScriptComponent>().then_some(|x| x.drop_as::<ScriptComponent>()),
@@ -69,17 +89,41 @@ impl WorldGuard<'_> {
6989
w.register_component_with_descriptor(descriptor)
7090
})?;
7191

72-
// we need to register this as a type in the type registry with this name so its retrievable as any other type
73-
let type_registry = self.type_registry();
74-
let mut type_registry = type_registry.write();
92+
let component_registry = self.component_registry();
93+
let mut component_registry = component_registry.write();
7594

76-
// TODO: we should probably retrieve this from the registry, but I don't see what people would want to register on this type
77-
// in addition to the existing registrations.
78-
Ok(ScriptComponentRegistration::new(
95+
let registration = ScriptComponentRegistration::new(
7996
ScriptTypeRegistration::new(Arc::new(
8097
<ScriptComponent as GetTypeRegistration>::get_type_registration(),
8198
)),
8299
component_id,
83-
))
100+
);
101+
102+
bevy::log::debug!(
103+
"Registering dynamic script component: {}, component id assigned: {:?}",
104+
component_name,
105+
component_id
106+
);
107+
108+
let component_info = ScriptComponentInfo {
109+
name: component_name.clone(),
110+
registration: registration.clone(),
111+
};
112+
113+
component_registry.register(component_info);
114+
115+
// TODO: we should probably retrieve this from the registry, but I don't see what people would want to register on this type
116+
// in addition to the existing registrations.
117+
Ok(registration)
118+
}
119+
}
120+
121+
/// A plugin to support dynamic script components
122+
pub(crate) struct DynamicScriptComponentPlugin;
123+
124+
impl Plugin for DynamicScriptComponentPlugin {
125+
fn build(&self, app: &mut App) {
126+
app.init_resource::<AppScriptComponentRegistry>()
127+
.register_type::<ScriptComponent>();
84128
}
85129
}

crates/bevy_mod_scripting_core/src/bindings/script_system.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ use super::{
55
function::{from::Val, into::IntoScript, script_function::AppScriptFunctionRegistry},
66
schedule::AppScheduleRegistry,
77
script_value::ScriptValue,
8-
AppReflectAllocator, ReflectBaseType, ReflectReference, ScriptQueryBuilder, ScriptQueryResult,
9-
ScriptResourceRegistration, WorldAccessGuard, WorldGuard,
8+
AppReflectAllocator, AppScriptComponentRegistry, ReflectBaseType, ReflectReference,
9+
ScriptQueryBuilder, ScriptQueryResult, ScriptResourceRegistration, WorldAccessGuard,
10+
WorldGuard,
1011
};
1112
use crate::{
1213
bindings::pretty_print::DisplayWithWorld,
@@ -288,6 +289,7 @@ struct ScriptSystemState {
288289
type_registry: AppTypeRegistry,
289290
function_registry: AppScriptFunctionRegistry,
290291
schedule_registry: AppScheduleRegistry,
292+
component_registry: AppScriptComponentRegistry,
291293
allocator: AppReflectAllocator,
292294
subset: HashSet<ReflectAccessId>,
293295
callback_label: CallbackLabel,
@@ -424,6 +426,7 @@ impl<P: IntoScriptPluginParams> System for DynamicScriptSystem<P> {
424426
state.allocator.clone(),
425427
state.function_registry.clone(),
426428
state.schedule_registry.clone(),
429+
state.component_registry.clone(),
427430
)
428431
};
429432

@@ -577,6 +580,9 @@ impl<P: IntoScriptPluginParams> System for DynamicScriptSystem<P> {
577580
.clone(),
578581
schedule_registry: world.get_resource_or_init::<AppScheduleRegistry>().clone(),
579582
allocator: world.get_resource_or_init::<AppReflectAllocator>().clone(),
583+
component_registry: world
584+
.get_resource_or_init::<AppScriptComponentRegistry>()
585+
.clone(),
580586
subset,
581587
callback_label: self.name.to_string().into(),
582588
system_params,

crates/bevy_mod_scripting_core/src/bindings/script_value.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@ use super::{
1313

1414
/// An abstraction of values that can be passed to and from scripts.
1515
/// This allows us to re-use logic between scripting languages.
16-
#[derive(Debug, Clone, PartialEq, Reflect)]
16+
#[derive(Debug, Clone, PartialEq, Reflect, Default)]
1717
#[reflect(opaque)]
1818
pub enum ScriptValue {
1919
/// Represents the absence of a value.
20+
#[default]
2021
Unit,
2122
/// Represents a boolean value.
2223
Bool(bool),

crates/bevy_mod_scripting_core/src/bindings/world.rs

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ impl<'w> WorldAccessGuard<'w> {
171171
allocator: AppReflectAllocator,
172172
function_registry: AppScriptFunctionRegistry,
173173
schedule_registry: AppScheduleRegistry,
174+
script_component_registry: AppScriptComponentRegistry,
174175
) -> Self {
175176
Self {
176177
inner: Rc::new(WorldAccessGuardInner {
@@ -184,6 +185,7 @@ impl<'w> WorldAccessGuard<'w> {
184185
allocator,
185186
function_registry,
186187
schedule_registry,
188+
script_component_registry,
187189
}),
188190
invalid: Rc::new(false.into()),
189191
}
@@ -206,6 +208,10 @@ impl<'w> WorldAccessGuard<'w> {
206208
.get_resource_or_init::<AppScriptFunctionRegistry>()
207209
.clone();
208210

211+
let script_component_registry = world
212+
.get_resource_or_init::<AppScriptComponentRegistry>()
213+
.clone();
214+
209215
let schedule_registry = world.get_resource_or_init::<AppScheduleRegistry>().clone();
210216
Self {
211217
inner: Rc::new(WorldAccessGuardInner {
@@ -215,6 +221,7 @@ impl<'w> WorldAccessGuard<'w> {
215221
type_registry,
216222
function_registry,
217223
schedule_registry,
224+
script_component_registry,
218225
}),
219226
invalid: Rc::new(false.into()),
220227
}
@@ -330,6 +337,11 @@ impl<'w> WorldAccessGuard<'w> {
330337
self.inner.schedule_registry.clone()
331338
}
332339

340+
/// Returns the component registry for the world
341+
pub fn component_registry(&self) -> AppScriptComponentRegistry {
342+
self.inner.script_component_registry.clone()
343+
}
344+
333345
/// Returns the script allocator for the world
334346
pub fn allocator(&self) -> AppReflectAllocator {
335347
self.inner.allocator.clone()
@@ -813,12 +825,12 @@ impl WorldAccessGuard<'_> {
813825
}
814826

815827
/// get a type registration for the type, without checking if it's a component or resource
816-
pub fn get_type_by_name(&self, type_name: String) -> Option<ScriptTypeRegistration> {
828+
pub fn get_type_by_name(&self, type_name: &str) -> Option<ScriptTypeRegistration> {
817829
let type_registry = self.type_registry();
818830
let type_registry = type_registry.read();
819831
type_registry
820-
.get_with_short_type_path(&type_name)
821-
.or_else(|| type_registry.get_with_type_path(&type_name))
832+
.get_with_short_type_path(type_name)
833+
.or_else(|| type_registry.get_with_type_path(type_name))
822834
.map(|registration| ScriptTypeRegistration::new(Arc::new(registration.clone())))
823835
}
824836

@@ -864,10 +876,17 @@ impl WorldAccessGuard<'_> {
864876
>,
865877
InteropError,
866878
> {
867-
let val = self.get_type_by_name(type_name);
879+
let val = self.get_type_by_name(&type_name);
868880
Ok(match val {
869881
Some(registration) => Some(self.get_type_registration(registration)?),
870-
None => None,
882+
None => {
883+
// try the component registry
884+
let components = self.component_registry();
885+
let components = components.read();
886+
components
887+
.get(&type_name)
888+
.map(|c| Union::new_right(Union::new_left(c.registration.clone())))
889+
}
871890
})
872891
}
873892

@@ -921,20 +940,25 @@ impl WorldAccessGuard<'_> {
921940
)
922941
})?;
923942

943+
bevy::log::debug!("found component data");
944+
924945
// we look for ReflectDefault or ReflectFromWorld data then a ReflectComponent data
925946
let instance = if let Some(default_td) = registration
926947
.type_registration()
927948
.type_registration()
928949
.data::<ReflectDefault>()
929950
{
951+
bevy::log::debug!("found default data");
930952
default_td.default()
931953
} else if let Some(from_world_td) = registration
932954
.type_registration()
933955
.type_registration()
934956
.data::<ReflectFromWorld>()
935957
{
958+
bevy::log::debug!("found reflect from world");
936959
self.with_global_access(|world| from_world_td.from_world(world))?
937960
} else {
961+
bevy::log::debug!("found neither");
938962
return Err(InteropError::missing_type_data(
939963
registration.registration.type_id(),
940964
"ReflectDefault or ReflectFromWorld".to_owned(),
@@ -950,6 +974,7 @@ impl WorldAccessGuard<'_> {
950974
.map_err(|_| InteropError::missing_entity(entity))?;
951975
{
952976
let registry = type_registry.read();
977+
bevy::log::debug!("inserting component instance using component data");
953978
component_data.insert(&mut entity, instance.as_partial_reflect(), &registry);
954979
}
955980
Ok(())
@@ -1001,12 +1026,23 @@ impl WorldAccessGuard<'_> {
10011026
.get_entity(entity)
10021027
.ok_or_else(|| InteropError::missing_entity(entity))?;
10031028

1029+
bevy::log::debug!("Retrieving component with component id: {:?}", component_id);
1030+
10041031
let component_info = cell
10051032
.components()
10061033
.get_info(component_id)
10071034
.ok_or_else(|| InteropError::invalid_component(component_id))?;
10081035

1009-
if entity.contains_id(component_id) {
1036+
bevy::log::debug!(
1037+
"Retrieved component with component info: {:?}",
1038+
component_info
1039+
);
1040+
1041+
if entity.contains_id(component_id)
1042+
|| component_info
1043+
.type_id()
1044+
.is_some_and(|t| entity.contains_type_id(t))
1045+
{
10101046
Ok(Some(ReflectReference {
10111047
base: ReflectBaseType {
10121048
type_id: component_info.type_id().ok_or_else(|| {

crates/bevy_mod_scripting_core/src/lib.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ use bindings::{
1414
globals::{core::CoreScriptGlobalsPlugin, AppScriptGlobalsRegistry},
1515
schedule::AppScheduleRegistry,
1616
script_value::ScriptValue,
17-
AppReflectAllocator, ReflectAllocator, ReflectReference, ScriptTypeRegistration,
17+
AppReflectAllocator, DynamicScriptComponentPlugin, ReflectAllocator, ReflectReference,
18+
ScriptTypeRegistration,
1819
};
1920
use commands::{AddStaticScript, RemoveStaticScript};
2021
use context::{
@@ -312,7 +313,7 @@ fn once_per_app_init(app: &mut App) {
312313
((garbage_collector).in_set(ScriptingSystemSet::GarbageCollection),),
313314
);
314315

315-
app.add_plugins(CoreScriptGlobalsPlugin);
316+
app.add_plugins((CoreScriptGlobalsPlugin, DynamicScriptComponentPlugin));
316317

317318
configure_asset_systems(app);
318319
}

crates/bevy_mod_scripting_functions/src/core.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,21 @@ impl World {
445445
let world = ctxt.world()?;
446446
world.exit()
447447
}
448+
449+
/// Registers a new component type with the world.
450+
/// Arguments:
451+
/// * `ctxt`: The function call context.
452+
/// * `name`: The name of the component type.
453+
/// Returns:
454+
/// * `registration`: The registration of the new component type if successful.
455+
fn register_new_component(
456+
ctxt: FunctionCallContext,
457+
name: String,
458+
) -> Result<Val<ScriptComponentRegistration>, InteropError> {
459+
profiling::function_scope!("register_new_component");
460+
let world = ctxt.world()?;
461+
world.register_script_component(name).map(Val)
462+
}
448463
}
449464

450465
#[script_bindings(

0 commit comments

Comments
 (0)