From b7189c1eff9ca598c8a6bd614231256381252aaa Mon Sep 17 00:00:00 2001 From: Marli Frost Date: Tue, 8 Feb 2022 00:09:28 +0000 Subject: [PATCH 1/4] update hecs dependency to upstream --- feather/ecs/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/feather/ecs/Cargo.toml b/feather/ecs/Cargo.toml index 976652287..c089cc8eb 100644 --- a/feather/ecs/Cargo.toml +++ b/feather/ecs/Cargo.toml @@ -7,7 +7,7 @@ edition = "2018" [dependencies] ahash = "0.7" anyhow = "1" -hecs = { git = "https://github.com/feather-rs/feather-hecs" } +hecs = "0.7.0" log = "0.4" thiserror = "1" utils = { path = "../utils", package = "feather-utils" } From bd30e34b58d10ca96ac6ed8e7edf437f525cecf9 Mon Sep 17 00:00:00 2001 From: Marli Frost Date: Tue, 8 Feb 2022 00:13:51 +0000 Subject: [PATCH 2/4] update references to old hecs APIs --- feather/ecs/src/lib.rs | 7 +------ feather/plugin-host/src/host_calls/component.rs | 4 ++-- feather/plugin-host/src/host_calls/entity.rs | 12 +++++++++--- .../plugin-host/src/host_calls/entity_builder.rs | 2 +- feather/plugin-host/src/host_calls/event.rs | 2 +- .../plugin-host/src/host_calls/plugin_message.rs | 3 ++- feather/plugin-host/src/host_calls/query.rs | 15 +++++---------- 7 files changed, 21 insertions(+), 24 deletions(-) diff --git a/feather/ecs/src/lib.rs b/feather/ecs/src/lib.rs index 77fd48c2d..ad5aa2b18 100644 --- a/feather/ecs/src/lib.rs +++ b/feather/ecs/src/lib.rs @@ -16,7 +16,7 @@ use hecs::{Component, DynamicBundle, Fetch, Query, World}; #[doc(inline)] pub use hecs::{ - BuiltEntity, ComponentError, DynamicQuery, DynamicQueryTypes, Entity, EntityBuilder, + BuiltEntity, ComponentError, Entity, EntityBuilder, MissingComponent, NoSuchEntity, QueryBorrow, Ref, RefMut, }; @@ -151,11 +151,6 @@ impl Ecs { self.world.query() } - /// Performs a dynamic query. Used for plugins. - pub fn query_dynamic<'q>(&'q self, types: DynamicQueryTypes<'q>) -> DynamicQuery<'q> { - self.world.query_dynamic(types) - } - /// Sets the index of the currently executing system, /// used for event tracking. pub fn set_current_system_index(&mut self, index: usize) { diff --git a/feather/plugin-host/src/host_calls/component.rs b/feather/plugin-host/src/host_calls/component.rs index b9722255d..2ecc279b1 100644 --- a/feather/plugin-host/src/host_calls/component.rs +++ b/feather/plugin-host/src/host_calls/component.rs @@ -33,7 +33,7 @@ pub fn entity_get_component( bytes_len_ptr: PluginPtrMut, ) -> anyhow::Result<()> { let component = HostComponent::from_u32(component).context("invalid component")?; - let entity = Entity::from_bits(entity); + let entity = Entity::from_bits(entity).context("invalid entity")?; let visitor = GetComponentVisitor { cx, entity }; let (bytes_ptr, bytes_len) = component.visit(visitor)?; @@ -87,7 +87,7 @@ pub fn entity_set_component( bytes_ptr: PluginPtr, bytes_len: u32, ) -> anyhow::Result<()> { - let entity = Entity::from_bits(entity); + let entity = Entity::from_bits(entity).context("invalid entity")?; let component = HostComponent::from_u32(component).context("invalid component")?; let visitor = InsertComponentVisitor { cx, diff --git a/feather/plugin-host/src/host_calls/entity.rs b/feather/plugin-host/src/host_calls/entity.rs index 0224f6a0c..e5fad35b0 100644 --- a/feather/plugin-host/src/host_calls/entity.rs +++ b/feather/plugin-host/src/host_calls/entity.rs @@ -1,3 +1,4 @@ +use anyhow::Context; use feather_base::Text; use feather_common::chat::{ChatKind, ChatMessage}; use feather_ecs::Entity; @@ -7,7 +8,12 @@ use crate::context::{PluginContext, PluginPtr}; #[host_function] pub fn entity_exists(cx: &PluginContext, entity: u64) -> anyhow::Result { - Ok(cx.game_mut().ecs.entity(Entity::from_bits(entity)).is_ok()).map(|b| b as u32) + Ok(cx + .game_mut() + .ecs + .entity(Entity::from_bits(entity).context("invalid entity")?) + .is_ok()) + .map(|b| b as u32) } #[host_function] @@ -18,7 +24,7 @@ pub fn entity_send_message( message_len: u32, ) -> anyhow::Result<()> { let message = cx.read_json(message_ptr, message_len)?; - let entity = Entity::from_bits(entity); + let entity = Entity::from_bits(entity).context("invalid entity")?; let _ = cx .game_mut() .send_message(entity, ChatMessage::new(ChatKind::System, message)); @@ -33,7 +39,7 @@ pub fn entity_send_title( title_len: u32, ) -> anyhow::Result<()> { let title = cx.read_json(title_ptr, title_len)?; - let entity = Entity::from_bits(entity); + let entity = Entity::from_bits(entity).context("invalid entity")?; cx.game_mut().send_title(entity, title); Ok(()) } diff --git a/feather/plugin-host/src/host_calls/entity_builder.rs b/feather/plugin-host/src/host_calls/entity_builder.rs index 3274ec0fe..0223fc010 100644 --- a/feather/plugin-host/src/host_calls/entity_builder.rs +++ b/feather/plugin-host/src/host_calls/entity_builder.rs @@ -85,5 +85,5 @@ pub fn entity_builder_finish(cx: &PluginContext, builder: u32) -> anyhow::Result .context("invalid entity builder")?; let entity = cx.game_mut().spawn_entity(builder); - Ok(entity.to_bits()) + Ok(entity.to_bits().get()) } diff --git a/feather/plugin-host/src/host_calls/event.rs b/feather/plugin-host/src/host_calls/event.rs index 649ffce65..afbbd5d07 100644 --- a/feather/plugin-host/src/host_calls/event.rs +++ b/feather/plugin-host/src/host_calls/event.rs @@ -13,7 +13,7 @@ pub fn entity_add_event( bytes_ptr: PluginPtr, bytes_len: u32, ) -> anyhow::Result<()> { - let entity = Entity::from_bits(entity); + let entity = Entity::from_bits(entity).context("invalid entity")?; let event = HostComponent::from_u32(event).context("invalid component")?; let visitor = InsertComponentVisitor { cx, diff --git a/feather/plugin-host/src/host_calls/plugin_message.rs b/feather/plugin-host/src/host_calls/plugin_message.rs index 55108733c..77f7b4cd4 100644 --- a/feather/plugin-host/src/host_calls/plugin_message.rs +++ b/feather/plugin-host/src/host_calls/plugin_message.rs @@ -1,3 +1,4 @@ +use anyhow::Context; use feather_common::events::PluginMessageEvent; use feather_ecs::Entity; use feather_plugin_host_macros::host_function; @@ -16,7 +17,7 @@ pub fn plugin_message_send( let channel = cx.read_string(channel_ptr, channel_len)?; let data = cx.read_bytes(data_ptr, data_len)?; - let entity = Entity::from_bits(entity); + let entity = Entity::from_bits(entity).context("invalid entity")?; let event = PluginMessageEvent { channel, data }; cx.game_mut().ecs.insert_entity_event(entity, event)?; diff --git a/feather/plugin-host/src/host_calls/query.rs b/feather/plugin-host/src/host_calls/query.rs index f52a85f4b..656e09481 100644 --- a/feather/plugin-host/src/host_calls/query.rs +++ b/feather/plugin-host/src/host_calls/query.rs @@ -3,7 +3,7 @@ use std::{alloc::Layout, any::TypeId, mem::size_of, ptr}; use anyhow::Context; -use feather_ecs::{DynamicQuery, DynamicQueryTypes, Ecs}; +use feather_ecs::{Ecs}; use feather_plugin_host_macros::host_function; use quill_common::{ component::{ComponentVisitor, SerializationMethod}, @@ -44,14 +44,13 @@ struct WrittenComponentData { /// `ComponentVisitor` implementation used to write /// component data to plugin memory. struct WriteComponentsVisitor<'a> { - query: &'a DynamicQuery<'a>, cx: &'a PluginContext, num_entities: usize, } impl<'a> ComponentVisitor> for WriteComponentsVisitor<'a> { fn visit(self) -> anyhow::Result { - let components = self.query.iter_component_slices(TypeId::of::()); + let components = todo!(); // Write each component. // We use a different strategy depending @@ -109,10 +108,7 @@ fn create_query_data( ecs: &Ecs, types: &[HostComponent], ) -> anyhow::Result { - let query_types: Vec = types.iter().copied().map(HostComponent::type_id).collect(); - let query = ecs.query_dynamic(DynamicQueryTypes::new(&query_types, &[])); - - let num_entities = query.iter_entities().count(); + let num_entities = todo!(); if num_entities == 0 { return Ok(QueryData { num_entities: 0, @@ -126,7 +122,6 @@ fn create_query_data( let component_lens = cx.bump_allocate(Layout::array::(types.len())?)?; for (i, &typ) in types.iter().enumerate() { let data = typ.visit(WriteComponentsVisitor { - query: &query, cx, num_entities, })?; @@ -138,8 +133,8 @@ fn create_query_data( } let entities_ptr = cx.bump_allocate(Layout::array::(num_entities)?)?; - for (i, entity) in query.iter_entities().enumerate() { - let bits = entity.to_bits(); + for (i, entity) in todo!().enumerate() { + let bits = entity.to_bits().get(); unsafe { cx.write_pod(entities_ptr.cast().add(i), bits)?; } From bd57f95338e01dd6883f5f2a01773ea3213c74de Mon Sep 17 00:00:00 2001 From: Marli Frost Date: Tue, 8 Feb 2022 00:32:39 +0000 Subject: [PATCH 3/4] reimplement dynamic archetype queries --- feather/ecs/src/lib.rs | 17 ++++++++- feather/plugin-host/src/host_calls/query.rs | 41 ++++++++++++++++++--- 2 files changed, 50 insertions(+), 8 deletions(-) diff --git a/feather/ecs/src/lib.rs b/feather/ecs/src/lib.rs index ad5aa2b18..7dd637f91 100644 --- a/feather/ecs/src/lib.rs +++ b/feather/ecs/src/lib.rs @@ -16,8 +16,8 @@ use hecs::{Component, DynamicBundle, Fetch, Query, World}; #[doc(inline)] pub use hecs::{ - BuiltEntity, ComponentError, Entity, EntityBuilder, - MissingComponent, NoSuchEntity, QueryBorrow, Ref, RefMut, + Archetype, BuiltEntity, ComponentError, Entity, EntityBuilder, MissingComponent, NoSuchEntity, + QueryBorrow, Ref, RefMut, }; mod system; @@ -151,6 +151,19 @@ impl Ecs { self.world.query() } + pub fn archetypes(&self) -> impl Iterator { + self.world.archetypes() + } + + /// + /// # Safety + /// + /// `id` must correspond to a currently live [`Entity`]. + /// A despawned or never-allocated `id` will produce undefined behavior. + pub unsafe fn find_entity_from_id(&self, id: u32) -> Entity { + self.world.find_entity_from_id(id) + } + /// Sets the index of the currently executing system, /// used for event tracking. pub fn set_current_system_index(&mut self, index: usize) { diff --git a/feather/plugin-host/src/host_calls/query.rs b/feather/plugin-host/src/host_calls/query.rs index 656e09481..94ca598a2 100644 --- a/feather/plugin-host/src/host_calls/query.rs +++ b/feather/plugin-host/src/host_calls/query.rs @@ -3,7 +3,7 @@ use std::{alloc::Layout, any::TypeId, mem::size_of, ptr}; use anyhow::Context; -use feather_ecs::{Ecs}; +use feather_ecs::{Archetype, Ecs}; use feather_plugin_host_macros::host_function; use quill_common::{ component::{ComponentVisitor, SerializationMethod}, @@ -44,13 +44,16 @@ struct WrittenComponentData { /// `ComponentVisitor` implementation used to write /// component data to plugin memory. struct WriteComponentsVisitor<'a> { + ecs: &'a Ecs, + types: &'a [HostComponent], cx: &'a PluginContext, num_entities: usize, } impl<'a> ComponentVisitor> for WriteComponentsVisitor<'a> { fn visit(self) -> anyhow::Result { - let components = todo!(); + let components = matching_archetypes(self.ecs, self.types) + .map(|archetype| archetype.get::().unwrap()); // Write each component. // We use a different strategy depending @@ -65,7 +68,7 @@ impl<'a> ComponentVisitor> for WriteCompone // Copy the components into the buffer. let mut byte_index = 0; for component_slice in components { - for component in component_slice.as_slice::() { + for component in component_slice.iter() { let bytes = component.as_bytes(); unsafe { @@ -86,7 +89,7 @@ impl<'a> ComponentVisitor> for WriteCompone // Write components into the buffer. for component_slice in components { - for component in component_slice.as_slice::() { + for component in component_slice.iter() { component.to_bytes(&mut bytes); } } @@ -103,12 +106,28 @@ impl<'a> ComponentVisitor> for WriteCompone } } +fn matching_archetypes<'a>( + ecs: &'a Ecs, + types: &'a [HostComponent], +) -> impl Iterator + 'a { + struct Has<'a>(&'a Archetype); + impl ComponentVisitor for Has<'_> { + fn visit(self) -> bool { + self.0.has::() + } + } + ecs.archetypes() + .filter(move |archetype| types.iter().all(|t| t.visit(Has(archetype)))) +} + fn create_query_data( cx: &PluginContext, ecs: &Ecs, types: &[HostComponent], ) -> anyhow::Result { - let num_entities = todo!(); + let num_entities = matching_archetypes(ecs, types) + .map(|archetype| archetype.ids().len()) + .sum(); if num_entities == 0 { return Ok(QueryData { num_entities: 0, @@ -122,6 +141,8 @@ fn create_query_data( let component_lens = cx.bump_allocate(Layout::array::(types.len())?)?; for (i, &typ) in types.iter().enumerate() { let data = typ.visit(WriteComponentsVisitor { + ecs, + types, cx, num_entities, })?; @@ -133,7 +154,15 @@ fn create_query_data( } let entities_ptr = cx.bump_allocate(Layout::array::(num_entities)?)?; - for (i, entity) in todo!().enumerate() { + for (i, entity) in matching_archetypes(ecs, types) + .flat_map(|archetype| { + archetype + .ids() + .iter() + .map(|id| unsafe { ecs.find_entity_from_id(*id) }) + }) + .enumerate() + { let bits = entity.to_bits().get(); unsafe { cx.write_pod(entities_ptr.cast().add(i), bits)?; From 1e23ff62d5ea7d768e372cfd9fec40b589c57ccd Mon Sep 17 00:00:00 2001 From: Marli Frost Date: Tue, 8 Feb 2022 00:32:43 +0000 Subject: [PATCH 4/4] update lockfile --- Cargo.lock | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0b4381092..c9c06c0f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1158,15 +1158,6 @@ version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" -[[package]] -name = "hashbrown" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" -dependencies = [ - "ahash 0.4.7", -] - [[package]] name = "hashbrown" version = "0.11.2" @@ -1193,10 +1184,12 @@ checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" [[package]] name = "hecs" -version = "0.3.2" -source = "git+https://github.com/feather-rs/feather-hecs#824712c4e4ab658e75fabf2a91a54f9d1c0b1790" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eafd3028a128921d1c4bf12998217d0f450b6e7492e7606fd7b247a1e464a317" dependencies = [ - "hashbrown 0.9.1", + "hashbrown", + "spin 0.9.2", ] [[package]] @@ -1243,7 +1236,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223" dependencies = [ "autocfg 1.0.1", - "hashbrown 0.11.2", + "hashbrown", "serde", ] @@ -2343,7 +2336,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49a37de5dfc60bae2d94961dacd03c7b80e426b66a99fa1b17799570dbdd8f96" dependencies = [ "bytecheck", - "hashbrown 0.11.2", + "hashbrown", "ptr_meta", "rend", "rkyv_derive",