#![warn(missing_docs)]
pub mod dylib;
pub mod error;
use crate::{
asset::{manager::ResourceManager, untyped::UntypedResource},
core::{
define_as_any_trait, dyntype::DynTypeConstructorContainer, log::Log, pool::Handle,
reflect::Reflect, variable::try_inherit_properties, visitor::error::VisitError,
visitor::Visit,
},
engine::{
input::InputState, task::TaskPoolHandler, ApplicationLoopController, GraphicsContext,
PerformanceStatistics, ScriptProcessor, SerializationContext,
},
event::Event,
graph::NodeMapping,
gui::{
constructor::WidgetConstructorContainer,
inspector::editors::PropertyEditorDefinitionContainer, message::UiMessage, UiContainer,
UserInterface,
},
plugin::error::{GameError, GameResult},
resource::model::Model,
scene::{graph::NodePool, navmesh, Scene, SceneContainer, SceneLoader},
};
use fyrox_core::err;
use std::path::{Path, PathBuf};
use std::{
any::TypeId,
ops::{Deref, DerefMut},
sync::Arc,
};
pub enum PluginContainer {
Static(Box<dyn Plugin>),
Dynamic(Box<dyn DynamicPlugin>),
}
pub trait DynamicPlugin {
fn display_name(&self) -> String;
fn is_reload_needed_now(&self) -> bool;
fn as_loaded_ref(&self) -> &dyn Plugin;
fn as_loaded_mut(&mut self) -> &mut dyn Plugin;
fn is_loaded(&self) -> bool;
fn prepare_to_reload(&mut self) {}
fn reload(
&mut self,
fill_and_register: &mut dyn FnMut(&mut dyn Plugin) -> Result<(), String>,
) -> Result<(), String>;
}
impl Deref for PluginContainer {
type Target = dyn Plugin;
fn deref(&self) -> &Self::Target {
match self {
PluginContainer::Static(plugin) => &**plugin,
PluginContainer::Dynamic(plugin) => plugin.as_loaded_ref(),
}
}
}
impl DerefMut for PluginContainer {
fn deref_mut(&mut self) -> &mut Self::Target {
match self {
PluginContainer::Static(plugin) => &mut **plugin,
PluginContainer::Dynamic(plugin) => plugin.as_loaded_mut(),
}
}
}
pub struct LoaderOutput<T> {
pub payload: T,
pub path: PathBuf,
pub data: Vec<u8>,
}
pub type SceneLoaderOutput = LoaderOutput<Scene>;
pub type SceneLoaderResult = Result<SceneLoaderOutput, VisitError>;
pub type UiLoaderOutput = LoaderOutput<UserInterface>;
pub type UiLoaderResult = Result<UiLoaderOutput, VisitError>;
pub struct PluginRegistrationContext<'a> {
pub serialization_context: &'a Arc<SerializationContext>,
pub widget_constructors: &'a Arc<WidgetConstructorContainer>,
pub dyn_type_constructors: &'a Arc<DynTypeConstructorContainer>,
pub resource_manager: &'a ResourceManager,
}
pub struct PluginContext<'a, 'b> {
pub scenes: &'a mut SceneContainer,
pub resource_manager: &'a ResourceManager,
pub user_interfaces: &'a mut UiContainer,
pub graphics_context: &'a mut GraphicsContext,
pub dt: f32,
pub lag: &'b mut f32,
pub serialization_context: &'a Arc<SerializationContext>,
pub widget_constructors: &'a Arc<WidgetConstructorContainer>,
pub dyn_type_constructors: &'a Arc<DynTypeConstructorContainer>,
pub performance_statistics: &'a PerformanceStatistics,
pub elapsed_time: f32,
pub script_processor: &'a ScriptProcessor,
pub loop_controller: ApplicationLoopController<'b>,
pub task_pool: &'a mut TaskPoolHandler,
pub input_state: &'a InputState,
}
impl<'a, 'b> PluginContext<'a, 'b> {
pub fn load_ui<U, P, C>(&mut self, path: U, callback: C)
where
U: Into<PathBuf>,
P: Plugin,
for<'c, 'd> C:
FnOnce(UiLoaderResult, &mut P, &mut PluginContext<'c, 'd>) -> GameResult + 'static,
{
let path = path.into();
self.task_pool.spawn_plugin_task(
UserInterface::load_from_file(
path.clone(),
self.widget_constructors.clone(),
self.dyn_type_constructors.clone(),
self.resource_manager.clone(),
),
move |result, plugin, ctx| match result {
Ok((ui, data)) => callback(
Ok(LoaderOutput {
payload: ui,
data,
path,
}),
plugin,
ctx,
),
Err(e) => callback(Err(e), plugin, ctx),
},
);
}
pub fn load_scene<U, P, C>(&mut self, path: U, is_derived: bool, callback: C)
where
U: Into<PathBuf>,
P: Plugin,
for<'c, 'd> C:
FnOnce(SceneLoaderResult, &mut P, &mut PluginContext<'c, 'd>) -> GameResult + 'static,
{
let path = path.into();
let serialization_context = self.serialization_context.clone();
let dyn_type_constructors = self.dyn_type_constructors.clone();
let resource_manager = self.resource_manager.clone();
let uuid = resource_manager.find::<Model>(&path).resource_uuid();
let io = resource_manager.resource_io();
self.task_pool.spawn_plugin_task(
{
let path = path.clone();
async move {
match SceneLoader::from_file(
path,
io.as_ref(),
serialization_context,
dyn_type_constructors,
resource_manager.clone(),
)
.await
{
Ok((loader, data)) => Ok((loader.finish().await, data)),
Err(e) => Err(e),
}
}
},
move |result, plugin, ctx| {
match result {
Ok((mut scene, data)) => {
if is_derived {
let model = ctx.resource_manager.find_uuid::<Model>(uuid);
let data = Model {
mapping: NodeMapping::UseHandles,
scene: scene.clone_one_to_one().0,
};
model.header().state.commit_ok(data);
for (handle, node) in scene.graph.pair_iter_mut() {
node.set_inheritance_data(handle, model.clone());
}
(&mut scene as &mut dyn Reflect).apply_recursively_mut(
&mut |object| {
let type_id = (*object).type_id();
if type_id != TypeId::of::<NodePool>() {
object.as_inheritable_variable_mut(&mut |variable| {
if let Some(variable) = variable {
variable.reset_modified_flag();
}
});
}
},
&[
TypeId::of::<UntypedResource>(),
TypeId::of::<navmesh::Container>(),
],
)
} else {
if let Some(source_asset) =
scene.graph[scene.graph.get_root()].root_resource()
{
let source_asset_ref = source_asset.data_ref();
let source_scene_ref = &source_asset_ref.scene;
Log::verify(try_inherit_properties(
&mut scene,
source_scene_ref,
&[
TypeId::of::<NodePool>(),
TypeId::of::<UntypedResource>(),
TypeId::of::<navmesh::Container>(),
],
));
}
}
callback(
Ok(LoaderOutput {
payload: scene,
path,
data,
}),
plugin,
ctx,
)
}
Err(error) => callback(Err(error), plugin, ctx),
}
},
);
}
pub fn load_scene_or_ui<P: Plugin>(&mut self, path: impl AsRef<Path>) {
let path = path.as_ref();
let ext = path
.extension()
.map(|ext| ext.to_string_lossy().to_string())
.unwrap_or_default();
match ext.as_str() {
"rgs" => self.load_scene(path, false, |result, _: &mut P, ctx| {
ctx.scenes.add(result?.payload);
Ok(())
}),
"ui" => self.load_ui(path, |result, _: &mut P, ctx| {
ctx.user_interfaces.add(result?.payload);
Ok(())
}),
_ => err!("File {path:?} is not a game scene nor a user interface!"),
}
}
}
define_as_any_trait!(PluginAsAny => Plugin);
impl dyn Plugin {
pub fn cast<T: Plugin>(&self) -> Option<&T> {
PluginAsAny::as_any(self).downcast_ref::<T>()
}
pub fn cast_mut<T: Plugin>(&mut self) -> Option<&mut T> {
PluginAsAny::as_any_mut(self).downcast_mut::<T>()
}
}
pub trait Plugin: PluginAsAny + Visit + Reflect {
fn register(
&self,
#[allow(unused_variables)] context: PluginRegistrationContext,
) -> GameResult {
Ok(())
}
fn register_property_editors(
&self,
#[allow(unused_variables)] editors: Arc<PropertyEditorDefinitionContainer>,
) {
}
fn init(
&mut self,
#[allow(unused_variables)] scene_path: Option<&str>,
#[allow(unused_variables)] context: PluginContext,
) -> GameResult {
Ok(())
}
fn on_loaded(&mut self, #[allow(unused_variables)] context: PluginContext) -> GameResult {
Ok(())
}
fn on_deinit(&mut self, #[allow(unused_variables)] context: PluginContext) -> GameResult {
Ok(())
}
fn update(&mut self, #[allow(unused_variables)] context: &mut PluginContext) -> GameResult {
Ok(())
}
fn post_update(
&mut self,
#[allow(unused_variables)] context: &mut PluginContext,
) -> GameResult {
Ok(())
}
fn on_os_event(
&mut self,
#[allow(unused_variables)] event: &Event<()>,
#[allow(unused_variables)] context: PluginContext,
) -> GameResult {
Ok(())
}
fn on_graphics_context_initialized(
&mut self,
#[allow(unused_variables)] context: PluginContext,
) -> GameResult {
Ok(())
}
fn before_rendering(
&mut self,
#[allow(unused_variables)] context: PluginContext,
) -> GameResult {
Ok(())
}
fn on_graphics_context_destroyed(
&mut self,
#[allow(unused_variables)] context: PluginContext,
) -> GameResult {
Ok(())
}
fn on_ui_message(
&mut self,
#[allow(unused_variables)] context: &mut PluginContext,
#[allow(unused_variables)] message: &UiMessage,
#[allow(unused_variables)] ui_handle: Handle<UserInterface>,
) -> GameResult {
Ok(())
}
fn on_game_error(
&mut self,
#[allow(unused_variables)] context: &mut PluginContext,
#[allow(unused_variables)] error: &GameError,
) -> bool {
false
}
}