From 20b35b203bd379371fac2248655498cedd803b32 Mon Sep 17 00:00:00 2001 From: Martin Lange <44003176+mlange-42@users.noreply.github.com> Date: Fri, 12 Jan 2024 13:41:09 +0100 Subject: [PATCH] Extend generic API to up to 12 components (#324) --- CHANGELOG.md | 3 +- generic/generate/main.go | 8 +- generic/map_generated.go | 884 ++++++++++++++++++++++++++++++++++ generic/map_generated_test.go | 416 ++++++++++++++++ generic/mask.go | 36 ++ generic/mask_test.go | 74 +++ generic/query_generated.go | 844 ++++++++++++++++++++++++++++++++ generic/query_test.go | 584 +++++++++++++++++++++- 8 files changed, 2843 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 645b3e18..2f0f2dad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,9 @@ ### Highlights -* Arche now supports 256 instead of 128 component types as well as resource types and engine locks (#313) * Arche supports full world serialization and deserialization, in conjunction with [arche-serde](https://github.com/mlange-42/arche-serde) (#319) +* Supports 256 instead of 128 component types as well as resource types and engine locks (#313) +* Generic API supports up to 12 instead of 8 component types (#324) ### Breaking changes diff --git a/generic/generate/main.go b/generic/generate/main.go index bb321125..04e4f528 100644 --- a/generic/generate/main.go +++ b/generic/generate/main.go @@ -9,10 +9,10 @@ import ( "text/template" ) -var typeLetters = []string{"A", "B", "C", "D", "E", "F", "G", "H"} -var variableLetters = []string{"a", "b", "c", "d", "e", "f", "g", "h"} -var numbers = []string{"0", "1", "2", "3", "4", "5", "6", "7", "8"} -var numberStr = []string{"zero", "one", "two", "three", "four", "five", "six", "seven", "eight"} +var typeLetters = []string{"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L"} +var variableLetters = []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l"} +var numbers = []string{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"} +var numberStr = []string{"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve"} type query struct { Index int diff --git a/generic/map_generated.go b/generic/map_generated.go index 7e583fb4..943a566d 100644 --- a/generic/map_generated.go +++ b/generic/map_generated.go @@ -1387,3 +1387,887 @@ func (m *Map8[A, B, C, D, E, F, G, H]) RemoveEntities(exclusive bool) int { } return m.world.Batch().RemoveEntities(m.mask) } + +////////////////////////////////////////////////////////////////////////// + +// Map9 is a helper for mapping nine components. +// +// # Example +// +// world := ecs.NewWorld() +// +// mapper := NewMap9[A, B, C, D, E, F, G, H, I](&world) +// +// entity := mapper.NewEntity() +// a, b, c, d, e, f, g, h, i := mapper.Get(entity) +type Map9[A any, B any, C any, D any, E any, F any, G any, H any, I any] struct { + world *ecs.World + mask ecs.Mask + relation int8 + ids []ecs.ID + id0 ecs.ID + id1 ecs.ID + id2 ecs.ID + id3 ecs.ID + id4 ecs.ID + id5 ecs.ID + id6 ecs.ID + id7 ecs.ID + id8 ecs.ID +} + +// NewMap9 creates a new Map9 object. +// +// The optional argument can be used to set an [ecs.Relation] component type. +func NewMap9[A any, B any, C any, D any, E any, F any, G any, H any, I any](w *ecs.World, relation ...Comp) Map9[A, B, C, D, E, F, G, H, I] { + m := Map9[A, B, C, D, E, F, G, H, I]{ + world: w, + id0: ecs.ComponentID[A](w), + id1: ecs.ComponentID[B](w), + id2: ecs.ComponentID[C](w), + id3: ecs.ComponentID[D](w), + id4: ecs.ComponentID[E](w), + id5: ecs.ComponentID[F](w), + id6: ecs.ComponentID[G](w), + id7: ecs.ComponentID[H](w), + id8: ecs.ComponentID[I](w), + } + m.ids = []ecs.ID{m.id0, m.id1, m.id2, m.id3, m.id4, m.id5, m.id6, m.id7, m.id8} + m.mask = ecs.All(m.ids...) + m.relation = -1 + if len(relation) > 0 { + m.relation = int8(ecs.TypeID(w, relation[0])) + } + return m +} + +// Get all the Map9's components for the given entity. +// +// See [Map9.GetUnchecked] for an optimized version for static entities. +// See also [ecs.World.Get]. +func (m *Map9[A, B, C, D, E, F, G, H, I]) Get(entity ecs.Entity) (*A, *B, *C, *D, *E, *F, *G, *H, *I) { + return (*A)(m.world.Get(entity, m.id0)), + (*B)(m.world.GetUnchecked(entity, m.id1)), + (*C)(m.world.GetUnchecked(entity, m.id2)), + (*D)(m.world.GetUnchecked(entity, m.id3)), + (*E)(m.world.GetUnchecked(entity, m.id4)), + (*F)(m.world.GetUnchecked(entity, m.id5)), + (*G)(m.world.GetUnchecked(entity, m.id6)), + (*H)(m.world.GetUnchecked(entity, m.id7)), + (*I)(m.world.GetUnchecked(entity, m.id8)) +} + +// GetUnchecked all the Map9's components for the given entity. +// +// GetUnchecked is an optimized version of [Map9.Get], +// for cases where entities are static or checked with [ecs.World.Alive] in user code. +// +// See also [ecs.World.GetUnchecked]. +func (m *Map9[A, B, C, D, E, F, G, H, I]) GetUnchecked(entity ecs.Entity) (*A, *B, *C, *D, *E, *F, *G, *H, *I) { + return (*A)(m.world.GetUnchecked(entity, m.id0)), + (*B)(m.world.GetUnchecked(entity, m.id1)), + (*C)(m.world.GetUnchecked(entity, m.id2)), + (*D)(m.world.GetUnchecked(entity, m.id3)), + (*E)(m.world.GetUnchecked(entity, m.id4)), + (*F)(m.world.GetUnchecked(entity, m.id5)), + (*G)(m.world.GetUnchecked(entity, m.id6)), + (*H)(m.world.GetUnchecked(entity, m.id7)), + (*I)(m.world.GetUnchecked(entity, m.id8)) +} + +// New creates a new [ecs.Entity] with the Map9's components. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map9's [ecs.Relation]. +// +// See also [ecs.World.NewEntity]. +func (m *Map9[A, B, C, D, E, F, G, H, I]) New(target ...ecs.Entity) ecs.Entity { + return newEntity(m.world, m.ids, m.relation, target...) +} + +// NewBatch creates entities with the Map9's components. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map9's [ecs.Relation]. +// +// See also [Map9.NewBatchQ] and [ecs.Batch.NewBatch]. +func (m *Map9[A, B, C, D, E, F, G, H, I]) NewBatch(count int, target ...ecs.Entity) { + newBatch(m.world, count, m.ids, m.relation, target...) +} + +// NewBatchQ creates entities with the Map9's components. +// It returns a [Query9] over the new entities. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map9's [ecs.Relation]. +// +// Listener notification is delayed until the query is closed of fully iterated. +// +// See also [Map9.NewBatch] and [ecs.Builder.NewBatchQ]. +func (m *Map9[A, B, C, D, E, F, G, H, I]) NewBatchQ(count int, target ...ecs.Entity) Query9[A, B, C, D, E, F, G, H, I] { + query := newQuery(m.world, count, m.ids, m.relation, target...) + return Query9[A, B, C, D, E, F, G, H, I]{ + Query: query, + id0: m.id0, + id1: m.id1, + id2: m.id2, + id3: m.id3, + id4: m.id4, + id5: m.id5, + id6: m.id6, + id7: m.id7, + id8: m.id8, + } +} + +// NewWith creates a new [ecs.Entity] with the Map9's components, using the supplied values. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map9's [ecs.Relation]. +// +// See also [ecs.NewBuilderWith]. +func (m *Map9[A, B, C, D, E, F, G, H, I]) NewWith(a *A, b *B, c *C, d *D, e *E, f *F, g *G, h *H, i *I, target ...ecs.Entity) ecs.Entity { + if len(target) == 0 { + return m.world.NewEntityWith(ecs.Component{ID: m.id0, Comp: a}, + ecs.Component{ID: m.id1, Comp: b}, + ecs.Component{ID: m.id2, Comp: c}, + ecs.Component{ID: m.id3, Comp: d}, + ecs.Component{ID: m.id4, Comp: e}, + ecs.Component{ID: m.id5, Comp: f}, + ecs.Component{ID: m.id6, Comp: g}, + ecs.Component{ID: m.id7, Comp: h}, + ecs.Component{ID: m.id8, Comp: i}, + ) + } + if m.relation < 0 { + panic("map has no relation defined") + } + return ecs.NewBuilderWith(m.world, ecs.Component{ID: m.id0, Comp: a}, + ecs.Component{ID: m.id1, Comp: b}, + ecs.Component{ID: m.id2, Comp: c}, + ecs.Component{ID: m.id3, Comp: d}, + ecs.Component{ID: m.id4, Comp: e}, + ecs.Component{ID: m.id5, Comp: f}, + ecs.Component{ID: m.id6, Comp: g}, + ecs.Component{ID: m.id7, Comp: h}, + ecs.Component{ID: m.id8, Comp: i}, + ).WithRelation(uint8(m.relation)).New(target[0]) +} + +// Add the Map9's components to the given entity. +// +// See also [ecs.World.Add]. +func (m *Map9[A, B, C, D, E, F, G, H, I]) Add(entity ecs.Entity) { + m.world.Add(entity, m.ids...) +} + +// Assign the Map9's components to the given entity, using the supplied values. +// +// See also [ecs.World.Assign]. +func (m *Map9[A, B, C, D, E, F, G, H, I]) Assign(entity ecs.Entity, a *A, b *B, c *C, d *D, e *E, f *F, g *G, h *H, i *I) { + m.world.Assign(entity, + ecs.Component{ID: m.id0, Comp: a}, + ecs.Component{ID: m.id1, Comp: b}, + ecs.Component{ID: m.id2, Comp: c}, + ecs.Component{ID: m.id3, Comp: d}, + ecs.Component{ID: m.id4, Comp: e}, + ecs.Component{ID: m.id5, Comp: f}, + ecs.Component{ID: m.id6, Comp: g}, + ecs.Component{ID: m.id7, Comp: h}, + ecs.Component{ID: m.id8, Comp: i}, + ) +} + +// Remove the Map9's components from the given entity. +// +// See also [ecs.World.Remove]. +func (m *Map9[A, B, C, D, E, F, G, H, I]) Remove(entity ecs.Entity) { + m.world.Remove(entity, m.ids...) +} + +// RemoveEntities removes all entities from the world that match the Map9's components. +// +// The argument determines whether to match the components exactly (i.e. no other components are allowed), +// or to use a simple filter that does not restrict further components. +// +// Returns the number of removed entities. +// +// See also [ecs.World.NewEntityWith]. +func (m *Map9[A, B, C, D, E, F, G, H, I]) RemoveEntities(exclusive bool) int { + if exclusive { + filter := m.mask.Exclusive() + return m.world.Batch().RemoveEntities(&filter) + } + return m.world.Batch().RemoveEntities(m.mask) +} + +////////////////////////////////////////////////////////////////////////// + +// Map10 is a helper for mapping ten components. +// +// # Example +// +// world := ecs.NewWorld() +// +// mapper := NewMap10[A, B, C, D, E, F, G, H, I, J](&world) +// +// entity := mapper.NewEntity() +// a, b, c, d, e, f, g, h, i, j := mapper.Get(entity) +type Map10[A any, B any, C any, D any, E any, F any, G any, H any, I any, J any] struct { + world *ecs.World + mask ecs.Mask + relation int8 + ids []ecs.ID + id0 ecs.ID + id1 ecs.ID + id2 ecs.ID + id3 ecs.ID + id4 ecs.ID + id5 ecs.ID + id6 ecs.ID + id7 ecs.ID + id8 ecs.ID + id9 ecs.ID +} + +// NewMap10 creates a new Map10 object. +// +// The optional argument can be used to set an [ecs.Relation] component type. +func NewMap10[A any, B any, C any, D any, E any, F any, G any, H any, I any, J any](w *ecs.World, relation ...Comp) Map10[A, B, C, D, E, F, G, H, I, J] { + m := Map10[A, B, C, D, E, F, G, H, I, J]{ + world: w, + id0: ecs.ComponentID[A](w), + id1: ecs.ComponentID[B](w), + id2: ecs.ComponentID[C](w), + id3: ecs.ComponentID[D](w), + id4: ecs.ComponentID[E](w), + id5: ecs.ComponentID[F](w), + id6: ecs.ComponentID[G](w), + id7: ecs.ComponentID[H](w), + id8: ecs.ComponentID[I](w), + id9: ecs.ComponentID[J](w), + } + m.ids = []ecs.ID{m.id0, m.id1, m.id2, m.id3, m.id4, m.id5, m.id6, m.id7, m.id8, m.id9} + m.mask = ecs.All(m.ids...) + m.relation = -1 + if len(relation) > 0 { + m.relation = int8(ecs.TypeID(w, relation[0])) + } + return m +} + +// Get all the Map10's components for the given entity. +// +// See [Map10.GetUnchecked] for an optimized version for static entities. +// See also [ecs.World.Get]. +func (m *Map10[A, B, C, D, E, F, G, H, I, J]) Get(entity ecs.Entity) (*A, *B, *C, *D, *E, *F, *G, *H, *I, *J) { + return (*A)(m.world.Get(entity, m.id0)), + (*B)(m.world.GetUnchecked(entity, m.id1)), + (*C)(m.world.GetUnchecked(entity, m.id2)), + (*D)(m.world.GetUnchecked(entity, m.id3)), + (*E)(m.world.GetUnchecked(entity, m.id4)), + (*F)(m.world.GetUnchecked(entity, m.id5)), + (*G)(m.world.GetUnchecked(entity, m.id6)), + (*H)(m.world.GetUnchecked(entity, m.id7)), + (*I)(m.world.GetUnchecked(entity, m.id8)), + (*J)(m.world.GetUnchecked(entity, m.id9)) +} + +// GetUnchecked all the Map10's components for the given entity. +// +// GetUnchecked is an optimized version of [Map10.Get], +// for cases where entities are static or checked with [ecs.World.Alive] in user code. +// +// See also [ecs.World.GetUnchecked]. +func (m *Map10[A, B, C, D, E, F, G, H, I, J]) GetUnchecked(entity ecs.Entity) (*A, *B, *C, *D, *E, *F, *G, *H, *I, *J) { + return (*A)(m.world.GetUnchecked(entity, m.id0)), + (*B)(m.world.GetUnchecked(entity, m.id1)), + (*C)(m.world.GetUnchecked(entity, m.id2)), + (*D)(m.world.GetUnchecked(entity, m.id3)), + (*E)(m.world.GetUnchecked(entity, m.id4)), + (*F)(m.world.GetUnchecked(entity, m.id5)), + (*G)(m.world.GetUnchecked(entity, m.id6)), + (*H)(m.world.GetUnchecked(entity, m.id7)), + (*I)(m.world.GetUnchecked(entity, m.id8)), + (*J)(m.world.GetUnchecked(entity, m.id9)) +} + +// New creates a new [ecs.Entity] with the Map10's components. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map10's [ecs.Relation]. +// +// See also [ecs.World.NewEntity]. +func (m *Map10[A, B, C, D, E, F, G, H, I, J]) New(target ...ecs.Entity) ecs.Entity { + return newEntity(m.world, m.ids, m.relation, target...) +} + +// NewBatch creates entities with the Map10's components. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map10's [ecs.Relation]. +// +// See also [Map10.NewBatchQ] and [ecs.Batch.NewBatch]. +func (m *Map10[A, B, C, D, E, F, G, H, I, J]) NewBatch(count int, target ...ecs.Entity) { + newBatch(m.world, count, m.ids, m.relation, target...) +} + +// NewBatchQ creates entities with the Map10's components. +// It returns a [Query10] over the new entities. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map10's [ecs.Relation]. +// +// Listener notification is delayed until the query is closed of fully iterated. +// +// See also [Map10.NewBatch] and [ecs.Builder.NewBatchQ]. +func (m *Map10[A, B, C, D, E, F, G, H, I, J]) NewBatchQ(count int, target ...ecs.Entity) Query10[A, B, C, D, E, F, G, H, I, J] { + query := newQuery(m.world, count, m.ids, m.relation, target...) + return Query10[A, B, C, D, E, F, G, H, I, J]{ + Query: query, + id0: m.id0, + id1: m.id1, + id2: m.id2, + id3: m.id3, + id4: m.id4, + id5: m.id5, + id6: m.id6, + id7: m.id7, + id8: m.id8, + id9: m.id9, + } +} + +// NewWith creates a new [ecs.Entity] with the Map10's components, using the supplied values. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map10's [ecs.Relation]. +// +// See also [ecs.NewBuilderWith]. +func (m *Map10[A, B, C, D, E, F, G, H, I, J]) NewWith(a *A, b *B, c *C, d *D, e *E, f *F, g *G, h *H, i *I, j *J, target ...ecs.Entity) ecs.Entity { + if len(target) == 0 { + return m.world.NewEntityWith(ecs.Component{ID: m.id0, Comp: a}, + ecs.Component{ID: m.id1, Comp: b}, + ecs.Component{ID: m.id2, Comp: c}, + ecs.Component{ID: m.id3, Comp: d}, + ecs.Component{ID: m.id4, Comp: e}, + ecs.Component{ID: m.id5, Comp: f}, + ecs.Component{ID: m.id6, Comp: g}, + ecs.Component{ID: m.id7, Comp: h}, + ecs.Component{ID: m.id8, Comp: i}, + ecs.Component{ID: m.id9, Comp: j}, + ) + } + if m.relation < 0 { + panic("map has no relation defined") + } + return ecs.NewBuilderWith(m.world, ecs.Component{ID: m.id0, Comp: a}, + ecs.Component{ID: m.id1, Comp: b}, + ecs.Component{ID: m.id2, Comp: c}, + ecs.Component{ID: m.id3, Comp: d}, + ecs.Component{ID: m.id4, Comp: e}, + ecs.Component{ID: m.id5, Comp: f}, + ecs.Component{ID: m.id6, Comp: g}, + ecs.Component{ID: m.id7, Comp: h}, + ecs.Component{ID: m.id8, Comp: i}, + ecs.Component{ID: m.id9, Comp: j}, + ).WithRelation(uint8(m.relation)).New(target[0]) +} + +// Add the Map10's components to the given entity. +// +// See also [ecs.World.Add]. +func (m *Map10[A, B, C, D, E, F, G, H, I, J]) Add(entity ecs.Entity) { + m.world.Add(entity, m.ids...) +} + +// Assign the Map10's components to the given entity, using the supplied values. +// +// See also [ecs.World.Assign]. +func (m *Map10[A, B, C, D, E, F, G, H, I, J]) Assign(entity ecs.Entity, a *A, b *B, c *C, d *D, e *E, f *F, g *G, h *H, i *I, j *J) { + m.world.Assign(entity, + ecs.Component{ID: m.id0, Comp: a}, + ecs.Component{ID: m.id1, Comp: b}, + ecs.Component{ID: m.id2, Comp: c}, + ecs.Component{ID: m.id3, Comp: d}, + ecs.Component{ID: m.id4, Comp: e}, + ecs.Component{ID: m.id5, Comp: f}, + ecs.Component{ID: m.id6, Comp: g}, + ecs.Component{ID: m.id7, Comp: h}, + ecs.Component{ID: m.id8, Comp: i}, + ecs.Component{ID: m.id9, Comp: j}, + ) +} + +// Remove the Map10's components from the given entity. +// +// See also [ecs.World.Remove]. +func (m *Map10[A, B, C, D, E, F, G, H, I, J]) Remove(entity ecs.Entity) { + m.world.Remove(entity, m.ids...) +} + +// RemoveEntities removes all entities from the world that match the Map10's components. +// +// The argument determines whether to match the components exactly (i.e. no other components are allowed), +// or to use a simple filter that does not restrict further components. +// +// Returns the number of removed entities. +// +// See also [ecs.World.NewEntityWith]. +func (m *Map10[A, B, C, D, E, F, G, H, I, J]) RemoveEntities(exclusive bool) int { + if exclusive { + filter := m.mask.Exclusive() + return m.world.Batch().RemoveEntities(&filter) + } + return m.world.Batch().RemoveEntities(m.mask) +} + +////////////////////////////////////////////////////////////////////////// + +// Map11 is a helper for mapping eleven components. +// +// # Example +// +// world := ecs.NewWorld() +// +// mapper := NewMap11[A, B, C, D, E, F, G, H, I, J, K](&world) +// +// entity := mapper.NewEntity() +// a, b, c, d, e, f, g, h, i, j, k := mapper.Get(entity) +type Map11[A any, B any, C any, D any, E any, F any, G any, H any, I any, J any, K any] struct { + world *ecs.World + mask ecs.Mask + relation int8 + ids []ecs.ID + id0 ecs.ID + id1 ecs.ID + id2 ecs.ID + id3 ecs.ID + id4 ecs.ID + id5 ecs.ID + id6 ecs.ID + id7 ecs.ID + id8 ecs.ID + id9 ecs.ID + id10 ecs.ID +} + +// NewMap11 creates a new Map11 object. +// +// The optional argument can be used to set an [ecs.Relation] component type. +func NewMap11[A any, B any, C any, D any, E any, F any, G any, H any, I any, J any, K any](w *ecs.World, relation ...Comp) Map11[A, B, C, D, E, F, G, H, I, J, K] { + m := Map11[A, B, C, D, E, F, G, H, I, J, K]{ + world: w, + id0: ecs.ComponentID[A](w), + id1: ecs.ComponentID[B](w), + id2: ecs.ComponentID[C](w), + id3: ecs.ComponentID[D](w), + id4: ecs.ComponentID[E](w), + id5: ecs.ComponentID[F](w), + id6: ecs.ComponentID[G](w), + id7: ecs.ComponentID[H](w), + id8: ecs.ComponentID[I](w), + id9: ecs.ComponentID[J](w), + id10: ecs.ComponentID[K](w), + } + m.ids = []ecs.ID{m.id0, m.id1, m.id2, m.id3, m.id4, m.id5, m.id6, m.id7, m.id8, m.id9, m.id10} + m.mask = ecs.All(m.ids...) + m.relation = -1 + if len(relation) > 0 { + m.relation = int8(ecs.TypeID(w, relation[0])) + } + return m +} + +// Get all the Map11's components for the given entity. +// +// See [Map11.GetUnchecked] for an optimized version for static entities. +// See also [ecs.World.Get]. +func (m *Map11[A, B, C, D, E, F, G, H, I, J, K]) Get(entity ecs.Entity) (*A, *B, *C, *D, *E, *F, *G, *H, *I, *J, *K) { + return (*A)(m.world.Get(entity, m.id0)), + (*B)(m.world.GetUnchecked(entity, m.id1)), + (*C)(m.world.GetUnchecked(entity, m.id2)), + (*D)(m.world.GetUnchecked(entity, m.id3)), + (*E)(m.world.GetUnchecked(entity, m.id4)), + (*F)(m.world.GetUnchecked(entity, m.id5)), + (*G)(m.world.GetUnchecked(entity, m.id6)), + (*H)(m.world.GetUnchecked(entity, m.id7)), + (*I)(m.world.GetUnchecked(entity, m.id8)), + (*J)(m.world.GetUnchecked(entity, m.id9)), + (*K)(m.world.GetUnchecked(entity, m.id10)) +} + +// GetUnchecked all the Map11's components for the given entity. +// +// GetUnchecked is an optimized version of [Map11.Get], +// for cases where entities are static or checked with [ecs.World.Alive] in user code. +// +// See also [ecs.World.GetUnchecked]. +func (m *Map11[A, B, C, D, E, F, G, H, I, J, K]) GetUnchecked(entity ecs.Entity) (*A, *B, *C, *D, *E, *F, *G, *H, *I, *J, *K) { + return (*A)(m.world.GetUnchecked(entity, m.id0)), + (*B)(m.world.GetUnchecked(entity, m.id1)), + (*C)(m.world.GetUnchecked(entity, m.id2)), + (*D)(m.world.GetUnchecked(entity, m.id3)), + (*E)(m.world.GetUnchecked(entity, m.id4)), + (*F)(m.world.GetUnchecked(entity, m.id5)), + (*G)(m.world.GetUnchecked(entity, m.id6)), + (*H)(m.world.GetUnchecked(entity, m.id7)), + (*I)(m.world.GetUnchecked(entity, m.id8)), + (*J)(m.world.GetUnchecked(entity, m.id9)), + (*K)(m.world.GetUnchecked(entity, m.id10)) +} + +// New creates a new [ecs.Entity] with the Map11's components. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map11's [ecs.Relation]. +// +// See also [ecs.World.NewEntity]. +func (m *Map11[A, B, C, D, E, F, G, H, I, J, K]) New(target ...ecs.Entity) ecs.Entity { + return newEntity(m.world, m.ids, m.relation, target...) +} + +// NewBatch creates entities with the Map11's components. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map11's [ecs.Relation]. +// +// See also [Map11.NewBatchQ] and [ecs.Batch.NewBatch]. +func (m *Map11[A, B, C, D, E, F, G, H, I, J, K]) NewBatch(count int, target ...ecs.Entity) { + newBatch(m.world, count, m.ids, m.relation, target...) +} + +// NewBatchQ creates entities with the Map11's components. +// It returns a [Query11] over the new entities. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map11's [ecs.Relation]. +// +// Listener notification is delayed until the query is closed of fully iterated. +// +// See also [Map11.NewBatch] and [ecs.Builder.NewBatchQ]. +func (m *Map11[A, B, C, D, E, F, G, H, I, J, K]) NewBatchQ(count int, target ...ecs.Entity) Query11[A, B, C, D, E, F, G, H, I, J, K] { + query := newQuery(m.world, count, m.ids, m.relation, target...) + return Query11[A, B, C, D, E, F, G, H, I, J, K]{ + Query: query, + id0: m.id0, + id1: m.id1, + id2: m.id2, + id3: m.id3, + id4: m.id4, + id5: m.id5, + id6: m.id6, + id7: m.id7, + id8: m.id8, + id9: m.id9, + id10: m.id10, + } +} + +// NewWith creates a new [ecs.Entity] with the Map11's components, using the supplied values. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map11's [ecs.Relation]. +// +// See also [ecs.NewBuilderWith]. +func (m *Map11[A, B, C, D, E, F, G, H, I, J, K]) NewWith(a *A, b *B, c *C, d *D, e *E, f *F, g *G, h *H, i *I, j *J, k *K, target ...ecs.Entity) ecs.Entity { + if len(target) == 0 { + return m.world.NewEntityWith(ecs.Component{ID: m.id0, Comp: a}, + ecs.Component{ID: m.id1, Comp: b}, + ecs.Component{ID: m.id2, Comp: c}, + ecs.Component{ID: m.id3, Comp: d}, + ecs.Component{ID: m.id4, Comp: e}, + ecs.Component{ID: m.id5, Comp: f}, + ecs.Component{ID: m.id6, Comp: g}, + ecs.Component{ID: m.id7, Comp: h}, + ecs.Component{ID: m.id8, Comp: i}, + ecs.Component{ID: m.id9, Comp: j}, + ecs.Component{ID: m.id10, Comp: k}, + ) + } + if m.relation < 0 { + panic("map has no relation defined") + } + return ecs.NewBuilderWith(m.world, ecs.Component{ID: m.id0, Comp: a}, + ecs.Component{ID: m.id1, Comp: b}, + ecs.Component{ID: m.id2, Comp: c}, + ecs.Component{ID: m.id3, Comp: d}, + ecs.Component{ID: m.id4, Comp: e}, + ecs.Component{ID: m.id5, Comp: f}, + ecs.Component{ID: m.id6, Comp: g}, + ecs.Component{ID: m.id7, Comp: h}, + ecs.Component{ID: m.id8, Comp: i}, + ecs.Component{ID: m.id9, Comp: j}, + ecs.Component{ID: m.id10, Comp: k}, + ).WithRelation(uint8(m.relation)).New(target[0]) +} + +// Add the Map11's components to the given entity. +// +// See also [ecs.World.Add]. +func (m *Map11[A, B, C, D, E, F, G, H, I, J, K]) Add(entity ecs.Entity) { + m.world.Add(entity, m.ids...) +} + +// Assign the Map11's components to the given entity, using the supplied values. +// +// See also [ecs.World.Assign]. +func (m *Map11[A, B, C, D, E, F, G, H, I, J, K]) Assign(entity ecs.Entity, a *A, b *B, c *C, d *D, e *E, f *F, g *G, h *H, i *I, j *J, k *K) { + m.world.Assign(entity, + ecs.Component{ID: m.id0, Comp: a}, + ecs.Component{ID: m.id1, Comp: b}, + ecs.Component{ID: m.id2, Comp: c}, + ecs.Component{ID: m.id3, Comp: d}, + ecs.Component{ID: m.id4, Comp: e}, + ecs.Component{ID: m.id5, Comp: f}, + ecs.Component{ID: m.id6, Comp: g}, + ecs.Component{ID: m.id7, Comp: h}, + ecs.Component{ID: m.id8, Comp: i}, + ecs.Component{ID: m.id9, Comp: j}, + ecs.Component{ID: m.id10, Comp: k}, + ) +} + +// Remove the Map11's components from the given entity. +// +// See also [ecs.World.Remove]. +func (m *Map11[A, B, C, D, E, F, G, H, I, J, K]) Remove(entity ecs.Entity) { + m.world.Remove(entity, m.ids...) +} + +// RemoveEntities removes all entities from the world that match the Map11's components. +// +// The argument determines whether to match the components exactly (i.e. no other components are allowed), +// or to use a simple filter that does not restrict further components. +// +// Returns the number of removed entities. +// +// See also [ecs.World.NewEntityWith]. +func (m *Map11[A, B, C, D, E, F, G, H, I, J, K]) RemoveEntities(exclusive bool) int { + if exclusive { + filter := m.mask.Exclusive() + return m.world.Batch().RemoveEntities(&filter) + } + return m.world.Batch().RemoveEntities(m.mask) +} + +////////////////////////////////////////////////////////////////////////// + +// Map12 is a helper for mapping twelve components. +// +// # Example +// +// world := ecs.NewWorld() +// +// mapper := NewMap12[A, B, C, D, E, F, G, H, I, J, K, L](&world) +// +// entity := mapper.NewEntity() +// a, b, c, d, e, f, g, h, i, j, k, l := mapper.Get(entity) +type Map12[A any, B any, C any, D any, E any, F any, G any, H any, I any, J any, K any, L any] struct { + world *ecs.World + mask ecs.Mask + relation int8 + ids []ecs.ID + id0 ecs.ID + id1 ecs.ID + id2 ecs.ID + id3 ecs.ID + id4 ecs.ID + id5 ecs.ID + id6 ecs.ID + id7 ecs.ID + id8 ecs.ID + id9 ecs.ID + id10 ecs.ID + id11 ecs.ID +} + +// NewMap12 creates a new Map12 object. +// +// The optional argument can be used to set an [ecs.Relation] component type. +func NewMap12[A any, B any, C any, D any, E any, F any, G any, H any, I any, J any, K any, L any](w *ecs.World, relation ...Comp) Map12[A, B, C, D, E, F, G, H, I, J, K, L] { + m := Map12[A, B, C, D, E, F, G, H, I, J, K, L]{ + world: w, + id0: ecs.ComponentID[A](w), + id1: ecs.ComponentID[B](w), + id2: ecs.ComponentID[C](w), + id3: ecs.ComponentID[D](w), + id4: ecs.ComponentID[E](w), + id5: ecs.ComponentID[F](w), + id6: ecs.ComponentID[G](w), + id7: ecs.ComponentID[H](w), + id8: ecs.ComponentID[I](w), + id9: ecs.ComponentID[J](w), + id10: ecs.ComponentID[K](w), + id11: ecs.ComponentID[L](w), + } + m.ids = []ecs.ID{m.id0, m.id1, m.id2, m.id3, m.id4, m.id5, m.id6, m.id7, m.id8, m.id9, m.id10, m.id11} + m.mask = ecs.All(m.ids...) + m.relation = -1 + if len(relation) > 0 { + m.relation = int8(ecs.TypeID(w, relation[0])) + } + return m +} + +// Get all the Map12's components for the given entity. +// +// See [Map12.GetUnchecked] for an optimized version for static entities. +// See also [ecs.World.Get]. +func (m *Map12[A, B, C, D, E, F, G, H, I, J, K, L]) Get(entity ecs.Entity) (*A, *B, *C, *D, *E, *F, *G, *H, *I, *J, *K, *L) { + return (*A)(m.world.Get(entity, m.id0)), + (*B)(m.world.GetUnchecked(entity, m.id1)), + (*C)(m.world.GetUnchecked(entity, m.id2)), + (*D)(m.world.GetUnchecked(entity, m.id3)), + (*E)(m.world.GetUnchecked(entity, m.id4)), + (*F)(m.world.GetUnchecked(entity, m.id5)), + (*G)(m.world.GetUnchecked(entity, m.id6)), + (*H)(m.world.GetUnchecked(entity, m.id7)), + (*I)(m.world.GetUnchecked(entity, m.id8)), + (*J)(m.world.GetUnchecked(entity, m.id9)), + (*K)(m.world.GetUnchecked(entity, m.id10)), + (*L)(m.world.GetUnchecked(entity, m.id11)) +} + +// GetUnchecked all the Map12's components for the given entity. +// +// GetUnchecked is an optimized version of [Map12.Get], +// for cases where entities are static or checked with [ecs.World.Alive] in user code. +// +// See also [ecs.World.GetUnchecked]. +func (m *Map12[A, B, C, D, E, F, G, H, I, J, K, L]) GetUnchecked(entity ecs.Entity) (*A, *B, *C, *D, *E, *F, *G, *H, *I, *J, *K, *L) { + return (*A)(m.world.GetUnchecked(entity, m.id0)), + (*B)(m.world.GetUnchecked(entity, m.id1)), + (*C)(m.world.GetUnchecked(entity, m.id2)), + (*D)(m.world.GetUnchecked(entity, m.id3)), + (*E)(m.world.GetUnchecked(entity, m.id4)), + (*F)(m.world.GetUnchecked(entity, m.id5)), + (*G)(m.world.GetUnchecked(entity, m.id6)), + (*H)(m.world.GetUnchecked(entity, m.id7)), + (*I)(m.world.GetUnchecked(entity, m.id8)), + (*J)(m.world.GetUnchecked(entity, m.id9)), + (*K)(m.world.GetUnchecked(entity, m.id10)), + (*L)(m.world.GetUnchecked(entity, m.id11)) +} + +// New creates a new [ecs.Entity] with the Map12's components. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map12's [ecs.Relation]. +// +// See also [ecs.World.NewEntity]. +func (m *Map12[A, B, C, D, E, F, G, H, I, J, K, L]) New(target ...ecs.Entity) ecs.Entity { + return newEntity(m.world, m.ids, m.relation, target...) +} + +// NewBatch creates entities with the Map12's components. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map12's [ecs.Relation]. +// +// See also [Map12.NewBatchQ] and [ecs.Batch.NewBatch]. +func (m *Map12[A, B, C, D, E, F, G, H, I, J, K, L]) NewBatch(count int, target ...ecs.Entity) { + newBatch(m.world, count, m.ids, m.relation, target...) +} + +// NewBatchQ creates entities with the Map12's components. +// It returns a [Query12] over the new entities. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map12's [ecs.Relation]. +// +// Listener notification is delayed until the query is closed of fully iterated. +// +// See also [Map12.NewBatch] and [ecs.Builder.NewBatchQ]. +func (m *Map12[A, B, C, D, E, F, G, H, I, J, K, L]) NewBatchQ(count int, target ...ecs.Entity) Query12[A, B, C, D, E, F, G, H, I, J, K, L] { + query := newQuery(m.world, count, m.ids, m.relation, target...) + return Query12[A, B, C, D, E, F, G, H, I, J, K, L]{ + Query: query, + id0: m.id0, + id1: m.id1, + id2: m.id2, + id3: m.id3, + id4: m.id4, + id5: m.id5, + id6: m.id6, + id7: m.id7, + id8: m.id8, + id9: m.id9, + id10: m.id10, + id11: m.id11, + } +} + +// NewWith creates a new [ecs.Entity] with the Map12's components, using the supplied values. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map12's [ecs.Relation]. +// +// See also [ecs.NewBuilderWith]. +func (m *Map12[A, B, C, D, E, F, G, H, I, J, K, L]) NewWith(a *A, b *B, c *C, d *D, e *E, f *F, g *G, h *H, i *I, j *J, k *K, l *L, target ...ecs.Entity) ecs.Entity { + if len(target) == 0 { + return m.world.NewEntityWith(ecs.Component{ID: m.id0, Comp: a}, + ecs.Component{ID: m.id1, Comp: b}, + ecs.Component{ID: m.id2, Comp: c}, + ecs.Component{ID: m.id3, Comp: d}, + ecs.Component{ID: m.id4, Comp: e}, + ecs.Component{ID: m.id5, Comp: f}, + ecs.Component{ID: m.id6, Comp: g}, + ecs.Component{ID: m.id7, Comp: h}, + ecs.Component{ID: m.id8, Comp: i}, + ecs.Component{ID: m.id9, Comp: j}, + ecs.Component{ID: m.id10, Comp: k}, + ecs.Component{ID: m.id11, Comp: l}, + ) + } + if m.relation < 0 { + panic("map has no relation defined") + } + return ecs.NewBuilderWith(m.world, ecs.Component{ID: m.id0, Comp: a}, + ecs.Component{ID: m.id1, Comp: b}, + ecs.Component{ID: m.id2, Comp: c}, + ecs.Component{ID: m.id3, Comp: d}, + ecs.Component{ID: m.id4, Comp: e}, + ecs.Component{ID: m.id5, Comp: f}, + ecs.Component{ID: m.id6, Comp: g}, + ecs.Component{ID: m.id7, Comp: h}, + ecs.Component{ID: m.id8, Comp: i}, + ecs.Component{ID: m.id9, Comp: j}, + ecs.Component{ID: m.id10, Comp: k}, + ecs.Component{ID: m.id11, Comp: l}, + ).WithRelation(uint8(m.relation)).New(target[0]) +} + +// Add the Map12's components to the given entity. +// +// See also [ecs.World.Add]. +func (m *Map12[A, B, C, D, E, F, G, H, I, J, K, L]) Add(entity ecs.Entity) { + m.world.Add(entity, m.ids...) +} + +// Assign the Map12's components to the given entity, using the supplied values. +// +// See also [ecs.World.Assign]. +func (m *Map12[A, B, C, D, E, F, G, H, I, J, K, L]) Assign(entity ecs.Entity, a *A, b *B, c *C, d *D, e *E, f *F, g *G, h *H, i *I, j *J, k *K, l *L) { + m.world.Assign(entity, + ecs.Component{ID: m.id0, Comp: a}, + ecs.Component{ID: m.id1, Comp: b}, + ecs.Component{ID: m.id2, Comp: c}, + ecs.Component{ID: m.id3, Comp: d}, + ecs.Component{ID: m.id4, Comp: e}, + ecs.Component{ID: m.id5, Comp: f}, + ecs.Component{ID: m.id6, Comp: g}, + ecs.Component{ID: m.id7, Comp: h}, + ecs.Component{ID: m.id8, Comp: i}, + ecs.Component{ID: m.id9, Comp: j}, + ecs.Component{ID: m.id10, Comp: k}, + ecs.Component{ID: m.id11, Comp: l}, + ) +} + +// Remove the Map12's components from the given entity. +// +// See also [ecs.World.Remove]. +func (m *Map12[A, B, C, D, E, F, G, H, I, J, K, L]) Remove(entity ecs.Entity) { + m.world.Remove(entity, m.ids...) +} + +// RemoveEntities removes all entities from the world that match the Map12's components. +// +// The argument determines whether to match the components exactly (i.e. no other components are allowed), +// or to use a simple filter that does not restrict further components. +// +// Returns the number of removed entities. +// +// See also [ecs.World.NewEntityWith]. +func (m *Map12[A, B, C, D, E, F, G, H, I, J, K, L]) RemoveEntities(exclusive bool) int { + if exclusive { + filter := m.mask.Exclusive() + return m.world.Batch().RemoveEntities(&filter) + } + return m.world.Batch().RemoveEntities(m.mask) +} diff --git a/generic/map_generated_test.go b/generic/map_generated_test.go index f6026187..1d129ccc 100644 --- a/generic/map_generated_test.go +++ b/generic/map_generated_test.go @@ -708,3 +708,419 @@ func TestMap8Generated(t *testing.T) { assert.Equal(t, target, mapper.GetRelation(e)) assert.Equal(t, target, mapper.GetRelationUnchecked(e)) } + +func TestMap9Generated(t *testing.T) { + w := ecs.NewWorld() + registerAll(&w) + + mut := NewMap9[ + testStruct0, testStruct1, testStruct2, testStruct3, + testStruct4, testStruct5, testStruct6, testStruct7, + testStruct8, + ](&w) + map0 := NewMap[testStruct0](&w) + map1 := NewMap[testStruct1](&w) + + e := mut.New() + s0, s1, _, _, _, _, _, _, _ := mut.Get(e) + assert.NotNil(t, s0) + assert.NotNil(t, s1) + + mut.Remove(e) + assert.False(t, map0.Has(e)) + assert.False(t, map1.Has(e)) + + mut.Add(e) + s0, s1, _, _, _, _, _, _, _ = mut.Get(e) + assert.NotNil(t, s0) + assert.NotNil(t, s1) + + e = w.NewEntity() + mut.Assign(e, + &testStruct0{}, &testStruct1{}, &testStruct2{}, &testStruct3{}, + &testStruct4{}, &testStruct5{}, &testStruct6{}, &testStruct7{}, + &testStruct8{}, + ) + assert.True(t, map0.Has(e)) + assert.True(t, map1.Has(e)) + + e = mut.New() + assert.True(t, map0.Has(e)) + assert.True(t, map1.Has(e)) + + e = mut.NewWith( + &testStruct0{}, &testStruct1{}, &testStruct2{}, &testStruct3{}, + &testStruct4{}, &testStruct5{}, &testStruct6{}, &testStruct7{}, + &testStruct8{}, + ) + + assert.Panics(t, func() { + mut.NewWith( + &testStruct0{}, &testStruct1{}, &testStruct2{}, &testStruct3{}, + &testStruct4{}, &testStruct5{}, &testStruct6{}, &testStruct7{}, + &testStruct8{}, + e, + ) + }) + + s0, s1, _, _, _, _, _, _, _ = mut.Get(e) + assert.NotNil(t, s0) + assert.NotNil(t, s1) + + mut.NewBatch(2) + + q := mut.NewBatchQ(2) + q.Close() + + mut.GetUnchecked(e) + + mut.RemoveEntities(true) + mut.RemoveEntities(false) + + assert.Panics(t, func() { mut.Get(e) }) + + target := w.NewEntity() + mut = NewMap9[ + testStruct0, testStruct1, testStruct2, testStruct3, + testStruct4, testStruct5, testStruct6, testStruct7, + testStruct8, + ](&w) + + assert.Panics(t, func() { mut.New(target) }) + assert.Panics(t, func() { mut.NewBatch(5, target) }) + assert.Panics(t, func() { mut.NewBatchQ(5, target) }) + + mut2 := NewMap9[ + testRelationA, testStruct1, testStruct2, testStruct3, + testStruct4, testStruct5, testStruct6, testStruct7, + testStruct8, + ](&w, T[testRelationA]()) + + mut2.New(target) + mut2.NewBatch(5, target) + q2 := mut2.NewBatchQ(5, target) + assert.Equal(t, 5, q2.Count()) + q2.Close() + + e = mut2.NewWith( + &testRelationA{}, &testStruct1{}, &testStruct2{}, &testStruct3{}, + &testStruct4{}, &testStruct5{}, &testStruct6{}, &testStruct7{}, + &testStruct8{}, + target, + ) + mapper := NewMap[testRelationA](&w) + assert.Equal(t, target, mapper.GetRelation(e)) + assert.Equal(t, target, mapper.GetRelationUnchecked(e)) +} + +func TestMap10Generated(t *testing.T) { + w := ecs.NewWorld() + registerAll(&w) + + mut := NewMap10[ + testStruct0, testStruct1, testStruct2, testStruct3, + testStruct4, testStruct5, testStruct6, testStruct7, + testStruct8, testStruct9, + ](&w) + map0 := NewMap[testStruct0](&w) + map1 := NewMap[testStruct1](&w) + + e := mut.New() + s0, s1, _, _, _, _, _, _, _, _ := mut.Get(e) + assert.NotNil(t, s0) + assert.NotNil(t, s1) + + mut.Remove(e) + assert.False(t, map0.Has(e)) + assert.False(t, map1.Has(e)) + + mut.Add(e) + s0, s1, _, _, _, _, _, _, _, _ = mut.Get(e) + assert.NotNil(t, s0) + assert.NotNil(t, s1) + + e = w.NewEntity() + mut.Assign(e, + &testStruct0{}, &testStruct1{}, &testStruct2{}, &testStruct3{}, + &testStruct4{}, &testStruct5{}, &testStruct6{}, &testStruct7{}, + &testStruct8{}, &testStruct9{}, + ) + assert.True(t, map0.Has(e)) + assert.True(t, map1.Has(e)) + + e = mut.New() + assert.True(t, map0.Has(e)) + assert.True(t, map1.Has(e)) + + e = mut.NewWith( + &testStruct0{}, &testStruct1{}, &testStruct2{}, &testStruct3{}, + &testStruct4{}, &testStruct5{}, &testStruct6{}, &testStruct7{}, + &testStruct8{}, &testStruct9{}, + ) + + assert.Panics(t, func() { + mut.NewWith( + &testStruct0{}, &testStruct1{}, &testStruct2{}, &testStruct3{}, + &testStruct4{}, &testStruct5{}, &testStruct6{}, &testStruct7{}, + &testStruct8{}, &testStruct9{}, + e, + ) + }) + + s0, s1, _, _, _, _, _, _, _, _ = mut.Get(e) + assert.NotNil(t, s0) + assert.NotNil(t, s1) + + mut.NewBatch(2) + + q := mut.NewBatchQ(2) + q.Close() + + mut.GetUnchecked(e) + + mut.RemoveEntities(true) + mut.RemoveEntities(false) + + assert.Panics(t, func() { mut.Get(e) }) + + target := w.NewEntity() + mut = NewMap10[ + testStruct0, testStruct1, testStruct2, testStruct3, + testStruct4, testStruct5, testStruct6, testStruct7, + testStruct8, testStruct9, + ](&w) + + assert.Panics(t, func() { mut.New(target) }) + assert.Panics(t, func() { mut.NewBatch(5, target) }) + assert.Panics(t, func() { mut.NewBatchQ(5, target) }) + + mut2 := NewMap10[ + testRelationA, testStruct1, testStruct2, testStruct3, + testStruct4, testStruct5, testStruct6, testStruct7, + testStruct8, testStruct9, + ](&w, T[testRelationA]()) + + mut2.New(target) + mut2.NewBatch(5, target) + q2 := mut2.NewBatchQ(5, target) + assert.Equal(t, 5, q2.Count()) + q2.Close() + + e = mut2.NewWith( + &testRelationA{}, &testStruct1{}, &testStruct2{}, &testStruct3{}, + &testStruct4{}, &testStruct5{}, &testStruct6{}, &testStruct7{}, + &testStruct8{}, &testStruct9{}, + target, + ) + mapper := NewMap[testRelationA](&w) + assert.Equal(t, target, mapper.GetRelation(e)) + assert.Equal(t, target, mapper.GetRelationUnchecked(e)) +} + +func TestMap11Generated(t *testing.T) { + w := ecs.NewWorld() + registerAll(&w) + + mut := NewMap11[ + testStruct0, testStruct1, testStruct2, testStruct3, + testStruct4, testStruct5, testStruct6, testStruct7, + testStruct8, testStruct9, testStruct10, + ](&w) + map0 := NewMap[testStruct0](&w) + map1 := NewMap[testStruct1](&w) + + e := mut.New() + s0, s1, _, _, _, _, _, _, _, _, _ := mut.Get(e) + assert.NotNil(t, s0) + assert.NotNil(t, s1) + + mut.Remove(e) + assert.False(t, map0.Has(e)) + assert.False(t, map1.Has(e)) + + mut.Add(e) + s0, s1, _, _, _, _, _, _, _, _, _ = mut.Get(e) + assert.NotNil(t, s0) + assert.NotNil(t, s1) + + e = w.NewEntity() + mut.Assign(e, + &testStruct0{}, &testStruct1{}, &testStruct2{}, &testStruct3{}, + &testStruct4{}, &testStruct5{}, &testStruct6{}, &testStruct7{}, + &testStruct8{}, &testStruct9{}, &testStruct10{}, + ) + assert.True(t, map0.Has(e)) + assert.True(t, map1.Has(e)) + + e = mut.New() + assert.True(t, map0.Has(e)) + assert.True(t, map1.Has(e)) + + e = mut.NewWith( + &testStruct0{}, &testStruct1{}, &testStruct2{}, &testStruct3{}, + &testStruct4{}, &testStruct5{}, &testStruct6{}, &testStruct7{}, + &testStruct8{}, &testStruct9{}, &testStruct10{}, + ) + + assert.Panics(t, func() { + mut.NewWith( + &testStruct0{}, &testStruct1{}, &testStruct2{}, &testStruct3{}, + &testStruct4{}, &testStruct5{}, &testStruct6{}, &testStruct7{}, + &testStruct8{}, &testStruct9{}, &testStruct10{}, + e, + ) + }) + + s0, s1, _, _, _, _, _, _, _, _, _ = mut.Get(e) + assert.NotNil(t, s0) + assert.NotNil(t, s1) + + mut.NewBatch(2) + + q := mut.NewBatchQ(2) + q.Close() + + mut.GetUnchecked(e) + + mut.RemoveEntities(true) + mut.RemoveEntities(false) + + assert.Panics(t, func() { mut.Get(e) }) + + target := w.NewEntity() + mut = NewMap11[ + testStruct0, testStruct1, testStruct2, testStruct3, + testStruct4, testStruct5, testStruct6, testStruct7, + testStruct8, testStruct9, testStruct10, + ](&w) + + assert.Panics(t, func() { mut.New(target) }) + assert.Panics(t, func() { mut.NewBatch(5, target) }) + assert.Panics(t, func() { mut.NewBatchQ(5, target) }) + + mut2 := NewMap11[ + testRelationA, testStruct1, testStruct2, testStruct3, + testStruct4, testStruct5, testStruct6, testStruct7, + testStruct8, testStruct9, testStruct10, + ](&w, T[testRelationA]()) + + mut2.New(target) + mut2.NewBatch(5, target) + q2 := mut2.NewBatchQ(5, target) + assert.Equal(t, 5, q2.Count()) + q2.Close() + + e = mut2.NewWith( + &testRelationA{}, &testStruct1{}, &testStruct2{}, &testStruct3{}, + &testStruct4{}, &testStruct5{}, &testStruct6{}, &testStruct7{}, + &testStruct8{}, &testStruct9{}, &testStruct10{}, + target, + ) + mapper := NewMap[testRelationA](&w) + assert.Equal(t, target, mapper.GetRelation(e)) + assert.Equal(t, target, mapper.GetRelationUnchecked(e)) +} + +func TestMap12Generated(t *testing.T) { + w := ecs.NewWorld() + registerAll(&w) + + mut := NewMap12[ + testStruct0, testStruct1, testStruct2, testStruct3, + testStruct4, testStruct5, testStruct6, testStruct7, + testStruct8, testStruct9, testStruct10, testStruct11, + ](&w) + map0 := NewMap[testStruct0](&w) + map1 := NewMap[testStruct1](&w) + + e := mut.New() + s0, s1, _, _, _, _, _, _, _, _, _, _ := mut.Get(e) + assert.NotNil(t, s0) + assert.NotNil(t, s1) + + mut.Remove(e) + assert.False(t, map0.Has(e)) + assert.False(t, map1.Has(e)) + + mut.Add(e) + s0, s1, _, _, _, _, _, _, _, _, _, _ = mut.Get(e) + assert.NotNil(t, s0) + assert.NotNil(t, s1) + + e = w.NewEntity() + mut.Assign(e, + &testStruct0{}, &testStruct1{}, &testStruct2{}, &testStruct3{}, + &testStruct4{}, &testStruct5{}, &testStruct6{}, &testStruct7{}, + &testStruct8{}, &testStruct9{}, &testStruct10{}, &testStruct11{}, + ) + assert.True(t, map0.Has(e)) + assert.True(t, map1.Has(e)) + + e = mut.New() + assert.True(t, map0.Has(e)) + assert.True(t, map1.Has(e)) + + e = mut.NewWith( + &testStruct0{}, &testStruct1{}, &testStruct2{}, &testStruct3{}, + &testStruct4{}, &testStruct5{}, &testStruct6{}, &testStruct7{}, + &testStruct8{}, &testStruct9{}, &testStruct10{}, &testStruct11{}, + ) + + assert.Panics(t, func() { + mut.NewWith( + &testStruct0{}, &testStruct1{}, &testStruct2{}, &testStruct3{}, + &testStruct4{}, &testStruct5{}, &testStruct6{}, &testStruct7{}, + &testStruct8{}, &testStruct9{}, &testStruct10{}, &testStruct11{}, + e, + ) + }) + + s0, s1, _, _, _, _, _, _, _, _, _, _ = mut.Get(e) + assert.NotNil(t, s0) + assert.NotNil(t, s1) + + mut.NewBatch(2) + + q := mut.NewBatchQ(2) + q.Close() + + mut.GetUnchecked(e) + + mut.RemoveEntities(true) + mut.RemoveEntities(false) + + assert.Panics(t, func() { mut.Get(e) }) + + target := w.NewEntity() + mut = NewMap12[ + testStruct0, testStruct1, testStruct2, testStruct3, + testStruct4, testStruct5, testStruct6, testStruct7, + testStruct8, testStruct9, testStruct10, testStruct11, + ](&w) + + assert.Panics(t, func() { mut.New(target) }) + assert.Panics(t, func() { mut.NewBatch(5, target) }) + assert.Panics(t, func() { mut.NewBatchQ(5, target) }) + + mut2 := NewMap12[ + testRelationA, testStruct1, testStruct2, testStruct3, + testStruct4, testStruct5, testStruct6, testStruct7, + testStruct8, testStruct9, testStruct10, testStruct11, + ](&w, T[testRelationA]()) + + mut2.New(target) + mut2.NewBatch(5, target) + q2 := mut2.NewBatchQ(5, target) + assert.Equal(t, 5, q2.Count()) + q2.Close() + + e = mut2.NewWith( + &testRelationA{}, &testStruct1{}, &testStruct2{}, &testStruct3{}, + &testStruct4{}, &testStruct5{}, &testStruct6{}, &testStruct7{}, + &testStruct8{}, &testStruct9{}, &testStruct10{}, &testStruct11{}, + target, + ) + mapper := NewMap[testRelationA](&w) + assert.Equal(t, target, mapper.GetRelation(e)) + assert.Equal(t, target, mapper.GetRelationUnchecked(e)) +} diff --git a/generic/mask.go b/generic/mask.go index 3ddc68bd..644fc543 100644 --- a/generic/mask.go +++ b/generic/mask.go @@ -65,3 +65,39 @@ func T8[A any, B any, C any, D any, E any, F any, G any, H any]() []Comp { typeOf[E](), typeOf[F](), typeOf[G](), typeOf[H](), } } + +// T9 creates a component type list for nine component types. +func T9[A any, B any, C any, D any, E any, F any, G any, H any, I any]() []Comp { + return []Comp{ + typeOf[A](), typeOf[B](), typeOf[C](), typeOf[D](), + typeOf[E](), typeOf[F](), typeOf[G](), typeOf[H](), + typeOf[I](), + } +} + +// T10 creates a component type list for ten component types. +func T10[A any, B any, C any, D any, E any, F any, G any, H any, I any, J any]() []Comp { + return []Comp{ + typeOf[A](), typeOf[B](), typeOf[C](), typeOf[D](), + typeOf[E](), typeOf[F](), typeOf[G](), typeOf[H](), + typeOf[I](), typeOf[J](), + } +} + +// T11 creates a component type list for eleven component types. +func T11[A any, B any, C any, D any, E any, F any, G any, H any, I any, J any, K any]() []Comp { + return []Comp{ + typeOf[A](), typeOf[B](), typeOf[C](), typeOf[D](), + typeOf[E](), typeOf[F](), typeOf[G](), typeOf[H](), + typeOf[I](), typeOf[J](), typeOf[K](), + } +} + +// T12 creates a component type list for twelve component types. +func T12[A any, B any, C any, D any, E any, F any, G any, H any, I any, J any, K any, L any]() []Comp { + return []Comp{ + typeOf[A](), typeOf[B](), typeOf[C](), typeOf[D](), + typeOf[E](), typeOf[F](), typeOf[G](), typeOf[H](), + typeOf[I](), typeOf[J](), typeOf[K](), typeOf[L](), + } +} diff --git a/generic/mask_test.go b/generic/mask_test.go index a66ddbdb..beb6bcb9 100644 --- a/generic/mask_test.go +++ b/generic/mask_test.go @@ -99,4 +99,78 @@ func TestGenericMask(t *testing.T) { typeOf[testStruct7](), }, ) + + assert.Equal(t, + T9[testStruct0, testStruct1, testStruct2, testStruct3, + testStruct4, testStruct5, testStruct6, testStruct7, + testStruct8](), + []Comp{ + typeOf[testStruct0](), + typeOf[testStruct1](), + typeOf[testStruct2](), + typeOf[testStruct3](), + typeOf[testStruct4](), + typeOf[testStruct5](), + typeOf[testStruct6](), + typeOf[testStruct7](), + typeOf[testStruct8](), + }, + ) + + assert.Equal(t, + T10[testStruct0, testStruct1, testStruct2, testStruct3, + testStruct4, testStruct5, testStruct6, testStruct7, + testStruct8, testStruct9](), + []Comp{ + typeOf[testStruct0](), + typeOf[testStruct1](), + typeOf[testStruct2](), + typeOf[testStruct3](), + typeOf[testStruct4](), + typeOf[testStruct5](), + typeOf[testStruct6](), + typeOf[testStruct7](), + typeOf[testStruct8](), + typeOf[testStruct9](), + }, + ) + + assert.Equal(t, + T11[testStruct0, testStruct1, testStruct2, testStruct3, + testStruct4, testStruct5, testStruct6, testStruct7, + testStruct8, testStruct9, testStruct10](), + []Comp{ + typeOf[testStruct0](), + typeOf[testStruct1](), + typeOf[testStruct2](), + typeOf[testStruct3](), + typeOf[testStruct4](), + typeOf[testStruct5](), + typeOf[testStruct6](), + typeOf[testStruct7](), + typeOf[testStruct8](), + typeOf[testStruct9](), + typeOf[testStruct10](), + }, + ) + + assert.Equal(t, + T12[testStruct0, testStruct1, testStruct2, testStruct3, + testStruct4, testStruct5, testStruct6, testStruct7, + testStruct8, testStruct9, testStruct10, testStruct11](), + []Comp{ + typeOf[testStruct0](), + typeOf[testStruct1](), + typeOf[testStruct2](), + typeOf[testStruct3](), + typeOf[testStruct4](), + typeOf[testStruct5](), + typeOf[testStruct6](), + typeOf[testStruct7](), + typeOf[testStruct8](), + typeOf[testStruct9](), + typeOf[testStruct10](), + typeOf[testStruct11](), + }, + ) } diff --git a/generic/query_generated.go b/generic/query_generated.go index 28e73934..5e21eb5c 100644 --- a/generic/query_generated.go +++ b/generic/query_generated.go @@ -1648,3 +1648,847 @@ func (q *Query8[A, B, C, D, E, F, G, H]) Relation() ecs.Entity { } return q.Query.Relation(ecs.ID(q.target)) } + +////////////////////////////////////////////////////////////////////////// + +// Filter9 is a helper for building [Query9] query iterators. +// +// # Example +// +// world := ecs.NewWorld() +// +// filter := NewFilter9[A, B, C, D, E, F, G, H, I]() +// query := filter.Query(&world) +// +// complexFilter := +// NewFilter9[A, B, C, D, E, F, G, H, I](). +// Optional(T[A]()). +// With(T2[V, W]()...). +// Without(T3[X, Y, Z]()...). +type Filter9[A any, B any, C any, D any, E any, F any, G any, H any, I any] filter + +// NewFilter9 creates a generic Filter9 for nine components. +// +// See also [ecs.World.Query]. +func NewFilter9[A any, B any, C any, D any, E any, F any, G any, H any, I any]() *Filter9[A, B, C, D, E, F, G, H, I] { + f := Filter9[A, B, C, D, E, F, G, H, I](newFilter( + typeOf[A](), + typeOf[B](), + typeOf[C](), + typeOf[D](), + typeOf[E](), + typeOf[F](), + typeOf[G](), + typeOf[H](), + typeOf[I](), + )) + return &f +} + +// Optional makes some of the query's components optional. +// +// Create the required mask items with [T]. +// +// Only affects component types that were specified in the query. +func (q *Filter9[A, B, C, D, E, F, G, H, I]) Optional(mask ...Comp) *Filter9[A, B, C, D, E, F, G, H, I] { + if q.compiled.locked { + panic("can't modify a registered filter") + } + q.optional = append(q.optional, mask...) + q.compiled.Reset() + return q +} + +// With adds components that are required, but not accessible via [Query9.Get]. +// +// Create the required mask items with [T]. +func (q *Filter9[A, B, C, D, E, F, G, H, I]) With(mask ...Comp) *Filter9[A, B, C, D, E, F, G, H, I] { + if q.compiled.locked { + panic("can't modify a registered filter") + } + q.include = append(q.include, mask...) + q.compiled.Reset() + return q +} + +// Without excludes entities with any of the given components from the query. +// +// Create the required mask items with [T]. +func (q *Filter9[A, B, C, D, E, F, G, H, I]) Without(mask ...Comp) *Filter9[A, B, C, D, E, F, G, H, I] { + if q.compiled.locked { + panic("can't modify a registered filter") + } + q.exclude = append(q.exclude, mask...) + q.compiled.Reset() + return q +} + +// WithRelation sets the filter's [ecs.Relation] component and optionally +// restricts the query to entities that have the given relation target. +// +// Use without the optional argument to specify the relation target in [Filter9.Query]. +// If the optional argument is provided, the filter's relation target is set permanently. +// +// Create the required component ID with [T]. +func (q *Filter9[A, B, C, D, E, F, G, H, I]) WithRelation(comp Comp, target ...ecs.Entity) *Filter9[A, B, C, D, E, F, G, H, I] { + if q.compiled.locked { + panic("can't modify a registered filter") + } + q.targetType = comp + if len(target) > 0 { + q.target = target[0] + q.hasTarget = true + } + q.compiled.Reset() + return q +} + +// Query builds a [Query9] query for iteration, with an optional relation target. +// +// A relation target can't be used: +// - if [Filter9.WithRelation] was not called +// - if the target was already set via [Filter9.WithRelation] +// - if the filter is registered for caching +// +// Panics in these cases. +func (q *Filter9[A, B, C, D, E, F, G, H, I]) Query(w *ecs.World, target ...ecs.Entity) Query9[A, B, C, D, E, F, G, H, I] { + q.compiled.Compile(w, q.include, q.optional, q.exclude, q.targetType, q.target, q.hasTarget) + + filter := q.compiled.filter + if len(target) > 0 { + if q.compiled.locked { + panic("can't change relation target on a cached query") + } + if q.hasTarget { + panic("can't change relation target on a query with fixed target") + } + q.compiled.relationFilter.Filter = &q.compiled.maskFilter + q.compiled.relationFilter.Target = target[0] + filter = &q.compiled.relationFilter + } + + return Query9[A, B, C, D, E, F, G, H, I]{ + Query: w.Query(filter), + target: q.compiled.TargetComp, + id0: q.compiled.Ids[0], + id1: q.compiled.Ids[1], + id2: q.compiled.Ids[2], + id3: q.compiled.Ids[3], + id4: q.compiled.Ids[4], + id5: q.compiled.Ids[5], + id6: q.compiled.Ids[6], + id7: q.compiled.Ids[7], + id8: q.compiled.Ids[8], + } +} + +// Register the filter for caching. +// +// See [ecs.Cache] for details on filter caching. +func (q *Filter9[A, B, C, D, E, F, G, H, I]) Register(w *ecs.World) { + q.compiled.Compile(w, q.include, q.optional, q.exclude, q.targetType, q.target, q.hasTarget) + q.compiled.Register(w) +} + +// Unregister the filter from caching. +// +// See [ecs.Cache] for details on filter caching. +func (q *Filter9[A, B, C, D, E, F, G, H, I]) Unregister(w *ecs.World) { + q.compiled.Unregister(w) +} + +// Query9 is a generic query iterator for nine components. +// +// Create it with [NewFilter9] and [Filter9.Query]. +// +// Also has all methods of [ecs.Query]. +// +// # Example +// +// world := ecs.NewWorld() +// +// filter := NewFilter9[A, B, C, D, E, F, G, H, I]() +// query := filter.Query(&world) +// for query.Next() { +// entity = query.Entity() +// a, b, c, d, e, f, g, h, i := query.Get() +// } +type Query9[A any, B any, C any, D any, E any, F any, G any, H any, I any] struct { + ecs.Query + id0 ecs.ID + id1 ecs.ID + id2 ecs.ID + id3 ecs.ID + id4 ecs.ID + id5 ecs.ID + id6 ecs.ID + id7 ecs.ID + id8 ecs.ID + target int8 +} + +// Get returns all queried components for the current query iterator position. +// +// Use [ecs.Query.Entity] to get the current Entity. +func (q *Query9[A, B, C, D, E, F, G, H, I]) Get() (*A, *B, *C, *D, *E, *F, *G, *H, *I) { + return (*A)(q.Query.Get(q.id0)), + (*B)(q.Query.Get(q.id1)), + (*C)(q.Query.Get(q.id2)), + (*D)(q.Query.Get(q.id3)), + (*E)(q.Query.Get(q.id4)), + (*F)(q.Query.Get(q.id5)), + (*G)(q.Query.Get(q.id6)), + (*H)(q.Query.Get(q.id7)), + (*I)(q.Query.Get(q.id8)) +} + +// Relation returns the target entity for the query's relation. +// +// Panics if the entity does not have the given component, or if the component is not an [ecs.Relation]. +// Panics if the underlying [Filter9] was not prepared for relations +// using [Filter9.WithRelation]. +func (q *Query9[A, B, C, D, E, F, G, H, I]) Relation() ecs.Entity { + if q.target < 0 { + panic("query has no relation") + } + return q.Query.Relation(ecs.ID(q.target)) +} + +////////////////////////////////////////////////////////////////////////// + +// Filter10 is a helper for building [Query10] query iterators. +// +// # Example +// +// world := ecs.NewWorld() +// +// filter := NewFilter10[A, B, C, D, E, F, G, H, I, J]() +// query := filter.Query(&world) +// +// complexFilter := +// NewFilter10[A, B, C, D, E, F, G, H, I, J](). +// Optional(T[A]()). +// With(T2[V, W]()...). +// Without(T3[X, Y, Z]()...). +type Filter10[A any, B any, C any, D any, E any, F any, G any, H any, I any, J any] filter + +// NewFilter10 creates a generic Filter10 for ten components. +// +// See also [ecs.World.Query]. +func NewFilter10[A any, B any, C any, D any, E any, F any, G any, H any, I any, J any]() *Filter10[A, B, C, D, E, F, G, H, I, J] { + f := Filter10[A, B, C, D, E, F, G, H, I, J](newFilter( + typeOf[A](), + typeOf[B](), + typeOf[C](), + typeOf[D](), + typeOf[E](), + typeOf[F](), + typeOf[G](), + typeOf[H](), + typeOf[I](), + typeOf[J](), + )) + return &f +} + +// Optional makes some of the query's components optional. +// +// Create the required mask items with [T]. +// +// Only affects component types that were specified in the query. +func (q *Filter10[A, B, C, D, E, F, G, H, I, J]) Optional(mask ...Comp) *Filter10[A, B, C, D, E, F, G, H, I, J] { + if q.compiled.locked { + panic("can't modify a registered filter") + } + q.optional = append(q.optional, mask...) + q.compiled.Reset() + return q +} + +// With adds components that are required, but not accessible via [Query10.Get]. +// +// Create the required mask items with [T]. +func (q *Filter10[A, B, C, D, E, F, G, H, I, J]) With(mask ...Comp) *Filter10[A, B, C, D, E, F, G, H, I, J] { + if q.compiled.locked { + panic("can't modify a registered filter") + } + q.include = append(q.include, mask...) + q.compiled.Reset() + return q +} + +// Without excludes entities with any of the given components from the query. +// +// Create the required mask items with [T]. +func (q *Filter10[A, B, C, D, E, F, G, H, I, J]) Without(mask ...Comp) *Filter10[A, B, C, D, E, F, G, H, I, J] { + if q.compiled.locked { + panic("can't modify a registered filter") + } + q.exclude = append(q.exclude, mask...) + q.compiled.Reset() + return q +} + +// WithRelation sets the filter's [ecs.Relation] component and optionally +// restricts the query to entities that have the given relation target. +// +// Use without the optional argument to specify the relation target in [Filter10.Query]. +// If the optional argument is provided, the filter's relation target is set permanently. +// +// Create the required component ID with [T]. +func (q *Filter10[A, B, C, D, E, F, G, H, I, J]) WithRelation(comp Comp, target ...ecs.Entity) *Filter10[A, B, C, D, E, F, G, H, I, J] { + if q.compiled.locked { + panic("can't modify a registered filter") + } + q.targetType = comp + if len(target) > 0 { + q.target = target[0] + q.hasTarget = true + } + q.compiled.Reset() + return q +} + +// Query builds a [Query10] query for iteration, with an optional relation target. +// +// A relation target can't be used: +// - if [Filter10.WithRelation] was not called +// - if the target was already set via [Filter10.WithRelation] +// - if the filter is registered for caching +// +// Panics in these cases. +func (q *Filter10[A, B, C, D, E, F, G, H, I, J]) Query(w *ecs.World, target ...ecs.Entity) Query10[A, B, C, D, E, F, G, H, I, J] { + q.compiled.Compile(w, q.include, q.optional, q.exclude, q.targetType, q.target, q.hasTarget) + + filter := q.compiled.filter + if len(target) > 0 { + if q.compiled.locked { + panic("can't change relation target on a cached query") + } + if q.hasTarget { + panic("can't change relation target on a query with fixed target") + } + q.compiled.relationFilter.Filter = &q.compiled.maskFilter + q.compiled.relationFilter.Target = target[0] + filter = &q.compiled.relationFilter + } + + return Query10[A, B, C, D, E, F, G, H, I, J]{ + Query: w.Query(filter), + target: q.compiled.TargetComp, + id0: q.compiled.Ids[0], + id1: q.compiled.Ids[1], + id2: q.compiled.Ids[2], + id3: q.compiled.Ids[3], + id4: q.compiled.Ids[4], + id5: q.compiled.Ids[5], + id6: q.compiled.Ids[6], + id7: q.compiled.Ids[7], + id8: q.compiled.Ids[8], + id9: q.compiled.Ids[9], + } +} + +// Register the filter for caching. +// +// See [ecs.Cache] for details on filter caching. +func (q *Filter10[A, B, C, D, E, F, G, H, I, J]) Register(w *ecs.World) { + q.compiled.Compile(w, q.include, q.optional, q.exclude, q.targetType, q.target, q.hasTarget) + q.compiled.Register(w) +} + +// Unregister the filter from caching. +// +// See [ecs.Cache] for details on filter caching. +func (q *Filter10[A, B, C, D, E, F, G, H, I, J]) Unregister(w *ecs.World) { + q.compiled.Unregister(w) +} + +// Query10 is a generic query iterator for ten components. +// +// Create it with [NewFilter10] and [Filter10.Query]. +// +// Also has all methods of [ecs.Query]. +// +// # Example +// +// world := ecs.NewWorld() +// +// filter := NewFilter10[A, B, C, D, E, F, G, H, I, J]() +// query := filter.Query(&world) +// for query.Next() { +// entity = query.Entity() +// a, b, c, d, e, f, g, h, i, j := query.Get() +// } +type Query10[A any, B any, C any, D any, E any, F any, G any, H any, I any, J any] struct { + ecs.Query + id0 ecs.ID + id1 ecs.ID + id2 ecs.ID + id3 ecs.ID + id4 ecs.ID + id5 ecs.ID + id6 ecs.ID + id7 ecs.ID + id8 ecs.ID + id9 ecs.ID + target int8 +} + +// Get returns all queried components for the current query iterator position. +// +// Use [ecs.Query.Entity] to get the current Entity. +func (q *Query10[A, B, C, D, E, F, G, H, I, J]) Get() (*A, *B, *C, *D, *E, *F, *G, *H, *I, *J) { + return (*A)(q.Query.Get(q.id0)), + (*B)(q.Query.Get(q.id1)), + (*C)(q.Query.Get(q.id2)), + (*D)(q.Query.Get(q.id3)), + (*E)(q.Query.Get(q.id4)), + (*F)(q.Query.Get(q.id5)), + (*G)(q.Query.Get(q.id6)), + (*H)(q.Query.Get(q.id7)), + (*I)(q.Query.Get(q.id8)), + (*J)(q.Query.Get(q.id9)) +} + +// Relation returns the target entity for the query's relation. +// +// Panics if the entity does not have the given component, or if the component is not an [ecs.Relation]. +// Panics if the underlying [Filter10] was not prepared for relations +// using [Filter10.WithRelation]. +func (q *Query10[A, B, C, D, E, F, G, H, I, J]) Relation() ecs.Entity { + if q.target < 0 { + panic("query has no relation") + } + return q.Query.Relation(ecs.ID(q.target)) +} + +////////////////////////////////////////////////////////////////////////// + +// Filter11 is a helper for building [Query11] query iterators. +// +// # Example +// +// world := ecs.NewWorld() +// +// filter := NewFilter11[A, B, C, D, E, F, G, H, I, J, K]() +// query := filter.Query(&world) +// +// complexFilter := +// NewFilter11[A, B, C, D, E, F, G, H, I, J, K](). +// Optional(T[A]()). +// With(T2[V, W]()...). +// Without(T3[X, Y, Z]()...). +type Filter11[A any, B any, C any, D any, E any, F any, G any, H any, I any, J any, K any] filter + +// NewFilter11 creates a generic Filter11 for eleven components. +// +// See also [ecs.World.Query]. +func NewFilter11[A any, B any, C any, D any, E any, F any, G any, H any, I any, J any, K any]() *Filter11[A, B, C, D, E, F, G, H, I, J, K] { + f := Filter11[A, B, C, D, E, F, G, H, I, J, K](newFilter( + typeOf[A](), + typeOf[B](), + typeOf[C](), + typeOf[D](), + typeOf[E](), + typeOf[F](), + typeOf[G](), + typeOf[H](), + typeOf[I](), + typeOf[J](), + typeOf[K](), + )) + return &f +} + +// Optional makes some of the query's components optional. +// +// Create the required mask items with [T]. +// +// Only affects component types that were specified in the query. +func (q *Filter11[A, B, C, D, E, F, G, H, I, J, K]) Optional(mask ...Comp) *Filter11[A, B, C, D, E, F, G, H, I, J, K] { + if q.compiled.locked { + panic("can't modify a registered filter") + } + q.optional = append(q.optional, mask...) + q.compiled.Reset() + return q +} + +// With adds components that are required, but not accessible via [Query11.Get]. +// +// Create the required mask items with [T]. +func (q *Filter11[A, B, C, D, E, F, G, H, I, J, K]) With(mask ...Comp) *Filter11[A, B, C, D, E, F, G, H, I, J, K] { + if q.compiled.locked { + panic("can't modify a registered filter") + } + q.include = append(q.include, mask...) + q.compiled.Reset() + return q +} + +// Without excludes entities with any of the given components from the query. +// +// Create the required mask items with [T]. +func (q *Filter11[A, B, C, D, E, F, G, H, I, J, K]) Without(mask ...Comp) *Filter11[A, B, C, D, E, F, G, H, I, J, K] { + if q.compiled.locked { + panic("can't modify a registered filter") + } + q.exclude = append(q.exclude, mask...) + q.compiled.Reset() + return q +} + +// WithRelation sets the filter's [ecs.Relation] component and optionally +// restricts the query to entities that have the given relation target. +// +// Use without the optional argument to specify the relation target in [Filter11.Query]. +// If the optional argument is provided, the filter's relation target is set permanently. +// +// Create the required component ID with [T]. +func (q *Filter11[A, B, C, D, E, F, G, H, I, J, K]) WithRelation(comp Comp, target ...ecs.Entity) *Filter11[A, B, C, D, E, F, G, H, I, J, K] { + if q.compiled.locked { + panic("can't modify a registered filter") + } + q.targetType = comp + if len(target) > 0 { + q.target = target[0] + q.hasTarget = true + } + q.compiled.Reset() + return q +} + +// Query builds a [Query11] query for iteration, with an optional relation target. +// +// A relation target can't be used: +// - if [Filter11.WithRelation] was not called +// - if the target was already set via [Filter11.WithRelation] +// - if the filter is registered for caching +// +// Panics in these cases. +func (q *Filter11[A, B, C, D, E, F, G, H, I, J, K]) Query(w *ecs.World, target ...ecs.Entity) Query11[A, B, C, D, E, F, G, H, I, J, K] { + q.compiled.Compile(w, q.include, q.optional, q.exclude, q.targetType, q.target, q.hasTarget) + + filter := q.compiled.filter + if len(target) > 0 { + if q.compiled.locked { + panic("can't change relation target on a cached query") + } + if q.hasTarget { + panic("can't change relation target on a query with fixed target") + } + q.compiled.relationFilter.Filter = &q.compiled.maskFilter + q.compiled.relationFilter.Target = target[0] + filter = &q.compiled.relationFilter + } + + return Query11[A, B, C, D, E, F, G, H, I, J, K]{ + Query: w.Query(filter), + target: q.compiled.TargetComp, + id0: q.compiled.Ids[0], + id1: q.compiled.Ids[1], + id2: q.compiled.Ids[2], + id3: q.compiled.Ids[3], + id4: q.compiled.Ids[4], + id5: q.compiled.Ids[5], + id6: q.compiled.Ids[6], + id7: q.compiled.Ids[7], + id8: q.compiled.Ids[8], + id9: q.compiled.Ids[9], + id10: q.compiled.Ids[10], + } +} + +// Register the filter for caching. +// +// See [ecs.Cache] for details on filter caching. +func (q *Filter11[A, B, C, D, E, F, G, H, I, J, K]) Register(w *ecs.World) { + q.compiled.Compile(w, q.include, q.optional, q.exclude, q.targetType, q.target, q.hasTarget) + q.compiled.Register(w) +} + +// Unregister the filter from caching. +// +// See [ecs.Cache] for details on filter caching. +func (q *Filter11[A, B, C, D, E, F, G, H, I, J, K]) Unregister(w *ecs.World) { + q.compiled.Unregister(w) +} + +// Query11 is a generic query iterator for eleven components. +// +// Create it with [NewFilter11] and [Filter11.Query]. +// +// Also has all methods of [ecs.Query]. +// +// # Example +// +// world := ecs.NewWorld() +// +// filter := NewFilter11[A, B, C, D, E, F, G, H, I, J, K]() +// query := filter.Query(&world) +// for query.Next() { +// entity = query.Entity() +// a, b, c, d, e, f, g, h, i, j, k := query.Get() +// } +type Query11[A any, B any, C any, D any, E any, F any, G any, H any, I any, J any, K any] struct { + ecs.Query + id0 ecs.ID + id1 ecs.ID + id2 ecs.ID + id3 ecs.ID + id4 ecs.ID + id5 ecs.ID + id6 ecs.ID + id7 ecs.ID + id8 ecs.ID + id9 ecs.ID + id10 ecs.ID + target int8 +} + +// Get returns all queried components for the current query iterator position. +// +// Use [ecs.Query.Entity] to get the current Entity. +func (q *Query11[A, B, C, D, E, F, G, H, I, J, K]) Get() (*A, *B, *C, *D, *E, *F, *G, *H, *I, *J, *K) { + return (*A)(q.Query.Get(q.id0)), + (*B)(q.Query.Get(q.id1)), + (*C)(q.Query.Get(q.id2)), + (*D)(q.Query.Get(q.id3)), + (*E)(q.Query.Get(q.id4)), + (*F)(q.Query.Get(q.id5)), + (*G)(q.Query.Get(q.id6)), + (*H)(q.Query.Get(q.id7)), + (*I)(q.Query.Get(q.id8)), + (*J)(q.Query.Get(q.id9)), + (*K)(q.Query.Get(q.id10)) +} + +// Relation returns the target entity for the query's relation. +// +// Panics if the entity does not have the given component, or if the component is not an [ecs.Relation]. +// Panics if the underlying [Filter11] was not prepared for relations +// using [Filter11.WithRelation]. +func (q *Query11[A, B, C, D, E, F, G, H, I, J, K]) Relation() ecs.Entity { + if q.target < 0 { + panic("query has no relation") + } + return q.Query.Relation(ecs.ID(q.target)) +} + +////////////////////////////////////////////////////////////////////////// + +// Filter12 is a helper for building [Query12] query iterators. +// +// # Example +// +// world := ecs.NewWorld() +// +// filter := NewFilter12[A, B, C, D, E, F, G, H, I, J, K, L]() +// query := filter.Query(&world) +// +// complexFilter := +// NewFilter12[A, B, C, D, E, F, G, H, I, J, K, L](). +// Optional(T[A]()). +// With(T2[V, W]()...). +// Without(T3[X, Y, Z]()...). +type Filter12[A any, B any, C any, D any, E any, F any, G any, H any, I any, J any, K any, L any] filter + +// NewFilter12 creates a generic Filter12 for twelve components. +// +// See also [ecs.World.Query]. +func NewFilter12[A any, B any, C any, D any, E any, F any, G any, H any, I any, J any, K any, L any]() *Filter12[A, B, C, D, E, F, G, H, I, J, K, L] { + f := Filter12[A, B, C, D, E, F, G, H, I, J, K, L](newFilter( + typeOf[A](), + typeOf[B](), + typeOf[C](), + typeOf[D](), + typeOf[E](), + typeOf[F](), + typeOf[G](), + typeOf[H](), + typeOf[I](), + typeOf[J](), + typeOf[K](), + typeOf[L](), + )) + return &f +} + +// Optional makes some of the query's components optional. +// +// Create the required mask items with [T]. +// +// Only affects component types that were specified in the query. +func (q *Filter12[A, B, C, D, E, F, G, H, I, J, K, L]) Optional(mask ...Comp) *Filter12[A, B, C, D, E, F, G, H, I, J, K, L] { + if q.compiled.locked { + panic("can't modify a registered filter") + } + q.optional = append(q.optional, mask...) + q.compiled.Reset() + return q +} + +// With adds components that are required, but not accessible via [Query12.Get]. +// +// Create the required mask items with [T]. +func (q *Filter12[A, B, C, D, E, F, G, H, I, J, K, L]) With(mask ...Comp) *Filter12[A, B, C, D, E, F, G, H, I, J, K, L] { + if q.compiled.locked { + panic("can't modify a registered filter") + } + q.include = append(q.include, mask...) + q.compiled.Reset() + return q +} + +// Without excludes entities with any of the given components from the query. +// +// Create the required mask items with [T]. +func (q *Filter12[A, B, C, D, E, F, G, H, I, J, K, L]) Without(mask ...Comp) *Filter12[A, B, C, D, E, F, G, H, I, J, K, L] { + if q.compiled.locked { + panic("can't modify a registered filter") + } + q.exclude = append(q.exclude, mask...) + q.compiled.Reset() + return q +} + +// WithRelation sets the filter's [ecs.Relation] component and optionally +// restricts the query to entities that have the given relation target. +// +// Use without the optional argument to specify the relation target in [Filter12.Query]. +// If the optional argument is provided, the filter's relation target is set permanently. +// +// Create the required component ID with [T]. +func (q *Filter12[A, B, C, D, E, F, G, H, I, J, K, L]) WithRelation(comp Comp, target ...ecs.Entity) *Filter12[A, B, C, D, E, F, G, H, I, J, K, L] { + if q.compiled.locked { + panic("can't modify a registered filter") + } + q.targetType = comp + if len(target) > 0 { + q.target = target[0] + q.hasTarget = true + } + q.compiled.Reset() + return q +} + +// Query builds a [Query12] query for iteration, with an optional relation target. +// +// A relation target can't be used: +// - if [Filter12.WithRelation] was not called +// - if the target was already set via [Filter12.WithRelation] +// - if the filter is registered for caching +// +// Panics in these cases. +func (q *Filter12[A, B, C, D, E, F, G, H, I, J, K, L]) Query(w *ecs.World, target ...ecs.Entity) Query12[A, B, C, D, E, F, G, H, I, J, K, L] { + q.compiled.Compile(w, q.include, q.optional, q.exclude, q.targetType, q.target, q.hasTarget) + + filter := q.compiled.filter + if len(target) > 0 { + if q.compiled.locked { + panic("can't change relation target on a cached query") + } + if q.hasTarget { + panic("can't change relation target on a query with fixed target") + } + q.compiled.relationFilter.Filter = &q.compiled.maskFilter + q.compiled.relationFilter.Target = target[0] + filter = &q.compiled.relationFilter + } + + return Query12[A, B, C, D, E, F, G, H, I, J, K, L]{ + Query: w.Query(filter), + target: q.compiled.TargetComp, + id0: q.compiled.Ids[0], + id1: q.compiled.Ids[1], + id2: q.compiled.Ids[2], + id3: q.compiled.Ids[3], + id4: q.compiled.Ids[4], + id5: q.compiled.Ids[5], + id6: q.compiled.Ids[6], + id7: q.compiled.Ids[7], + id8: q.compiled.Ids[8], + id9: q.compiled.Ids[9], + id10: q.compiled.Ids[10], + id11: q.compiled.Ids[11], + } +} + +// Register the filter for caching. +// +// See [ecs.Cache] for details on filter caching. +func (q *Filter12[A, B, C, D, E, F, G, H, I, J, K, L]) Register(w *ecs.World) { + q.compiled.Compile(w, q.include, q.optional, q.exclude, q.targetType, q.target, q.hasTarget) + q.compiled.Register(w) +} + +// Unregister the filter from caching. +// +// See [ecs.Cache] for details on filter caching. +func (q *Filter12[A, B, C, D, E, F, G, H, I, J, K, L]) Unregister(w *ecs.World) { + q.compiled.Unregister(w) +} + +// Query12 is a generic query iterator for twelve components. +// +// Create it with [NewFilter12] and [Filter12.Query]. +// +// Also has all methods of [ecs.Query]. +// +// # Example +// +// world := ecs.NewWorld() +// +// filter := NewFilter12[A, B, C, D, E, F, G, H, I, J, K, L]() +// query := filter.Query(&world) +// for query.Next() { +// entity = query.Entity() +// a, b, c, d, e, f, g, h, i, j, k, l := query.Get() +// } +type Query12[A any, B any, C any, D any, E any, F any, G any, H any, I any, J any, K any, L any] struct { + ecs.Query + id0 ecs.ID + id1 ecs.ID + id2 ecs.ID + id3 ecs.ID + id4 ecs.ID + id5 ecs.ID + id6 ecs.ID + id7 ecs.ID + id8 ecs.ID + id9 ecs.ID + id10 ecs.ID + id11 ecs.ID + target int8 +} + +// Get returns all queried components for the current query iterator position. +// +// Use [ecs.Query.Entity] to get the current Entity. +func (q *Query12[A, B, C, D, E, F, G, H, I, J, K, L]) Get() (*A, *B, *C, *D, *E, *F, *G, *H, *I, *J, *K, *L) { + return (*A)(q.Query.Get(q.id0)), + (*B)(q.Query.Get(q.id1)), + (*C)(q.Query.Get(q.id2)), + (*D)(q.Query.Get(q.id3)), + (*E)(q.Query.Get(q.id4)), + (*F)(q.Query.Get(q.id5)), + (*G)(q.Query.Get(q.id6)), + (*H)(q.Query.Get(q.id7)), + (*I)(q.Query.Get(q.id8)), + (*J)(q.Query.Get(q.id9)), + (*K)(q.Query.Get(q.id10)), + (*L)(q.Query.Get(q.id11)) +} + +// Relation returns the target entity for the query's relation. +// +// Panics if the entity does not have the given component, or if the component is not an [ecs.Relation]. +// Panics if the underlying [Filter12] was not prepared for relations +// using [Filter12.WithRelation]. +func (q *Query12[A, B, C, D, E, F, G, H, I, J, K, L]) Relation() ecs.Entity { + if q.target < 0 { + panic("query has no relation") + } + return q.Query.Relation(ecs.ID(q.target)) +} diff --git a/generic/query_test.go b/generic/query_test.go index a6c2685a..635c59fd 100644 --- a/generic/query_test.go +++ b/generic/query_test.go @@ -961,6 +961,518 @@ func TestQuery8(t *testing.T) { filter2.Unregister(&w) } +func TestQuery9(t *testing.T) { + w := ecs.NewWorld() + + registerAll(&w) + relID := ecs.ComponentID[testRelationA](&w) + + e0 := w.NewEntity() + e1 := w.NewEntity() + e2 := w.NewEntity() + + w.Assign(e0, ecs.Component{ID: 0, Comp: &testStruct0{1}}) + w.Assign(e1, ecs.Component{ID: 0, Comp: &testStruct0{2}}) + + w.Assign(e0, ecs.Component{ID: 1, Comp: &testStruct1{2}}) + w.Assign(e1, ecs.Component{ID: 1, Comp: &testStruct1{3}}) + + w.Assign(e0, ecs.Component{ID: 2, Comp: &testStruct2{3, 0}}) + w.Assign(e1, ecs.Component{ID: 2, Comp: &testStruct2{4, 0}}) + + w.Assign(e0, ecs.Component{ID: 3, Comp: &testStruct3{4, 0, 0}}) + w.Assign(e1, ecs.Component{ID: 3, Comp: &testStruct3{5, 0, 0}}) + + w.Assign(e0, ecs.Component{ID: 4, Comp: &testStruct4{5, 0, 0, 0}}) + w.Assign(e1, ecs.Component{ID: 4, Comp: &testStruct4{6, 0, 0, 0}}) + + w.Assign(e0, ecs.Component{ID: 5, Comp: &testStruct5{6, 0, 0, 0, 0}}) + w.Assign(e1, ecs.Component{ID: 5, Comp: &testStruct5{7, 0, 0, 0, 0}}) + + w.Assign(e0, ecs.Component{ID: 6, Comp: &testStruct6{7, 0, 0, 0, 0, 0}}) + w.Assign(e1, ecs.Component{ID: 6, Comp: &testStruct6{8, 0, 0, 0, 0, 0}}) + + w.Assign(e0, ecs.Component{ID: 7, Comp: &testStruct7{8, 0, 0, 0, 0, 0, 0}}) + w.Assign(e1, ecs.Component{ID: 7, Comp: &testStruct7{9, 0, 0, 0, 0, 0, 0}}) + + w.Assign(e0, ecs.Component{ID: 8, Comp: &testStruct8{9, 0, 0, 0, 0, 0, 0, 0}}) + w.Assign(e1, ecs.Component{ID: 8, Comp: &testStruct8{10, 0, 0, 0, 0, 0, 0, 0}}) + + w.Assign(e0, ecs.Component{ID: 9, Comp: &testStruct9{10, 0, 0, 0, 0, 0, 0, 0, 0}}) + + w.Assign(e2, ecs.Component{ID: 10, Comp: &testStruct10{}}) + + cnt := 0 + filter := + NewFilter9[ + testStruct0, testStruct1, testStruct2, testStruct3, + testStruct4, testStruct5, testStruct6, testStruct7, + testStruct8, + ](). + Optional(T[testStruct1]()). + With(T[testStruct9]()). + Without(T[testStruct10]()) + + filter.Register(&w) + + query := filter.Query(&w) + for query.Next() { + c1, c2, c3, c4, c5, c6, c7, c8, c9 := query.Get() + assert.Equal(t, cnt+1, int(c1.val)) + assert.Equal(t, cnt+2, int(c2.val)) + assert.Equal(t, cnt+3, int(c3.val)) + assert.Equal(t, cnt+4, int(c4.val)) + assert.Equal(t, cnt+5, int(c5.val)) + assert.Equal(t, cnt+6, int(c6.val)) + assert.Equal(t, cnt+7, int(c7.val)) + assert.Equal(t, cnt+8, int(c8.val)) + assert.Equal(t, cnt+9, int(c9.val)) + assert.Panics(t, func() { query.Relation() }) + cnt++ + } + assert.Equal(t, 1, cnt) + + filter.Unregister(&w) + assert.Panics(t, func() { filter.Unregister(&w) }) + + targ := w.NewEntity(0) + + w.Add(e0, relID) + w.Add(e1, relID) + w.Add(e2, relID) + + w.Relations().Set(e0, relID, targ) + + filter2 := + NewFilter9[ + testStruct0, testStruct1, testStruct2, testStruct3, + testStruct4, testStruct5, testStruct6, testStruct7, + testRelationA, + ](). + WithRelation(T[testRelationA](), targ) + + q := filter2.Query(&w) + assert.Equal(t, 1, q.Count()) + for q.Next() { + trg := q.Relation() + assert.Equal(t, targ, trg) + } + + assert.Panics(t, func() { filter2.Query(&w, targ) }) + + filter2 = + NewFilter9[ + testStruct0, testStruct1, testStruct2, testStruct3, + testStruct4, testStruct5, testStruct6, testStruct7, + testRelationA, + ](). + WithRelation(T[testRelationA]()) + q = filter2.Query(&w) + assert.Equal(t, 2, q.Count()) + q.Close() + q = filter2.Query(&w, targ) + assert.Equal(t, 1, q.Count()) + q.Close() + + filter2.Register(&w) + assert.Panics(t, func() { filter2.Query(&w, targ) }) + assert.Panics(t, func() { filter2.Optional(T[testStruct0]()) }) + assert.Panics(t, func() { filter2.With(T[testStruct0]()) }) + assert.Panics(t, func() { filter2.Without(T[testStruct0]()) }) + assert.Panics(t, func() { filter2.WithRelation(T[testRelationA](), targ) }) + filter2.Unregister(&w) +} + +func TestQuery10(t *testing.T) { + w := ecs.NewWorld() + + registerAll(&w) + relID := ecs.ComponentID[testRelationA](&w) + + e0 := w.NewEntity() + e1 := w.NewEntity() + e2 := w.NewEntity() + + w.Assign(e0, ecs.Component{ID: 0, Comp: &testStruct0{1}}) + w.Assign(e1, ecs.Component{ID: 0, Comp: &testStruct0{2}}) + + w.Assign(e0, ecs.Component{ID: 1, Comp: &testStruct1{2}}) + w.Assign(e1, ecs.Component{ID: 1, Comp: &testStruct1{3}}) + + w.Assign(e0, ecs.Component{ID: 2, Comp: &testStruct2{3, 0}}) + w.Assign(e1, ecs.Component{ID: 2, Comp: &testStruct2{4, 0}}) + + w.Assign(e0, ecs.Component{ID: 3, Comp: &testStruct3{4, 0, 0}}) + w.Assign(e1, ecs.Component{ID: 3, Comp: &testStruct3{5, 0, 0}}) + + w.Assign(e0, ecs.Component{ID: 4, Comp: &testStruct4{5, 0, 0, 0}}) + w.Assign(e1, ecs.Component{ID: 4, Comp: &testStruct4{6, 0, 0, 0}}) + + w.Assign(e0, ecs.Component{ID: 5, Comp: &testStruct5{6, 0, 0, 0, 0}}) + w.Assign(e1, ecs.Component{ID: 5, Comp: &testStruct5{7, 0, 0, 0, 0}}) + + w.Assign(e0, ecs.Component{ID: 6, Comp: &testStruct6{7, 0, 0, 0, 0, 0}}) + w.Assign(e1, ecs.Component{ID: 6, Comp: &testStruct6{8, 0, 0, 0, 0, 0}}) + + w.Assign(e0, ecs.Component{ID: 7, Comp: &testStruct7{8, 0, 0, 0, 0, 0, 0}}) + w.Assign(e1, ecs.Component{ID: 7, Comp: &testStruct7{9, 0, 0, 0, 0, 0, 0}}) + + w.Assign(e0, ecs.Component{ID: 8, Comp: &testStruct8{9, 0, 0, 0, 0, 0, 0, 0}}) + w.Assign(e1, ecs.Component{ID: 8, Comp: &testStruct8{10, 0, 0, 0, 0, 0, 0, 0}}) + + w.Assign(e0, ecs.Component{ID: 9, Comp: &testStruct9{10, 0, 0, 0, 0, 0, 0, 0, 0}}) + w.Assign(e1, ecs.Component{ID: 9, Comp: &testStruct9{11, 0, 0, 0, 0, 0, 0, 0, 0}}) + + w.Assign(e0, ecs.Component{ID: 10, Comp: &testStruct10{11, 0, 0, 0, 0, 0, 0, 0, 0, 0}}) + + w.Assign(e2, ecs.Component{ID: 11, Comp: &testStruct11{}}) + + cnt := 0 + filter := + NewFilter10[ + testStruct0, testStruct1, testStruct2, testStruct3, + testStruct4, testStruct5, testStruct6, testStruct7, + testStruct8, testStruct9, + ](). + Optional(T[testStruct1]()). + With(T[testStruct10]()). + Without(T[testStruct11]()) + + filter.Register(&w) + + query := filter.Query(&w) + for query.Next() { + c1, c2, c3, c4, c5, c6, c7, c8, c9, c10 := query.Get() + assert.Equal(t, cnt+1, int(c1.val)) + assert.Equal(t, cnt+2, int(c2.val)) + assert.Equal(t, cnt+3, int(c3.val)) + assert.Equal(t, cnt+4, int(c4.val)) + assert.Equal(t, cnt+5, int(c5.val)) + assert.Equal(t, cnt+6, int(c6.val)) + assert.Equal(t, cnt+7, int(c7.val)) + assert.Equal(t, cnt+8, int(c8.val)) + assert.Equal(t, cnt+9, int(c9.val)) + assert.Equal(t, cnt+10, int(c10.val)) + assert.Panics(t, func() { query.Relation() }) + cnt++ + } + assert.Equal(t, 1, cnt) + + filter.Unregister(&w) + assert.Panics(t, func() { filter.Unregister(&w) }) + + targ := w.NewEntity(0) + + w.Add(e0, relID) + w.Add(e1, relID) + w.Add(e2, relID) + + w.Relations().Set(e0, relID, targ) + + filter2 := + NewFilter10[ + testStruct0, testStruct1, testStruct2, testStruct3, + testStruct4, testStruct5, testStruct6, testStruct7, + testStruct8, testRelationA, + ](). + WithRelation(T[testRelationA](), targ) + + q := filter2.Query(&w) + assert.Equal(t, 1, q.Count()) + for q.Next() { + trg := q.Relation() + assert.Equal(t, targ, trg) + } + + assert.Panics(t, func() { filter2.Query(&w, targ) }) + + filter2 = + NewFilter10[ + testStruct0, testStruct1, testStruct2, testStruct3, + testStruct4, testStruct5, testStruct6, testStruct7, + testStruct8, testRelationA, + ](). + WithRelation(T[testRelationA]()) + q = filter2.Query(&w) + assert.Equal(t, 2, q.Count()) + q.Close() + q = filter2.Query(&w, targ) + assert.Equal(t, 1, q.Count()) + q.Close() + + filter2.Register(&w) + assert.Panics(t, func() { filter2.Query(&w, targ) }) + assert.Panics(t, func() { filter2.Optional(T[testStruct0]()) }) + assert.Panics(t, func() { filter2.With(T[testStruct0]()) }) + assert.Panics(t, func() { filter2.Without(T[testStruct0]()) }) + assert.Panics(t, func() { filter2.WithRelation(T[testRelationA](), targ) }) + filter2.Unregister(&w) +} + +func TestQuery11(t *testing.T) { + w := ecs.NewWorld() + + registerAll(&w) + relID := ecs.ComponentID[testRelationA](&w) + + e0 := w.NewEntity() + e1 := w.NewEntity() + e2 := w.NewEntity() + + w.Assign(e0, ecs.Component{ID: 0, Comp: &testStruct0{1}}) + w.Assign(e1, ecs.Component{ID: 0, Comp: &testStruct0{2}}) + + w.Assign(e0, ecs.Component{ID: 1, Comp: &testStruct1{2}}) + w.Assign(e1, ecs.Component{ID: 1, Comp: &testStruct1{3}}) + + w.Assign(e0, ecs.Component{ID: 2, Comp: &testStruct2{3, 0}}) + w.Assign(e1, ecs.Component{ID: 2, Comp: &testStruct2{4, 0}}) + + w.Assign(e0, ecs.Component{ID: 3, Comp: &testStruct3{4, 0, 0}}) + w.Assign(e1, ecs.Component{ID: 3, Comp: &testStruct3{5, 0, 0}}) + + w.Assign(e0, ecs.Component{ID: 4, Comp: &testStruct4{5, 0, 0, 0}}) + w.Assign(e1, ecs.Component{ID: 4, Comp: &testStruct4{6, 0, 0, 0}}) + + w.Assign(e0, ecs.Component{ID: 5, Comp: &testStruct5{6, 0, 0, 0, 0}}) + w.Assign(e1, ecs.Component{ID: 5, Comp: &testStruct5{7, 0, 0, 0, 0}}) + + w.Assign(e0, ecs.Component{ID: 6, Comp: &testStruct6{7, 0, 0, 0, 0, 0}}) + w.Assign(e1, ecs.Component{ID: 6, Comp: &testStruct6{8, 0, 0, 0, 0, 0}}) + + w.Assign(e0, ecs.Component{ID: 7, Comp: &testStruct7{8, 0, 0, 0, 0, 0, 0}}) + w.Assign(e1, ecs.Component{ID: 7, Comp: &testStruct7{9, 0, 0, 0, 0, 0, 0}}) + + w.Assign(e0, ecs.Component{ID: 8, Comp: &testStruct8{9, 0, 0, 0, 0, 0, 0, 0}}) + w.Assign(e1, ecs.Component{ID: 8, Comp: &testStruct8{10, 0, 0, 0, 0, 0, 0, 0}}) + + w.Assign(e0, ecs.Component{ID: 9, Comp: &testStruct9{10, 0, 0, 0, 0, 0, 0, 0, 0}}) + w.Assign(e1, ecs.Component{ID: 9, Comp: &testStruct9{11, 0, 0, 0, 0, 0, 0, 0, 0}}) + + w.Assign(e0, ecs.Component{ID: 10, Comp: &testStruct10{11, 0, 0, 0, 0, 0, 0, 0, 0, 0}}) + w.Assign(e1, ecs.Component{ID: 10, Comp: &testStruct10{12, 0, 0, 0, 0, 0, 0, 0, 0, 0}}) + + w.Assign(e0, ecs.Component{ID: 11, Comp: &testStruct11{12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}) + + w.Assign(e2, ecs.Component{ID: 12, Comp: &testStruct12{}}) + + cnt := 0 + filter := + NewFilter11[ + testStruct0, testStruct1, testStruct2, testStruct3, + testStruct4, testStruct5, testStruct6, testStruct7, + testStruct8, testStruct9, testStruct10, + ](). + Optional(T[testStruct1]()). + With(T[testStruct11]()). + Without(T[testStruct12]()) + + filter.Register(&w) + + query := filter.Query(&w) + for query.Next() { + c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11 := query.Get() + assert.Equal(t, cnt+1, int(c1.val)) + assert.Equal(t, cnt+2, int(c2.val)) + assert.Equal(t, cnt+3, int(c3.val)) + assert.Equal(t, cnt+4, int(c4.val)) + assert.Equal(t, cnt+5, int(c5.val)) + assert.Equal(t, cnt+6, int(c6.val)) + assert.Equal(t, cnt+7, int(c7.val)) + assert.Equal(t, cnt+8, int(c8.val)) + assert.Equal(t, cnt+9, int(c9.val)) + assert.Equal(t, cnt+10, int(c10.val)) + assert.Equal(t, cnt+11, int(c11.val)) + assert.Panics(t, func() { query.Relation() }) + cnt++ + } + assert.Equal(t, 1, cnt) + + filter.Unregister(&w) + assert.Panics(t, func() { filter.Unregister(&w) }) + + targ := w.NewEntity(0) + + w.Add(e0, relID) + w.Add(e1, relID) + w.Add(e2, relID) + + w.Relations().Set(e0, relID, targ) + + filter2 := + NewFilter11[ + testStruct0, testStruct1, testStruct2, testStruct3, + testStruct4, testStruct5, testStruct6, testStruct7, + testStruct8, testStruct9, testRelationA, + ](). + WithRelation(T[testRelationA](), targ) + + q := filter2.Query(&w) + assert.Equal(t, 1, q.Count()) + for q.Next() { + trg := q.Relation() + assert.Equal(t, targ, trg) + } + + assert.Panics(t, func() { filter2.Query(&w, targ) }) + + filter2 = + NewFilter11[ + testStruct0, testStruct1, testStruct2, testStruct3, + testStruct4, testStruct5, testStruct6, testStruct7, + testStruct8, testStruct9, testRelationA, + ](). + WithRelation(T[testRelationA]()) + q = filter2.Query(&w) + assert.Equal(t, 2, q.Count()) + q.Close() + q = filter2.Query(&w, targ) + assert.Equal(t, 1, q.Count()) + q.Close() + + filter2.Register(&w) + assert.Panics(t, func() { filter2.Query(&w, targ) }) + assert.Panics(t, func() { filter2.Optional(T[testStruct0]()) }) + assert.Panics(t, func() { filter2.With(T[testStruct0]()) }) + assert.Panics(t, func() { filter2.Without(T[testStruct0]()) }) + assert.Panics(t, func() { filter2.WithRelation(T[testRelationA](), targ) }) + filter2.Unregister(&w) +} + +func TestQuery12(t *testing.T) { + w := ecs.NewWorld() + + registerAll(&w) + relID := ecs.ComponentID[testRelationA](&w) + + e0 := w.NewEntity() + e1 := w.NewEntity() + e2 := w.NewEntity() + + w.Assign(e0, ecs.Component{ID: 0, Comp: &testStruct0{1}}) + w.Assign(e1, ecs.Component{ID: 0, Comp: &testStruct0{2}}) + + w.Assign(e0, ecs.Component{ID: 1, Comp: &testStruct1{2}}) + w.Assign(e1, ecs.Component{ID: 1, Comp: &testStruct1{3}}) + + w.Assign(e0, ecs.Component{ID: 2, Comp: &testStruct2{3, 0}}) + w.Assign(e1, ecs.Component{ID: 2, Comp: &testStruct2{4, 0}}) + + w.Assign(e0, ecs.Component{ID: 3, Comp: &testStruct3{4, 0, 0}}) + w.Assign(e1, ecs.Component{ID: 3, Comp: &testStruct3{5, 0, 0}}) + + w.Assign(e0, ecs.Component{ID: 4, Comp: &testStruct4{5, 0, 0, 0}}) + w.Assign(e1, ecs.Component{ID: 4, Comp: &testStruct4{6, 0, 0, 0}}) + + w.Assign(e0, ecs.Component{ID: 5, Comp: &testStruct5{6, 0, 0, 0, 0}}) + w.Assign(e1, ecs.Component{ID: 5, Comp: &testStruct5{7, 0, 0, 0, 0}}) + + w.Assign(e0, ecs.Component{ID: 6, Comp: &testStruct6{7, 0, 0, 0, 0, 0}}) + w.Assign(e1, ecs.Component{ID: 6, Comp: &testStruct6{8, 0, 0, 0, 0, 0}}) + + w.Assign(e0, ecs.Component{ID: 7, Comp: &testStruct7{8, 0, 0, 0, 0, 0, 0}}) + w.Assign(e1, ecs.Component{ID: 7, Comp: &testStruct7{9, 0, 0, 0, 0, 0, 0}}) + + w.Assign(e0, ecs.Component{ID: 8, Comp: &testStruct8{9, 0, 0, 0, 0, 0, 0, 0}}) + w.Assign(e1, ecs.Component{ID: 8, Comp: &testStruct8{10, 0, 0, 0, 0, 0, 0, 0}}) + + w.Assign(e0, ecs.Component{ID: 9, Comp: &testStruct9{10, 0, 0, 0, 0, 0, 0, 0, 0}}) + w.Assign(e1, ecs.Component{ID: 9, Comp: &testStruct9{11, 0, 0, 0, 0, 0, 0, 0, 0}}) + + w.Assign(e0, ecs.Component{ID: 10, Comp: &testStruct10{11, 0, 0, 0, 0, 0, 0, 0, 0, 0}}) + w.Assign(e1, ecs.Component{ID: 10, Comp: &testStruct10{12, 0, 0, 0, 0, 0, 0, 0, 0, 0}}) + + w.Assign(e0, ecs.Component{ID: 11, Comp: &testStruct11{12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}) + w.Assign(e1, ecs.Component{ID: 11, Comp: &testStruct11{13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}) + + w.Assign(e0, ecs.Component{ID: 12, Comp: &testStruct12{13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}) + + w.Assign(e2, ecs.Component{ID: 13, Comp: &testStruct13{}}) + + cnt := 0 + filter := + NewFilter12[ + testStruct0, testStruct1, testStruct2, testStruct3, + testStruct4, testStruct5, testStruct6, testStruct7, + testStruct8, testStruct9, testStruct10, testStruct11, + ](). + Optional(T[testStruct1]()). + With(T[testStruct12]()). + Without(T[testStruct13]()) + + filter.Register(&w) + + query := filter.Query(&w) + for query.Next() { + c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12 := query.Get() + assert.Equal(t, cnt+1, int(c1.val)) + assert.Equal(t, cnt+2, int(c2.val)) + assert.Equal(t, cnt+3, int(c3.val)) + assert.Equal(t, cnt+4, int(c4.val)) + assert.Equal(t, cnt+5, int(c5.val)) + assert.Equal(t, cnt+6, int(c6.val)) + assert.Equal(t, cnt+7, int(c7.val)) + assert.Equal(t, cnt+8, int(c8.val)) + assert.Equal(t, cnt+9, int(c9.val)) + assert.Equal(t, cnt+10, int(c10.val)) + assert.Equal(t, cnt+11, int(c11.val)) + assert.Equal(t, cnt+12, int(c12.val)) + assert.Panics(t, func() { query.Relation() }) + cnt++ + } + assert.Equal(t, 1, cnt) + + filter.Unregister(&w) + assert.Panics(t, func() { filter.Unregister(&w) }) + + targ := w.NewEntity(0) + + w.Add(e0, relID) + w.Add(e1, relID) + w.Add(e2, relID) + + w.Relations().Set(e0, relID, targ) + + filter2 := + NewFilter12[ + testStruct0, testStruct1, testStruct2, testStruct3, + testStruct4, testStruct5, testStruct6, testStruct7, + testStruct8, testStruct9, testStruct10, testRelationA, + ](). + WithRelation(T[testRelationA](), targ) + + q := filter2.Query(&w) + assert.Equal(t, 1, q.Count()) + for q.Next() { + trg := q.Relation() + assert.Equal(t, targ, trg) + } + + assert.Panics(t, func() { filter2.Query(&w, targ) }) + + filter2 = + NewFilter12[ + testStruct0, testStruct1, testStruct2, testStruct3, + testStruct4, testStruct5, testStruct6, testStruct7, + testStruct8, testStruct9, testStruct10, testRelationA, + ](). + WithRelation(T[testRelationA]()) + q = filter2.Query(&w) + assert.Equal(t, 2, q.Count()) + q.Close() + q = filter2.Query(&w, targ) + assert.Equal(t, 1, q.Count()) + q.Close() + + filter2.Register(&w) + assert.Panics(t, func() { filter2.Query(&w, targ) }) + assert.Panics(t, func() { filter2.Optional(T[testStruct0]()) }) + assert.Panics(t, func() { filter2.With(T[testStruct0]()) }) + assert.Panics(t, func() { filter2.Without(T[testStruct0]()) }) + assert.Panics(t, func() { filter2.WithRelation(T[testRelationA](), targ) }) + filter2.Unregister(&w) +} + func TestQueryGeneric(t *testing.T) { count := 1000 world := ecs.NewWorld() @@ -1019,8 +1531,12 @@ func registerAll(w *ecs.World) []ecs.ID { _ = testStruct7{} _ = testStruct8{} _ = testStruct9{} + _ = testStruct10{} + _ = testStruct11{} + _ = testStruct12{} + _ = testStruct13{} - ids := make([]ecs.ID, 10) + ids := make([]ecs.ID, 14) ids[0] = ecs.ComponentID[testStruct0](w) ids[1] = ecs.ComponentID[testStruct1](w) ids[2] = ecs.ComponentID[testStruct2](w) @@ -1031,6 +1547,10 @@ func registerAll(w *ecs.World) []ecs.ID { ids[7] = ecs.ComponentID[testStruct7](w) ids[8] = ecs.ComponentID[testStruct8](w) ids[9] = ecs.ComponentID[testStruct9](w) + ids[10] = ecs.ComponentID[testStruct10](w) + ids[11] = ecs.ComponentID[testStruct11](w) + ids[12] = ecs.ComponentID[testStruct12](w) + ids[13] = ecs.ComponentID[testStruct13](w) return ids } @@ -1116,3 +1636,65 @@ type testStruct9 struct { val8 int32 val9 int32 } + +//lint:ignore U1000 test type +type testStruct10 struct { + val int32 + val2 int32 + val3 int32 + val4 int32 + val5 int32 + val6 int32 + val7 int32 + val8 int32 + val9 int32 + val10 int32 +} + +//lint:ignore U1000 test type +type testStruct11 struct { + val int32 + val2 int32 + val3 int32 + val4 int32 + val5 int32 + val6 int32 + val7 int32 + val8 int32 + val9 int32 + val10 int32 + val11 int32 +} + +//lint:ignore U1000 test type +type testStruct12 struct { + val int32 + val2 int32 + val3 int32 + val4 int32 + val5 int32 + val6 int32 + val7 int32 + val8 int32 + val9 int32 + val10 int32 + val11 int32 + val12 int32 +} + +//lint:ignore U1000 test type +type testStruct13 struct { + val int32 + val2 int32 + val3 int32 + val4 int32 + val5 int32 + val6 int32 + val7 int32 + val8 int32 + val9 int32 + val10 int32 + val11 int32 + val12 int32 + val13 int32 +}