Skip to content

Commit fac74f1

Browse files
committed
fix: bugs to do with multiple languages being initialized
1 parent e6f817c commit fac74f1

File tree

13 files changed

+343
-346
lines changed

13 files changed

+343
-346
lines changed

Cargo.toml

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -106,11 +106,11 @@ inherits = "release"
106106
debug = true
107107

108108
[[example]]
109-
name = "game_of_life_lua"
110-
path = "examples/lua/game_of_life.rs"
111-
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"
109+
name = "game_of_life"
110+
path = "examples/game_of_life.rs"
111+
required-features = [
112+
"lua54",
113+
"rhai",
114+
"bevy/file_watcher",
115+
"bevy/multi_threaded",
116+
]

crates/bevy_mod_scripting_core/src/asset.rs

Lines changed: 65 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,42 @@ 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+
/// Initial setting before being processed by the script synchronization systems
21+
Unset,
22+
/// Set if none of the asset path to language mappers match
23+
Unknown,
24+
}
25+
26+
impl std::fmt::Display for Language {
27+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28+
match self {
29+
Language::Rhai => "Rhai".fmt(f),
30+
Language::Lua => "Lua".fmt(f),
31+
Language::Rune => "Rune".fmt(f),
32+
Language::External(cow) => cow.fmt(f),
33+
Language::Unset => "Unset".fmt(f),
34+
Language::Unknown => "Unknown".fmt(f),
35+
}
36+
}
37+
}
38+
1339
/// Represents a script loaded into memory as an asset
1440
#[derive(Asset, TypePath, Clone)]
1541
pub struct ScriptAsset {
1642
pub content: Box<[u8]>,
1743
/// The virtual filesystem path of the asset, used to map to the script Id for asset backed scripts
1844
pub asset_path: PathBuf,
19-
pub language: Cow<'static, str>,
45+
pub language: Language,
2046
}
2147

2248
pub struct ScriptAssetLoader {
23-
/// Used to set the language of the script
24-
pub language: Cow<'static, str>,
2549
/// The file extensions this loader should handle
2650
pub extensions: &'static [&'static str],
2751
/// preprocessor to run on the script before saving the content to an asset
@@ -52,7 +76,7 @@ impl AssetLoader for ScriptAssetLoader {
5276
let asset = ScriptAsset {
5377
content: content.into_boxed_slice(),
5478
asset_path: load_context.path().to_owned(),
55-
language: self.language.clone(),
79+
language: Language::Unset,
5680
};
5781
Ok(asset)
5882
}
@@ -62,9 +86,24 @@ impl AssetLoader for ScriptAssetLoader {
6286
}
6387
}
6488

65-
#[derive(Clone, Copy, Resource)]
89+
#[derive(Clone, Resource)]
6690
pub struct ScriptAssetSettings {
6791
pub script_id_mapper: AssetPathToScriptIdMapper,
92+
pub script_language_mappers: Vec<AssetPathToLanguageMapper>,
93+
}
94+
95+
impl ScriptAssetSettings {
96+
pub fn select_script_language(&self, path: &Path) -> Language {
97+
for mapper in &self.script_language_mappers {
98+
let language = (mapper.map)(path);
99+
match language {
100+
Language::Unset | Language::Unknown => continue,
101+
_ => return language,
102+
}
103+
}
104+
105+
Language::Unknown
106+
}
68107
}
69108

70109
impl Default for ScriptAssetSettings {
@@ -73,6 +112,9 @@ impl Default for ScriptAssetSettings {
73112
script_id_mapper: AssetPathToScriptIdMapper {
74113
map: (|path: &Path| path.to_string_lossy().into_owned().into()),
75114
},
115+
script_language_mappers: vec![AssetPathToLanguageMapper {
116+
map: (|_: &Path| Language::Unset),
117+
}],
76118
}
77119
}
78120
}
@@ -83,22 +125,33 @@ pub struct AssetPathToScriptIdMapper {
83125
pub map: fn(&Path) -> ScriptId,
84126
}
85127

