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 4 commits into
base: main
Choose a base branch
from

Conversation

shanecelis
Copy link
Contributor

Summary

I added an on_script_reloaded function that is called before and after on_script_loaded on reloads. This addresses the issue I filed #419.

I updated the core-callbacks doc with example code. Here's the example code shown there:

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

I'm not certain about the name. on_script_reload might be the name I'd choose with a blank slate.

Alternatives

One could designate a special variable to capture. But I think a function is preferable because a reload can interrupt at any time and keeping that value up to date all the time instead of when it's required seems inefficient.

One could break this into two functions, but they don't do anything unless both are implemented so it seemed wise to keep them as a single function.

Testing

I exercised it manually with errors in both the saving of state and restoring state, and both were reported as the usual mechanism.

Mistake

I started this branch on top of my other pull request #418. Oops. Luckily #418 is not controversial or large.

@shanecelis
Copy link
Contributor Author

A further alternative

I guess as an alternative going with the two functions, one could do this:

mode = 1
function on_script_reload()
    print("Before I go, take this.")
    return mode
end

function on_script_loaded(value)
    if value then
        print("I'm back. Where was I?")
        mode = value
    end
end

My criticism above doesn't apply because on_script_loaded() is useful without on_script_reload(). We'd then only have this set of function calls when reloading:

  1. on_script_loaded()
  2. INITIATE RELOAD
  3. value = on_script_reload()
  4. CONTEXT CLEARED
  5. on_script_loaded(value)

The preceding design looks like this for comparison:

  1. on_script_loaded()
  2. INITIATE RELOAD
  3. value = on_script_reload(true)
  4. CONTEXT CLEARED
  5. on_script_loaded()
  6. on_script_reload(false, value)

@shanecelis
Copy link
Contributor Author

NOTE: This currently only works when context sharing is enabled. I made an attempt to support non-shared contexts, but it seemed potentially infeasible. I'd love to hear @makspll's take on whether that seems right.

@makspll makspll requested a review from Copilot May 25, 2025 10:59
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds an on_script_reloaded callback to support state preservation during script reloads, addressing issue #419.

  • Adds documentation for the on_script_reloaded callback.
  • Registers the new callback event in the event system.
  • Implements state saving and restoring logic during script reloads.

Reviewed Changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
docs/src/ScriptingReference/core-callbacks.md Updates documentation to include on_script_reloaded details and example code.
crates/bevy_mod_scripting_core/src/event.rs Registers the OnScriptReloaded callback label.
crates/bevy_mod_scripting_core/src/commands.rs Implements state handling for on_script_reloaded in the script reloading command.
crates/bevy_mod_scripting_core/src/asset.rs Adds a warning when a script’s language is unknown.

Repository owner deleted a comment from Copilot AI May 25, 2025
Repository owner deleted a comment from Copilot AI May 25, 2025
&OnScriptReloaded::into_callback_label(),
&self.id,
Entity::from_raw(0),
vec![ScriptValue::Bool(true)],
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ for languages like rhai, where the arity of the callback is used to actually identify the function in the script, we would probably want to pass in the same number of arguments both times, but instead pass in a None for the missing argument

Copy link
Contributor

🐰 Bencher Report

Branchreload-hook
Testbedlinux-gha

⚠️ WARNING: No Threshold found!

Without a Threshold, no Alerts will ever be generated.

Click here to create a new Threshold
For more information, see the Threshold documentation.
To only post results if a Threshold exists, set the --ci-only-thresholds flag.

