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()

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.
  • 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.