Skip to content

Commit a6a989c

Browse files
authored
feat: ✨ Dynamic Script Components, register_new_component binding, remove_component no longer requires ReflectComponent data (#379)
# Summary In order to support dynamic script components we need: - to relax some requirements on having a `ReflectComponent` registration to allow component operations - add a script component registry, which stores metadata about each dynamically registered component - Add `ScriptValue` to `into_script_ref` and `from_script_ref` implementations to allow any value to be inserted into the components payload The nice thing about this implementation is it *just* works with the reference system. The current problems are that the global type cache will not reflect this script component, to fix that we'd need a mechanism for "re-computing" certain globals for scripts. This also does not allow setting a schema on these, which is handy but also footgunny As a side effect I've also made `remove_component` no longer require the `ReflectComponent` registration
1 parent e1d17f6 commit a6a989c

File tree

23 files changed

+464
-129
lines changed

23 files changed

+464
-129
lines changed

.github/workflows/mdbook.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,15 @@ on:
99
- 'docs/**'
1010
- 'crates/xtask/**'
1111
- '.github/workflows/mdbook.yml'
12+
- 'crates/bevy_mod_scripting_functions/**'
1213
pull_request:
1314
branches:
1415
- "**"
1516
paths:
1617
- 'docs/**'
1718
- 'crates/xtask/**'
1819
- '.github/workflows/mdbook.yml'
20+
- 'crates/bevy_mod_scripting_functions/**'
1921

2022
jobs:
2123

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
local NewComponent = world.register_new_component("ScriptComponentA")
2+
local entity = world.spawn()
3+
4+
assert(world.has_component(entity, NewComponent) == false, "Entity should not have component")
5+
world.add_default_component(entity, NewComponent)
6+
assert(world.has_component(entity, NewComponent) == true, "Entity should have component")
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
let NewComponent = world.register_new_component.call("ScriptComponentA");
2+
let entity = world.spawn_.call();
3+
4+
assert(world.has_component.call(entity, NewComponent) == false, "Entity should not have component");
5+
world.add_default_component.call(entity, NewComponent);
6+
assert(world.has_component.call(entity, NewComponent) == true, "Entity should have component");
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("ScriptComponentA")
2+
assert(NewComponent ~= nil, "Failed to register new component")
3+
assert(NewComponent:short_name() == "DynamicComponent", "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: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
function on_test()
2+
local NewComponent = world.register_new_component("ScriptComponentA")
3+
4+
local new_entity = world.spawn()
5+
world.insert_component(new_entity, NewComponent, construct(types.DynamicComponent, {
6+
data = "Hello World"
7+
}))
8+
9+
local component_instance = world.get_component(new_entity, NewComponent)
10+
assert(component_instance.data == "Hello World", "unexpected value: " .. component_instance.data)
11+
12+
component_instance.data = {
13+
foo = "bar"
14+
}
15+
16+
assert(component_instance.data.foo == "bar", "unexpected value: " .. component_instance.data.foo)
17+
end
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
local NewComponent = world.register_new_component("ScriptComponentA")
2+
local new_entity = world.spawn()
3+
world.add_default_component(new_entity, NewComponent)
4+
5+
local component_instance = world.get_component(new_entity, NewComponent)
6+
assert(component_instance ~= nil, "unexpected value: " .. tostring(component_instance.data))
7+
8+
world.remove_component(new_entity, NewComponent)
9+
local component_instance = world.get_component(new_entity, NewComponent)
10+
11+
assert(component_instance == nil, "unexpected value: " .. tostring(component_instance))
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
let NewComponent = world.register_new_component.call("ScriptComponentA");
2+
let new_entity = world.spawn_.call();
3+
world.add_default_component.call(new_entity, NewComponent);
4+
5+
let component_instance = world.get_component.call(new_entity, NewComponent);
6+
assert(type_of(component_instance) != "()", "unexpected value: " + component_instance.data);
7+
8+
world.remove_component.call(new_entity, NewComponent);
9+
let component_instance_after = world.get_component.call(new_entity, NewComponent);
10+
11+
assert(type_of(component_instance_after) == "()", "unexpected value: " + component_instance_after);
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
2+
local entity = world._get_entity_with_test_component("CompWithDefault")
3+
local component = world.get_type_by_name("CompWithDefault")
4+
world.remove_component(entity, component)
5+
assert(world.has_component(entity, component) == false, "Component was not removed")

assets/tests/remove_component/no_component_data_errors.rhai renamed to assets/tests/remove_component/no_component_data.rhai

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,5 @@
22
let entity = world._get_entity_with_test_component.call("CompWithDefault");
33
let component = world.get_type_by_name.call("CompWithDefault");
44

5-
assert_throws(||{
6-
world.remove_component.call(entity, component);
7-
}, "Missing type data ReflectComponent for type: .*CompWithDefault.*")
5+
world.remove_component.call(entity, component);
6+
assert(world.has_component.call(entity, component) == false, "Component was not removed");

assets/tests/remove_component/no_component_data_errors.lua

Lines changed: 0 additions & 7 deletions
This file was deleted.

0 commit comments

Comments
 (0)