#![allow(deprecated)]
use bevy_app::{App, Plugin};
use bevy_ecs::component::Component;
use bevy_ecs::observer::Trigger;
use bevy_ecs::prelude::{Name, OnRemove, Resource};
use bevy_ecs::schedule::{Schedule, ScheduleLabel};
use bevy_ecs::system::{Local, Query, SystemParam};
use std::any::TypeId;
use std::marker::PhantomData;
use std::time::{Duration, Instant};
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
pub struct PrePhysicsUpdate;
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
pub struct PhysicsUpdate;
#[derive(Resource, Default)]
pub struct PhysicsDelta {
pub delta_seconds: f32,
}
impl PhysicsDelta {
pub fn new(delta: f64) -> Self {
Self {
delta_seconds: delta as f32,
}
}
pub fn delta(&self) -> Duration {
Duration::from_secs_f32(self.delta_seconds)
}
}
#[derive(Resource, Default, Debug)]
pub struct MainThreadMarker;
use crate::interop::GodotNodeHandle;
use crate::prelude::main_thread_system;
use bevy_ecs::system::EntityCommands;
use godot::{classes::Node, obj::Gd};
use tracing::debug;
type ComponentInserter = Box<dyn Fn(&mut EntityCommands, &GodotNodeHandle) + Send + Sync>;
#[derive(Resource, Default)]
pub struct SceneTreeComponentRegistry {
components: Vec<(TypeId, ComponentInserter)>,
}
impl SceneTreeComponentRegistry {
pub fn register<C>(&mut self)
where
C: Component + Default,
{
let type_id = TypeId::of::<C>();
if self.components.iter().any(|(id, _)| *id == type_id) {
return;
}
let inserter = Box::new(|entity: &mut EntityCommands, _node: &GodotNodeHandle| {
entity.insert(C::default());
});
self.components.push((type_id, inserter));
}
pub fn register_with_init<C, F>(&mut self, init_fn: F)
where
C: Component,
F: Fn(&mut EntityCommands, &GodotNodeHandle) + Send + Sync + 'static,
{
let type_id = TypeId::of::<C>();
if self.components.iter().any(|(id, _)| *id == type_id) {
return;
}
let inserter = Box::new(init_fn);
self.components.push((type_id, inserter));
}
pub fn add_to_entity(&self, entity: &mut EntityCommands, node: &GodotNodeHandle) {
for (_, inserter) in &self.components {
inserter(entity, node);
}
}
}
pub trait AppSceneTreeExt {
fn register_scene_tree_component<C>(&mut self) -> &mut Self
where
C: Component + Default;
fn register_scene_tree_component_with_init<C, F>(&mut self, init_fn: F) -> &mut Self
where
C: Component,
F: Fn(&mut EntityCommands, &GodotNodeHandle) + Send + Sync + 'static;
}
impl AppSceneTreeExt for App {
fn register_scene_tree_component<C>(&mut self) -> &mut Self
where
C: Component + Default,
{
if !self
.world()
.contains_resource::<SceneTreeComponentRegistry>()
{
self.world_mut()
.init_resource::<SceneTreeComponentRegistry>();
}
self.world_mut()
.resource_mut::<SceneTreeComponentRegistry>()
.register::<C>();
self
}
fn register_scene_tree_component_with_init<C, F>(&mut self, init_fn: F) -> &mut Self
where
C: Component,
F: Fn(&mut EntityCommands, &GodotNodeHandle) + Send + Sync + 'static,
{
if !self
.world()
.contains_resource::<SceneTreeComponentRegistry>()
{
self.world_mut()
.init_resource::<SceneTreeComponentRegistry>();
}
self.world_mut()
.resource_mut::<SceneTreeComponentRegistry>()
.register_with_init::<C, F>(init_fn);
self
}
}
#[derive(Default)]
pub struct GodotBaseCorePlugin;
impl Plugin for GodotBaseCorePlugin {
fn build(&self, app: &mut App) {
app.add_plugins(bevy_time::TimePlugin)
.add_plugins(bevy_app::TaskPoolPlugin::default())
.add_plugins(bevy_diagnostic::FrameCountPlugin)
.add_plugins(bevy_diagnostic::DiagnosticsPlugin)
.init_resource::<PhysicsDelta>()
.init_non_send_resource::<MainThreadMarker>()
.init_resource::<SceneTreeComponentRegistry>()
.add_observer(on_godot_node_handle_removed);
app.add_schedule(Schedule::new(PrePhysicsUpdate));
app.add_schedule(Schedule::new(PhysicsUpdate));
}
}
#[derive(SystemParam)]
#[deprecated(note = "Use PhysicsDelta instead")]
pub struct SystemDeltaTimer<'w, 's> {
last_time: Local<'s, Option<Instant>>,
marker: PhantomData<&'w ()>,
}
#[allow(deprecated)]
impl<'w, 's> SystemDeltaTimer<'w, 's> {
pub fn delta(&mut self) -> Duration {
let now = Instant::now();
let last_time = self.last_time.unwrap_or(now);
*self.last_time = Some(now);
now - last_time
}
pub fn delta_seconds(&mut self) -> f32 {
self.delta().as_secs_f32()
}
pub fn delta_seconds_f64(&mut self) -> f64 {
self.delta().as_secs_f64()
}
}
pub trait FindEntityByNameExt<T> {
fn find_entity_by_name(self, name: &str) -> Option<T>;
}
impl<'a, T: 'a, U> FindEntityByNameExt<T> for U
where
U: Iterator<Item = (&'a Name, T)>,
{
fn find_entity_by_name(mut self, name: &str) -> Option<T> {
self.find_map(|(ent_name, t)| (ent_name.as_str() == name).then_some(t))
}
}
#[main_thread_system]
fn on_godot_node_handle_removed(
trigger: Trigger<OnRemove, GodotNodeHandle>,
query: Query<&GodotNodeHandle>,
) {
if let Ok(handle) = query.get(trigger.target())
&& let Ok(mut node) = Gd::<Node>::try_from_instance_id(handle.instance_id())
{
debug!(
"Freeing Godot node with instance_id {:?}",
handle.instance_id()
);
node.queue_free();
}
}