Skip to content

High-Level Entity Methods (WIP) #397

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion quill/api/src/entities.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,26 @@
//! Defines components for all Minecraft entities.
//!
//! # Entity wrappers
//! In an ECS, components provide access to raw _data_,
//! like an entity's position, health, inventory, etc.
//! But components don't have to abstract high-level
//! methods that need access to multiple components. For example,
//! "deal damage" requires access to `Health` and `Inventory` (to handle armor),
//! and possibly potion effects.
//!
//! To solve this problem, Quill provides entity _wrappers_ that provide
//! these high-level methods. Each entity has a wrapper struct
//! that implements a series of traits. For example, the `Player` wrapper
//! struct provides the `send_message` function via the `SendMessage` trait.
//! Similarly, the `DealDamage` trait provides the `damage()` method
//! for entities with health.
//!
//! # Marker components
//! Each entity has a "marker component":
//! just a struct (often with no fields)
//! that signifies the type of an entity.
//!
//! For example, all horse entities have the [`Horse`]
//! For example, all horse entities have the [`HorseMarker`]
//! marker component.
//!
//! For certain entities, these components also
Expand Down
17 changes: 12 additions & 5 deletions quill/api/src/game.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@ use libcraft_core::{BlockPosition, ChunkPosition, Position, CHUNK_HEIGHT};
use libcraft_particles::Particle;
use quill_common::entity_init::EntityInit;

use crate::{
query::{Query, QueryIter},
EntityBuilder,
};
use crate::{query::QueryIter, EntityBuilder};
use crate::{Entity, EntityId};
use quill_common::entity::FromQuery;

/// Error returned when getting or setting a block fails.
#[derive(Debug, thiserror::Error)]
Expand Down Expand Up @@ -109,7 +107,16 @@ impl Game {
/// println!("Found an entity with position {:?} and UUID {}", position, uuid);
/// }
/// ```
pub fn query<Q: Query>(&self) -> QueryIter<Q> {
///
/// Iterate over all player entities, and send them messages:
/// ```no_run
/// use quill::entities::Player;
/// # let game: quill::Game = unimplemented!();
/// for (_entity, player) in game.query::<Player>() {
/// player.send_message("Hello world");
/// }
/// ```
pub fn query<Q: FromQuery>(&self) -> QueryIter<Q> {
QueryIter::new()
}

Expand Down
106 changes: 11 additions & 95 deletions quill/api/src/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,100 +2,13 @@

use std::{marker::PhantomData, mem::MaybeUninit};

use quill_common::{entity::QueryData, Component, HostComponent, PointerMut};
use quill_common::{
entity::{FromQuery, QueryData},
PointerMut,
};

use crate::{Entity, EntityId};

/// A type that can be used for a query.
///
/// Implemented for tuples of `Query`s as well.
pub trait Query {
type Item;

fn add_component_types(types: &mut Vec<HostComponent>);

/// # Safety
/// `component_index` must be a valid index less
/// than the number of entities in the query data.
///
/// `component_offsets` must contain the proper byte offset
/// of the current component index.
unsafe fn get_unchecked(
data: &QueryData,
component_index: &mut usize,
component_offsets: &mut [usize],
) -> Self::Item;
}

impl<'a, T> Query for &'a T
where
T: Component,
[T]: ToOwned,
{
type Item = T;

fn add_component_types(types: &mut Vec<HostComponent>) {
types.push(T::host_component());
}

unsafe fn get_unchecked(
data: &QueryData,
component_index: &mut usize,
component_offsets: &mut [usize],
) -> T {
let component_len = *((data.component_lens.as_mut_ptr()).add(*component_index)) as usize;
let component_ptr =
(*(data.component_ptrs.as_mut_ptr().add(*component_index))).as_mut_ptr();

let offset = component_offsets[*component_index];
let component_ptr = component_ptr.add(offset);
let component_len = component_len - offset;

let component_bytes = std::slice::from_raw_parts(component_ptr, component_len);
let (value, advance) = T::from_bytes_unchecked(component_bytes);

component_offsets[*component_index] += advance;

*component_index += 1;

value
}
}

macro_rules! impl_query_tuple {
($($query:ident),* $(,)?) => {
impl <$($query: Query),*> Query for ($($query,)*) {
type Item = ($($query::Item),*);
fn add_component_types(types: &mut Vec<HostComponent>) {
$(
$query::add_component_types(types);
)*
}

unsafe fn get_unchecked(data: &QueryData, component_index: &mut usize, component_offsets: &mut [usize]) -> Self::Item {
(
$(
$query::get_unchecked(data, component_index, component_offsets)
),*
)
}
}
}
}

impl_query_tuple!(A, B);
impl_query_tuple!(A, B, C);
impl_query_tuple!(A, B, C, D);
impl_query_tuple!(A, B, C, D, E);
impl_query_tuple!(A, B, C, D, E, F);
impl_query_tuple!(A, B, C, D, E, F, G);
impl_query_tuple!(A, B, C, D, E, F, G, H);
impl_query_tuple!(A, B, C, D, E, F, G, H, I);
impl_query_tuple!(A, B, C, D, E, F, G, H, I, J);
impl_query_tuple!(A, B, C, D, E, F, G, H, I, J, K);
impl_query_tuple!(A, B, C, D, E, F, G, H, I, J, K, L);
impl_query_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M);

/// An iterator over all entities matching a query.
pub struct QueryIter<Q> {
data: QueryData,
Expand All @@ -106,7 +19,7 @@ pub struct QueryIter<Q> {

impl<Q> QueryIter<Q>
where
Q: Query,
Q: FromQuery,
{
pub(crate) fn new() -> Self {
let mut component_types = Vec::new();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this maybe not be a set, since FromQuery can have overlapping components?

Expand Down Expand Up @@ -136,7 +49,7 @@ where

impl<Q> Iterator for QueryIter<Q>
where
Q: Query,
Q: FromQuery,
{
type Item = (Entity, Q::Item);

Expand All @@ -145,16 +58,19 @@ where
return None;
}

let entity_id =
EntityId(unsafe { *(self.data.entities_ptr.as_mut_ptr()).add(self.entity_index) });
let entity = Entity::new(entity_id);

let components = unsafe {
let mut component_index = 0;
Q::get_unchecked(
entity_id.0,
&self.data,
&mut component_index,
&mut self.component_offsets,
)
};
let entity_id = unsafe { *(self.data.entities_ptr.as_mut_ptr()).add(self.entity_index) };
let entity = Entity::new(EntityId(entity_id));

self.entity_index += 1;

Expand Down
Loading