Skip to content

Commit defec17

Browse files
committed
improve docs
1 parent f7d5aaf commit defec17

File tree

11 files changed

+176
-71
lines changed

11 files changed

+176
-71
lines changed

crates/bevy_mod_scripting_core/src/asset.rs

Lines changed: 20 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@ use bevy::{
1616
ResMut,
1717
},
1818
reflect::TypePath,
19-
utils::HashMap,
19+
utils::hashbrown::HashMap,
2020
};
2121
use std::borrow::Cow;
2222

2323
/// Represents a scripting language. Languages which compile into another language should use the target language as their language.
24-
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
24+
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Default)]
2525
pub enum Language {
2626
/// The Rhai scripting language
2727
Rhai,
@@ -32,6 +32,7 @@ pub enum Language {
3232
/// An external scripting language
3333
External(Cow<'static, str>),
3434
/// Set if none of the asset path to language mappers match
35+
#[default]
3536
Unknown,
3637
}
3738

@@ -110,8 +111,8 @@ impl AssetLoader for ScriptAssetLoader {
110111
pub struct ScriptAssetSettings {
111112
/// Strategy for mapping asset paths to script ids, by default this is the identity function
112113
pub script_id_mapper: AssetPathToScriptIdMapper,
113-
/// Strategies for mapping asset paths to languages
114-
pub script_language_mappers: Vec<AssetPathToLanguageMapper>,
114+
/// Mapping from extension to script language
115+
pub extension_to_language_map: HashMap<&'static str, Language>,
115116

116117
/// The currently supported asset extensions
117118
/// Should be updated by each scripting plugin to include the extensions it supports.
@@ -123,15 +124,11 @@ pub struct ScriptAssetSettings {
123124
impl ScriptAssetSettings {
124125
/// Selects the language for a given asset path
125126
pub fn select_script_language(&self, path: &AssetPath) -> Language {
126-
for mapper in &self.script_language_mappers {
127-
let language = (mapper.map)(path);
128-
match language {
129-
Language::Unknown => continue,
130-
_ => return language,
131-
}
132-
}
133-
134-
Language::Unknown
127+
let extension = path.path().extension().and_then(|ext| ext.to_str()).unwrap_or_default();
128+
self.extension_to_language_map
129+
.get(extension)
130+
.cloned()
131+
.unwrap_or_default()
135132
}
136133
}
137134

@@ -141,7 +138,12 @@ impl Default for ScriptAssetSettings {
141138
script_id_mapper: AssetPathToScriptIdMapper {
142139
map: (|path: &AssetPath| path.path().to_string_lossy().into_owned().into()),
143140
},
144-
script_language_mappers: vec![],
141+
extension_to_language_map: HashMap::from_iter(vec![
142+
("lua", Language::Lua),
143+
("luau", Language::Lua),
144+
("rhai", Language::Rhai),
145+
("rn", Language::Rune),
146+
]),
145147
supported_extensions: &[],
146148
}
147149
}
@@ -154,20 +156,6 @@ pub struct AssetPathToScriptIdMapper {
154156
pub map: fn(&AssetPath) -> ScriptId,
155157
}
156158

157-
#[derive(Clone, Copy)]
158-
/// Strategy for mapping asset paths to languages
159-
pub struct AssetPathToLanguageMapper {
160-
/// The mapping function
161-
pub map: fn(&AssetPath) -> Language,
162-
}
163-
164-
impl Default for AssetPathToLanguageMapper {
165-
fn default() -> Self {
166-
Self {
167-
map: |_| Language::Unknown,
168-
}
169-
}
170-
}
171159

172160
/// 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.
173161
#[derive(Default, Debug, Resource)]
@@ -393,26 +381,10 @@ mod tests {
393381
script_id_mapper: AssetPathToScriptIdMapper {
394382
map: |path| path.path().to_string_lossy().into_owned().into(),
395383
},
396-
script_language_mappers: vec![
397-
AssetPathToLanguageMapper {
398-
map: |path| {
399-
if path.path().extension().unwrap() == "lua" {
400-
Language::Lua
401-
} else {
402-
Language::Unknown
403-
}
404-
},
405-
},
406-
AssetPathToLanguageMapper {
407-
map: |path| {
408-
if path.path().extension().unwrap() == "rhai" {
409-
Language::Rhai
410-
} else {
411-
Language::Unknown
412-
}
413-
},
414-
},
415-
],
384+
extension_to_language_map: HashMap::from_iter(vec![
385+
("lua", Language::Lua),
386+
("rhai", Language::Rhai),
387+
]),
416388
}
417389
}
418390

crates/bevy_mod_scripting_core/src/lib.rs

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
55
use crate::event::ScriptErrorEvent;
66
use asset::{
7-
configure_asset_systems, configure_asset_systems_for_plugin, AssetPathToLanguageMapper,
7+
configure_asset_systems, configure_asset_systems_for_plugin,
88
Language, ScriptAsset, ScriptAssetLoader, ScriptAssetSettings,
99
};
1010
use bevy::prelude::*;
@@ -87,16 +87,17 @@ pub struct ScriptingPlugin<P: IntoScriptPluginParams> {
8787
/// The strategy for assigning contexts to scripts
8888
pub context_assignment_strategy: ContextAssignmentStrategy,
8989

90-
/// The asset path to language mapper for the plugin
91-
pub language_mapper: AssetPathToLanguageMapper,
90+
/// The language this plugin declares
91+
pub language: Language,
92+
/// Supported extensions to be added to the asset settings without the dot
93+
/// By default BMS populates a set of extensions for the languages it supports.
94+
pub additional_supported_extensions: &'static [&'static str],
9295

9396
/// initializers for the contexts, run when loading the script
9497
pub context_initializers: Vec<ContextInitializer<P>>,
9598
/// initializers for the contexts run every time before handling events
9699
pub context_pre_handling_initializers: Vec<ContextPreHandlingInitializer<P>>,
97100

98-
/// Supported extensions to be added to the asset settings without the dot
99-
pub supported_extensions: &'static [&'static str],
100101
}
101102

102103
impl<P: IntoScriptPluginParams> Default for ScriptingPlugin<P> {
@@ -106,10 +107,10 @@ impl<P: IntoScriptPluginParams> Default for ScriptingPlugin<P> {
106107
callback_handler: CallbackSettings::<P>::default().callback_handler,
107108
context_builder: Default::default(),
108109
context_assignment_strategy: Default::default(),
109-
language_mapper: Default::default(),
110+
language: Default::default(),
110111
context_initializers: Default::default(),
111112
context_pre_handling_initializers: Default::default(),
112-
supported_extensions: Default::default(),
113+
additional_supported_extensions: Default::default(),
113114
}
114115
}
115116
}
@@ -136,13 +137,9 @@ impl<P: IntoScriptPluginParams> Plugin for ScriptingPlugin<P> {
136137
// add extension for the language to the asset loader
137138
once_per_app_init(app);
138139

139-
app.add_supported_script_extensions(self.supported_extensions);
140-
141-
app.world_mut()
142-
.resource_mut::<ScriptAssetSettings>()
143-
.as_mut()
144-
.script_language_mappers
145-
.push(self.language_mapper);
140+
if !self.additional_supported_extensions.is_empty() {
141+
app.add_supported_script_extensions(self.additional_supported_extensions, self.language.clone());
142+
}
146143

147144
register_types(app);
148145
}
@@ -203,6 +200,11 @@ pub trait ConfigureScriptPlugin {
203200
/// This means that all scripts will share the same context. This is useful for when you want to share data between scripts easilly.
204201
/// Be careful however as this also means that scripts can interfere with each other in unexpected ways! Including overwriting each other's handlers.
205202
fn enable_context_sharing(self) -> Self;
203+
204+
/// Set the set of extensions to be added for the plugin's language.
205+
///
206+
/// This is useful for adding extensions that are not supported by default by BMS.
207+
fn set_additional_supported_extensions(self, extensions: &'static [&'static str]) -> Self;
206208
}
207209

