RunScriptCallback {
self
}
+ fn handle_error(res: &Result, guard: WorldGuard) {
+ if let Err(err) = res {
+ send_script_errors(guard, [err]);
+ }
+ }
+
/// Run the command on the given context.
///
/// Assumes this context matches the attachment for the command.
+ /// Does not send the error as a message, this needs to be done explicitly by the caller.
pub fn run_with_context(
self,
guard: WorldGuard,
@@ -109,6 +116,8 @@ impl RunScriptCallback {
}
/// Equivalent to [`Self::run`], but usable in the case where you already have [`ScriptContext`] and [`ScriptCallbacks`] resources available.
+ ///
+ /// Does not send the error as a message, this needs to be done explicitly by the caller.
pub fn run_with_contexts(
self,
guard: WorldGuard,
@@ -125,7 +134,6 @@ impl RunScriptCallback {
)
.with_script(self.attachment.script().display())
.with_language(P::LANGUAGE);
- send_script_errors(guard, [&err]);
return Err(err);
}
};
@@ -135,19 +143,22 @@ impl RunScriptCallback {
/// Equivalent to running the command, but also returns the result of the callback.
///
- /// The returned errors will NOT be sent as events or printed
- pub fn run(self, world: &mut World) -> Result {
+ /// The returned errors will NOT be sent as events or printed unless send errors is set to true
+ pub fn run(self, world: &mut World, send_errors: bool) -> Result {
let script_contexts = world.get_resource_or_init::>().clone();
let script_callbacks = world.get_resource_or_init::>().clone();
let guard = WorldGuard::new_exclusive(world);
- self.run_with_contexts(guard, script_contexts, script_callbacks)
+ let res = self.run_with_contexts(guard.clone(), script_contexts, script_callbacks);
+ if send_errors && res.is_err() {
+ Self::handle_error(&res, guard);
+ }
+ res
}
}
impl Command for RunScriptCallback {
fn apply(self, world: &mut World) {
- // Internals handle this.
- let _ = self.run(world);
+ let _ = self.run(world, true);
}
}
@@ -175,14 +186,14 @@ impl DetachScript {
impl Command for AttachScript {
fn apply(self, world: &mut World) {
- world.send_event(self.0);
+ world.write_message(self.0);
RunProcessingPipelineOnce::
::new(Some(Duration::from_secs(9999))).apply(world)
}
}
impl Command for DetachScript {
fn apply(self, world: &mut World) {
- world.send_event(self.0);
+ world.write_message(self.0);
RunProcessingPipelineOnce::
::new(Some(Duration::from_secs(9999))).apply(world)
}
}
diff --git a/crates/bevy_mod_scripting_core/src/event.rs b/crates/bevy_mod_scripting_core/src/event.rs
index 86667e589b..bcff5235f8 100644
--- a/crates/bevy_mod_scripting_core/src/event.rs
+++ b/crates/bevy_mod_scripting_core/src/event.rs
@@ -2,9 +2,10 @@
use std::{marker::PhantomData, sync::Arc};
-use ::{bevy_asset::Handle, bevy_ecs::entity::Entity, bevy_reflect::Reflect};
-use bevy_ecs::event::Event;
-use bevy_mod_scripting_asset::Language;
+use ::{bevy_ecs::entity::Entity, bevy_reflect::Reflect};
+use bevy_asset::AssetId;
+use bevy_ecs::message::Message;
+use bevy_mod_scripting_asset::{Language, ScriptAsset};
use bevy_mod_scripting_bindings::ScriptValue;
use bevy_mod_scripting_script::ScriptAttachment;
use parking_lot::Mutex;
@@ -16,7 +17,7 @@ use crate::{
};
/// An error coming from a script
-#[derive(Debug, Event)]
+#[derive(Debug, Message)]
pub struct ScriptErrorEvent {
/// The script that caused the error
pub error: ScriptError,
@@ -30,18 +31,18 @@ impl ScriptErrorEvent {
}
/// Emitted when a script is attached.
-#[derive(Event, Clone, Debug)]
+#[derive(Message, Clone, Debug)]
pub struct ScriptAttachedEvent(pub ScriptAttachment);
/// Emitted when a script is detached.
-#[derive(Event, Clone, Debug)]
+#[derive(Message, Clone, Debug)]
pub struct ScriptDetachedEvent(pub ScriptAttachment);
/// Emitted when a script asset is modified and all its attachments require re-loading
-#[derive(Event, Clone, Debug)]
-pub struct ScriptAssetModifiedEvent(pub ScriptId);
+#[derive(Message, Clone, Debug)]
+pub struct ScriptAssetModifiedEvent(pub AssetId);
-#[derive(Event)]
+#[derive(Message)]
/// Wrapper around a script event making it available to read by a specific plugin only
pub struct ForPlugin(T, PhantomData);
@@ -65,8 +66,8 @@ impl Clone for ForPlugin {
impl ForPlugin {
/// Creates a new wrapper for the specific plugin
- pub fn new(event: T) -> Self {
- Self(event, Default::default())
+ pub fn new(message: T) -> Self {
+ Self(message, Default::default())
}
/// Retrieves the inner event
@@ -218,7 +219,7 @@ impl Recipients {
Recipients::AllScripts => script_context.all_residents().collect(),
Recipients::AllContexts => script_context.first_resident_from_each_context().collect(),
Recipients::ScriptEntity(script, entity) => {
- let attachment = ScriptAttachment::EntityScript(*entity, Handle::Weak(*script));
+ let attachment = ScriptAttachment::EntityScript(*entity, script.clone());
script_context
.get_context(&attachment)
.into_iter()
@@ -226,7 +227,7 @@ impl Recipients {
.collect()
}
Recipients::StaticScript(script) => {
- let attachment = ScriptAttachment::StaticScript(Handle::Weak(*script));
+ let attachment = ScriptAttachment::StaticScript(script.clone());
script_context
.get_context(&attachment)
.into_iter()
@@ -238,7 +239,7 @@ impl Recipients {
}
/// A callback event meant to trigger a callback in a subset/set of scripts in the world with the given arguments
-#[derive(Clone, Event, Debug)]
+#[derive(Clone, Message, Debug)]
#[non_exhaustive]
pub struct ScriptCallbackEvent {
/// The label of the callback
@@ -290,7 +291,7 @@ impl ScriptCallbackEvent {
}
/// Event published when a script completes a callback and a response is requested.
-#[derive(Clone, Event, Debug)]
+#[derive(Clone, Message, Debug)]
#[non_exhaustive]
pub struct ScriptCallbackResponseEvent {
/// the label of the callback
@@ -419,11 +420,12 @@ mod test {
use ::{
bevy_app::{App, Plugin},
- bevy_asset::{AssetId, AssetIndex, Handle},
+ bevy_asset::{AssetId, Handle},
bevy_ecs::entity::Entity,
};
use parking_lot::Mutex;
use test_utils::make_test_plugin;
+ use uuid::uuid;
use super::FORBIDDEN_KEYWORDS;
use crate::{
@@ -476,18 +478,18 @@ mod test {
/// make the following arrangement:
/// use AssetId's to identify residents
/// ContextA:
- /// - EntityScriptA (Entity::from_raw(0), AssetId::from_bits(0))
- /// - EntityScriptB (Entity::from_raw(0), AssetId::from_bits(1))
+ /// - EntityScriptA (Entity::from_raw(0), 163f1128-62f9-456f-9b76-a326fbe86fa8)
+ /// - EntityScriptB (Entity::from_raw(0), 263f1128-62f9-456f-9b76-a326fbe86fa8)
///
/// ContextB:
- /// - EntityScriptC (Entity::from_raw(1), AssetId::from_bits(2))
- /// - EntityScriptD (Entity::from_raw(1), AssetId::from_bits(3))
+ /// - EntityScriptC (Entity::from_raw(1), 363f1128-62f9-456f-9b76-a326fbe86fa8)
+ /// - EntityScriptD (Entity::from_raw(1), 463f1128-62f9-456f-9b76-a326fbe86fa8)
///
/// ContextC:
- /// - StaticScriptA (AssetId::from_bits(4))
+ /// - StaticScriptA (563f1128-62f9-456f-9b76-a326fbe86fa8)
///
/// ContextD:
- /// - StaticScriptB (AssetId::from_bits(5))
+ /// - StaticScriptB (663f1128-62f9-456f-9b76-a326fbe86fa8)
fn make_test_contexts() -> ScriptContext {
let policy = ContextPolicy::per_entity();
let script_context = ScriptContext::::new(policy);
@@ -505,37 +507,55 @@ mod test {
invocations: vec![ScriptValue::String("d".to_string().into())],
}));
- let entity_script_a = Handle::Weak(AssetId::from(AssetIndex::from_bits(0)));
- let entity_script_b = Handle::Weak(AssetId::from(AssetIndex::from_bits(1)));
- let entity_script_c = Handle::Weak(AssetId::from(AssetIndex::from_bits(2)));
- let entity_script_d = Handle::Weak(AssetId::from(AssetIndex::from_bits(3)));
+ let entity_script_a = Handle::Uuid(
+ uuid!("163f1128-62f9-456f-9b76-a326fbe86fa8"),
+ Default::default(),
+ );
+ let entity_script_b = Handle::Uuid(
+ uuid!("263f1128-62f9-456f-9b76-a326fbe86fa8"),
+ Default::default(),
+ );
+ let entity_script_c = Handle::Uuid(
+ uuid!("363f1128-62f9-456f-9b76-a326fbe86fa8"),
+ Default::default(),
+ );
+ let entity_script_d = Handle::Uuid(
+ uuid!("463f1128-62f9-456f-9b76-a326fbe86fa8"),
+ Default::default(),
+ );
- let static_script_a = Handle::Weak(AssetId::from(AssetIndex::from_bits(4)));
- let static_script_b = Handle::Weak(AssetId::from(AssetIndex::from_bits(5)));
+ let static_script_a = Handle::Uuid(
+ uuid!("563f1128-62f9-456f-9b76-a326fbe86fa8"),
+ Default::default(),
+ );
+ let static_script_b = Handle::Uuid(
+ uuid!("663f1128-62f9-456f-9b76-a326fbe86fa8"),
+ Default::default(),
+ );
script_context_guard
.insert(
- ScriptAttachment::EntityScript(Entity::from_raw(0), entity_script_a),
+ ScriptAttachment::EntityScript(Entity::from_raw_u32(0).unwrap(), entity_script_a),
context_a,
)
.unwrap();
script_context_guard
.insert_resident(ScriptAttachment::EntityScript(
- Entity::from_raw(0),
+ Entity::from_raw_u32(0).unwrap(),
entity_script_b,
))
.unwrap();
script_context_guard
.insert(
- ScriptAttachment::EntityScript(Entity::from_raw(1), entity_script_c),
+ ScriptAttachment::EntityScript(Entity::from_raw_u32(1).unwrap(), entity_script_c),
context_b,
)
.unwrap();
script_context_guard
.insert_resident(ScriptAttachment::EntityScript(
- Entity::from_raw(1),
+ Entity::from_raw_u32(1).unwrap(),
entity_script_d,
))
.unwrap();
@@ -629,9 +649,14 @@ mod test {
#[test]
fn test_script_entity_recipients() {
let script_context = make_test_contexts();
- let recipients =
- Recipients::ScriptEntity(AssetId::from(AssetIndex::from_bits(0)), Entity::from_raw(0))
- .get_recipients(script_context);
+ let recipients = Recipients::ScriptEntity(
+ Handle::Uuid(
+ uuid!("163f1128-62f9-456f-9b76-a326fbe86fa8"),
+ Default::default(),
+ ),
+ Entity::from_raw_u32(0).unwrap(),
+ )
+ .get_recipients(script_context);
assert_eq!(recipients.len(), 1);
let id_context_pairs = recipients_to_asset_ids(&recipients);
@@ -641,8 +666,11 @@ mod test {
#[test]
fn test_static_script_recipients() {
let script_context = make_test_contexts();
- let recipients = Recipients::StaticScript(AssetId::from(AssetIndex::from_bits(4)))
- .get_recipients(script_context);
+ let recipients = Recipients::StaticScript(Handle::Uuid(
+ uuid!("563f1128-62f9-456f-9b76-a326fbe86fa8"),
+ Default::default(),
+ ))
+ .get_recipients(script_context);
assert_eq!(recipients.len(), 1);
let id_context_pairs = recipients_to_asset_ids(&recipients);
diff --git a/crates/bevy_mod_scripting_core/src/extractors.rs b/crates/bevy_mod_scripting_core/src/extractors.rs
index 4973e199c6..9a5cc6574e 100644
--- a/crates/bevy_mod_scripting_core/src/extractors.rs
+++ b/crates/bevy_mod_scripting_core/src/extractors.rs
@@ -3,126 +3,134 @@
//! These are designed to be used to pipe inputs into other systems which require them, while handling any configuration erorrs nicely.
use bevy_ecs::{
- archetype::Archetype,
- component::{ComponentId, Tick},
+ component::ComponentId,
query::{Access, AccessConflicts},
storage::SparseSetIndex,
- system::{SystemMeta, SystemParam, SystemParamValidationError},
- world::{DeferredWorld, World, unsafe_world_cell::UnsafeWorldCell},
};
-use bevy_mod_scripting_bindings::{WorldAccessGuard, WorldGuard, access_map::ReflectAccessId};
+use bevy_mod_scripting_bindings::access_map::ReflectAccessId;
use fixedbitset::FixedBitSet;
-/// A wrapper around a world which pre-populates access, to safely co-exist with other system params,
-/// acts exactly like `&mut World` so this should be your only top-level system param
-///
-/// The reason is the guard needs to know the underlying access that
-pub struct WithWorldGuard<'w, 's, T: SystemParam> {
- world_guard: WorldGuard<'w>,
- param: T::Item<'w, 's>,
-}
-
-impl<'w, 's, T: SystemParam> WithWorldGuard<'w, 's, T> {
- /// Get the world guard and the inner system param
- pub fn get(&self) -> (WorldGuard<'w>, &T::Item<'w, 's>) {
- (self.world_guard.clone(), &self.param)
- }
-
- /// Get the world guard and the inner system param mutably
- pub fn get_mut(&mut self) -> (WorldGuard<'w>, &mut T::Item<'w, 's>) {
- (self.world_guard.clone(), &mut self.param)
- }
-}
-
-unsafe impl SystemParam for WithWorldGuard<'_, '_, T> {
- type State = (T::State, Vec<(ReflectAccessId, bool)>);
-
- type Item<'world, 'state> = WithWorldGuard<'world, 'state, T>;
-
- fn init_state(world: &mut World, system_meta: &mut SystemMeta) -> Self::State {
- // verify there are no accesses previously
- let other_accessed_components =
- system_meta.component_access_set().combined_access().clone();
-
- let inner_state = T::init_state(world, system_meta);
-
- let accessed_components = system_meta.component_access_set().combined_access();
- let access_ids = get_all_access_ids(accessed_components);
- let other_access_ids = get_all_access_ids(&other_accessed_components);
-
- // reason: we can't handle this error nicely, and continuing is a safety issue
- #[allow(clippy::panic)]
- if !other_access_ids.is_empty() {
- panic!(
- "WithWorldGuard must be the only top-level system param, cannot run system: `{}`",
- system_meta.name()
- );
- }
-
- // Safety: not removing any accesses
- unsafe { system_meta.component_access_set_mut().write_all() }
- unsafe { system_meta.archetype_component_access_mut().write_all() }
- (inner_state, access_ids)
- }
-
- unsafe fn get_param<'world, 'state>(
- state: &'state mut Self::State,
- system_meta: &SystemMeta,
- world: UnsafeWorldCell<'world>,
- change_tick: Tick,
- ) -> Self::Item<'world, 'state> {
- // create a guard which can only access the resources/components specified by the system.
- let guard = WorldAccessGuard::new_exclusive(unsafe { world.world_mut() });
-
- #[allow(
- clippy::panic,
- reason = "This API does not allow us to handle this error nicely, and continuing is a safety issue."
- )]
- for (raid, is_write) in &state.1 {
- if *is_write {
- if !guard.claim_write_access(*raid) {
- panic!(
- "System tried to access set of system params which break rust aliasing rules. Aliasing access: {raid:#?}",
- );
- }
- } else if !guard.claim_read_access(*raid) {
- panic!(
- "System tried to access set of system params which break rust aliasing rules. Aliasing access: {raid:#?}",
- );
- }
- }
-
- WithWorldGuard {
- world_guard: guard,
- param: unsafe { T::get_param(&mut state.0, system_meta, world, change_tick) },
- }
- }
-
- unsafe fn new_archetype(
- state: &mut Self::State,
- archetype: &Archetype,
- system_meta: &mut SystemMeta,
- ) {
- unsafe { T::new_archetype(&mut state.0, archetype, system_meta) }
- }
-
- fn apply(state: &mut Self::State, system_meta: &SystemMeta, world: &mut World) {
- T::apply(&mut state.0, system_meta, world)
- }
-
- fn queue(state: &mut Self::State, system_meta: &SystemMeta, world: DeferredWorld) {
- T::queue(&mut state.0, system_meta, world)
- }
-
- unsafe fn validate_param(
- state: &Self::State,
- system_meta: &SystemMeta,
- world: UnsafeWorldCell,
- ) -> Result<(), SystemParamValidationError> {
- unsafe { T::validate_param(&state.0, system_meta, world) }
- }
-}
+// /// A wrapper around a world which pre-populates access, to safely co-exist with other system params,
+// /// acts exactly like `&mut World` so this should be your only top-level system param
+// ///
+// /// The reason is the guard needs to know the underlying access that
+// pub struct WithWorldGuard<'w, 's, T: SystemParam> {
+// world_guard: WorldGuard<'w>,
+// param: T::Item<'w, 's>,
+// }
+
+// impl<'w, 's, T: SystemParam> WithWorldGuard<'w, 's, T> {
+// /// Get the world guard and the inner system param
+// pub fn get(&self) -> (WorldGuard<'w>, &T::Item<'w, 's>) {
+// (self.world_guard.clone(), &self.param)
+// }
+
+// /// Get the world guard and the inner system param mutably
+// pub fn get_mut(&mut self) -> (WorldGuard<'w>, &mut T::Item<'w, 's>) {
+// (self.world_guard.clone(), &mut self.param)
+// }
+// }
+
+// unsafe impl SystemParam for WithWorldGuard<'_, '_, T> {
+// type State = (T::State, Vec<(ReflectAccessId, bool)>);
+
+// type Item<'world, 'state> = WithWorldGuard<'world, 'state, T>;
+
+// fn init_access(
+// _state: &Self::State,
+// _system_meta: &mut SystemMeta,
+// component_access_set: &mut bevy_ecs::query::FilteredAccessSet,
+// _world: &mut World,
+// ) {
+// // verify there are no accesses previously
+// // let other_accessed_components = component_access_set.combined_access().clone();
+
+// // let accessed_components = component_access_set.combined_access();
+// // let access_ids = get_all_access_ids(accessed_components);
+// // let other_access_ids = get_all_access_ids(&other_accessed_components);
+
+// // reason: we can't handle this error nicely, and continuing is a safety issue
+// // #[allow(clippy::panic)]
+// // if !other_access_ids.is_empty() {
+// // panic!(
+// // "WithWorldGuard must be the only top-level system param, cannot run system: `{}`",
+// // system_meta.name()
+// // );
+// // }
+
+// // Safety: not removing any accesses
+// component_access_set.write_all()
+// }
+
+// fn init_state(world: &mut World) -> Self::State {
+// // // verify there are no accesses previously
+// // let other_accessed_components =
+// // system_meta.component_access_set().combined_access().clone();
+
+// // let inner_state = T::init_state(world);
+
+// // let accessed_components = system_meta.component_access_set().combined_access();
+// // let inner_state = T::init_access(T::init_state(world));
+// // let access_ids = get_all_access_ids(accessed_components);
+// // let other_access_ids = get_all_access_ids(&other_accessed_components);
+
+// (T::init_state(world), vec![])
+// }
+
+// unsafe fn get_param<'world, 'state>(
+// state: &'state mut (T::State, Vec<(ReflectAccessId, bool)>),
+// system_meta: &SystemMeta,
+// world: UnsafeWorldCell<'world>,
+// change_tick: Tick,
+// ) -> Self::Item<'world, 'state> {
+// if state.1.is_empty() {
+// T::init_access()
+// }
+
+// // create a guard which can only access the resources/components specified by the system.
+// let guard = WorldAccessGuard::new_exclusive(unsafe { world.world_mut() });
+
+// #[allow(
+// clippy::panic,
+// reason = "This API does not allow us to handle this error nicely, and continuing is a safety issue."
+// )]
+// for (raid, is_write) in &state.1 {
+// if *is_write {
+// if !guard.claim_write_access(*raid) {
+// panic!(
+// "System tried to access set of system params which break rust aliasing rules. Aliasing access: {raid:#?}",
+// );
+// }
+// } else if !guard.claim_read_access(*raid) {
+// panic!(
+// "System tried to access set of system params which break rust aliasing rules. Aliasing access: {raid:#?}",
+// );
+// }
+// }
+
+// WithWorldGuard {
+// world_guard: guard,
+// param: unsafe { T::get_param(&mut state.0, system_meta, world, change_tick) },
+// }
+// }
+
+// fn apply(state: &mut Self::State, system_meta: &SystemMeta, world: &mut World) {
+// T::apply(&mut state.0, system_meta, world)
+// }
+
+// fn queue(state: &mut Self::State, system_meta: &SystemMeta, world: DeferredWorld) {
+// T::queue(&mut state.0, system_meta, world)
+// }
+
+// unsafe fn validate_param(
+// state: &mut Self::State,
+// system_meta: &SystemMeta,
+// world: UnsafeWorldCell,
+// ) -> Result<(), SystemParamValidationError> {
+// unsafe { T::validate_param(&mut state.0, system_meta, world) }
+// }
+// }
fn individual_conflicts(conflicts: AccessConflicts) -> FixedBitSet {
match conflicts {
@@ -132,11 +140,11 @@ fn individual_conflicts(conflicts: AccessConflicts) -> FixedBitSet {
}
}
-pub(crate) fn get_all_access_ids(access: &Access) -> Vec<(ReflectAccessId, bool)> {
- let mut access_all_read = Access::::default();
+pub(crate) fn get_all_access_ids(access: &Access) -> Vec<(ReflectAccessId, bool)> {
+ let mut access_all_read = Access::default();
access_all_read.read_all();
- let mut access_all_write = Access::::default();
+ let mut access_all_write = Access::default();
access_all_write.write_all();
// read conflicts with each set to figure out the necessary locks
@@ -164,96 +172,96 @@ pub(crate) fn get_all_access_ids(access: &Access) -> Vec<(ReflectAc
result
}
-#[cfg(test)]
-mod test {
- use crate::config::{GetPluginThreadConfig, ScriptingPluginConfiguration};
- use bevy_ecs::resource::Resource;
- use bevy_mod_scripting_bindings::ScriptValue;
- use test_utils::make_test_plugin;
-
- use {
- bevy_app::{App, Plugin, Update},
- bevy_ecs::{
- component::Component,
- entity::Entity,
- event::{Event, EventReader},
- system::{Query, ResMut},
- },
- };
-
- use super::*;
-
- make_test_plugin!(crate);
-
- #[derive(Component)]
- struct Comp;
-
- #[derive(Resource, Default)]
- struct Res;
-
- #[test]
- pub fn check_with_world_correctly_locks_resource_and_component() {
- let system_fn = |mut guard: WithWorldGuard<(ResMut, Query<&'static Comp>)>| {
- let (guard, (_res, _entity)) = guard.get_mut();
- assert_eq!(guard.list_accesses().len(), 2, "Expected 2 accesses");
- assert!(
- !guard.claim_read_access(
- ReflectAccessId::for_resource::(&guard.as_unsafe_world_cell().unwrap())
- .unwrap()
- )
- );
- assert!(
- !guard.claim_write_access(
- ReflectAccessId::for_resource::(&guard.as_unsafe_world_cell().unwrap())
- .unwrap()
- )
- );
- };
-
- let mut app = App::new();
- app.add_systems(Update, system_fn);
- app.insert_resource(Res);
- app.world_mut().spawn(Comp);
-
- app.cleanup();
- app.finish();
- app.update();
- }
-
- #[test]
- #[should_panic(
- expected = "WithWorldGuard must be the only top-level system param, cannot run system"
- )]
- pub fn check_with_world_panics_when_used_with_resource_top_level() {
- let system_fn = |_res: ResMut, mut _guard: WithWorldGuard>| {};
-
- let mut app = App::new();
- app.add_systems(Update, system_fn);
- app.insert_resource(Res);
- app.world_mut().spawn(Comp);
-
- app.cleanup();
- app.finish();
- app.update();
- }
-
- #[test]
- #[should_panic(
- expected = "WithWorldGuard must be the only top-level system param, cannot run system"
- )]
- pub fn check_with_world_panics_when_used_with_event_reader_top_level() {
- #[derive(Event)]
- struct TestEvent;
- let system_fn =
- |_res: EventReader, mut _guard: WithWorldGuard>| {};
-
- let mut app = App::new();
- app.add_systems(Update, system_fn);
- app.insert_resource(Res);
- app.world_mut().spawn(Comp);
-
- app.cleanup();
- app.finish();
- app.update();
- }
-}
+// #[cfg(test)]
+// mod test {
+// use crate::config::{GetPluginThreadConfig, ScriptingPluginConfiguration};
+// use bevy_ecs::resource::Resource;
+// use bevy_mod_scripting_bindings::ScriptValue;
+// use test_utils::make_test_plugin;
+
+// use {
+// bevy_app::{App, Plugin, Update},
+// bevy_ecs::{
+// component::Component,
+// entity::Entity,
+// event::{Event, EventReader},
+// system::{Query, ResMut},
+// },
+// };
+
+// use super::*;
+
+// make_test_plugin!(crate);
+
+// #[derive(Component)]
+// struct Comp;
+
+// #[derive(Resource, Default)]
+// struct Res;
+
+// #[test]
+// pub fn check_with_world_correctly_locks_resource_and_component() {
+// let system_fn = |mut guard: WithWorldGuard<(ResMut, Query<&'static Comp>)>| {
+// let (guard, (_res, _entity)) = guard.get_mut();
+// assert_eq!(guard.list_accesses().len(), 2, "Expected 2 accesses");
+// assert!(
+// !guard.claim_read_access(
+// ReflectAccessId::for_resource::(&guard.as_unsafe_world_cell().unwrap())
+// .unwrap()
+// )
+// );
+// assert!(
+// !guard.claim_write_access(
+// ReflectAccessId::for_resource::(&guard.as_unsafe_world_cell().unwrap())
+// .unwrap()
+// )
+// );
+// };
+
+// let mut app = App::new();
+// app.add_systems(Update, system_fn);
+// app.insert_resource(Res);
+// app.world_mut().spawn(Comp);
+
+// app.cleanup();
+// app.finish();
+// app.update();
+// }
+
+// #[test]
+// #[should_panic(
+// expected = "WithWorldGuard must be the only top-level system param, cannot run system"
+// )]
+// pub fn check_with_world_panics_when_used_with_resource_top_level() {
+// let system_fn = |_res: ResMut, mut _guard: WithWorldGuard>| {};
+
+// let mut app = App::new();
+// app.add_systems(Update, system_fn);
+// app.insert_resource(Res);
+// app.world_mut().spawn(Comp);
+
+// app.cleanup();
+// app.finish();
+// app.update();
+// }
+
+// #[test]
+// #[should_panic(
+// expected = "WithWorldGuard must be the only top-level system param, cannot run system"
+// )]
+// pub fn check_with_world_panics_when_used_with_event_reader_top_level() {
+// #[derive(Event)]
+// struct TestEvent;
+// let system_fn =
+// |_res: EventReader, mut _guard: WithWorldGuard>| {};
+
+// let mut app = App::new();
+// app.add_systems(Update, system_fn);
+// app.insert_resource(Res);
+// app.world_mut().spawn(Comp);
+
+// app.cleanup();
+// app.finish();
+// app.update();
+// }
+// }
diff --git a/crates/bevy_mod_scripting_core/src/handler.rs b/crates/bevy_mod_scripting_core/src/handler.rs
index 631e3d15b7..67fca54053 100644
--- a/crates/bevy_mod_scripting_core/src/handler.rs
+++ b/crates/bevy_mod_scripting_core/src/handler.rs
@@ -1,5 +1,8 @@
//! Contains the logic for handling script callback events
-use bevy_ecs::world::WorldId;
+use bevy_ecs::{
+ message::{MessageCursor, Messages},
+ world::WorldId,
+};
use bevy_mod_scripting_bindings::{
InteropError, ScriptValue, ThreadScriptContext, ThreadWorldContainer, WorldAccessGuard,
WorldGuard,
@@ -15,13 +18,10 @@ use crate::{
CallbackLabel, IntoCallbackLabel, ScriptCallbackEvent, ScriptCallbackResponseEvent,
ScriptErrorEvent,
},
- extractors::WithWorldGuard,
script::ScriptContext,
};
use {
bevy_ecs::{
- event::EventCursor,
- event::Events,
system::{Local, SystemState},
world::{Mut, World},
},
@@ -90,14 +90,14 @@ impl ScriptingHandler for P {
#[allow(deprecated)]
pub fn event_handler(
world: &mut World,
- state: &mut EventHandlerSystemState,
+ state: &mut SystemState>>,
) {
// we wrap the inner event handler, so that we can guarantee that the handler context is released statically
{
let script_context = world.get_resource_or_init::>().clone();
let script_callbacks = world.get_resource_or_init::>().clone();
- let (event_cursor, mut guard) = state.get_mut(world);
- let (guard, _) = guard.get_mut();
+ let event_cursor = state.get_mut(world);
+ let guard = WorldAccessGuard::new_exclusive(world);
event_handler_inner::(
L::into_callback_label(),
event_cursor,
@@ -108,22 +108,17 @@ pub fn event_handler(
}
}
-type EventHandlerSystemState<'w, 's> = SystemState<(
- Local<'s, EventCursor>,
- WithWorldGuard<'w, 's, ()>,
-)>;
-
#[profiling::function]
#[allow(deprecated)]
pub(crate) fn event_handler_inner(
callback_label: CallbackLabel,
- mut event_cursor: Local>,
+ mut event_cursor: Local>,
script_context: ScriptContext,
script_callbacks: ScriptCallbacks
,
guard: WorldAccessGuard,
) {
let mut errors = Vec::default();
- let events = guard.with_resource(|events: &Events| {
+ let events = guard.with_resource(|events: &Messages| {
event_cursor
.read(events)
.filter(|e| e.label == callback_label)
@@ -195,8 +190,8 @@ fn collect_errors(call_result: Result, errors: &mut Ve
/// Sends a callback response event to the world
pub fn send_callback_response(world: WorldGuard, response: ScriptCallbackResponseEvent) {
- let err = world.with_resource_mut(|mut events: Mut>| {
- events.send(response);
+ let err = world.with_resource_mut(|mut events: Mut>| {
+ events.write(response);
});
if let Err(err) = err {
@@ -213,9 +208,9 @@ pub fn send_script_errors<'e>(
errors: impl IntoIterator- ,
) {
let iter = errors.into_iter();
- let err = world.with_resource_mut(|mut error_events: Mut>| {
+ let err = world.with_resource_mut(|mut error_events: Mut>| {
for error in iter {
- error_events.send(ScriptErrorEvent {
+ error_events.write(ScriptErrorEvent {
error: error.clone(),
});
}
@@ -232,10 +227,10 @@ pub fn send_script_errors<'e>(
/// A system which receives all script errors and logs them to console
pub fn script_error_logger(
world: &mut World,
- mut errors_cursor: Local>,
+ mut errors_cursor: Local>,
) {
let guard = WorldGuard::new_exclusive(world);
- let errors = guard.with_resource(|events: &Events| {
+ let errors = guard.with_resource(|events: &Messages| {
errors_cursor
.read(events)
.map(|e| e.error.clone())
diff --git a/crates/bevy_mod_scripting_core/src/lib.rs b/crates/bevy_mod_scripting_core/src/lib.rs
index 6ddd081a7b..06d8fc8a83 100644
--- a/crates/bevy_mod_scripting_core/src/lib.rs
+++ b/crates/bevy_mod_scripting_core/src/lib.rs
@@ -336,9 +336,9 @@ pub struct BMSScriptingInfrastructurePlugin {
impl Plugin for BMSScriptingInfrastructurePlugin {
fn build(&self, app: &mut App) {
- app.add_event::()
- .add_event::()
- .add_event::()
+ app.add_message::()
+ .add_message::()
+ .add_message::()
.init_resource::()
.init_asset::()
.init_resource::()
diff --git a/crates/bevy_mod_scripting_core/src/pipeline/hooks.rs b/crates/bevy_mod_scripting_core/src/pipeline/hooks.rs
index 7f1d846153..e25169eb23 100644
--- a/crates/bevy_mod_scripting_core/src/pipeline/hooks.rs
+++ b/crates/bevy_mod_scripting_core/src/pipeline/hooks.rs
@@ -23,7 +23,10 @@ impl TransitionListener> for OnLoa
let emit_responses = P::readonly_configuration(world_id).emit_responses;
let callbacks = world.get_resource_or_init::>().clone();
let guard = WorldGuard::new_exclusive(world);
-
+ bevy_log::trace!(
+ "Running on_script_loaded hook for script: {}",
+ ctxt.attachment
+ );
RunScriptCallback::
::new(
ctxt.attachment.clone(),
OnScriptLoaded::into_callback_label(),
@@ -49,7 +52,10 @@ impl TransitionListener>
let emit_responses = P::readonly_configuration(world_id).emit_responses;
let callbacks = world.get_resource_or_init::>().clone();
let guard = WorldGuard::new_exclusive(world);
-
+ bevy_log::trace!(
+ "Running on_script_unloaded hook for script: {}",
+ ctxt.attachment
+ );
let v = RunScriptCallback::::new(
ctxt.attachment.clone(),
OnScriptUnloaded::into_callback_label(),
@@ -77,6 +83,11 @@ impl TransitionListener>
let callbacks = world.get_resource_or_init::>().clone();
let guard = WorldGuard::new_exclusive(world);
+ bevy_log::trace!(
+ "Running on_script_unloaded for reload hook for script: {}",
+ ctxt.attachment
+ );
+
let v = RunScriptCallback::::new(
ctxt.attachment.clone(),
OnScriptUnloaded::into_callback_label(),
@@ -106,6 +117,11 @@ impl TransitionListener> for OnRel
return Ok(());
}
+ bevy_log::trace!(
+ "Running on_script_reloaded hook for script: {}",
+ ctxt.attachment
+ );
+
let unload_state = ctxt.get_first_typed::(UNLOADED_SCRIPT_STATE_KEY);
let unload_state = unload_state.unwrap_or(ScriptValue::Unit);
diff --git a/crates/bevy_mod_scripting_core/src/pipeline/machines.rs b/crates/bevy_mod_scripting_core/src/pipeline/machines.rs
index 32dfe922ab..2ca903e6e9 100644
--- a/crates/bevy_mod_scripting_core/src/pipeline/machines.rs
+++ b/crates/bevy_mod_scripting_core/src/pipeline/machines.rs
@@ -6,6 +6,7 @@ use std::{
time::{Duration, Instant},
};
+use bevy_ecs::message::{MessageCursor, Messages};
use bevy_log::debug;
use bevy_mod_scripting_bindings::InteropError;
use bevy_mod_scripting_script::ScriptAttachment;
@@ -19,8 +20,8 @@ use super::*;
/// Allows re-publishing of the same events too
#[derive(SystemParam)]
pub struct StateMachine<'w, 's, T: Send + Sync + 'static, P: IntoScriptPluginParams> {
- events: ResMut<'w, Events>>,
- cursor: Local<'s, EventCursor>>,
+ events: ResMut<'w, Messages>>,
+ cursor: Local<'s, MessageCursor>>,
}
impl<'w, 's, T: Send + Sync + 'static, P: IntoScriptPluginParams> StateMachine<'w, 's, T, P> {
@@ -62,7 +63,7 @@ impl<'w, 's, T: Send + Sync + 'static, P: IntoScriptPluginParams> StateMachine<'
/// Consumes an iterator of state machines and writes them to the asset pipe
pub fn write_batch(&mut self, batch: impl IntoIterator- ) {
self.events
- .send_batch(batch.into_iter().map(ForPlugin::new));
+ .write_batch(batch.into_iter().map(ForPlugin::new));
}
}
@@ -165,7 +166,8 @@ impl ActiveMachines
{
// removed
}
Some(Err(err)) => {
- _ = world.send_event(ScriptErrorEvent::new(err));
+ _ = world
+ .write_message(ScriptErrorEvent::new(err.with_language(P::LANGUAGE)));
// removed
}
None => {
@@ -244,7 +246,7 @@ impl ScriptMachine {
if let Err(err) =
(on_entered)(machine_state.as_mut(), world, &mut self.context)
{
- _ = world.send_event(ScriptErrorEvent::new(
+ _ = world.write_message(ScriptErrorEvent::new(
err.with_context(self.context.attachment.to_string())
.with_context(machine_state.state_name())
.with_language(P::LANGUAGE),
@@ -523,13 +525,14 @@ impl MachineState for ContextAssigned
{
let mut contexts_guard = contexts.write();
// drop any strong handles
- match contexts_guard.insert(attachment.clone().into_weak(), self.context.clone()) {
+ match contexts_guard.insert(attachment.clone(), self.context.clone()) {
Ok(_) => {}
Err(_) => {
drop(contexts_guard);
- _ = world.send_event(ScriptErrorEvent::new(ScriptError::from(InteropError::str(
- "no context policy matched",
- ))))
+ _ = world.write_message(ScriptErrorEvent::new(
+ ScriptError::from(InteropError::str("no context policy matched"))
+ .with_language(P::LANGUAGE),
+ ))
}
}
Box::new(ready(Ok(
diff --git a/crates/bevy_mod_scripting_core/src/pipeline/mod.rs b/crates/bevy_mod_scripting_core/src/pipeline/mod.rs
index f759e874c8..10bbe91fd8 100644
--- a/crates/bevy_mod_scripting_core/src/pipeline/mod.rs
+++ b/crates/bevy_mod_scripting_core/src/pipeline/mod.rs
@@ -5,14 +5,14 @@ use std::{any::Any, collections::VecDeque, marker::PhantomData, sync::Arc, time:
use bevy_app::{App, Plugin, PostUpdate};
use bevy_asset::{AssetServer, Assets, Handle, LoadState};
use bevy_ecs::{
- event::{Event, EventCursor, EventReader, EventWriter, Events},
+ message::{Message, MessageReader},
resource::Resource,
schedule::{IntoScheduleConfigs, Schedule, ScheduleLabel, SystemSet},
system::{Command, Local, Res, ResMut, SystemParam},
world::World,
};
use bevy_log::debug;
-use bevy_mod_scripting_asset::ScriptAsset;
+use bevy_mod_scripting_asset::{Language, ScriptAsset};
use bevy_mod_scripting_bindings::WorldGuard;
use bevy_mod_scripting_display::DisplayProxy;
use bevy_platform::collections::HashSet;
@@ -138,8 +138,8 @@ impl std::fmt::Debug for ScriptProcessingSchedule
}
impl ScriptLoadingPipeline {
- fn add_plugin_event(&self, app: &mut App) -> &Self {
- app.add_event::().add_event::>();
+ fn add_plugin_message(&self, app: &mut App) -> &Self {
+ app.add_message::().add_message::>();
self
}
}
@@ -156,21 +156,21 @@ pub trait GetScriptHandle {
/// is loaded, will also guarantee a strong handle, otherwise the whole thing is skipped.
///
/// Think of this as a proxy for "baby'ing" asset handles
-pub struct LoadedWithHandles<'w, 's, T: GetScriptHandle + Event + Clone> {
+pub struct LoadedWithHandles<'w, 's, T: GetScriptHandle + Message + Clone> {
assets: ResMut<'w, Assets>,
asset_server: Res<'w, AssetServer>,
- fresh_events: EventReader<'w, 's, T>,
- loaded_with_handles: Local<'s, VecDeque<(T, StrongScriptHandle)>>,
+ fresh_events: MessageReader<'w, 's, T>,
+ loaded_with_handles: Local<'s, VecDeque<(T, StrongScriptHandle, Language)>>,
loading: Local<'s, VecDeque>,
}
-impl LoadedWithHandles<'_, '_, T> {
+impl LoadedWithHandles<'_, '_, T> {
/// Retrieves all of the events of type `T`, which have finished loading and have a strong handle,
/// the rest will be discarded.
///
/// This uses a [`EventReader`] underneath, meaning if you don't call this method once every frame (or every other frame).
/// You may miss events.
- pub fn get_loaded(&mut self) -> impl Iterator- {
+ pub fn get_loaded(&mut self) -> impl Iterator
- {
// first get all of the fresh_events
self.loading.extend(self.fresh_events.read().cloned());
// now process the loading queue
@@ -180,7 +180,8 @@ impl LoadedWithHandles<'_, '_, T> {
Some(LoadState::Loaded) | None => { // none in case this is added in memory and not through asset server
let strong = StrongScriptHandle::from_assets(handle, &mut self.assets);
if let Some(strong) = strong {
- self.loaded_with_handles.push_front((e.clone(), strong));
+ let lang = strong.get(&self.assets).language.clone();
+ self.loaded_with_handles.push_front((e.clone(), strong, lang));
}
false
}
@@ -203,9 +204,9 @@ impl LoadedWithHandles<'_, '_, T> {
impl Plugin for ScriptLoadingPipeline
{
fn build(&self, app: &mut App) {
- self.add_plugin_event::(app)
- .add_plugin_event::(app)
- .add_plugin_event::(app);
+ self.add_plugin_message::(app)
+ .add_plugin_message::(app)
+ .add_plugin_message::(app);
let mut active_machines = app.world_mut().get_resource_or_init::>();
if self.on_script_loaded_callback {
@@ -405,7 +406,7 @@ impl<'w, P: IntoScriptPluginParams> ScriptPipelineState<'w, P> {
#[cfg(test)]
mod test {
- use bevy_asset::{AssetApp, AssetId, AssetPlugin};
+ use bevy_asset::{AssetApp, AssetPlugin};
use bevy_ecs::{entity::Entity, system::SystemState, world::FromWorld};
use bevy_mod_scripting_asset::Language;
use bevy_mod_scripting_bindings::ScriptValue;
@@ -418,7 +419,7 @@ mod test {
#[test]
fn test_system_params() {
let mut app = App::default();
- app.add_event::();
+ app.add_message::();
app.add_plugins(AssetPlugin::default());
app.init_asset::();
app.finish();
@@ -441,9 +442,9 @@ mod test {
language: Language::Lua,
};
let handle = asset_server.add(asset);
- let handle_invalid = Handle::Weak(AssetId::invalid());
- world.send_event(ScriptAttachedEvent(ScriptAttachment::StaticScript(handle)));
- world.send_event(ScriptAttachedEvent(ScriptAttachment::StaticScript(
+ let handle_invalid = Handle::default();
+ world.write_message(ScriptAttachedEvent(ScriptAttachment::StaticScript(handle)));
+ world.write_message(ScriptAttachedEvent(ScriptAttachment::StaticScript(
handle_invalid,
)));
@@ -468,7 +469,7 @@ mod test {
#[test]
fn test_run_override_is_undid() {
let mut app = App::default();
- app.add_event::();
+ app.add_message::();
app.add_plugins((AssetPlugin::default(), TestPlugin::default()));
app.init_asset::();
diff --git a/crates/bevy_mod_scripting_core/src/pipeline/start.rs b/crates/bevy_mod_scripting_core/src/pipeline/start.rs
index f02b15a111..6e94a85221 100644
--- a/crates/bevy_mod_scripting_core/src/pipeline/start.rs
+++ b/crates/bevy_mod_scripting_core/src/pipeline/start.rs
@@ -1,16 +1,17 @@
use super::*;
use bevy_asset::AssetEvent;
+use bevy_ecs::message::{MessageReader, MessageWriter};
use bevy_log::{debug, trace};
/// A handle to a script asset which can only be made from a strong handle
#[derive(Clone, Debug)]
pub struct StrongScriptHandle(Handle);
-impl GetScriptHandle for ScriptAssetModifiedEvent {
- fn get_script_handle(&self) -> Handle {
- Handle::Weak(self.0)
- }
-}
+// impl GetScriptHandle for ScriptAssetModifiedEvent {
+// fn get_script_handle(&self) -> Handle {
+// self.0.clone()
+// }
+// }
impl GetScriptHandle for ScriptAttachedEvent {
fn get_script_handle(&self) -> Handle {
@@ -58,8 +59,8 @@ impl StrongScriptHandle {
/// Generate [`ScriptAssetModifiedEvent`]'s from asset modification events, filtering to only forward those matching the plugin's language
pub fn filter_script_modifications(
- mut events: EventReader>,
- mut filtered: EventWriter