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

Migrating from 0.23 to 0.24

Scripts

The only globals left are import_namespace, export_namespace and register_callback. Everything else, world included, is imported from a namespace, see Namespaces:

+ local world = import_namespace("bms").world
  world.spawn()

Types (Vec3, Transform, and the deprecated types table) are imported from their namespace. Bevy types are grouped by category (math, ecs, transform, ui, …), BMS’ types live in bms:

- local t = Transform.from_translation(Vec3.new(1, 2, 3))
+ local Vec3 = import_namespace("math").Vec3
+ local Transform = import_namespace("transform").Transform
+ local t = Transform.from_translation(Vec3.new(1, 2, 3))

BMS’ functions moved to bms: construct, system_builder, run_script_callback, queue_script_callback, pack_args, unpack_args and log_*. entity and script_asset are now functions:

+ local bms = import_namespace("bms")
- log_info("hi")
+ bms.log_info("hi")
- world.despawn(entity)
+ world.despawn(bms.current_entity())
- script_asset
+ bms.current_script()

Query results are plain lists of the entity followed by the components, result:entity() and result:components() are removed:

  for _, result in pairs(world.query():component(Transform):build()) do
-     local entity, transform = result:entity(), result:components()[1]
+     local entity, transform = result[1], result[2]
  end

Other changes:

  • world.get_type_by_name only takes full paths, i.e. "bevy_transform::components::transform::Transform" or "script.my_game.Enemy".
  • Numbers work on the left of operators: 2 * v, 1 - v (dispatched to mul, rsub, …), in Rhai too.

