Skip to content

Commit 7cd7015

Browse files
committed
feat: warn on duplicate globals
1 parent da5e7d3 commit 7cd7015

File tree

3 files changed

+69
-25
lines changed

3 files changed

+69
-25
lines changed

crates/bevy_mod_scripting_bindings/src/globals/core.rs

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use ::{
77
bevy_reflect::TypeRegistration,
88
};
99
use bevy_app::App;
10+
use bevy_log::warn;
1011
use bevy_mod_scripting_asset::ScriptAsset;
1112
use bevy_mod_scripting_derive::script_globals;
1213
use bevy_platform::collections::HashMap;
@@ -63,6 +64,7 @@ impl Plugin for CoreScriptGlobalsPlugin {
6364
register_core_globals(app.world_mut());
6465
}
6566
}
67+
const MSG_DUPLICATE_GLOBAL: &str = "This can cause confusing issues for script users, use `CoreScriptGlobalsPlugin.filter` to filter out uneeded duplicate types.";
6668

6769
#[profiling::function]
6870
fn register_static_core_globals(world: &mut World, filter: fn(&TypeRegistration) -> bool) {
@@ -82,20 +84,41 @@ fn register_static_core_globals(world: &mut World, filter: fn(&TypeRegistration)
8284
if let Some(global_name) = registration.type_info().type_path_table().ident() {
8385
let documentation = "A reference to the type, allowing you to call static methods.";
8486
let type_info = registration.type_info();
85-
global_registry.register_static_documented_dynamic(
86-
registration.type_id(),
87-
into_through_type_info(type_info),
88-
global_name.into(),
89-
documentation.into(),
90-
);
87+
if global_registry
88+
.register_static_documented_dynamic(
89+
registration.type_id(),
90+
into_through_type_info(type_info),
91+
global_name.into(),
92+
documentation.into(),
93+
)
94+
.is_some()
95+
{
96+
warn!(
97+
"Duplicate global registration for type: '{}'. {MSG_DUPLICATE_GLOBAL}",
98+
type_info.type_path_table().short_path()
99+
)
100+
};
91101
}
92102
}
93103

94104
// register basic globals
95-
global_registry.register_dummy::<World>("world", "The current ECS world.");
96-
global_registry
97-
.register_dummy::<Entity>("entity", "The entity this script is attached to if any.");
98-
global_registry.register_dummy_typed::<Val<Handle<ScriptAsset>>>("script_asset", "the asset handle for this script. If the asset is ever unloaded, the handle will be less useful.");
105+
if global_registry
106+
.register_dummy::<World>("world", "The current ECS world.")
107+
.is_some()
108+
{
109+
warn!("existing `world` global was replaced by the core `world` dummy type.")
110+
};
111+
112+
if global_registry
113+
.register_dummy::<Entity>("entity", "The entity this script is attached to if any.")
114+
.is_some()
115+
{
116+
warn!("existing `entity` global was replaced by the core `entity` dummy type.")
117+
}
118+
119+
if global_registry.register_dummy_typed::<Val<Handle<ScriptAsset>>>("script_asset", "the asset handle for this script. If the asset is ever unloaded, the handle will be less useful.").is_some() {
120+
warn!("existing `script_asset` global was replaced by the core `script_asset` dummy type.")
121+
};
99122
}
100123

101124
#[script_globals(bms_bindings_path = "crate", name = "core_globals")]
@@ -128,7 +151,15 @@ impl CoreGlobals {
128151
let registration = guard.clone().get_type_registration(registration)?;
129152
let registration =
130153
registration.map_both(Val::from, |u| u.map_both(Val::from, Val::from));
131-
type_cache.insert(type_path.to_owned(), registration);
154+
if type_cache
155+
.insert(type_path.to_owned(), registration)
156+
.is_some()
157+
{
158+
warn!(
159+
"duplicate entry inside `types` global for type: {}. {MSG_DUPLICATE_GLOBAL}",
160+
type_path
161+
)
162+
};
132163
}
133164

134165
Ok(type_cache)

crates/bevy_mod_scripting_bindings/src/globals/mod.rs

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ impl ScriptGlobalsRegistry {
116116
}
117117

118118
/// Inserts a global into the registry, returns the previous value if it existed
119+
#[must_use]
119120
pub fn register<
120121
T: ScriptReturn + 'static + Typed,
121122
F: Fn(WorldGuard) -> Result<T, InteropError> + 'static + Send + Sync,
@@ -140,40 +141,43 @@ impl ScriptGlobalsRegistry {
140141
/// This can be useful for globals which you cannot expose normally.
141142
///
142143
/// Dummy globals are stored as non-static instances, i.e. they're expected to be values not type references.
144+
#[must_use]
143145
pub fn register_dummy<T: 'static>(
144146
&mut self,
145147
name: impl Into<Cow<'static, str>>,
146148
documentation: impl Into<Cow<'static, str>>,
147-
) {
149+
) -> Option<ScriptGlobalDummy> {
148150
self.dummies.insert(
149151
name.into(),
150152
ScriptGlobalDummy {
151153
documentation: Some(documentation.into()),
152154
type_id: TypeId::of::<T>(),
153155
type_information: None,
154156
},
155-
);
157+
)
156158
}
157159

