diff --git a/guest/rust/api_core/api_macros/src/main_macro.rs b/guest/rust/api_core/api_macros/src/main_macro.rs index 84d3f15208..96754e5a14 100644 --- a/guest/rust/api_core/api_macros/src/main_macro.rs +++ b/guest/rust/api_core/api_macros/src/main_macro.rs @@ -33,9 +33,9 @@ pub fn main(item: TokenStream, ambient_toml: RetrievableFile) -> TokenStream { let call_stmt = if let Some(ParsedFunction { fn_name, is_async }) = parsed { let call_expr = if is_async { - quote! { #fn_name() } + quote! { #fn_name(world) } } else { - quote! { async { #fn_name() } } + quote! { async { #fn_name(world) } } }; quote! { #path::global::run_async(#call_expr) } @@ -50,7 +50,7 @@ pub fn main(item: TokenStream, ambient_toml: RetrievableFile) -> TokenStream { #[no_mangle] #[doc(hidden)] - pub fn main() { + pub fn main(world: &mut dyn #path::ecs::World) { #call_stmt } } diff --git a/guest/rust/api_core/src/animation.rs b/guest/rust/api_core/src/animation.rs index 1ebe316d95..2d2ce80c13 100644 --- a/guest/rust/api_core/src/animation.rs +++ b/guest/rust/api_core/src/animation.rs @@ -8,8 +8,8 @@ use crate::{ app::components::{name, ref_count}, ecs::components::{children, parent}, }, - entity, - prelude::{epoch_time, Entity, EntityId}, + ecs, + prelude::{epoch_time, Entity, EntityId, World}, }; /// This plays animations, and can handle blending and masking of animations together to create @@ -18,40 +18,40 @@ use crate::{ pub struct AnimationPlayer(pub EntityId); impl AnimationPlayer { /// Create a new animation player, with `root` as the currently playing node - pub fn new(root: impl AsRef) -> Self { + pub fn new(world: &mut World, root: impl AsRef) -> Self { let root: &AnimationNode = root.as_ref(); let player = Entity::new() .with(is_animation_player(), ()) .with(children(), vec![root.0]) .with(name(), "Animation player".to_string()) - .spawn(); - entity::add_component(root.0, parent(), player); + .spawn(world); + world.add_component(root.0, parent(), player); Self(player) } - fn root(&self) -> Option { - if let Some(children) = entity::get_component(self.0, children()) { + fn root(&self, world: &World) -> Option { + if let Ok(children) = world.get_component(self.0, children()) { children.get(0).copied() } else { None } } - fn free_root(&self) { - if let Some(root) = self.root() { - entity::remove_component(root, parent()); + fn free_root(&self, world: &mut World) { + if let Some(root) = self.root(world) { + world.remove_component(root, parent()); } } /// Replaces the current root node of the animation player with a new node - pub fn play(&self, node: impl AsRef) { - self.free_root(); + pub fn play(&self, world: &mut World, node: impl AsRef) { + self.free_root(world); let new_root: &AnimationNode = node.as_ref(); - entity::add_component(self.0, children(), vec![new_root.0]); - entity::add_component(new_root.0, parent(), self.0); + world.add_component(self.0, children(), vec![new_root.0]); + world.add_component(new_root.0, parent(), self.0); } /// Despawn this animation player. /// Note that dropping this player won't despawn it automatically; only call this method will despawn it. - pub fn despawn(self) { - self.free_root(); - entity::despawn(self.0); + pub fn despawn(self, world: &mut World) { + self.free_root(world); + world.despawn(self.0); } } @@ -64,22 +64,22 @@ impl AnimationNode { self.0 } /// Use an existing node - pub fn from_entity(entity: EntityId) -> Self { - entity::mutate_component(entity, ref_count(), |x| *x += 1); + pub fn from_entity(world: &mut World, entity: EntityId) -> Self { + world.mutate_component(entity, ref_count(), |x| *x += 1); Self(entity) } } -impl Clone for AnimationNode { - fn clone(&self) -> Self { - entity::mutate_component(self.0, ref_count(), |x| *x += 1); - Self(self.0) - } -} -impl Drop for AnimationNode { - fn drop(&mut self) { - entity::mutate_component(self.0, ref_count(), |x| *x -= 1); - } -} +// impl Clone for AnimationNode { +// fn clone(&self) -> Self { +// world.mutate_component(self.0, ref_count(), |x| *x += 1); +// Self(self.0) +// } +// } +// impl Drop for AnimationNode { +// fn drop(&mut self) { +// world.mutate_component(self.0, ref_count(), |x| *x -= 1); +// } +// } /// Play clip from url animation node. /// This is an animation node which can be plugged into an animation player or other animation nodes. @@ -87,48 +87,48 @@ impl Drop for AnimationNode { pub struct PlayClipFromUrlNode(pub AnimationNode); impl PlayClipFromUrlNode { /// Create a new node. - pub fn new(url: impl Into) -> Self { + pub fn new(world: &mut World, url: impl Into) -> Self { let node = Entity::new() .with(play_clip_from_url(), url.into()) .with(name(), "Play clip from URL".to_string()) .with(looping(), true) - .with(start_time(), epoch_time()) + .with(start_time(), epoch_time(world)) .with(ref_count(), 1) - .spawn(); + .spawn(world); Self(AnimationNode(node)) } /// Use an existing node - pub fn from_entity(entity: EntityId) -> Self { - Self(AnimationNode::from_entity(entity)) + pub fn from_entity(world: &mut World, entity: EntityId) -> Self { + Self(AnimationNode::from_entity(world, entity)) } /// Set if the animation should loop or not - pub fn looping(&self, value: bool) { - entity::add_component(self.0 .0, looping(), value); + pub fn looping(&self, world: &mut World, value: bool) { + world.add_component(self.0 .0, looping(), value); } /// Freeze the animation at time - pub fn freeze_at_time(&self, time: f32) { - entity::add_component(self.0 .0, freeze_at_time(), time); + pub fn freeze_at_time(&self, world: &mut World, time: f32) { + world.add_component(self.0 .0, freeze_at_time(), time); } /// Freeze the animation at time = percentage * duration - pub fn freeze_at_percentage(&self, percentage: f32) { - entity::add_component(self.0 .0, freeze_at_percentage(), percentage); + pub fn freeze_at_percentage(&self, world: &mut World, percentage: f32) { + world.add_component(self.0 .0, freeze_at_percentage(), percentage); } /// Set up retargeting - pub fn set_retargeting(&self, retargeting: AnimationRetargeting) { + pub fn set_retargeting(&self, world: &mut World, retargeting: AnimationRetargeting) { match retargeting { AnimationRetargeting::None => { - entity::remove_component(self.0 .0, retarget_model_from_url()); + world.remove_component(self.0 .0, retarget_model_from_url()); } AnimationRetargeting::Skeleton { model_url } => { - entity::remove_component(self.0 .0, retarget_animation_scaled()); - entity::add_component(self.0 .0, retarget_model_from_url(), model_url); + world.remove_component(self.0 .0, retarget_animation_scaled()); + world.add_component(self.0 .0, retarget_model_from_url(), model_url); } AnimationRetargeting::AnimationScaled { normalize_hip, model_url, } => { - entity::add_component(self.0 .0, retarget_animation_scaled(), normalize_hip); - entity::add_component(self.0 .0, retarget_model_from_url(), model_url); + world.add_component(self.0 .0, retarget_animation_scaled(), normalize_hip); + world.add_component(self.0 .0, retarget_model_from_url(), model_url); } } } @@ -142,36 +142,38 @@ impl PlayClipFromUrlNode { /// character models which don't have the same pre-rotations we need to make sure they're up to sync /// /// I.e. this is mostly relevant for retargeting - pub fn apply_base_pose(&self, value: bool) { + pub fn apply_base_pose(&self, world: &mut World, value: bool) { if value { - entity::add_component(self.0 .0, apply_base_pose(), ()); + world.add_component(self.0 .0, apply_base_pose(), ()); } else { - entity::remove_component(self.0 .0, apply_base_pose()); + world.remove_component(self.0 .0, apply_base_pose()); } } /// Returns None if the duration hasn't been loaded yet - pub fn peek_clip_duration(&self) -> Option { - entity::get_component(self.0 .0, clip_duration()) + pub fn peek_clip_duration(&self, world: &World) -> ecs::Result { + world.get_component(self.0 .0, clip_duration()) } /// Returns the duration of this clip. This is async because it needs to wait for the clip to load before the duration can be returned. - pub async fn clip_duration(&self) -> f32 { - entity::wait_for_component(self.0 .0, clip_duration()) + pub async fn clip_duration(&self, world: &World) -> f32 { + world + .wait_for_component(self.0 .0, clip_duration()) .await .unwrap_or_default() } /// Returns None if the clip hasn't been loaded yet - pub fn peek_bind_ids(&self) -> Option> { - entity::get_component(self.0 .0, bind_ids()) + pub fn peek_bind_ids(&self, world: &World) -> ecs::Result> { + world.get_component(self.0 .0, bind_ids()) } /// Returns the bind ids of this clip. This is async because it needs to wait for the clip to load before the bind ids can be returned. - pub async fn bind_ids(&self) -> Vec { - entity::wait_for_component(self.0 .0, bind_ids()) + pub async fn bind_ids(&self, world: &World) -> Vec { + world + .wait_for_component(self.0 .0, bind_ids()) .await .unwrap_or_default() } /// Wait until the clip has been loaded - pub async fn wait_for_load(&self) { - self.clip_duration().await; + pub async fn wait_for_load(&self, world: &World) { + self.clip_duration(world).await; } } impl AsRef for PlayClipFromUrlNode { @@ -182,7 +184,7 @@ impl AsRef for PlayClipFromUrlNode { /// Blend animation node. /// This is an animation node which can be plugged into an animation player or other animation nodes. -#[derive(Debug, Clone)] +#[derive(Debug /*, Clone*/)] pub struct BlendNode(pub AnimationNode); impl BlendNode { /// Create a new blend animation node. @@ -191,6 +193,7 @@ impl BlendNode { /// If the weight is 1, only the right animation will play. /// Values in between blend between the two animations. pub fn new( + world: &mut World, left: impl AsRef, right: impl AsRef, weight: f32, @@ -202,38 +205,39 @@ impl BlendNode { .with(name(), "Blend".to_string()) .with(children(), vec![left.0, right.0]) .with(ref_count(), 1) - .spawn(); - entity::add_component(left.0, parent(), node); - entity::add_component(right.0, parent(), node); + .spawn(world); + world.add_component(left.0, parent(), node); + world.add_component(right.0, parent(), node); Self(AnimationNode(node)) } /// Use an existing node - pub fn from_entity(entity: EntityId) -> Self { - Self(AnimationNode::from_entity(entity)) + pub fn from_entity(world: &mut World, entity: EntityId) -> Self { + Self(AnimationNode::from_entity(world, entity)) } /// Set the weight of this blend node. /// /// If the weight is 0, only the left animation will play. /// If the weight is 1, only the right animation will play. /// Values in between blend between the two animations. - pub fn set_weight(&self, weight: f32) { - entity::set_component(self.0 .0, blend(), weight); + pub fn set_weight(&self, world: &mut World, weight: f32) { + world.set_component(self.0 .0, blend(), weight); } /// Sets the mask of this blend node. /// /// For example `blend_node.set_mask(vec![("LeftLeg".to_string(), 1.)])` means /// that the LeftLeg is always controlled by the right animation. - pub fn set_mask(&self, weights: Vec<(BindId, f32)>) { + pub fn set_mask(&self, world: &mut World, weights: Vec<(BindId, f32)>) { let (bind_ids, weights): (Vec<_>, Vec<_>) = weights .into_iter() .map(|(a, b)| (a.as_str().to_string(), b)) .unzip(); - entity::add_component(self.0 .0, mask_bind_ids(), bind_ids); - entity::add_component(self.0 .0, mask_weights(), weights); + world.add_component(self.0 .0, mask_bind_ids(), bind_ids); + world.add_component(self.0 .0, mask_weights(), weights); } /// Sets a mask value to all bones of a humanoids lower body - pub fn set_mask_humanoid_lower_body(&self, weight: f32) { + pub fn set_mask_humanoid_lower_body(&self, world: &mut World, weight: f32) { self.set_mask( + world, BindId::HUMANOID_LOWER_BODY .iter() .map(|x| ((*x).clone(), weight)) @@ -241,8 +245,9 @@ impl BlendNode { ); } /// Sets a mask value to all bones of a humanoids upper body - pub fn set_mask_humanoid_upper_body(&self, weight: f32) { + pub fn set_mask_humanoid_upper_body(&self, world: &mut World, weight: f32) { self.set_mask( + world, BindId::HUMANOID_UPPER_BODY .iter() .map(|x| ((*x).clone(), weight)) @@ -283,15 +288,15 @@ impl Default for AnimationRetargeting { } /// Get the bone entity from the bind_id; for example "LeftFoot" -pub fn get_bone_by_bind_id(entity: EntityId, bind_id: &BindId) -> Option { - if let Some(bid) = entity::get_component(entity, self::bind_id()) { +pub fn get_bone_by_bind_id(world: &World, entity: EntityId, bind_id: &BindId) -> Option { + if let Ok(bid) = world.get_component(entity, self::bind_id()) { if bid == bind_id.as_str() { return Some(entity); } } - if let Some(childs) = entity::get_component(entity, children()) { + if let Ok(childs) = world.get_component(entity, children()) { for c in childs { - if let Some(bid) = get_bone_by_bind_id(c, bind_id) { + if let Some(bid) = get_bone_by_bind_id(world, c, bind_id) { return Some(bid); } } diff --git a/guest/rust/api_core/src/client/audio.rs b/guest/rust/api_core/src/client/audio.rs index 5c48a1a74a..20d95a4c49 100644 --- a/guest/rust/api_core/src/client/audio.rs +++ b/guest/rust/api_core/src/client/audio.rs @@ -4,14 +4,13 @@ use crate::{ audio::components::*, ecs::components::{children, parent}, }, - entity, - prelude::{Entity, EntityId}, + prelude::{Entity, EntityId, World}, }; /// stop the audio on the given entity -pub fn stop(entity: EntityId) { - if entity::exists(entity) { - entity::add_component(entity, stop_now(), ()); +pub fn stop(world: &mut World, entity: EntityId) { + if world.exists(entity) { + world.add_component(entity, stop_now(), ()); } else { eprintln!("Tried to stop audio on non-existent entity {}", entity); } @@ -23,42 +22,35 @@ pub struct SpatialAudioPlayer { /// the entity that represents the spatial audio player pub player: EntityId, } - -impl Default for SpatialAudioPlayer { - fn default() -> Self { - Self::new() - } -} - impl SpatialAudioPlayer { - pub fn new() -> Self { + pub fn new(world: &mut World) -> Self { let player = Entity::new() .with(is_spatial_audio_player(), ()) .with(name(), "Spatial audio player".to_string()) - .spawn(); + .spawn(world); Self { player } } - pub fn set_listener(&self, listener: EntityId) { - entity::add_component(self.player, spatial_audio_listener(), listener); + pub fn set_listener(&self, world: &mut World, listener: EntityId) { + world.add_component(self.player, spatial_audio_listener(), listener); } - pub fn set_emitter(&self, emitter: EntityId) { - entity::add_component(self.player, spatial_audio_emitter(), emitter); + pub fn set_emitter(&self, world: &mut World, emitter: EntityId) { + world.add_component(self.player, spatial_audio_emitter(), emitter); } - pub fn set_amplitude(&self, amp: f32) { - entity::add_component(self.player, amplitude(), amp); + pub fn set_amplitude(&self, world: &mut World, amp: f32) { + world.add_component(self.player, amplitude(), amp); } - pub fn set_looping(&self, val: bool) { - entity::add_component(self.player, looping(), val); + pub fn set_looping(&self, world: &mut World, val: bool) { + world.add_component(self.player, looping(), val); } - pub fn play_sound_on_entity(&self, url: String, emitter: EntityId) { - entity::add_component(self.player, spatial_audio_emitter(), emitter); - entity::add_component(self.player, audio_url(), url); - entity::add_component(self.player, play_now(), ()); + pub fn play_sound_on_entity(&self, world: &mut World, url: String, emitter: EntityId) { + world.add_component(self.player, spatial_audio_emitter(), emitter); + world.add_component(self.player, audio_url(), url); + world.add_component(self.player, play_now(), ()); } } @@ -68,51 +60,44 @@ pub struct AudioPlayer { /// The entity that represents the audio player pub entity: EntityId, } - -impl Default for AudioPlayer { - fn default() -> Self { - Self::new() - } -} - impl AudioPlayer { /// Create new audio player from URL - pub fn new() -> Self { + pub fn new(world: &mut World) -> Self { let player = Entity::new() .with(is_audio_player(), ()) .with(name(), "Audio player".to_string()) .with(children(), vec![]) - .spawn(); + .spawn(world); Self { entity: player } } /// Set the sound looping or not - pub fn set_looping(&self, val: bool) { - entity::add_component(self.entity, looping(), val); + pub fn set_looping(&self, world: &mut World, val: bool) { + world.add_component(self.entity, looping(), val); } /// Add a simple onepole lowpass filter to the sound with one param: roll off frequency - pub fn add_one_pole_lpf(&self, rolloff_freq: f32) { - entity::add_component(self.entity, onepole_lpf(), rolloff_freq); + pub fn add_one_pole_lpf(&self, world: &mut World, rolloff_freq: f32) { + world.add_component(self.entity, onepole_lpf(), rolloff_freq); } /// Set the amp/volume of the sound 0.0 is 0%, 1.0 is 100% - pub fn set_amplitude(&self, amp: f32) { - entity::add_component(self.entity, amplitude(), amp); + pub fn set_amplitude(&self, world: &mut World, amp: f32) { + world.add_component(self.entity, amplitude(), amp); } /// Set the panning of the sound -1.0 is 100% left, 1.0 is 100% right. - pub fn set_panning(&self, pan: f32) { - entity::add_component(self.entity, panning(), pan); + pub fn set_panning(&self, world: &mut World, pan: f32) { + world.add_component(self.entity, panning(), pan); } /// Play the sound, this will generate a new entity that represents the playing sound. - pub fn play(&self, url: String) -> EntityId { - entity::add_component(self.entity, audio_url(), url); - entity::add_component(self.entity, play_now(), ()); + pub fn play(&self, world: &mut World, url: String) -> EntityId { + world.add_component(self.entity, audio_url(), url); + world.add_component(self.entity, play_now(), ()); let id = Entity::new() .with(playing_sound(), ()) .with(name(), "Playing sound".to_string()) .with(parent(), self.entity) - .spawn(); - entity::mutate_component(self.entity, children(), |val| { + .spawn(world); + world.mutate_component(self.entity, children(), |val| { val.push(id); }); id diff --git a/guest/rust/api_core/src/ecs.rs b/guest/rust/api_core/src/ecs.rs index 4528803ed0..170caae3df 100644 --- a/guest/rust/api_core/src/ecs.rs +++ b/guest/rust/api_core/src/ecs.rs @@ -1,3 +1,6 @@ +#![allow(clippy::single_match)] +use glam::{Mat4, Vec3}; + pub use crate::internal::component::{ query::{ change_query, despawn_query, query, spawn_query, ChangeQuery, EventQuery, GeneralQuery, @@ -9,3 +12,368 @@ pub use crate::internal::component::{ #[doc(hidden)] pub use crate::internal::wit::component::Value as WitComponentValue; +use crate::{ + core::ecs::components::{children, parent}, + internal::HostWorld, + prelude::{block_until, EntityId}, +}; + +#[derive(Debug)] +/// An error that can occur when interacting with the ECS. +pub enum ECSError { + /// The entity does not have the component. + EntityDoesntHaveComponent, + /// The entity does not exist. + NoSuchEntity, +} + +/// A result that can occur when interacting with the ECS. +pub type Result = core::result::Result; + +/// An ECS world. +pub enum World { + #[doc(hidden)] + Host(HostWorld), +} + +impl World { + /// Spawns an entity containing the `components`. + /// + /// Returns `spawned_entity_id`. + pub fn spawn(&mut self, components: Entity) -> EntityId { + match self { + World::Host(w) => w.spawn(components), + } + } + + /// Despawns `entity` from the world. `entity` will not work with any other functions afterwards. + /// + /// Returns the data of the despawned entity, if it existed. + pub fn despawn(&mut self, entity: EntityId) -> Option { + match self { + World::Host(w) => w.despawn(entity), + } + } + + /// Gets a list of world transforms relative to origin entity + /// Origin can be null entity for a list of world transforms + pub fn get_transforms_relative_to(&self, list: &[EntityId], origin: EntityId) -> Vec { + match self { + World::Host(w) => w.get_transforms_relative_to(list, origin), + } + } + + /// Checks if the `entity` exists. + pub fn exists(&self, entity: EntityId) -> bool { + match self { + World::Host(w) => w.exists(entity), + } + } + + /// Gets all of the entities that have the given `component`. + #[doc(hidden)] + pub fn get_all_untyped(&self, component: &dyn UntypedComponent) -> Vec { + match self { + World::Host(w) => w.get_all_untyped(component), + } + } + + /// Gets all of the entities within `radius` of `position`. + pub fn in_area(&self, position: Vec3, radius: f32) -> Vec { + match self { + World::Host(w) => w.in_area(position, radius), + } + } + + /// Retrieves the component `component` for `entity` if it exists, or `None` if it doesn't. + #[doc(hidden)] + pub fn get_component_untyped( + &self, + entity: EntityId, + component: &dyn UntypedComponent, + ) -> Result { + match self { + World::Host(w) => w.get_component_untyped(entity, component), + } + } + + /// Retrieves the components `components` for `entity`. Will return an empty `Entity` if no components are found. + pub fn get_components(&self, entity: EntityId, components: &[&dyn UntypedComponent]) -> Entity { + match self { + World::Host(w) => w.get_components(entity, components), + } + } + + /// Retrieves all guest-visible components for `entity`. Will return an empty `Entity` if no components are found. + /// + /// Note that this may not be all of the components on the entity, as some components are not visible to the guest. + pub fn get_all_components(&self, entity: EntityId) -> Entity { + match self { + World::Host(w) => w.get_all_components(entity), + } + } + + /// Adds the component `component` for `entity` with `value`. Will replace an existing component if present. + #[doc(hidden)] + pub fn add_component_untyped( + &mut self, + entity: EntityId, + component: &dyn UntypedComponent, + value: ComponentValue, + ) -> Result<()> { + match self { + World::Host(w) => w.add_component_untyped(entity, component, value), + } + } + + /// Adds the components `components` for `entity` with `value`. Will replace any existing components specified in `components`. + pub fn add_components(&mut self, entity: EntityId, components: Entity) -> Result<()> { + match self { + World::Host(w) => w.add_components(entity, components), + } + } + + /// Sets the component `component` for `entity` with `value`. + #[doc(hidden)] + pub fn set_component_untyped( + &mut self, + entity: EntityId, + component: &dyn UntypedComponent, + value: ComponentValue, + ) -> Result<()> { + match self { + World::Host(w) => w.set_component_untyped(entity, component, value), + } + } + + /// Sets the components `components` for `entity` with `value`. + pub fn set_components(&mut self, entity: EntityId, components: Entity) { + match self { + World::Host(w) => w.set_components(entity, components), + } + } + + /// Checks if the `entity` has a `component`. + #[doc(hidden)] + pub fn has_component_untyped( + &self, + entity: EntityId, + component: &dyn UntypedComponent, + ) -> bool { + match self { + World::Host(w) => w.has_component_untyped(entity, component), + } + } + + /// Checks if the `entity` has `components`. + pub fn has_components(&self, entity: EntityId, components: &[&dyn UntypedComponent]) -> bool { + match self { + World::Host(w) => w.has_components(entity, components), + } + } + + /// Removes the `component` from `entity`. + /// + /// Does nothing if the component does not exist. + #[doc(hidden)] + pub fn remove_component_untyped(&mut self, entity: EntityId, component: &dyn UntypedComponent) { + match self { + World::Host(w) => w.remove_component_untyped(entity, component), + } + } + /// Removes the `components` from `entity`. + /// + /// Does nothing if the component does not exist. + pub fn remove_components(&mut self, entity: EntityId, components: &[&dyn UntypedComponent]) { + match self { + World::Host(w) => w.remove_components(entity, components), + } + } + + /// Gets the resource entity. The components of this entity contain global state for this ECS world. + /// + /// Components with the `Resource` attribute can be found here. + pub fn resources(&self) -> EntityId { + match self { + World::Host(w) => w.resources(), + } + } + + /// Gets the synchronized resource entity. The components of this entity contain global state that should be networked, but not persisted. + pub fn synchronized_resources(&self) -> EntityId { + match self { + World::Host(w) => w.synchronized_resources(), + } + } + + /// Gets the persisted resource entity. The components of this entity contain global state that should be networked and persisted. + pub fn persisted_resources(&self) -> EntityId { + match self { + World::Host(w) => w.persisted_resources(), + } + } +} + +impl World { + /// Gets all of the entities that have the given `component`. + pub fn get_all(&self, component: Component) -> Vec { + self.get_all_untyped(&component) + } + + /// Retrieves the component `component` for `entity` if it exists, or `None` if it doesn't. + pub fn get_component( + &self, + entity: EntityId, + component: Component, + ) -> Result { + self.get_component_untyped(entity, &component) + .map(|x| T::from_value(x).unwrap()) + } + /// Retrieves the component `component` for `entity` if it exists, or `None` if it doesn't. + pub fn get(&self, entity: EntityId, component: Component) -> Result { + self.get_component(entity, component) + } + #[doc(hidden)] + pub fn get_cloned( + &self, + entity: EntityId, + component: Component, + ) -> Result { + self.get_component(entity, component) + } + + /// Retrieves the `resource` if it exists and panics if it doesn't. + pub fn resource(&self, component: Component) -> T { + self.get_component(self.resources(), component).unwrap() + } + + /// Adds the component `component` for `entity` with `value`. Will replace an existing component if present. + pub fn add_component( + &mut self, + entity: EntityId, + component: Component, + value: T, + ) -> Result<()> { + self.add_component_untyped(entity, &component, value.into_value()) + } + + /// Sets the component `component` for `entity` with `value`. + pub fn set_component( + &mut self, + entity: EntityId, + component: Component, + value: T, + ) -> Result<()> { + self.set_component_untyped(entity, &component, value.into_value()) + } + /// Sets the component `component` for `entity` with `value`. + pub fn set( + &mut self, + entity: EntityId, + component: Component, + value: T, + ) -> Result<()> { + self.set_component(entity, component, value) + } + + /// Checks if the `entity` has a `component`. + pub fn has_component( + &self, + entity: EntityId, + component: Component, + ) -> bool { + self.has_component_untyped(entity, &component) + } + + /// Removes the `component` from `entity`. + /// + /// Does nothing if the component does not exist. + pub fn remove_component( + &mut self, + entity: EntityId, + component: Component, + ) { + self.remove_component_untyped(entity, &component) + } + + /// Waits until `id` has the `component`. If the entity was deleted the method returns None. + pub async fn wait_for_component( + &self, + entity: EntityId, + component: Component, + ) -> Result { + block_until(move || !self.exists(entity) || self.has_component(entity, component)).await; + self.get_component(entity, component) + } + + /// Despawns `entity` and all of its children. + pub fn despawn_recursive(&mut self, entity: EntityId) { + if let Some(res) = self.despawn(entity) { + if let Some(children) = res.get_ref(children()) { + for c in children { + self.despawn_recursive(*c); + } + } + } + } + + /// Mutates the component `component` for `entity` using the passed in `mutator`, and returns its value. + /// + /// This will not set the component if the value is the same, which will prevent change events from + /// being unnecessarily fired. + pub fn mutate_component( + &mut self, + entity: EntityId, + component: Component, + mutator: impl FnOnce(&mut T), + ) -> Result { + let mut value: T = self.get_component(entity, component)?; + let orig_value = value.clone(); + mutator(&mut value); + if value != orig_value { + self.set_component(entity, component, value.clone()); + } + Ok(value) + } + + /// Mutates the component `component` for `entity` using the passed in `mutator`, or sets it + /// to `default` if it doesn't exist, and returns its value. + /// + /// This will not set the component if the value is the same, which will prevent change events from + /// being unnecessarily fired. + pub fn mutate_component_with_default( + &mut self, + entity: EntityId, + component: Component, + default: T, + mutator: impl FnOnce(&mut T), + ) -> T { + let value = self.mutate_component(entity, component, mutator); + if let Ok(value) = value { + value + } else { + self.add_component(entity, component, default.clone()); + default + } + } + + /// Adds `child` as a child to `entity`. + pub fn add_child(&mut self, entity: EntityId, child: EntityId) { + if self.has_component(entity, children()) { + self.mutate_component(entity, children(), |children| children.push(child)); + } else { + self.add_component(entity, children(), vec![child]); + } + self.add_component(child, parent(), entity); + } + + /// Removes `child` as a child to `entity`. + pub fn remove_child(&mut self, entity: EntityId, child: EntityId) { + if self.has_component(entity, children()) { + self.mutate_component(entity, children(), |children| { + children.retain(|x| *x != child) + }); + } + self.remove_component(child, parent()); + } +} diff --git a/guest/rust/api_core/src/entity.rs b/guest/rust/api_core/src/entity.rs deleted file mode 100644 index 01678220b3..0000000000 --- a/guest/rust/api_core/src/entity.rs +++ /dev/null @@ -1,245 +0,0 @@ -use crate::{ - core::ecs::components::{children, parent}, - global::{EntityId, Vec3}, - internal::{ - component::{Component, Entity, SupportedValue, UntypedComponent}, - conversion::{FromBindgen, IntoBindgen}, - wit, - }, - prelude::block_until, -}; - -/// Spawns an entity containing the `components`. -/// -/// Returns `spawned_entity_uid`. -pub fn spawn(components: &Entity) -> EntityId { - wit::entity::spawn(&components.clone().into_bindgen()).from_bindgen() -} - -/// Waits until `id` has the `component`. If the entity was deleted the method returns None. -pub async fn wait_for_component( - entity: EntityId, - component: Component, -) -> Option { - block_until(move || !exists(entity) || has_component(entity, component)).await; - get_component(entity, component) -} - -/// Despawns `entity` from the world. `entity` will not work with any other functions afterwards. -/// -/// Returns the data of the despawned entity, if it existed. -pub fn despawn(entity: EntityId) -> Option { - wit::entity::despawn(entity.into_bindgen()).from_bindgen() -} -/// Despawns `entity` and all of its children. -pub fn despawn_recursive(entity: EntityId) { - if let Some(res) = despawn(entity) { - if let Some(children) = res.get_ref(children()) { - for c in children { - despawn_recursive(*c); - } - } - } -} - -/// Unconverted bindgen transforms -pub struct RawTransforms { - transforms: Vec, -} - -impl RawTransforms { - /// Convert transforms into a list of Mat4 - pub fn into_mat4(self) -> Vec { - self.transforms.from_bindgen() - } - - /// Convert transforms into mat4 as an iterator - pub fn iter_mat4(&self) -> impl ExactSizeIterator + '_ { - self.transforms.iter().map(|&x| x.from_bindgen()) - } -} - -/// Gets a list of world transforms relative to origin entity -/// Origin can be null entity for a list of world transforms -pub fn get_transforms_relative_to(list: &[EntityId], origin: EntityId) -> RawTransforms { - let entities: Vec = list.iter().map(|x| x.into_bindgen()).collect(); - RawTransforms { - transforms: wit::entity::get_transforms_relative_to(&entities, origin.into_bindgen()), - } -} - -/// Checks if the `entity` exists. -pub fn exists(entity: EntityId) -> bool { - wit::entity::exists(entity.into_bindgen()) -} - -/// Gets all of the entities that have the given `component`. -pub fn get_all(component: Component) -> Vec { - wit::entity::get_all(component.index()).from_bindgen() -} - -/// Gets all of the entities within `radius` of `position`. -pub fn in_area(position: Vec3, radius: f32) -> Vec { - wit::entity::in_area(position.into_bindgen(), radius).from_bindgen() -} - -/// Retrieves the component `component` for `entity` if it exists, or `None` if it doesn't. -pub fn get_component(entity: EntityId, component: Component) -> Option { - T::from_result(wit::component::get_component( - entity.into_bindgen(), - component.index(), - )?) -} - -/// Retrieves the components `components` for `entity`. Will return an empty `Entity` if no components are found. -pub fn get_components(entity: EntityId, components: &[&dyn UntypedComponent]) -> Entity { - let components: Vec<_> = components.iter().map(|c| c.index()).collect(); - wit::component::get_components(entity.into_bindgen(), &components).from_bindgen() -} - -/// Retrieves all guest-visible components for `entity`. Will return an empty `Entity` if no components are found. -/// -/// Note that this may not be all of the components on the entity, as some components are not visible to the guest. -pub fn get_all_components(entity: EntityId) -> Entity { - wit::component::get_all_components(entity.into_bindgen()).from_bindgen() -} - -/// Adds the component `component` for `entity` with `value`. Will replace an existing component if present. -pub fn add_component(entity: EntityId, component: Component, value: T) { - wit::component::add_component( - entity.into_bindgen(), - component.index(), - &value.into_result(), - ) -} - -/// Adds the components `components` for `entity` with `value`. Will replace any existing components specified in `components`. -pub fn add_components(entity: EntityId, components: Entity) { - wit::component::add_components(entity.into_bindgen(), &components.into_bindgen()) -} - -/// Sets the component `component` for `entity` with `value`. -pub fn set_component(entity: EntityId, component: Component, value: T) { - wit::component::set_component( - entity.into_bindgen(), - component.index(), - &value.into_result(), - ) -} - -/// Sets the components `components` for `entity` with `value`. -pub fn set_components(entity: EntityId, components: Entity) { - wit::component::set_components(entity.into_bindgen(), &components.into_bindgen()) -} - -/// Checks if the `entity` has a `component`. -pub fn has_component(entity: EntityId, component: Component) -> bool { - wit::component::has_component(entity.into_bindgen(), component.index()) -} - -/// Checks if the `entity` has `components`. -pub fn has_components(entity: EntityId, components: &[&dyn UntypedComponent]) -> bool { - let components: Vec<_> = components.iter().map(|c| c.index()).collect(); - wit::component::has_components(entity.into_bindgen(), &components) -} - -/// Adds the `component` with `value` to `entity` if `entity` does not already have that component. -pub fn add_component_if_required( - entity: EntityId, - component: Component, - value: T, -) { - if !has_component(entity, component) { - add_component(entity, component, value) - } -} - -/// Removes the `component` from `entity`. -/// -/// Does nothing if the component does not exist. -pub fn remove_component(entity: EntityId, component: Component) { - wit::component::remove_component(entity.into_bindgen(), component.index()) -} - -/// Removes the `components` from `entity`. -/// -/// Does nothing if the component does not exist. -pub fn remove_components(entity: EntityId, components: &[&dyn UntypedComponent]) { - let components: Vec<_> = components.iter().map(|c| c.index()).collect(); - wit::component::remove_components(entity.into_bindgen(), &components) -} - -/// Mutates the component `component` for `entity` using the passed in `mutator`, and returns its value. -/// -/// This will not set the component if the value is the same, which will prevent change events from -/// being unnecessarily fired. -pub fn mutate_component( - entity: EntityId, - component: Component, - mutator: impl FnOnce(&mut T), -) -> Option { - let mut value: T = get_component(entity, component)?; - let orig_value = value.clone(); - mutator(&mut value); - if value != orig_value { - set_component(entity, component, value.clone()); - } - Some(value) -} - -/// Mutates the component `component` for `entity` using the passed in `mutator`, or sets it -/// to `default` if it doesn't exist, and returns its value. -/// -/// This will not set the component if the value is the same, which will prevent change events from -/// being unnecessarily fired. -pub fn mutate_component_with_default( - entity: EntityId, - component: Component, - default: T, - mutator: impl FnOnce(&mut T), -) -> T { - let value = mutate_component(entity, component, mutator); - if let Some(value) = value { - value - } else { - add_component(entity, component, default.clone()); - default - } -} - -/// Adds `child` as a child to `entity`. -pub fn add_child(entity: EntityId, child: EntityId) { - if has_component(entity, children()) { - mutate_component(entity, children(), |children| children.push(child)); - } else { - add_component(entity, children(), vec![child]); - } - add_component(child, parent(), entity); -} - -/// Removes `child` as a child to `entity`. -pub fn remove_child(entity: EntityId, child: EntityId) { - if has_component(entity, children()) { - mutate_component(entity, children(), |children| { - children.retain(|x| *x != child) - }); - } - remove_component(child, parent()); -} - -/// Gets the resource entity. The components of this entity contain global state for this ECS world. -/// -/// Components with the `Resource` attribute can be found here. -pub fn resources() -> EntityId { - EntityId::resources() -} - -/// Gets the synchronized resource entity. The components of this entity contain global state that should be networked, but not persisted. -pub fn synchronized_resources() -> EntityId { - wit::entity::synchronized_resources().from_bindgen() -} - -/// Gets the persisted resource entity. The components of this entity contain global state that should be networked and persisted. -pub fn persisted_resources() -> EntityId { - wit::entity::persisted_resources().from_bindgen() -} diff --git a/guest/rust/api_core/src/global/runtime.rs b/guest/rust/api_core/src/global/runtime.rs index d1aadef3a2..f144141145 100644 --- a/guest/rust/api_core/src/global/runtime.rs +++ b/guest/rust/api_core/src/global/runtime.rs @@ -8,25 +8,30 @@ use std::{ use crate::{ core::app, - entity, global::{OkEmpty, ResultEmpty}, internal::executor::EXECUTOR, - prelude::RuntimeMessage, + prelude::{RuntimeMessage, World}, }; /// The time, relative to the start of the game. Guaranteed to be monotonic. -pub fn game_time() -> Duration { - entity::get_component(entity::resources(), app::components::game_time()).unwrap() +pub fn game_time(world: &World) -> Duration { + world + .get_component(world.resources(), app::components::game_time()) + .unwrap() } /// The time, relative to Jan 1, 1970. Not guaranteed to be monotonic. Use [game_time] for most applications. -pub fn epoch_time() -> Duration { - entity::get_component(entity::resources(), app::components::epoch_time()).unwrap() +pub fn epoch_time(world: &World) -> Duration { + world + .get_component(world.resources(), app::components::epoch_time()) + .unwrap() } /// The length of the previous frame, in seconds. -pub fn delta_time() -> f32 { - entity::get_component(entity::resources(), app::components::delta_time()).unwrap() +pub fn delta_time(world: &World) -> f32 { + world + .get_component(world.resources(), app::components::delta_time()) + .unwrap() } /// A trait that abstracts over return types so that you can return an [ResultEmpty] or nothing. diff --git a/guest/rust/api_core/src/global/shapes.rs b/guest/rust/api_core/src/global/shapes.rs index 0a750e08a0..6a11670a81 100644 --- a/guest/rust/api_core/src/global/shapes.rs +++ b/guest/rust/api_core/src/global/shapes.rs @@ -1,8 +1,9 @@ use super::EntityId; use crate::{ core::transform::components::local_to_world, - entity::get_component, + ecs, internal::{conversion::FromBindgen, wit}, + prelude::World, }; use glam::{vec3, Vec3}; @@ -16,12 +17,12 @@ pub struct Ray { } impl Ray { /// This creates a ray from a cameras view matrix (i.e. from `local_to_world` of a camera entity). - pub fn from_camera_view_matrix(camera: EntityId) -> Option { - let mat4 = get_component(camera, local_to_world())?; + pub fn from_camera_view_matrix(world: &World, camera: EntityId) -> ecs::Result { + let mat4 = world.get_component(camera, local_to_world())?; let origin = mat4.project_point3(Vec3::ZERO); let end = mat4.project_point3(vec3(0., 0., 1.)); let dir = (end - origin).normalize(); - Some(Ray { origin, dir }) + Ok(Ray { origin, dir }) } } diff --git a/guest/rust/api_core/src/internal/component/entity.rs b/guest/rust/api_core/src/internal/component/entity.rs index d53e7497ee..56047cb64f 100644 --- a/guest/rust/api_core/src/internal/component/entity.rs +++ b/guest/rust/api_core/src/internal/component/entity.rs @@ -1,8 +1,11 @@ use std::collections::HashMap; -use crate::internal::{ - conversion::{FromBindgen, IntoBindgen}, - wit, +use crate::{ + internal::{ + conversion::{FromBindgen, IntoBindgen}, + wit, + }, + prelude::World, }; use super::{Component, ComponentValue, SupportedValue, SupportedValueRef, UntypedComponent}; @@ -67,9 +70,9 @@ impl Entity { /// Spawns an entity with these components. /// - /// Returns `spawned_entity_uid`. - pub fn spawn(&self) -> crate::prelude::EntityId { - crate::entity::spawn(self) + /// Returns `spawned_entity_id`. + pub fn spawn(&self, world: &mut World) -> crate::prelude::EntityId { + world.spawn(self.clone()) } } impl FromBindgen for wit::component::Entity { diff --git a/guest/rust/api_core/src/internal/component/mod.rs b/guest/rust/api_core/src/internal/component/mod.rs index d672e87319..6722226a18 100644 --- a/guest/rust/api_core/src/internal/component/mod.rs +++ b/guest/rust/api_core/src/internal/component/mod.rs @@ -44,6 +44,8 @@ impl UntypedComponent for Component { self.index } } +unsafe impl Send for Component {} +unsafe impl Sync for Component {} /// A tuple of [Component]s. pub trait ComponentsTuple { diff --git a/guest/rust/api_core/src/internal/generated.rs b/guest/rust/api_core/src/internal/generated.rs index fbf8291c07..f837189167 100644 --- a/guest/rust/api_core/src/internal/generated.rs +++ b/guest/rust/api_core/src/internal/generated.rs @@ -531,9 +531,9 @@ mod raw { ) } #[doc = "Checks if the entity is a *Camera*.\n\nBase components for a camera. You will need other components to make a fully-functioning camera.\n\n*Definition*:\n\n```ignore\n{\n \"ambient_core::camera::near\": f32 = 0.1,\n \"ambient_core::camera::projection\": Mat4 = Mat4 { x_axis: Vec4(1.0, 0.0, 0.0, 0.0), y_axis: Vec4(0.0, 1.0, 0.0, 0.0), z_axis: Vec4(0.0, 0.0, 1.0, 0.0), w_axis: Vec4(0.0, 0.0, 0.0, 1.0) },\n \"ambient_core::camera::projection_view\": Mat4 = Mat4 { x_axis: Vec4(1.0, 0.0, 0.0, 0.0), y_axis: Vec4(0.0, 1.0, 0.0, 0.0), z_axis: Vec4(0.0, 0.0, 1.0, 0.0), w_axis: Vec4(0.0, 0.0, 0.0, 1.0) },\n \"ambient_core::camera::active_camera\": f32 = 0.0,\n \"ambient_core::transform::local_to_world\": Mat4 = Mat4 { x_axis: Vec4(1.0, 0.0, 0.0, 0.0), y_axis: Vec4(0.0, 1.0, 0.0, 0.0), z_axis: Vec4(0.0, 0.0, 1.0, 0.0), w_axis: Vec4(0.0, 0.0, 0.0, 1.0) },\n \"ambient_core::transform::inv_local_to_world\": Mat4 = Mat4 { x_axis: Vec4(1.0, 0.0, 0.0, 0.0), y_axis: Vec4(0.0, 1.0, 0.0, 0.0), z_axis: Vec4(0.0, 0.0, 1.0, 0.0), w_axis: Vec4(0.0, 0.0, 0.0, 1.0) },\n \"ambient_core::transform::transformable\": { // Concept.\n \"ambient_core::transform::translation\": Vec3 = Vec3(0.0, 0.0, 0.0),\n \"ambient_core::transform::rotation\": Quat = Quat(0.0, 0.0, 0.0, 1.0),\n \"ambient_core::transform::scale\": Vec3 = Vec3(1.0, 1.0, 1.0),\n \"ambient_core::transform::local_to_world\": Mat4 = Mat4 { x_axis: Vec4(1.0, 0.0, 0.0, 0.0), y_axis: Vec4(0.0, 1.0, 0.0, 0.0), z_axis: Vec4(0.0, 0.0, 1.0, 0.0), w_axis: Vec4(0.0, 0.0, 0.0, 1.0) },\n },\n}\n```\n"] - pub fn is_camera(id: EntityId) -> bool { - crate::ambient_core::transform::concepts::is_transformable(id) - && entity::has_components( + pub fn is_camera(world: &crate::prelude::World, id: EntityId) -> bool { + crate::ambient_core::transform::concepts::is_transformable(world, id) + && world.has_components( id, &[ &crate::ambient_core::camera::components::near(), @@ -576,9 +576,12 @@ mod raw { ) } #[doc = "Checks if the entity is a *Perspective Common Camera*.\n\nBase components for a perspective camera. Consider `perspective_camera` or `perspective_infinite_reverse_camera`.\n\n*Definition*:\n\n```ignore\n{\n \"ambient_core::camera::fovy\": f32 = 1.0,\n \"ambient_core::camera::aspect_ratio\": f32 = 1.0,\n \"ambient_core::camera::camera\": { // Concept.\n \"ambient_core::camera::near\": f32 = 0.1,\n \"ambient_core::camera::projection\": Mat4 = Mat4 { x_axis: Vec4(1.0, 0.0, 0.0, 0.0), y_axis: Vec4(0.0, 1.0, 0.0, 0.0), z_axis: Vec4(0.0, 0.0, 1.0, 0.0), w_axis: Vec4(0.0, 0.0, 0.0, 1.0) },\n \"ambient_core::camera::projection_view\": Mat4 = Mat4 { x_axis: Vec4(1.0, 0.0, 0.0, 0.0), y_axis: Vec4(0.0, 1.0, 0.0, 0.0), z_axis: Vec4(0.0, 0.0, 1.0, 0.0), w_axis: Vec4(0.0, 0.0, 0.0, 1.0) },\n \"ambient_core::camera::active_camera\": f32 = 0.0,\n \"ambient_core::transform::local_to_world\": Mat4 = Mat4 { x_axis: Vec4(1.0, 0.0, 0.0, 0.0), y_axis: Vec4(0.0, 1.0, 0.0, 0.0), z_axis: Vec4(0.0, 0.0, 1.0, 0.0), w_axis: Vec4(0.0, 0.0, 0.0, 1.0) },\n \"ambient_core::transform::inv_local_to_world\": Mat4 = Mat4 { x_axis: Vec4(1.0, 0.0, 0.0, 0.0), y_axis: Vec4(0.0, 1.0, 0.0, 0.0), z_axis: Vec4(0.0, 0.0, 1.0, 0.0), w_axis: Vec4(0.0, 0.0, 0.0, 1.0) },\n \"ambient_core::transform::transformable\": { // Concept.\n \"ambient_core::transform::translation\": Vec3 = Vec3(0.0, 0.0, 0.0),\n \"ambient_core::transform::rotation\": Quat = Quat(0.0, 0.0, 0.0, 1.0),\n \"ambient_core::transform::scale\": Vec3 = Vec3(1.0, 1.0, 1.0),\n \"ambient_core::transform::local_to_world\": Mat4 = Mat4 { x_axis: Vec4(1.0, 0.0, 0.0, 0.0), y_axis: Vec4(0.0, 1.0, 0.0, 0.0), z_axis: Vec4(0.0, 0.0, 1.0, 0.0), w_axis: Vec4(0.0, 0.0, 0.0, 1.0) },\n },\n },\n}\n```\n"] - pub fn is_perspective_common_camera(id: EntityId) -> bool { - crate::ambient_core::camera::concepts::is_camera(id) - && entity::has_components( + pub fn is_perspective_common_camera( + world: &crate::prelude::World, + id: EntityId, + ) -> bool { + crate::ambient_core::camera::concepts::is_camera(world, id) + && world.has_components( id, &[ &crate::ambient_core::camera::components::fovy(), @@ -605,9 +608,9 @@ mod raw { .with(crate::ambient_core::camera::components::far(), 1000f32) } #[doc = "Checks if the entity is a *Perspective Camera*.\n\nA perspective camera.\n\n*Definition*:\n\n```ignore\n{\n \"ambient_core::camera::perspective\": () = (),\n \"ambient_core::camera::far\": f32 = 1000.0,\n \"ambient_core::camera::perspective_common_camera\": { // Concept.\n \"ambient_core::camera::fovy\": f32 = 1.0,\n \"ambient_core::camera::aspect_ratio\": f32 = 1.0,\n \"ambient_core::camera::camera\": { // Concept.\n \"ambient_core::camera::near\": f32 = 0.1,\n \"ambient_core::camera::projection\": Mat4 = Mat4 { x_axis: Vec4(1.0, 0.0, 0.0, 0.0), y_axis: Vec4(0.0, 1.0, 0.0, 0.0), z_axis: Vec4(0.0, 0.0, 1.0, 0.0), w_axis: Vec4(0.0, 0.0, 0.0, 1.0) },\n \"ambient_core::camera::projection_view\": Mat4 = Mat4 { x_axis: Vec4(1.0, 0.0, 0.0, 0.0), y_axis: Vec4(0.0, 1.0, 0.0, 0.0), z_axis: Vec4(0.0, 0.0, 1.0, 0.0), w_axis: Vec4(0.0, 0.0, 0.0, 1.0) },\n \"ambient_core::camera::active_camera\": f32 = 0.0,\n \"ambient_core::transform::local_to_world\": Mat4 = Mat4 { x_axis: Vec4(1.0, 0.0, 0.0, 0.0), y_axis: Vec4(0.0, 1.0, 0.0, 0.0), z_axis: Vec4(0.0, 0.0, 1.0, 0.0), w_axis: Vec4(0.0, 0.0, 0.0, 1.0) },\n \"ambient_core::transform::inv_local_to_world\": Mat4 = Mat4 { x_axis: Vec4(1.0, 0.0, 0.0, 0.0), y_axis: Vec4(0.0, 1.0, 0.0, 0.0), z_axis: Vec4(0.0, 0.0, 1.0, 0.0), w_axis: Vec4(0.0, 0.0, 0.0, 1.0) },\n \"ambient_core::transform::transformable\": { // Concept.\n \"ambient_core::transform::translation\": Vec3 = Vec3(0.0, 0.0, 0.0),\n \"ambient_core::transform::rotation\": Quat = Quat(0.0, 0.0, 0.0, 1.0),\n \"ambient_core::transform::scale\": Vec3 = Vec3(1.0, 1.0, 1.0),\n \"ambient_core::transform::local_to_world\": Mat4 = Mat4 { x_axis: Vec4(1.0, 0.0, 0.0, 0.0), y_axis: Vec4(0.0, 1.0, 0.0, 0.0), z_axis: Vec4(0.0, 0.0, 1.0, 0.0), w_axis: Vec4(0.0, 0.0, 0.0, 1.0) },\n },\n },\n },\n}\n```\n"] - pub fn is_perspective_camera(id: EntityId) -> bool { - crate::ambient_core::camera::concepts::is_perspective_common_camera(id) - && entity::has_components( + pub fn is_perspective_camera(world: &crate::prelude::World, id: EntityId) -> bool { + crate::ambient_core::camera::concepts::is_perspective_common_camera(world, id) + && world.has_components( id, &[ &crate::ambient_core::camera::components::perspective(), @@ -636,8 +639,11 @@ mod raw { ) } #[doc = "Checks if the entity is a *Perspective-Infinite-Reverse Camera*.\n\nA perspective-infinite-reverse camera. This is recommended for most use-cases.\n\n*Definition*:\n\n```ignore\n{\n \"ambient_core::camera::perspective_infinite_reverse\": () = (),\n \"ambient_core::camera::perspective_common_camera\": { // Concept.\n \"ambient_core::camera::fovy\": f32 = 1.0,\n \"ambient_core::camera::aspect_ratio\": f32 = 1.0,\n \"ambient_core::camera::camera\": { // Concept.\n \"ambient_core::camera::near\": f32 = 0.1,\n \"ambient_core::camera::projection\": Mat4 = Mat4 { x_axis: Vec4(1.0, 0.0, 0.0, 0.0), y_axis: Vec4(0.0, 1.0, 0.0, 0.0), z_axis: Vec4(0.0, 0.0, 1.0, 0.0), w_axis: Vec4(0.0, 0.0, 0.0, 1.0) },\n \"ambient_core::camera::projection_view\": Mat4 = Mat4 { x_axis: Vec4(1.0, 0.0, 0.0, 0.0), y_axis: Vec4(0.0, 1.0, 0.0, 0.0), z_axis: Vec4(0.0, 0.0, 1.0, 0.0), w_axis: Vec4(0.0, 0.0, 0.0, 1.0) },\n \"ambient_core::camera::active_camera\": f32 = 0.0,\n \"ambient_core::transform::local_to_world\": Mat4 = Mat4 { x_axis: Vec4(1.0, 0.0, 0.0, 0.0), y_axis: Vec4(0.0, 1.0, 0.0, 0.0), z_axis: Vec4(0.0, 0.0, 1.0, 0.0), w_axis: Vec4(0.0, 0.0, 0.0, 1.0) },\n \"ambient_core::transform::inv_local_to_world\": Mat4 = Mat4 { x_axis: Vec4(1.0, 0.0, 0.0, 0.0), y_axis: Vec4(0.0, 1.0, 0.0, 0.0), z_axis: Vec4(0.0, 0.0, 1.0, 0.0), w_axis: Vec4(0.0, 0.0, 0.0, 1.0) },\n \"ambient_core::transform::transformable\": { // Concept.\n \"ambient_core::transform::translation\": Vec3 = Vec3(0.0, 0.0, 0.0),\n \"ambient_core::transform::rotation\": Quat = Quat(0.0, 0.0, 0.0, 1.0),\n \"ambient_core::transform::scale\": Vec3 = Vec3(1.0, 1.0, 1.0),\n \"ambient_core::transform::local_to_world\": Mat4 = Mat4 { x_axis: Vec4(1.0, 0.0, 0.0, 0.0), y_axis: Vec4(0.0, 1.0, 0.0, 0.0), z_axis: Vec4(0.0, 0.0, 1.0, 0.0), w_axis: Vec4(0.0, 0.0, 0.0, 1.0) },\n },\n },\n },\n}\n```\n"] - pub fn is_perspective_infinite_reverse_camera(id: EntityId) -> bool { - crate :: ambient_core :: camera :: concepts :: is_perspective_common_camera (id) && entity :: has_components (id , & [& crate :: ambient_core :: camera :: components :: perspective_infinite_reverse ()]) + pub fn is_perspective_infinite_reverse_camera( + world: &crate::prelude::World, + id: EntityId, + ) -> bool { + crate :: ambient_core :: camera :: concepts :: is_perspective_common_camera (world , id) && world . has_components (id , & [& crate :: ambient_core :: camera :: components :: perspective_infinite_reverse ()]) } #[doc = "Returns the components that comprise *Perspective-Infinite-Reverse Camera* as a tuple.\n\nA perspective-infinite-reverse camera. This is recommended for most use-cases.\n\n*Definition*:\n\n```ignore\n{\n \"ambient_core::camera::perspective_infinite_reverse\": () = (),\n \"ambient_core::camera::perspective_common_camera\": { // Concept.\n \"ambient_core::camera::fovy\": f32 = 1.0,\n \"ambient_core::camera::aspect_ratio\": f32 = 1.0,\n \"ambient_core::camera::camera\": { // Concept.\n \"ambient_core::camera::near\": f32 = 0.1,\n \"ambient_core::camera::projection\": Mat4 = Mat4 { x_axis: Vec4(1.0, 0.0, 0.0, 0.0), y_axis: Vec4(0.0, 1.0, 0.0, 0.0), z_axis: Vec4(0.0, 0.0, 1.0, 0.0), w_axis: Vec4(0.0, 0.0, 0.0, 1.0) },\n \"ambient_core::camera::projection_view\": Mat4 = Mat4 { x_axis: Vec4(1.0, 0.0, 0.0, 0.0), y_axis: Vec4(0.0, 1.0, 0.0, 0.0), z_axis: Vec4(0.0, 0.0, 1.0, 0.0), w_axis: Vec4(0.0, 0.0, 0.0, 1.0) },\n \"ambient_core::camera::active_camera\": f32 = 0.0,\n \"ambient_core::transform::local_to_world\": Mat4 = Mat4 { x_axis: Vec4(1.0, 0.0, 0.0, 0.0), y_axis: Vec4(0.0, 1.0, 0.0, 0.0), z_axis: Vec4(0.0, 0.0, 1.0, 0.0), w_axis: Vec4(0.0, 0.0, 0.0, 1.0) },\n \"ambient_core::transform::inv_local_to_world\": Mat4 = Mat4 { x_axis: Vec4(1.0, 0.0, 0.0, 0.0), y_axis: Vec4(0.0, 1.0, 0.0, 0.0), z_axis: Vec4(0.0, 0.0, 1.0, 0.0), w_axis: Vec4(0.0, 0.0, 0.0, 1.0) },\n \"ambient_core::transform::transformable\": { // Concept.\n \"ambient_core::transform::translation\": Vec3 = Vec3(0.0, 0.0, 0.0),\n \"ambient_core::transform::rotation\": Quat = Quat(0.0, 0.0, 0.0, 1.0),\n \"ambient_core::transform::scale\": Vec3 = Vec3(1.0, 1.0, 1.0),\n \"ambient_core::transform::local_to_world\": Mat4 = Mat4 { x_axis: Vec4(1.0, 0.0, 0.0, 0.0), y_axis: Vec4(0.0, 1.0, 0.0, 0.0), z_axis: Vec4(0.0, 0.0, 1.0, 0.0), w_axis: Vec4(0.0, 0.0, 0.0, 1.0) },\n },\n },\n },\n}\n```\n"] #[allow(clippy::type_complexity)] @@ -670,9 +676,9 @@ mod raw { .with(crate::ambient_core::camera::components::far(), 1f32) } #[doc = "Checks if the entity is a *Orthographic Camera*.\n\nAn orthographic camera.\n\n*Definition*:\n\n```ignore\n{\n \"ambient_core::camera::orthographic\": () = (),\n \"ambient_core::camera::orthographic_left\": f32 = -1.0,\n \"ambient_core::camera::orthographic_right\": f32 = 1.0,\n \"ambient_core::camera::orthographic_top\": f32 = 1.0,\n \"ambient_core::camera::orthographic_bottom\": f32 = -1.0,\n \"ambient_core::camera::near\": f32 = -1.0,\n \"ambient_core::camera::far\": f32 = 1.0,\n \"ambient_core::camera::camera\": { // Concept.\n \"ambient_core::camera::near\": f32 = 0.1,\n \"ambient_core::camera::projection\": Mat4 = Mat4 { x_axis: Vec4(1.0, 0.0, 0.0, 0.0), y_axis: Vec4(0.0, 1.0, 0.0, 0.0), z_axis: Vec4(0.0, 0.0, 1.0, 0.0), w_axis: Vec4(0.0, 0.0, 0.0, 1.0) },\n \"ambient_core::camera::projection_view\": Mat4 = Mat4 { x_axis: Vec4(1.0, 0.0, 0.0, 0.0), y_axis: Vec4(0.0, 1.0, 0.0, 0.0), z_axis: Vec4(0.0, 0.0, 1.0, 0.0), w_axis: Vec4(0.0, 0.0, 0.0, 1.0) },\n \"ambient_core::camera::active_camera\": f32 = 0.0,\n \"ambient_core::transform::local_to_world\": Mat4 = Mat4 { x_axis: Vec4(1.0, 0.0, 0.0, 0.0), y_axis: Vec4(0.0, 1.0, 0.0, 0.0), z_axis: Vec4(0.0, 0.0, 1.0, 0.0), w_axis: Vec4(0.0, 0.0, 0.0, 1.0) },\n \"ambient_core::transform::inv_local_to_world\": Mat4 = Mat4 { x_axis: Vec4(1.0, 0.0, 0.0, 0.0), y_axis: Vec4(0.0, 1.0, 0.0, 0.0), z_axis: Vec4(0.0, 0.0, 1.0, 0.0), w_axis: Vec4(0.0, 0.0, 0.0, 1.0) },\n \"ambient_core::transform::transformable\": { // Concept.\n \"ambient_core::transform::translation\": Vec3 = Vec3(0.0, 0.0, 0.0),\n \"ambient_core::transform::rotation\": Quat = Quat(0.0, 0.0, 0.0, 1.0),\n \"ambient_core::transform::scale\": Vec3 = Vec3(1.0, 1.0, 1.0),\n \"ambient_core::transform::local_to_world\": Mat4 = Mat4 { x_axis: Vec4(1.0, 0.0, 0.0, 0.0), y_axis: Vec4(0.0, 1.0, 0.0, 0.0), z_axis: Vec4(0.0, 0.0, 1.0, 0.0), w_axis: Vec4(0.0, 0.0, 0.0, 1.0) },\n },\n },\n}\n```\n"] - pub fn is_orthographic_camera(id: EntityId) -> bool { - crate::ambient_core::camera::concepts::is_camera(id) - && entity::has_components( + pub fn is_orthographic_camera(world: &crate::prelude::World, id: EntityId) -> bool { + crate::ambient_core::camera::concepts::is_camera(world, id) + && world.has_components( id, &[ &crate::ambient_core::camera::components::orthographic(), @@ -1794,8 +1800,8 @@ mod raw { ) } #[doc = "Checks if the entity is a *Sphere*.\n\nA primitive sphere.\n\n*Definition*:\n\n```ignore\n{\n \"ambient_core::primitives::sphere\": () = (),\n \"ambient_core::primitives::sphere_radius\": f32 = 0.5,\n \"ambient_core::primitives::sphere_sectors\": u32 = 36,\n \"ambient_core::primitives::sphere_stacks\": u32 = 18,\n}\n```\n"] - pub fn is_sphere(id: EntityId) -> bool { - entity::has_components( + pub fn is_sphere(world: &crate::prelude::World, id: EntityId) -> bool { + world.has_components( id, &[ &crate::ambient_core::primitives::components::sphere(), @@ -1847,8 +1853,8 @@ mod raw { ) } #[doc = "Checks if the entity is a *Capsule*.\n\nA primitive capsule. Defined as a cylinder capped by hemispheres.\n\n*Definition*:\n\n```ignore\n{\n \"ambient_core::primitives::capsule\": () = (),\n \"ambient_core::primitives::capsule_radius\": f32 = 0.5,\n \"ambient_core::primitives::capsule_half_height\": f32 = 0.5,\n \"ambient_core::primitives::capsule_rings\": u32 = 0,\n \"ambient_core::primitives::capsule_latitudes\": u32 = 16,\n \"ambient_core::primitives::capsule_longitudes\": u32 = 32,\n}\n```\n"] - pub fn is_capsule(id: EntityId) -> bool { - entity::has_components( + pub fn is_capsule(world: &crate::prelude::World, id: EntityId) -> bool { + world.has_components( id, &[ &crate::ambient_core::primitives::components::capsule(), @@ -1902,8 +1908,8 @@ mod raw { ) } #[doc = "Checks if the entity is a *Torus*.\n\nA primitive Torus, surface of revolution generated by revolving a circle in three-dimensional space one full revolution.\n\n*Definition*:\n\n```ignore\n{\n \"ambient_core::primitives::torus\": () = (),\n \"ambient_core::primitives::torus_inner_radius\": f32 = 0.25,\n \"ambient_core::primitives::torus_outer_radius\": f32 = 0.35,\n \"ambient_core::primitives::torus_slices\": u32 = 32,\n \"ambient_core::primitives::torus_loops\": u32 = 16,\n}\n```\n"] - pub fn is_torus(id: EntityId) -> bool { - entity::has_components( + pub fn is_torus(world: &crate::prelude::World, id: EntityId) -> bool { + world.has_components( id, &[ &crate::ambient_core::primitives::components::torus(), @@ -2465,8 +2471,8 @@ mod raw { ) } #[doc = "Checks if the entity is a *Transformable*.\n\nCan be translated, rotated and scaled.\n\n*Definition*:\n\n```ignore\n{\n \"ambient_core::transform::translation\": Vec3 = Vec3(0.0, 0.0, 0.0),\n \"ambient_core::transform::rotation\": Quat = Quat(0.0, 0.0, 0.0, 1.0),\n \"ambient_core::transform::scale\": Vec3 = Vec3(1.0, 1.0, 1.0),\n \"ambient_core::transform::local_to_world\": Mat4 = Mat4 { x_axis: Vec4(1.0, 0.0, 0.0, 0.0), y_axis: Vec4(0.0, 1.0, 0.0, 0.0), z_axis: Vec4(0.0, 0.0, 1.0, 0.0), w_axis: Vec4(0.0, 0.0, 0.0, 1.0) },\n}\n```\n"] - pub fn is_transformable(id: EntityId) -> bool { - entity::has_components( + pub fn is_transformable(world: &crate::prelude::World, id: EntityId) -> bool { + world.has_components( id, &[ &crate::ambient_core::transform::components::translation(), diff --git a/guest/rust/api_core/src/internal/mod.rs b/guest/rust/api_core/src/internal/mod.rs index 475be17aec..a1e16f2fab 100644 --- a/guest/rust/api_core/src/internal/mod.rs +++ b/guest/rust/api_core/src/internal/mod.rs @@ -6,20 +6,24 @@ pub(crate) mod generated; #[allow(missing_docs)] pub(crate) mod wit; -use crate::internal::executor::EXECUTOR; +mod world; + +use crate::{ecs::World, internal::executor::EXECUTOR}; use wit::{__link_section, exports, guest}; + +pub(crate) use self::world::HostWorld; wit::export_bindings!(Guest); extern "Rust" { - fn main(); + fn main(world: &mut World); } struct Guest; impl guest::Guest for Guest { fn init() { once_cell::sync::Lazy::force(&EXECUTOR); - unsafe { main() }; + unsafe { main(&mut World::Host(HostWorld)) }; } fn exec(source: guest::Source, message_name: String, message_data: Vec) { diff --git a/guest/rust/api_core/src/internal/world.rs b/guest/rust/api_core/src/internal/world.rs new file mode 100644 index 0000000000..82c9a0a225 --- /dev/null +++ b/guest/rust/api_core/src/internal/world.rs @@ -0,0 +1,134 @@ +use glam::Mat4; + +use crate::{ + ecs::{ComponentValue, Result}, + global::{EntityId, Vec3}, + internal::{ + component::{Entity, UntypedComponent}, + conversion::{FromBindgen, IntoBindgen}, + wit, + }, + prelude::ECSError, +}; + +pub struct HostWorld; +impl HostWorld { + pub fn spawn(&mut self, components: Entity) -> EntityId { + wit::entity::spawn(&components.into_bindgen()).from_bindgen() + } + + pub fn despawn(&mut self, entity: EntityId) -> Option { + wit::entity::despawn(entity.into_bindgen()).from_bindgen() + } + + pub fn get_transforms_relative_to(&self, list: &[EntityId], origin: EntityId) -> Vec { + wit::entity::get_transforms_relative_to( + &list.iter().map(|x| x.into_bindgen()).collect::>(), + origin.into_bindgen(), + ) + .from_bindgen() + } + + pub fn exists(&self, entity: EntityId) -> bool { + wit::entity::exists(entity.into_bindgen()) + } + + pub fn get_all_untyped(&self, component: &dyn UntypedComponent) -> Vec { + wit::entity::get_all(component.index()).from_bindgen() + } + + pub fn in_area(&self, position: Vec3, radius: f32) -> Vec { + wit::entity::in_area(position.into_bindgen(), radius).from_bindgen() + } + + pub fn get_component_untyped( + &self, + entity: EntityId, + component: &dyn UntypedComponent, + ) -> Result { + // TODO: Need to actually get a meaningful error + wit::component::get_component(entity.into_bindgen(), component.index()) + .from_bindgen() + .ok_or(ECSError::NoSuchEntity) + } + + pub fn get_components(&self, entity: EntityId, components: &[&dyn UntypedComponent]) -> Entity { + let components: Vec<_> = components.iter().map(|c| c.index()).collect(); + wit::component::get_components(entity.into_bindgen(), &components).from_bindgen() + } + + pub fn get_all_components(&self, entity: EntityId) -> Entity { + wit::component::get_all_components(entity.into_bindgen()).from_bindgen() + } + + pub fn add_component_untyped( + &mut self, + entity: EntityId, + component: &dyn UntypedComponent, + value: ComponentValue, + ) -> Result<()> { + wit::component::add_component( + entity.into_bindgen(), + component.index(), + &value.into_bindgen(), + ); + Ok(()) + } + + pub fn add_components(&mut self, entity: EntityId, components: Entity) -> Result<()> { + wit::component::add_components(entity.into_bindgen(), &components.into_bindgen()); + Ok(()) + } + + pub fn set_component_untyped( + &mut self, + entity: EntityId, + component: &dyn UntypedComponent, + value: ComponentValue, + ) -> Result<()> { + wit::component::set_component( + entity.into_bindgen(), + component.index(), + &value.into_bindgen(), + ); + Ok(()) + } + + pub fn set_components(&mut self, entity: EntityId, components: Entity) { + wit::component::set_components(entity.into_bindgen(), &components.into_bindgen()) + } + + pub fn has_component_untyped( + &self, + entity: EntityId, + component: &dyn UntypedComponent, + ) -> bool { + wit::component::has_component(entity.into_bindgen(), component.index()) + } + + pub fn has_components(&self, entity: EntityId, components: &[&dyn UntypedComponent]) -> bool { + let components: Vec<_> = components.iter().map(|c| c.index()).collect(); + wit::component::has_components(entity.into_bindgen(), &components) + } + + pub fn remove_component_untyped(&mut self, entity: EntityId, component: &dyn UntypedComponent) { + wit::component::remove_component(entity.into_bindgen(), component.index()) + } + + pub fn remove_components(&mut self, entity: EntityId, components: &[&dyn UntypedComponent]) { + let components: Vec<_> = components.iter().map(|c| c.index()).collect(); + wit::component::remove_components(entity.into_bindgen(), &components) + } + + pub fn resources(&self) -> EntityId { + EntityId::resources() + } + + pub fn synchronized_resources(&self) -> EntityId { + wit::entity::synchronized_resources().from_bindgen() + } + + pub fn persisted_resources(&self) -> EntityId { + wit::entity::persisted_resources().from_bindgen() + } +} diff --git a/guest/rust/api_core/src/lib.rs b/guest/rust/api_core/src/lib.rs index 7c50b41a6a..1c36533615 100644 --- a/guest/rust/api_core/src/lib.rs +++ b/guest/rust/api_core/src/lib.rs @@ -19,8 +19,6 @@ pub mod server; pub mod asset; /// ECS-related functionality not directly related to entities. pub mod ecs; -/// Manipulation, creation, removal, search and more for entities. -pub mod entity; /// Global functions and types for your convenience. pub mod global; /// Messaging to other packages and to the other side of the network boundary. diff --git a/guest/rust/api_core/src/prelude.rs b/guest/rust/api_core/src/prelude.rs index d19829a823..47b3a46ab6 100644 --- a/guest/rust/api_core/src/prelude.rs +++ b/guest/rust/api_core/src/prelude.rs @@ -1,7 +1,9 @@ pub use crate::{ asset, - ecs::{change_query, despawn_query, query, spawn_query, Component, Entity, QueryEvent}, - entity, + ecs::{ + change_query, despawn_query, query, spawn_query, Component, ECSError, Entity, QueryEvent, + World, + }, global::*, main, message, message::{Message, ModuleMessage, RuntimeMessage}, diff --git a/guest/rust/examples/basics/asset_loading/src/client.rs b/guest/rust/examples/basics/asset_loading/src/client.rs index 79dd69a3c1..667b3121fa 100644 --- a/guest/rust/examples/basics/asset_loading/src/client.rs +++ b/guest/rust/examples/basics/asset_loading/src/client.rs @@ -1,7 +1,7 @@ use ambient_api::prelude::*; #[main] -fn main() { +fn main(_world: &mut World) { // Load the asset println!( "asset url can be accessed from client: {}", diff --git a/guest/rust/examples/basics/asset_loading/src/server.rs b/guest/rust/examples/basics/asset_loading/src/server.rs index 56c2c959db..2525c6189d 100644 --- a/guest/rust/examples/basics/asset_loading/src/server.rs +++ b/guest/rust/examples/basics/asset_loading/src/server.rs @@ -23,20 +23,20 @@ use ambient_api::{ use packages::this::{assets, components::is_the_best}; #[main] -pub async fn main() { +pub async fn main(world: &mut World) { Entity::new() .with_merge(make_perspective_infinite_reverse_camera()) .with(aspect_ratio_from_window(), EntityId::resources()) .with(main_scene(), ()) .with(translation(), vec3(2., 2., 1.)) .with(lookat_target(), vec3(0., 0., 0.)) - .spawn(); + .spawn(world); Entity::new() .with_merge(make_transformable()) .with(quad(), ()) .with(scale(), Vec3::ONE * 2.0) - .spawn(); + .spawn(world); println!("Hello, Ambient!"); @@ -47,22 +47,22 @@ pub async fn main() { .with(main_scene(), ()) .with(light_diffuse(), Vec3::ONE * 5.0) .with(light_ambient(), Vec3::ZERO) - .spawn(); + .spawn(world); let model = Entity::new() .with_merge(make_transformable()) .with(cast_shadows(), ()) .with(prefab_from_url(), assets::url("Teapot.glb")) .with(is_the_best(), true) - .spawn(); + .spawn(world); - entity::wait_for_component(model, spawned()).await; + world.wait_for_component(model, spawned()).await; - println!("Entity components: {:?}", entity::get_all_components(model)); + println!("Entity components: {:?}", world.get_all_components(model)); Frame::subscribe(move |_| { - let t = game_time().as_secs_f32(); - entity::set_component( + let t = game_time(world).as_secs_f32(); + world.set_component( model, rotation(), Quat::from_euler(EulerRot::ZXY, t % TAU, (t * 2.0).sin() * 0.5, 0.0), diff --git a/guest/rust/examples/basics/physics/src/client.rs b/guest/rust/examples/basics/physics/src/client.rs index 7f1ba8a5bc..3541410618 100644 --- a/guest/rust/examples/basics/physics/src/client.rs +++ b/guest/rust/examples/basics/physics/src/client.rs @@ -1,8 +1,8 @@ -use ambient_api::prelude::*; +use ambient_api::{ecs::World, prelude::*}; use packages::this::{assets, messages::Bonk}; #[main] -pub fn main() { +pub fn main(world: &mut World) { let spatial_audio_player = audio::SpatialAudioPlayer::new(); Bonk::subscribe(move |_source, data| { diff --git a/shared_crates/element/src/hooks.rs b/shared_crates/element/src/hooks.rs index f78157a0c7..1cbf2f529c 100644 --- a/shared_crates/element/src/hooks.rs +++ b/shared_crates/element/src/hooks.rs @@ -34,6 +34,9 @@ use crate::{AnyCloneable, ElementTree, HookContext, InstanceId}; /// Helper type for a callback that sets some value. pub type Setter = Cb; +/// Helper type for a callback that sets some value using the world. +#[cfg(feature = "guest")] +pub type SetterWithWorld = Cb; type SpawnFn = Box DespawnFn + Sync + Send>; /// The return type of a function passed to [Hooks::use_spawn]. This function is called @@ -230,9 +233,9 @@ impl<'a> Hooks<'a> { { let handler = self.use_ref_with(|_| None); *handler.lock() = Some(cb(func)); - self.use_effect((), move |_, _| { + self.use_effect((), move |world, _| { let listener = T::subscribe(move |event| { - (handler.lock().as_ref().unwrap())(&mut World, &event); + (handler.lock().as_ref().unwrap())(world, &event); }); |_| listener.stop() }); @@ -249,9 +252,9 @@ impl<'a> Hooks<'a> { ) { let handler = self.use_ref_with(|_| None); *handler.lock() = Some(cb(func)); - self.use_effect((), move |_, _| { + self.use_effect((), move |world, _| { let listener = T::subscribe(move |source, event| { - (handler.lock().as_ref().unwrap())(&mut World, source, &event); + (handler.lock().as_ref().unwrap())(world, source, &event); }); |_| listener.stop() }); @@ -600,8 +603,8 @@ impl<'a> Hooks<'a> { &mut self, id: EntityId, component: ambient_guest_bridge::api::ecs::Component, - ) -> (Option, Setter) { - use ambient_guest_bridge::api::prelude::{change_query, entity}; + ) -> (Option, SetterWithWorld) { + use ambient_guest_bridge::api::prelude::change_query; let refresh = self.use_rerender_signal(); self.use_spawn(move |_| { @@ -615,8 +618,10 @@ impl<'a> Hooks<'a> { }); ( - entity::get_component(id, component), - cb(move |value| entity::add_component(id, component, value)), + self.world.get(id, component).ok(), + cb(move |world, value| { + world.add_component(id, component, value); + }), ) } @@ -636,10 +641,8 @@ impl<'a> Hooks<'a> { >( &mut self, component: ambient_guest_bridge::api::ecs::Component, - ) -> (Option, Setter) { - use ambient_guest_bridge::api::entity; - - self.use_entity_component(entity::resources(), component) + ) -> (Option, SetterWithWorld) { + self.use_entity_component(self.world.resources(), component) } /// Run `cb` every `seconds` seconds. diff --git a/shared_crates/element/src/lib.rs b/shared_crates/element/src/lib.rs index a060b8a31d..f9369dbe88 100644 --- a/shared_crates/element/src/lib.rs +++ b/shared_crates/element/src/lib.rs @@ -274,18 +274,11 @@ impl Element { ); world.spawn(entity) } - /// This spawns the elemet tree and returns it. The tree won't be automatically updated, but can manually be updated + /// This spawns the element tree and returns it. The tree won't be automatically updated, but can manually be updated /// by calling the `update` method. - #[cfg(feature = "native")] pub fn spawn_tree(self, world: &mut World) -> ElementTree { ElementTree::new(world, self) } - /// This spawns the elemet tree and returns it. The tree won't be automatically updated, but can manually be updated - /// by calling the `update` method. - #[cfg(feature = "guest")] - pub fn spawn_tree(self) -> ElementTree { - ElementTree::new(&mut World, self) - } /// This spawns the element tree and sets up listeners to automatically update it. /// /// This is equivalent to calling [Self::spawn_tree] and then calling [ElementTree::update] on the tree each frame. @@ -301,14 +294,14 @@ impl Element { /// }); /// ``` #[cfg(feature = "guest")] - pub fn spawn_interactive(self) { + pub fn spawn_interactive(self, world: &mut World) { use ambient_guest_bridge::api::{ core::messages::Frame, message::RuntimeMessage, prelude::OkEmpty, }; - let mut tree = self.spawn_tree(); + let mut tree = self.spawn_tree(world); Frame::subscribe(move |_| { - tree.update(&mut World); + tree.update(world); OkEmpty }); } diff --git a/shared_crates/guest_bridge/src/guest.rs b/shared_crates/guest_bridge/src/guest.rs index 5e4c9d884a..e0a95eb876 100644 --- a/shared_crates/guest_bridge/src/guest.rs +++ b/shared_crates/guest_bridge/src/guest.rs @@ -31,83 +31,10 @@ pub async fn sleep(seconds: f32) { pub mod ecs { use super::api; pub use api::{ - ecs::{Component, SupportedValue as ComponentValue, UntypedComponent}, + ecs::{Component, ECSError, SupportedValue as ComponentValue, UntypedComponent, World}, prelude::{Entity, EntityId}, }; - #[derive(Clone, Copy)] - pub struct World; - impl World { - pub fn spawn(&self, entity: Entity) -> EntityId { - api::entity::spawn(&entity) - } - pub fn despawn(&self, entity_id: EntityId) -> Option { - api::entity::despawn(entity_id) - } - pub fn exists(&self, entity_id: EntityId) -> bool { - api::entity::exists(entity_id) - } - pub fn set( - &self, - entity_id: EntityId, - component: Component, - value: T, - ) -> Result<(), ECSError> { - // TODO: set_component needs to return errors - api::entity::set_component(entity_id, component, value); - Ok(()) - } - pub fn add_component( - &self, - entity_id: EntityId, - component: Component, - value: T, - ) -> Result<(), ECSError> { - // TODO: add_component needs to return errors - api::entity::add_component(entity_id, component, value); - Ok(()) - } - pub fn add_components( - &self, - entity_id: EntityId, - components: Entity, - ) -> Result<(), ECSError> { - // TODO: add_components needs to return errors - api::entity::add_components(entity_id, components); - Ok(()) - } - pub fn get( - &self, - entity_id: EntityId, - component: Component, - ) -> Result { - api::entity::get_component(entity_id, component) - .ok_or_else(|| ECSError::EntityDoesntHaveComponent) - } - pub fn get_cloned( - &self, - entity_id: EntityId, - component: Component, - ) -> Result { - self.get(entity_id, component) - } - pub fn has_component( - &self, - entity_id: EntityId, - component: Component, - ) -> bool { - api::entity::has_component(entity_id, component) - } - pub fn resource(&self, component: Component) -> T { - api::entity::get_component(api::entity::resources(), component).unwrap() - } - } - #[derive(Debug)] - pub enum ECSError { - EntityDoesntHaveComponent, - NoSuchEntity, - } - pub struct ComponentDesc(Box); impl ComponentDesc { pub fn index(&self) -> u32 { diff --git a/shared_crates/package_macro_common/src/concepts.rs b/shared_crates/package_macro_common/src/concepts.rs index 0af4c01f2b..fa8d392272 100644 --- a/shared_crates/package_macro_common/src/concepts.rs +++ b/shared_crates/package_macro_common/src/concepts.rs @@ -146,14 +146,17 @@ fn generate_is( }) } }, - Context::GuestApi | Context::GuestUser => quote! { - #[doc = #is_comment] - pub fn #is_ident(id: EntityId) -> bool { - #(#extends(id) && )* entity::has_components(id, &[ - #(&#components),* - ]) + Context::GuestApi | Context::GuestUser => { + let api_path = context.guest_api_path().unwrap(); + quote! { + #[doc = #is_comment] + pub fn #is_ident(world: &#api_path::prelude::World, id: EntityId) -> bool { + #(#extends(world, id) && )* world.has_components(id, &[ + #(&#components),* + ]) + } } - }, + } }) }