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

Migration Guide: 0.13 to 0.14-alpha or 0.14 to 0.15

The most important changes to be aware of in this release are:

  • BMS now uses Handle<ScriptAsset> as its principle means of referring to scripts as opposed to the ScriptId that was used previously.

  • BMS exposes many more choices for deciding how scripts are associated with the contexts in which they run.

  • script_id is replaced with script_asset which now has a type of Handle<ScriptAsset>.

Handles

The use of handles for scripts is perhaps the biggest user-facing change. It makes BMS asset usage follow Bevy's conventions. It requires less configuration. It is more idiomatic.

ScriptComponent Change

In prior versions, ScriptComponent accepted a vector of ScriptIds, which was a type alias for Cow<'static, str>.

-pub struct ScriptComponent(pub Vec<Cow<'static, str>>);
+pub struct ScriptComponent(pub Vec<Handle<ScriptAsset>>);

Because ScriptComponent accepts handles, it is no longer necessary to store the handles somewhere to prevent the script from unloading. Nor is it necessary to configure an asset path to script id mapper.

ScriptComponent(vec![asset_server.load("foo.lua")])

It is still beneficial to retain script assets in memory, for certain features to work. For example, script_asset will not be able to retrieve the asset path of the script if it's not retained.

ScriptId Change

The ScriptId has changed from being a string to being an asset ID.

-type ScriptId = Cow<'static, str>
+type ScriptId = AssetId<ScriptAsset>

No Evaluation on Load

In prior versions, BMS would immediately evaluate a ScriptAsset when it was loaded, and if multiple script assets were loaded, they would be evaluated in non-deterministic order. (See issue #426.) Script assets no longer evaluate immediately. Script assets are only evaluated when their handles are added to a ScriptComponent or they are added to StaticScripts.

In addition when a ScriptComponent loads its scripts, it loads them sequentially.

ScriptAssetSettings Removed

The ScriptAssetSettings has been removed. Let us address each of its fields in turn.

script_id_mapper

See AssetPathToScriptIdMapper section.

extension_to_language_map and supported_extensions

This is now represented by the LanguageExtensions resource, which can be configured directly during initialization

pub struct LanguageExtensions(HashMap<&'static str, Language>);

or by the ConfigureScriptAssetSettings trait:

app.add_supported_script_extensions(&["p8lua"], Language::Lua);

In addition one can configure the language of an asset when it is loaded:

asset_server.load_with_settings("hello.p8lua", |settings: &mut ScriptSettings| {
   settings.language = Some(Language::Lua);
});

or when it is created:

let content = String::from("x = 0");
let mut script = ScriptAsset::from(content);
script.language = Language::Lua;

ScriptMetadata and ScriptMetadataStore Removed

These were present to associate the previous ScriptId with the asset ID and language. That is no longer necessary as the ScriptAsset knows its own language.

pub struct ScriptAsset {
    /// The body of the script
    pub content: Box<[u8]>,
    /// The language of the script
    pub language: Language,
}

AssetPathToScriptIdMapper Removed

No mapper is necessary between a script and a script ID. If you have a script handle, you have its script ID

let handle: Handle<ScriptAsset> = ...;
let script_id: ScriptId = handle.id(); // ScriptId = AssetId<ScriptAsset>

and vice versa.

let script_id: ScriptId = ...;
let handle: Handle<ScriptAsset> = Handle::Weak(script_id);

Contexts

Choosing how scripts run is a big policy decision. Previously BMS had two options:

  • Each script ran in its own context.
  • All scripts ran in one context.

This was controlled by the enable_context_sharing() method on ConfigureScriptPlugin. That function is now deprecated. Instead use the set_context_policy method:

app.add_plugins(LuaScriptingPlugin::default().set_context_policy(
    ContextPolicy::shared(),
));

The reason for this change is there are many more choices than before, namely:

  • ContextPolicy::shared()
  • ContextPolicy::per_script()
  • ContextPolicy::per_entity()
  • ContextPolicy::per_entity_and_script()

See Contexts for more information.

ContextKey Added

Previously BMS used a ScriptId and sometimes an Entity to refer to a context. If there was no entity then Entity::from_raw(0) was used. Instead BMS now uses this ContextKey to look up contexts.

pub struct ContextKey {
    /// Entity if there is one.
    pub entity: Option<Entity>,
    /// Script ID if there is one.
    pub script_id: Option<Handle<ScriptAsset>>
}

This change affects the parameters for the context_pre_handling_initializers

- context_pre_handling_initializers: vec![|script_id, entity, context| {
+ context_pre_handling_initializers: vec![|context_key, context| {

and context_initializers:

- context_initializers: vec![|script_id, context| {
+ context_initializers: vec![|context_key, context| {

Recipients Changes

The Recipients enum now looks like this:

#[derive(Clone, Debug)]
pub enum Recipients {
    /// The event needs to be handled by all scripts, if multiple scripts share a context, the event will be sent once per script in the context.
    AllScripts,
    /// The event is to be handled by all unique contexts, i.e. if two scripts share the same context, the event will be sent only once per the context.
    AllContexts,
    /// The event is to be handled by a specific script-entity pair
    ScriptEntity(ScriptId, Entity),
    /// the event is to be handled by a specific static script
    StaticScript(ScriptId),
}

This aligns with how scripts are associated with contexts better.

ScriptCallbackEvent changes

The language for recipients is now controlled through the language field of the ScriptCallbackEvent. This allows for more flexibility in how callbacks are handled, especially when multiple languages are involved.

If no language is specified, the callback will apply to all languages.

#[derive(Clone, Event, Debug)]
#[non_exhaustive]
pub struct ScriptCallbackEvent {
    /// The label of the callback
    pub label: CallbackLabel,
    /// The recipients of the callback
    pub recipients: Recipients,
    /// The language of the callback, if unspecified will apply to all languages
+   pub language: Option<Language>
    /// The arguments to the callback
    pub args: Vec<ScriptValue>,
    /// Whether the callback should emit a response event
    pub trigger_response: bool,
}

Bindings Changes

ScriptId

script_id is replaced with script_asset which now has a type of Handle<ScriptAsset>. This means that you can no longer use a string to refer to a script, but rather you must use a handle.

Scripts can still access the asset path of the script using:

-- prints: "my_script.lua"
print(script_asset:asset_path())

ScriptAttachment

The concept of script attachments has been introduced to describe the idea of a script instance. Previously this was muddied due to the fact script ID's were the primary way to refer to scripts. Now the instance of a script and its mapping to a context is clearer.

This is reflected in a brand new set of bindings:

-- will create backing ScriptAttachment instances, that can be used in other bindings.
local entity_script = ScriptAttachment.new_entity_script(entity, script_asset)
local static_script = ScriptAttachment.new_static_script(script_asset)

System Builder

the system builder no longer accepts script id's as parameters. Instead it accepts script attachments

-system_builder("my_callback", "this_script_id.lua")
+system_builder("my_callback", ScriptAttachment.new_static_script(script_asset))