1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
use crate::{
event::ScriptErrorEvent,
hosts::{APIProvider, APIProviders, ScriptHost},
};
use bevy::{ecs::schedule::ScheduleLabel, prelude::*};
use event::ScriptLoaded;
use systems::script_event_handler;
pub mod asset;
pub mod docs;
pub mod error;
pub mod event;
pub mod hosts;
pub mod systems;
pub mod world;
pub mod prelude {
// general
pub use {
crate::asset::CodeAsset,
crate::docs::DocFragment,
crate::error::ScriptError,
crate::event::{ScriptErrorEvent, ScriptEvent},
crate::hosts::{
APIProvider, APIProviders, Recipients, Script, ScriptCollection, ScriptContexts,
ScriptData, ScriptHost,
},
crate::systems::script_event_handler,
crate::{
AddScriptApiProvider, AddScriptHost, AddScriptHostHandler, GenDocumentation,
ScriptingPlugin,
},
bevy_event_priority::{
AddPriorityEvent, PriorityEvent, PriorityEventReader, PriorityEventWriter,
PriorityEvents, PriorityIterator,
},
};
}
pub use bevy_event_priority as events;
#[derive(Default)]
/// Bevy plugin enabling run-time scripting
pub struct ScriptingPlugin;
impl Plugin for ScriptingPlugin {
fn build(&self, app: &mut bevy::prelude::App) {
app.add_event::<ScriptErrorEvent>();
}
}
pub trait GenDocumentation {
fn update_documentation<T: ScriptHost>(&mut self) -> &mut Self;
}
impl GenDocumentation for App {
/// Updates/Generates documentation and any other artifacts required for script API's. Disabled in optimized builds unless `doc_always` feature is enabled.
fn update_documentation<T: ScriptHost>(&mut self) -> &mut Self {
#[cfg(any(debug_assertions, feature = "doc_always"))]
{
info!("Generating documentation");
let w = &mut self.world;
let providers: &APIProviders<T> = w.resource();
if let Err(e) = providers.gen_all() {
error!("{}", e);
}
info!("Documentation generated");
}
self
}
}
/// Trait for app builder notation
pub trait AddScriptHost {
/// registers the given script host with your app,
/// the given system set will contain systems handling script loading, re-loading, removal etc.
/// This system set will also send events related to the script lifecycle.
///
/// Note: any systems which need to run the same frame a script is loaded must run after this set.
fn add_script_host<T: ScriptHost>(&mut self, schedule: impl ScheduleLabel) -> &mut Self;
/// Similar to `add_script_host` but allows you to specify a system set to add the script host to.
fn add_script_host_to_set<T: ScriptHost>(
&mut self,
schedule: impl ScheduleLabel,
set: impl SystemSet,
) -> &mut Self;
}
impl AddScriptHost for App {
fn add_script_host_to_set<T>(
&mut self,
schedule: impl ScheduleLabel,
set: impl SystemSet,
) -> &mut Self
where
T: ScriptHost,
{
T::register_with_app_in_set(self, schedule, set);
self.init_resource::<T>();
self.add_event::<ScriptLoaded>();
self
}
fn add_script_host<T>(&mut self, schedule: impl ScheduleLabel) -> &mut Self
where
T: ScriptHost,
{
T::register_with_app(self, schedule);
self.init_resource::<T>();
self.add_event::<ScriptLoaded>();
self
}
}
pub trait AddScriptApiProvider {
fn add_api_provider<T: ScriptHost>(
&mut self,
provider: Box<
dyn APIProvider<
APITarget = T::APITarget,
DocTarget = T::DocTarget,
ScriptContext = T::ScriptContext,
>,
>,
) -> &mut Self;
}
impl AddScriptApiProvider for App {
fn add_api_provider<T: ScriptHost>(
&mut self,
provider: Box<
dyn APIProvider<
APITarget = T::APITarget,
DocTarget = T::DocTarget,
ScriptContext = T::ScriptContext,
>,
>,
) -> &mut Self {
provider.register_with_app(self);
let w = &mut self.world;
let providers: &mut APIProviders<T> = &mut w.resource_mut();
providers.providers.push(provider);
self
}
}
pub trait AddScriptHostHandler {
/// Enables this script host to handle events with priorities in the range [0,min_prio] (inclusive),
/// during from within the given set.
///
/// Note: this is identical to adding the script_event_handler system manually, so if you require more complex setup, you can use the following:
/// ```rust,ignore
/// self.add_systems(
/// MySchedule,
/// script_event_handler::<T, MAX, MIN>
/// );
/// ```
///
/// Think of event handler systems as event sinks, which collect and "unpack" the instructions in each event every frame.
/// Because events are also prioritised, you can enforce a particular order of execution for your events (within each frame)
/// regardless of where they were fired from.
///
/// A good example of this is Unity [game loop's](https://docs.unity3d.com/Manual/ExecutionOrder.html) `onUpdate` and `onFixedUpdate`.
/// FixedUpdate runs *before* any physics while Update runs after physics and input events.
///
/// In this crate you can achieve this by using a separate system set before and after your physics,
/// then assigning event priorities such that your events are forced to run at the points you want them to, for example:
///
/// PrePhysics priority range [0,1]
/// PostPhysics priority range [2,4]
///
/// | Priority | Handler | Event |
/// | -------- | ----------- | ------------ |
/// | 0 | PrePhysics | Start 0 |
/// | 1 | PrePhysics | FixedUpdate 1 |
/// | 2 | PostPhysics | OnCollision 2 |
/// | 3 | PostPhysics | OnMouse 3 |
/// | 4 | PostPhysics | Update 4 |
///
/// Note: in this example, if your FixedUpdate event is fired *after* the handler system set has run, it will be discarded (since other handlers discard events of higher priority).
fn add_script_handler<T: ScriptHost, const MAX: u32, const MIN: u32>(
&mut self,
schedule: impl ScheduleLabel,
) -> &mut Self;
/// The same as `add_script_handler` but allows you to specify a system set to add the handler to.
fn add_script_handler_to_set<T: ScriptHost, const MAX: u32, const MIN: u32>(
&mut self,
schedule: impl ScheduleLabel,
set: impl SystemSet,
) -> &mut Self;
}
impl AddScriptHostHandler for App {
fn add_script_handler_to_set<T: ScriptHost, const MAX: u32, const MIN: u32>(
&mut self,
schedule: impl ScheduleLabel,
set: impl SystemSet,
) -> &mut Self {
self.add_systems(schedule, script_event_handler::<T, MAX, MIN>.in_set(set));
self
}
fn add_script_handler<T: ScriptHost, const MAX: u32, const MIN: u32>(
&mut self,
schedule: impl ScheduleLabel,
) -> &mut Self {
self.add_systems(schedule, script_event_handler::<T, MAX, MIN>);
self
}
}