Skip to content

Commit 7595742

Browse files
feat: Improvements to BMS in multi-language context (#194)
* fix: bugs to do with multiple languages being initialized * rhai re-implementation WIP * feat: switch vscode to xtask * re-write lua tests to use common integration test framework * setup rhai tests * add assertions * start writing marshalling code for rhai * move world pointers to thread locals * move namespace stuff into core * chore(codegen): update bevy bindings (#195) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * refactor function interface, reduce amount of string cloning, happy cows * remove extra import, fix lua to_string * clean up comments * remove rhai and rune support for now * correct for changed features in xtasks * Add note in readme about removal of features * start adding documentation for new languages * format * fix failing test utils test * generate matrix from xtask --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent fc03230 commit 7595742

File tree

85 files changed

+6165
-5959
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

85 files changed

+6165
-5959
lines changed

.github/workflows/bevy_mod_scripting.yml

-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ jobs:
3232
{label: Windows, os: windows-latest },
3333
{label: MacOS, os: macOS-latest },
3434
{label: Ubuntu, os: ubuntu-latest },
35-
{label: Ubuntu Aarch64, os: ubuntu-latest }
3635
]
3736
steps:
3837
- name: Checkout

.vscode/settings.json

+5-3
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,10 @@
1010
},
1111
"rust-analyzer.rustc.source": "discover",
1212
"rust-analyzer.linkedProjects": [
13-
"./crates/bevy_api_gen/Cargo.toml",
13+
// "./crates/bevy_api_gen/Cargo.toml",
1414
"Cargo.toml",
1515
],
16-
"rust-analyzer.check.invocationStrategy": "per_workspace",
17-
"rust-analyzer.check.invocationLocation": "workspace",
16+
"rust-analyzer.check.invocationStrategy": "once",
1817
"rust-analyzer.check.overrideCommand": [
1918
"/home/makspll/git/bevy_mod_scripting/check.sh"
2019
],
@@ -28,5 +27,8 @@
2827
"rust-analyzer.runnables.extraArgs": [
2928
"--profile=release-with-debug",
3029
],
30+
// "rust-analyzer.cargo.features": [
31+
// "bevy_mod_scripting_functions/test_functions"
32+
// ]
3133
// "rust-analyzer.semanticHighlighting.operator.enable": false
3234
}

CONTRIBUTING.MD

