diff --git a/quill/api/src/hlist.rs b/quill/api/src/hlist.rs
new file mode 100644
index 000000000..49bda1fc3
--- /dev/null
+++ b/quill/api/src/hlist.rs
@@ -0,0 +1,174 @@
+use std::marker::PhantomData;
+
+pub struct HCons
{
+ pub head: Head,
+ pub tail: Tail,
+}
+
+pub trait HList: Sized {
+ type Tuple: Tuple;
+
+ fn flatten(self) -> Self::Tuple;
+}
+
+pub trait Tuple: Sized {
+ type HList: HList;
+
+ fn hlist(self) -> Self::HList;
+}
+
+impl HList for () {
+ type Tuple = ();
+
+ #[inline]
+ fn flatten(self) -> Self::Tuple {}
+}
+
+impl Tuple for () {
+ type HList = ();
+
+ #[inline]
+ fn hlist(self) -> Self::HList {}
+}
+
+macro_rules! HList {
+ () => { () };
+ ($head:ty $(,$tail:ty)* $(,)?) => {
+ HCons<$head, HList!($($tail),*)>
+ };
+}
+
+macro_rules! hlist_pat {
+ () => { () };
+ ($head:ident $(,$tail:ident)* $(,)?) => {
+ HCons {
+ head: $head,
+ tail: hlist_pat!($($tail),*)
+ }
+ };
+}
+
+macro_rules! impl_tuple {
+ ($head:ident $(,$tail:ident)* $(,)?) => {
+ impl<$head $(,$tail)*> HList for HList!($head $(,$tail)*) {
+ type Tuple = ($head, $($tail),*);
+
+ #[inline]
+ fn flatten(self) -> Self::Tuple {
+ #[allow(non_snake_case)]
+ let hlist_pat!($head, $($tail),*) = self;
+ ($head, $($tail),*)
+ }
+ }
+
+ impl<$head $(,$tail)*> Tuple for ($head, $($tail),*) {
+ type HList = HCons<$head, <($($tail,)*) as Tuple>::HList>;
+
+ #[inline]
+ fn hlist(self) -> Self::HList {
+ #[allow(non_snake_case)]
+ let ($head, $($tail),*) = self;
+ HCons {
+ head: $head,
+ tail: ($($tail,)*).hlist(),
+ }
+ }
+ }
+ };
+}
+
+macro_rules! smaller_tuples_too {
+ ($macro:ident, $head:ident $(,)?) => {
+ $macro!($head);
+ };
+ ($macro:ident, $head:ident, $($tail:ident),* $(,)?) => {
+ $macro!($head, $($tail),*);
+ smaller_tuples_too!($macro, $($tail),*);
+ };
+}
+
+smaller_tuples_too!(
+ impl_tuple, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18,
+ T19, T20
+);
+
+pub trait Plucker {
+ type Remainder;
+
+ fn pluck(self) -> (Target, Self::Remainder);
+}
+
+pub trait PluckerRef {
+ fn pluck(&self) -> &Target;
+}
+
+pub trait PluckerMut {
+ fn pluck(&mut self) -> &mut Target;
+}
+
+pub struct Here();
+
+impl Plucker for HCons {
+ type Remainder = Tail;
+
+ #[inline]
+ fn pluck(self) -> (Target, Self::Remainder) {
+ (self.head, self.tail)
+ }
+}
+
+impl PluckerRef for HCons<&'_ Target, Tail> {
+ #[inline]
+ fn pluck(&self) -> &Target {
+ &self.head
+ }
+}
+
+impl PluckerMut for HCons<&'_ mut Target, Tail> {
+ #[inline]
+ fn pluck(&mut self) -> &mut Target {
+ &mut self.head
+ }
+}
+
+pub struct There(PhantomData);
+
+impl Plucker> for HCons
+where
+ Tail: Plucker,
+{
+ type Remainder = HCons>::Remainder>;
+
+ #[inline]
+ fn pluck(self) -> (Target, Self::Remainder) {
+ let (target, tail_remainder): (Target, >::Remainder) =
+ >::pluck(self.tail);
+ (
+ target,
+ HCons {
+ head: self.head,
+ tail: tail_remainder,
+ },
+ )
+ }
+}
+
+impl PluckerRef> for HCons
+where
+ Tail: PluckerRef,
+{
+ #[inline]
+ fn pluck(&self) -> &Target {
+ >::pluck(&self.tail)
+ }
+}
+
+impl PluckerMut> for HCons
+where
+ Tail: PluckerMut,
+{
+ #[inline]
+ fn pluck(&mut self) -> &mut Target {
+ >::pluck(&mut self.tail)
+ }
+}
diff --git a/quill/api/src/lib.rs b/quill/api/src/lib.rs
index 658995958..3b6d43727 100644
--- a/quill/api/src/lib.rs
+++ b/quill/api/src/lib.rs
@@ -4,12 +4,15 @@ pub mod entities;
mod entity;
mod entity_builder;
mod game;
+pub mod hlist;
pub mod query;
+pub mod send_message;
mod setup;
pub use entity::{Entity, EntityId};
pub use entity_builder::EntityBuilder;
pub use game::Game;
+pub use hlist::*;
pub use setup::Setup;
#[doc(inline)]
diff --git a/quill/api/src/query.rs b/quill/api/src/query.rs
index 64da4919e..7c8170ed7 100644
--- a/quill/api/src/query.rs
+++ b/quill/api/src/query.rs
@@ -4,7 +4,7 @@ use std::{marker::PhantomData, mem::MaybeUninit};
use quill_common::{entity::QueryData, Component, HostComponent, PointerMut};
-use crate::{Entity, EntityId};
+use crate::{Entity, EntityId, Tuple};
/// A type that can be used for a query.
///
@@ -65,24 +65,19 @@ where
macro_rules! impl_query_tuple {
($($query:ident),* $(,)?) => {
impl <$($query: Query),*> Query for ($($query,)*) {
- type Item = ($($query::Item),*);
+ type Item = ($($query::Item,)*);
fn add_component_types(types: &mut Vec) {
- $(
- $query::add_component_types(types);
- )*
+ $($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)
- ),*
- )
+ ($($query::get_unchecked(data, component_index, component_offsets),)*)
}
}
}
}
+impl_query_tuple!(A);
impl_query_tuple!(A, B);
impl_query_tuple!(A, B, C);
impl_query_tuple!(A, B, C, D);
@@ -137,6 +132,7 @@ where
impl Iterator for QueryIter
where
Q: Query,
+ ::Item: Tuple,
{
type Item = (Entity, Q::Item);
diff --git a/quill/api/src/send_message.rs b/quill/api/src/send_message.rs
new file mode 100644
index 000000000..43cc809c3
--- /dev/null
+++ b/quill/api/src/send_message.rs
@@ -0,0 +1,17 @@
+use quill_common::entities::Player;
+
+use crate::{Entity, PluckerRef, Tuple};
+
+pub trait SendMessage {
+ fn send_message(&self, message: &str);
+}
+
+impl SendMessage for (&'_ Entity, T)
+where
+ T: Tuple,
+ ::HList: PluckerRef,
+{
+ fn send_message(&self, _message: &str) {
+ // let _player: &Player = PluckerRef::::pluck(self.1.hlist());
+ }
+}
diff --git a/quill/example-plugins/block-place/src/lib.rs b/quill/example-plugins/block-place/src/lib.rs
index fa1be6692..651d890bf 100644
--- a/quill/example-plugins/block-place/src/lib.rs
+++ b/quill/example-plugins/block-place/src/lib.rs
@@ -16,7 +16,7 @@ impl Plugin for BlockPlace {
}
fn system(_plugin: &mut BlockPlace, game: &mut Game) {
- for (_entity, _event) in game.query::<&BlockPlacementEvent>() {
+ for (_entity, _event) in game.query::<(&BlockPlacementEvent,)>() {
println!("A client has placed a block!");
}
}
diff --git a/quill/example-plugins/query-entities/src/lib.rs b/quill/example-plugins/query-entities/src/lib.rs
index c5851b0af..791d137e4 100644
--- a/quill/example-plugins/query-entities/src/lib.rs
+++ b/quill/example-plugins/query-entities/src/lib.rs
@@ -1,7 +1,11 @@
//! An example plugin that spawns 10,000 entities
//! on startup, then moves them each tick using a query.
-use quill::{entities::PiglinBrute, EntityInit, Game, Plugin, Position};
+use quill::{
+ entities::{PiglinBrute, Player},
+ send_message::SendMessage,
+ EntityInit, Game, Plugin, Position,
+};
use rand::Rng;
quill::plugin!(QueryEntities);
@@ -47,4 +51,8 @@ fn query_system(plugin: &mut QueryEntities, game: &mut Game) {
..position
});
}
+
+ for (entity, (player,)) in game.query::<(&Player,)>() {
+ (&entity, (&player,)).send_message("foo");
+ }
}