0.23.0 - Scripts calling scripts, constructors, namespaces
See the migration guide for breaking changes.
Scripts calling scripts
Scripts can run each other’s callbacks, in any language:
run_script_callbackruns a callback right away and returns its result.queue_script_callbackruns it as soon as the current callback finishes, or inLastwhen called from a non-exclusive script system (ScriptingSystemSet::RunQueuedCallbacks).
Nested callbacks are now safe: re-entering a context that is already running (a script calling itself, scripts sharing a context, or a loop A → B → A) returns an error instead of freezing the app, and nesting is capped at 64. This is built on AppScriptCallbackRunners, which lets language-agnostic code run callbacks in any scripting language.
Constructors and enum variants
Every reflected type gets a new constructor, and enum variants are values:
local v = Vec3.new{ x = 1, y = 2 } -- z is filled in from the default
local all = Recipients.AllScripts:new{} -- construct a variant
if event.recipients:variant() == Recipients.AllScripts then ... end
Missing fields are filled in from defaults, and unknown fields are an error. The global construct follows the same rules. The generated LAD files and Lua language server definitions describe constructor payloads and variants, so editors complete and check them. In Rhai, call Type.new_.call(...), since new is reserved.
Events, messages and observers
world.trigger_event(type, value)andworld.write_message(type, value)send reflected Bevy events and messages.trigger_eventapplies all pending commands, including those its observers queued, before it returns.world.add_observer(event, callback, components?, entities?)runs a script callback when an event is triggered. That includes the component lifecycle events (AddEvent,InsertEvent,RemoveEvent, …), for native and script components alike. Observer callbacks never re-enter a busy script: they wait until it is free. Their errors are reported like those of other callbacks.world.register_new_event(namespace, name)lets scripts define their own events, triggered with the sameworld.trigger_event(event, payload).
See Events and Messages.
Namespaces and modules
Types now live in namespaces: the crate that defines them, or bms for BMS’ own types. Override this with ScriptNamespace type data or #[script_bindings(namespace = "...")].
import_namespace("bevy_transform")returns the types in a namespace. It replaces thetypesglobal, which is now deprecated.- Scripts register components and events into
script.*namespaces, and other scripts import them from there. export_namespace(namespace, name, callback)shares a script function with other scripts, in any language.- A script’s exports and observers are removed when it unloads or reloads.
- LAD files record each type’s namespace, and the Lua language server types
import_namespaceper namespace.
Types are indexed once in a shared, lazily rebuilt type catalogue, instead of once per script context. See Namespaces.
Script components and bundles
- Inserting a script component no longer leaks memory.
register_new_componentandregister_new_eventtake anallow_existingargument, which returns the existing registration instead of failing when the name is taken (useful for scripts which run again).world.insert_bundleandworld.spawn_bundleinsert bundles registered with#[reflect(Bundle)], all at once.
Fixes
on_script_reloadedalways runs afteron_script_loaded.- Fixed a rare ordering bug when iterating
Vec<T>from Lua. - A nested callback no longer invalidates the world of the callback that called it.