+1-2
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,7 @@ Enhancement suggestions are tracked as [GitHub issues](https://github.com/makspl
135135

136136
### Your First Code Contribution
137137

138-
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.
139-
138+
Before contributing see the [book](https://makspll.github.io/bevy_mod_scripting) for helpful guides on how to get started with the project.
140139

141140

142141
### Improving The Documentation

Cargo.toml

+17-18
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ name = "bevy_mod_scripting"
1717
path = "src/lib.rs"
1818

1919
[package.metadata."docs.rs"]
20-
features = ["lua54", "rhai", "rune"]
20+
features = ["lua54"]
2121

2222
[features]
2323
default = ["core_functions", "bevy_bindings", "unsafe_lua_modules"]
@@ -44,44 +44,48 @@ mlua_macros = ["bevy_mod_scripting_lua/mlua_macros"]
4444
mlua_async = ["bevy_mod_scripting_lua/mlua_async"]
4545

4646
## rhai
47-
rhai = ["bevy_mod_scripting_rhai"]
47+
# rhai = ["bevy_mod_scripting_rhai"]
4848

4949
## rune
50-
rune = ["bevy_mod_scripting_rune"]
50+
# rune = ["bevy_mod_scripting_rune"]
5151

5252
[dependencies]
5353
bevy = { workspace = true }
5454
bevy_mod_scripting_core = { workspace = true }
5555
bevy_mod_scripting_lua = { path = "crates/languages/bevy_mod_scripting_lua", version = "0.9.0-alpha.2", optional = true }
56-
bevy_mod_scripting_rhai = { path = "crates/languages/bevy_mod_scripting_rhai", version = "0.9.0-alpha.2", optional = true }
57-
bevy_mod_scripting_rune = { path = "crates/languages/bevy_mod_scripting_rune", version = "0.9.0-alpha.2", optional = true }
56+
# bevy_mod_scripting_rhai = { path = "crates/languages/bevy_mod_scripting_rhai", version = "0.9.0-alpha.2", optional = true }
57+
# bevy_mod_scripting_rune = { path = "crates/languages/bevy_mod_scripting_rune", version = "0.9.0-alpha.2", optional = true }
5858
bevy_mod_scripting_functions = { workspace = true }
5959

6060
[workspace.dependencies]
6161
bevy = { version = "0.15.0", default-features = false }
6262
bevy_mod_scripting_core = { path = "crates/bevy_mod_scripting_core", version = "0.9.0-alpha.2" }
63-
bevy_mod_scripting_functions = { path = "crates/bevy_mod_scripting_functions", version = "0.9.0-alpha.2" }
64-
test_utils = { path = "crates/test_utils" }
63+
bevy_mod_scripting_functions = { path = "crates/bevy_mod_scripting_functions", version = "0.9.0-alpha.2", default-features = false }
6564
mlua = { version = "0.10" }
66-
rhai = { version = "1.20.1" }
65+
# rhai = { version = "1.20.1" }
66+
67+
# test utilities
68+
script_integration_test_harness = { path = "crates/script_integration_test_harness" }
69+
test_utils = { path = "crates/test_utils" }
6770

6871
[dev-dependencies]
6972
bevy = { workspace = true, default-features = true }
7073
clap = { version = "4.1", features = ["derive"] }
7174
rand = "0.8.5"
7275
bevy_console = "0.13"
73-
rhai-rand = "0.1"
76+
# rhai-rand = "0.1"
7477
ansi-parser = "0.9"
7578

7679
[workspace]
7780
members = [
7881
"crates/bevy_mod_scripting_core",
7982
"crates/languages/bevy_mod_scripting_lua",
80-
"crates/languages/bevy_mod_scripting_rhai",
81-
"crates/languages/bevy_mod_scripting_rune",
83+
# "crates/languages/bevy_mod_scripting_rhai",
84+
# "crates/languages/bevy_mod_scripting_rune",
8285
"crates/test_utils",
8386
"crates/bevy_mod_scripting_functions",
8487
"crates/xtask",
88+
"crates/script_integration_test_harness",
8589
]
8690
resolver = "2"
8791
exclude = ["crates/bevy_api_gen", "crates/macro_tests"]
@@ -106,11 +110,6 @@ inherits = "release"
106110
debug = true
107111

108112
[[example]]
109-
name = "game_of_life_lua"
110-
path = "examples/lua/game_of_life.rs"
113+
name = "game_of_life"
114+
path = "examples/game_of_life.rs"
111115
required-features = ["lua54", "bevy/file_watcher", "bevy/multi_threaded"]
112-
113-
# [[example]]
114-
# required-features = ["rhai", "bevy/file_watcher", "bevy/multi_threaded"]
115-
# name = "game_of_life_rhai"
116-
# path = "examples/rhai/game_of_life.rs"

assets/scripts/game_of_life.lua

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ end
2828
function on_click(x,y)
2929
-- get the settings
3030
world.info("Lua: Clicked at x: " .. x .. " y: " .. y)
31+
print(entity)
3132
local life_state = fetch_life_state()
3233
local cells = life_state.cells
3334

assets/scripts/game_of_life.rhai

+10-9
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
fn init() {
2-
let LifeState = world.get_type_by_name("LifeState");
3-
let life_state = world.get_component(entity,LifeState);
4-
let cells = life_state.cells;
1+
fn on_script_loaded() {
2+
world.info("Game of Life script loaded");
3+
// let LifeState = world.get_type_by_name("LifeState");
4+
// let life_state = world.get_component(entity,LifeState);
5+
// let cells = life_state.cells;
56

6-
// set some cells alive
7-
for x in 1..10000 {
8-
let index = rand(0..cells.len());
9-
cells[index] = 255;
10-
}
7+
// // set some cells alive
8+
// for x in 1..10000 {
9+
// let index = rand(0..cells.len());
10+
// cells[index] = 255;
11+
// }
1112
}
1213

1314
fn on_update() {

check.sh

+2-9
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,3 @@
11
#!/bin/bash
2-
unset RUSTUP_TOOLCHAIN
3-
CURRENT_DIR=$(basename "$PWD")
4-
5-
6-
if [[ "$CURRENT_DIR" == "bevy_api_gen" ]]; then
7-
cargo +nightly-2024-11-05 clippy --all-targets --message-format=json
8-
else
9-
cargo clippy --workspace --all-targets --message-format=json --features="lua54 rhai rune bevy/file_watcher bevy/multi_threaded"
10-
fi
2+
cd "$(dirname "$0")"
3+
cargo xtask check --ide-mode

crates/bevy_api_gen/templates/header.tera

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use bevy_mod_scripting_core::{
1414
StoreDocumentation,
1515
bindings::{
1616
ReflectReference,
17-
function::from::{Ref, Mut, Val}
17+
function::{from::{Ref, Mut, Val}, namespace::{NamespaceBuilder}}
1818
}
1919
};
2020
{% if args.self_is_bms_lua %}

crates/bevy_mod_scripting_core/Cargo.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@ doc_always = []
2121

2222
# if enabled enables some common mlua trait implementations
2323
mlua_impls = ["mlua"]
24-
rhai_impls = ["rhai"]
24+
# rhai_impls = ["rhai"]
2525

2626
[dependencies]
2727
mlua = { optional = true, workspace = true }
28-
rhai = { optional = true, workspace = true }
28+
# rhai = { optional = true, workspace = true }
2929

3030
bevy = { workspace = true, default-features = false, features = [
3131
"bevy_asset",

crates/bevy_mod_scripting_core/src/asset.rs

+60-12
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,39 @@ use std::{
1010
path::{Path, PathBuf},
1111
};
1212

13+
/// Represents a scripting language. Languages which compile into another language should use the target language as their language.
14+
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
15+
pub enum Language {
16+
Rhai,
17+
Lua,
18+
Rune,
19+
External(Cow<'static, str>),
20+
/// Set if none of the asset path to language mappers match
21+
Unknown,
22+
}
23+
24+
impl std::fmt::Display for Language {
25+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26+
match self {
27+
Language::Rhai => "Rhai".fmt(f),
28+
Language::Lua => "Lua".fmt(f),
29+
Language::Rune => "Rune".fmt(f),
30+
Language::External(cow) => cow.fmt(f),
31+
Language::Unknown => "Unknown".fmt(f),
32+
}
33+
}
34+
}
35+
1336
/// Represents a script loaded into memory as an asset
1437
#[derive(Asset, TypePath, Clone)]
1538
pub struct ScriptAsset {
1639
pub content: Box<[u8]>,
1740
/// The virtual filesystem path of the asset, used to map to the script Id for asset backed scripts
1841
pub asset_path: PathBuf,
19-
pub language: Cow<'static, str>,
2042
}
2143

44+
#[derive(Default)]
2245
pub struct ScriptAssetLoader {
23-
/// Used to set the language of the script
24-
pub language: Cow<'static, str>,
2546
/// The file extensions this loader should handle
2647
pub extensions: &'static [&'static str],
2748
/// preprocessor to run on the script before saving the content to an asset
@@ -52,7 +73,6 @@ impl AssetLoader for ScriptAssetLoader {
5273
let asset = ScriptAsset {
5374
content: content.into_boxed_slice(),
5475
asset_path: load_context.path().to_owned(),
55-
language: self.language.clone(),
5676
};
5777
Ok(asset)
5878
}
@@ -62,9 +82,24 @@ impl AssetLoader for ScriptAssetLoader {
6282
}
6383
}
6484

65-
#[derive(Clone, Copy, Resource)]
85+
#[derive(Clone, Resource)]
6686
pub struct ScriptAssetSettings {
6787
pub script_id_mapper: AssetPathToScriptIdMapper,
88+
pub script_language_mappers: Vec<AssetPathToLanguageMapper>,
89+
}
90+
91+
impl ScriptAssetSettings {
92+
pub fn select_script_language(&self, path: &Path) -> Language {
93+
for mapper in &self.script_language_mappers {
94+
let language = (mapper.map)(path);
95+
match language {
96+
Language::Unknown => continue,
97+
_ => return language,
98+
}
99+
}
100+
101+
Language::Unknown
102+
}
68103
}
69104

70105
impl Default for ScriptAssetSettings {
@@ -73,6 +108,7 @@ impl Default for ScriptAssetSettings {
73108
script_id_mapper: AssetPathToScriptIdMapper {
74109
map: (|path: &Path| path.to_string_lossy().into_owned().into()),
75110
},
111+
script_language_mappers: vec![],
76112
}
77113
}
78114
}
@@ -83,22 +119,34 @@ pub struct AssetPathToScriptIdMapper {
83119
pub map: fn(&Path) -> ScriptId,
84120
}
85121

122+
#[derive(Clone, Copy)]
123+
pub struct AssetPathToLanguageMapper {
124+
pub map: fn(&Path) -> Language,
125+
}
126+
86127
/// 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.
87128
#[derive(Default, Debug, Resource)]
88-
pub struct AssetIdToScriptIdMap {
89-
pub map: HashMap<AssetId<ScriptAsset>, ScriptId>,
129+
pub struct ScriptMetadataStore {
130+
pub map: HashMap<AssetId<ScriptAsset>, ScriptMetadata>,
131+
}
132+
133+
#[derive(Debug, Clone)]
134+
pub struct ScriptMetadata {
135+
pub script_id: ScriptId,
136+
pub language: Language,
90137
}
91138

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

97-
pub fn get(&self, id: AssetId<ScriptAsset>) -> Option<&ScriptId> {
145+
pub fn get(&self, id: AssetId<ScriptAsset>) -> Option<&ScriptMetadata> {
98146
self.map.get(&id)
99147
}
100148

101-
pub fn remove(&mut self, id: AssetId<ScriptAsset>) -> Option<ScriptId> {
149+
pub fn remove(&mut self, id: AssetId<ScriptAsset>) -> Option<ScriptMetadata> {
102150
self.map.remove(&id)
103151
}
104152
}

crates/bevy_mod_scripting_core/src/bindings/allocator.rs

-1
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,6 @@ impl ReflectAllocator {
195195
/// Runs a garbage collection pass on the allocations, removing any allocations which have no more strong references
196196
/// Needs to be run periodically to prevent memory leaks
197197
pub fn clean_garbage_allocations(&mut self) {
198-
bevy::log::trace!("Cleaning garbage allocations");
199198
self.allocations.retain(|k, _| Arc::strong_count(&k.0) > 1);
200199
}
201200

crates/bevy_mod_scripting_core/src/bindings/function/from.rs

+36
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ use std::{
1111
path::PathBuf,
1212
};
1313

14+
use super::script_function::{DynamicScriptFunction, DynamicScriptFunctionMut};
15+
1416
/// Describes the procedure for constructing a value of type `T` from a [`ScriptValue`].
1517
///
1618
/// The [`FromScript::This`] associated type is used to allow for the implementation of this trait to return
@@ -387,3 +389,37 @@ where
387389
}
388390
}
389391
}
392+
393+
impl FromScript for DynamicScriptFunctionMut {
394+
type This<'w> = Self;
395+
396+
fn from_script(value: ScriptValue, _: WorldGuard<'_>) -> Result<Self::This<'_>, InteropError>
397+
where
398+
Self: Sized,
399+
{
400+
match value {
401+
ScriptValue::FunctionMut(f) => Ok(f),
402+
_ => Err(InteropError::value_mismatch(
403+
std::any::TypeId::of::<Self>(),
404+
value,
405+
)),
406+
}
407+
}
408+
}
409+
410+
impl FromScript for DynamicScriptFunction {
411+
type This<'w> = Self;
412+
413+
fn from_script(value: ScriptValue, _: WorldGuard<'_>) -> Result<Self::This<'_>, InteropError>
414+
where
415+
Self: Sized,
416+
{
417+
match value {
418+
ScriptValue::Function(f) => Ok(f),
419+
_ => Err(InteropError::value_mismatch(
420+
std::any::TypeId::of::<Self>(),
421+
value,
422+
)),
423+
}
424+
}
425+
}

crates/bevy_mod_scripting_core/src/bindings/function/into.rs

+6
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ impl IntoScript for () {
3333
self_type_dependency_only!(());
3434

3535
impl IntoScript for DynamicScriptFunctionMut {
36+
fn into_script(self, _world: WorldGuard) -> Result<ScriptValue, InteropError> {
37+
Ok(ScriptValue::FunctionMut(self))
38+
}
39+
}
40+
41+
impl IntoScript for DynamicScriptFunction {
3642
fn into_script(self, _world: WorldGuard) -> Result<ScriptValue, InteropError> {
3743
Ok(ScriptValue::Function(self))
3844
}

0 commit comments

Comments
 (0)