Skip to content

Commit 1f329aa

Browse files
committed
update more examples
1 parent debaa16 commit 1f329aa

File tree

14 files changed

+324
-464
lines changed

14 files changed

+324
-464
lines changed

assets/scripts/bevy_api.lua

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
function table_to_string(t)
2+
local result = "["
3+
for k,v in pairs(t) do
4+
result = result .. string.format("%s:%s,",k,v)
5+
end
6+
return result .. "]"
7+
end
8+
9+
10+
-- the api provides us with 3 globals
11+
print(entity)
12+
print(script)
13+
print(world)
14+
15+
-- we first retrieve ID's for our component and resource by their short name (long name/full path also work)
16+
local my_component_type = world:get_type_by_name("MyComponent")
17+
18+
-- then ask the world to give us a reference to `MyComponent` on the entity we just spawned
19+
-- resources work the same way, but we use `get_resource` instead of `get_component`
20+
-- the comp object is resolved to a `bevy_script_api::script_ref::ReflectValue` which implements UserData.
21+
-- we can use a custom proxy instead (by implementing LuaProxyable), but this is the simplest way to get started.
22+
local comp = world:get_component(entity, my_component_type)
23+
print("Before script: ", comp)
24+
25+
print("============")
26+
27+
-- the index metamethod on ReflectValue's uses bevy's reflection mechanism on top of some custom sub-reflection logic to
28+
-- allow reflecting inside Options, Vectors etc.
29+
-- when we index into ReflectValue's we either get back a custom proxy or another ReflectValue
30+
31+
-- the LuaBevyAPIProvider provides us custom proxies for many bevy types as well as std types.
32+
-- all of these implementations can be overridden via the bevy TypeRegistry
33+
comp.usize = 2
34+
print("comp.usize after assigning to 2: ", comp.usize)
35+
36+
-- vec's and matrices have custom __index and __newindex overrides
37+
print("comp.vec2 before: ", comp.vec2)
38+
comp.vec2[1] = 69
39+
print("comp.vec2 after: ", comp.vec2)
40+
41+
-- Option's get converted to nil or the value inside
42+
print("comp.option_vec3 before: ", comp.option_vec3)
43+
comp.option_vec3 = Vec3.new(2,1,3)
44+
print("comp.option_vec3 after: ", comp.option_vec3)
45+
46+
-- reflection via index is indexed starting at 1, unlike in Rust to match Lua's indexing
47+
print("comp.option_vec3[1] before: ", comp.option_vec3[1])
48+
comp.option_vec3[1] = 5
49+
print("comp.option_vec3[1] after: ", comp.option_vec3[1])
50+
51+
print("============")
52+
53+
-- Vec<T> references get converted to a custom proxy `LuaVec<T>` which is
54+
-- also assignable via lua tables
55+
56+
print("comp.vec_of_option_bools before: ", table_to_string(comp.vec_of_option_bools))
57+
comp.vec_of_option_bools = {true,false,true}
58+
print("comp.vec_of_option_bools after assignment: ", table_to_string(comp.vec_of_option_bools))
59+
60+
print("comp.vec_of_option_bools[1] before: ", comp.vec_of_option_bools[1])
61+
comp.vec_of_option_bools[1] = false
62+
print("comp.vec_of_option_bools[1] after: ", comp.vec_of_option_bools[1])
63+
64+
-- there are some additional methods available on LuaVec proxies imitating the Vec<T> api
65+
print("comp.vec_of_option_bools before insert: ", table_to_string(comp.vec_of_option_bools))
66+
comp.vec_of_option_bools:insert(1,nil)
67+
print("comp.vec_of_option_bools after insert: ", table_to_string(comp.vec_of_option_bools))
68+
69+
print("comp.vec_of_option_bools before push: ", table_to_string(comp.vec_of_option_bools))
70+
comp.vec_of_option_bools:push(false)
71+
print("comp.vec_of_option_bools after push: ", table_to_string(comp.vec_of_option_bools))
72+
73+
print("comp.vec_of_option_bools len after push: ", #comp.vec_of_option_bools)
74+
75+
print("comp.vec_of_option_bools before pop: ", table_to_string(comp.vec_of_option_bools))
76+
print(comp.vec_of_option_bools:pop())
77+
print("comp.vec_of_option_bools after pop: ", table_to_string(comp.vec_of_option_bools))
78+
79+
print("the pairs inside comp.vec_of_option_bools: ")
80+
for k,v in pairs(comp.vec_of_option_bools) do
81+
print(string.format(" - %s:%s",k,v))
82+
end
83+
84+
comp.vec_of_option_bools:clear()
85+
print("comp.vec_of_option_bools after clear: ", table_to_string(comp.vec_of_option_bools))
86+
87+
print("comp.vec_of_option_bools len after clear: ", #comp.vec_of_option_bools)
88+
print("============")
89+
90+
print("comp.option_vec_of_bools before: ", table_to_string(comp.option_vec_of_bools))
91+
print(comp.option_vec_of_bools:pop())
92+
print("comp.option_vec_of_bools after pop: ", table_to_string(comp.option_vec_of_bools))
93+
94+
95+
print("comp.option_vec_of_bools len after pop: ", #comp.option_vec_of_bools)
96+
97+
print("the pairs inside comp.option_vec_of_bools: ")
98+
for k,v in pairs(comp.option_vec_of_bools) do
99+
print(string.format(" - %s:%s",k,v))
100+
end
101+
102+
print("============")
103+
104+
local complex_vec_op = Vec3.new(0,1,0):any_orthonormal_vector() + comp.mat3.x_axis
105+
print("(0,1,0).any_orthonormal_vector() + mat3.x_axis is: ", complex_vec_op)
106+
107+
local new_mat3 = Mat3.from_cols(Vec3.new(1,0,0),Vec3.new(0,1,0),Vec3.new(0,0,-1))
108+
print("new_mat3 is:", new_mat3)
109+
110+
comp.vec2 = comp.vec2 + comp.vec2
111+
comp.usize = comp.vec2:min_element()
112+
comp.f32 = comp.f32 + comp.f32 + comp.vec2:min_element()
113+
comp.vec2 = Vec2.new(2,1)
114+
comp.quat = Quat.from_xyzw(3,2,1,4)
115+
comp.mat3.x_axis = Vec3.new(69,69,69)
116+
117+
print("============")
118+
119+
-- this is an example of something impossible to achieve with plain bevy reflection under the hood
120+
comp.mat3[1][1] = 42
121+
122+
-- now let's retrieve these again to see if we actually changed their values permanently
123+
comp = world:get_component(entity,my_component_type)
124+
125+
print("After script:")
126+
print(comp)
127+
128+
world:exit()

assets/scripts/coroutines.lua

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
local my_routine;
22

33
function on_update()
4-
54
if my_routine == nil then
65
my_routine = coroutine.create(function()
76
local starttime = os.time()
@@ -18,7 +17,7 @@ function on_update()
1817
coroutine.resume(my_routine)
1918
else
2019
print("Couroutine has finished, no longer running")
20+
world:exit()
2121
end
2222
end
23-
2423
end

assets/scripts/dynamic_queries.lua

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,23 @@
1-
function on_event()
2-
local component_a = world:get_type_by_name("ComponentA")
3-
local component_b = world:get_type_by_name("ComponentB")
4-
local component_c = world:get_type_by_name("ComponentC")
5-
6-
for entity, _ in world:query(component_a):with(component_b):without(component_c):iter() do
7-
print(entity)
8-
end
1+
local component_a = world:get_type_by_name("ComponentA")
2+
local component_b = world:get_type_by_name("ComponentB")
3+
local component_c = world:get_type_by_name("ComponentC")
4+
5+
print("Querying for entities with component_a and without component_c")
6+
for entity, c in world:query(component_a):without(component_c):iter() do
7+
print("Entity with index: " .. entity:index() .. " component: " .. tostring(c))
8+
end
9+
10+
print("Querying for entities with component_b and without component_a")
11+
for entity, c in world:query(component_b):without(component_a):iter() do
12+
print("Entity with index: " .. entity:index() .. " component: " .. tostring(c))
913
end
14+
15+
print("Querying for all components at once")
16+
for entity, c1,c2,c3 in world:query(component_a, component_b, component_c):iter() do
17+
print("Entity with index: " .. entity:index())
18+
print("\tComponentA: " .. tostring(c1))
19+
print("\tComponentB: " .. tostring(c2))
20+
print("\tComponentC: " .. tostring(c3))
21+
end
22+
23+
world:exit()

crates/bevy_mod_scripting_core/src/bindings/reference.rs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,16 @@ pub struct ReflectReference {
5757
}
5858

5959

60-
6160
impl ReflectReference {
6261

62+
pub fn print_with_world(&self, world: &WorldAccessGuard) -> String {
63+
let base = world.with_resource(|_, type_registry: Mut<AppTypeRegistry>| {
64+
self.base.display_with_type_name(&type_registry.read())
65+
});
66+
67+
format!("Reference(base: {}, path: {:?})", base, self.reflect_path)
68+
}
69+
6370
/// If this is a reference to something with a length accessible via reflection, returns that length.
6471
pub fn len(&self, world: &WorldAccessGuard) -> Option<usize> {
6572
world.with_resource(|world, type_registry: Mut<AppTypeRegistry>| {
@@ -371,11 +378,15 @@ impl ReflectBaseType {
371378
}
372379

373380
pub fn display_with_type_name(&self, type_registry: &TypeRegistry) -> String {
374-
format!(
375-
"ReflectBase({}, {:?})",
376-
Self::type_name(self.type_id, type_registry),
377-
self.base_id
378-
)
381+
let type_name = Self::type_name(self.type_id, type_registry);
382+
383+
let kind = match self.base_id {
384+
ReflectBase::Component(entity, _) => "Component",
385+
ReflectBase::Resource(_) => "Resource",
386+
ReflectBase::Owned(id) => "Allocation",
387+
};
388+
389+
format!("{}({})", kind, type_name)
379390
}
380391
}
381392

crates/bevy_mod_scripting_core/src/bindings/world.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use std::{
1818
};
1919

2020
use bevy::{
21+
app::AppExit,
2122
ecs::{
2223
component::{Component, ComponentId},
2324
entity::Entity,
@@ -234,6 +235,11 @@ impl WorldCallbackAccess {
234235
let world = self.read().unwrap_or_else(|| panic!("{STALE_WORLD_MSG}"));
235236
world.despawn_descendants(entity)
236237
}
238+
239+
pub fn exit(&self) {
240+
let world = self.read().unwrap_or_else(|| panic!("{STALE_WORLD_MSG}"));
241+
world.exit()
242+
}
237243
}
238244

239245
/// Unit of world access
@@ -973,6 +979,15 @@ impl<'w> WorldAccessGuard<'w> {
973979

974980
Ok(())
975981
}
982+
983+
/// Sends AppExit event to the world with success status
984+
pub fn exit(&self) {
985+
if let Some(world) = self.get_whole_world_access() {
986+
world.send_event(AppExit::Success);
987+
} else {
988+
panic!("{CONCURRENT_WORLD_ACCESS_MSG}")
989+
}
990+
}
976991
}
977992

978993
/// Having this is permission to access the contained [`ReflectAccessId`], there is no way to access anything safely through a [`WorldAccessGuard`]

crates/bevy_mod_scripting_core/src/commands.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::marker::PhantomData;
1+
use std::{any::type_name, marker::PhantomData};
22

33
use bevy::{asset::Handle, ecs::world::Mut, log::debug, prelude::Command};
44

@@ -7,6 +7,7 @@ use crate::{
77
context::{Context, ContextLoadingSettings, ScriptContexts},
88
prelude::{Runtime, RuntimeContainer},
99
script::{Script, ScriptId, Scripts},
10+
systems::handle_script_errors,
1011
};
1112

1213
pub struct DeleteScript<C: Context, R: Runtime> {
@@ -111,8 +112,14 @@ impl<C: Context, R: Runtime> Command for CreateOrUpdateScript<C, R> {
111112
let current_context_id = if let Some(id) = current_context_id {
112113
id
113114
} else {
114-
let ctxt = (builder.load)(&self.id, &self.content, &settings.context_initializers, &settings.context_pre_handling_initializers, world, &mut runtime.runtime).unwrap();
115-
contexts.insert(ctxt)
115+
let ctxt = (builder.load)(&self.id, &self.content, &settings.context_initializers, &settings.context_pre_handling_initializers, world, &mut runtime.runtime);
116+
match ctxt {
117+
Ok(ctxt) => contexts.insert(ctxt),
118+
Err(e) => {
119+
handle_script_errors(world, &format!("Failed to load context for script with id: {}. With runtime type: {} and context type: {}", self.id, type_name::<R>(), type_name::<C>()), [e]);
120+
return;
121+
}
122+
}
116123
};
117124

118125
if let Some(previous) = previous_context_id {

crates/bevy_mod_scripting_core/src/systems.rs

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use crate::{
66
bindings::ReflectAllocator,
77
commands::{CreateOrUpdateScript, DeleteScript},
88
context::{Context, ContextLoadingSettings, ScriptContexts},
9+
error::{ScriptError, ScriptResult},
910
event::{IntoCallbackLabel, ScriptCallbackEvent, ScriptErrorEvent},
1011
handler::{Args, CallbackSettings},
1112
prelude::RuntimeSettings,
@@ -181,18 +182,30 @@ pub fn event_handler<L: IntoCallbackLabel, A: Args, C: Context, R: Runtime>(
181182
world.insert_non_send_resource(runtime_container);
182183
world.insert_non_send_resource(script_contexts);
183184

184-
for error in errors {
185-
let mut error_events = world
186-
.get_resource_mut::<Events<ScriptErrorEvent>>()
187-
.expect("Missing events resource");
188-
189-
bevy::log::error!(
190-
"Encountered error in event handling for - Runtime {}, Context: {}, Args: {}. {}",
185+
handle_script_errors(
186+
world,
187+
&format!(
188+
"Encountered error in event handling for: Runtime {}, Context: {}, Args: {}",
191189
type_name::<R>(),
192190
type_name::<C>(),
193-
type_name::<A>(),
194-
error.to_string()
195-
);
191+
type_name::<A>()
192+
),
193+
errors,
194+
);
195+
}
196+
197+
/// Handles errors caused by script execution and sends them to the error event channel
198+
pub(crate) fn handle_script_errors<I: IntoIterator<Item = ScriptError>>(
199+
world: &mut World,
200+
context: &str,
201+
errors: I,
202+
) {
203+
let mut error_events = world
204+
.get_resource_mut::<Events<ScriptErrorEvent>>()
205+
.expect("Missing events resource");
206+
207+
for error in errors {
208+
bevy::log::error!("{}. {}", context, error.to_string());
196209
error_events.send(ScriptErrorEvent { error });
197210
}
198211
}

crates/languages/bevy_mod_scripting_lua/src/bindings/reference.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,12 @@ impl TealData for LuaReflectReference {
224224

225225
m.add_meta_function(MetaMethod::Len, |l, self_: LuaReflectReference| {
226226
self_.len(l)
227-
})
227+
});
228+
229+
m.add_meta_function(MetaMethod::ToString, |lua, self_: LuaReflectReference| {
230+
let world = lua.get_world()?;
231+
Ok(self_.0.print_with_world(&world))
232+
});
228233
}
229234
}
230235

crates/languages/bevy_mod_scripting_lua/src/bindings/world.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,15 @@ impl TealData for LuaWorld {
396396
Ok(builder)
397397
},
398398
);
399+
400+
methods.add_method("exit", |_, this, ()| {
401+
// TODO: somehow end control flow on lua side
402+
let world = this.0.read().ok_or_else(|| {
403+
mlua::Error::external(ScriptError::new_reflection_error("Stale world access"))
404+
})?;
405+
world.exit();
406+
Ok(())
407+
});
399408
}
400409

401410
fn add_fields<'lua, F: tealr::mlu::TealDataFields<'lua, Self>>(_fields: &mut F) {}

0 commit comments

Comments
 (0)