Skip to content

Commit 1ec4e78

Browse files
committed
ecs: Fix panic when query contains unknown components
1 parent f5ec9e0 commit 1ec4e78

File tree

7 files changed

+32
-7
lines changed

7 files changed

+32
-7
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

quill/ecs/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,6 @@ edition = "2018"
88
ahash = "0.7"
99
arrayvec = "0.5"
1010
itertools = "0.10"
11+
once_cell = "1"
1112
thiserror = "1"
13+
thread_local= "1"

quill/ecs/src/ecs.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ impl Ecs {
145145
///
146146
/// Returns an iterator over tuples of `(entity, components)`.
147147
pub fn query<'w, 'q, Q: QueryTuple<'w>>(&'w self) -> Query<'w, 'q, Q> {
148-
let sparse_sets = Q::sparse_sets(&self.components).unwrap_or_else(|| todo!());
148+
let sparse_sets = Q::sparse_sets(&self.components);
149149
let sparse_set_refs: Vec<_> = sparse_sets.iter().map(|set| set.to_ref()).collect();
150150
let dense_indices = Q::dense_indices();
151151

quill/ecs/src/query.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ pub trait QueryTuple<'a> {
131131
type Output: 'a;
132132

133133
// avoiding allocations here is blocked on const generics and/or GATs
134-
fn sparse_sets(components: &Components) -> Option<Vec<&SparseSetStorage>>;
134+
fn sparse_sets(components: &Components) -> Vec<&SparseSetStorage>;
135135

136136
fn dense_indices() -> Vec<Cell<u32>>;
137137

@@ -153,12 +153,12 @@ macro_rules! query_tuple_impl {
153153
impl <'a, $($ty: QueryParameter<'a>),*> QueryTuple<'a> for ($($ty),*) {
154154
type Output = ($($ty::Output),*);
155155

156-
fn sparse_sets(components: &Components) -> Option<Vec<&SparseSetStorage>> {
157-
Some(vec![
156+
fn sparse_sets(components: &Components) -> Vec<&SparseSetStorage> {
157+
vec![
158158
$(
159-
components.storage_for::<$ty::Component>().ok()?,
159+
components.storage_for::<$ty::Component>().unwrap_or_else(|_| SparseSetStorage::empty()),
160160
)*
161-
])
161+
]
162162
}
163163

164164
fn dense_indices() -> Vec<Cell<u32>> {

quill/ecs/src/storage/blob_array.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,8 @@ impl Drop for BlobArray {
188188
}
189189
}
190190

191+
unsafe impl Send for BlobArray {}
192+
191193
#[cfg(test)]
192194
mod tests {
193195
use super::*;

quill/ecs/src/storage/sparse_set.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,18 @@ use std::{
66
ptr::NonNull,
77
};
88

9+
use once_cell::sync::Lazy;
10+
use thread_local::ThreadLocal;
11+
912
use crate::{
1013
borrow::BorrowFlag,
1114
component::{self, ComponentMeta},
1215
};
1316

1417
use super::{blob_array::BlobArray, component_vec::ComponentVec};
1518

19+
static EMPTY: Lazy<ThreadLocal<SparseSetStorage>> = Lazy::new(ThreadLocal::new);
20+
1621
/// Stores components in a sparse set.
1722
pub struct SparseSetStorage {
1823
// Data structure invariant: if `dense[sparse[i]] == i`,
@@ -37,6 +42,13 @@ impl SparseSetStorage {
3742
}
3843
}
3944

45+
/// The empty sparse set.
46+
/// Attempting to access components from this sparse
47+
/// set will usually cause a panic.
48+
pub fn empty() -> &'static Self {
49+
EMPTY.get_or(|| SparseSetStorage::new(ComponentMeta::of::<()>()))
50+
}
51+
4052
pub fn insert<T: 'static>(&mut self, index: u32, value: T) {
4153
self.assert_type_matches::<T>();
4254
let value = MaybeUninit::new(value);

quill/ecs/tests/ecs.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ fn query_big_ecs_after_despawn() {
106106
let mut ecs = Ecs::new();
107107

108108
let mut entities = Vec::new();
109-
for i in 0..10_000usize {
109+
for i in 0..100usize {
110110
let mut builder = EntityBuilder::new();
111111
if i % 3 == 0 {
112112
builder.add(format!("entity #{}", i));
@@ -130,3 +130,10 @@ fn query_big_ecs_after_despawn() {
130130

131131
assert_eq!(queried.len(), entities.len());
132132
}
133+
134+
#[test]
135+
fn empty_query() {
136+
let ecs = Ecs::new();
137+
138+
assert_eq!(ecs.query::<&i32>().iter().count(), 0);
139+
}

0 commit comments

Comments
 (0)