Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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_callback runs a callback right away and returns its result.
  • queue_script_callback runs it as soon as the current callback finishes, or in Last when 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) and world.write_message(type, value) send reflected Bevy events and messages. trigger_event applies 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 same world.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 the types global, 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_namespace per 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_component and register_new_event take an allow_existing argument, which returns the existing registration instead of failing when the name is taken (useful for scripts which run again).
  • world.insert_bundle and world.spawn_bundle insert bundles registered with #[reflect(Bundle)], all at once.

Fixes

  • on_script_reloaded always runs after on_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.