128+
#[derive(Clone, Copy)]
129+
pub struct AssetPathToLanguageMapper {
130+
pub map: fn(&Path) -> Language,
131+
}
132+
86133
/// 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.
87134
#[derive(Default, Debug, Resource)]
88-
pub struct AssetIdToScriptIdMap {
89-
pub map: HashMap<AssetId<ScriptAsset>, ScriptId>,
135+
pub struct ScriptMetadataStore {
136+
pub map: HashMap<AssetId<ScriptAsset>, ScriptMetadata>,
137+
}
138+
139+
#[derive(Debug, Clone)]
140+
pub struct ScriptMetadata {
141+
pub script_id: ScriptId,
142+
pub language: Language,
90143
}
91144

92-
impl AssetIdToScriptIdMap {
93-
pub fn insert(&mut self, id: AssetId<ScriptAsset>, path: ScriptId) {
94-
self.map.insert(id, path);
145+
impl ScriptMetadataStore {
146+
pub fn insert(&mut self, id: AssetId<ScriptAsset>, meta: ScriptMetadata) {
147+
self.map.insert(id, meta);
95148
}
96149

97-
pub fn get(&self, id: AssetId<ScriptAsset>) -> Option<&ScriptId> {
150+
pub fn get(&self, id: AssetId<ScriptAsset>) -> Option<&ScriptMetadata> {
98151
self.map.get(&id)
99152
}
100153

101-
pub fn remove(&mut self, id: AssetId<ScriptAsset>) -> Option<ScriptId> {
154+
pub fn remove(&mut self, id: AssetId<ScriptAsset>) -> Option<ScriptMetadata> {
102155
self.map.remove(&id)
103156
}
104157
}

crates/bevy_mod_scripting_core/src/commands.rs

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::{
99
IntoScriptPluginParams,
1010
};
1111
use bevy::{asset::Handle, ecs::world::Mut, log::debug, prelude::Command};
12-
use std::{any::type_name, marker::PhantomData};
12+
use std::marker::PhantomData;
1313

1414
pub struct DeleteScript<P: IntoScriptPluginParams> {
1515
pub id: ScriptId,
@@ -62,9 +62,17 @@ impl<P: IntoScriptPluginParams> Command for DeleteScript<P> {
6262
&mut runtime_container.runtime,
6363
world,
6464
) {
65-
Ok(_) => {},
65+
Ok(_) => {}
6666
Err(e) => {
67-
handle_script_errors(world, [e.with_context(format!("Running unload hook for script with id: {}. Runtime type: {}, Context type: {}", self.id, type_name::<P::R>(), type_name::<P::C>()))].into_iter());
67+
handle_script_errors(
68+
world,
69+
[e.with_context(format!(
70+
"Running unload hook for script with id: {}. Language: {}",
71+
self.id,
72+
P::LANGUAGE
73+
))]
74+
.into_iter(),
75+
);
6876
}
6977
}
7078

@@ -151,28 +159,32 @@ impl<P: IntoScriptPluginParams> Command for CreateOrUpdateScript<P> {
151159

152160
let current_context_id = if let Some(id) = current_context_id {
153161
// reload existing context
154-
let current_context = contexts.get_mut(id).unwrap();
155-
match (builder.reload)(&self.id, &self.content, current_context, &settings.context_initializers, &settings.context_pre_handling_initializers, world, &mut runtime.runtime) {
156-
Ok(_) => {},
157-
Err(e) => {
158-
handle_script_errors(world, [e.with_context(format!("Reloading script with id: {}. Runtime type: {}, Context type: {}", self.id, type_name::<P::R>(), type_name::<P::C>()))].into_iter());
159-
return;
160-
}
161-
};
162162
id
163163
} else {
164164
let ctxt = (builder.load)(&self.id, &self.content, &settings.context_initializers, &settings.context_pre_handling_initializers, world, &mut runtime.runtime);
165165
match ctxt {
166166
Ok(ctxt) => contexts.insert(ctxt),
167167
Err(e) => {
168-
handle_script_errors(world, [e.with_context(format!("Loading script with id: {}. Runtime type: {}, Context type: {}", self.id, type_name::<P::R>(), type_name::<P::C>()))].into_iter());
168+
handle_script_errors(world, [e.with_context(format!("Loading script with id: {}. Language: {}", self.id, P::LANGUAGE))].into_iter());
169169
return;
170170
}
171171
}
172172
};
173173

174174

175175
if let Some(previous) = previous_context_id {
176+
if let Some(previous_context_id) = contexts.get_mut(previous) {
177+
match (builder.reload)(&self.id, &self.content, previous_context_id, &settings.context_initializers, &settings.context_pre_handling_initializers, world, &mut runtime.runtime) {
178+
Ok(_) => {},
179+
Err(e) => {
180+
handle_script_errors(world, [e.with_context(format!("Reloading script with id: {}. Language: {}", self.id, P::LANGUAGE))].into_iter());
181+
return;
182+
}
183+
};
184+
} else {
185+
bevy::log::error!("Could not find previous context with id: {}. Could not reload script id: {}", previous, self.id);
186+
}
187+
176188
if previous != current_context_id {
177189
debug!(
178190
"Script is being moved to a new context with id: {}, removing up old context.",
@@ -183,12 +195,15 @@ impl<P: IntoScriptPluginParams> Command for CreateOrUpdateScript<P> {
183195
}
184196
}
185197

186-
let context = contexts.get_mut(current_context_id).expect("Context not found");
187-
match (runner)(vec![], bevy::ecs::entity::Entity::from_raw(0), &self.id, &OnScriptLoaded::into_callback_label(), context, &settings.context_pre_handling_initializers, &mut runtime.runtime, world) {
188-
Ok(_) => {},
189-
Err(e) => {
190-
handle_script_errors(world, [e.with_context(format!("Running initialization hook for script with id: {}. Runtime type: {}, Context type: {}", self.id, type_name::<P::R>(), type_name::<P::C>()))].into_iter());
191-
},
198+
if let Some(context) = contexts.get_mut(current_context_id) {
199+
match (runner)(vec![], bevy::ecs::entity::Entity::from_raw(0), &self.id, &OnScriptLoaded::into_callback_label(), context, &settings.context_pre_handling_initializers, &mut runtime.runtime, world) {
200+
Ok(_) => {},
201+
Err(e) => {
202+
handle_script_errors(world, [e.with_context(format!("Running initialization hook for script with id: {}. Language: {}", self.id, P::LANGUAGE))].into_iter());
203+
},
204+
}
205+
} else {
206+
bevy::log::error!("Could not find context with id: {}. Could not run initialization hook for script id: {}. This may be because the script failed to load.", current_context_id, self.id);
192207
}
193208

194209
// now we can insert the actual script

0 commit comments

Comments
 (0)