Skip to content

Commit 5dd665a

Browse files
authored
refactor: add world-local static plugin config, remove ContextLoadingSettings resource (#470)
1 parent 5108fbb commit 5dd665a

File tree

17 files changed

+174
-239
lines changed

17 files changed

+174
-239
lines changed

crates/bevy_mod_scripting_core/src/asset.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@ use bevy_ecs::{
1818
event::{EventReader, EventWriter},
1919
schedule::IntoScheduleConfigs,
2020
system::{Commands, Local, Query, Res},
21+
world::WorldId,
2122
};
2223
use serde::{Deserialize, Serialize};
2324

2425
use crate::{
2526
IntoScriptPluginParams, LanguageExtensions, ScriptComponent, ScriptingSystemSet, StaticScripts,
2627
commands::{CreateOrUpdateScript, DeleteScript},
27-
context::ContextLoadingSettings,
2828
error::ScriptError,
2929
event::ScriptEvent,
3030
script::{ContextKey, DisplayProxy, ScriptAttachment},
@@ -220,7 +220,7 @@ fn handle_script_events<P: IntoScriptPluginParams>(
220220
asset_server: Res<AssetServer>,
221221
mut script_queue: Local<ScriptQueue>,
222222
mut commands: Commands,
223-
context_loading_settings: Res<ContextLoadingSettings<P>>,
223+
world_id: WorldId,
224224
) {
225225
for event in events.read() {
226226
trace!("{}: Received script event: {:?}", P::LANGUAGE, event);
@@ -242,7 +242,7 @@ fn handle_script_events<P: IntoScriptPluginParams>(
242242
entity,
243243
handle.clone(),
244244
))
245-
.with_responses(context_loading_settings.emit_responses),
245+
.with_responses(P::readonly_configuration(world_id).emit_responses),
246246
);
247247
}
248248
}
@@ -252,15 +252,15 @@ fn handle_script_events<P: IntoScriptPluginParams>(
252252
CreateOrUpdateScript::<P>::new(ScriptAttachment::StaticScript(
253253
handle.clone(),
254254
))
255-
.with_responses(context_loading_settings.emit_responses),
255+
.with_responses(P::readonly_configuration(world_id).emit_responses),
256256
);
257257
}
258258
}
259259
}
260260
ScriptEvent::Detached { key } => {
261261
commands.queue(
262262
DeleteScript::<P>::new(key.clone())
263-
.with_responses(context_loading_settings.emit_responses),
263+
.with_responses(P::readonly_configuration(world_id).emit_responses),
264264
);
265265
}
266266
ScriptEvent::Attached { key } => {
@@ -322,7 +322,7 @@ fn handle_script_events<P: IntoScriptPluginParams>(
322322
if language == P::LANGUAGE {
323323
commands.queue(
324324
CreateOrUpdateScript::<P>::new(context_key)
325-
.with_responses(context_loading_settings.emit_responses),
325+
.with_responses(P::readonly_configuration(world_id).emit_responses),
326326
);
327327
}
328328
}

crates/bevy_mod_scripting_core/src/bindings/schedule.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,14 +192,17 @@ impl WorldAccessGuard<'_> {
192192
reason = "tests are there but not working currently"
193193
)]
194194
mod tests {
195+
use crate::config::{GetPluginThreadConfig, ScriptingPluginConfiguration};
195196
use ::{
196197
bevy_app::{App, Plugin, Update},
197198
bevy_ecs::{
198199
entity::Entity,
199200
schedule::{NodeId, Schedules},
200201
system::IntoSystem,
201202
},
203+
std::{cell::OnceCell, rc::Rc},
202204
};
205+
203206
use test_utils::make_test_plugin;
204207

205208
use super::*;

crates/bevy_mod_scripting_core/src/bindings/script_system.rs

Lines changed: 11 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ use super::{
1212
use crate::{
1313
IntoScriptPluginParams,
1414
bindings::pretty_print::DisplayWithWorld,
15-
context::ContextLoadingSettings,
1615
error::{InteropError, ScriptError},
1716
event::CallbackLabel,
1817
extractors::get_all_access_ids,
@@ -161,12 +160,6 @@ impl ScriptSystemBuilder {
161160
let after_systems = self.after.clone();
162161
let system_name = self.name.to_string();
163162

164-
// let system: DynamicScriptSystem<P> =
165-
// IntoSystem::<(), (), (IsDynamicScriptSystem<P>, ())>::into_system(self);
166-
167-
// dummy node id for now
168-
// let mut reflect_system = ReflectSystem::from_system(&system, NodeId::System(0));
169-
170163
// this is quite important, by default systems are placed in a set defined by their TYPE, i.e. in this case
171164
// all script systems would be the same
172165

@@ -208,7 +201,6 @@ impl ScriptSystemBuilder {
208201

209202
struct DynamicHandlerContext<'w, P: IntoScriptPluginParams> {
210203
script_context: &'w ScriptContext<P>,
211-
context_loading_settings: &'w ContextLoadingSettings<P>,
212204
runtime_container: &'w RuntimeContainer<P>,
213205
}
214206

@@ -220,17 +212,17 @@ impl<'w, P: IntoScriptPluginParams> DynamicHandlerContext<'w, P> {
220212
)]
221213
pub fn init_param(world: &mut World, system: &mut FilteredAccessSet<ComponentId>) {
222214
let mut access = FilteredAccess::<ComponentId>::matches_nothing();
223-
// let scripts_res_id = world
224-
// .query::<&Script<P>>();
225-
let context_loading_settings_res_id = world
226-
.resource_id::<ContextLoadingSettings<P>>()
227-
.expect("ContextLoadingSettings resource not found");
215+
228216
let runtime_container_res_id = world
229217
.resource_id::<RuntimeContainer<P>>()
230218
.expect("RuntimeContainer resource not found");
231219

232-
access.add_resource_read(context_loading_settings_res_id);
220+
let script_context_res_id = world
221+
.resource_id::<ScriptContext<P>>()
222+
.expect("Scripts resource not found");
223+
233224
access.add_resource_read(runtime_container_res_id);
225+
access.add_resource_read(script_context_res_id);
234226

235227
system.add(access);
236228
}
@@ -243,9 +235,6 @@ impl<'w, P: IntoScriptPluginParams> DynamicHandlerContext<'w, P> {
243235
unsafe {
244236
Self {
245237
script_context: system.get_resource().expect("Scripts resource not found"),
246-
context_loading_settings: system
247-
.get_resource()
248-
.expect("ContextLoadingSettings resource not found"),
249238
runtime_container: system
250239
.get_resource()
251240
.expect("RuntimeContainer resource not found"),
@@ -268,22 +257,11 @@ impl<'w, P: IntoScriptPluginParams> DynamicHandlerContext<'w, P> {
268257
};
269258

270259
// call the script
271-
let pre_handling_initializers = &self
272-
.context_loading_settings
273-
.context_pre_handling_initializers;
274260
let runtime = &self.runtime_container.runtime;
275261

276262
let mut context = context.lock();
277263

278-
P::handle(
279-
payload,
280-
context_key,
281-
label,
282-
&mut context,
283-
pre_handling_initializers,
284-
runtime,
285-
guard,
286-
)
264+
P::handle(payload, context_key, label, &mut context, runtime, guard)
287265
}
288266
}
289267

@@ -684,7 +662,10 @@ mod test {
684662
};
685663
use test_utils::make_test_plugin;
686664

687-
use crate::BMSScriptingInfrastructurePlugin;
665+
use crate::{
666+
BMSScriptingInfrastructurePlugin,
667+
config::{GetPluginThreadConfig, ScriptingPluginConfiguration},
668+
};
688669

689670
use super::*;
690671

crates/bevy_mod_scripting_core/src/bindings/world.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ use bevy_ecs::{
5353
component::Mutable,
5454
hierarchy::{ChildOf, Children},
5555
system::Command,
56+
world::WorldId,
5657
};
5758
use bevy_platform::collections::HashMap;
5859
use bevy_reflect::{TypeInfo, VariantInfo};
@@ -80,6 +81,13 @@ pub struct WorldAccessGuard<'w> {
8081
/// stored separate from the contents of the guard
8182
invalid: Rc<AtomicBool>,
8283
}
84+
impl WorldAccessGuard<'_> {
85+
/// Returns the id of the world this guard provides access to
86+
pub fn id(&self) -> WorldId {
87+
self.inner.cell.id()
88+
}
89+
}
90+
8391
/// Used to decrease the stack size of [`WorldAccessGuard`]
8492
pub(crate) struct WorldAccessGuardInner<'w> {
8593
/// Safety: cannot be used unless the scope depth is less than the max valid scope

crates/bevy_mod_scripting_core/src/commands.rs

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@ use crate::{
1616
handler::{handle_script_errors, send_callback_response},
1717
script::{DisplayProxy, ScriptAttachment, StaticScripts},
1818
};
19-
use ::{
19+
use bevy_ecs::{system::Command, world::World};
20+
use bevy_log::{error, info, trace};
21+
use {
2022
bevy_asset::{Assets, Handle},
2123
bevy_ecs::event::Events,
2224
bevy_log::{debug, warn},
2325
};
24-
use bevy_ecs::{system::Command, world::World};
25-
use bevy_log::{error, info, trace};
2626

2727
/// Detaches a script, invoking the `on_script_unloaded` callback if it exists, and removes the script from the static scripts collection.
2828
pub struct DeleteScript<P: IntoScriptPluginParams> {
@@ -157,10 +157,6 @@ impl<P: IntoScriptPluginParams> CreateOrUpdateScript<P> {
157157
attachment,
158158
content,
159159
context,
160-
&handler_ctxt.context_loading_settings.context_initializers,
161-
&handler_ctxt
162-
.context_loading_settings
163-
.context_pre_handling_initializers,
164160
guard.clone(),
165161
&handler_ctxt.runtime_container.runtime,
166162
)
@@ -176,10 +172,6 @@ impl<P: IntoScriptPluginParams> CreateOrUpdateScript<P> {
176172
let context = P::load(
177173
attachment,
178174
content,
179-
&handler_ctxt.context_loading_settings.context_initializers,
180-
&handler_ctxt
181-
.context_loading_settings
182-
.context_pre_handling_initializers,
183175
guard.clone(),
184176
&handler_ctxt.runtime_container.runtime,
185177
)?;
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
//! Init only configuration and relevant types.
2+
3+
use bevy_ecs::world::WorldId;
4+
5+
use crate::{
6+
IntoScriptPluginParams,
7+
context::{ContextInitializer, ContextPreHandlingInitializer},
8+
};
9+
10+
/// A set of global* configs keyed by the plugin params type.
11+
///
12+
/// Configuration is immutable after initialization.
13+
///
14+
/// Configs contained here should be
15+
///
16+
/// *global meaning stored in thread-locals, i.e. not annoyingly global, but pretty global.
17+
#[derive(Debug, Default)]
18+
pub struct ScriptingPluginConfiguration<P: IntoScriptPluginParams + ?Sized> {
19+
/// callbacks executed before a handler callback is executed every time
20+
pub pre_handling_callbacks: &'static [ContextPreHandlingInitializer<P>],
21+
/// callbacks executed once after creating a context but before executing it for the first time
22+
pub context_initialization_callbacks: &'static [ContextInitializer<P>],
23+
/// Whether to emit responses from the core callbacks like `on_script_loaded`.
24+
pub emit_responses: bool,
25+
}
26+
27+
impl<P: IntoScriptPluginParams + ?Sized> Clone for ScriptingPluginConfiguration<P> {
28+
fn clone(&self) -> Self {
29+
*self
30+
}
31+
}
32+
33+
impl<P: IntoScriptPluginParams + ?Sized> Copy for ScriptingPluginConfiguration<P> {}
34+
35+
/// A utility trait for accessing the readonly configuration for types that provide some.
36+
///
37+
/// This is typically implemented using the `make_plugin_config_static!` macro.
38+
///
39+
/// The default implementation will allow you to statically retrieve the configuration for a given world id.
40+
///
41+
/// I.e. this config is not quite thread-local but world-local, meaning it should play nice with tests.
42+
pub trait GetPluginThreadConfig<P: IntoScriptPluginParams + ?Sized> {
43+
/// Get a reference to the readonly configuration.
44+
fn readonly_configuration(world: WorldId) -> ScriptingPluginConfiguration<P>;
45+
46+
/// Set the configuration or overwrites it if already set.
47+
fn set_thread_config(world: WorldId, config: ScriptingPluginConfiguration<P>);
48+
}
49+
50+
#[macro_export]
51+
/// A macro to implement `WithReadonlyConfiguration` for a given plugin type using thread-local storage.
52+
macro_rules! make_plugin_config_static {
53+
($ty:ty) => {
54+
static CONFIG: std::sync::RwLock<
55+
bevy_platform::prelude::Vec<
56+
Option<ScriptingPluginConfiguration<$ty>>,
57+
>,
58+
> = std::sync::RwLock::new(bevy_platform::prelude::Vec::new());
59+
impl GetPluginThreadConfig<$ty> for $ty {
60+
fn readonly_configuration(
61+
world: bevy_ecs::world::WorldId,
62+
) -> ScriptingPluginConfiguration<$ty> {
63+
CONFIG
64+
.read()
65+
.unwrap()
66+
.get(<bevy_ecs::world::WorldId as bevy_ecs::storage::SparseSetIndex>::sparse_set_index(&world))
67+
.and_then(|c| *c)
68+
.unwrap_or_else(||
69+
panic!(
70+
"Configuration for plugin {} not set for world {:?}. Did you add the plugin to the app?",
71+
stringify!($ty),
72+
world
73+
),
74+
)
75+
}
76+
77+
fn set_thread_config(
78+
world: bevy_ecs::world::WorldId,
79+
config: ScriptingPluginConfiguration<$ty>,
80+
) {
81+
let mut guard = CONFIG.write().unwrap();
82+
let index = <bevy_ecs::world::WorldId as bevy_ecs::storage::SparseSetIndex>::sparse_set_index(&world) as usize;
83+
if index >= guard.len() {
84+
guard.resize_with(index + 1, || None);
85+
}
86+
guard[index] = Some(config);
87+
}
88+
}
89+
};
90+
}

0 commit comments

Comments
 (0)