Skip to content

feat: Add on_script_reloaded callback. #421

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 61 additions & 3 deletions crates/bevy_mod_scripting_core/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::{
context::ContextBuilder,
error::{InteropError, ScriptError},
event::{
CallbackLabel, IntoCallbackLabel, OnScriptLoaded, OnScriptUnloaded,
CallbackLabel, IntoCallbackLabel, OnScriptLoaded, OnScriptReloaded, OnScriptUnloaded,
ScriptCallbackResponseEvent,
},
extractors::{with_handler_system_state, HandlerContext},
Expand Down Expand Up @@ -150,6 +150,7 @@ impl<P: IntoScriptPluginParams> CreateOrUpdateScript<P> {
#[profiling::all_functions]
impl<P: IntoScriptPluginParams> Command for CreateOrUpdateScript<P> {
fn apply(self, world: &mut bevy::prelude::World) {
let mut reload_state = None;
let success = with_handler_system_state(
world,
|guard, handler_ctxt: &mut HandlerContext<P>| {
Expand Down Expand Up @@ -194,11 +195,57 @@ impl<P: IntoScriptPluginParams> Command for CreateOrUpdateScript<P> {
// it can potentially be loaded but without a successful script reload but that
// leaves us in an okay state
handler_ctxt.scripts.scripts.insert(self.id.clone(), script);
} else {
match handler_ctxt.call_dynamic_label(
&OnScriptReloaded::into_callback_label(),
&self.id,
Entity::from_raw(0),
vec![ScriptValue::Bool(true), ScriptValue::Unit],
guard.clone(),
) {
Ok(state) => {
reload_state = Some(state);
}
Err(err) => {
handle_script_errors(
guard.clone(),
vec![err
.with_script(self.id.clone())
.with_context(P::LANGUAGE)
.with_context("saving reload state (shared-context)")]
.into_iter(),
);
}
}
}
bevy::log::debug!("{}: reloading script with id: {}", P::LANGUAGE, self.id);
self.reload_context(guard.clone(), handler_ctxt)
}
None => {
match handler_ctxt.call_dynamic_label(
&OnScriptReloaded::into_callback_label(),
&self.id,
Entity::from_raw(0),
vec![ScriptValue::Bool(true), ScriptValue::Unit],
guard.clone(),
) {
Ok(state) => {
reload_state = Some(state);
}
Err(err) => {
let missing_script = err.downcast_interop_inner().map(|e| matches!(e, crate::error::InteropErrorInner::MissingScript { .. })).unwrap_or(false);
if !missing_script {
handle_script_errors(
guard.clone(),
vec![err
.with_script(self.id.clone())
.with_context(P::LANGUAGE)
.with_context("saving reload state")]
.into_iter(),
);
}
}
}
bevy::log::debug!("{}: loading script with id: {}", P::LANGUAGE, self.id);
self.load_context(guard.clone(), handler_ctxt)
}
Expand Down Expand Up @@ -235,13 +282,24 @@ impl<P: IntoScriptPluginParams> Command for CreateOrUpdateScript<P> {
// immediately run command for callback, but only if loading went fine
if success {
RunScriptCallback::<P>::new(
self.id,
self.id.clone(),
Entity::from_raw(0),
OnScriptLoaded::into_callback_label(),
vec![],
false,
)
.apply(world)
.apply(world);

if let Some(state) = reload_state {
RunScriptCallback::<P>::new(
self.id,
Entity::from_raw(0),
OnScriptReloaded::into_callback_label(),
vec![ScriptValue::Bool(false), state],
false,
)
.apply(world);
}
}
}
}
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_mod_scripting_core/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ macro_rules! callback_labels {
callback_labels!(
OnScriptLoaded => "on_script_loaded",
OnScriptUnloaded => "on_script_unloaded",
OnScriptReloaded => "on_script_reloaded",
);

/// A trait for types that can be converted into a callback label
Expand Down
29 changes: 28 additions & 1 deletion docs/src/ScriptingReference/core-callbacks.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@

On top of callbacks which are registered by your application, BMS provides a set of core callbacks which are always available.

The two core callbacks are:
The three core callbacks are:
- `on_script_loaded`
- `on_script_unloaded`
- `on_script_reloaded`

## `on_script_loaded`

Expand All @@ -30,3 +31,29 @@ function on_script_unloaded()
print("Goodbye world")
end
```

## `on_script_reloaded`

This will be called twice: right before and after a script is reloaded.

The first parameter `save` informs whether it is time to save a value or restore it.

Before the script reload, `on_script_reloaded` is called with two arguments:
`true`, `nil`. The value returned is kept. After the script reload,
`on_script_reloaded` is called with two arguments: `false` and the value
returned from the preceding call.

```lua
mode = 1
function on_script_reloaded(save, value)
if save then
print("Before I go, take this.")
return mode
else
print("I'm back. Where was I?")
mode = value
end
end
```

Using `on_script_reloaded` one can make a script reload event not disrupt the current script state.
Loading