208210
impl<P: IntoScriptPluginParams + AsMut<ScriptingPlugin<P>>> ConfigureScriptPlugin for P {
@@ -231,6 +233,13 @@ impl<P: IntoScriptPluginParams + AsMut<ScriptingPlugin<P>>> ConfigureScriptPlugi
231233
self.as_mut().context_assignment_strategy = ContextAssignmentStrategy::Global;
232234
self
233235
}
236+
237+
fn set_additional_supported_extensions(mut self, extensions: &'static [&'static str]) -> Self {
238+
self.as_mut().additional_supported_extensions = extensions;
239+
self
240+
}
241+
242+
234243
}
235244

236245
fn once_per_app_finalize(app: &mut App) {
@@ -386,11 +395,13 @@ impl ManageStaticScripts for App {
386395
/// Any changes to the asset settings after that will not be reflected in the asset loader.
387396
pub trait ConfigureScriptAssetSettings {
388397
/// Adds a supported extension to the asset settings
389-
fn add_supported_script_extensions(&mut self, extensions: &[&'static str]) -> &mut Self;
398+
///
399+
/// This is only valid to call in the plugin building phase, as the asset loader will be created in the `finalize` phase.
400+
fn add_supported_script_extensions(&mut self, extensions: &[&'static str], language: Language) -> &mut Self;
390401
}
391402

392403
impl ConfigureScriptAssetSettings for App {
393-
fn add_supported_script_extensions(&mut self, extensions: &[&'static str]) -> &mut Self {
404+
fn add_supported_script_extensions(&mut self, extensions: &[&'static str], language: Language) -> &mut Self {
394405
let mut asset_settings = self
395406
.world_mut()
396407
.get_resource_or_init::<ScriptAssetSettings>();
@@ -402,6 +413,9 @@ impl ConfigureScriptAssetSettings for App {
402413
let new_arr_static = Vec::leak(new_arr);
403414

404415
asset_settings.supported_extensions = new_arr_static;
416+
for extension in extensions {
417+
asset_settings.extension_to_language_map.insert(*extension, language.clone());
418+
}
405419

406420
self
407421
}

crates/languages/bevy_mod_scripting_lua/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ impl Default for LuaScriptingPlugin {
134134
.map_err(ScriptError::from_mlua_error)?;
135135
Ok(())
136136
}],
137-
supported_extensions: &["lua"],
137+
additional_supported_extensions: &["lua"],
138138
},
139139
}
140140
}

crates/languages/bevy_mod_scripting_rhai/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ impl Default for RhaiScriptingPlugin {
159159
context.scope.set_or_push("script_id", script.to_owned());
160160
Ok(())
161161
}],
162-
supported_extensions: &["rhai"],
162+
additional_supported_extensions: &["rhai"],
163163
},
164164
}
165165
}

docs/src/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
# Scripting Reference
1717

1818
- [Introduction](./ScriptingReference/introduction.md)
19+
- [Constructing Arbitrary Types](./ScriptingReference/constructing-arbitrary-types.md)
1920
- [Core Bindings](./ScriptingReference/core-api.md)
2021
- [World](./ScriptingReference/world.md)
2122
- [ReflectReference](./ScriptingReference/reflect-reference.md)
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# Constructing Arbitrary Types
2+
3+
When interfacing with bevy, we do this via reflection.
4+
While the generated bindings do not cover constructors for every single type that bevy or other libraries provide, reflection allows us to construct some (not all types implement `FromReflect`) types from dynamic structs.
5+
6+
BMS exposes this ability to all script writers via the `construct` global function.
7+
8+
9+
## Structs
10+
11+
The following struct:
12+
```rust,ignore
13+
pub struct MyStruct {
14+
pub my_field: String
15+
}
16+
```
17+
18+
can be constructed from lua like so:
19+
```lua
20+
local MyStruct = world.get_type_by_name("MyStruct")
21+
local concrete_my_struct = construct(MyStruct, {
22+
my_field = "hello"
23+
})
24+
```
25+
26+
## Tuple Structs
27+
The following tuple struct:
28+
```rust,ignore
29+
30+
pub struct MyTupleStruct(pub String);
31+
```
32+
33+
can be constructed like so:
34+
```lua
35+
36+
local MyTupleStruct = world.get_type_by_name("MyTupleStruct")
37+
local concrete_my_tuple_struct = construct(MyTupleStruct, {
38+
_1 = "hello"
39+
})
40+
```
41+
42+
## Enums
43+
The following enum:
44+
```rust,ignore
45+
pub enum MyEnum {
46+
VariantA {
47+
field: String
48+
},
49+
VariantB
50+
}
51+
```
52+
53+
can be constructed like so:
54+
```lua
55+
56+
local MyEnum = world.get_type_by_name("MyEnum")
57+
local variantA = construct(MyEnum, {
58+
variant = "VariantA",
59+
field = "hello"
60+
})
61+
local variantB = construct(MyEnum, {
62+
variant = "VariantB"
63+
})
64+
```
65+
66+
When working with enums you can also figure out the variant at runtime using `variant_name`:
67+
68+
```lua
69+
if my_enum:variant_name() == "VariantA" then
70+
print(my_enum.field)
71+
end
72+
```

docs/src/ScriptingReference/introduction.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@ If you are a modder, welcome! 👋, apologies for the rust-centricity of this gu
88

99
Scripts will have access to a few global variables in most callbacks:
1010
- `world`: a static reference to the world, with all sorts of functions available
11-
- `entity`: the entity the script is attached to, not available on load/unload callbacks
11+
- `entity`: the entity the script is attached to, not available on load/unload callbacks, and in dynamic system callbacks.
1212
- `script_id`: the ID of the current script

docs/src/Summary/controlling-script-bindings.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,32 @@ hello_world2("hi from global!");
5959

6060
Note the `new_unregistered` call instead of `new`, this is because `GlobalNamespace` is not a `Reflect` type, and the `new` call also automatically registers the type in the reflection registry.
6161

62+
## Macros
63+
The above is a bit tedious, so instead you can use the `script_bindings` macro, which applies to impl blocks like so:
64+
65+
```rust,ignore
66+
#[script_bindings("test_fn")]
67+
impl TestStruct {
68+
/// My docs !!
69+
///
70+
/// Arguments:
71+
/// * `_self` - the first argument
72+
/// * `arg1` - the second argument
73+
/// Returns:
74+
/// * `return` - nothing
75+
fn test_fn(_self: Ref<TestStruct>, mut arg1: usize) {}
76+
}
77+
78+
79+
pub fn main() {
80+
let mut app = App::new();
81+
register_test_fn(app.world_mut())
82+
}
83+
```
84+
85+
Note the documentation will automatically be picked up and stored for the purposes of reflection and documentation generation, including argument/return type specific docs.
86+
87+
6288
## Context Arguments
6389

6490
Each script function call always receives an additional context argument: `FunctionCallContext`.

0 commit comments

Comments
 (0)