Skip to content

Commit 4aba668

Browse files
committed
rhai re-implementation WIP
1 parent fac74f1 commit 4aba668

File tree

23 files changed

+486
-109
lines changed

23 files changed

+486
-109
lines changed

.vscode/settings.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
},
1111
"rust-analyzer.rustc.source": "discover",
1212
"rust-analyzer.linkedProjects": [
13-
"./crates/bevy_api_gen/Cargo.toml",
13+
// "./crates/bevy_api_gen/Cargo.toml",
1414
"Cargo.toml",
1515
],
1616
"rust-analyzer.check.invocationStrategy": "per_workspace",
@@ -28,5 +28,8 @@
2828
"rust-analyzer.runnables.extraArgs": [
2929
"--profile=release-with-debug",
3030
],
31+
"rust-analyzer.cargo.features": [
32+
"bevy_mod_scripting_functions/test_functions"
33+
]
3134
// "rust-analyzer.semanticHighlighting.operator.enable": false
3235
}

assets/scripts/game_of_life.rhai

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
fn init() {
2-
let LifeState = world.get_type_by_name("LifeState");
3-
let life_state = world.get_component(entity,LifeState);
4-
let cells = life_state.cells;
1+
fn on_script_loaded() {
2+
world.info("Game of Life script loaded");
3+
// let LifeState = world.get_type_by_name("LifeState");
4+
// let life_state = world.get_component(entity,LifeState);
5+
// let cells = life_state.cells;
56

6-
// set some cells alive
7-
for x in 1..10000 {
8-
let index = rand(0..cells.len());
9-
cells[index] = 255;
10-
}
7+
// // set some cells alive
8+
// for x in 1..10000 {
9+
// let index = rand(0..cells.len());
10+
// cells[index] = 255;
11+
// }
1112
}
1213

1314
fn on_update() {

check.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@ CURRENT_DIR=$(basename "$PWD")
66
if [[ "$CURRENT_DIR" == "bevy_api_gen" ]]; then
77
cargo +nightly-2024-11-05 clippy --all-targets --message-format=json
88
else
9-
cargo clippy --workspace --all-targets --message-format=json --features="lua54 rhai rune bevy/file_watcher bevy/multi_threaded"
9+
cargo xtask check --ide-mode
10+
# cargo clippy --workspace --all-targets --message-format=json --features="lua54 rhai rune bevy/file_watcher bevy/multi_threaded "
1011
fi

crates/bevy_mod_scripting_core/src/asset.rs

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ pub enum Language {
1717
Lua,
1818
Rune,
1919
External(Cow<'static, str>),
20-
/// Initial setting before being processed by the script synchronization systems
21-
Unset,
2220
/// Set if none of the asset path to language mappers match
2321
Unknown,
2422
}
@@ -30,7 +28,6 @@ impl std::fmt::Display for Language {
3028
Language::Lua => "Lua".fmt(f),
3129
Language::Rune => "Rune".fmt(f),
3230
Language::External(cow) => cow.fmt(f),
33-
Language::Unset => "Unset".fmt(f),
3431
Language::Unknown => "Unknown".fmt(f),
3532
}
3633
}
@@ -42,9 +39,9 @@ pub struct ScriptAsset {
4239
pub content: Box<[u8]>,
4340
/// The virtual filesystem path of the asset, used to map to the script Id for asset backed scripts
4441
pub asset_path: PathBuf,
45-
pub language: Language,
4642
}
4743

44+
#[derive(Default)]
4845
pub struct ScriptAssetLoader {
4946
/// The file extensions this loader should handle
5047
pub extensions: &'static [&'static str],
@@ -76,7 +73,6 @@ impl AssetLoader for ScriptAssetLoader {
7673
let asset = ScriptAsset {
7774
content: content.into_boxed_slice(),
7875
asset_path: load_context.path().to_owned(),
79-
language: Language::Unset,
8076
};
8177
Ok(asset)
8278
}
@@ -97,7 +93,7 @@ impl ScriptAssetSettings {
9793
for mapper in &self.script_language_mappers {
9894
let language = (mapper.map)(path);
9995
match language {
100-
Language::Unset | Language::Unknown => continue,
96+
Language::Unknown => continue,
10197
_ => return language,
10298
}
10399
}
@@ -112,9 +108,7 @@ impl Default for ScriptAssetSettings {
112108
script_id_mapper: AssetPathToScriptIdMapper {
113109
map: (|path: &Path| path.to_string_lossy().into_owned().into()),
114110
},
115-
script_language_mappers: vec![AssetPathToLanguageMapper {
116-
map: (|_: &Path| Language::Unset),
117-
}],
111+
script_language_mappers: vec![],
118112
}
119113
}
120114
}
@@ -144,6 +138,7 @@ pub struct ScriptMetadata {
144138

145139
impl ScriptMetadataStore {
146140
pub fn insert(&mut self, id: AssetId<ScriptAsset>, meta: ScriptMetadata) {
141+
// TODO: new generations of assets are not going to have the same ID as the old one
147142
self.map.insert(id, meta);
148143
}
149144

crates/bevy_mod_scripting_core/src/bindings/allocator.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,6 @@ impl ReflectAllocator {
195195
/// Runs a garbage collection pass on the allocations, removing any allocations which have no more strong references
196196
/// Needs to be run periodically to prevent memory leaks
197197
pub fn clean_garbage_allocations(&mut self) {
198-
bevy::log::trace!("Cleaning garbage allocations");
199198
self.allocations.retain(|k, _| Arc::strong_count(&k.0) > 1);
200199
}
201200

crates/bevy_mod_scripting_core/src/bindings/function/from.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ use std::{
1111
path::PathBuf,
1212
};
1313

14+
use super::script_function::DynamicScriptFunctionMut;
15+
1416
/// Describes the procedure for constructing a value of type `T` from a [`ScriptValue`].
1517
///
1618
/// The [`FromScript::This`] associated type is used to allow for the implementation of this trait to return
@@ -387,3 +389,20 @@ where
387389
}
388390
}
389391
}
392+
393+
impl FromScript for DynamicScriptFunctionMut {
394+
type This<'w> = Self;
395+
396+
fn from_script(value: ScriptValue, _: WorldGuard<'_>) -> Result<Self::This<'_>, InteropError>
397+
where
398+
Self: Sized,
399+
{
400+
match value {
401+
ScriptValue::Function(f) => Ok(f),
402+
_ => Err(InteropError::value_mismatch(
403+
std::any::TypeId::of::<Self>(),
404+
value,
405+
)),
406+
}
407+
}
408+
}

crates/bevy_mod_scripting_core/src/bindings/function/mod.rs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,7 @@ use script_function::{CallerContext, DynamicScriptFunction, DynamicScriptFunctio
88

99
use crate::error::InteropError;
1010

11-
use super::{
12-
pretty_print::DisplayWithWorld, script_value::ScriptValue, WorldCallbackAccess, WorldGuard,
13-
};
11+
use super::{script_value::ScriptValue, WorldCallbackAccess, WorldGuard};
1412

1513
/// Can be implemented for callables which require dynamic access to the world to be called.
1614
///
@@ -33,11 +31,6 @@ impl CallScriptFunction for DynamicScriptFunction {
3331
) -> Result<ScriptValue, InteropError> {
3432
let args = args.into_iter().collect::<Vec<_>>();
3533
let world_callback_access = WorldCallbackAccess::from_guard(world.clone());
36-
bevy::log::debug!(
37-
"Calling function {} with args {:?}",
38-
self.name(),
39-
args.display_with_world(world.clone())
40-
);
4134
// should we be inlining call errors into the return value?
4235
let return_val = self.call(context, world_callback_access, args);
4336
match return_val {

crates/bevy_mod_scripting_core/src/bindings/function/script_function.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ pub trait GetFunctionTypeDependencies<Marker> {
116116

117117
/// The caller context when calling a script function.
118118
/// Functions can choose to react to caller preferences such as converting 1-indexed numbers to 0-indexed numbers
119-
#[derive(Clone, Copy, Debug, Reflect)]
119+
#[derive(Clone, Copy, Debug, Reflect, Default)]
120120
#[reflect(opaque)]
121121
pub struct CallerContext {
122122
pub convert_to_0_indexed: bool,

crates/bevy_mod_scripting_core/src/commands.rs

Lines changed: 24 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,6 @@ impl<P: IntoScriptPluginParams> CreateOrUpdateScript<P> {
120120

121121
impl<P: IntoScriptPluginParams> Command for CreateOrUpdateScript<P> {
122122
fn apply(self, world: &mut bevy::prelude::World) {
123-
debug!(
124-
"CreateOrUpdateScript command applying to script_id: {}",
125-
self.id
126-
);
127123
let settings = world
128124
.get_resource::<ContextLoadingSettings<P>>()
129125
.unwrap()
@@ -148,24 +144,28 @@ impl<P: IntoScriptPluginParams> Command for CreateOrUpdateScript<P> {
148144
let mut script = scripts.scripts.get_mut(&self.id);
149145
let previous_context_id = script.as_ref().map(|s| s.context_id);
150146
debug!(
151-
"CreateOrUpdateScript command applying with to (script_id: {}, previous_context_id: {:?})",
147+
"{}: CreateOrUpdateScript command applying (script_id: {}, previous_context_id: {:?})",
148+
P::LANGUAGE,
152149
self.id, previous_context_id
153150
);
154151

155152
// If None assign new context ID, otherwise assign the old one
156153
// If re-loading and different from the previous one, the old one will be removed
157154
let current_context_id = (assigner.assign)(script.as_deref(), &self.id, &self.content, &mut contexts);
158-
debug!("Context assigned: {:?}", current_context_id);
155+
156+
debug!("{}: New context assigned?: {:?}", P::LANGUAGE, current_context_id.is_none() || current_context_id != previous_context_id);
159157

160158
let current_context_id = if let Some(id) = current_context_id {
161159
// reload existing context
162160
id
163161
} else {
162+
let log_context = format!("{}: Loading script: {}", P::LANGUAGE, self.id);
163+
bevy::log::info!("{}", log_context);
164164
let ctxt = (builder.load)(&self.id, &self.content, &settings.context_initializers, &settings.context_pre_handling_initializers, world, &mut runtime.runtime);
165165
match ctxt {
166166
Ok(ctxt) => contexts.insert(ctxt),
167167
Err(e) => {
168-
handle_script_errors(world, [e.with_context(format!("Loading script with id: {}. Language: {}", self.id, P::LANGUAGE))].into_iter());
168+
handle_script_errors(world, [e.with_context(log_context)].into_iter());
169169
return;
170170
}
171171
}
@@ -174,22 +174,21 @@ impl<P: IntoScriptPluginParams> Command for CreateOrUpdateScript<P> {
174174

175175
if let Some(previous) = previous_context_id {
176176
if let Some(previous_context_id) = contexts.get_mut(previous) {
177+
let log_context = format!("{}: Reloading script: {}.", P::LANGUAGE, self.id);
178+
bevy::log::info!("{}", log_context);
177179
match (builder.reload)(&self.id, &self.content, previous_context_id, &settings.context_initializers, &settings.context_pre_handling_initializers, world, &mut runtime.runtime) {
178180
Ok(_) => {},
179181
Err(e) => {
180-
handle_script_errors(world, [e.with_context(format!("Reloading script with id: {}. Language: {}", self.id, P::LANGUAGE))].into_iter());
182+
handle_script_errors(world, [e.with_context(log_context)].into_iter());
181183
return;
182184
}
183185
};
184186
} else {
185-
bevy::log::error!("Could not find previous context with id: {}. Could not reload script id: {}", previous, self.id);
187+
bevy::log::error!("{}: Could not find previous context with id: {}. Could not reload script: {}", P::LANGUAGE, previous, self.id);
186188
}
187189

188190
if previous != current_context_id {
189-
debug!(
190-
"Script is being moved to a new context with id: {}, removing up old context.",
191-
current_context_id
192-
);
191+
bevy::log::info!("{}: Unloading script with id: {}. As it was assigned to a new context", P::LANGUAGE, self.id);
193192
script.as_deref_mut().unwrap().context_id = current_context_id;
194193
(assigner.remove)(previous, script.unwrap(), &mut contexts);
195194
}
@@ -199,24 +198,22 @@ impl<P: IntoScriptPluginParams> Command for CreateOrUpdateScript<P> {
199198
match (runner)(vec![], bevy::ecs::entity::Entity::from_raw(0), &self.id, &OnScriptLoaded::into_callback_label(), context, &settings.context_pre_handling_initializers, &mut runtime.runtime, world) {
200199
Ok(_) => {},
201200
Err(e) => {
202-
handle_script_errors(world, [e.with_context(format!("Running initialization hook for script with id: {}. Language: {}", self.id, P::LANGUAGE))].into_iter());
201+
handle_script_errors(world, [e.with_context(format!("{}: Running initialization hook for script with id: {}", P::LANGUAGE, self.id))].into_iter());
203202
},
204203
}
204+
205+
// we only want to insert the script if a context is present, otherwise something went wrong
206+
scripts.scripts.insert(
207+
self.id.clone(),
208+
Script {
209+
id: self.id,
210+
asset: self.asset,
211+
context_id: current_context_id,
212+
},
213+
);
205214
} else {
206-
bevy::log::error!("Could not find context with id: {}. Could not run initialization hook for script id: {}. This may be because the script failed to load.", current_context_id, self.id);
215+
bevy::log::error!("{}: Context loading failed for script: {}. Did not run on_script_loaded hook",P::LANGUAGE ,self.id);
207216
}
208-
209-
// now we can insert the actual script
210-
scripts.scripts.insert(
211-
self.id.clone(),
212-
Script {
213-
id: self.id,
214-
asset: self.asset,
215-
context_id: current_context_id,
216-
},
217-
);
218-
219-
// finally we trigger on_script_loaded
220217
});
221218
world.insert_resource(settings);
222219
world.insert_non_send_resource(runtime);

crates/bevy_mod_scripting_core/src/handler.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ pub type HandlerFn<P> = fn(
1616
pre_handling_initializers: &[ContextPreHandlingInitializer<P>],
1717
runtime: &mut <P as IntoScriptPluginParams>::R,
1818
world: &mut World,
19-
) -> Result<(), ScriptError>;
19+
) -> Result<ScriptValue, ScriptError>;
2020

2121
/// A resource that holds the settings for the callback handler for a specific combination of type parameters
2222
#[derive(Resource)]

0 commit comments

Comments
 (0)