3
3
use crate :: {
4
4
bindings:: { ThreadWorldContainer , WorldContainer , WorldGuard } ,
5
5
error:: { InteropError , ScriptError } ,
6
- script:: { Script , ScriptId } ,
6
+ script:: ScriptId ,
7
7
IntoScriptPluginParams ,
8
8
} ;
9
9
use bevy:: ecs:: { entity:: Entity , system:: Resource } ;
10
- use std:: { collections:: HashMap , sync:: atomic:: AtomicU32 } ;
11
10
12
11
/// A trait that all script contexts must implement.
13
- pub trait Context : ' static + Send + Sync { }
14
- impl < T : ' static + Send + Sync > Context for T { }
15
-
16
- /// The type of a context id
17
- pub type ContextId = u32 ;
18
-
19
- /// Stores script state for a scripting plugin. Scripts are identified by their `ScriptId`, while contexts are identified by their `ContextId`.
20
- #[ derive( Resource ) ]
21
- pub struct ScriptContexts < P : IntoScriptPluginParams > {
22
- /// The contexts of the scripts
23
- pub contexts : HashMap < ContextId , P :: C > ,
24
- }
25
-
26
- impl < P : IntoScriptPluginParams > Default for ScriptContexts < P > {
27
- fn default ( ) -> Self {
28
- Self {
29
- contexts : Default :: default ( ) ,
30
- }
31
- }
32
- }
33
-
34
- static CONTEXT_ID_COUNTER : AtomicU32 = AtomicU32 :: new ( 0 ) ;
35
- impl < P : IntoScriptPluginParams > ScriptContexts < P > {
36
- /// Allocates a new ContextId and inserts the context into the map
37
- pub fn insert ( & mut self , ctxt : P :: C ) -> ContextId {
38
- let id = CONTEXT_ID_COUNTER . fetch_add ( 1 , std:: sync:: atomic:: Ordering :: Relaxed ) ;
39
- self . contexts . insert ( id, ctxt) ;
40
- id
41
- }
42
-
43
- /// Inserts a context with a specific id
44
- pub fn insert_with_id ( & mut self , id : ContextId , ctxt : P :: C ) -> Option < P :: C > {
45
- self . contexts . insert ( id, ctxt)
46
- }
47
-
48
- /// Allocate new context id without inserting a context
49
- pub fn allocate_id ( & self ) -> ContextId {
50
- CONTEXT_ID_COUNTER . fetch_add ( 1 , std:: sync:: atomic:: Ordering :: Relaxed )
51
- }
52
-
53
- /// Removes a context from the map
54
- pub fn remove ( & mut self , id : ContextId ) -> Option < P :: C > {
55
- self . contexts . remove ( & id)
56
- }
57
-
58
- /// Get a reference to a context
59
- pub fn get ( & self , id : ContextId ) -> Option < & P :: C > {
60
- self . contexts . get ( & id)
61
- }
62
-
63
- /// Get a mutable reference to a context
64
- pub fn get_mut ( & mut self , id : ContextId ) -> Option < & mut P :: C > {
65
- self . contexts . get_mut ( & id)
66
- }
67
-
68
- /// Check if a context exists
69
- pub fn contains ( & self , id : ContextId ) -> bool {
70
- self . contexts . contains_key ( & id)
71
- }
72
- }
12
+ ///
13
+ /// Contexts are not required to be `Sync` as they are internally stored behind a `Mutex` but they must satisfy `Send` so they can be
14
+ /// freely sent between threads.
15
+ pub trait Context : ' static + Send { }
16
+ impl < T : ' static + Send > Context for T { }
73
17
74
18
/// Initializer run once after creating a context but before executing it for the first time as well as after re-loading the script
75
19
pub type ContextInitializer < P > =
@@ -85,7 +29,7 @@ pub struct ContextLoadingSettings<P: IntoScriptPluginParams> {
85
29
/// Defines the strategy used to load and reload contexts
86
30
pub loader : ContextBuilder < P > ,
87
31
/// Defines the strategy used to assign contexts to scripts
88
- pub assigner : ContextAssigner < P > ,
32
+ pub assignment_strategy : ContextAssignmentStrategy ,
89
33
/// Initializers run once after creating a context but before executing it for the first time
90
34
pub context_initializers : Vec < ContextInitializer < P > > ,
91
35
/// Initializers run every time before executing or loading a script
@@ -96,7 +40,7 @@ impl<P: IntoScriptPluginParams> Default for ContextLoadingSettings<P> {
96
40
fn default ( ) -> Self {
97
41
Self {
98
42
loader : ContextBuilder :: default ( ) ,
99
- assigner : ContextAssigner :: default ( ) ,
43
+ assignment_strategy : Default :: default ( ) ,
100
44
context_initializers : Default :: default ( ) ,
101
45
context_pre_handling_initializers : Default :: default ( ) ,
102
46
}
@@ -107,7 +51,7 @@ impl<T: IntoScriptPluginParams> Clone for ContextLoadingSettings<T> {
107
51
fn clone ( & self ) -> Self {
108
52
Self {
109
53
loader : self . loader . clone ( ) ,
110
- assigner : self . assigner . clone ( ) ,
54
+ assignment_strategy : self . assignment_strategy ,
111
55
context_initializers : self . context_initializers . clone ( ) ,
112
56
context_pre_handling_initializers : self . context_pre_handling_initializers . clone ( ) ,
113
57
}
@@ -119,7 +63,7 @@ pub type ContextLoadFn<P> = fn(
119
63
content : & [ u8 ] ,
120
64
context_initializers : & [ ContextInitializer < P > ] ,
121
65
pre_handling_initializers : & [ ContextPreHandlingInitializer < P > ] ,
122
- runtime : & mut <P as IntoScriptPluginParams >:: R ,
66
+ runtime : & <P as IntoScriptPluginParams >:: R ,
123
67
) -> Result < <P as IntoScriptPluginParams >:: C , ScriptError > ;
124
68
125
69
/// A strategy for reloading contexts
@@ -129,7 +73,7 @@ pub type ContextReloadFn<P> = fn(
129
73
previous_context : & mut <P as IntoScriptPluginParams >:: C ,
130
74
context_initializers : & [ ContextInitializer < P > ] ,
131
75
pre_handling_initializers : & [ ContextPreHandlingInitializer < P > ] ,
132
- runtime : & mut <P as IntoScriptPluginParams >:: R ,
76
+ runtime : & <P as IntoScriptPluginParams >:: R ,
133
77
) -> Result < ( ) , ScriptError > ;
134
78
135
79
/// A strategy for loading and reloading contexts
@@ -160,7 +104,7 @@ impl<P: IntoScriptPluginParams> ContextBuilder<P> {
160
104
context_initializers : & [ ContextInitializer < P > ] ,
161
105
pre_handling_initializers : & [ ContextPreHandlingInitializer < P > ] ,
162
106
world : WorldGuard ,
163
- runtime : & mut P :: R ,
107
+ runtime : & P :: R ,
164
108
) -> Result < P :: C , ScriptError > {
165
109
WorldGuard :: with_existing_static_guard ( world. clone ( ) , |world| {
166
110
ThreadWorldContainer . set_world ( world) ?;
@@ -183,7 +127,7 @@ impl<P: IntoScriptPluginParams> ContextBuilder<P> {
183
127
context_initializers : & [ ContextInitializer < P > ] ,
184
128
pre_handling_initializers : & [ ContextPreHandlingInitializer < P > ] ,
185
129
world : WorldGuard ,
186
- runtime : & mut P :: R ,
130
+ runtime : & P :: R ,
187
131
) -> Result < ( ) , ScriptError > {
188
132
WorldGuard :: with_existing_static_guard ( world, |world| {
189
133
ThreadWorldContainer . set_world ( world) ?;
@@ -208,108 +152,12 @@ impl<P: IntoScriptPluginParams> Clone for ContextBuilder<P> {
208
152
}
209
153
}
210
154
211
- /// A strategy for assigning contexts to new and existing but re-loaded scripts as well as for managing old contexts
212
- pub struct ContextAssigner < P : IntoScriptPluginParams > {
213
- /// Assign a context to the script.
214
- /// The assigner can either return `Some(id)` or `None`.
215
- /// Returning None will request the processor to assign a new context id to assign to this script.
216
- ///
217
- /// Regardless, whether a script gets a new context id or not, the processor will check if the given context exists.
218
- /// If it does not exist, it will create a new context and assign it to the script.
219
- /// If it does exist, it will NOT create a new context, but assign the existing one to the script, and re-load the context.
220
- ///
221
- /// This function is only called once for each script, when it is loaded for the first time.
222
- pub assign : fn (
223
- script_id : & ScriptId ,
224
- new_content : & [ u8 ] ,
225
- contexts : & ScriptContexts < P > ,
226
- ) -> Option < ContextId > ,
227
-
228
- /// Handle the removal of the script, if any clean up in contexts is necessary perform it here.
229
- ///
230
- /// If you do not clean up the context here, it will stay in the context map!
231
- pub remove : fn ( context_id : ContextId , script : & Script , contexts : & mut ScriptContexts < P > ) ,
232
- }
233
-
234
- impl < P : IntoScriptPluginParams > ContextAssigner < P > {
235
- /// Create an assigner which re-uses a single global context for all scripts, only use if you know what you're doing.
236
- /// Will not perform any clean up on removal.
237
- pub fn new_global_context_assigner ( ) -> Self {
238
- Self {
239
- assign : |_, _, _| Some ( 0 ) , // always use the same id in rotation
240
- remove : |_, _, _| { } , // do nothing
241
- }
242
- }
243
-
244
- /// Create an assigner which assigns a new context to each script. This is the default strategy.
245
- pub fn new_individual_context_assigner ( ) -> Self {
246
- Self {
247
- assign : |_, _, _| None ,
248
- remove : |id, _, c| _ = c. remove ( id) ,
249
- }
250
- }
251
- }
252
-
253
- impl < P : IntoScriptPluginParams > Default for ContextAssigner < P > {
254
- fn default ( ) -> Self {
255
- Self :: new_individual_context_assigner ( )
256
- }
257
- }
258
-
259
- impl < P : IntoScriptPluginParams > Clone for ContextAssigner < P > {
260
- fn clone ( & self ) -> Self {
261
- Self {
262
- assign : self . assign ,
263
- remove : self . remove ,
264
- }
265
- }
266
- }
267
-
268
- #[ cfg( test) ]
269
- mod tests {
270
- use crate :: asset:: Language ;
271
-
272
- use super :: * ;
273
-
274
- struct DummyParams ;
275
- impl IntoScriptPluginParams for DummyParams {
276
- type C = String ;
277
- type R = ( ) ;
278
-
279
- const LANGUAGE : Language = Language :: Lua ;
280
-
281
- fn build_runtime ( ) -> Self :: R { }
282
- }
283
-
284
- #[ test]
285
- fn test_script_contexts_insert_get ( ) {
286
- let mut contexts: ScriptContexts < DummyParams > = ScriptContexts :: default ( ) ;
287
- let id = contexts. insert ( "context1" . to_string ( ) ) ;
288
- assert_eq ! ( contexts. contexts. get( & id) , Some ( & "context1" . to_string( ) ) ) ;
289
- assert_eq ! (
290
- contexts. contexts. get_mut( & id) ,
291
- Some ( & mut "context1" . to_string( ) )
292
- ) ;
293
- }
294
-
295
- #[ test]
296
- fn test_script_contexts_allocate_id ( ) {
297
- let contexts: ScriptContexts < DummyParams > = ScriptContexts :: default ( ) ;
298
- let id = contexts. allocate_id ( ) ;
299
- let next_id = contexts. allocate_id ( ) ;
300
- assert_eq ! ( next_id, id + 1 ) ;
301
- }
302
-
303
- #[ test]
304
- fn test_script_contexts_remove ( ) {
305
- let mut contexts: ScriptContexts < DummyParams > = ScriptContexts :: default ( ) ;
306
- let id = contexts. insert ( "context1" . to_string ( ) ) ;
307
- let removed = contexts. remove ( id) ;
308
- assert_eq ! ( removed, Some ( "context1" . to_string( ) ) ) ;
309
- assert ! ( !contexts. contexts. contains_key( & id) ) ;
310
-
311
- // assert next id is still incremented
312
- let next_id = contexts. allocate_id ( ) ;
313
- assert_eq ! ( next_id, id + 1 ) ;
314
- }
155
+ /// The strategy used in assigning contexts to scripts
156
+ #[ derive( Default , Clone , Copy ) ]
157
+ pub enum ContextAssignmentStrategy {
158
+ /// Assign a new context to each script
159
+ #[ default]
160
+ Individual ,
161
+ /// Share contexts with all other scripts
162
+ Global ,
315
163
}
0 commit comments