Rust

  • Script globals are removed (AppScriptGlobalsRegistry, #[script_globals], register_static*, GlobalNamespace, FunctionScope::Global, DummyScriptFunctionRegistry, use_dummy_registry, …). Put functions in a namespace instead:
    #[script_bindings(namespace = "my_game")]
    impl NamespaceMembers {
        /// `import_namespace("my_game").spawn_enemy(5)` in scripts
        fn spawn_enemy(ctxt: FunctionCallContext, hp: usize) -> Result<(), InteropError> { .. }
    }
    Values go in AppScriptNamespaceMembers::register_value, made each time the namespace is imported.
  • Types with bindings from the bevy crates are in category namespaces. Move a type with app.override_script_namespace.
  • Registering a function name twice replaces it, there are no overloads (overwrite*, iter_overloads, remove_all_overloads are removed). Take a Union or ScriptValue for arguments of different types.
  • #[derive(ScriptType)] implements everything a type needs to be used directly in function signatures (FromScript, IntoScript, ArgMeta, GetTypeDependencies, TypedThrough). Use #[script_type(from = custom)] / (into = custom) for a hand written conversion.
  • Renames: Namespace → FunctionScope, IntoNamespace → IntoFunctionScope, FromScriptRef/IntoScriptRef → FromScriptErased/IntoScriptErased, try_call_overloads → call_operator.
  • Functions on StaticTypeRef take StaticTypeRef instead of V<StaticTypeRef>.
  • ScriptFunctionRegistry is keyed by TypeId instead of FunctionScope: register(type_id, ..), get_function(type_id, name), iter_namespace → iter_type.
  • Values allocated for scripts are owned by the references to them and freed with the last one, the allocator is removed (ReflectAllocator, AppReflectAllocator, WorldExtensions::allocator, AllocatorDiagnosticPlugin, ScriptingSystemSet::GarbageCollection, InteropError::GarbageCollectedAllocation). Drop the allocator argument:
    // before
    let reference = ReflectReference::new_allocated(value, &mut world.allocator().write());
    // after
    let reference = ReflectReference::new_allocated(value);
    new_allocated_boxed and new_allocated_boxed_parial_reflect change the same way.
  • ScriptLoadingPipeline::time_budget: Option<Duration> is replaced by tick_budget: Option<usize>, the maximum amount of pipeline steps per frame, unlimited by default. ActiveMachines::budget and RunProcessingPipelineOnce::new take the same step count.
  • ScriptAttachedMessage(a) / ScriptDetachedMessage(a) are one message, ScriptLifecycleMessage::Attached(a) / ::Detached(a), so attachments and detachments are processed in the order they were written. GetScriptHandle::get_script_handle returns an Option (None: the event doesn’t wait for its script to load) and ScriptLifecycleQueue::get_ready, formerly known as LoadedWithHandles yields (event, Option<(StrongScriptHandle, Language)>).
  • An attachment whose script is still loading holds back the attachments and detachments after it, and callbacks reach scripts in a deterministic order (attachment order, until scripts are removed), see Determinism. Lua’s math.random is seeded with 0 in new contexts.
  • Script systems are identified by attachment and callback label (adding one again replaces it) and are removed with their script. ScriptSystemSet holds a recycled slot instead of the callback label, ScriptSystemBuilder no longer implements IntoSystem (use ScriptSystemBuilder::build), and their errors are sent as ScriptErrorMessages instead of being logged. WorldAccessGuard::new_non_exclusive takes an Option<Arc<ArchetypeFilter>>, restricting component accesses to the given archetypes (None for no restriction).
  • Script system parameters are declared to Bevy as they’re accessed: resource and query components as writes (they were declared as reads, or as accessing nothing for resources, while scripts could write them), resource_read / component_read as reads. Script systems writing the same resources or components no longer run in parallel. ScriptQueryBuilder::as_query_state takes a QueryAccess: Declared declares the written components as written (for queries reported to the scheduler), MatchOnly declares all components as read (i.e. for EntityRef queries only finding entities).
  • Script systems are put in and ordered against system set values (ReflectSystemSet) instead of names: scripts register sets with world.register_new_system_set(namespace, name), Rust sets are registered into a namespace with app.register_script_system_set(namespace, name, set) (ScriptSystemSetAppExt, now in bevy_mod_scripting_bindings), both are found with world.get_system_set_by_name. ScriptSystemBuilder::{in_set, before_set, after_set} take a SystemSet. ReflectSystemSet is opaque, and only its set nodes of a schedule graph have a node id.
  • ScriptQueryResult converts to a list instead of a reference, its script functions are removed.
  • ScriptQueryResult::components is a Vec<Option<ReflectReference>>, None for absent optional components. ArchetypeFilter::allow / is_allowed and WorldAccessGuard::check_archetype_access take whether the component is written.
  • AccessByteSet::from_allowed_lists(read_write, read_only) creates subsets with read only accesses. At most 63 accesses to the same value can be claimed at once, claims beyond fail (the count used to overflow).
  • WorldAccessRange::External holds the value’s owner (AccessOwner) instead of an id, and the access map keeps it alive until the claim is released. Build it from the value ((&allocation_id).into()); WorldAccessRange is no longer Copy.

Language implementations

  • Dispatch every script operation through bevy_mod_scripting_bindings::dispatch (index, set_index, static_index, binary, unary, len, iter, display, debug), see Necessary Features. WorldExtensions::lookup_function, lookup_instance_function, lookup_type_function and lookup_reserved_function are private.
  • Convert the new ScriptValue::Type(TypeId), a type itself (what world and StaticTypeRef are).
  • Set bindings::builtins::builtins() (import_namespace, export_namespace) and your register_callback as globals, nothing else. Don’t set world, entity or script_asset anymore.
  • LuaStaticReflectReference / RhaiStaticReflectReference are LuaTypeRef / RhaiTypeRef.

Documentation tooling

  • The Lua language server files are split per namespace (bindings.d.lua, bindings.<namespace>.d.lua, point workspace.library at the directory), classes are named after their namespace (math.Vec3), and generic types are declared once (Handle<Image>, needs LuaLS 3.17+).
  • cargo xtask docgen is replaced by cargo xtask docs (all binding features, --test-bindings for the script test functions). cargo xtask bless-lad updates the LAD test fixtures.
  • ThroughTypeInfo is flat (ThroughTypeInfo::Vec(..), ThroughTypeInfo::Ref(type_info)), ThroughTypeInfo::TypeInfo is Opaque. #[derive(TypedThrough)] documents a type as opaque, #[typed_through(value)] as a value.
  • LAD: LadFieldOrVariableKind → LadTypeKind; globals is removed, builtin functions are LadFunctionNamespace::Global (the mdbook “Globals” and “Functions” pages are one “Builtins” page), namespaces have values (add_instance* → add_namespace_value_of / add_namespace_instance); new namespaces, generic_types, LadTypeDefinition::generic_type, LadFunction::receiver, LadFunctionMetadata::operator (replaces is_operator); arguments are always named and never include the call context; polymorphizied_types is removed. ArgumentVisitor has an Output type and visit_* methods.