Skip to content

feat: Improvements to BMS in multi-language context #194

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 20 commits into from
Jan 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/workflows/bevy_mod_scripting.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ jobs:
{label: Windows, os: windows-latest },
{label: MacOS, os: macOS-latest },
{label: Ubuntu, os: ubuntu-latest },
{label: Ubuntu Aarch64, os: ubuntu-latest }
]
steps:
- name: Checkout
Expand Down
8 changes: 5 additions & 3 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,10 @@
},
"rust-analyzer.rustc.source": "discover",
"rust-analyzer.linkedProjects": [
"./crates/bevy_api_gen/Cargo.toml",
// "./crates/bevy_api_gen/Cargo.toml",
"Cargo.toml",
],
"rust-analyzer.check.invocationStrategy": "per_workspace",
"rust-analyzer.check.invocationLocation": "workspace",
"rust-analyzer.check.invocationStrategy": "once",
"rust-analyzer.check.overrideCommand": [
"/home/makspll/git/bevy_mod_scripting/check.sh"
],
Expand All @@ -28,5 +27,8 @@
"rust-analyzer.runnables.extraArgs": [
"--profile=release-with-debug",
],
// "rust-analyzer.cargo.features": [
// "bevy_mod_scripting_functions/test_functions"
// ]
// "rust-analyzer.semanticHighlighting.operator.enable": false
}
3 changes: 1 addition & 2 deletions CONTRIBUTING.MD
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,7 @@ Enhancement suggestions are tracked as [GitHub issues](https://github.com/makspl

### Your First Code Contribution

This project uses xtask to manage the build and test process. You can run `cargo xtask` to see the available commands. Run `xtask init` to setup your local development environment.

Before contributing see the [book](https://makspll.github.io/bevy_mod_scripting) for helpful guides on how to get started with the project.


### Improving The Documentation
Expand Down
35 changes: 17 additions & 18 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ name = "bevy_mod_scripting"
path = "src/lib.rs"

[package.metadata."docs.rs"]
features = ["lua54", "rhai", "rune"]
features = ["lua54"]

[features]
default = ["core_functions", "bevy_bindings", "unsafe_lua_modules"]
Expand All @@ -44,44 +44,48 @@ mlua_macros = ["bevy_mod_scripting_lua/mlua_macros"]
mlua_async = ["bevy_mod_scripting_lua/mlua_async"]

## rhai
rhai = ["bevy_mod_scripting_rhai"]
# rhai = ["bevy_mod_scripting_rhai"]

## rune
rune = ["bevy_mod_scripting_rune"]
# rune = ["bevy_mod_scripting_rune"]

[dependencies]
bevy = { workspace = true }
bevy_mod_scripting_core = { workspace = true }
bevy_mod_scripting_lua = { path = "crates/languages/bevy_mod_scripting_lua", version = "0.9.0-alpha.2", optional = true }
bevy_mod_scripting_rhai = { path = "crates/languages/bevy_mod_scripting_rhai", version = "0.9.0-alpha.2", optional = true }
bevy_mod_scripting_rune = { path = "crates/languages/bevy_mod_scripting_rune", version = "0.9.0-alpha.2", optional = true }
# bevy_mod_scripting_rhai = { path = "crates/languages/bevy_mod_scripting_rhai", version = "0.9.0-alpha.2", optional = true }
# bevy_mod_scripting_rune = { path = "crates/languages/bevy_mod_scripting_rune", version = "0.9.0-alpha.2", optional = true }
bevy_mod_scripting_functions = { workspace = true }

[workspace.dependencies]
bevy = { version = "0.15.0", default-features = false }
bevy_mod_scripting_core = { path = "crates/bevy_mod_scripting_core", version = "0.9.0-alpha.2" }
bevy_mod_scripting_functions = { path = "crates/bevy_mod_scripting_functions", version = "0.9.0-alpha.2" }
test_utils = { path = "crates/test_utils" }
bevy_mod_scripting_functions = { path = "crates/bevy_mod_scripting_functions", version = "0.9.0-alpha.2", default-features = false }
mlua = { version = "0.10" }
rhai = { version = "1.20.1" }
# rhai = { version = "1.20.1" }

# test utilities
script_integration_test_harness = { path = "crates/script_integration_test_harness" }
test_utils = { path = "crates/test_utils" }

[dev-dependencies]
bevy = { workspace = true, default-features = true }
clap = { version = "4.1", features = ["derive"] }
rand = "0.8.5"
bevy_console = "0.13"
rhai-rand = "0.1"
# rhai-rand = "0.1"
ansi-parser = "0.9"

[workspace]
members = [
"crates/bevy_mod_scripting_core",
"crates/languages/bevy_mod_scripting_lua",
"crates/languages/bevy_mod_scripting_rhai",
"crates/languages/bevy_mod_scripting_rune",
# "crates/languages/bevy_mod_scripting_rhai",
# "crates/languages/bevy_mod_scripting_rune",
"crates/test_utils",
"crates/bevy_mod_scripting_functions",
"crates/xtask",
"crates/script_integration_test_harness",
]
resolver = "2"
exclude = ["crates/bevy_api_gen", "crates/macro_tests"]
Expand All @@ -106,11 +110,6 @@ inherits = "release"
debug = true

[[example]]
name = "game_of_life_lua"
path = "examples/lua/game_of_life.rs"
name = "game_of_life"
path = "examples/game_of_life.rs"
required-features = ["lua54", "bevy/file_watcher", "bevy/multi_threaded"]

# [[example]]
# required-features = ["rhai", "bevy/file_watcher", "bevy/multi_threaded"]
# name = "game_of_life_rhai"
# path = "examples/rhai/game_of_life.rs"
1 change: 1 addition & 0 deletions assets/scripts/game_of_life.lua
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ end
function on_click(x,y)
-- get the settings
world.info("Lua: Clicked at x: " .. x .. " y: " .. y)
print(entity)
local life_state = fetch_life_state()
local cells = life_state.cells

Expand Down
19 changes: 10 additions & 9 deletions assets/scripts/game_of_life.rhai
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
fn init() {
let LifeState = world.get_type_by_name("LifeState");
let life_state = world.get_component(entity,LifeState);
let cells = life_state.cells;
fn on_script_loaded() {
world.info("Game of Life script loaded");
// let LifeState = world.get_type_by_name("LifeState");
// let life_state = world.get_component(entity,LifeState);
// let cells = life_state.cells;

// set some cells alive
for x in 1..10000 {
let index = rand(0..cells.len());
cells[index] = 255;
}
// // set some cells alive
// for x in 1..10000 {
// let index = rand(0..cells.len());
// cells[index] = 255;
// }
}

fn on_update() {
Expand Down
11 changes: 2 additions & 9 deletions check.sh
Original file line number Diff line number Diff line change
@@ -1,10 +1,3 @@
#!/bin/bash
unset RUSTUP_TOOLCHAIN
CURRENT_DIR=$(basename "$PWD")


if [[ "$CURRENT_DIR" == "bevy_api_gen" ]]; then
cargo +nightly-2024-11-05 clippy --all-targets --message-format=json
else
cargo clippy --workspace --all-targets --message-format=json --features="lua54 rhai rune bevy/file_watcher bevy/multi_threaded"
fi
cd "$(dirname "$0")"
cargo xtask check --ide-mode
2 changes: 1 addition & 1 deletion crates/bevy_api_gen/templates/header.tera
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use bevy_mod_scripting_core::{
StoreDocumentation,
bindings::{
ReflectReference,
function::from::{Ref, Mut, Val}
function::{from::{Ref, Mut, Val}, namespace::{NamespaceBuilder}}
}
};
{% if args.self_is_bms_lua %}
Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_mod_scripting_core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ doc_always = []

# if enabled enables some common mlua trait implementations
mlua_impls = ["mlua"]
rhai_impls = ["rhai"]
# rhai_impls = ["rhai"]

[dependencies]
mlua = { optional = true, workspace = true }
rhai = { optional = true, workspace = true }
# rhai = { optional = true, workspace = true }

bevy = { workspace = true, default-features = false, features = [
"bevy_asset",
Expand Down
72 changes: 60 additions & 12 deletions crates/bevy_mod_scripting_core/src/asset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,39 @@ use std::{
path::{Path, PathBuf},
};

/// Represents a scripting language. Languages which compile into another language should use the target language as their language.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum Language {
Rhai,
Lua,
Rune,
External(Cow<'static, str>),
/// Set if none of the asset path to language mappers match
Unknown,
}

impl std::fmt::Display for Language {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Language::Rhai => "Rhai".fmt(f),
Language::Lua => "Lua".fmt(f),
Language::Rune => "Rune".fmt(f),
Language::External(cow) => cow.fmt(f),
Language::Unknown => "Unknown".fmt(f),
}
}
}

/// Represents a script loaded into memory as an asset
#[derive(Asset, TypePath, Clone)]
pub struct ScriptAsset {
pub content: Box<[u8]>,
/// The virtual filesystem path of the asset, used to map to the script Id for asset backed scripts
pub asset_path: PathBuf,
pub language: Cow<'static, str>,
}

#[derive(Default)]
pub struct ScriptAssetLoader {
/// Used to set the language of the script
pub language: Cow<'static, str>,
/// The file extensions this loader should handle
pub extensions: &'static [&'static str],
/// preprocessor to run on the script before saving the content to an asset
Expand Down Expand Up @@ -52,7 +73,6 @@ impl AssetLoader for ScriptAssetLoader {
let asset = ScriptAsset {
content: content.into_boxed_slice(),
asset_path: load_context.path().to_owned(),
language: self.language.clone(),
};
Ok(asset)
}
Expand All @@ -62,9 +82,24 @@ impl AssetLoader for ScriptAssetLoader {
}
}

#[derive(Clone, Copy, Resource)]
#[derive(Clone, Resource)]
pub struct ScriptAssetSettings {
pub script_id_mapper: AssetPathToScriptIdMapper,
pub script_language_mappers: Vec<AssetPathToLanguageMapper>,
}

impl ScriptAssetSettings {
pub fn select_script_language(&self, path: &Path) -> Language {
for mapper in &self.script_language_mappers {
let language = (mapper.map)(path);
match language {
Language::Unknown => continue,
_ => return language,
}
}

Language::Unknown
}
}

impl Default for ScriptAssetSettings {
Expand All @@ -73,6 +108,7 @@ impl Default for ScriptAssetSettings {
script_id_mapper: AssetPathToScriptIdMapper {
map: (|path: &Path| path.to_string_lossy().into_owned().into()),
},
script_language_mappers: vec![],
}
}
}
Expand All @@ -83,22 +119,34 @@ pub struct AssetPathToScriptIdMapper {
pub map: fn(&Path) -> ScriptId,
}

#[derive(Clone, Copy)]
pub struct AssetPathToLanguageMapper {
pub map: fn(&Path) -> Language,
}

/// A cache of asset id's to their script id's. Necessary since when we drop an asset we won't have the ability to get the path from the asset.
#[derive(Default, Debug, Resource)]
pub struct AssetIdToScriptIdMap {
pub map: HashMap<AssetId<ScriptAsset>, ScriptId>,
pub struct ScriptMetadataStore {
pub map: HashMap<AssetId<ScriptAsset>, ScriptMetadata>,
}

#[derive(Debug, Clone)]
pub struct ScriptMetadata {
pub script_id: ScriptId,
pub language: Language,
}

impl AssetIdToScriptIdMap {
pub fn insert(&mut self, id: AssetId<ScriptAsset>, path: ScriptId) {
self.map.insert(id, path);
impl ScriptMetadataStore {
pub fn insert(&mut self, id: AssetId<ScriptAsset>, meta: ScriptMetadata) {
// TODO: new generations of assets are not going to have the same ID as the old one
self.map.insert(id, meta);
}

pub fn get(&self, id: AssetId<ScriptAsset>) -> Option<&ScriptId> {
pub fn get(&self, id: AssetId<ScriptAsset>) -> Option<&ScriptMetadata> {
self.map.get(&id)
}

pub fn remove(&mut self, id: AssetId<ScriptAsset>) -> Option<ScriptId> {
pub fn remove(&mut self, id: AssetId<ScriptAsset>) -> Option<ScriptMetadata> {
self.map.remove(&id)
}
}
1 change: 0 additions & 1 deletion crates/bevy_mod_scripting_core/src/bindings/allocator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@
}

impl ReflectAllocator {
/// Allocates a new [`Reflect`] value and returns an [`AllocationId`] which can be used to access it later.

Check warning on line 146 in crates/bevy_mod_scripting_core/src/bindings/allocator.rs

View workflow job for this annotation

GitHub Actions / Check - MacOS

unresolved link to `Reflect`

Check warning on line 146 in crates/bevy_mod_scripting_core/src/bindings/allocator.rs

View workflow job for this annotation

GitHub Actions / Check - MacOS

unresolved link to `AllocationId`

Check warning on line 146 in crates/bevy_mod_scripting_core/src/bindings/allocator.rs

View workflow job for this annotation

GitHub Actions / Check - Ubuntu

unresolved link to `Reflect`

Check warning on line 146 in crates/bevy_mod_scripting_core/src/bindings/allocator.rs

View workflow job for this annotation

GitHub Actions / Check - Ubuntu

unresolved link to `AllocationId`
/// Use [`Self::allocate_boxed`] if you already have an allocated boxed value.
pub fn allocate<T: PartialReflect>(&mut self, value: T) -> ReflectAllocationId {
self.allocate_boxed(Box::new(value))
Expand Down Expand Up @@ -187,7 +187,7 @@
self.allocations.get(id)
}

/// Deallocates the [`PartialReflect`] value with the given [`AllocationId`]

Check warning on line 190 in crates/bevy_mod_scripting_core/src/bindings/allocator.rs

View workflow job for this annotation

GitHub Actions / Check - MacOS

unresolved link to `AllocationId`

Check warning on line 190 in crates/bevy_mod_scripting_core/src/bindings/allocator.rs

View workflow job for this annotation

GitHub Actions / Check - Ubuntu

unresolved link to `AllocationId`
pub fn deallocate(&mut self, id: &ReflectAllocationId) {
self.allocations.remove(id);
}
Expand All @@ -195,7 +195,6 @@
/// Runs a garbage collection pass on the allocations, removing any allocations which have no more strong references
/// Needs to be run periodically to prevent memory leaks
pub fn clean_garbage_allocations(&mut self) {
bevy::log::trace!("Cleaning garbage allocations");
self.allocations.retain(|k, _| Arc::strong_count(&k.0) > 1);
}

Expand Down
36 changes: 36 additions & 0 deletions crates/bevy_mod_scripting_core/src/bindings/function/from.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
path::PathBuf,
};

use super::script_function::{DynamicScriptFunction, DynamicScriptFunctionMut};

/// Describes the procedure for constructing a value of type `T` from a [`ScriptValue`].
///
/// The [`FromScript::This`] associated type is used to allow for the implementation of this trait to return
Expand Down Expand Up @@ -204,7 +206,7 @@
/// Before downcasting the reference, it will claim write access to the object to ensure that the reference is valid.
///
/// However, the access is NOT released when the `Mut` is dropped. This is not unsafe but can lead to deadlocks if not released later.
/// The [`ScriptFunction`] calling mechanism will take care of releasing all accesses claimed during the function call.

Check warning on line 209 in crates/bevy_mod_scripting_core/src/bindings/function/from.rs

View workflow job for this annotation

GitHub Actions / Check - MacOS

unresolved link to `ScriptFunction`

Check warning on line 209 in crates/bevy_mod_scripting_core/src/bindings/function/from.rs

View workflow job for this annotation

GitHub Actions / Check - Ubuntu

unresolved link to `ScriptFunction`
pub struct Ref<'w, T>(pub &'w T);

impl<T> Deref for Ref<'_, T> {
Expand Down Expand Up @@ -266,7 +268,7 @@
/// Before downcasting the reference, it will claim write access to the object to ensure that the reference is valid.
///
/// However, the access is NOT released when the `Mut` is dropped. This is not unsafe but can lead to deadlocks if not released later.
/// The [`ScriptFunction`] calling mechanism will take care of releasing all accesses claimed during the function call.

Check warning on line 271 in crates/bevy_mod_scripting_core/src/bindings/function/from.rs

View workflow job for this annotation

GitHub Actions / Check - MacOS

unresolved link to `ScriptFunction`

Check warning on line 271 in crates/bevy_mod_scripting_core/src/bindings/function/from.rs

View workflow job for this annotation

GitHub Actions / Check - Ubuntu

unresolved link to `ScriptFunction`
pub struct Mut<'w, T>(pub &'w mut T);

impl<T> Deref for Mut<'_, T> {
Expand Down Expand Up @@ -387,3 +389,37 @@
}
}
}

impl FromScript for DynamicScriptFunctionMut {
type This<'w> = Self;

fn from_script(value: ScriptValue, _: WorldGuard<'_>) -> Result<Self::This<'_>, InteropError>
where
Self: Sized,
{
match value {
ScriptValue::FunctionMut(f) => Ok(f),
_ => Err(InteropError::value_mismatch(
std::any::TypeId::of::<Self>(),
value,
)),
}
}
}

impl FromScript for DynamicScriptFunction {
type This<'w> = Self;

fn from_script(value: ScriptValue, _: WorldGuard<'_>) -> Result<Self::This<'_>, InteropError>
where
Self: Sized,
{
match value {
ScriptValue::Function(f) => Ok(f),
_ => Err(InteropError::value_mismatch(
std::any::TypeId::of::<Self>(),
value,
)),
}
}
}
6 changes: 6 additions & 0 deletions crates/bevy_mod_scripting_core/src/bindings/function/into.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ impl IntoScript for () {
self_type_dependency_only!(());

impl IntoScript for DynamicScriptFunctionMut {
fn into_script(self, _world: WorldGuard) -> Result<ScriptValue, InteropError> {
Ok(ScriptValue::FunctionMut(self))
}
}

impl IntoScript for DynamicScriptFunction {
fn into_script(self, _world: WorldGuard) -> Result<ScriptValue, InteropError> {
Ok(ScriptValue::Function(self))
}
Expand Down
Loading
Loading