158160
/// Typed equivalent to [`Self::register_dummy`].
161+
#[must_use]
159162
pub fn register_dummy_typed<T: 'static + TypedThrough>(
160163
&mut self,
161164
name: impl Into<Cow<'static, str>>,
162165
documentation: impl Into<Cow<'static, str>>,
163-
) {
166+
) -> Option<ScriptGlobalDummy> {
164167
self.dummies.insert(
165168
name.into(),
166169
ScriptGlobalDummy {
167170
documentation: Some(documentation.into()),
168171
type_id: TypeId::of::<T>(),
169172
type_information: Some(T::through_type_info()),
170173
},
171-
);
174+
)
172175
}
173176

174177
/// Inserts a global into the registry, returns the previous value if it existed.
175178
///
176179
/// This is a version of [`Self::register`] which stores type information regarding the global.
180+
#[must_use]
177181
pub fn register_documented<
178182
T: TypedScriptReturn + 'static,
179183
F: Fn(WorldGuard) -> Result<T, InteropError> + 'static + Send + Sync,
@@ -195,7 +199,11 @@ impl ScriptGlobalsRegistry {
195199
}
196200

197201
/// Registers a static global into the registry.
198-
pub fn register_static<T: 'static + Typed>(&mut self, name: Cow<'static, str>) {
202+
#[must_use]
203+
pub fn register_static<T: 'static + Typed>(
204+
&mut self,
205+
name: Cow<'static, str>,
206+
) -> Option<ScriptGlobal> {
199207
self.globals.insert(
200208
name,
201209
ScriptGlobal {
@@ -204,17 +212,18 @@ impl ScriptGlobalsRegistry {
204212
type_id: TypeId::of::<T>(),
205213
type_information: into_through_type_info(T::type_info()),
206214
},
207-
);
215+
)
208216
}
209217

210218
/// Registers a static global into the registry.
211219
///
212220
/// This is a version of [`Self::register_static`] which stores rich type information regarding the global.
221+
#[must_use]
213222
pub fn register_static_documented<T: TypedScriptReturn + 'static>(
214223
&mut self,
215224
name: Cow<'static, str>,
216225
documentation: Cow<'static, str>,
217-
) {
226+
) -> Option<ScriptGlobal> {
218227
self.globals.insert(
219228
name,
220229
ScriptGlobal {
@@ -223,19 +232,20 @@ impl ScriptGlobalsRegistry {
223232
type_id: TypeId::of::<T>(),
224233
type_information: T::through_type_info(),
225234
},
226-
);
235+
)
227236
}
228237

229238
/// Registers a static global into the registry.
230239
///
231240
/// This is a version of [`Self::register_static_documented`] which does not require compile time type knowledge.
241+
#[must_use]
232242
pub fn register_static_documented_dynamic(
233243
&mut self,
234244
type_id: TypeId,
235245
type_information: ThroughTypeInfo,
236246
name: Cow<'static, str>,
237247
documentation: Cow<'static, str>,
238-
) {
248+
) -> Option<ScriptGlobal> {
239249
self.globals.insert(
240250
name,
241251
ScriptGlobal {
@@ -244,7 +254,7 @@ impl ScriptGlobalsRegistry {
244254
type_id,
245255
type_information,
246256
},
247-
);
257+
)
248258
}
249259
}
250260

@@ -307,14 +317,14 @@ mod test {
307317
fn test_static_globals() {
308318
let mut registry = ScriptGlobalsRegistry::default();
309319

310-
registry.register_static::<i32>(Cow::Borrowed("foo"));
320+
_ = registry.register_static::<i32>(Cow::Borrowed("foo"));
311321

312322
let global = registry.get("foo").unwrap();
313323
assert!(global.maker.is_none());
314324
assert_eq!(global.type_id, TypeId::of::<i32>());
315325

316326
// the same but documented
317-
registry.register_static_documented::<i32>(
327+
_ = registry.register_static_documented::<i32>(
318328
Cow::Borrowed("bar"),
319329
Cow::Borrowed("This is a test"),
320330
);

crates/bevy_mod_scripting_derive/src/derive/script_globals.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,11 @@ pub fn script_globals(
4242
let registry = world.get_resource_or_init::<#bms_bindings_path::globals::AppScriptGlobalsRegistry>();
4343
let mut registry = registry.write();
4444

45-
registry
46-
#(#function_registrations)*;
45+
#(
46+
if (registry #function_registrations).is_some() {
47+
warn!("conflicting global registration under name: {}. This might cause confusing problems, use `CoreScriptGlobalsPlugin.filter` to filter out uneeded duplicate types.", stringify!(#function_name))
48+
}
49+
)*;
4750
}
4851
};
4952

0 commit comments

Comments
 (0)