Move ecs into new repo
This commit is contained in:
parent
856f24e65e
commit
f646c11d21
20 changed files with 14 additions and 2216 deletions
|
@ -7,7 +7,6 @@ members = [
|
||||||
"asset",
|
"asset",
|
||||||
"context",
|
"context",
|
||||||
"controllable_thread",
|
"controllable_thread",
|
||||||
"ecs",
|
|
||||||
"engine",
|
"engine",
|
||||||
"entity_manager",
|
"entity_manager",
|
||||||
"examples/simple_window",
|
"examples/simple_window",
|
||||||
|
@ -18,7 +17,6 @@ members = [
|
||||||
"presentation",
|
"presentation",
|
||||||
"promise",
|
"promise",
|
||||||
"ring_buffer",
|
"ring_buffer",
|
||||||
"scene_update_macros",
|
|
||||||
"skybox",
|
"skybox",
|
||||||
"transaction_derive",
|
"transaction_derive",
|
||||||
]
|
]
|
||||||
|
@ -65,6 +63,7 @@ assetpath = { git = "https://gavania.de/hodasemi/vulkan_lib.git" }
|
||||||
audio = { git = "https://gavania.de/hodasemi/audio.git" }
|
audio = { git = "https://gavania.de/hodasemi/audio.git" }
|
||||||
library_loader = { git = "https://gavania.de/hodasemi/vulkan_lib.git" }
|
library_loader = { git = "https://gavania.de/hodasemi/vulkan_lib.git" }
|
||||||
ui = { git = "https://gavania.de/hodasemi/ui.git" }
|
ui = { git = "https://gavania.de/hodasemi/ui.git" }
|
||||||
|
ecs = { git = "https://gavania.de/hodasemi/ecs.git" }
|
||||||
|
|
||||||
[profile.release-lto]
|
[profile.release-lto]
|
||||||
inherits = "release"
|
inherits = "release"
|
||||||
|
|
|
@ -7,9 +7,9 @@ edition = "2024"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
audio = { workspace = true, optional = true }
|
audio = { workspace = true, optional = true }
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
|
ecs = { workspace = true }
|
||||||
|
|
||||||
presentation = { path = "../presentation" }
|
presentation = { path = "../presentation" }
|
||||||
ecs = { path = "../ecs" }
|
|
||||||
|
|
||||||
[target.'cfg(target_os = "linux")'.dependencies]
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
shared_library = { workspace = true }
|
shared_library = { workspace = true }
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "ecs"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["hodasemi <michaelh.95@t-online.de>"]
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
anyhow.workspace = true
|
|
||||||
destructure_traitobject.workspace = true
|
|
||||||
indexmap.workspace = true
|
|
||||||
serde = { workspace = true, features = ["derive"] }
|
|
||||||
paste.workspace = true
|
|
||||||
ron.workspace = true
|
|
||||||
utilities.workspace = true
|
|
||||||
|
|
||||||
scene_update_macros = { path = "../scene_update_macros" }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
timings = []
|
|
|
@ -1,313 +0,0 @@
|
||||||
use core::fmt;
|
|
||||||
use std::any::TypeId;
|
|
||||||
use std::collections::HashSet;
|
|
||||||
use std::{num::ParseIntError, str::FromStr};
|
|
||||||
|
|
||||||
use anyhow::Result;
|
|
||||||
use indexmap::IndexMap;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{ComponentDebug, ComponentNotFoundError, EntityComponent, MultiMut, TypeMap};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct EntityNotFoundError {
|
|
||||||
entity: Entity,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EntityNotFoundError {
|
|
||||||
pub(crate) fn new(entity: Entity) -> Self {
|
|
||||||
Self { entity }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Display for EntityNotFoundError {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(f, "Entity (ID: {}) not found!", self.entity)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::error::Error for EntityNotFoundError {}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct ActivationState {
|
|
||||||
activated: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ActivationState {
|
|
||||||
pub fn is_active(&self) -> bool {
|
|
||||||
self.activated
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn apply_change(&mut self) {
|
|
||||||
self.activated = !self.activated;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct EntityObject {
|
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
pub debug_name: Option<String>,
|
|
||||||
|
|
||||||
// gltf file name
|
|
||||||
pub gltf_file: Option<String>,
|
|
||||||
|
|
||||||
// activation state of Entity
|
|
||||||
pub(crate) activation_state: ActivationState,
|
|
||||||
|
|
||||||
// program local ID
|
|
||||||
pub entity_id: u32,
|
|
||||||
|
|
||||||
// component map
|
|
||||||
pub components: TypeMap,
|
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
component_names: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Send for EntityObject {}
|
|
||||||
unsafe impl Sync for EntityObject {}
|
|
||||||
|
|
||||||
impl EntityObject {
|
|
||||||
pub(crate) fn new(id: u32) -> Self {
|
|
||||||
Self {
|
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
debug_name: None,
|
|
||||||
|
|
||||||
gltf_file: None,
|
|
||||||
|
|
||||||
activation_state: ActivationState::default(),
|
|
||||||
|
|
||||||
entity_id: id,
|
|
||||||
|
|
||||||
components: TypeMap::default(),
|
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
component_names: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn gltf_file(&self) -> Option<&String> {
|
|
||||||
self.gltf_file.as_ref()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn multi_mut(&mut self) -> MultiMut<'_> {
|
|
||||||
self.components.multi_mut()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert_component<T: EntityComponent + ComponentDebug>(
|
|
||||||
&mut self,
|
|
||||||
component: T,
|
|
||||||
) -> Option<T> {
|
|
||||||
assert!(
|
|
||||||
!self.activation_state.is_active(),
|
|
||||||
"inserting components while the entity is activated is not allowed"
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
{
|
|
||||||
let name = component.name().to_string();
|
|
||||||
|
|
||||||
if !self.component_names.contains(&name) {
|
|
||||||
self.component_names.push(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.components.insert(component)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn insert_component_by_id(
|
|
||||||
&mut self,
|
|
||||||
type_id: TypeId,
|
|
||||||
component: Box<dyn EntityComponent>,
|
|
||||||
) -> Option<Box<dyn EntityComponent>> {
|
|
||||||
assert!(
|
|
||||||
!self.activation_state.is_active(),
|
|
||||||
"inserting components while the entity is activated is not allowed"
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
{
|
|
||||||
let name = component.name().to_string();
|
|
||||||
|
|
||||||
if !self.component_names.contains(&name) {
|
|
||||||
self.component_names.push(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.components.insert_type(type_id, component)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove_component<T: EntityComponent>(&mut self) -> Option<T> {
|
|
||||||
assert!(
|
|
||||||
!self.activation_state.is_active(),
|
|
||||||
"removing components while the entity is activated is not allowed"
|
|
||||||
);
|
|
||||||
|
|
||||||
let t: Option<T> = self.components.remove();
|
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
if let Some(t) = &t {
|
|
||||||
if let Some(index) = self
|
|
||||||
.component_names
|
|
||||||
.iter()
|
|
||||||
.position(|name| name == t.name())
|
|
||||||
{
|
|
||||||
self.component_names.remove(index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
t
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn remove_component_by_id(
|
|
||||||
&mut self,
|
|
||||||
type_id: TypeId,
|
|
||||||
) -> Option<Box<dyn EntityComponent>> {
|
|
||||||
assert!(
|
|
||||||
!self.activation_state.is_active(),
|
|
||||||
"removing components while the entity is activated is not allowed"
|
|
||||||
);
|
|
||||||
|
|
||||||
let t = self.components.remove_by_type_id(&type_id);
|
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
if let Some(t) = &t {
|
|
||||||
if let Some(index) = self
|
|
||||||
.component_names
|
|
||||||
.iter()
|
|
||||||
.position(|name| name == t.name())
|
|
||||||
{
|
|
||||||
self.component_names.remove(index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
t
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
pub fn component_names(&self) -> &[String] {
|
|
||||||
&self.component_names
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_component<T: EntityComponent + ComponentDebug>(
|
|
||||||
&self,
|
|
||||||
) -> std::result::Result<&T, ComponentNotFoundError> {
|
|
||||||
self.components.get()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_component_mut<T: EntityComponent + ComponentDebug>(
|
|
||||||
&mut self,
|
|
||||||
) -> std::result::Result<&mut T, ComponentNotFoundError> {
|
|
||||||
self.components.get_mut()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn contains_component<T: EntityComponent>(&self) -> bool {
|
|
||||||
self.components.contains::<T>()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_activated(&self) -> bool {
|
|
||||||
self.activation_state.is_active()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_entity(&self) -> Entity {
|
|
||||||
Entity { id: self.entity_id }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn clone_without_components(&self, id: u32) -> EntityObject {
|
|
||||||
EntityObject {
|
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
debug_name: self.debug_name.clone(),
|
|
||||||
|
|
||||||
// gltf file name
|
|
||||||
gltf_file: self.gltf_file.clone(),
|
|
||||||
|
|
||||||
activation_state: Default::default(),
|
|
||||||
|
|
||||||
entity_id: id,
|
|
||||||
|
|
||||||
// component map
|
|
||||||
components: TypeMap::default(),
|
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
component_names: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clone_component_from<T: EntityComponent + ComponentDebug + Clone>(
|
|
||||||
&mut self,
|
|
||||||
other: &Self,
|
|
||||||
) -> Result<()> {
|
|
||||||
let component = other.get_component::<T>()?;
|
|
||||||
self.insert_component(component.clone());
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::derive_hash_xor_eq)]
|
|
||||||
#[derive(Debug, Clone, Copy, Hash, Eq, Serialize, Deserialize)]
|
|
||||||
pub struct Entity {
|
|
||||||
pub(crate) id: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq<EntityObject> for Entity {
|
|
||||||
fn eq(&self, entity: &EntityObject) -> bool {
|
|
||||||
self.id == entity.entity_id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq<Entity> for Entity {
|
|
||||||
fn eq(&self, entity: &Entity) -> bool {
|
|
||||||
self.id == entity.id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Entity {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(f, "{}", self.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for Entity {
|
|
||||||
type Err = ParseIntError;
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
|
|
||||||
Ok(Self { id: s.parse()? })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct EntityMultiMut<'a> {
|
|
||||||
entities: &'a mut IndexMap<Entity, EntityObject>,
|
|
||||||
buffer: HashSet<Entity>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> EntityMultiMut<'a> {
|
|
||||||
pub(crate) fn new(entities: &'a mut IndexMap<Entity, EntityObject>) -> Self {
|
|
||||||
Self {
|
|
||||||
entities,
|
|
||||||
buffer: HashSet::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get(
|
|
||||||
&mut self,
|
|
||||||
entity: Entity,
|
|
||||||
) -> std::result::Result<&'a mut EntityObject, EntityNotFoundError> {
|
|
||||||
match self.entities.get_mut(&entity) {
|
|
||||||
Some(v) => {
|
|
||||||
let ptr = v as *mut EntityObject;
|
|
||||||
|
|
||||||
assert!(
|
|
||||||
self.buffer.get(&entity).is_none(),
|
|
||||||
"Entity ({}) already borrowed",
|
|
||||||
entity.id
|
|
||||||
);
|
|
||||||
self.buffer.insert(entity);
|
|
||||||
|
|
||||||
let e: Option<&'a mut EntityObject> = Some(unsafe { &mut *ptr });
|
|
||||||
|
|
||||||
e.ok_or_else(|| EntityNotFoundError::new(entity))
|
|
||||||
}
|
|
||||||
None => Err(EntityNotFoundError::new(entity)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
use crate::EntityObject;
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct EntityObjectManager {
|
|
||||||
current_entity_id: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EntityObjectManager {
|
|
||||||
pub(crate) fn fetch_add_entity_id(&mut self) -> u32 {
|
|
||||||
let id = self.current_entity_id;
|
|
||||||
self.current_entity_id += 1;
|
|
||||||
|
|
||||||
id
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn create_entity(&mut self) -> EntityObject {
|
|
||||||
EntityObject::new(self.fetch_add_entity_id())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,88 +0,0 @@
|
||||||
use std::{
|
|
||||||
any::{Any, TypeId},
|
|
||||||
collections::HashMap,
|
|
||||||
ops::DerefMut,
|
|
||||||
sync::Arc,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::World;
|
|
||||||
|
|
||||||
pub struct Events {
|
|
||||||
events: HashMap<
|
|
||||||
TypeId, // TypeId of Payload
|
|
||||||
(
|
|
||||||
Vec<Box<dyn Any + Send + Sync>>, // Payload
|
|
||||||
Vec<Arc<dyn Fn(&mut World, &dyn Any) -> anyhow::Result<()> + Send + Sync>>, // Listener on Payload
|
|
||||||
),
|
|
||||||
>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Events {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
events: HashMap::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Events {
|
|
||||||
pub(crate) fn take_events(&mut self) -> Self {
|
|
||||||
Self {
|
|
||||||
events: self
|
|
||||||
.events
|
|
||||||
.iter_mut()
|
|
||||||
.filter_map(|(type_id, (payload, listener))| {
|
|
||||||
(!payload.is_empty())
|
|
||||||
.then(|| (*type_id, (std::mem::take(payload), listener.clone())))
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clear(&mut self) {
|
|
||||||
for (payloads, listener) in self.events.values_mut() {
|
|
||||||
payloads.clear();
|
|
||||||
listener.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn register_event<T: Any + Send + Sync>(&mut self) {
|
|
||||||
self.events
|
|
||||||
.insert(TypeId::of::<T>(), (Vec::new(), Vec::new()));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_reader<T: Any + Send + Sync, F>(&mut self, f: F)
|
|
||||||
where
|
|
||||||
F: Fn(&mut World, &T) -> anyhow::Result<()> + Send + Sync + 'static,
|
|
||||||
{
|
|
||||||
match self.events.get_mut(&TypeId::of::<T>()) {
|
|
||||||
Some((_, listener)) => listener.push(Arc::new(move |world, payload| {
|
|
||||||
let typed_payload: &T = payload.downcast_ref().unwrap();
|
|
||||||
|
|
||||||
f(world, typed_payload)
|
|
||||||
})),
|
|
||||||
None => panic!("register event type first!"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_event<T: Any + Send + Sync>(&mut self, payload: T) {
|
|
||||||
match self.events.get_mut(&TypeId::of::<T>()) {
|
|
||||||
Some((payloads, _)) => payloads.push(Box::new(payload)),
|
|
||||||
None => panic!("register event type first!"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn fire_events(&mut self, world: &mut World) -> anyhow::Result<()> {
|
|
||||||
for (payloads, listeners) in self.events.values_mut() {
|
|
||||||
for payload in payloads.iter_mut() {
|
|
||||||
for listener in listeners.iter_mut() {
|
|
||||||
(listener)(world, payload.deref_mut())?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
payloads.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
mod entity;
|
|
||||||
mod entity_object_manager;
|
|
||||||
mod events;
|
|
||||||
mod resources;
|
|
||||||
mod type_map;
|
|
||||||
mod unsafe_component_store;
|
|
||||||
mod updates;
|
|
||||||
mod world;
|
|
||||||
|
|
||||||
pub use crate::entity::{Entity, EntityMultiMut, EntityNotFoundError, EntityObject};
|
|
||||||
pub use crate::events::Events;
|
|
||||||
pub use crate::resources::{ResourceMultiMut, Resources};
|
|
||||||
pub use crate::type_map::{
|
|
||||||
ComponentCreateInfo, ComponentDebug, ComponentNotFoundError, EntityComponent, MultiMut, TypeMap,
|
|
||||||
};
|
|
||||||
pub use crate::unsafe_component_store::UnsafeComponentStore;
|
|
||||||
pub use crate::updates::*;
|
|
||||||
pub use crate::world::{World, WorldBuilder};
|
|
|
@ -1,178 +0,0 @@
|
||||||
use std::{
|
|
||||||
any::{Any, TypeId},
|
|
||||||
collections::HashMap,
|
|
||||||
mem::transmute,
|
|
||||||
};
|
|
||||||
|
|
||||||
use utilities::prelude::{remove_life_time, remove_life_time_mut};
|
|
||||||
|
|
||||||
type Untyped = dyn Any + Send + Sync;
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct Resources {
|
|
||||||
map: HashMap<TypeId, Box<Untyped>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Resources {
|
|
||||||
pub fn insert<T: Any + Send + Sync>(&mut self, value: T) -> Option<T> {
|
|
||||||
self.map
|
|
||||||
.insert(TypeId::of::<T>(), Box::new(value))
|
|
||||||
.map(|any| *Self::downcast_unchecked(any))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert_if_not_exists<T: Default + Any + Send + Sync>(&mut self) {
|
|
||||||
if !self.contains::<T>() {
|
|
||||||
self.insert(T::default());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove<T: Any + Send + Sync>(&mut self) -> Option<T> {
|
|
||||||
self.map
|
|
||||||
.remove(&TypeId::of::<T>())
|
|
||||||
.map(|any| *Self::downcast_unchecked(any))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get<T: Any + Send + Sync>(&self) -> &T {
|
|
||||||
self.get_opt::<T>().unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_by_type_id<T: Any + Send + Sync>(&self, type_id: TypeId) -> &T {
|
|
||||||
self.get_opt_by_type_id(type_id).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_unchecked<'a, T: Any + Send + Sync>(&self) -> &'a T {
|
|
||||||
unsafe { remove_life_time(self.get::<T>()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_opt<T: Any + Send + Sync>(&self) -> Option<&T> {
|
|
||||||
self.get_opt_by_type_id(TypeId::of::<T>())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_opt_by_type_id<T: Any + Send + Sync>(&self, type_id: TypeId) -> Option<&T> {
|
|
||||||
debug_assert_eq!(type_id, TypeId::of::<T>());
|
|
||||||
|
|
||||||
self.map
|
|
||||||
.get(&type_id)
|
|
||||||
.map(|any| Self::downcast_ref_unchecked(any))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_mut<T: Any + Send + Sync>(&mut self) -> &mut T {
|
|
||||||
self.get_mut_opt::<T>().unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_mut_by_type_id<T: Any + Send + Sync>(&mut self, type_id: TypeId) -> &mut T {
|
|
||||||
self.get_mut_opt_by_type_id(type_id).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_mut_by_type_id_untyped(&mut self, type_id: TypeId) -> &mut Untyped {
|
|
||||||
self.get_mut_opt_by_type_id_untyped(type_id).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_mut_unchecked<'a, T: Any + Send + Sync>(&mut self) -> &'a mut T {
|
|
||||||
unsafe { remove_life_time_mut(self.get_mut::<T>()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_mut_opt<T: Any + Send + Sync>(&mut self) -> Option<&mut T> {
|
|
||||||
self.get_mut_opt_by_type_id(TypeId::of::<T>())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_mut_opt_by_type_id<T: Any + Send + Sync>(
|
|
||||||
&mut self,
|
|
||||||
type_id: TypeId,
|
|
||||||
) -> Option<&mut T> {
|
|
||||||
debug_assert_eq!(type_id, TypeId::of::<T>());
|
|
||||||
|
|
||||||
self.map
|
|
||||||
.get_mut(&type_id)
|
|
||||||
.map(|any| Self::downcast_mut_unchecked(any))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_mut_opt_by_type_id_untyped(&mut self, type_id: TypeId) -> Option<&mut Untyped> {
|
|
||||||
self.map.get_mut(&type_id).map(|any| any.as_mut())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn multi_mut(&mut self) -> ResourceMultiMut<'_> {
|
|
||||||
ResourceMultiMut::new(&mut self.map)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn contains<T: Any + Send + Sync>(&self) -> bool {
|
|
||||||
self.map.contains_key(&TypeId::of::<T>())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// helper
|
|
||||||
impl Resources {
|
|
||||||
fn downcast_unchecked<T: Any + Send + Sync>(boxed: Box<Untyped>) -> Box<T> {
|
|
||||||
unsafe { Box::from_raw(Box::into_raw(boxed) as *mut T) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn downcast_ref_unchecked<T: Any + Send + Sync>(boxed_ref: &Box<Untyped>) -> &T {
|
|
||||||
unsafe {
|
|
||||||
let ptr_to_ptr: *const *const T =
|
|
||||||
transmute(destructure_traitobject::data(boxed_ref as *const _));
|
|
||||||
|
|
||||||
&**ptr_to_ptr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn downcast_mut_unchecked<T: Any + Send + Sync>(boxed_ref: &mut Box<Untyped>) -> &mut T {
|
|
||||||
unsafe {
|
|
||||||
let ptr_to_ptr: *mut *mut T =
|
|
||||||
transmute(destructure_traitobject::data(boxed_ref as *mut _));
|
|
||||||
|
|
||||||
&mut **ptr_to_ptr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Allows mutable access to multiple components at once
|
|
||||||
pub struct ResourceMultiMut<'a> {
|
|
||||||
map: &'a mut HashMap<TypeId, Box<dyn Any + Send + Sync>>,
|
|
||||||
buffer: Vec<*mut Box<dyn Any + Send + Sync>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> ResourceMultiMut<'a> {
|
|
||||||
fn new(map: &'a mut HashMap<TypeId, Box<dyn Any + Send + Sync>>) -> Self {
|
|
||||||
ResourceMultiMut {
|
|
||||||
map,
|
|
||||||
buffer: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns requested type on success
|
|
||||||
pub fn get<T: Any + Send + Sync>(&mut self) -> &'a mut T {
|
|
||||||
self.get_by_type_id(&TypeId::of::<T>())
|
|
||||||
.map(|component| Resources::downcast_mut_unchecked(component))
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns requested type behind this type id on success
|
|
||||||
pub fn get_by_type_id(
|
|
||||||
&mut self,
|
|
||||||
type_id: &TypeId,
|
|
||||||
) -> Option<&'a mut Box<dyn Any + Send + Sync>> {
|
|
||||||
self.map.get_mut(type_id).map(|v| {
|
|
||||||
let ptr = v as *mut _;
|
|
||||||
|
|
||||||
match self.buffer.iter().find(|v| **v == ptr) {
|
|
||||||
Some(_) => {
|
|
||||||
panic!("This key has already been borrowed!");
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
self.buffer.push(ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let t: Option<&'a mut Box<dyn Any + Send + Sync>> = unsafe { transmute(ptr) };
|
|
||||||
|
|
||||||
t.unwrap()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// use this only when there are no references left
|
|
||||||
pub unsafe fn clear_all_usages(&mut self) {
|
|
||||||
self.buffer.clear();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,332 +0,0 @@
|
||||||
use std::mem::transmute;
|
|
||||||
use std::{
|
|
||||||
any::{Any, TypeId},
|
|
||||||
collections::{
|
|
||||||
HashMap,
|
|
||||||
hash_map::{Iter, IterMut},
|
|
||||||
},
|
|
||||||
fmt::Display,
|
|
||||||
};
|
|
||||||
|
|
||||||
use anyhow::Result;
|
|
||||||
use destructure_traitobject;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::World;
|
|
||||||
|
|
||||||
pub trait EntityComponent: Any + Send + Sync {
|
|
||||||
fn enable(&mut self, _world: &mut World) -> Result<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn disable(&mut self, _world: &mut World) -> Result<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn name(&self) -> &str;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait ComponentDebug {
|
|
||||||
fn debug_name() -> &'static str;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait ComponentCreateInfo<'de>: Serialize + Deserialize<'de> {
|
|
||||||
fn to_string(&self) -> Result<String> {
|
|
||||||
Ok(ron::to_string(self)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_str(s: &'de str) -> Option<Self> {
|
|
||||||
match ron::from_str(s) {
|
|
||||||
Ok(t) => Some(t),
|
|
||||||
Err(err) => {
|
|
||||||
println!("{:?}", err);
|
|
||||||
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct TypeMap {
|
|
||||||
map: HashMap<TypeId, Box<dyn EntityComponent>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for TypeMap {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
map: HashMap::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TypeMap {
|
|
||||||
pub fn insert<T: EntityComponent + ComponentDebug>(&mut self, value: T) -> Option<T> {
|
|
||||||
self.map
|
|
||||||
.insert(TypeId::of::<T>(), Box::new(value))
|
|
||||||
.map(|any| *Self::downcast_unchecked(any))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert_type(
|
|
||||||
&mut self,
|
|
||||||
type_id: TypeId,
|
|
||||||
component: Box<dyn EntityComponent>,
|
|
||||||
) -> Option<Box<dyn EntityComponent>> {
|
|
||||||
self.map.insert(type_id, component)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove<T: EntityComponent>(&mut self) -> Option<T> {
|
|
||||||
self.remove_by_type_id(&TypeId::of::<T>())
|
|
||||||
.map(|any| *Self::downcast_unchecked(any))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove_by_type_id(&mut self, type_id: &TypeId) -> Option<Box<dyn EntityComponent>> {
|
|
||||||
self.map.remove(type_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get<T: EntityComponent + ComponentDebug>(
|
|
||||||
&self,
|
|
||||||
) -> std::result::Result<&T, ComponentNotFoundError> {
|
|
||||||
self.map
|
|
||||||
.get(&TypeId::of::<T>())
|
|
||||||
.map(|any| Self::downcast_ref_unchecked(any))
|
|
||||||
.ok_or_else(ComponentNotFoundError::component::<T>)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_by_type_id(
|
|
||||||
&self,
|
|
||||||
type_id: &TypeId,
|
|
||||||
) -> std::result::Result<&Box<dyn EntityComponent>, ComponentNotFoundError> {
|
|
||||||
self.map
|
|
||||||
.get(type_id)
|
|
||||||
.ok_or_else(|| ComponentNotFoundError::type_id(*type_id))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_mut<T: EntityComponent + ComponentDebug>(
|
|
||||||
&mut self,
|
|
||||||
) -> std::result::Result<&mut T, ComponentNotFoundError> {
|
|
||||||
self.map
|
|
||||||
.get_mut(&TypeId::of::<T>())
|
|
||||||
.map(|any| Self::downcast_mut_unchecked(any))
|
|
||||||
.ok_or_else(ComponentNotFoundError::component::<T>)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_mut_by_type_id(
|
|
||||||
&mut self,
|
|
||||||
type_id: &TypeId,
|
|
||||||
) -> std::result::Result<&mut Box<dyn EntityComponent>, ComponentNotFoundError> {
|
|
||||||
self.map
|
|
||||||
.get_mut(type_id)
|
|
||||||
.ok_or_else(|| ComponentNotFoundError::type_id(*type_id))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn contains<T: EntityComponent>(&self) -> bool {
|
|
||||||
self.contains_type_id(&TypeId::of::<T>())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn contains_type_id(&self, type_id: &TypeId) -> bool {
|
|
||||||
self.map.contains_key(type_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn multi_mut(&mut self) -> MultiMut<'_> {
|
|
||||||
MultiMut::new(&mut self.map)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn iter(&self) -> Iter<'_, TypeId, Box<dyn EntityComponent>> {
|
|
||||||
self.map.iter()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn iter_mut(&mut self) -> IterMut<'_, TypeId, Box<dyn EntityComponent>> {
|
|
||||||
self.map.iter_mut()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn downcast_unchecked<T: EntityComponent>(boxed: Box<dyn EntityComponent>) -> Box<T> {
|
|
||||||
unsafe { Box::from_raw(Box::into_raw(boxed) as *mut T) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn downcast_ref_unchecked<T: EntityComponent>(boxed_ref: &Box<dyn EntityComponent>) -> &T {
|
|
||||||
unsafe {
|
|
||||||
let ptr_to_ptr: *const *const T =
|
|
||||||
transmute(destructure_traitobject::data(boxed_ref as *const _));
|
|
||||||
|
|
||||||
&**ptr_to_ptr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn downcast_mut_unchecked<T: EntityComponent>(
|
|
||||||
boxed_ref: &mut Box<dyn EntityComponent>,
|
|
||||||
) -> &mut T {
|
|
||||||
unsafe {
|
|
||||||
let ptr_to_ptr: *mut *mut T =
|
|
||||||
transmute(destructure_traitobject::data(boxed_ref as *mut _));
|
|
||||||
|
|
||||||
&mut **ptr_to_ptr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Allows mutable access to multiple components at once
|
|
||||||
pub struct MultiMut<'a> {
|
|
||||||
map: &'a mut HashMap<TypeId, Box<dyn EntityComponent>>,
|
|
||||||
buffer: Vec<*mut Box<dyn EntityComponent>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> MultiMut<'a> {
|
|
||||||
fn new(map: &'a mut HashMap<TypeId, Box<dyn EntityComponent>>) -> Self {
|
|
||||||
MultiMut {
|
|
||||||
map,
|
|
||||||
buffer: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns requested type on success
|
|
||||||
pub fn get<T: EntityComponent + ComponentDebug>(
|
|
||||||
&mut self,
|
|
||||||
) -> std::result::Result<&'a mut T, ComponentNotFoundError> {
|
|
||||||
self.get_by_type_id(&TypeId::of::<T>())
|
|
||||||
.map(|component| TypeMap::downcast_mut_unchecked(component))
|
|
||||||
.map_err(|_| ComponentNotFoundError::component::<T>())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns requested type behind this type id on success
|
|
||||||
pub fn get_by_type_id(
|
|
||||||
&mut self,
|
|
||||||
type_id: &TypeId,
|
|
||||||
) -> std::result::Result<&'a mut Box<dyn EntityComponent>, ComponentNotFoundError> {
|
|
||||||
match self.map.get_mut(type_id) {
|
|
||||||
Some(v) => {
|
|
||||||
let ptr = v as *mut _;
|
|
||||||
|
|
||||||
match self.buffer.iter().find(|v| **v == ptr) {
|
|
||||||
Some(_) => {
|
|
||||||
panic!("This key has already been borrowed!");
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
self.buffer.push(ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let t: Option<&'a mut Box<dyn EntityComponent>> = unsafe { transmute(ptr) };
|
|
||||||
|
|
||||||
t.ok_or_else(|| ComponentNotFoundError::type_id(*type_id))
|
|
||||||
}
|
|
||||||
None => Err(ComponentNotFoundError::type_id(*type_id)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// use this only when there are no references left
|
|
||||||
pub unsafe fn clear_all_usages(&mut self) {
|
|
||||||
self.buffer.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum ComponentRequestType {
|
|
||||||
TypeId(TypeId),
|
|
||||||
ComponentName(&'static str),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct ComponentNotFoundError {
|
|
||||||
pub request_type: ComponentRequestType,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ComponentNotFoundError {
|
|
||||||
fn type_id(type_id: TypeId) -> Self {
|
|
||||||
Self {
|
|
||||||
request_type: ComponentRequestType::TypeId(type_id),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn component<T: ComponentDebug>() -> Self {
|
|
||||||
Self {
|
|
||||||
request_type: ComponentRequestType::ComponentName(T::debug_name()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for ComponentNotFoundError {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(f, "Entity Component ({:?}) not found!", self.request_type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::error::Error for ComponentNotFoundError {}
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
|
||||||
struct Test {
|
|
||||||
x: u32,
|
|
||||||
y: u32,
|
|
||||||
z: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EntityComponent for Test {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"Test"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ComponentDebug for Test {
|
|
||||||
fn debug_name() -> &'static str {
|
|
||||||
"Test"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq for Test {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.x == other.x && self.y == other.y && self.z == other.z
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn verify_multi_mut() {
|
|
||||||
let mut map = TypeMap::default();
|
|
||||||
|
|
||||||
map.insert(Test::default());
|
|
||||||
|
|
||||||
// test it multiple times, just for sanity
|
|
||||||
for _ in 0..10 {
|
|
||||||
let test = map.get::<Test>().unwrap().clone();
|
|
||||||
|
|
||||||
let mut multi_mut = map.multi_mut();
|
|
||||||
|
|
||||||
let multi_mut_test = multi_mut.get::<Test>().unwrap().clone();
|
|
||||||
|
|
||||||
assert_eq!(test, multi_mut_test);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn verify_insert() {
|
|
||||||
let mut map = TypeMap::default();
|
|
||||||
|
|
||||||
let reference = Test { x: 5, y: 20, z: 30 };
|
|
||||||
|
|
||||||
map.insert(reference.clone());
|
|
||||||
let old = map.insert(reference.clone()).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(old, reference);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn verify_get() {
|
|
||||||
let mut map = TypeMap::default();
|
|
||||||
|
|
||||||
let reference = Test { x: 5, y: 20, z: 30 };
|
|
||||||
|
|
||||||
map.insert(reference.clone());
|
|
||||||
|
|
||||||
assert_eq!(map.get::<Test>().unwrap().clone(), reference);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn verify_get_mut() {
|
|
||||||
let mut map = TypeMap::default();
|
|
||||||
|
|
||||||
let reference = Test { x: 5, y: 20, z: 30 };
|
|
||||||
|
|
||||||
map.insert(reference.clone());
|
|
||||||
|
|
||||||
assert_eq!(map.get_mut::<Test>().unwrap().clone(), reference);
|
|
||||||
}
|
|
|
@ -1,67 +0,0 @@
|
||||||
use std::{fmt, ptr};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct UnsafeComponentStore<T> {
|
|
||||||
ptr: *mut T,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> UnsafeComponentStore<T> {
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// Creates structure with null pointer. Should never be called like this.
|
|
||||||
pub unsafe fn empty() -> Self {
|
|
||||||
Self {
|
|
||||||
ptr: ptr::null_mut(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// Returns mutable reference to the inner pointer. Use always with caution
|
|
||||||
/// and make 100% sure that the actual struct is still present.
|
|
||||||
#[allow(clippy::mut_from_ref)]
|
|
||||||
pub unsafe fn as_mut(&self) -> &mut T {
|
|
||||||
assert_ne!(
|
|
||||||
self.ptr,
|
|
||||||
ptr::null_mut(),
|
|
||||||
"Called UnsafeComponentStore while being empty"
|
|
||||||
);
|
|
||||||
|
|
||||||
unsafe { self.ptr.as_mut().unwrap() }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_init(&self) -> bool {
|
|
||||||
!self.ptr.is_null()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> From<&mut T> for UnsafeComponentStore<T> {
|
|
||||||
fn from(ptr: &mut T) -> Self {
|
|
||||||
Self { ptr: ptr as *mut T }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> From<&T> for UnsafeComponentStore<T> {
|
|
||||||
fn from(ptr: &T) -> Self {
|
|
||||||
Self {
|
|
||||||
ptr: ptr as *const T as *mut T,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> fmt::Debug for UnsafeComponentStore<T> {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.debug_struct("UnsafeComponentStore")
|
|
||||||
.field("ptr", &self.ptr)
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Default for UnsafeComponentStore<T> {
|
|
||||||
fn default() -> Self {
|
|
||||||
unsafe { Self::empty() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl<T> Send for UnsafeComponentStore<T> {}
|
|
||||||
unsafe impl<T> Sync for UnsafeComponentStore<T> {}
|
|
|
@ -1,675 +0,0 @@
|
||||||
#![allow(clippy::type_complexity)]
|
|
||||||
|
|
||||||
use std::any::TypeId;
|
|
||||||
|
|
||||||
use std::marker::PhantomData;
|
|
||||||
#[cfg(feature = "timings")]
|
|
||||||
use std::time::Instant;
|
|
||||||
|
|
||||||
use anyhow::Result;
|
|
||||||
use indexmap::IndexMap;
|
|
||||||
|
|
||||||
#[cfg(feature = "timings")]
|
|
||||||
use super::super::timings::Timings;
|
|
||||||
|
|
||||||
use crate::*;
|
|
||||||
use scene_update_macros::implement_pair_update;
|
|
||||||
|
|
||||||
macro_rules! impl_singleton_update {
|
|
||||||
( $name: ident, $([$var: ident]$(,)?)+ ) => {
|
|
||||||
impl Archetype {
|
|
||||||
paste::item! {
|
|
||||||
pub fn [<create_ $name>]<F, Filter, $($var,)+>(f: F, filter: Filter) -> Self
|
|
||||||
where
|
|
||||||
F: Fn(&mut World, Entity, $(&mut $var,)+) -> Result<()> + Send + Sync + Clone + 'static,
|
|
||||||
Filter: CheckFilter + 'static,
|
|
||||||
$(
|
|
||||||
$var: EntityComponent + ComponentDebug,
|
|
||||||
)+
|
|
||||||
{
|
|
||||||
$(
|
|
||||||
filter.verify_dedup::<$var>();
|
|
||||||
)+
|
|
||||||
|
|
||||||
Self {
|
|
||||||
check_entity: Box::new({
|
|
||||||
move |entity| {
|
|
||||||
$(
|
|
||||||
if !entity.components.contains::<$var>() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
)+
|
|
||||||
|
|
||||||
if !filter.check(entity) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
|
|
||||||
create_callback: Box::new(move |entity| {
|
|
||||||
$(
|
|
||||||
let [< $var:lower >] = UnsafeComponentStore::from(
|
|
||||||
entity.get_component::<$var>()?
|
|
||||||
);
|
|
||||||
)+
|
|
||||||
|
|
||||||
let f = f.clone();
|
|
||||||
|
|
||||||
Ok(Box::new(move |e, scene_contents| {
|
|
||||||
unsafe { f(scene_contents, e, $([< $var:lower >].as_mut(),)+) }
|
|
||||||
}))
|
|
||||||
}),
|
|
||||||
|
|
||||||
entities: IndexMap::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Func, Filter, $( $var, )+> AddUpdates2<( $( $var, )+ ), Func, Filter> for Updates
|
|
||||||
where
|
|
||||||
$(
|
|
||||||
$var: EntityComponent + ComponentDebug,
|
|
||||||
)+
|
|
||||||
Func: Fn(& mut World, Entity, $(&mut $var,)+) -> Result<()> + Send + Sync + Clone + 'static,
|
|
||||||
Filter: CheckFilter + 'static
|
|
||||||
{
|
|
||||||
fn add_update(
|
|
||||||
&mut self,
|
|
||||||
name: &str,
|
|
||||||
priority: u32,
|
|
||||||
func: Func,
|
|
||||||
filter: Filter
|
|
||||||
) -> Result<()> {
|
|
||||||
paste::item! {
|
|
||||||
self.add(name, priority, Update::Single(Archetype::[<create_ $name>](func, filter)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Func, Filter, $( $var, )+> AddUpdates<( $( $var, )+ ), Func, Filter> for WorldBuilder
|
|
||||||
where
|
|
||||||
$(
|
|
||||||
$var: EntityComponent + ComponentDebug,
|
|
||||||
)+
|
|
||||||
Func: Fn(& mut World, Entity, $(&mut $var,)+) -> Result<()> + Send + Sync + Clone + 'static,
|
|
||||||
Filter: CheckFilter + 'static
|
|
||||||
{
|
|
||||||
fn add_update(
|
|
||||||
&mut self,
|
|
||||||
name: &str,
|
|
||||||
priority: u32,
|
|
||||||
func: Func,
|
|
||||||
filter: Filter,
|
|
||||||
) -> Result<()> {
|
|
||||||
self.updates.add_update(name, priority, func, filter)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! impl_pair_update {
|
|
||||||
(
|
|
||||||
$lhs_id: expr,
|
|
||||||
( $([$lhs_little: ident: $lhs_big: ident]$(,)?)+ ),
|
|
||||||
$rhs_id: expr,
|
|
||||||
( $([$rhs_little: ident: $rhs_big: ident]$(,)?)+ )
|
|
||||||
) => {
|
|
||||||
impl ArchetypePair {
|
|
||||||
paste::item! {
|
|
||||||
pub fn [<create_lhs_ $lhs_id _rhs_ $rhs_id>] <F, LeftFilter, RightFilter, $($lhs_big,)+ $($rhs_big,)+>
|
|
||||||
(f: F, left_filter: LeftFilter, right_filter: RightFilter) -> Self
|
|
||||||
where
|
|
||||||
F: Fn(&mut World, (Entity, $(&mut $lhs_big,)+), (Entity, $(&mut $rhs_big,)+)) -> Result<()> + Send + Sync + Clone + 'static,
|
|
||||||
LeftFilter: CheckFilter + 'static,
|
|
||||||
RightFilter: CheckFilter + 'static,
|
|
||||||
$(
|
|
||||||
$rhs_big: EntityComponent + ComponentDebug,
|
|
||||||
)+
|
|
||||||
$(
|
|
||||||
$lhs_big: EntityComponent + ComponentDebug,
|
|
||||||
)+
|
|
||||||
{
|
|
||||||
$(
|
|
||||||
left_filter.verify_dedup::<$lhs_big>();
|
|
||||||
)+
|
|
||||||
|
|
||||||
$(
|
|
||||||
right_filter.verify_dedup::<$rhs_big>();
|
|
||||||
)+
|
|
||||||
|
|
||||||
Self {
|
|
||||||
check_left_entity: Box::new({
|
|
||||||
move |entity| {
|
|
||||||
$(
|
|
||||||
if !entity.components.contains::<$lhs_big>() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
)+
|
|
||||||
|
|
||||||
if !left_filter.check(entity) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
check_right_entity: Box::new({
|
|
||||||
move |entity| {
|
|
||||||
$(
|
|
||||||
if !entity.components.contains::<$rhs_big>() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
)+
|
|
||||||
|
|
||||||
if !right_filter.check(entity) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
|
|
||||||
create_callback: Box::new(move |lhs_entity, rhs_entity| {
|
|
||||||
$(
|
|
||||||
let $lhs_little = UnsafeComponentStore::from(
|
|
||||||
lhs_entity.get_component::<$lhs_big>()?
|
|
||||||
);
|
|
||||||
)+
|
|
||||||
|
|
||||||
$(
|
|
||||||
let $rhs_little = UnsafeComponentStore::from(
|
|
||||||
rhs_entity.get_component::<$rhs_big>()?
|
|
||||||
);
|
|
||||||
)+
|
|
||||||
|
|
||||||
let f = f.clone();
|
|
||||||
|
|
||||||
Ok(Box::new(move |lhs_e, rhs_e, scene_contents| {
|
|
||||||
unsafe { f(scene_contents, (lhs_e, $($lhs_little.as_mut(),)+), (rhs_e, $($rhs_little.as_mut(),)+) ) }
|
|
||||||
}))
|
|
||||||
}),
|
|
||||||
|
|
||||||
entities: IndexMap::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Func, LhsFilter, RhsFilter, $( $lhs_big, )+ $($rhs_big,)+> AddUpdates2<( ($( $lhs_big, )+), ($($rhs_big,)+) ), Func, (LhsFilter, RhsFilter)> for Updates
|
|
||||||
where
|
|
||||||
$(
|
|
||||||
$rhs_big: EntityComponent + ComponentDebug,
|
|
||||||
)+
|
|
||||||
$(
|
|
||||||
$lhs_big: EntityComponent + ComponentDebug,
|
|
||||||
)+
|
|
||||||
Func: Fn(& mut World, (Entity, $(&mut $lhs_big,)+), (Entity, $(&mut $rhs_big,)+)) -> Result<()> + Send + Sync + Clone + 'static,
|
|
||||||
LhsFilter: CheckFilter + 'static,
|
|
||||||
RhsFilter: CheckFilter + 'static
|
|
||||||
{
|
|
||||||
fn add_update(
|
|
||||||
&mut self,
|
|
||||||
name: &str,
|
|
||||||
priority: u32,
|
|
||||||
func: Func,
|
|
||||||
filter: (LhsFilter, RhsFilter)
|
|
||||||
) -> Result<()> {
|
|
||||||
paste::item! {
|
|
||||||
self.add(name, priority, Update::Pair(ArchetypePair::[<create_lhs_ $lhs_id _rhs_ $rhs_id>](func, filter.0, filter.1)))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Func, LhsFilter, RhsFilter, $( $lhs_big, )+ $($rhs_big,)+> AddUpdates<( ($( $lhs_big, )+), ($($rhs_big,)+) ), Func, (LhsFilter, RhsFilter)> for WorldBuilder
|
|
||||||
where
|
|
||||||
$(
|
|
||||||
$rhs_big: EntityComponent + ComponentDebug,
|
|
||||||
)+
|
|
||||||
$(
|
|
||||||
$lhs_big: EntityComponent + ComponentDebug,
|
|
||||||
)+
|
|
||||||
Func: Fn(& mut World, (Entity, $(&mut $lhs_big,)+), (Entity, $(&mut $rhs_big,)+)) -> Result<()> + Send + Sync + Clone + 'static,
|
|
||||||
LhsFilter: CheckFilter + 'static,
|
|
||||||
RhsFilter: CheckFilter + 'static
|
|
||||||
{
|
|
||||||
fn add_update(
|
|
||||||
&mut self,
|
|
||||||
name: &str,
|
|
||||||
priority: u32,
|
|
||||||
func: Func,
|
|
||||||
filter: (LhsFilter, RhsFilter),
|
|
||||||
) -> Result<()> {
|
|
||||||
self.updates.add_update(name, priority, func, filter)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! impl_update_filter {
|
|
||||||
( $name: ident, $([$big: ident, $little: ident]$(,)?)+ ) => {
|
|
||||||
paste::item! {
|
|
||||||
pub struct [<$name Filter>]<$($big: EntityComponent,)+> {
|
|
||||||
$(
|
|
||||||
$little: std::marker::PhantomData<$big>,
|
|
||||||
)+
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<$($big: EntityComponent,)+> Default for [<$name Filter>]<$($big,)+> {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
$(
|
|
||||||
$little: std::marker::PhantomData,
|
|
||||||
)+
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<$($big: EntityComponent,)+> CheckFilter for [<$name Filter>]<$($big,)+> {
|
|
||||||
fn check(&self, entity: &EntityObject) -> bool {
|
|
||||||
$(
|
|
||||||
if entity.contains_component::<$big>() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
)+
|
|
||||||
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn verify_dedup<O: EntityComponent>(&self) {
|
|
||||||
$(
|
|
||||||
if TypeId::of::<O>() == TypeId::of::<$big>() {
|
|
||||||
panic!("Type is used as input and filter at the same time");
|
|
||||||
}
|
|
||||||
)+
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<$($big: EntityComponent,)+> Clone for [<$name Filter>]<$($big,)+> {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self {
|
|
||||||
$(
|
|
||||||
$little: self.$little.clone(),
|
|
||||||
)+
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Query<T, F = EmptyFilter>
|
|
||||||
where
|
|
||||||
F: CheckFilter,
|
|
||||||
{
|
|
||||||
pub components: T,
|
|
||||||
|
|
||||||
d: PhantomData<F>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait AddUpdates<T, Func, Filter> {
|
|
||||||
fn add_update(&mut self, name: &str, priority: u32, func: Func, filter: Filter) -> Result<()>;
|
|
||||||
}
|
|
||||||
|
|
||||||
trait AddUpdates2<T, Func, Filter> {
|
|
||||||
fn add_update(&mut self, name: &str, priority: u32, func: Func, filter: Filter) -> Result<()>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait CheckFilter: Send + Sync + Default + Clone {
|
|
||||||
fn check(&self, entity: &EntityObject) -> bool;
|
|
||||||
fn verify_dedup<O: EntityComponent>(&self);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Clone)]
|
|
||||||
pub struct EmptyFilter;
|
|
||||||
|
|
||||||
impl CheckFilter for EmptyFilter {
|
|
||||||
fn check(&self, _entity: &EntityObject) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn verify_dedup<O>(&self) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Clone, Debug)]
|
|
||||||
pub struct ArchetypeInfo {
|
|
||||||
entities: Vec<(Entity, Option<String>)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ArchetypeInfo {
|
|
||||||
pub fn new(entities: Vec<(Entity, Option<String>)>) -> Self {
|
|
||||||
Self { entities }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn entities(&self) -> &[(Entity, Option<String>)] {
|
|
||||||
&self.entities
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn count(&self) -> usize {
|
|
||||||
self.entities.len()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Archetype {
|
|
||||||
check_entity: Box<dyn Fn(&EntityObject) -> bool + Send + Sync>,
|
|
||||||
create_callback: Box<
|
|
||||||
dyn Fn(&EntityObject) -> Result<Box<dyn Fn(Entity, &mut World) -> Result<()> + Send + Sync>>
|
|
||||||
+ Send
|
|
||||||
+ Sync,
|
|
||||||
>,
|
|
||||||
|
|
||||||
entities: IndexMap<Entity, Box<dyn Fn(Entity, &mut World) -> Result<()> + Send + Sync>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Archetype {
|
|
||||||
pub fn add_entity(&mut self, entity_object: &EntityObject) -> Result<()> {
|
|
||||||
if (self.check_entity)(entity_object) {
|
|
||||||
let cb = (self.create_callback)(entity_object)?;
|
|
||||||
|
|
||||||
self.entities.insert(entity_object.as_entity(), cb);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove_entity(&mut self, entity: Entity) {
|
|
||||||
self.entities.swap_remove(&entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn execute(&self, scene_contents: &mut World) -> Result<()> {
|
|
||||||
for (entity, callback) in self.entities.iter() {
|
|
||||||
callback(*entity, scene_contents)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn entities(
|
|
||||||
&self,
|
|
||||||
) -> &IndexMap<Entity, Box<dyn Fn(Entity, &mut World) -> Result<()> + Send + Sync>> {
|
|
||||||
&self.entities
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ArchetypePair {
|
|
||||||
check_left_entity: Box<dyn Fn(&EntityObject) -> bool + Send + Sync>,
|
|
||||||
check_right_entity: Box<dyn Fn(&EntityObject) -> bool + Send + Sync>,
|
|
||||||
|
|
||||||
create_callback: Box<
|
|
||||||
dyn Fn(
|
|
||||||
&EntityObject,
|
|
||||||
&EntityObject,
|
|
||||||
)
|
|
||||||
-> Result<Box<dyn Fn(Entity, Entity, &mut World) -> Result<()> + Send + Sync>>
|
|
||||||
+ Send
|
|
||||||
+ Sync,
|
|
||||||
>,
|
|
||||||
|
|
||||||
entities: IndexMap<
|
|
||||||
(Entity, Entity),
|
|
||||||
Box<dyn Fn(Entity, Entity, &mut World) -> Result<()> + Send + Sync>,
|
|
||||||
>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ArchetypePair {
|
|
||||||
pub(crate) fn add_entity(
|
|
||||||
&mut self,
|
|
||||||
entity_object: &EntityObject,
|
|
||||||
entities: &IndexMap<Entity, EntityObject>,
|
|
||||||
) -> Result<()> {
|
|
||||||
for (other_entity, other_entity_object) in entities.iter() {
|
|
||||||
if entity_object.as_entity() == *other_entity {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if the entities can be on both sides
|
|
||||||
if (self.check_left_entity)(entity_object)
|
|
||||||
&& (self.check_right_entity)(other_entity_object)
|
|
||||||
{
|
|
||||||
let cb = (self.create_callback)(entity_object, other_entity_object)?;
|
|
||||||
|
|
||||||
self.entities
|
|
||||||
.insert((entity_object.as_entity(), *other_entity), cb);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self.check_left_entity)(other_entity_object)
|
|
||||||
&& (self.check_right_entity)(entity_object)
|
|
||||||
{
|
|
||||||
let cb = (self.create_callback)(other_entity_object, entity_object)?;
|
|
||||||
|
|
||||||
self.entities
|
|
||||||
.insert((*other_entity, entity_object.as_entity()), cb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn remove_entity(&mut self, entity: Entity) {
|
|
||||||
while let Some((left_entity, right_entity)) = self
|
|
||||||
.entities
|
|
||||||
.keys()
|
|
||||||
.find(|(left_entity, right_entity)| *left_entity == entity || *right_entity == entity)
|
|
||||||
.cloned()
|
|
||||||
{
|
|
||||||
self.entities.swap_remove(&(left_entity, right_entity));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn execute(&self, scene_contents: &mut World) -> Result<()> {
|
|
||||||
for ((left_entity, right_entity), callback) in self.entities.iter() {
|
|
||||||
callback(*left_entity, *right_entity, scene_contents)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum Update {
|
|
||||||
Single(Archetype),
|
|
||||||
Pair(ArchetypePair),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Archetype> for Update {
|
|
||||||
fn from(archetype: Archetype) -> Self {
|
|
||||||
Self::Single(archetype)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ArchetypePair> for Update {
|
|
||||||
fn from(pair: ArchetypePair) -> Self {
|
|
||||||
Self::Pair(pair)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Updates {
|
|
||||||
#[cfg(feature = "timings")]
|
|
||||||
timings: Timings,
|
|
||||||
|
|
||||||
updates: Vec<(String, u32, Update)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Updates {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
#[cfg(feature = "timings")]
|
|
||||||
timings: Timings::default,
|
|
||||||
|
|
||||||
updates: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Updates {
|
|
||||||
pub(crate) fn update(&mut self, world: &mut World) -> Result<()> {
|
|
||||||
#[cfg(feature = "timings")]
|
|
||||||
if let Some(timings) = self.timings.check_timing(world.now(), None) {
|
|
||||||
if !timings.is_empty() {
|
|
||||||
println!("timings: {:#?}", timings);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "timings")]
|
|
||||||
let timings = &mut self.timings;
|
|
||||||
|
|
||||||
self.updates
|
|
||||||
.iter()
|
|
||||||
.try_for_each(|(_name, _, update)| -> Result<()> {
|
|
||||||
#[cfg(feature = "timings")]
|
|
||||||
let before = Instant::now();
|
|
||||||
|
|
||||||
match update {
|
|
||||||
Update::Single(archetype) => {
|
|
||||||
archetype.execute(world)?;
|
|
||||||
}
|
|
||||||
Update::Pair(archetype_pair) => {
|
|
||||||
archetype_pair.execute(world)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "timings")]
|
|
||||||
timings.add(_name, Instant::now().duration_since(before));
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn add_entity(
|
|
||||||
&mut self,
|
|
||||||
entity_object: &EntityObject,
|
|
||||||
entities: &IndexMap<Entity, EntityObject>,
|
|
||||||
) -> Result<()> {
|
|
||||||
for (_, _, update) in self.updates.iter_mut() {
|
|
||||||
match update {
|
|
||||||
Update::Single(archetype) => {
|
|
||||||
archetype.add_entity(entity_object)?;
|
|
||||||
}
|
|
||||||
Update::Pair(archetype_pair) => {
|
|
||||||
archetype_pair.add_entity(entity_object, entities)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn remove_entity(&mut self, entity: Entity) {
|
|
||||||
for (_, _, update) in self.updates.iter_mut() {
|
|
||||||
match update {
|
|
||||||
Update::Single(archetype) => {
|
|
||||||
archetype.remove_entity(entity);
|
|
||||||
}
|
|
||||||
Update::Pair(archetype_pair) => {
|
|
||||||
archetype_pair.remove_entity(entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// pub(crate) fn clear(&mut self) {
|
|
||||||
// self.updates.clear();
|
|
||||||
|
|
||||||
// #[cfg(feature = "timings")]
|
|
||||||
// self.timings.clear();
|
|
||||||
// }
|
|
||||||
|
|
||||||
pub(crate) fn add(&mut self, name: &str, priority: u32, update: Update) -> Result<()> {
|
|
||||||
#[cfg(feature = "timings")]
|
|
||||||
self.timings.add_timing_afterwards(name);
|
|
||||||
|
|
||||||
self.updates.push((name.to_string(), priority, update));
|
|
||||||
self.updates
|
|
||||||
.sort_by(|(_, lhs_prio, _), (_, rhs_prio, _)| lhs_prio.cmp(rhs_prio));
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// #[derive(Default)]
|
|
||||||
// pub struct Archetypes {
|
|
||||||
// archetypes: HashMap<String, Archetype>,
|
|
||||||
// }
|
|
||||||
|
|
||||||
// impl Archetypes {
|
|
||||||
// pub(crate) fn add_entity(&mut self, entity_object: &EntityObject) -> Result<()> {
|
|
||||||
// for archetype in self.archetypes.values_mut() {
|
|
||||||
// archetype.add_entity(entity_object)?;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Ok(())
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pub(crate) fn remove_entity(&mut self, entity: Entity) {
|
|
||||||
// for archetype in self.archetypes.values_mut() {
|
|
||||||
// archetype.remove_entity(entity);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pub(crate) fn clear(&mut self) {
|
|
||||||
// self.archetypes.clear();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pub(crate) fn insert(&mut self, name: String, archetype: Archetype) {
|
|
||||||
// assert!(self.archetypes.insert(name, archetype).is_none());
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pub(crate) fn get(&self, name: &String) -> Option<&Archetype> {
|
|
||||||
// self.archetypes.get(name)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pub(crate) fn execute(
|
|
||||||
// &self,
|
|
||||||
// name: &String,
|
|
||||||
// scene_contents: &mut SceneContents<'_>,
|
|
||||||
// ) -> Result<()> {
|
|
||||||
// self.archetypes[name].execute(scene_contents)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
#[rustfmt::skip]
|
|
||||||
impl_singleton_update!(single, [R]);
|
|
||||||
#[rustfmt::skip]
|
|
||||||
impl_singleton_update!(double, [R], [S]);
|
|
||||||
#[rustfmt::skip]
|
|
||||||
impl_singleton_update!(triple, [R], [S], [T]);
|
|
||||||
#[rustfmt::skip]
|
|
||||||
impl_singleton_update!(quadruple, [R], [S], [T], [U]);
|
|
||||||
#[rustfmt::skip]
|
|
||||||
impl_singleton_update!(quintuple, [R], [S], [T], [U], [V]);
|
|
||||||
#[rustfmt::skip]
|
|
||||||
impl_singleton_update!(sextuple, [R], [S], [T], [U], [V], [W]);
|
|
||||||
#[rustfmt::skip]
|
|
||||||
impl_singleton_update!(septuple, [R], [S], [T], [U], [V], [W], [X]);
|
|
||||||
#[rustfmt::skip]
|
|
||||||
impl_singleton_update!(octuple, [R], [S], [T], [U], [V], [W], [X], [Y]);
|
|
||||||
#[rustfmt::skip]
|
|
||||||
impl_singleton_update!(ninetuple, [R], [S], [T], [U], [V], [W], [X], [Y], [Z]);
|
|
||||||
|
|
||||||
implement_pair_update!(impl_pair_update, 1, 10);
|
|
||||||
|
|
||||||
#[rustfmt::skip]
|
|
||||||
impl_update_filter!(Monuple, [R, r]);
|
|
||||||
#[rustfmt::skip]
|
|
||||||
impl_update_filter!(Couple, [R, r], [S, s]);
|
|
||||||
#[rustfmt::skip]
|
|
||||||
impl_update_filter!(Triple, [R, r], [S, s], [T, t]);
|
|
||||||
#[rustfmt::skip]
|
|
||||||
impl_update_filter!(Quadruple, [R, r], [S, s], [T, t], [U, u]);
|
|
||||||
#[rustfmt::skip]
|
|
||||||
impl_update_filter!(Quintuple, [R, r], [S, s], [T, t], [U, u], [V, v]);
|
|
||||||
#[rustfmt::skip]
|
|
||||||
impl_update_filter!(Sextuple, [R, r], [S, s], [T, t], [U, u], [V, v], [W, w]);
|
|
||||||
#[rustfmt::skip]
|
|
||||||
impl_update_filter!(Septuple, [R, r], [S, s], [T, t], [U, u], [V, v], [W, w], [X, x]);
|
|
||||||
#[rustfmt::skip]
|
|
||||||
impl_update_filter!(Octuple, [R, r], [S, s], [T, t], [U, u], [V, v], [W, w], [X, x], [Y, y]);
|
|
||||||
#[rustfmt::skip]
|
|
||||||
impl_update_filter!(Nonuple, [R, r], [S, s], [T, t], [U, u], [V, v], [W, w], [X, x], [Y, y], [Z, z]);
|
|
||||||
#[rustfmt::skip]
|
|
||||||
impl_update_filter!(Decuple, [R, r], [S, s], [T, t], [U, u], [V, v], [W, w], [X, x], [Y, y], [Z, z], [A, a]);
|
|
362
ecs/src/world.rs
362
ecs/src/world.rs
|
@ -1,362 +0,0 @@
|
||||||
use std::{
|
|
||||||
any::TypeId,
|
|
||||||
collections::HashMap,
|
|
||||||
time::{Duration, Instant},
|
|
||||||
};
|
|
||||||
|
|
||||||
use anyhow::{Result, bail};
|
|
||||||
use indexmap::IndexMap;
|
|
||||||
use utilities::prelude::{remove_life_time, remove_life_time_mut};
|
|
||||||
|
|
||||||
use crate::{entity_object_manager::EntityObjectManager, *};
|
|
||||||
|
|
||||||
pub struct WorldBuilder {
|
|
||||||
pub(crate) updates: Updates,
|
|
||||||
pub events: Events,
|
|
||||||
pub resources: Resources,
|
|
||||||
systems: Vec<Box<dyn Fn(&mut World) -> Result<bool> + Send + Sync + 'static>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WorldBuilder {
|
|
||||||
pub fn add_system<F>(&mut self, f: F)
|
|
||||||
where
|
|
||||||
F: Fn(&mut World) -> Result<bool> + Send + Sync + 'static,
|
|
||||||
{
|
|
||||||
self.systems.push(Box::new(f));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WorldBuilder {
|
|
||||||
pub fn build(self) -> World {
|
|
||||||
World {
|
|
||||||
updates: self.updates,
|
|
||||||
events: self.events,
|
|
||||||
resources: self.resources,
|
|
||||||
entities: Default::default(),
|
|
||||||
|
|
||||||
entities_to_remove: Default::default(),
|
|
||||||
entities_to_add: Default::default(),
|
|
||||||
entities_updates: Default::default(),
|
|
||||||
|
|
||||||
entity_object_manager: Default::default(),
|
|
||||||
|
|
||||||
start_time: Instant::now(),
|
|
||||||
|
|
||||||
systems: self.systems,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum ComponentChange {
|
|
||||||
Added(TypeId, Box<dyn EntityComponent>),
|
|
||||||
Removed(TypeId),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct World {
|
|
||||||
pub(crate) updates: Updates,
|
|
||||||
pub events: Events,
|
|
||||||
pub resources: Resources,
|
|
||||||
pub(crate) entities: IndexMap<Entity, EntityObject>,
|
|
||||||
|
|
||||||
entities_to_remove: Vec<Entity>,
|
|
||||||
entities_to_add: Vec<EntityObject>,
|
|
||||||
entities_updates: HashMap<Entity, Vec<ComponentChange>>,
|
|
||||||
|
|
||||||
entity_object_manager: EntityObjectManager,
|
|
||||||
|
|
||||||
start_time: Instant,
|
|
||||||
|
|
||||||
systems: Vec<Box<dyn Fn(&mut World) -> Result<bool> + Send + Sync + 'static>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl World {
|
|
||||||
pub fn builder() -> WorldBuilder {
|
|
||||||
WorldBuilder {
|
|
||||||
updates: Default::default(),
|
|
||||||
events: Default::default(),
|
|
||||||
resources: Default::default(),
|
|
||||||
systems: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn now(&self) -> Duration {
|
|
||||||
self.start_time.elapsed()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_entity(&mut self) -> EntityObject {
|
|
||||||
self.entity_object_manager.create_entity()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clone_without_components(&mut self, entity: &EntityObject) -> EntityObject {
|
|
||||||
entity.clone_without_components(self.entity_object_manager.fetch_add_entity_id())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn entities(&self) -> impl Iterator<Item = &EntityObject> {
|
|
||||||
self.entities.values()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn entity(
|
|
||||||
&self,
|
|
||||||
entity: Entity,
|
|
||||||
) -> std::result::Result<&EntityObject, EntityNotFoundError> {
|
|
||||||
self.entities
|
|
||||||
.get(&entity)
|
|
||||||
.ok_or_else(|| EntityNotFoundError::new(entity))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn entity_unchecked<'a>(
|
|
||||||
&self,
|
|
||||||
entity: Entity,
|
|
||||||
) -> std::result::Result<&'a EntityObject, EntityNotFoundError> {
|
|
||||||
self.entity(entity).map(|e| unsafe { remove_life_time(e) })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn entity_mut(
|
|
||||||
&mut self,
|
|
||||||
entity: Entity,
|
|
||||||
) -> std::result::Result<&mut EntityObject, EntityNotFoundError> {
|
|
||||||
self.entities
|
|
||||||
.get_mut(&entity)
|
|
||||||
.ok_or_else(|| EntityNotFoundError::new(entity))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn entity_mut_unchecked<'a>(
|
|
||||||
&mut self,
|
|
||||||
entity: Entity,
|
|
||||||
) -> std::result::Result<&'a mut EntityObject, EntityNotFoundError> {
|
|
||||||
self.entity_mut(entity)
|
|
||||||
.map(|e| unsafe { remove_life_time_mut(e) })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn entities_multi_mut(&mut self) -> EntityMultiMut<'_> {
|
|
||||||
EntityMultiMut::new(&mut self.entities)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_entity(&mut self, entity_object: EntityObject) -> Result<Entity> {
|
|
||||||
let entity = entity_object.as_entity();
|
|
||||||
self.entities_to_add.push(entity_object);
|
|
||||||
|
|
||||||
Ok(entity)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert_component<T: EntityComponent + ComponentDebug>(
|
|
||||||
&mut self,
|
|
||||||
entity: Entity,
|
|
||||||
component: T,
|
|
||||||
) -> Result<()> {
|
|
||||||
println!("insert component {}", T::debug_name());
|
|
||||||
|
|
||||||
let change = ComponentChange::Added(TypeId::of::<T>(), Box::new(component));
|
|
||||||
|
|
||||||
match self.entities_updates.get_mut(&entity) {
|
|
||||||
Some(changes) => {
|
|
||||||
changes.push(change);
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
self.entities_updates.insert(entity, vec![change]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove_component<T: EntityComponent + ComponentDebug>(
|
|
||||||
&mut self,
|
|
||||||
entity: Entity,
|
|
||||||
) -> Result<()> {
|
|
||||||
println!("remove component {}", T::debug_name());
|
|
||||||
|
|
||||||
let change = ComponentChange::Removed(TypeId::of::<T>());
|
|
||||||
|
|
||||||
match self.entities_updates.get_mut(&entity) {
|
|
||||||
Some(changes) => {
|
|
||||||
changes.push(change);
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
self.entities_updates.insert(entity, vec![change]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove_entity(&mut self, entity: Entity) -> Result<Option<EntityObject>> {
|
|
||||||
self.entities_to_remove.push(entity);
|
|
||||||
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// async application of changes
|
|
||||||
impl World {
|
|
||||||
fn _add_entity(&mut self, mut entity: EntityObject) -> Result<Entity> {
|
|
||||||
// call enable event for all components
|
|
||||||
for (_, component) in entity.components.iter_mut() {
|
|
||||||
component.enable(self)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
entity.activation_state.apply_change();
|
|
||||||
|
|
||||||
let e = entity.as_entity();
|
|
||||||
|
|
||||||
self.updates.add_entity(&mut entity, &self.entities)?;
|
|
||||||
assert!(self.entities.insert(e, entity).is_none());
|
|
||||||
|
|
||||||
Ok(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn _remove_entity(&mut self, entity: Entity) -> Result<Option<EntityObject>> {
|
|
||||||
if let Some(mut entity_object) = self.entities.swap_remove(&entity) {
|
|
||||||
for (_, component) in entity_object.components.iter_mut() {
|
|
||||||
component.disable(self)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
entity_object.activation_state.apply_change();
|
|
||||||
self.updates.remove_entity(entity);
|
|
||||||
|
|
||||||
return Ok(Some(entity_object));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn _insert_component<T: EntityComponent + ComponentDebug>(
|
|
||||||
&mut self,
|
|
||||||
entity: Entity,
|
|
||||||
mut component: T,
|
|
||||||
) -> Result<()> {
|
|
||||||
let entities = unsafe { remove_life_time_mut(&mut self.entities) };
|
|
||||||
|
|
||||||
let entity_object = entities
|
|
||||||
.get_mut(&entity)
|
|
||||||
.ok_or_else(|| EntityNotFoundError::new(entity))?;
|
|
||||||
|
|
||||||
self.updates.remove_entity(entity);
|
|
||||||
|
|
||||||
entity_object.activation_state.apply_change();
|
|
||||||
|
|
||||||
component.enable(self)?;
|
|
||||||
if entity_object.insert_component(component).is_some() {
|
|
||||||
bail!("component {} already present", T::debug_name());
|
|
||||||
}
|
|
||||||
|
|
||||||
entity_object.activation_state.apply_change();
|
|
||||||
|
|
||||||
self.updates.add_entity(entity_object, &self.entities)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn _remove_component<T: EntityComponent + ComponentDebug>(
|
|
||||||
&mut self,
|
|
||||||
entity: Entity,
|
|
||||||
) -> Result<()> {
|
|
||||||
let entities = unsafe { remove_life_time_mut(&mut self.entities) };
|
|
||||||
|
|
||||||
let entity_object = entities
|
|
||||||
.get_mut(&entity)
|
|
||||||
.ok_or_else(|| EntityNotFoundError::new(entity))?;
|
|
||||||
|
|
||||||
self.updates.remove_entity(entity);
|
|
||||||
|
|
||||||
entity_object.activation_state.apply_change();
|
|
||||||
|
|
||||||
if let Some(mut component) = entity_object.remove_component_by_id(TypeId::of::<T>()) {
|
|
||||||
component.disable(self)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
entity_object.activation_state.apply_change();
|
|
||||||
|
|
||||||
self.updates.add_entity(entity_object, &self.entities)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn commit_entity_changes(&mut self) -> Result<()> {
|
|
||||||
if !self.entities_to_remove.is_empty() {
|
|
||||||
println!("entities to remove {}", self.entities_to_remove.len());
|
|
||||||
}
|
|
||||||
|
|
||||||
for entity in core::mem::take(&mut self.entities_to_remove) {
|
|
||||||
self._remove_entity(entity)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if !self.entities_to_add.is_empty() {
|
|
||||||
println!("entities to add {}", self.entities_to_add.len());
|
|
||||||
}
|
|
||||||
|
|
||||||
for entity_object in core::mem::take(&mut self.entities_to_add) {
|
|
||||||
self._add_entity(entity_object)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if !self.entities_updates.is_empty() {
|
|
||||||
println!("entities to update {}", self.entities_updates.len());
|
|
||||||
}
|
|
||||||
|
|
||||||
for (entity, changes) in core::mem::take(&mut self.entities_updates) {
|
|
||||||
self.updates.remove_entity(entity);
|
|
||||||
|
|
||||||
if let Some(entity_object) = unsafe { self.entity_mut_unchecked(entity).ok() } {
|
|
||||||
entity_object.activation_state.apply_change();
|
|
||||||
|
|
||||||
for change in changes {
|
|
||||||
match change {
|
|
||||||
ComponentChange::Added(type_id, mut component) => {
|
|
||||||
component.enable(self)?;
|
|
||||||
let name = component.name().to_string();
|
|
||||||
|
|
||||||
if entity_object
|
|
||||||
.insert_component_by_id(type_id, component)
|
|
||||||
.is_some()
|
|
||||||
{
|
|
||||||
bail!("component {name} already present");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ComponentChange::Removed(type_id) => {
|
|
||||||
if let Some(mut component) =
|
|
||||||
entity_object.remove_component_by_id(type_id)
|
|
||||||
{
|
|
||||||
component.disable(self)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
entity_object.activation_state.apply_change();
|
|
||||||
|
|
||||||
self.updates.add_entity(entity_object, &self.entities)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl World {
|
|
||||||
pub fn run(&mut self) -> Result<()> {
|
|
||||||
let systems = std::mem::take(&mut self.systems);
|
|
||||||
|
|
||||||
loop {
|
|
||||||
// we need to take all events because of borrowing rules
|
|
||||||
let mut events = self.events.take_events();
|
|
||||||
events.fire_events(self)?;
|
|
||||||
|
|
||||||
{
|
|
||||||
// actually safe:
|
|
||||||
// - updates can't be altered on a running world
|
|
||||||
// - entity changes are processed afterwards
|
|
||||||
let w = unsafe { remove_life_time_mut(self) };
|
|
||||||
|
|
||||||
self.updates.update(w)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.commit_entity_changes()?;
|
|
||||||
|
|
||||||
for system in systems.iter() {
|
|
||||||
if !system(self)? {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -16,12 +16,12 @@ indexmap = { workspace = true }
|
||||||
utilities = { workspace = true }
|
utilities = { workspace = true }
|
||||||
assetpath = { workspace = true }
|
assetpath = { workspace = true }
|
||||||
shaderc = { workspace = true }
|
shaderc = { workspace = true }
|
||||||
|
ecs = { workspace = true }
|
||||||
|
|
||||||
config_handler = { path = "../config_handler" }
|
config_handler = { path = "../config_handler" }
|
||||||
asset = { path = "../asset" }
|
asset = { path = "../asset" }
|
||||||
loading_screen = { path = "../loading-screen" }
|
loading_screen = { path = "../loading-screen" }
|
||||||
context = { path = "../context", features = ["bundle_sdl2", "sound"] }
|
context = { path = "../context", features = ["bundle_sdl2", "sound"] }
|
||||||
ecs = { path = "../ecs" }
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
timings = []
|
timings = []
|
||||||
|
|
|
@ -260,7 +260,7 @@ impl Engine {
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
gui_handler.process_callbacks()?;
|
gui_handler.process_callbacks(world)?;
|
||||||
|
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
|
@ -183,7 +183,7 @@ impl Engine {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Keycode::Escape => {
|
Keycode::Escape => {
|
||||||
if gui_handler.decline_topgui()? {
|
if gui_handler.decline_topgui(world)? {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -262,31 +262,31 @@ impl Engine {
|
||||||
}
|
}
|
||||||
|
|
||||||
ControllerButton::B => {
|
ControllerButton::B => {
|
||||||
if gui_handler.decline_topgui()? {
|
if gui_handler.decline_topgui(world)? {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ControllerButton::RightButton => {
|
ControllerButton::RightButton => {
|
||||||
if gui_handler.next_tab_topgui(false)? {
|
if gui_handler.next_tab_topgui(world, false)? {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ControllerButton::LeftButton => {
|
ControllerButton::LeftButton => {
|
||||||
if gui_handler.previous_tab_topgui(false)? {
|
if gui_handler.previous_tab_topgui(world, false)? {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ControllerButton::RightTrigger => {
|
ControllerButton::RightTrigger => {
|
||||||
if gui_handler.next_tab_topgui(true)? {
|
if gui_handler.next_tab_topgui(world, true)? {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ControllerButton::LeftTrigger => {
|
ControllerButton::LeftTrigger => {
|
||||||
if gui_handler.previous_tab_topgui(true)? {
|
if gui_handler.previous_tab_topgui(world, true)? {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -330,7 +330,7 @@ impl Engine {
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
if !gui_handler.accept_custom_selection(button)? {
|
if !gui_handler.accept_custom_selection(world, button)? {
|
||||||
consumer.event(world, EngineEvent::ControllerButtonDown(controller, button))?;
|
consumer.event(world, EngineEvent::ControllerButtonDown(controller, button))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ edition = "2024"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
plexus = "0.0.11"
|
plexus = "0.0.11"
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
|
ecs.workspace = true
|
||||||
|
|
||||||
ecs = { path = "../../ecs" }
|
|
||||||
engine = { path = "../../engine" }
|
engine = { path = "../../engine" }
|
||||||
skybox = { path = "../../skybox" }
|
skybox = { path = "../../skybox" }
|
||||||
|
|
|
@ -13,7 +13,7 @@ anyhow = { workspace = true }
|
||||||
openxr = { workspace = true, optional = true }
|
openxr = { workspace = true, optional = true }
|
||||||
openvr = { workspace = true, optional = true }
|
openvr = { workspace = true, optional = true }
|
||||||
|
|
||||||
ecs = { path = "../ecs" }
|
ecs = { workspace = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
OpenXR = ["openxr"]
|
OpenXR = ["openxr"]
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "scene_update_macros"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["hodasemi <michaelh.95@t-online.de>"]
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
proc-macro = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
syn = { workspace = true }
|
|
||||||
quote = { workspace = true }
|
|
||||||
proc-macro2 = { workspace = true }
|
|
|
@ -1,117 +0,0 @@
|
||||||
use proc_macro::TokenStream;
|
|
||||||
use proc_macro2::{Span, TokenStream as TokenStream2};
|
|
||||||
use quote::{format_ident, quote};
|
|
||||||
use syn::{
|
|
||||||
parse::{Parse, ParseStream},
|
|
||||||
parse_macro_input,
|
|
||||||
token::Comma,
|
|
||||||
Ident, LitInt, Result,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct InputInfo {
|
|
||||||
macro_ident: Ident,
|
|
||||||
start: usize,
|
|
||||||
end: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Parse for InputInfo {
|
|
||||||
fn parse(input: ParseStream) -> Result<Self> {
|
|
||||||
let macro_ident = input.parse::<Ident>()?;
|
|
||||||
input.parse::<Comma>()?;
|
|
||||||
let start = input.parse::<LitInt>()?.base10_parse()?;
|
|
||||||
input.parse::<Comma>()?;
|
|
||||||
let end = input.parse::<LitInt>()?.base10_parse()?;
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
macro_ident,
|
|
||||||
start,
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct TupleType {
|
|
||||||
little: Ident,
|
|
||||||
big: Ident,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[proc_macro]
|
|
||||||
pub fn implement_pair_update(input: TokenStream) -> TokenStream {
|
|
||||||
let input = parse_macro_input!(input as InputInfo);
|
|
||||||
|
|
||||||
let mut generic_count = Vec::new();
|
|
||||||
|
|
||||||
for lhs in input.start..=input.end {
|
|
||||||
for rhs in input.start..=input.end {
|
|
||||||
generic_count.push((lhs, rhs));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let generic_tuples: Vec<(Vec<TupleType>, Vec<TupleType>)> = generic_count
|
|
||||||
.iter()
|
|
||||||
.map(|(lhs_count, rhs_count)| {
|
|
||||||
let lhs = (input.start..(input.start + lhs_count))
|
|
||||||
.map(|i| TupleType {
|
|
||||||
little: format_ident!("l{}", i),
|
|
||||||
big: format_ident!("L{}", i),
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let rhs = (input.start..(input.start + rhs_count))
|
|
||||||
.map(|i| TupleType {
|
|
||||||
little: format_ident!("r{}", i),
|
|
||||||
big: format_ident!("R{}", i),
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
(lhs, rhs)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let invocations: Vec<TokenStream2> = generic_tuples
|
|
||||||
.iter()
|
|
||||||
.map(|(lhs, rhs)| {
|
|
||||||
let lhs_expr = LitInt::new(&format!("{}", lhs.len()), Span::call_site());
|
|
||||||
let rhs_expr = LitInt::new(&format!("{}", rhs.len()), Span::call_site());
|
|
||||||
|
|
||||||
let lhs_args: Vec<TokenStream2> = lhs
|
|
||||||
.iter()
|
|
||||||
.map(|tuple| {
|
|
||||||
let little = &tuple.little;
|
|
||||||
let big = &tuple.big;
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
[#little: #big],
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let rhs_args: Vec<TokenStream2> = rhs
|
|
||||||
.iter()
|
|
||||||
.map(|tuple| {
|
|
||||||
let little = &tuple.little;
|
|
||||||
let big = &tuple.big;
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
[#little: #big],
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let macro_ident = &input.macro_ident;
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
#macro_ident!(
|
|
||||||
#lhs_expr, (#(#lhs_args)*),
|
|
||||||
#rhs_expr, (#(#rhs_args)*)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
TokenStream::from(quote! {
|
|
||||||
#(
|
|
||||||
#invocations
|
|
||||||
)*
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -8,7 +8,7 @@ edition = "2024"
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
plexus.workspace = true
|
plexus.workspace = true
|
||||||
utilities.workspace = true
|
utilities.workspace = true
|
||||||
|
ecs.workspace = true
|
||||||
|
|
||||||
ecs = { path = "../ecs" }
|
|
||||||
context = { path = "../context" }
|
context = { path = "../context" }
|
||||||
engine = { path = "../engine" }
|
engine = { path = "../engine" }
|
||||||
|
|
Loading…
Reference in a new issue