Click to view all benchmark results
BenchmarkLatencynanoseconds (ns)
component/access Lua📈 view plot
⚠️ NO THRESHOLD
3,447.00 ns
component/access Rhai📈 view plot
⚠️ NO THRESHOLD
6,044.20 ns
component/get Lua📈 view plot
⚠️ NO THRESHOLD
2,120.70 ns
component/get Rhai📈 view plot
⚠️ NO THRESHOLD
4,849.10 ns
conversions/Mut::from📈 view plot
⚠️ NO THRESHOLD
83.09 ns
conversions/Ref::from📈 view plot
⚠️ NO THRESHOLD
84.00 ns
conversions/ScriptValue::List📈 view plot
⚠️ NO THRESHOLD
259.13 ns
conversions/ScriptValue::Map📈 view plot
⚠️ NO THRESHOLD
1,178.70 ns
conversions/ScriptValue::Reference::from_into📈 view plot
⚠️ NO THRESHOLD
26.30 ns
conversions/Val::from_into📈 view plot
⚠️ NO THRESHOLD
225.42 ns
function/call 4 args Lua📈 view plot
⚠️ NO THRESHOLD
1,604.50 ns
function/call 4 args Rhai📈 view plot
⚠️ NO THRESHOLD
1,397.50 ns
function/call Lua📈 view plot
⚠️ NO THRESHOLD
239.30 ns
function/call Rhai📈 view plot
⚠️ NO THRESHOLD
426.06 ns
loading/empty Lua📈 view plot
⚠️ NO THRESHOLD
103,780.00 ns
loading/empty Rhai📈 view plot
⚠️ NO THRESHOLD
1,137,400.00 ns
math/vec mat ops Lua📈 view plot
⚠️ NO THRESHOLD
6,878.80 ns
math/vec mat ops Rhai📈 view plot
⚠️ NO THRESHOLD
5,878.80 ns
query/10 entities Lua📈 view plot
⚠️ NO THRESHOLD
17,611.00 ns
query/10 entities Rhai📈 view plot
⚠️ NO THRESHOLD
21,193.00 ns
query/100 entities Lua📈 view plot
⚠️ NO THRESHOLD
38,793.00 ns
query/100 entities Rhai📈 view plot
⚠️ NO THRESHOLD
32,944.00 ns
query/1000 entities Lua📈 view plot
⚠️ NO THRESHOLD
251,080.00 ns
query/1000 entities Rhai📈 view plot
⚠️ NO THRESHOLD
164,980.00 ns
reflection/10 Lua📈 view plot
⚠️ NO THRESHOLD
5,461.80 ns
reflection/10 Rhai📈 view plot
⚠️ NO THRESHOLD
14,357.00 ns
reflection/100 Lua📈 view plot
⚠️ NO THRESHOLD
45,161.00 ns
reflection/100 Rhai📈 view plot
⚠️ NO THRESHOLD
710,310.00 ns
resource/access Lua📈 view plot
⚠️ NO THRESHOLD
3,122.00 ns
resource/access Rhai📈 view plot
⚠️ NO THRESHOLD
5,291.20 ns
resource/get Lua📈 view plot
⚠️ NO THRESHOLD
1,812.20 ns
resource/get Rhai📈 view plot
⚠️ NO THRESHOLD
4,159.20 ns
🐰 View full continuous benchmarking report in Bencher

@makspll
Copy link
Owner

makspll commented May 25, 2025

My thoughts so far:

In favour ✅

  • I think the logic is sound, having an explicit callback you have control of as a script writer is nice
    • I suppose we can also go in the direction of having on_script_serialized and on_script_deserialized if we want to have more explicit control from the rust side
  • Since working with shared contexts is currently impossible and I am not actively working on them, and with this making them usable, very happy to merge and experiment with this approach

Points of hmm 🤔

  • it seems to me this should be possible to make work in non-shared contexts too
    • what were the problems you faced here?
    • having these callbacks work slightly differently between the two modes of bms would be unnecessarily confusing IMO
  • (very minor) performance downgrade
    • but ONLY on loads and reloads, which I don't really have a problem with, and in the future we'd need to add more callbacks into this lifecycle anyhow (so optimizing them somehow will be future work)


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

Before the reload, it is called with one argument: `true`. After the script is reloaded, it is called with two parameters: the first is `false` and the second is value returned from before.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as of now this seems to be misleading for non-shared contexts, in which case only the second half of the callback would happen

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is misleading. The second callback though will never happen because it's dependent on the first callback happening.

Sorry, I wrote it before I realized it didn't cover non-shared context.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ohh, I didn't catch the second conditional

@shanecelis
Copy link
Contributor Author

  • it seems to me this should be possible to make work in non-shared contexts too

    • what were the problems you faced here?

Let me see if I can recreate the problem I faced. Huh. Well, that's funny. It's working now and it's as easy as I hoped it would be. New PR #424 incoming.

The block of code that calls the on_script_reloaded(true) is virtually identical for both cases and maybe can be extracted into one block. The shared-context case had some conditional code that suggested it made sense to only call it in one of its cases. It works now for both cases, but I'll leave it to your more practiced hands on this code base if the code duplication ought to be refactored.

  • having these callbacks work slightly differently between the two modes of bms would be unnecessarily confusing IMO

I agree. It'd be best to unify them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants