#![warn(missing_docs)]
use crate::plugin::error::GameResult;
use crate::{
asset::manager::ResourceManager,
core::{
log::Log,
pool::{Handle, PoolError},
reflect::{FieldMut, FieldRef, Reflect, ReflectArray, ReflectList},
type_traits::ComponentProvider,
uuid::Uuid,
visitor::{Visit, VisitResult, Visitor},
TypeUuidProvider,
},
engine::{input::InputState, task::TaskPoolHandler, GraphicsContext, ScriptMessageDispatcher},
event::Event,
gui::UiContainer,
plugin::{Plugin, PluginContainer},
scene::{base::NodeScriptMessage, node::Node, Scene},
};
use fyrox_core::pool::ObjectOrVariant;
pub use fyrox_core_derive::ScriptMessagePayload;
use fyrox_graph::SceneGraph;
use std::{
any::{Any, TypeId},
fmt::{Debug, Formatter},
ops::{Deref, DerefMut},
str::FromStr,
sync::mpsc::Sender,
};
pub mod constructor;
pub(crate) trait UniversalScriptContext {
fn node(&mut self) -> Result<&mut Node, PoolError>;
fn destroy_script_deferred(&self, script: Script, index: usize);
fn set_script_index(&mut self, index: usize);
}
pub type DynamicTypeId = i64;
pub trait ScriptMessagePayload: Any + Send + Debug {
fn get_dynamic_type_id(&self) -> Option<DynamicTypeId> {
None
}
}
impl dyn ScriptMessagePayload {
pub fn downcast_ref<T: 'static>(&self) -> Option<&T> {
(self as &dyn Any).downcast_ref::<T>()
}
pub fn downcast_mut<T: 'static>(&mut self) -> Option<&mut T> {
(self as &mut dyn Any).downcast_mut::<T>()
}
}
#[derive(Debug)]
pub enum RoutingStrategy {
Up,
Down,
}
#[derive(Debug)]
pub struct ScriptMessage {
pub payload: Box<dyn ScriptMessagePayload>,
pub kind: ScriptMessageKind,
}
#[derive(Debug)]
pub enum ScriptMessageKind {
Targeted(Handle<Node>),
Hierarchical {
root: Handle<Node>,
routing: RoutingStrategy,
},
Global,
}
#[derive(Clone)]
pub struct ScriptMessageSender {
pub(crate) sender: Sender<ScriptMessage>,
}
impl Debug for ScriptMessageSender {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "ScriptMessageSender")
}
}
impl ScriptMessageSender {
pub fn send(&self, message: ScriptMessage) {
if self.sender.send(message).is_err() {
Log::err("Failed to send script message, it means the scene is already deleted!");
}
}
pub fn send_to_target<T>(&self, target: Handle<impl ObjectOrVariant<Node>>, payload: T)
where
T: ScriptMessagePayload,
{
self.send(ScriptMessage {
payload: Box::new(payload),
kind: ScriptMessageKind::Targeted(target.transmute()),
})
}
pub fn send_global<T>(&self, payload: T)
where
T: ScriptMessagePayload,
{
self.send(ScriptMessage {
payload: Box::new(payload),
kind: ScriptMessageKind::Global,
})
}
pub fn send_hierarchical<T>(
&self,
root: Handle<impl ObjectOrVariant<Node>>,
routing: RoutingStrategy,
payload: T,
) where
T: ScriptMessagePayload,
{
self.send(ScriptMessage {
payload: Box::new(payload),
kind: ScriptMessageKind::Hierarchical {
root: root.transmute(),
routing,
},
})
}
}
pub trait BaseScript: Visit + Reflect + Send + Debug + 'static {
fn clone_box(&self) -> Box<dyn ScriptTrait>;
fn as_any_ref(&self) -> &dyn Any;
fn as_any_ref_mut(&mut self) -> &mut dyn Any;
fn id(&self) -> Uuid;
}
impl<T> BaseScript for T
where
T: Clone + ScriptTrait + Any + TypeUuidProvider,
{
fn clone_box(&self) -> Box<dyn ScriptTrait> {
Box::new(self.clone())
}
fn as_any_ref(&self) -> &dyn Any {
self
}
fn as_any_ref_mut(&mut self) -> &mut dyn Any {
self
}
fn id(&self) -> Uuid {
T::type_uuid()
}
}
pub struct PluginsRefMut<'a>(pub &'a mut [PluginContainer]);
impl Deref for PluginsRefMut<'_> {
type Target = [PluginContainer];
fn deref(&self) -> &Self::Target {
self.0
}
}
impl DerefMut for PluginsRefMut<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.0
}
}
impl PluginsRefMut<'_> {
#[inline]
pub fn of_type_ref<T>(&self) -> Option<&T>
where
T: Plugin,
{
self.0.iter().find_map(|p| p.cast::<T>())
}
#[inline]
pub fn of_type_mut<T>(&mut self) -> Option<&mut T>
where
T: Plugin,
{
self.0.iter_mut().find_map(|p| p.cast_mut::<T>())
}
#[inline]
pub fn get<T>(&self) -> &T
where
T: Plugin,
{
self.of_type_ref().unwrap()
}
#[inline]
pub fn get_mut<T>(&mut self) -> &mut T
where
T: Plugin,
{
self.of_type_mut().unwrap()
}
}
pub struct ScriptContext<'a, 'b, 'c> {
pub dt: f32,
pub elapsed_time: f32,
pub plugins: PluginsRefMut<'a>,
pub handle: Handle<Node>,
pub scene: &'b mut Scene,
pub scene_handle: Handle<Scene>,
pub resource_manager: &'a ResourceManager,
pub message_sender: &'c ScriptMessageSender,
pub message_dispatcher: &'c mut ScriptMessageDispatcher,
pub task_pool: &'a mut TaskPoolHandler,
pub graphics_context: &'a mut GraphicsContext,
pub user_interfaces: &'a mut UiContainer,
pub script_index: usize,
pub input_state: &'a InputState,
}
impl UniversalScriptContext for ScriptContext<'_, '_, '_> {
fn node(&mut self) -> Result<&mut Node, PoolError> {
self.scene.graph.try_get_node_mut(self.handle)
}
fn destroy_script_deferred(&self, script: Script, index: usize) {
Log::verify(
self.scene
.graph
.script_message_sender
.send(NodeScriptMessage::DestroyScript {
script,
handle: self.handle,
script_index: index,
}),
)
}
fn set_script_index(&mut self, index: usize) {
self.script_index = index;
}
}
pub struct ScriptMessageContext<'a, 'b, 'c> {
pub dt: f32,
pub elapsed_time: f32,
pub plugins: PluginsRefMut<'a>,
pub handle: Handle<Node>,
pub scene: &'b mut Scene,
pub scene_handle: Handle<Scene>,
pub resource_manager: &'a ResourceManager,
pub message_sender: &'c ScriptMessageSender,
pub task_pool: &'a mut TaskPoolHandler,
pub graphics_context: &'a mut GraphicsContext,
pub user_interfaces: &'a mut UiContainer,
pub script_index: usize,
pub input_state: &'a InputState,
}
impl UniversalScriptContext for ScriptMessageContext<'_, '_, '_> {
fn node(&mut self) -> Result<&mut Node, PoolError> {
self.scene.graph.try_get_node_mut(self.handle)
}
fn destroy_script_deferred(&self, script: Script, index: usize) {
Log::verify(
self.scene
.graph
.script_message_sender
.send(NodeScriptMessage::DestroyScript {
script,
handle: self.handle,
script_index: index,
}),
)
}
fn set_script_index(&mut self, index: usize) {
self.script_index = index;
}
}
pub struct ScriptDeinitContext<'a, 'b, 'c> {
pub elapsed_time: f32,
pub plugins: PluginsRefMut<'a>,
pub resource_manager: &'a ResourceManager,
pub scene: &'b mut Scene,
pub scene_handle: Handle<Scene>,
pub node_handle: Handle<Node>,
pub message_sender: &'c ScriptMessageSender,
pub task_pool: &'a mut TaskPoolHandler,
pub graphics_context: &'a mut GraphicsContext,
pub user_interfaces: &'a mut UiContainer,
pub script_index: usize,
pub input_state: &'a InputState,
}
impl UniversalScriptContext for ScriptDeinitContext<'_, '_, '_> {
fn node(&mut self) -> Result<&mut Node, PoolError> {
self.scene.graph.try_get_node_mut(self.node_handle)
}
fn destroy_script_deferred(&self, script: Script, index: usize) {
Log::verify(
self.scene
.graph
.script_message_sender
.send(NodeScriptMessage::DestroyScript {
script,
handle: self.node_handle,
script_index: index,
}),
)
}
fn set_script_index(&mut self, index: usize) {
self.script_index = index;
}
}
pub trait ScriptTrait: BaseScript + ComponentProvider {
fn on_init(&mut self, #[allow(unused_variables)] ctx: &mut ScriptContext) -> GameResult {
Ok(())
}
fn on_start(&mut self, #[allow(unused_variables)] ctx: &mut ScriptContext) -> GameResult {
Ok(())
}
fn on_deinit(
&mut self,
#[allow(unused_variables)] ctx: &mut ScriptDeinitContext,
) -> GameResult {
Ok(())
}
fn on_os_event(
&mut self,
#[allow(unused_variables)] event: &Event<()>,
#[allow(unused_variables)] ctx: &mut ScriptContext,
) -> GameResult {
Ok(())
}
fn on_update(&mut self, #[allow(unused_variables)] ctx: &mut ScriptContext) -> GameResult {
Ok(())
}
fn on_message(
&mut self,
#[allow(unused_variables)] message: &mut dyn ScriptMessagePayload,
#[allow(unused_variables)] ctx: &mut ScriptMessageContext,
) -> GameResult {
Ok(())
}
}
#[derive(Debug)]
pub struct Script {
instance: Box<dyn ScriptTrait>,
pub(crate) initialized: bool,
pub(crate) started: bool,
}
impl TypeUuidProvider for Script {
fn type_uuid() -> Uuid {
Uuid::from_str("24ecd17d-9b46-4cc8-9d07-a1273e50a20e").unwrap()
}
}
impl Reflect for Script {
fn source_path() -> &'static str {
file!()
}
fn derived_types() -> &'static [TypeId] {
&[]
}
fn query_derived_types(&self) -> &'static [TypeId] {
Self::derived_types()
}
fn type_name(&self) -> &'static str {
self.instance.type_name()
}
fn doc(&self) -> &'static str {
self.instance.doc()
}
fn assembly_name(&self) -> &'static str {
self.instance.assembly_name()
}
fn type_assembly_name() -> &'static str {
env!("CARGO_PKG_NAME")
}
fn fields_ref(&self, func: &mut dyn FnMut(&[FieldRef])) {
self.instance.fields_ref(func)
}
fn fields_mut(&mut self, func: &mut dyn FnMut(&mut [FieldMut])) {
self.instance.fields_mut(func)
}
fn into_any(self: Box<Self>) -> Box<dyn Any> {
self.instance.into_any()
}
fn as_any(&self, func: &mut dyn FnMut(&dyn Any)) {
self.instance.deref().as_any(func)
}
fn as_any_mut(&mut self, func: &mut dyn FnMut(&mut dyn Any)) {
self.instance.deref_mut().as_any_mut(func)
}
fn as_reflect(&self, func: &mut dyn FnMut(&dyn Reflect)) {
self.instance.deref().as_reflect(func)
}
fn as_reflect_mut(&mut self, func: &mut dyn FnMut(&mut dyn Reflect)) {
self.instance.deref_mut().as_reflect_mut(func)
}
fn set(&mut self, value: Box<dyn Reflect>) -> Result<Box<dyn Reflect>, Box<dyn Reflect>> {
self.instance.deref_mut().set(value)
}
fn field(&self, name: &str, func: &mut dyn FnMut(Option<&dyn Reflect>)) {
self.instance.deref().field(name, func)
}
fn field_mut(&mut self, name: &str, func: &mut dyn FnMut(Option<&mut dyn Reflect>)) {
self.instance.deref_mut().field_mut(name, func)
}
fn as_array(&self, func: &mut dyn FnMut(Option<&dyn ReflectArray>)) {
self.instance.deref().as_array(func)
}
fn as_array_mut(&mut self, func: &mut dyn FnMut(Option<&mut dyn ReflectArray>)) {
self.instance.deref_mut().as_array_mut(func)
}
fn as_list(&self, func: &mut dyn FnMut(Option<&dyn ReflectList>)) {
self.instance.deref().as_list(func)
}
fn as_list_mut(&mut self, func: &mut dyn FnMut(Option<&mut dyn ReflectList>)) {
self.instance.deref_mut().as_list_mut(func)
}
fn try_clone_box(&self) -> Option<Box<dyn Reflect>> {
Some(Box::new(self.clone()))
}
}
impl Deref for Script {
type Target = dyn ScriptTrait;
fn deref(&self) -> &Self::Target {
&*self.instance
}
}
impl DerefMut for Script {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut *self.instance
}
}
impl Visit for Script {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
let mut region_guard = visitor.enter_region(name)?;
if self.instance.visit("Data", &mut region_guard).is_ok() {
self.initialized.visit("Initialized", &mut region_guard)?;
} else {
Log::warn(format!(
"Unable to load script instance of id {} in new format! Trying to load in old format...",
self.id()
));
drop(region_guard);
self.instance.visit(name, visitor)?;
Log::warn(format!(
"Script instance of id {} loaded successfully using compatibility loader! Resave the script!",
self.id()
));
}
Ok(())
}
}
impl Clone for Script {
fn clone(&self) -> Self {
Self {
instance: self.instance.clone_box(),
initialized: false,
started: false,
}
}
}
impl Script {
#[inline]
pub fn new<T: ScriptTrait>(script_object: T) -> Self {
Self {
instance: Box::new(script_object),
initialized: false,
started: false,
}
}
pub fn summary(&self) -> String {
let mut summary = String::new();
if self.initialized {
summary.push_str("init ");
}
if self.started {
summary.push_str("start ");
}
use std::fmt::Write;
write!(summary, "{:?}", self.instance).unwrap();
summary
}
#[inline]
pub fn cast<T: ScriptTrait>(&self) -> Option<&T> {
self.instance.deref().as_any_ref().downcast_ref::<T>()
}
#[inline]
pub fn cast_mut<T: ScriptTrait>(&mut self) -> Option<&mut T> {
self.instance
.deref_mut()
.as_any_ref_mut()
.downcast_mut::<T>()
}
#[inline]
pub fn query_component_ref<T: Any>(&self) -> Option<&T> {
self.instance
.query_component_ref(TypeId::of::<T>())
.and_then(|c| c.downcast_ref())
}
#[inline]
pub fn query_component_mut<T: Any>(&mut self) -> Option<&mut T> {
self.instance
.query_component_mut(TypeId::of::<T>())
.and_then(|c| c.downcast_mut())
}
}
#[cfg(test)]
mod test {
use crate::scene::base::ScriptRecord;
use crate::{
core::{
impl_component_provider, reflect::prelude::*, variable::try_inherit_properties,
variable::InheritableVariable, visitor::prelude::*,
},
scene::base::Base,
script::{Script, ScriptTrait},
};
use fyrox_core::uuid_provider;
#[derive(Reflect, Visit, Debug, Clone, Default)]
struct MyScript {
field: InheritableVariable<f32>,
}
impl_component_provider!(MyScript);
uuid_provider!(MyScript = "eed9bf56-7d71-44a0-ba8e-0f3163c59669");
impl ScriptTrait for MyScript {}
#[test]
fn test_script_property_inheritance_on_nodes() {
let mut child = Base::default();
child.scripts.push(ScriptRecord::new(Script::new(MyScript {
field: InheritableVariable::new_non_modified(1.23),
})));
let mut parent = Base::default();
parent.scripts.push(ScriptRecord::new(Script::new(MyScript {
field: InheritableVariable::new_non_modified(3.21),
})));
child.as_reflect_mut(&mut |child| {
parent.as_reflect(&mut |parent| {
try_inherit_properties(child, parent, &[]).unwrap();
})
});
assert_eq!(
*child.script(0).unwrap().cast::<MyScript>().unwrap().field,
3.21
);
}
#[test]
fn test_script_property_inheritance() {
let mut child = Script::new(MyScript {
field: InheritableVariable::new_non_modified(1.23),
});
let parent = Script::new(MyScript {
field: InheritableVariable::new_non_modified(3.21),
});
child.as_reflect_mut(&mut |child| {
parent.as_reflect(&mut |parent| {
try_inherit_properties(child, parent, &[]).unwrap();
})
});
assert_eq!(*child.cast::<MyScript>().unwrap().field, 3.21);
}
#[test]
fn test_script_property_inheritance_option() {
let mut child = Some(Script::new(MyScript {
field: InheritableVariable::new_non_modified(1.23),
}));
let parent = Some(Script::new(MyScript {
field: InheritableVariable::new_non_modified(3.21),
}));
child.as_reflect_mut(&mut |child| {
parent.as_reflect(&mut |parent| {
try_inherit_properties(child, parent, &[]).unwrap();
})
});
assert_eq!(
*child.as_ref().unwrap().cast::<MyScript>().unwrap().field,
3.21
);
}
}