From 85f8228647bea7dac360892b11a7d5670f68409b Mon Sep 17 00:00:00 2001 From: mlange-42 Date: Thu, 18 Jan 2024 17:17:48 +0100 Subject: [PATCH 01/12] allow for batch-exchange with relation target --- ecs/batch.go | 43 +++++- ecs/world_internal.go | 303 +++++++++++++++++++++++------------------- ecs/world_test.go | 9 +- 3 files changed, 211 insertions(+), 144 deletions(-) diff --git a/ecs/batch.go b/ecs/batch.go index b8dbae1a..0b477561 100644 --- a/ecs/batch.go +++ b/ecs/batch.go @@ -15,7 +15,7 @@ type Batch struct { // // See also [Batch.AddQ] and [World.Add]. func (b *Batch) Add(filter Filter, comps ...ID) { - b.world.exchangeBatch(filter, comps, nil) + b.world.exchangeBatch(filter, comps, nil, ID{}, false, Entity{}) } // AddQ adds components to many entities, matching a filter. @@ -27,7 +27,7 @@ func (b *Batch) Add(filter Filter, comps ...ID) { // // See also [Batch.Add] and [World.Add]. func (b *Batch) AddQ(filter Filter, comps ...ID) Query { - return b.world.exchangeBatchQuery(filter, comps, nil) + return b.world.exchangeBatchQuery(filter, comps, nil, ID{}, false, Entity{}) } // Remove removes components from many entities, matching a filter. @@ -38,7 +38,7 @@ func (b *Batch) AddQ(filter Filter, comps ...ID) Query { // // See also [Batch.RemoveQ] and [World.Remove]. func (b *Batch) Remove(filter Filter, comps ...ID) { - b.world.exchangeBatch(filter, nil, comps) + b.world.exchangeBatch(filter, nil, comps, ID{}, false, Entity{}) } // RemoveQ removes components from many entities, matching a filter. @@ -50,7 +50,7 @@ func (b *Batch) Remove(filter Filter, comps ...ID) { // // See also [Batch.Remove] and [World.Remove]. func (b *Batch) RemoveQ(filter Filter, comps ...ID) Query { - return b.world.exchangeBatchQuery(filter, nil, comps) + return b.world.exchangeBatchQuery(filter, nil, comps, ID{}, false, Entity{}) } // SetRelation sets the [Relation] target for many entities, matching a filter. @@ -95,7 +95,7 @@ func (b *Batch) SetRelationQ(filter Filter, comp ID, target Entity) Query { // // See also [Batch.ExchangeQ] and [World.Exchange]. func (b *Batch) Exchange(filter Filter, add []ID, rem []ID) { - b.world.exchangeBatch(filter, add, rem) + b.world.exchangeBatch(filter, add, rem, ID{}, false, Entity{}) } // ExchangeQ exchanges components for many entities, matching a filter. @@ -110,7 +110,38 @@ func (b *Batch) Exchange(filter Filter, add []ID, rem []ID) { // // See also [Batch.Exchange] and [World.Exchange]. func (b *Batch) ExchangeQ(filter Filter, add []ID, rem []ID) Query { - return b.world.exchangeBatchQuery(filter, add, rem) + return b.world.exchangeBatchQuery(filter, add, rem, ID{}, false, Entity{}) +} + +// ExchangeRelation exchanges components for many entities, matching a filter. +// In contrast to [Batch.Exchange], it allows to also set a relation target. +// +// Panics: +// - when called with components that can't be added or removed because they are already present/not present, respectively. +// - when called for a missing relation component. +// - when called for a component that is not a relation. +// - when called without any components to add or remove. Use [Batch.SetRelation] instead. +// - when called on a locked world. Do not use during [Query] iteration! +// +// See also [Batch.Exchange], [Batch.ExchangeQ], [Batch.ExchangeRelationQ] and [World.Exchange]. +func (b *Batch) ExchangeRelation(filter Filter, add []ID, rem []ID, relation ID, target Entity) { + b.world.exchangeBatch(filter, add, rem, relation, true, target) +} + +// ExchangeRelationQ exchanges components for many entities, matching a filter. +// It returns a query over the affected entities. +// In contrast to [Batch.ExchangeQ], it allows to also set a relation target. +// +// Panics: +// - when called with components that can't be added or removed because they are already present/not present, respectively. +// - when called for a missing relation component. +// - when called for a component that is not a relation. +// - when called without any components to add or remove. Use [Batch.SetRelationQ] instead. +// - when called on a locked world. Do not use during [Query] iteration! +// +// See also [Batch.Exchange], [Batch.ExchangeQ], [Batch.ExchangeRelation] and [World.Exchange]. +func (b *Batch) ExchangeRelationQ(filter Filter, add []ID, rem []ID, relation ID, target Entity) Query { + return b.world.exchangeBatchQuery(filter, add, rem, relation, true, target) } // RemoveEntities removes and recycles all entities matching a filter. diff --git a/ecs/world_internal.go b/ecs/world_internal.go index c461d264..107b81d1 100644 --- a/ecs/world_internal.go +++ b/ecs/world_internal.go @@ -198,6 +198,127 @@ func (w *World) newEntitiesWithQuery(count int, targetID ID, hasTarget bool, tar return newBatchQuery(w, lock, &batches) } +// Internal method to create new entities. +func (w *World) newEntitiesNoNotify(count int, targetID ID, hasTarget bool, target Entity, comps ...ID) (*archetype, uint32) { + w.checkLocked() + + if count < 1 { + panic("can only create a positive number of entities") + } + + if !target.IsZero() && !w.entityPool.Alive(target) { + panic("can't make a dead entity a relation target") + } + + arch := w.archetypes.Get(0) + if len(comps) > 0 { + arch = w.findOrCreateArchetype(arch, comps, nil, target) + } + if hasTarget { + w.checkRelation(arch, targetID) + if !target.IsZero() { + w.targetEntities.Set(target.id, true) + } + } + + startIdx := arch.Len() + w.createEntities(arch, uint32(count)) + + return arch, startIdx +} + +// Internal method to create new entities with component values. +func (w *World) newEntitiesWithNoNotify(count int, targetID ID, hasTarget bool, target Entity, ids []ID, comps ...Component) (*archetype, uint32) { + w.checkLocked() + + if count < 1 { + panic("can only create a positive number of entities") + } + + if !target.IsZero() && !w.entityPool.Alive(target) { + panic("can't make a dead entity a relation target") + } + + if len(comps) == 0 { + return w.newEntitiesNoNotify(count, targetID, hasTarget, target) + } + + cnt := uint32(count) + + arch := w.archetypes.Get(0) + if len(comps) > 0 { + arch = w.findOrCreateArchetype(arch, ids, nil, target) + } + if hasTarget { + w.checkRelation(arch, targetID) + if !target.IsZero() { + w.targetEntities.Set(target.id, true) + } + } + + startIdx := arch.Len() + w.createEntities(arch, uint32(count)) + + var i uint32 + for i = 0; i < cnt; i++ { + idx := startIdx + i + entity := arch.GetEntity(idx) + for _, c := range comps { + w.copyTo(entity, c.ID, c.Comp) + } + } + + return arch, startIdx +} + +// createEntity creates an Entity and adds it to the given archetype. +func (w *World) createEntity(arch *archetype) Entity { + entity := w.entityPool.Get() + idx := arch.Alloc(entity) + len := len(w.entities) + if int(entity.id) == len { + if len == cap(w.entities) { + old := w.entities + w.entities = make([]entityIndex, len, len+w.config.CapacityIncrement) + copy(w.entities, old) + + } + w.entities = append(w.entities, entityIndex{arch: arch, index: idx}) + w.targetEntities.ExtendTo(len + w.config.CapacityIncrement) + } else { + w.entities[entity.id] = entityIndex{arch: arch, index: idx} + w.targetEntities.Set(entity.id, false) + } + return entity +} + +// createEntity creates multiple Entities and adds them to the given archetype. +func (w *World) createEntities(arch *archetype, count uint32) { + startIdx := arch.Len() + arch.AllocN(count) + + len := len(w.entities) + required := len + int(count) - w.entityPool.Available() + capacity := capacity(required, w.config.CapacityIncrement) + if required > cap(w.entities) { + old := w.entities + w.entities = make([]entityIndex, required, capacity) + copy(w.entities, old) + } else if required > len { + w.entities = w.entities[:required] + } + w.targetEntities.ExtendTo(capacity) + + var i uint32 + for i = 0; i < count; i++ { + idx := startIdx + i + entity := w.entityPool.Get() + arch.SetEntity(idx, entity) + w.entities[entity.id] = entityIndex{arch: arch, index: idx} + w.targetEntities.Set(entity.id, false) + } +} + // RemoveEntities removes and recycles all entities matching a filter. // // Returns the number of removed entities. @@ -351,6 +472,10 @@ func (w *World) exchange(entity Entity, add []ID, rem []ID, relation ID, hasRela } oldTarget := oldArch.RelationTarget + if !target.IsZero() { + w.targetEntities.Set(target.id, true) + } + w.cleanupArchetype(oldArch) if w.listener != nil { @@ -408,35 +533,38 @@ func (w *World) getExchangeMask(mask Mask, add []ID, rem []ID) Mask { // - when called on a locked world. Do not use during [Query] iteration! // // See also [World.Exchange]. -func (w *World) exchangeBatch(filter Filter, add []ID, rem []ID) { +func (w *World) exchangeBatch(filter Filter, add []ID, rem []ID, relation ID, hasRelation bool, target Entity) { batches := batchArchetypes{ Added: add, Removed: rem, } - w.exchangeBatchNoNotify(filter, add, rem, &batches) + w.exchangeBatchNoNotify(filter, add, rem, relation, hasRelation, target, &batches) if w.listener != nil { w.notifyQuery(&batches) } } -func (w *World) exchangeBatchQuery(filter Filter, add []ID, rem []ID) Query { +func (w *World) exchangeBatchQuery(filter Filter, add []ID, rem []ID, relation ID, hasRelation bool, target Entity) Query { batches := batchArchetypes{ Added: add, Removed: rem, } - w.exchangeBatchNoNotify(filter, add, rem, &batches) + w.exchangeBatchNoNotify(filter, add, rem, relation, hasRelation, target, &batches) lock := w.lock() return newBatchQuery(w, lock, &batches) } -func (w *World) exchangeBatchNoNotify(filter Filter, add []ID, rem []ID, batches *batchArchetypes) { +func (w *World) exchangeBatchNoNotify(filter Filter, add []ID, rem []ID, relation ID, hasRelation bool, target Entity, batches *batchArchetypes) { w.checkLocked() if len(add) == 0 && len(rem) == 0 { + if hasRelation { + panic("exchange operation has no effect, but a relation is specified. Use Batch.SetRelation instead") + } return } @@ -453,22 +581,33 @@ func (w *World) exchangeBatchNoNotify(filter Filter, add []ID, rem []ID, batches continue } - newArch, start := w.exchangeArch(arch, archLen, add, rem) + newArch, start := w.exchangeArch(arch, archLen, add, rem, relation, hasRelation, target) batches.Add(newArch, arch, start, newArch.Len()) } } -func (w *World) exchangeArch(oldArch *archetype, oldArchLen uint32, add []ID, rem []ID) (*archetype, uint32) { +func (w *World) exchangeArch(oldArch *archetype, oldArchLen uint32, add []ID, rem []ID, relation ID, hasRelation bool, target Entity) (*archetype, uint32) { mask := w.getExchangeMask(oldArch.Mask, add, rem) oldIDs := oldArch.Components() - target := oldArch.RelationTarget - if !target.IsZero() && oldArch.Mask.ContainsAny(&w.registry.IsRelation) { - for _, id := range rem { - // Removing a relation - if w.registry.IsRelation.Get(id) { - target = Entity{} - break + if hasRelation { + if !mask.Get(relation) { + tp, _ := w.registry.ComponentType(relation.id) + panic(fmt.Sprintf("can't add relation: resulting entity has no component %s", tp.Name())) + } + if !w.registry.IsRelation.Get(relation) { + tp, _ := w.registry.ComponentType(relation.id) + panic(fmt.Sprintf("can't add relation: %s is not a relation component", tp.Name())) + } + } else { + target = oldArch.RelationTarget + if !target.IsZero() && oldArch.Mask.ContainsAny(&w.registry.IsRelation) { + for _, id := range rem { + // Removing a relation + if w.registry.IsRelation.Get(id) { + target = Entity{} + break + } } } } @@ -496,6 +635,10 @@ func (w *World) exchangeArch(oldArch *archetype, oldArchLen uint32, add []ID, re } } + if !target.IsZero() { + w.targetEntities.Set(target.id, true) + } + // Theoretically, it could be oldArchLen < oldArch.Len(), // which means we can't reset the archetype. // However, this should not be possible as processing an entity twice @@ -581,7 +724,10 @@ func (w *World) setRelation(entity Entity, comp ID, target Entity) { w.entities[swapEntity.id].index = index.index } w.entities[entity.id] = entityIndex{arch: arch, index: newIndex} - w.targetEntities.Set(target.id, true) + + if !target.IsZero() { + w.targetEntities.Set(target.id, true) + } oldTarget := oldArch.RelationTarget w.cleanupArchetype(oldArch) @@ -643,7 +789,7 @@ func (w *World) setRelationArch(oldArch *archetype, oldArchLen uint32, comp ID, w.checkRelation(oldArch, comp) // Before, entities with unchanged target were included in the query, - // end events were emitted for them. Seems better to skip them completely, + // and events were emitted for them. Seems better to skip them completely, // which is done in World.setRelationBatchNoNotify. //if oldArch.RelationTarget == target { // return oldArch, 0, oldArchLen @@ -675,6 +821,10 @@ func (w *World) setRelationArch(oldArch *archetype, oldArchLen uint32, comp ID, } } + if !target.IsZero() { + w.targetEntities.Set(target.id, true) + } + // Theoretically, it could be oldArchLen < oldArch.Len(), // which means we can't reset the archetype. // However, this should not be possible as processing an entity twice @@ -715,127 +865,6 @@ func (w *World) checkLocked() { } } -// Internal method to create new entities. -func (w *World) newEntitiesNoNotify(count int, targetID ID, hasTarget bool, target Entity, comps ...ID) (*archetype, uint32) { - w.checkLocked() - - if count < 1 { - panic("can only create a positive number of entities") - } - - if !target.IsZero() && !w.entityPool.Alive(target) { - panic("can't make a dead entity a relation target") - } - - arch := w.archetypes.Get(0) - if len(comps) > 0 { - arch = w.findOrCreateArchetype(arch, comps, nil, target) - } - if hasTarget { - w.checkRelation(arch, targetID) - if !target.IsZero() { - w.targetEntities.Set(target.id, true) - } - } - - startIdx := arch.Len() - w.createEntities(arch, uint32(count)) - - return arch, startIdx -} - -// Internal method to create new entities with component values. -func (w *World) newEntitiesWithNoNotify(count int, targetID ID, hasTarget bool, target Entity, ids []ID, comps ...Component) (*archetype, uint32) { - w.checkLocked() - - if count < 1 { - panic("can only create a positive number of entities") - } - - if !target.IsZero() && !w.entityPool.Alive(target) { - panic("can't make a dead entity a relation target") - } - - if len(comps) == 0 { - return w.newEntitiesNoNotify(count, targetID, hasTarget, target) - } - - cnt := uint32(count) - - arch := w.archetypes.Get(0) - if len(comps) > 0 { - arch = w.findOrCreateArchetype(arch, ids, nil, target) - } - if hasTarget { - w.checkRelation(arch, targetID) - if !target.IsZero() { - w.targetEntities.Set(target.id, true) - } - } - - startIdx := arch.Len() - w.createEntities(arch, uint32(count)) - - var i uint32 - for i = 0; i < cnt; i++ { - idx := startIdx + i - entity := arch.GetEntity(idx) - for _, c := range comps { - w.copyTo(entity, c.ID, c.Comp) - } - } - - return arch, startIdx -} - -// createEntity creates an Entity and adds it to the given archetype. -func (w *World) createEntity(arch *archetype) Entity { - entity := w.entityPool.Get() - idx := arch.Alloc(entity) - len := len(w.entities) - if int(entity.id) == len { - if len == cap(w.entities) { - old := w.entities - w.entities = make([]entityIndex, len, len+w.config.CapacityIncrement) - copy(w.entities, old) - - } - w.entities = append(w.entities, entityIndex{arch: arch, index: idx}) - w.targetEntities.ExtendTo(len + w.config.CapacityIncrement) - } else { - w.entities[entity.id] = entityIndex{arch: arch, index: idx} - w.targetEntities.Set(entity.id, false) - } - return entity -} - -// createEntity creates multiple Entities and adds them to the given archetype. -func (w *World) createEntities(arch *archetype, count uint32) { - startIdx := arch.Len() - arch.AllocN(count) - - len := len(w.entities) - required := len + int(count) - w.entityPool.Available() - capacity := capacity(required, w.config.CapacityIncrement) - if required > cap(w.entities) { - old := w.entities - w.entities = make([]entityIndex, required, capacity) - copy(w.entities, old) - } else if required > len { - w.entities = w.entities[:required] - } - w.targetEntities.ExtendTo(capacity) - - var i uint32 - for i = 0; i < count; i++ { - idx := startIdx + i - entity := w.entityPool.Get() - arch.SetEntity(idx, entity) - w.entities[entity.id] = entityIndex{arch: arch, index: idx} - w.targetEntities.Set(entity.id, false) - } -} - // Copies a component to an entity func (w *World) copyTo(entity Entity, id ID, comp interface{}) unsafe.Pointer { if !w.Has(entity, id) { diff --git a/ecs/world_test.go b/ecs/world_test.go index 91a0a197..71551eea 100644 --- a/ecs/world_test.go +++ b/ecs/world_test.go @@ -314,12 +314,19 @@ func TestWorldExchangeBatch(t *testing.T) { }, events[201]) filter := All(posID, relID) - query := w.Batch().ExchangeQ(filter, []ID{velID}, []ID{posID}) + query := w.Query(filter) + assert.Equal(t, 200, query.Count()) + for query.Next() { + assert.False(t, query.Relation(relID).IsZero()) + } + + query = w.Batch().ExchangeQ(filter, []ID{velID}, []ID{posID}) assert.Equal(t, 200, query.Count()) for query.Next() { assert.True(t, query.Has(velID)) assert.True(t, query.Has(relID)) assert.False(t, query.Has(posID)) + assert.False(t, query.Relation(relID).IsZero()) } query = w.Query(All(posID)) From 97b04a7d482fb2d48a6a81d0edff7220d87607a0 Mon Sep 17 00:00:00 2001 From: mlange-42 Date: Thu, 18 Jan 2024 17:34:49 +0100 Subject: [PATCH 02/12] add tests for batch-exchange with relation target --- ecs/batch_examples_test.go | 174 +++++++++++++++++++++++++++++ ecs/batch_test.go | 221 +++++++++---------------------------- 2 files changed, 225 insertions(+), 170 deletions(-) create mode 100644 ecs/batch_examples_test.go diff --git a/ecs/batch_examples_test.go b/ecs/batch_examples_test.go new file mode 100644 index 00000000..0e2070d4 --- /dev/null +++ b/ecs/batch_examples_test.go @@ -0,0 +1,174 @@ +package ecs_test + +import "github.com/mlange-42/arche/ecs" + +func ExampleBatch() { + world := ecs.NewWorld() + + posID := ecs.ComponentID[Position](&world) + velID := ecs.ComponentID[Velocity](&world) + + builder := ecs.NewBuilder(&world, posID, velID) + builder.NewBatch(100) + + world.Batch().Remove(ecs.All(posID, velID), velID) + world.Batch().RemoveEntities(ecs.All(posID)) + // Output: +} + +func ExampleBatch_Add() { + world := ecs.NewWorld() + + posID := ecs.ComponentID[Position](&world) + velID := ecs.ComponentID[Velocity](&world) + + builder := ecs.NewBuilder(&world, posID) + builder.NewBatch(100) + + filter := ecs.All(posID) + world.Batch().Add(filter, velID) + // Output: +} + +func ExampleBatch_AddQ() { + world := ecs.NewWorld() + + posID := ecs.ComponentID[Position](&world) + velID := ecs.ComponentID[Velocity](&world) + + builder := ecs.NewBuilder(&world, posID) + builder.NewBatch(100) + + filter := ecs.All(posID) + query := world.Batch().AddQ(filter, velID) + + for query.Next() { + pos := (*Position)(query.Get(posID)) + pos.X = 100 + } + // Output: +} + +func ExampleBatch_Remove() { + world := ecs.NewWorld() + + posID := ecs.ComponentID[Position](&world) + velID := ecs.ComponentID[Velocity](&world) + + builder := ecs.NewBuilder(&world, posID, velID) + builder.NewBatch(100) + + filter := ecs.All(posID, velID) + world.Batch().Remove(filter, velID) + // Output: +} + +func ExampleBatch_RemoveQ() { + world := ecs.NewWorld() + + posID := ecs.ComponentID[Position](&world) + velID := ecs.ComponentID[Velocity](&world) + + builder := ecs.NewBuilder(&world, posID, velID) + builder.NewBatch(100) + + filter := ecs.All(posID, velID) + query := world.Batch().RemoveQ(filter, velID) + + for query.Next() { + pos := (*Position)(query.Get(posID)) + pos.X = 100 + } + // Output: +} + +func ExampleBatch_Exchange() { + world := ecs.NewWorld() + + posID := ecs.ComponentID[Position](&world) + velID := ecs.ComponentID[Velocity](&world) + + builder := ecs.NewBuilder(&world, posID) + builder.NewBatch(100) + + filter := ecs.All(posID) + world.Batch().Exchange( + filter, // Filter + []ecs.ID{velID}, // Add components + []ecs.ID{posID}, // Remove components + ) + // Output: +} + +func ExampleBatch_ExchangeQ() { + world := ecs.NewWorld() + + posID := ecs.ComponentID[Position](&world) + velID := ecs.ComponentID[Velocity](&world) + + builder := ecs.NewBuilder(&world, posID) + builder.NewBatch(100) + + filter := ecs.All(posID) + query := world.Batch().ExchangeQ( + filter, // Filter + []ecs.ID{velID}, // Add components + []ecs.ID{posID}, // Remove components + ) + + for query.Next() { + vel := (*Velocity)(query.Get(velID)) + vel.X = 100 + } + // Output: +} + +func ExampleBatch_RemoveEntities() { + world := ecs.NewWorld() + + posID := ecs.ComponentID[Position](&world) + + builder := ecs.NewBuilder(&world, posID) + builder.NewBatch(100) + + filter := ecs.All(posID) + world.Batch().RemoveEntities(filter) + // Output: +} + +func ExampleBatch_SetRelation() { + world := ecs.NewWorld() + + posID := ecs.ComponentID[Position](&world) + childID := ecs.ComponentID[ChildOf](&world) + + target := world.NewEntity() + + builder := ecs.NewBuilder(&world, posID, childID) + builder.NewBatch(100) + + filter := ecs.All(childID) + world.Batch().SetRelation(filter, childID, target) + // Output: +} + +func ExampleBatch_SetRelationQ() { + world := ecs.NewWorld() + + posID := ecs.ComponentID[Position](&world) + childID := ecs.ComponentID[ChildOf](&world) + + target := world.NewEntity() + + builder := ecs.NewBuilder(&world, posID, childID) + builder.NewBatch(100) + + filter := ecs.All(childID) + query := world.Batch().SetRelationQ(filter, childID, target) + + for query.Next() { + pos := (*Position)(query.Get(posID)) + pos.X = 100 + } + // Output: +} diff --git a/ecs/batch_test.go b/ecs/batch_test.go index 0e2070d4..833b68e5 100644 --- a/ecs/batch_test.go +++ b/ecs/batch_test.go @@ -1,174 +1,55 @@ package ecs_test -import "github.com/mlange-42/arche/ecs" +import ( + "testing" + + "github.com/mlange-42/arche/ecs" + "github.com/stretchr/testify/assert" +) + +func TestBatchExchangeRelation(t *testing.T) { + w := ecs.NewWorld() + posID := ecs.ComponentID[Position](&w) + velID := ecs.ComponentID[Velocity](&w) + childID := ecs.ComponentID[ChildOf](&w) + + parent1 := w.NewEntity(posID) + parent2 := w.NewEntity(posID) + + builder := ecs.NewBuilder(&w, posID, childID).WithRelation(childID) + builder.NewBatch(10, parent1) + + relFilter := ecs.NewRelationFilter(ecs.All(posID, childID), parent1) + query := w.Query(&relFilter) + assert.Equal(t, 10, query.Count()) + query.Close() + + filter := ecs.All(posID, childID) + assert.Panics(t, func() { + w.Batch().ExchangeRelation(filter, nil, []ecs.ID{posID}, velID, parent2) + }) + assert.Panics(t, func() { + w.Batch().ExchangeRelation(filter, []ecs.ID{velID}, []ecs.ID{posID}, velID, parent2) + }) + assert.Panics(t, func() { + w.Batch().ExchangeRelation(filter, nil, nil, childID, parent2) + }) + assert.Panics(t, func() { + _ = w.Batch().ExchangeRelationQ(filter, nil, nil, childID, parent2) + }) + + w.Batch().ExchangeRelation(filter, []ecs.ID{velID}, nil, childID, parent2) + + relFilter = ecs.NewRelationFilter(ecs.All(posID, velID, childID), parent2) + query = w.Query(&relFilter) + assert.Equal(t, 10, query.Count()) + query.Close() + + w.Batch().ExchangeRelation(filter, nil, []ecs.ID{velID}, childID, parent1) + + relFilter = ecs.NewRelationFilter(ecs.All(posID, childID), parent1) + query = w.Query(&relFilter) + assert.Equal(t, 10, query.Count()) + query.Close() -func ExampleBatch() { - world := ecs.NewWorld() - - posID := ecs.ComponentID[Position](&world) - velID := ecs.ComponentID[Velocity](&world) - - builder := ecs.NewBuilder(&world, posID, velID) - builder.NewBatch(100) - - world.Batch().Remove(ecs.All(posID, velID), velID) - world.Batch().RemoveEntities(ecs.All(posID)) - // Output: -} - -func ExampleBatch_Add() { - world := ecs.NewWorld() - - posID := ecs.ComponentID[Position](&world) - velID := ecs.ComponentID[Velocity](&world) - - builder := ecs.NewBuilder(&world, posID) - builder.NewBatch(100) - - filter := ecs.All(posID) - world.Batch().Add(filter, velID) - // Output: -} - -func ExampleBatch_AddQ() { - world := ecs.NewWorld() - - posID := ecs.ComponentID[Position](&world) - velID := ecs.ComponentID[Velocity](&world) - - builder := ecs.NewBuilder(&world, posID) - builder.NewBatch(100) - - filter := ecs.All(posID) - query := world.Batch().AddQ(filter, velID) - - for query.Next() { - pos := (*Position)(query.Get(posID)) - pos.X = 100 - } - // Output: -} - -func ExampleBatch_Remove() { - world := ecs.NewWorld() - - posID := ecs.ComponentID[Position](&world) - velID := ecs.ComponentID[Velocity](&world) - - builder := ecs.NewBuilder(&world, posID, velID) - builder.NewBatch(100) - - filter := ecs.All(posID, velID) - world.Batch().Remove(filter, velID) - // Output: -} - -func ExampleBatch_RemoveQ() { - world := ecs.NewWorld() - - posID := ecs.ComponentID[Position](&world) - velID := ecs.ComponentID[Velocity](&world) - - builder := ecs.NewBuilder(&world, posID, velID) - builder.NewBatch(100) - - filter := ecs.All(posID, velID) - query := world.Batch().RemoveQ(filter, velID) - - for query.Next() { - pos := (*Position)(query.Get(posID)) - pos.X = 100 - } - // Output: -} - -func ExampleBatch_Exchange() { - world := ecs.NewWorld() - - posID := ecs.ComponentID[Position](&world) - velID := ecs.ComponentID[Velocity](&world) - - builder := ecs.NewBuilder(&world, posID) - builder.NewBatch(100) - - filter := ecs.All(posID) - world.Batch().Exchange( - filter, // Filter - []ecs.ID{velID}, // Add components - []ecs.ID{posID}, // Remove components - ) - // Output: -} - -func ExampleBatch_ExchangeQ() { - world := ecs.NewWorld() - - posID := ecs.ComponentID[Position](&world) - velID := ecs.ComponentID[Velocity](&world) - - builder := ecs.NewBuilder(&world, posID) - builder.NewBatch(100) - - filter := ecs.All(posID) - query := world.Batch().ExchangeQ( - filter, // Filter - []ecs.ID{velID}, // Add components - []ecs.ID{posID}, // Remove components - ) - - for query.Next() { - vel := (*Velocity)(query.Get(velID)) - vel.X = 100 - } - // Output: -} - -func ExampleBatch_RemoveEntities() { - world := ecs.NewWorld() - - posID := ecs.ComponentID[Position](&world) - - builder := ecs.NewBuilder(&world, posID) - builder.NewBatch(100) - - filter := ecs.All(posID) - world.Batch().RemoveEntities(filter) - // Output: -} - -func ExampleBatch_SetRelation() { - world := ecs.NewWorld() - - posID := ecs.ComponentID[Position](&world) - childID := ecs.ComponentID[ChildOf](&world) - - target := world.NewEntity() - - builder := ecs.NewBuilder(&world, posID, childID) - builder.NewBatch(100) - - filter := ecs.All(childID) - world.Batch().SetRelation(filter, childID, target) - // Output: -} - -func ExampleBatch_SetRelationQ() { - world := ecs.NewWorld() - - posID := ecs.ComponentID[Position](&world) - childID := ecs.ComponentID[ChildOf](&world) - - target := world.NewEntity() - - builder := ecs.NewBuilder(&world, posID, childID) - builder.NewBatch(100) - - filter := ecs.All(childID) - query := world.Batch().SetRelationQ(filter, childID, target) - - for query.Next() { - pos := (*Position)(query.Get(posID)) - pos.X = 100 - } - // Output: } From 604fca299ba085561c798dc9cdd9b108d3c8c2b8 Mon Sep 17 00:00:00 2001 From: mlange-42 Date: Thu, 18 Jan 2024 18:04:33 +0100 Subject: [PATCH 03/12] rename Builder.New to Builder.NewEntity --- CHANGELOG.md | 1 + benchmark/arche/fragmentation/arche_test.go | 4 +- ecs/builder.go | 44 ++++++++++----------- ecs/builder_test.go | 26 ++++++------ ecs/cache_test.go | 14 +++---- ecs/query_test.go | 2 +- ecs/relation_examples_test.go | 2 +- ecs/relation_stress_test.go | 2 +- examples/relations/main.go | 2 +- generic/map_generated.go | 24 +++++------ generic/util.go | 2 +- 11 files changed, 62 insertions(+), 61 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b4c2227..f6dd9d3d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ This change was necessary to get the same performance as before, despite the mor * Restructures `EntityEvent` to remove redundant information and better handle relation changes (#333) * World event listener changed from a simple function to a `Listener` interface (#334) * Removes `World.ComponentType(ID)`, use function `ComponentInfo(ID)` instead (#341) +* Renames `Builder.New` to `Builder.NewEntity` (#342) ### Features diff --git a/benchmark/arche/fragmentation/arche_test.go b/benchmark/arche/fragmentation/arche_test.go index 85d10d4c..a8424943 100644 --- a/benchmark/arche/fragmentation/arche_test.go +++ b/benchmark/arche/fragmentation/arche_test.go @@ -267,7 +267,7 @@ func runQuery1Of1kTargets(b *testing.B, count int) { childBuilder := ecs.NewBuilder(&world, posID, relID).WithRelation(relID) for _, target := range targets { - childBuilder.New(target) + childBuilder.NewEntity(target) } target := targets[0] childBuilder.NewBatch(count, target) @@ -299,7 +299,7 @@ func runQuery1Of1kTargetsCached(b *testing.B, count int) { childBuilder := ecs.NewBuilder(&world, posID, relID).WithRelation(relID) for _, target := range targets { - childBuilder.New(target) + childBuilder.NewEntity(target) } target := targets[0] diff --git a/ecs/builder.go b/ecs/builder.go index 6bf844c3..7df83f3a 100644 --- a/ecs/builder.go +++ b/ecs/builder.go @@ -2,11 +2,11 @@ package ecs // Builder for more flexible and batched entity creation. type Builder struct { - world *World - ids []ID - comps []Component - hasTarget bool - targetID ID + world *World + ids []ID + comps []Component + hasRelation bool + relationID ID } // NewBuilder creates a builder from component IDs. @@ -29,28 +29,28 @@ func NewBuilderWith(w *World, comps ...Component) *Builder { // WithRelation sets the [Relation] component for the builder. // -// Use in conjunction with the optional target argument of [Builder.New], [Builder.NewBatch] and [Builder.NewBatchQ]. +// Use in conjunction with the optional target argument of [Builder.NewEntity], [Builder.NewBatch] and [Builder.NewBatchQ]. // // See [Relation] for details and examples. func (b *Builder) WithRelation(comp ID) *Builder { - b.hasTarget = true - b.targetID = comp + b.hasRelation = true + b.relationID = comp return b } -// New creates an entity. +// NewEntity creates an entity. // // The optional argument can be used to set the target [Entity] for the Builder's [Relation]. // See [Builder.WithRelation]. -func (b *Builder) New(target ...Entity) Entity { +func (b *Builder) NewEntity(target ...Entity) Entity { if len(target) > 0 { - if !b.hasTarget { + if !b.hasRelation { panic("can't set target entity: builder has no relation") } if b.comps == nil { - return b.world.newEntityTarget(b.targetID, target[0], b.ids...) + return b.world.newEntityTarget(b.relationID, target[0], b.ids...) } - return b.world.newEntityTargetWith(b.targetID, target[0], b.comps...) + return b.world.newEntityTargetWith(b.relationID, target[0], b.comps...) } if b.comps == nil { return b.world.NewEntity(b.ids...) @@ -64,14 +64,14 @@ func (b *Builder) New(target ...Entity) Entity { // See [Builder.WithRelation]. func (b *Builder) NewBatch(count int, target ...Entity) { if len(target) > 0 { - if !b.hasTarget { + if !b.hasRelation { panic("can't set target entity: builder has no relation") } if b.comps == nil { - b.world.newEntities(count, b.targetID, true, target[0], b.ids...) + b.world.newEntities(count, b.relationID, true, target[0], b.ids...) return } - b.world.newEntitiesWith(count, b.targetID, true, target[0], b.comps...) + b.world.newEntitiesWith(count, b.relationID, true, target[0], b.comps...) return } if b.comps == nil { @@ -87,13 +87,13 @@ func (b *Builder) NewBatch(count int, target ...Entity) { // See [Builder.WithRelation]. func (b *Builder) NewBatchQ(count int, target ...Entity) Query { if len(target) > 0 { - if !b.hasTarget { + if !b.hasRelation { panic("can't set target entity: builder has no relation") } if b.comps == nil { - return b.world.newEntitiesQuery(count, b.targetID, true, target[0], b.ids...) + return b.world.newEntitiesQuery(count, b.relationID, true, target[0], b.ids...) } - return b.world.newEntitiesWithQuery(count, b.targetID, true, target[0], b.comps...) + return b.world.newEntitiesWithQuery(count, b.relationID, true, target[0], b.comps...) } if b.comps == nil { return b.world.newEntitiesQuery(count, ID{}, false, Entity{}, b.ids...) @@ -107,14 +107,14 @@ func (b *Builder) NewBatchQ(count int, target ...Entity) Query { // See [Builder.WithRelation]. func (b *Builder) Add(entity Entity, target ...Entity) { if len(target) > 0 { - if !b.hasTarget { + if !b.hasRelation { panic("can't set target entity: builder has no relation") } if b.comps == nil { - b.world.exchange(entity, b.ids, nil, b.targetID, b.hasTarget, target[0]) + b.world.exchange(entity, b.ids, nil, b.relationID, b.hasRelation, target[0]) return } - b.world.assign(entity, b.targetID, b.hasTarget, target[0], b.comps...) + b.world.assign(entity, b.relationID, b.hasRelation, target[0], b.comps...) return } if b.comps == nil { diff --git a/ecs/builder_test.go b/ecs/builder_test.go index 32fdd5f9..82d6085a 100644 --- a/ecs/builder_test.go +++ b/ecs/builder_test.go @@ -18,14 +18,14 @@ func TestBuilder(t *testing.T) { b1 := ecs.NewBuilder(&w, posID, velID, relID) - e1 := b1.New() + e1 := b1.NewEntity() assert.True(t, w.Has(e1, posID)) assert.True(t, w.Has(e1, velID)) e2 := w.NewEntity() b1.Add(e2) - assert.Panics(t, func() { b1.New(target) }) + assert.Panics(t, func() { b1.NewEntity(target) }) assert.Panics(t, func() { b1.NewBatch(10, target) }) assert.Panics(t, func() { b1.NewBatchQ(10, target) }) assert.Panics(t, func() { b1.Add(e1, target) }) @@ -37,14 +37,14 @@ func TestBuilder(t *testing.T) { b1 = ecs.NewBuilderWith(&w, ecs.Component{ID: posID, Comp: &Position{}}) - e1 = b1.New() + e1 = b1.NewEntity() assert.True(t, w.Has(e1, posID)) e2 = w.NewEntity() b1.Add(e2) e2 = w.NewEntity() - assert.Panics(t, func() { b1.New(target) }) + assert.Panics(t, func() { b1.NewEntity(target) }) assert.Panics(t, func() { b1.NewBatch(10, target) }) assert.Panics(t, func() { b1.NewBatchQ(10, target) }) assert.Panics(t, func() { b1.Add(e2, target) }) @@ -56,8 +56,8 @@ func TestBuilder(t *testing.T) { b1 = ecs.NewBuilder(&w, posID, velID, relID).WithRelation(relID) - b1.New() - e2 = b1.New(target) + b1.NewEntity() + e2 = b1.NewEntity(target) assert.Equal(t, target, w.Relations().Get(e2, relID)) e2 = w.NewEntity() @@ -76,8 +76,8 @@ func TestBuilder(t *testing.T) { ecs.Component{ID: relID, Comp: &ChildOf{}}, ).WithRelation(relID) - b1.New() - e2 = b1.New(target) + b1.NewEntity() + e2 = b1.NewEntity(target) assert.Equal(t, target, w.Relations().Get(e2, relID)) e2 = w.NewEntity() @@ -99,7 +99,7 @@ func ExampleBuilder() { builder := ecs.NewBuilder(&world, posID, velID) - _ = builder.New() + _ = builder.NewEntity() // Output: } @@ -110,7 +110,7 @@ func ExampleNewBuilder() { builder := ecs.NewBuilder(&world, posID, velID) - _ = builder.New() + _ = builder.NewEntity() // Output: } @@ -126,7 +126,7 @@ func ExampleNewBuilderWith() { builder := ecs.NewBuilderWith(&world, components...) - _ = builder.New() + _ = builder.NewEntity() // Output: } @@ -137,7 +137,7 @@ func ExampleBuilder_New() { builder := ecs.NewBuilder(&world, posID, velID) - _ = builder.New() + _ = builder.NewEntity() // Output: } @@ -189,6 +189,6 @@ func ExampleBuilder_WithRelation() { builder := ecs.NewBuilder(&world, posID, childID). WithRelation(childID) - builder.New(target) + builder.NewEntity(target) // Output: } diff --git a/ecs/cache_test.go b/ecs/cache_test.go index 770bfbbe..906fbbe9 100644 --- a/ecs/cache_test.go +++ b/ecs/cache_test.go @@ -79,25 +79,25 @@ func TestFilterCacheRelation(t *testing.T) { assert.Equal(t, int32(0), c2.Archetypes.Len()) assert.Equal(t, int32(0), c3.Archetypes.Len()) - e1 := NewBuilder(&world, rel1ID).WithRelation(rel1ID).New(target1) + e1 := NewBuilder(&world, rel1ID).WithRelation(rel1ID).NewEntity(target1) assert.Equal(t, int32(1), c1.Archetypes.Len()) assert.Equal(t, int32(1), c2.Archetypes.Len()) - _ = NewBuilder(&world, rel1ID).WithRelation(rel1ID).New(target3) + _ = NewBuilder(&world, rel1ID).WithRelation(rel1ID).NewEntity(target3) assert.Equal(t, int32(2), c1.Archetypes.Len()) assert.Equal(t, int32(1), c2.Archetypes.Len()) - _ = NewBuilder(&world, rel2ID).WithRelation(rel2ID).New(target2) + _ = NewBuilder(&world, rel2ID).WithRelation(rel2ID).NewEntity(target2) world.RemoveEntity(e1) world.RemoveEntity(target1) assert.Equal(t, int32(1), c1.Archetypes.Len()) assert.Equal(t, int32(0), c2.Archetypes.Len()) - _ = NewBuilder(&world, rel1ID).WithRelation(rel1ID).New(target2) - _ = NewBuilder(&world, rel1ID, posID).WithRelation(rel1ID).New(target2) - _ = NewBuilder(&world, rel1ID, posID).WithRelation(rel1ID).New(target3) - _ = NewBuilder(&world, rel1ID, posID).WithRelation(rel1ID).New(target4) + _ = NewBuilder(&world, rel1ID).WithRelation(rel1ID).NewEntity(target2) + _ = NewBuilder(&world, rel1ID, posID).WithRelation(rel1ID).NewEntity(target2) + _ = NewBuilder(&world, rel1ID, posID).WithRelation(rel1ID).NewEntity(target3) + _ = NewBuilder(&world, rel1ID, posID).WithRelation(rel1ID).NewEntity(target4) assert.Equal(t, int32(5), c1.Archetypes.Len()) assert.Equal(t, int32(2), c3.Archetypes.Len()) diff --git a/ecs/query_test.go b/ecs/query_test.go index b26ba88f..7103c5bc 100644 --- a/ecs/query_test.go +++ b/ecs/query_test.go @@ -206,7 +206,7 @@ func TestQueryEmptyNode(t *testing.T) { assert.False(t, w.nodes.Get(2).IsActive) builder := NewBuilder(&w, relID).WithRelation(relID) - child := builder.New(target) + child := builder.NewEntity(target) w.RemoveEntity(child) w.RemoveEntity(target) diff --git a/ecs/relation_examples_test.go b/ecs/relation_examples_test.go index a7ea81a1..f098d35c 100644 --- a/ecs/relation_examples_test.go +++ b/ecs/relation_examples_test.go @@ -19,7 +19,7 @@ func ExampleRelation() { // Create a child entity with a relation to the parent. childBuilder := ecs.NewBuilder(&world, childID).WithRelation(childID) - child := childBuilder.New(parent) + child := childBuilder.NewEntity(parent) // Get the relation target of the child. _ = world.Relations().Get(child, childID) diff --git a/ecs/relation_stress_test.go b/ecs/relation_stress_test.go index c9be4d47..6c563984 100644 --- a/ecs/relation_stress_test.go +++ b/ecs/relation_stress_test.go @@ -49,7 +49,7 @@ func TestRelationStress(t *testing.T) { assert.Equal(t, numParents, stats.Nodes[2].ArchetypeCount) assert.Equal(t, numParents-1, stats.Nodes[2].ActiveArchetypeCount) - parent = parBuilder.New() + parent = parBuilder.NewEntity() parents[parIdx] = parent childBuilder.NewBatch(numChildren, parent) diff --git a/examples/relations/main.go b/examples/relations/main.go index ccc996c7..4acaf387 100644 --- a/examples/relations/main.go +++ b/examples/relations/main.go @@ -34,7 +34,7 @@ func run() { parent2 := world.NewEntity() // Create an entity with a ChildOf relation to a parent entity. - child := childBuilder.New(parent1) + child := childBuilder.NewEntity(parent1) // Change the child's relation target. world.Relations().Set(child, childID, parent2) // Get the child's relation target. diff --git a/generic/map_generated.go b/generic/map_generated.go index feb4d24f..9e3fd3f4 100644 --- a/generic/map_generated.go +++ b/generic/map_generated.go @@ -110,7 +110,7 @@ func (m *Map1[A]) NewWith(a *A, target ...ecs.Entity) ecs.Entity { if !m.hasRelation { panic("map has no relation defined") } - return ecs.NewBuilderWith(m.world, ecs.Component{ID: m.id0, Comp: a}).WithRelation(m.relation).New(target[0]) + return ecs.NewBuilderWith(m.world, ecs.Component{ID: m.id0, Comp: a}).WithRelation(m.relation).NewEntity(target[0]) } // Add the Map1's components to the given entity. @@ -265,7 +265,7 @@ func (m *Map2[A, B]) NewWith(a *A, b *B, target ...ecs.Entity) ecs.Entity { } return ecs.NewBuilderWith(m.world, ecs.Component{ID: m.id0, Comp: a}, ecs.Component{ID: m.id1, Comp: b}, - ).WithRelation(m.relation).New(target[0]) + ).WithRelation(m.relation).NewEntity(target[0]) } // Add the Map2's components to the given entity. @@ -428,7 +428,7 @@ func (m *Map3[A, B, C]) NewWith(a *A, b *B, c *C, target ...ecs.Entity) ecs.Enti 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}, - ).WithRelation(m.relation).New(target[0]) + ).WithRelation(m.relation).NewEntity(target[0]) } // Add the Map3's components to the given entity. @@ -599,7 +599,7 @@ func (m *Map4[A, B, C, D]) NewWith(a *A, b *B, c *C, d *D, target ...ecs.Entity) ecs.Component{ID: m.id1, Comp: b}, ecs.Component{ID: m.id2, Comp: c}, ecs.Component{ID: m.id3, Comp: d}, - ).WithRelation(m.relation).New(target[0]) + ).WithRelation(m.relation).NewEntity(target[0]) } // Add the Map4's components to the given entity. @@ -778,7 +778,7 @@ func (m *Map5[A, B, C, D, E]) NewWith(a *A, b *B, c *C, d *D, e *E, target ...ec ecs.Component{ID: m.id2, Comp: c}, ecs.Component{ID: m.id3, Comp: d}, ecs.Component{ID: m.id4, Comp: e}, - ).WithRelation(m.relation).New(target[0]) + ).WithRelation(m.relation).NewEntity(target[0]) } // Add the Map5's components to the given entity. @@ -965,7 +965,7 @@ func (m *Map6[A, B, C, D, E, F]) NewWith(a *A, b *B, c *C, d *D, e *E, f *F, tar ecs.Component{ID: m.id3, Comp: d}, ecs.Component{ID: m.id4, Comp: e}, ecs.Component{ID: m.id5, Comp: f}, - ).WithRelation(m.relation).New(target[0]) + ).WithRelation(m.relation).NewEntity(target[0]) } // Add the Map6's components to the given entity. @@ -1160,7 +1160,7 @@ func (m *Map7[A, B, C, D, E, F, G]) NewWith(a *A, b *B, c *C, d *D, e *E, f *F, ecs.Component{ID: m.id4, Comp: e}, ecs.Component{ID: m.id5, Comp: f}, ecs.Component{ID: m.id6, Comp: g}, - ).WithRelation(m.relation).New(target[0]) + ).WithRelation(m.relation).NewEntity(target[0]) } // Add the Map7's components to the given entity. @@ -1363,7 +1363,7 @@ func (m *Map8[A, B, C, D, E, F, G, H]) NewWith(a *A, b *B, c *C, d *D, e *E, f * ecs.Component{ID: m.id5, Comp: f}, ecs.Component{ID: m.id6, Comp: g}, ecs.Component{ID: m.id7, Comp: h}, - ).WithRelation(m.relation).New(target[0]) + ).WithRelation(m.relation).NewEntity(target[0]) } // Add the Map8's components to the given entity. @@ -1574,7 +1574,7 @@ func (m *Map9[A, B, C, D, E, F, G, H, I]) NewWith(a *A, b *B, c *C, d *D, e *E, ecs.Component{ID: m.id6, Comp: g}, ecs.Component{ID: m.id7, Comp: h}, ecs.Component{ID: m.id8, Comp: i}, - ).WithRelation(m.relation).New(target[0]) + ).WithRelation(m.relation).NewEntity(target[0]) } // Add the Map9's components to the given entity. @@ -1793,7 +1793,7 @@ func (m *Map10[A, B, C, D, E, F, G, H, I, J]) NewWith(a *A, b *B, c *C, d *D, e ecs.Component{ID: m.id7, Comp: h}, ecs.Component{ID: m.id8, Comp: i}, ecs.Component{ID: m.id9, Comp: j}, - ).WithRelation(m.relation).New(target[0]) + ).WithRelation(m.relation).NewEntity(target[0]) } // Add the Map10's components to the given entity. @@ -2020,7 +2020,7 @@ func (m *Map11[A, B, C, D, E, F, G, H, I, J, K]) NewWith(a *A, b *B, c *C, d *D, ecs.Component{ID: m.id8, Comp: i}, ecs.Component{ID: m.id9, Comp: j}, ecs.Component{ID: m.id10, Comp: k}, - ).WithRelation(m.relation).New(target[0]) + ).WithRelation(m.relation).NewEntity(target[0]) } // Add the Map11's components to the given entity. @@ -2255,7 +2255,7 @@ func (m *Map12[A, B, C, D, E, F, G, H, I, J, K, L]) NewWith(a *A, b *B, c *C, d ecs.Component{ID: m.id9, Comp: j}, ecs.Component{ID: m.id10, Comp: k}, ecs.Component{ID: m.id11, Comp: l}, - ).WithRelation(m.relation).New(target[0]) + ).WithRelation(m.relation).NewEntity(target[0]) } // Add the Map12's components to the given entity. diff --git a/generic/util.go b/generic/util.go index 25452409..20eb0c06 100644 --- a/generic/util.go +++ b/generic/util.go @@ -63,7 +63,7 @@ func newEntity(w *ecs.World, ids []ecs.ID, relation ecs.ID, hasRelation bool, ta if !hasRelation { panic("map has no relation defined") } - return ecs.NewBuilder(w, ids...).WithRelation(relation).New(target[0]) + return ecs.NewBuilder(w, ids...).WithRelation(relation).NewEntity(target[0]) } func newBatch(w *ecs.World, count int, ids []ecs.ID, relation ecs.ID, hasRelation bool, target ...ecs.Entity) { From 9f028928778d0b77452519ac654fca9f7f000673 Mon Sep 17 00:00:00 2001 From: mlange-42 Date: Thu, 18 Jan 2024 18:05:41 +0100 Subject: [PATCH 04/12] add Relations.Exchange to allow exchange and relation assignment at once --- ecs/relations.go | 17 +++++++++++++++++ ecs/world.go | 2 +- ecs/world_internal.go | 3 +++ ecs/world_test.go | 25 +++++++++++++++++++++++++ 4 files changed, 46 insertions(+), 1 deletion(-) diff --git a/ecs/relations.go b/ecs/relations.go index f80c0e1c..c5d1d2d0 100644 --- a/ecs/relations.go +++ b/ecs/relations.go @@ -65,3 +65,20 @@ func (r *Relations) SetBatch(filter Filter, comp ID, target Entity) { func (r *Relations) SetBatchQ(filter Filter, comp ID, target Entity) Query { return r.world.setRelationBatchQuery(filter, comp, target) } + +// Exchange adds and removes components in one pass. +// This is more efficient than subsequent use of [World.Add] and [World.Remove]. +// In contrast to [World.Exchange], it allows to also set a relation target. +// +// Panics: +// - when called for a removed (and potentially recycled) entity. +// - when called with components that can't be added or removed because they are already present/not present, respectively. +// - when called for a missing relation component. +// - when called for a component that is not a relation. +// - when called without any components to add or remove. Use [World.Relations] instead. +// - when called on a locked world. Do not use during [Query] iteration! +// +// See also the generic variants under [github.com/mlange-42/arche/generic.Exchange]. +func (r *Relations) Exchange(entity Entity, add []ID, rem []ID, relation ID, target Entity) { + r.world.exchange(entity, add, rem, relation, true, target) +} diff --git a/ecs/world.go b/ecs/world.go index 1c3772fa..2fb0a759 100644 --- a/ecs/world.go +++ b/ecs/world.go @@ -326,7 +326,7 @@ func (w *World) Remove(entity Entity, comps ...ID) { // - when called with components that can't be added or removed because they are already present/not present, respectively. // - when called on a locked world. Do not use during [Query] iteration! // -// See also the generic variants under [github.com/mlange-42/arche/generic.Exchange]. +// See also [Relations.Exchange] and the generic variants under [github.com/mlange-42/arche/generic.Exchange]. func (w *World) Exchange(entity Entity, add []ID, rem []ID) { w.exchange(entity, add, rem, ID{}, false, Entity{}) } diff --git a/ecs/world_internal.go b/ecs/world_internal.go index 107b81d1..1fa9cfec 100644 --- a/ecs/world_internal.go +++ b/ecs/world_internal.go @@ -416,6 +416,9 @@ func (w *World) exchange(entity Entity, add []ID, rem []ID, relation ID, hasRela } if len(add) == 0 && len(rem) == 0 { + if hasRelation { + panic("exchange operation has no effect, but a relation is specified. Use World.Relation instead") + } return } index := &w.entities[entity.id] diff --git a/ecs/world_test.go b/ecs/world_test.go index 71551eea..fbeded7a 100644 --- a/ecs/world_test.go +++ b/ecs/world_test.go @@ -284,6 +284,31 @@ func TestWorldExchange(t *testing.T) { assert.Panics(t, func() { w.exchange(e0, []ID{posID}, nil, rel1ID, true, target) }) } +func TestWorldExchangeRelation(t *testing.T) { + w := NewWorld() + + posID := ComponentID[Position](&w) + rel1ID := ComponentID[testRelationA](&w) + rel2ID := ComponentID[testRelationB](&w) + + e0 := w.NewEntity() + e1 := w.NewEntity() + e2 := w.NewEntity() + + w.Relations().Exchange(e0, []ID{posID, rel1ID}, nil, rel1ID, e1) + + assert.Equal(t, []ID{posID, rel1ID}, w.Ids(e0)) + assert.Equal(t, e1, w.Relations().Get(e0, rel1ID)) + + assert.Panics(t, func() { + w.Relations().Exchange(e0, nil, nil, rel1ID, e2) + }) + + w.Relations().Exchange(e0, []ID{rel2ID}, []ID{rel1ID}, rel2ID, e2) + assert.Equal(t, []ID{posID, rel2ID}, w.Ids(e0)) + assert.Equal(t, e2, w.Relations().Get(e0, rel2ID)) +} + func TestWorldExchangeBatch(t *testing.T) { w := NewWorld() From 3fe45164a491634f0963402ffe5da17d01cf4ba5 Mon Sep 17 00:00:00 2001 From: mlange-42 Date: Thu, 18 Jan 2024 18:06:03 +0100 Subject: [PATCH 05/12] add relation operations to generic Exchange --- generic/exchange.go | 78 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 65 insertions(+), 13 deletions(-) diff --git a/generic/exchange.go b/generic/exchange.go index e65bae91..0569d815 100644 --- a/generic/exchange.go +++ b/generic/exchange.go @@ -12,9 +12,11 @@ import "github.com/mlange-42/arche/ecs" // // For only adding or removing components, see also [Map1], [Map2] etc. type Exchange struct { - add []ecs.ID - remove []ecs.ID - world *ecs.World + add []ecs.ID + remove []ecs.ID + hasRelation bool + relationID ecs.ID + world *ecs.World } // NewExchange creates a new Exchange object. @@ -24,6 +26,17 @@ func NewExchange(w *ecs.World) *Exchange { } } +// WithRelation sets the [Relation] component for the Exchange. +// +// Use in conjunction with the optional target argument of [Exchange.NewEntity], [Exchange.Add], [Exchange.Remove] and [Exchange.Exchange]. +// +// See [Relation] for details and examples. +func (m *Exchange) WithRelation(comp Comp) *Exchange { + m.hasRelation = true + m.relationID = ecs.TypeID(m.world, comp) + return m +} + // Adds sets components to add in calls to [Exchange.Add] and [Exchange.Exchange]. // // Create the required mask items with [T]. @@ -42,24 +55,53 @@ func (m *Exchange) Removes(remove ...Comp) *Exchange { // NewEntity creates a new [ecs.Entity] with the components set via [Exchange.Adds]. // +// The optional argument can be used to set the target [Entity] for the Exchange's [Relation]. +// See [Exchange.WithRelation]. +// // See also [ecs.World.NewEntity]. -func (m *Exchange) NewEntity() ecs.Entity { - entity := m.world.NewEntity(m.add...) - return entity +func (m *Exchange) NewEntity(target ...ecs.Entity) ecs.Entity { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Exchange has no relation") + } + builder := ecs.NewBuilder(m.world, m.add...).WithRelation(m.relationID) + return builder.NewEntity(target[0]) + } + return m.world.NewEntity(m.add...) } // Add the components set via [Exchange.Adds] to the given entity. // +// The optional argument can be used to set the target [Entity] for the Exchange's [Relation]. +// See [Exchange.WithRelation]. +// // See also [ecs.World.Add]. -func (m *Exchange) Add(entity ecs.Entity) { - m.world.Add(entity, m.add...) +func (m *Exchange) Add(entity ecs.Entity, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Exchange has no relation") + } + m.world.Relations().Exchange(entity, m.add, nil, m.relationID, target[0]) + } else { + m.world.Add(entity, m.add...) + } } // Remove the components set via [Exchange.Removes] from the given entity. // +// The optional argument can be used to set the target [Entity] for the Exchange's [Relation]. +// See [Exchange.WithRelation]. +// // See also [ecs.World.Remove]. -func (m *Exchange) Remove(entity ecs.Entity) { - m.world.Remove(entity, m.remove...) +func (m *Exchange) Remove(entity ecs.Entity, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Exchange has no relation") + } + m.world.Relations().Exchange(entity, nil, m.remove, m.relationID, target[0]) + } else { + m.world.Remove(entity, m.remove...) + } } // Exchange components on an entity. @@ -68,9 +110,19 @@ func (m *Exchange) Remove(entity ecs.Entity) { // Adds the components set via [Exchange.Adds]. // // When a [Relation] component is removed and another one is added, -// the target entity of the relation remains unchanged. +// the target entity of the relation is set to zero. +// +// The optional argument can be used to set the target [Entity] for the Exchange's [Relation]. +// See [Exchange.WithRelation]. // // See also [ecs.World.Exchange]. -func (m *Exchange) Exchange(entity ecs.Entity) { - m.world.Exchange(entity, m.add, m.remove) +func (m *Exchange) Exchange(entity ecs.Entity, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Exchange has no relation") + } + m.world.Relations().Exchange(entity, m.add, m.remove, m.relationID, target[0]) + } else { + m.world.Exchange(entity, m.add, m.remove) + } } From f1802660d6b043deaec89939038748aebcd1698b Mon Sep 17 00:00:00 2001 From: mlange-42 Date: Thu, 18 Jan 2024 18:42:29 +0100 Subject: [PATCH 06/12] add tests for generic exchange with relation target --- generic/exchange.go | 12 +++++- generic/exchange_test.go | 87 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 2 deletions(-) diff --git a/generic/exchange.go b/generic/exchange.go index 0569d815..6bc4a5b6 100644 --- a/generic/exchange.go +++ b/generic/exchange.go @@ -16,6 +16,7 @@ type Exchange struct { remove []ecs.ID hasRelation bool relationID ecs.ID + builder ecs.Builder world *ecs.World } @@ -34,6 +35,8 @@ func NewExchange(w *ecs.World) *Exchange { func (m *Exchange) WithRelation(comp Comp) *Exchange { m.hasRelation = true m.relationID = ecs.TypeID(m.world, comp) + + m.builder = *ecs.NewBuilder(m.world, m.add...).WithRelation(m.relationID) return m } @@ -42,6 +45,12 @@ func (m *Exchange) WithRelation(comp Comp) *Exchange { // Create the required mask items with [T]. func (m *Exchange) Adds(add ...Comp) *Exchange { m.add = toIds(m.world, add) + + b := ecs.NewBuilder(m.world, m.add...) + if m.hasRelation { + b = b.WithRelation(m.relationID) + } + m.builder = *b return m } @@ -64,8 +73,7 @@ func (m *Exchange) NewEntity(target ...ecs.Entity) ecs.Entity { if !m.hasRelation { panic("can't set target entity: Exchange has no relation") } - builder := ecs.NewBuilder(m.world, m.add...).WithRelation(m.relationID) - return builder.NewEntity(target[0]) + return m.builder.NewEntity(target[0]) } return m.world.NewEntity(m.add...) } diff --git a/generic/exchange_test.go b/generic/exchange_test.go index 0d95b71d..ca5568ad 100644 --- a/generic/exchange_test.go +++ b/generic/exchange_test.go @@ -44,3 +44,90 @@ func TestExchange(t *testing.T) { assert.NotNil(t, s2) assert.NotNil(t, s3) } + +func TestExchangeRelation(t *testing.T) { + w := ecs.NewWorld() + + posID := ecs.ComponentID[Position](&w) + velID := ecs.ComponentID[Velocity](&w) + rel1ID := ecs.ComponentID[testRelationA](&w) + + ex1 := NewExchange(&w). + Adds(T3[Position, Velocity, testRelationA]()...). + WithRelation(T[testRelationA]()) + + ex2 := NewExchange(&w). + Removes(T[Position]()). + WithRelation(T[testRelationA]()) + + ex3 := NewExchange(&w). + WithRelation(T[testRelationA]()). + Adds(T[Position]()). + Removes(T[Velocity]()) + + relMap := NewMap[testRelationA](&w) + + e0 := ex1.NewEntity() + e1 := ex1.NewEntity() + e2 := ex1.NewEntity(e0) + + assert.Equal(t, []ecs.ID{posID, velID, rel1ID}, w.Ids(e0)) + assert.Equal(t, []ecs.ID{posID, velID, rel1ID}, w.Ids(e1)) + assert.Equal(t, []ecs.ID{posID, velID, rel1ID}, w.Ids(e2)) + + assert.Equal(t, ecs.Entity{}, relMap.GetRelation(e0)) + assert.Equal(t, ecs.Entity{}, relMap.GetRelation(e1)) + assert.Equal(t, e0, relMap.GetRelation(e2)) + + e3 := w.NewEntity() + e4 := w.NewEntity() + ex1.Add(e3) + ex1.Add(e4, e0) + + assert.Equal(t, []ecs.ID{posID, velID, rel1ID}, w.Ids(e3)) + assert.Equal(t, []ecs.ID{posID, velID, rel1ID}, w.Ids(e4)) + assert.Equal(t, ecs.Entity{}, relMap.GetRelation(e3)) + assert.Equal(t, e0, relMap.GetRelation(e4)) + + ex2.Remove(e3, e1) + ex2.Remove(e4) + + assert.Equal(t, []ecs.ID{velID, rel1ID}, w.Ids(e3)) + assert.Equal(t, []ecs.ID{velID, rel1ID}, w.Ids(e4)) + assert.Equal(t, e1, relMap.GetRelation(e3)) + assert.Equal(t, e0, relMap.GetRelation(e4)) + + ex3.Exchange(e3) + ex3.Exchange(e4, e2) + + assert.Equal(t, []ecs.ID{posID, rel1ID}, w.Ids(e3)) + assert.Equal(t, []ecs.ID{posID, rel1ID}, w.Ids(e4)) + assert.Equal(t, e1, relMap.GetRelation(e3)) + assert.Equal(t, e2, relMap.GetRelation(e4)) +} + +func TestExchangeRelationPanics(t *testing.T) { + w := ecs.NewWorld() + + e0 := w.NewEntity() + + ex1 := NewExchange(&w). + Adds(T2[Position, testRelationA]()...). + Removes(T[Velocity]()) + + assert.Panics(t, func() { + _ = ex1.NewEntity(e0) + }) + + assert.Panics(t, func() { + ex1.Add(e0, e0) + }) + + assert.Panics(t, func() { + ex1.Remove(e0, e0) + }) + + assert.Panics(t, func() { + ex1.Exchange(e0, e0) + }) +} From 8e7135bca6a7b655b0d436b3dd6c1557ac4d929b Mon Sep 17 00:00:00 2001 From: mlange-42 Date: Thu, 18 Jan 2024 18:47:31 +0100 Subject: [PATCH 07/12] update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f6dd9d3d..9d3881a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,8 @@ This change was necessary to get the same performance as before, despite the mor * Adds functions `ComponentIDs(*World)` and `ResourceIDs(*World)` to get all registered IDs (#329) * Adds methods `Mask.And`, `Mask.Or` and `Mask.Xor` (#335) * Adds build tag `tiny` to restrict to 64 components for an extra bit of performance (#338) +* Adds methods `Relations.Exchange`, `Batch.ExchangeRelation`, `Batch.ExchangeRelationQ` for exchange with relation target (#342) +* Generic API adds `Exchange.WithRelation`and optional target argument for operations with relation target (#342) ### Performance From 76d40e780b58cae90402317bf193fa743670a42a Mon Sep 17 00:00:00 2001 From: mlange-42 Date: Thu, 18 Jan 2024 18:55:20 +0100 Subject: [PATCH 08/12] undo rename Builder.New to Builder.NewEntity --- CHANGELOG.md | 1 - benchmark/arche/fragmentation/arche_test.go | 4 ++-- ecs/builder.go | 6 ++--- ecs/builder_test.go | 26 ++++++++++----------- ecs/cache_test.go | 14 +++++------ ecs/query_test.go | 2 +- ecs/relation_examples_test.go | 2 +- ecs/relation_stress_test.go | 2 +- examples/relations/main.go | 2 +- generic/exchange.go | 2 +- generic/map_generated.go | 24 +++++++++---------- generic/util.go | 2 +- 12 files changed, 43 insertions(+), 44 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d3881a6..4ee6f4c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,6 @@ This change was necessary to get the same performance as before, despite the mor * Restructures `EntityEvent` to remove redundant information and better handle relation changes (#333) * World event listener changed from a simple function to a `Listener` interface (#334) * Removes `World.ComponentType(ID)`, use function `ComponentInfo(ID)` instead (#341) -* Renames `Builder.New` to `Builder.NewEntity` (#342) ### Features diff --git a/benchmark/arche/fragmentation/arche_test.go b/benchmark/arche/fragmentation/arche_test.go index a8424943..85d10d4c 100644 --- a/benchmark/arche/fragmentation/arche_test.go +++ b/benchmark/arche/fragmentation/arche_test.go @@ -267,7 +267,7 @@ func runQuery1Of1kTargets(b *testing.B, count int) { childBuilder := ecs.NewBuilder(&world, posID, relID).WithRelation(relID) for _, target := range targets { - childBuilder.NewEntity(target) + childBuilder.New(target) } target := targets[0] childBuilder.NewBatch(count, target) @@ -299,7 +299,7 @@ func runQuery1Of1kTargetsCached(b *testing.B, count int) { childBuilder := ecs.NewBuilder(&world, posID, relID).WithRelation(relID) for _, target := range targets { - childBuilder.NewEntity(target) + childBuilder.New(target) } target := targets[0] diff --git a/ecs/builder.go b/ecs/builder.go index 7df83f3a..f5fe37dc 100644 --- a/ecs/builder.go +++ b/ecs/builder.go @@ -29,7 +29,7 @@ func NewBuilderWith(w *World, comps ...Component) *Builder { // WithRelation sets the [Relation] component for the builder. // -// Use in conjunction with the optional target argument of [Builder.NewEntity], [Builder.NewBatch] and [Builder.NewBatchQ]. +// Use in conjunction with the optional target argument of [Builder.New], [Builder.NewBatch] and [Builder.NewBatchQ]. // // See [Relation] for details and examples. func (b *Builder) WithRelation(comp ID) *Builder { @@ -38,11 +38,11 @@ func (b *Builder) WithRelation(comp ID) *Builder { return b } -// NewEntity creates an entity. +// New creates an entity. // // The optional argument can be used to set the target [Entity] for the Builder's [Relation]. // See [Builder.WithRelation]. -func (b *Builder) NewEntity(target ...Entity) Entity { +func (b *Builder) New(target ...Entity) Entity { if len(target) > 0 { if !b.hasRelation { panic("can't set target entity: builder has no relation") diff --git a/ecs/builder_test.go b/ecs/builder_test.go index 82d6085a..32fdd5f9 100644 --- a/ecs/builder_test.go +++ b/ecs/builder_test.go @@ -18,14 +18,14 @@ func TestBuilder(t *testing.T) { b1 := ecs.NewBuilder(&w, posID, velID, relID) - e1 := b1.NewEntity() + e1 := b1.New() assert.True(t, w.Has(e1, posID)) assert.True(t, w.Has(e1, velID)) e2 := w.NewEntity() b1.Add(e2) - assert.Panics(t, func() { b1.NewEntity(target) }) + assert.Panics(t, func() { b1.New(target) }) assert.Panics(t, func() { b1.NewBatch(10, target) }) assert.Panics(t, func() { b1.NewBatchQ(10, target) }) assert.Panics(t, func() { b1.Add(e1, target) }) @@ -37,14 +37,14 @@ func TestBuilder(t *testing.T) { b1 = ecs.NewBuilderWith(&w, ecs.Component{ID: posID, Comp: &Position{}}) - e1 = b1.NewEntity() + e1 = b1.New() assert.True(t, w.Has(e1, posID)) e2 = w.NewEntity() b1.Add(e2) e2 = w.NewEntity() - assert.Panics(t, func() { b1.NewEntity(target) }) + assert.Panics(t, func() { b1.New(target) }) assert.Panics(t, func() { b1.NewBatch(10, target) }) assert.Panics(t, func() { b1.NewBatchQ(10, target) }) assert.Panics(t, func() { b1.Add(e2, target) }) @@ -56,8 +56,8 @@ func TestBuilder(t *testing.T) { b1 = ecs.NewBuilder(&w, posID, velID, relID).WithRelation(relID) - b1.NewEntity() - e2 = b1.NewEntity(target) + b1.New() + e2 = b1.New(target) assert.Equal(t, target, w.Relations().Get(e2, relID)) e2 = w.NewEntity() @@ -76,8 +76,8 @@ func TestBuilder(t *testing.T) { ecs.Component{ID: relID, Comp: &ChildOf{}}, ).WithRelation(relID) - b1.NewEntity() - e2 = b1.NewEntity(target) + b1.New() + e2 = b1.New(target) assert.Equal(t, target, w.Relations().Get(e2, relID)) e2 = w.NewEntity() @@ -99,7 +99,7 @@ func ExampleBuilder() { builder := ecs.NewBuilder(&world, posID, velID) - _ = builder.NewEntity() + _ = builder.New() // Output: } @@ -110,7 +110,7 @@ func ExampleNewBuilder() { builder := ecs.NewBuilder(&world, posID, velID) - _ = builder.NewEntity() + _ = builder.New() // Output: } @@ -126,7 +126,7 @@ func ExampleNewBuilderWith() { builder := ecs.NewBuilderWith(&world, components...) - _ = builder.NewEntity() + _ = builder.New() // Output: } @@ -137,7 +137,7 @@ func ExampleBuilder_New() { builder := ecs.NewBuilder(&world, posID, velID) - _ = builder.NewEntity() + _ = builder.New() // Output: } @@ -189,6 +189,6 @@ func ExampleBuilder_WithRelation() { builder := ecs.NewBuilder(&world, posID, childID). WithRelation(childID) - builder.NewEntity(target) + builder.New(target) // Output: } diff --git a/ecs/cache_test.go b/ecs/cache_test.go index 906fbbe9..770bfbbe 100644 --- a/ecs/cache_test.go +++ b/ecs/cache_test.go @@ -79,25 +79,25 @@ func TestFilterCacheRelation(t *testing.T) { assert.Equal(t, int32(0), c2.Archetypes.Len()) assert.Equal(t, int32(0), c3.Archetypes.Len()) - e1 := NewBuilder(&world, rel1ID).WithRelation(rel1ID).NewEntity(target1) + e1 := NewBuilder(&world, rel1ID).WithRelation(rel1ID).New(target1) assert.Equal(t, int32(1), c1.Archetypes.Len()) assert.Equal(t, int32(1), c2.Archetypes.Len()) - _ = NewBuilder(&world, rel1ID).WithRelation(rel1ID).NewEntity(target3) + _ = NewBuilder(&world, rel1ID).WithRelation(rel1ID).New(target3) assert.Equal(t, int32(2), c1.Archetypes.Len()) assert.Equal(t, int32(1), c2.Archetypes.Len()) - _ = NewBuilder(&world, rel2ID).WithRelation(rel2ID).NewEntity(target2) + _ = NewBuilder(&world, rel2ID).WithRelation(rel2ID).New(target2) world.RemoveEntity(e1) world.RemoveEntity(target1) assert.Equal(t, int32(1), c1.Archetypes.Len()) assert.Equal(t, int32(0), c2.Archetypes.Len()) - _ = NewBuilder(&world, rel1ID).WithRelation(rel1ID).NewEntity(target2) - _ = NewBuilder(&world, rel1ID, posID).WithRelation(rel1ID).NewEntity(target2) - _ = NewBuilder(&world, rel1ID, posID).WithRelation(rel1ID).NewEntity(target3) - _ = NewBuilder(&world, rel1ID, posID).WithRelation(rel1ID).NewEntity(target4) + _ = NewBuilder(&world, rel1ID).WithRelation(rel1ID).New(target2) + _ = NewBuilder(&world, rel1ID, posID).WithRelation(rel1ID).New(target2) + _ = NewBuilder(&world, rel1ID, posID).WithRelation(rel1ID).New(target3) + _ = NewBuilder(&world, rel1ID, posID).WithRelation(rel1ID).New(target4) assert.Equal(t, int32(5), c1.Archetypes.Len()) assert.Equal(t, int32(2), c3.Archetypes.Len()) diff --git a/ecs/query_test.go b/ecs/query_test.go index 7103c5bc..b26ba88f 100644 --- a/ecs/query_test.go +++ b/ecs/query_test.go @@ -206,7 +206,7 @@ func TestQueryEmptyNode(t *testing.T) { assert.False(t, w.nodes.Get(2).IsActive) builder := NewBuilder(&w, relID).WithRelation(relID) - child := builder.NewEntity(target) + child := builder.New(target) w.RemoveEntity(child) w.RemoveEntity(target) diff --git a/ecs/relation_examples_test.go b/ecs/relation_examples_test.go index f098d35c..a7ea81a1 100644 --- a/ecs/relation_examples_test.go +++ b/ecs/relation_examples_test.go @@ -19,7 +19,7 @@ func ExampleRelation() { // Create a child entity with a relation to the parent. childBuilder := ecs.NewBuilder(&world, childID).WithRelation(childID) - child := childBuilder.NewEntity(parent) + child := childBuilder.New(parent) // Get the relation target of the child. _ = world.Relations().Get(child, childID) diff --git a/ecs/relation_stress_test.go b/ecs/relation_stress_test.go index 6c563984..c9be4d47 100644 --- a/ecs/relation_stress_test.go +++ b/ecs/relation_stress_test.go @@ -49,7 +49,7 @@ func TestRelationStress(t *testing.T) { assert.Equal(t, numParents, stats.Nodes[2].ArchetypeCount) assert.Equal(t, numParents-1, stats.Nodes[2].ActiveArchetypeCount) - parent = parBuilder.NewEntity() + parent = parBuilder.New() parents[parIdx] = parent childBuilder.NewBatch(numChildren, parent) diff --git a/examples/relations/main.go b/examples/relations/main.go index 4acaf387..ccc996c7 100644 --- a/examples/relations/main.go +++ b/examples/relations/main.go @@ -34,7 +34,7 @@ func run() { parent2 := world.NewEntity() // Create an entity with a ChildOf relation to a parent entity. - child := childBuilder.NewEntity(parent1) + child := childBuilder.New(parent1) // Change the child's relation target. world.Relations().Set(child, childID, parent2) // Get the child's relation target. diff --git a/generic/exchange.go b/generic/exchange.go index 6bc4a5b6..2072f556 100644 --- a/generic/exchange.go +++ b/generic/exchange.go @@ -73,7 +73,7 @@ func (m *Exchange) NewEntity(target ...ecs.Entity) ecs.Entity { if !m.hasRelation { panic("can't set target entity: Exchange has no relation") } - return m.builder.NewEntity(target[0]) + return m.builder.New(target[0]) } return m.world.NewEntity(m.add...) } diff --git a/generic/map_generated.go b/generic/map_generated.go index 9e3fd3f4..feb4d24f 100644 --- a/generic/map_generated.go +++ b/generic/map_generated.go @@ -110,7 +110,7 @@ func (m *Map1[A]) NewWith(a *A, target ...ecs.Entity) ecs.Entity { if !m.hasRelation { panic("map has no relation defined") } - return ecs.NewBuilderWith(m.world, ecs.Component{ID: m.id0, Comp: a}).WithRelation(m.relation).NewEntity(target[0]) + return ecs.NewBuilderWith(m.world, ecs.Component{ID: m.id0, Comp: a}).WithRelation(m.relation).New(target[0]) } // Add the Map1's components to the given entity. @@ -265,7 +265,7 @@ func (m *Map2[A, B]) NewWith(a *A, b *B, target ...ecs.Entity) ecs.Entity { } return ecs.NewBuilderWith(m.world, ecs.Component{ID: m.id0, Comp: a}, ecs.Component{ID: m.id1, Comp: b}, - ).WithRelation(m.relation).NewEntity(target[0]) + ).WithRelation(m.relation).New(target[0]) } // Add the Map2's components to the given entity. @@ -428,7 +428,7 @@ func (m *Map3[A, B, C]) NewWith(a *A, b *B, c *C, target ...ecs.Entity) ecs.Enti 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}, - ).WithRelation(m.relation).NewEntity(target[0]) + ).WithRelation(m.relation).New(target[0]) } // Add the Map3's components to the given entity. @@ -599,7 +599,7 @@ func (m *Map4[A, B, C, D]) NewWith(a *A, b *B, c *C, d *D, target ...ecs.Entity) ecs.Component{ID: m.id1, Comp: b}, ecs.Component{ID: m.id2, Comp: c}, ecs.Component{ID: m.id3, Comp: d}, - ).WithRelation(m.relation).NewEntity(target[0]) + ).WithRelation(m.relation).New(target[0]) } // Add the Map4's components to the given entity. @@ -778,7 +778,7 @@ func (m *Map5[A, B, C, D, E]) NewWith(a *A, b *B, c *C, d *D, e *E, target ...ec ecs.Component{ID: m.id2, Comp: c}, ecs.Component{ID: m.id3, Comp: d}, ecs.Component{ID: m.id4, Comp: e}, - ).WithRelation(m.relation).NewEntity(target[0]) + ).WithRelation(m.relation).New(target[0]) } // Add the Map5's components to the given entity. @@ -965,7 +965,7 @@ func (m *Map6[A, B, C, D, E, F]) NewWith(a *A, b *B, c *C, d *D, e *E, f *F, tar ecs.Component{ID: m.id3, Comp: d}, ecs.Component{ID: m.id4, Comp: e}, ecs.Component{ID: m.id5, Comp: f}, - ).WithRelation(m.relation).NewEntity(target[0]) + ).WithRelation(m.relation).New(target[0]) } // Add the Map6's components to the given entity. @@ -1160,7 +1160,7 @@ func (m *Map7[A, B, C, D, E, F, G]) NewWith(a *A, b *B, c *C, d *D, e *E, f *F, ecs.Component{ID: m.id4, Comp: e}, ecs.Component{ID: m.id5, Comp: f}, ecs.Component{ID: m.id6, Comp: g}, - ).WithRelation(m.relation).NewEntity(target[0]) + ).WithRelation(m.relation).New(target[0]) } // Add the Map7's components to the given entity. @@ -1363,7 +1363,7 @@ func (m *Map8[A, B, C, D, E, F, G, H]) NewWith(a *A, b *B, c *C, d *D, e *E, f * ecs.Component{ID: m.id5, Comp: f}, ecs.Component{ID: m.id6, Comp: g}, ecs.Component{ID: m.id7, Comp: h}, - ).WithRelation(m.relation).NewEntity(target[0]) + ).WithRelation(m.relation).New(target[0]) } // Add the Map8's components to the given entity. @@ -1574,7 +1574,7 @@ func (m *Map9[A, B, C, D, E, F, G, H, I]) NewWith(a *A, b *B, c *C, d *D, e *E, ecs.Component{ID: m.id6, Comp: g}, ecs.Component{ID: m.id7, Comp: h}, ecs.Component{ID: m.id8, Comp: i}, - ).WithRelation(m.relation).NewEntity(target[0]) + ).WithRelation(m.relation).New(target[0]) } // Add the Map9's components to the given entity. @@ -1793,7 +1793,7 @@ func (m *Map10[A, B, C, D, E, F, G, H, I, J]) NewWith(a *A, b *B, c *C, d *D, e ecs.Component{ID: m.id7, Comp: h}, ecs.Component{ID: m.id8, Comp: i}, ecs.Component{ID: m.id9, Comp: j}, - ).WithRelation(m.relation).NewEntity(target[0]) + ).WithRelation(m.relation).New(target[0]) } // Add the Map10's components to the given entity. @@ -2020,7 +2020,7 @@ func (m *Map11[A, B, C, D, E, F, G, H, I, J, K]) NewWith(a *A, b *B, c *C, d *D, ecs.Component{ID: m.id8, Comp: i}, ecs.Component{ID: m.id9, Comp: j}, ecs.Component{ID: m.id10, Comp: k}, - ).WithRelation(m.relation).NewEntity(target[0]) + ).WithRelation(m.relation).New(target[0]) } // Add the Map11's components to the given entity. @@ -2255,7 +2255,7 @@ func (m *Map12[A, B, C, D, E, F, G, H, I, J, K, L]) NewWith(a *A, b *B, c *C, d ecs.Component{ID: m.id9, Comp: j}, ecs.Component{ID: m.id10, Comp: k}, ecs.Component{ID: m.id11, Comp: l}, - ).WithRelation(m.relation).NewEntity(target[0]) + ).WithRelation(m.relation).New(target[0]) } // Add the Map12's components to the given entity. diff --git a/generic/util.go b/generic/util.go index 20eb0c06..25452409 100644 --- a/generic/util.go +++ b/generic/util.go @@ -63,7 +63,7 @@ func newEntity(w *ecs.World, ids []ecs.ID, relation ecs.ID, hasRelation bool, ta if !hasRelation { panic("map has no relation defined") } - return ecs.NewBuilder(w, ids...).WithRelation(relation).NewEntity(target[0]) + return ecs.NewBuilder(w, ids...).WithRelation(relation).New(target[0]) } func newBatch(w *ecs.World, count int, ids []ecs.ID, relation ecs.ID, hasRelation bool, target ...ecs.Entity) { From abd29e42d133e1c508454d42bc012446100e2199 Mon Sep 17 00:00:00 2001 From: mlange-42 Date: Thu, 18 Jan 2024 20:56:55 +0100 Subject: [PATCH 09/12] add more batch processing possibilities and optional relation target arg --- CHANGELOG.md | 7 +- generic/exchange.go | 23 + generic/generate/map.go.txt | 101 ++- generic/generate/query.go.txt | 17 +- generic/map_generated.go | 1356 +++++++++++++++++++++++++++++++-- generic/query_generated.go | 221 +++++- 6 files changed, 1630 insertions(+), 95 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ee6f4c7..01a39484 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,8 +29,11 @@ This change was necessary to get the same performance as before, despite the mor * Adds functions `ComponentIDs(*World)` and `ResourceIDs(*World)` to get all registered IDs (#329) * Adds methods `Mask.And`, `Mask.Or` and `Mask.Xor` (#335) * Adds build tag `tiny` to restrict to 64 components for an extra bit of performance (#338) -* Adds methods `Relations.Exchange`, `Batch.ExchangeRelation`, `Batch.ExchangeRelationQ` for exchange with relation target (#342) -* Generic API adds `Exchange.WithRelation`and optional target argument for operations with relation target (#342) +* Adds methods `Relations.Exchange()`, `Batch.ExchangeRelation()`, `Batch.ExchangeRelationQ()` for exchange with relation target (#342) +* Generic API adds `Exchange.WithRelation()` and optional target argument for operations with relation target (#342) +* Generic API adds `MapX.AddBatch()`, `MapX.AddBatchQ()`, `MapX.RemoveBatch()`and `MapX.RemoveBatchQ()` (#342) +* Generic API adds optional relation target argument to most `MapX` methods (#342) +* Generic API adds `FilterX.Filter()` to get an `ecs.Filter` from a generic one (#342) ### Performance diff --git a/generic/exchange.go b/generic/exchange.go index 2072f556..4a294a65 100644 --- a/generic/exchange.go +++ b/generic/exchange.go @@ -134,3 +134,26 @@ func (m *Exchange) Exchange(entity ecs.Entity, target ...ecs.Entity) { m.world.Exchange(entity, m.add, m.remove) } } + +// ExchangeBatch exchanges components on many entities. +// +// Removes the components set via [Exchange.Removes]. +// Adds the components set via [Exchange.Adds]. +// +// When a [Relation] component is removed and another one is added, +// the target entity of the relation is set to zero. +// +// The optional argument can be used to set the target [ecs.Entity] for the Exchange's [ecs.Relation]. +// See [Exchange.WithRelation]. +// +// See also [Batch.Exchange] and [Batch.ExchangeQ]. +func (m *Exchange) ExchangeBatch(filter ecs.Filter, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Exchange has no relation") + } + m.world.Batch().ExchangeRelation(filter, m.add, m.remove, m.relationID, target[0]) + } else { + m.world.Batch().Exchange(filter, m.add, m.remove) + } +} diff --git a/generic/generate/map.go.txt b/generic/generate/map.go.txt index e1b5671e..a1c5b7f1 100644 --- a/generic/generate/map.go.txt +++ b/generic/generate/map.go.txt @@ -109,9 +109,55 @@ func (m *Map{{ .Index }}{{ .Types }}) NewWith({{ .Arguments }}, target ...ecs.En // Add the Map{{ .Index }}'s components to the given entity. // +// The optional argument can be used to set the target [ecs.Entity] for the Map{{ .Index }}'s [ecs.Relation]. +// // See also [ecs.World.Add]. -func (m *Map{{ .Index }}{{ .Types }}) Add(entity ecs.Entity) { - m.world.Add(entity, m.ids...) +func (m *Map{{ .Index }}{{ .Types }}) Add(entity ecs.Entity, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map{{ .Index }} has no relation") + } + m.world.Relations().Exchange(entity, m.ids, nil, m.relation, target[0]) + } else { + m.world.Add(entity, m.ids...) + } +} + +// AddBatch adds the Map{{ .Index }}'s components to multiple entities. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map{{ .Index }}'s [ecs.Relation]. +// +// See also [Batch.Add]. +func (m *Map{{ .Index }}{{ .Types }}) AddBatch(filter ecs.Filter, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map{{ .Index }} has no relation") + } + m.world.Batch().ExchangeRelation(filter, m.ids, nil, m.relation, target[0]) + } else { + m.world.Batch().Exchange(filter, m.ids, nil) + } +} + +// AddBatchQ adds the Map{{ .Index }}'s components to multiple entities and returns a query over them. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map{{ .Index }}'s [ecs.Relation]. +// +// See also [Batch.AddQ]. +func (m *Map{{ .Index }}{{ .Types }}) AddBatchQ(filter ecs.Filter, target ...ecs.Entity) Query{{ .Index }}{{ .Types }} { + var query ecs.Query + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map{{ .Index }} has no relation") + } + query = m.world.Batch().ExchangeRelationQ(filter, m.ids, nil, m.relation, target[0]) + } else { + query = m.world.Batch().ExchangeQ(filter, m.ids, nil) + } + return Query{{ .Index }}{{ .Types }}{ + Query: query, + {{ .IDAssign2 }} + } } // Assign the Map{{ .Index }}'s components to the given entity, using the supplied values. @@ -126,11 +172,56 @@ func (m *Map{{ .Index }}{{ .Types }}) Assign(entity ecs.Entity, {{ .Arguments }} // Remove the Map{{ .Index }}'s components from the given entity. // +// The optional argument can be used to set the target [ecs.Entity] for the Map{{ .Index }}'s [ecs.Relation]. +// // See also [ecs.World.Remove]. -func (m *Map{{ .Index }}{{ .Types }}) Remove(entity ecs.Entity) { - m.world.Remove(entity, m.ids...) +func (m *Map{{ .Index }}{{ .Types }}) Remove(entity ecs.Entity, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map{{ .Index }} has no relation") + } + m.world.Relations().Exchange(entity, nil, m.ids, m.relation, target[0]) + } else { + m.world.Remove(entity, m.ids...) + } +} + +// RemoveBatch removes the Map{{ .Index }}'s components to multiple entities. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map{{ .Index }}'s [ecs.Relation]. +// +// See also [Batch.Remove]. +func (m *Map{{ .Index }}{{ .Types }}) RemoveBatch(filter ecs.Filter, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map{{ .Index }} has no relation") + } + m.world.Batch().ExchangeRelation(filter, nil, m.ids, m.relation, target[0]) + } else { + m.world.Batch().Exchange(filter, nil, m.ids) + } } +// RemoveBatchQ adds the Map{{ .Index }}'s components to multiple entities and returns a query over them. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map{{ .Index }}'s [ecs.Relation]. +// +// See also [Batch.RemoveQ]. +func (m *Map{{ .Index }}{{ .Types }}) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query{{ .Index }}{{ .Types }} { + var query ecs.Query + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map{{ .Index }} has no relation") + } + query = m.world.Batch().ExchangeRelationQ(filter, nil, m.ids, m.relation, target[0]) + } else { + query = m.world.Batch().ExchangeQ(filter, nil, m.ids) + } + return Query{{ .Index }}{{ .Types }}{ + Query: query, + {{ .IDAssign2 }} + } +} // RemoveEntities removes all entities from the world that match the Map{{ .Index }}'s components. // @@ -139,7 +230,7 @@ func (m *Map{{ .Index }}{{ .Types }}) Remove(entity ecs.Entity) { // // Returns the number of removed entities. // -// See also [ecs.World.NewEntityWith]. +// See also [Batch.RemoveEntities]. func (m *Map{{ .Index }}{{ .Types }}) RemoveEntities(exclusive bool) int { if exclusive { filter := m.mask.Exclusive() diff --git a/generic/generate/query.go.txt b/generic/generate/query.go.txt index 3610790e..b778a6a4 100644 --- a/generic/generate/query.go.txt +++ b/generic/generate/query.go.txt @@ -87,7 +87,7 @@ func (q *Filter{{ .Index }}{{ .Types }}) WithRelation(comp Comp, target ...ecs.E return q } -// Query builds a [Query{{ .Index }}] query for iteration, with an optional relation target. +// Filter builds an [ecs.Filter], with an optional relation target. // // A relation target can't be used: // - if [Filter{{ .Index }}.WithRelation] was not called @@ -95,7 +95,7 @@ func (q *Filter{{ .Index }}{{ .Types }}) WithRelation(comp Comp, target ...ecs.E // - if the filter is registered for caching // // Panics in these cases. -func (q *Filter{{ .Index }}{{ .Types }}) Query(w *ecs.World, target ...ecs.Entity) Query{{ .Index }}{{ .Types }} { +func (q *Filter{{ .Index }}{{ .Types }}) Filter(w *ecs.World, target ...ecs.Entity) ecs.Filter { q.compiled.Compile(w, q.include, q.optional, q.exclude, q.targetType, q.target, q.hasTarget) filter := q.compiled.filter @@ -111,6 +111,19 @@ func (q *Filter{{ .Index }}{{ .Types }}) Query(w *ecs.World, target ...ecs.Entit filter = &q.compiled.relationFilter } + return filter +} + +// Query builds a [Query{{ .Index }}] query for iteration, with an optional relation target. +// +// A relation target can't be used: +// - if [Filter{{ .Index }}.WithRelation] was not called +// - if the target was already set via [Filter{{ .Index }}.WithRelation] +// - if the filter is registered for caching +// +// Panics in these cases. +func (q *Filter{{ .Index }}{{ .Types }}) Query(w *ecs.World, target ...ecs.Entity) Query{{ .Index }}{{ .Types }} { + filter := q.Filter(w, target...) return Query{{ .Index }}{{ .Types }}{ Query: w.Query(filter), target: q.compiled.TargetComp, diff --git a/generic/map_generated.go b/generic/map_generated.go index feb4d24f..2e9d5215 100644 --- a/generic/map_generated.go +++ b/generic/map_generated.go @@ -115,9 +115,55 @@ func (m *Map1[A]) NewWith(a *A, target ...ecs.Entity) ecs.Entity { // Add the Map1's components to the given entity. // +// The optional argument can be used to set the target [ecs.Entity] for the Map1's [ecs.Relation]. +// // See also [ecs.World.Add]. -func (m *Map1[A]) Add(entity ecs.Entity) { - m.world.Add(entity, m.ids...) +func (m *Map1[A]) Add(entity ecs.Entity, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map1 has no relation") + } + m.world.Relations().Exchange(entity, m.ids, nil, m.relation, target[0]) + } else { + m.world.Add(entity, m.ids...) + } +} + +// AddBatch adds the Map1's components to multiple entities. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map1's [ecs.Relation]. +// +// See also [Batch.Add]. +func (m *Map1[A]) AddBatch(filter ecs.Filter, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map1 has no relation") + } + m.world.Batch().ExchangeRelation(filter, m.ids, nil, m.relation, target[0]) + } else { + m.world.Batch().Exchange(filter, m.ids, nil) + } +} + +// AddBatchQ adds the Map1's components to multiple entities and returns a query over them. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map1's [ecs.Relation]. +// +// See also [Batch.AddQ]. +func (m *Map1[A]) AddBatchQ(filter ecs.Filter, target ...ecs.Entity) Query1[A] { + var query ecs.Query + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map1 has no relation") + } + query = m.world.Batch().ExchangeRelationQ(filter, m.ids, nil, m.relation, target[0]) + } else { + query = m.world.Batch().ExchangeQ(filter, m.ids, nil) + } + return Query1[A]{ + Query: query, + id0: m.id0, + } } // Assign the Map1's components to the given entity, using the supplied values. @@ -131,9 +177,55 @@ func (m *Map1[A]) Assign(entity ecs.Entity, a *A) { // Remove the Map1's components from the given entity. // +// The optional argument can be used to set the target [ecs.Entity] for the Map1's [ecs.Relation]. +// // See also [ecs.World.Remove]. -func (m *Map1[A]) Remove(entity ecs.Entity) { - m.world.Remove(entity, m.ids...) +func (m *Map1[A]) Remove(entity ecs.Entity, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map1 has no relation") + } + m.world.Relations().Exchange(entity, nil, m.ids, m.relation, target[0]) + } else { + m.world.Remove(entity, m.ids...) + } +} + +// RemoveBatch removes the Map1's components to multiple entities. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map1's [ecs.Relation]. +// +// See also [Batch.Remove]. +func (m *Map1[A]) RemoveBatch(filter ecs.Filter, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map1 has no relation") + } + m.world.Batch().ExchangeRelation(filter, nil, m.ids, m.relation, target[0]) + } else { + m.world.Batch().Exchange(filter, nil, m.ids) + } +} + +// RemoveBatchQ adds the Map1's components to multiple entities and returns a query over them. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map1's [ecs.Relation]. +// +// See also [Batch.RemoveQ]. +func (m *Map1[A]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query1[A] { + var query ecs.Query + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map1 has no relation") + } + query = m.world.Batch().ExchangeRelationQ(filter, nil, m.ids, m.relation, target[0]) + } else { + query = m.world.Batch().ExchangeQ(filter, nil, m.ids) + } + return Query1[A]{ + Query: query, + id0: m.id0, + } } // RemoveEntities removes all entities from the world that match the Map1's components. @@ -143,7 +235,7 @@ func (m *Map1[A]) Remove(entity ecs.Entity) { // // Returns the number of removed entities. // -// See also [ecs.World.NewEntityWith]. +// See also [Batch.RemoveEntities]. func (m *Map1[A]) RemoveEntities(exclusive bool) int { if exclusive { filter := m.mask.Exclusive() @@ -270,9 +362,56 @@ func (m *Map2[A, B]) NewWith(a *A, b *B, target ...ecs.Entity) ecs.Entity { // Add the Map2's components to the given entity. // +// The optional argument can be used to set the target [ecs.Entity] for the Map2's [ecs.Relation]. +// // See also [ecs.World.Add]. -func (m *Map2[A, B]) Add(entity ecs.Entity) { - m.world.Add(entity, m.ids...) +func (m *Map2[A, B]) Add(entity ecs.Entity, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map2 has no relation") + } + m.world.Relations().Exchange(entity, m.ids, nil, m.relation, target[0]) + } else { + m.world.Add(entity, m.ids...) + } +} + +// AddBatch adds the Map2's components to multiple entities. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map2's [ecs.Relation]. +// +// See also [Batch.Add]. +func (m *Map2[A, B]) AddBatch(filter ecs.Filter, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map2 has no relation") + } + m.world.Batch().ExchangeRelation(filter, m.ids, nil, m.relation, target[0]) + } else { + m.world.Batch().Exchange(filter, m.ids, nil) + } +} + +// AddBatchQ adds the Map2's components to multiple entities and returns a query over them. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map2's [ecs.Relation]. +// +// See also [Batch.AddQ]. +func (m *Map2[A, B]) AddBatchQ(filter ecs.Filter, target ...ecs.Entity) Query2[A, B] { + var query ecs.Query + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map2 has no relation") + } + query = m.world.Batch().ExchangeRelationQ(filter, m.ids, nil, m.relation, target[0]) + } else { + query = m.world.Batch().ExchangeQ(filter, m.ids, nil) + } + return Query2[A, B]{ + Query: query, + id0: m.id0, + id1: m.id1, + } } // Assign the Map2's components to the given entity, using the supplied values. @@ -287,9 +426,56 @@ func (m *Map2[A, B]) Assign(entity ecs.Entity, a *A, b *B) { // Remove the Map2's components from the given entity. // +// The optional argument can be used to set the target [ecs.Entity] for the Map2's [ecs.Relation]. +// // See also [ecs.World.Remove]. -func (m *Map2[A, B]) Remove(entity ecs.Entity) { - m.world.Remove(entity, m.ids...) +func (m *Map2[A, B]) Remove(entity ecs.Entity, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map2 has no relation") + } + m.world.Relations().Exchange(entity, nil, m.ids, m.relation, target[0]) + } else { + m.world.Remove(entity, m.ids...) + } +} + +// RemoveBatch removes the Map2's components to multiple entities. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map2's [ecs.Relation]. +// +// See also [Batch.Remove]. +func (m *Map2[A, B]) RemoveBatch(filter ecs.Filter, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map2 has no relation") + } + m.world.Batch().ExchangeRelation(filter, nil, m.ids, m.relation, target[0]) + } else { + m.world.Batch().Exchange(filter, nil, m.ids) + } +} + +// RemoveBatchQ adds the Map2's components to multiple entities and returns a query over them. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map2's [ecs.Relation]. +// +// See also [Batch.RemoveQ]. +func (m *Map2[A, B]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query2[A, B] { + var query ecs.Query + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map2 has no relation") + } + query = m.world.Batch().ExchangeRelationQ(filter, nil, m.ids, m.relation, target[0]) + } else { + query = m.world.Batch().ExchangeQ(filter, nil, m.ids) + } + return Query2[A, B]{ + Query: query, + id0: m.id0, + id1: m.id1, + } } // RemoveEntities removes all entities from the world that match the Map2's components. @@ -299,7 +485,7 @@ func (m *Map2[A, B]) Remove(entity ecs.Entity) { // // Returns the number of removed entities. // -// See also [ecs.World.NewEntityWith]. +// See also [Batch.RemoveEntities]. func (m *Map2[A, B]) RemoveEntities(exclusive bool) int { if exclusive { filter := m.mask.Exclusive() @@ -433,9 +619,57 @@ func (m *Map3[A, B, C]) NewWith(a *A, b *B, c *C, target ...ecs.Entity) ecs.Enti // Add the Map3's components to the given entity. // +// The optional argument can be used to set the target [ecs.Entity] for the Map3's [ecs.Relation]. +// // See also [ecs.World.Add]. -func (m *Map3[A, B, C]) Add(entity ecs.Entity) { - m.world.Add(entity, m.ids...) +func (m *Map3[A, B, C]) Add(entity ecs.Entity, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map3 has no relation") + } + m.world.Relations().Exchange(entity, m.ids, nil, m.relation, target[0]) + } else { + m.world.Add(entity, m.ids...) + } +} + +// AddBatch adds the Map3's components to multiple entities. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map3's [ecs.Relation]. +// +// See also [Batch.Add]. +func (m *Map3[A, B, C]) AddBatch(filter ecs.Filter, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map3 has no relation") + } + m.world.Batch().ExchangeRelation(filter, m.ids, nil, m.relation, target[0]) + } else { + m.world.Batch().Exchange(filter, m.ids, nil) + } +} + +// AddBatchQ adds the Map3's components to multiple entities and returns a query over them. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map3's [ecs.Relation]. +// +// See also [Batch.AddQ]. +func (m *Map3[A, B, C]) AddBatchQ(filter ecs.Filter, target ...ecs.Entity) Query3[A, B, C] { + var query ecs.Query + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map3 has no relation") + } + query = m.world.Batch().ExchangeRelationQ(filter, m.ids, nil, m.relation, target[0]) + } else { + query = m.world.Batch().ExchangeQ(filter, m.ids, nil) + } + return Query3[A, B, C]{ + Query: query, + id0: m.id0, + id1: m.id1, + id2: m.id2, + } } // Assign the Map3's components to the given entity, using the supplied values. @@ -451,9 +685,57 @@ func (m *Map3[A, B, C]) Assign(entity ecs.Entity, a *A, b *B, c *C) { // Remove the Map3's components from the given entity. // +// The optional argument can be used to set the target [ecs.Entity] for the Map3's [ecs.Relation]. +// // See also [ecs.World.Remove]. -func (m *Map3[A, B, C]) Remove(entity ecs.Entity) { - m.world.Remove(entity, m.ids...) +func (m *Map3[A, B, C]) Remove(entity ecs.Entity, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map3 has no relation") + } + m.world.Relations().Exchange(entity, nil, m.ids, m.relation, target[0]) + } else { + m.world.Remove(entity, m.ids...) + } +} + +// RemoveBatch removes the Map3's components to multiple entities. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map3's [ecs.Relation]. +// +// See also [Batch.Remove]. +func (m *Map3[A, B, C]) RemoveBatch(filter ecs.Filter, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map3 has no relation") + } + m.world.Batch().ExchangeRelation(filter, nil, m.ids, m.relation, target[0]) + } else { + m.world.Batch().Exchange(filter, nil, m.ids) + } +} + +// RemoveBatchQ adds the Map3's components to multiple entities and returns a query over them. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map3's [ecs.Relation]. +// +// See also [Batch.RemoveQ]. +func (m *Map3[A, B, C]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query3[A, B, C] { + var query ecs.Query + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map3 has no relation") + } + query = m.world.Batch().ExchangeRelationQ(filter, nil, m.ids, m.relation, target[0]) + } else { + query = m.world.Batch().ExchangeQ(filter, nil, m.ids) + } + return Query3[A, B, C]{ + Query: query, + id0: m.id0, + id1: m.id1, + id2: m.id2, + } } // RemoveEntities removes all entities from the world that match the Map3's components. @@ -463,7 +745,7 @@ func (m *Map3[A, B, C]) Remove(entity ecs.Entity) { // // Returns the number of removed entities. // -// See also [ecs.World.NewEntityWith]. +// See also [Batch.RemoveEntities]. func (m *Map3[A, B, C]) RemoveEntities(exclusive bool) int { if exclusive { filter := m.mask.Exclusive() @@ -604,9 +886,58 @@ func (m *Map4[A, B, C, D]) NewWith(a *A, b *B, c *C, d *D, target ...ecs.Entity) // Add the Map4's components to the given entity. // +// The optional argument can be used to set the target [ecs.Entity] for the Map4's [ecs.Relation]. +// // See also [ecs.World.Add]. -func (m *Map4[A, B, C, D]) Add(entity ecs.Entity) { - m.world.Add(entity, m.ids...) +func (m *Map4[A, B, C, D]) Add(entity ecs.Entity, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map4 has no relation") + } + m.world.Relations().Exchange(entity, m.ids, nil, m.relation, target[0]) + } else { + m.world.Add(entity, m.ids...) + } +} + +// AddBatch adds the Map4's components to multiple entities. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map4's [ecs.Relation]. +// +// See also [Batch.Add]. +func (m *Map4[A, B, C, D]) AddBatch(filter ecs.Filter, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map4 has no relation") + } + m.world.Batch().ExchangeRelation(filter, m.ids, nil, m.relation, target[0]) + } else { + m.world.Batch().Exchange(filter, m.ids, nil) + } +} + +// AddBatchQ adds the Map4's components to multiple entities and returns a query over them. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map4's [ecs.Relation]. +// +// See also [Batch.AddQ]. +func (m *Map4[A, B, C, D]) AddBatchQ(filter ecs.Filter, target ...ecs.Entity) Query4[A, B, C, D] { + var query ecs.Query + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map4 has no relation") + } + query = m.world.Batch().ExchangeRelationQ(filter, m.ids, nil, m.relation, target[0]) + } else { + query = m.world.Batch().ExchangeQ(filter, m.ids, nil) + } + return Query4[A, B, C, D]{ + Query: query, + id0: m.id0, + id1: m.id1, + id2: m.id2, + id3: m.id3, + } } // Assign the Map4's components to the given entity, using the supplied values. @@ -623,9 +954,58 @@ func (m *Map4[A, B, C, D]) Assign(entity ecs.Entity, a *A, b *B, c *C, d *D) { // Remove the Map4's components from the given entity. // +// The optional argument can be used to set the target [ecs.Entity] for the Map4's [ecs.Relation]. +// // See also [ecs.World.Remove]. -func (m *Map4[A, B, C, D]) Remove(entity ecs.Entity) { - m.world.Remove(entity, m.ids...) +func (m *Map4[A, B, C, D]) Remove(entity ecs.Entity, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map4 has no relation") + } + m.world.Relations().Exchange(entity, nil, m.ids, m.relation, target[0]) + } else { + m.world.Remove(entity, m.ids...) + } +} + +// RemoveBatch removes the Map4's components to multiple entities. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map4's [ecs.Relation]. +// +// See also [Batch.Remove]. +func (m *Map4[A, B, C, D]) RemoveBatch(filter ecs.Filter, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map4 has no relation") + } + m.world.Batch().ExchangeRelation(filter, nil, m.ids, m.relation, target[0]) + } else { + m.world.Batch().Exchange(filter, nil, m.ids) + } +} + +// RemoveBatchQ adds the Map4's components to multiple entities and returns a query over them. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map4's [ecs.Relation]. +// +// See also [Batch.RemoveQ]. +func (m *Map4[A, B, C, D]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query4[A, B, C, D] { + var query ecs.Query + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map4 has no relation") + } + query = m.world.Batch().ExchangeRelationQ(filter, nil, m.ids, m.relation, target[0]) + } else { + query = m.world.Batch().ExchangeQ(filter, nil, m.ids) + } + return Query4[A, B, C, D]{ + Query: query, + id0: m.id0, + id1: m.id1, + id2: m.id2, + id3: m.id3, + } } // RemoveEntities removes all entities from the world that match the Map4's components. @@ -635,7 +1015,7 @@ func (m *Map4[A, B, C, D]) Remove(entity ecs.Entity) { // // Returns the number of removed entities. // -// See also [ecs.World.NewEntityWith]. +// See also [Batch.RemoveEntities]. func (m *Map4[A, B, C, D]) RemoveEntities(exclusive bool) int { if exclusive { filter := m.mask.Exclusive() @@ -783,9 +1163,59 @@ func (m *Map5[A, B, C, D, E]) NewWith(a *A, b *B, c *C, d *D, e *E, target ...ec // Add the Map5's components to the given entity. // +// The optional argument can be used to set the target [ecs.Entity] for the Map5's [ecs.Relation]. +// // See also [ecs.World.Add]. -func (m *Map5[A, B, C, D, E]) Add(entity ecs.Entity) { - m.world.Add(entity, m.ids...) +func (m *Map5[A, B, C, D, E]) Add(entity ecs.Entity, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map5 has no relation") + } + m.world.Relations().Exchange(entity, m.ids, nil, m.relation, target[0]) + } else { + m.world.Add(entity, m.ids...) + } +} + +// AddBatch adds the Map5's components to multiple entities. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map5's [ecs.Relation]. +// +// See also [Batch.Add]. +func (m *Map5[A, B, C, D, E]) AddBatch(filter ecs.Filter, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map5 has no relation") + } + m.world.Batch().ExchangeRelation(filter, m.ids, nil, m.relation, target[0]) + } else { + m.world.Batch().Exchange(filter, m.ids, nil) + } +} + +// AddBatchQ adds the Map5's components to multiple entities and returns a query over them. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map5's [ecs.Relation]. +// +// See also [Batch.AddQ]. +func (m *Map5[A, B, C, D, E]) AddBatchQ(filter ecs.Filter, target ...ecs.Entity) Query5[A, B, C, D, E] { + var query ecs.Query + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map5 has no relation") + } + query = m.world.Batch().ExchangeRelationQ(filter, m.ids, nil, m.relation, target[0]) + } else { + query = m.world.Batch().ExchangeQ(filter, m.ids, nil) + } + return Query5[A, B, C, D, E]{ + Query: query, + id0: m.id0, + id1: m.id1, + id2: m.id2, + id3: m.id3, + id4: m.id4, + } } // Assign the Map5's components to the given entity, using the supplied values. @@ -803,9 +1233,59 @@ func (m *Map5[A, B, C, D, E]) Assign(entity ecs.Entity, a *A, b *B, c *C, d *D, // Remove the Map5's components from the given entity. // +// The optional argument can be used to set the target [ecs.Entity] for the Map5's [ecs.Relation]. +// // See also [ecs.World.Remove]. -func (m *Map5[A, B, C, D, E]) Remove(entity ecs.Entity) { - m.world.Remove(entity, m.ids...) +func (m *Map5[A, B, C, D, E]) Remove(entity ecs.Entity, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map5 has no relation") + } + m.world.Relations().Exchange(entity, nil, m.ids, m.relation, target[0]) + } else { + m.world.Remove(entity, m.ids...) + } +} + +// RemoveBatch removes the Map5's components to multiple entities. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map5's [ecs.Relation]. +// +// See also [Batch.Remove]. +func (m *Map5[A, B, C, D, E]) RemoveBatch(filter ecs.Filter, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map5 has no relation") + } + m.world.Batch().ExchangeRelation(filter, nil, m.ids, m.relation, target[0]) + } else { + m.world.Batch().Exchange(filter, nil, m.ids) + } +} + +// RemoveBatchQ adds the Map5's components to multiple entities and returns a query over them. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map5's [ecs.Relation]. +// +// See also [Batch.RemoveQ]. +func (m *Map5[A, B, C, D, E]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query5[A, B, C, D, E] { + var query ecs.Query + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map5 has no relation") + } + query = m.world.Batch().ExchangeRelationQ(filter, nil, m.ids, m.relation, target[0]) + } else { + query = m.world.Batch().ExchangeQ(filter, nil, m.ids) + } + return Query5[A, B, C, D, E]{ + Query: query, + id0: m.id0, + id1: m.id1, + id2: m.id2, + id3: m.id3, + id4: m.id4, + } } // RemoveEntities removes all entities from the world that match the Map5's components. @@ -815,7 +1295,7 @@ func (m *Map5[A, B, C, D, E]) Remove(entity ecs.Entity) { // // Returns the number of removed entities. // -// See also [ecs.World.NewEntityWith]. +// See also [Batch.RemoveEntities]. func (m *Map5[A, B, C, D, E]) RemoveEntities(exclusive bool) int { if exclusive { filter := m.mask.Exclusive() @@ -970,9 +1450,60 @@ func (m *Map6[A, B, C, D, E, F]) NewWith(a *A, b *B, c *C, d *D, e *E, f *F, tar // Add the Map6's components to the given entity. // +// The optional argument can be used to set the target [ecs.Entity] for the Map6's [ecs.Relation]. +// // See also [ecs.World.Add]. -func (m *Map6[A, B, C, D, E, F]) Add(entity ecs.Entity) { - m.world.Add(entity, m.ids...) +func (m *Map6[A, B, C, D, E, F]) Add(entity ecs.Entity, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map6 has no relation") + } + m.world.Relations().Exchange(entity, m.ids, nil, m.relation, target[0]) + } else { + m.world.Add(entity, m.ids...) + } +} + +// AddBatch adds the Map6's components to multiple entities. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map6's [ecs.Relation]. +// +// See also [Batch.Add]. +func (m *Map6[A, B, C, D, E, F]) AddBatch(filter ecs.Filter, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map6 has no relation") + } + m.world.Batch().ExchangeRelation(filter, m.ids, nil, m.relation, target[0]) + } else { + m.world.Batch().Exchange(filter, m.ids, nil) + } +} + +// AddBatchQ adds the Map6's components to multiple entities and returns a query over them. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map6's [ecs.Relation]. +// +// See also [Batch.AddQ]. +func (m *Map6[A, B, C, D, E, F]) AddBatchQ(filter ecs.Filter, target ...ecs.Entity) Query6[A, B, C, D, E, F] { + var query ecs.Query + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map6 has no relation") + } + query = m.world.Batch().ExchangeRelationQ(filter, m.ids, nil, m.relation, target[0]) + } else { + query = m.world.Batch().ExchangeQ(filter, m.ids, nil) + } + return Query6[A, B, C, D, E, F]{ + Query: query, + id0: m.id0, + id1: m.id1, + id2: m.id2, + id3: m.id3, + id4: m.id4, + id5: m.id5, + } } // Assign the Map6's components to the given entity, using the supplied values. @@ -991,9 +1522,60 @@ func (m *Map6[A, B, C, D, E, F]) Assign(entity ecs.Entity, a *A, b *B, c *C, d * // Remove the Map6's components from the given entity. // +// The optional argument can be used to set the target [ecs.Entity] for the Map6's [ecs.Relation]. +// // See also [ecs.World.Remove]. -func (m *Map6[A, B, C, D, E, F]) Remove(entity ecs.Entity) { - m.world.Remove(entity, m.ids...) +func (m *Map6[A, B, C, D, E, F]) Remove(entity ecs.Entity, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map6 has no relation") + } + m.world.Relations().Exchange(entity, nil, m.ids, m.relation, target[0]) + } else { + m.world.Remove(entity, m.ids...) + } +} + +// RemoveBatch removes the Map6's components to multiple entities. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map6's [ecs.Relation]. +// +// See also [Batch.Remove]. +func (m *Map6[A, B, C, D, E, F]) RemoveBatch(filter ecs.Filter, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map6 has no relation") + } + m.world.Batch().ExchangeRelation(filter, nil, m.ids, m.relation, target[0]) + } else { + m.world.Batch().Exchange(filter, nil, m.ids) + } +} + +// RemoveBatchQ adds the Map6's components to multiple entities and returns a query over them. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map6's [ecs.Relation]. +// +// See also [Batch.RemoveQ]. +func (m *Map6[A, B, C, D, E, F]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query6[A, B, C, D, E, F] { + var query ecs.Query + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map6 has no relation") + } + query = m.world.Batch().ExchangeRelationQ(filter, nil, m.ids, m.relation, target[0]) + } else { + query = m.world.Batch().ExchangeQ(filter, nil, m.ids) + } + return Query6[A, B, C, D, E, F]{ + Query: query, + id0: m.id0, + id1: m.id1, + id2: m.id2, + id3: m.id3, + id4: m.id4, + id5: m.id5, + } } // RemoveEntities removes all entities from the world that match the Map6's components. @@ -1003,7 +1585,7 @@ func (m *Map6[A, B, C, D, E, F]) Remove(entity ecs.Entity) { // // Returns the number of removed entities. // -// See also [ecs.World.NewEntityWith]. +// See also [Batch.RemoveEntities]. func (m *Map6[A, B, C, D, E, F]) RemoveEntities(exclusive bool) int { if exclusive { filter := m.mask.Exclusive() @@ -1165,9 +1747,61 @@ func (m *Map7[A, B, C, D, E, F, G]) NewWith(a *A, b *B, c *C, d *D, e *E, f *F, // Add the Map7's components to the given entity. // +// The optional argument can be used to set the target [ecs.Entity] for the Map7's [ecs.Relation]. +// // See also [ecs.World.Add]. -func (m *Map7[A, B, C, D, E, F, G]) Add(entity ecs.Entity) { - m.world.Add(entity, m.ids...) +func (m *Map7[A, B, C, D, E, F, G]) Add(entity ecs.Entity, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map7 has no relation") + } + m.world.Relations().Exchange(entity, m.ids, nil, m.relation, target[0]) + } else { + m.world.Add(entity, m.ids...) + } +} + +// AddBatch adds the Map7's components to multiple entities. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map7's [ecs.Relation]. +// +// See also [Batch.Add]. +func (m *Map7[A, B, C, D, E, F, G]) AddBatch(filter ecs.Filter, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map7 has no relation") + } + m.world.Batch().ExchangeRelation(filter, m.ids, nil, m.relation, target[0]) + } else { + m.world.Batch().Exchange(filter, m.ids, nil) + } +} + +// AddBatchQ adds the Map7's components to multiple entities and returns a query over them. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map7's [ecs.Relation]. +// +// See also [Batch.AddQ]. +func (m *Map7[A, B, C, D, E, F, G]) AddBatchQ(filter ecs.Filter, target ...ecs.Entity) Query7[A, B, C, D, E, F, G] { + var query ecs.Query + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map7 has no relation") + } + query = m.world.Batch().ExchangeRelationQ(filter, m.ids, nil, m.relation, target[0]) + } else { + query = m.world.Batch().ExchangeQ(filter, m.ids, nil) + } + return Query7[A, B, C, D, E, F, G]{ + Query: query, + id0: m.id0, + id1: m.id1, + id2: m.id2, + id3: m.id3, + id4: m.id4, + id5: m.id5, + id6: m.id6, + } } // Assign the Map7's components to the given entity, using the supplied values. @@ -1187,9 +1821,61 @@ func (m *Map7[A, B, C, D, E, F, G]) Assign(entity ecs.Entity, a *A, b *B, c *C, // Remove the Map7's components from the given entity. // +// The optional argument can be used to set the target [ecs.Entity] for the Map7's [ecs.Relation]. +// // See also [ecs.World.Remove]. -func (m *Map7[A, B, C, D, E, F, G]) Remove(entity ecs.Entity) { - m.world.Remove(entity, m.ids...) +func (m *Map7[A, B, C, D, E, F, G]) Remove(entity ecs.Entity, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map7 has no relation") + } + m.world.Relations().Exchange(entity, nil, m.ids, m.relation, target[0]) + } else { + m.world.Remove(entity, m.ids...) + } +} + +// RemoveBatch removes the Map7's components to multiple entities. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map7's [ecs.Relation]. +// +// See also [Batch.Remove]. +func (m *Map7[A, B, C, D, E, F, G]) RemoveBatch(filter ecs.Filter, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map7 has no relation") + } + m.world.Batch().ExchangeRelation(filter, nil, m.ids, m.relation, target[0]) + } else { + m.world.Batch().Exchange(filter, nil, m.ids) + } +} + +// RemoveBatchQ adds the Map7's components to multiple entities and returns a query over them. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map7's [ecs.Relation]. +// +// See also [Batch.RemoveQ]. +func (m *Map7[A, B, C, D, E, F, G]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query7[A, B, C, D, E, F, G] { + var query ecs.Query + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map7 has no relation") + } + query = m.world.Batch().ExchangeRelationQ(filter, nil, m.ids, m.relation, target[0]) + } else { + query = m.world.Batch().ExchangeQ(filter, nil, m.ids) + } + return Query7[A, B, C, D, E, F, G]{ + Query: query, + id0: m.id0, + id1: m.id1, + id2: m.id2, + id3: m.id3, + id4: m.id4, + id5: m.id5, + id6: m.id6, + } } // RemoveEntities removes all entities from the world that match the Map7's components. @@ -1199,7 +1885,7 @@ func (m *Map7[A, B, C, D, E, F, G]) Remove(entity ecs.Entity) { // // Returns the number of removed entities. // -// See also [ecs.World.NewEntityWith]. +// See also [Batch.RemoveEntities]. func (m *Map7[A, B, C, D, E, F, G]) RemoveEntities(exclusive bool) int { if exclusive { filter := m.mask.Exclusive() @@ -1368,9 +2054,62 @@ func (m *Map8[A, B, C, D, E, F, G, H]) NewWith(a *A, b *B, c *C, d *D, e *E, f * // Add the Map8's components to the given entity. // +// The optional argument can be used to set the target [ecs.Entity] for the Map8's [ecs.Relation]. +// // See also [ecs.World.Add]. -func (m *Map8[A, B, C, D, E, F, G, H]) Add(entity ecs.Entity) { - m.world.Add(entity, m.ids...) +func (m *Map8[A, B, C, D, E, F, G, H]) Add(entity ecs.Entity, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map8 has no relation") + } + m.world.Relations().Exchange(entity, m.ids, nil, m.relation, target[0]) + } else { + m.world.Add(entity, m.ids...) + } +} + +// AddBatch adds the Map8's components to multiple entities. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map8's [ecs.Relation]. +// +// See also [Batch.Add]. +func (m *Map8[A, B, C, D, E, F, G, H]) AddBatch(filter ecs.Filter, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map8 has no relation") + } + m.world.Batch().ExchangeRelation(filter, m.ids, nil, m.relation, target[0]) + } else { + m.world.Batch().Exchange(filter, m.ids, nil) + } +} + +// AddBatchQ adds the Map8's components to multiple entities and returns a query over them. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map8's [ecs.Relation]. +// +// See also [Batch.AddQ]. +func (m *Map8[A, B, C, D, E, F, G, H]) AddBatchQ(filter ecs.Filter, target ...ecs.Entity) Query8[A, B, C, D, E, F, G, H] { + var query ecs.Query + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map8 has no relation") + } + query = m.world.Batch().ExchangeRelationQ(filter, m.ids, nil, m.relation, target[0]) + } else { + query = m.world.Batch().ExchangeQ(filter, m.ids, nil) + } + return Query8[A, B, C, D, E, F, G, H]{ + 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, + } } // Assign the Map8's components to the given entity, using the supplied values. @@ -1391,9 +2130,62 @@ func (m *Map8[A, B, C, D, E, F, G, H]) Assign(entity ecs.Entity, a *A, b *B, c * // Remove the Map8's components from the given entity. // +// The optional argument can be used to set the target [ecs.Entity] for the Map8's [ecs.Relation]. +// // See also [ecs.World.Remove]. -func (m *Map8[A, B, C, D, E, F, G, H]) Remove(entity ecs.Entity) { - m.world.Remove(entity, m.ids...) +func (m *Map8[A, B, C, D, E, F, G, H]) Remove(entity ecs.Entity, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map8 has no relation") + } + m.world.Relations().Exchange(entity, nil, m.ids, m.relation, target[0]) + } else { + m.world.Remove(entity, m.ids...) + } +} + +// RemoveBatch removes the Map8's components to multiple entities. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map8's [ecs.Relation]. +// +// See also [Batch.Remove]. +func (m *Map8[A, B, C, D, E, F, G, H]) RemoveBatch(filter ecs.Filter, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map8 has no relation") + } + m.world.Batch().ExchangeRelation(filter, nil, m.ids, m.relation, target[0]) + } else { + m.world.Batch().Exchange(filter, nil, m.ids) + } +} + +// RemoveBatchQ adds the Map8's components to multiple entities and returns a query over them. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map8's [ecs.Relation]. +// +// See also [Batch.RemoveQ]. +func (m *Map8[A, B, C, D, E, F, G, H]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query8[A, B, C, D, E, F, G, H] { + var query ecs.Query + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map8 has no relation") + } + query = m.world.Batch().ExchangeRelationQ(filter, nil, m.ids, m.relation, target[0]) + } else { + query = m.world.Batch().ExchangeQ(filter, nil, m.ids) + } + return Query8[A, B, C, D, E, F, G, H]{ + 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, + } } // RemoveEntities removes all entities from the world that match the Map8's components. @@ -1403,7 +2195,7 @@ func (m *Map8[A, B, C, D, E, F, G, H]) Remove(entity ecs.Entity) { // // Returns the number of removed entities. // -// See also [ecs.World.NewEntityWith]. +// See also [Batch.RemoveEntities]. func (m *Map8[A, B, C, D, E, F, G, H]) RemoveEntities(exclusive bool) int { if exclusive { filter := m.mask.Exclusive() @@ -1579,9 +2371,63 @@ func (m *Map9[A, B, C, D, E, F, G, H, I]) NewWith(a *A, b *B, c *C, d *D, e *E, // Add the Map9's components to the given entity. // +// The optional argument can be used to set the target [ecs.Entity] for the Map9's [ecs.Relation]. +// // 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...) +func (m *Map9[A, B, C, D, E, F, G, H, I]) Add(entity ecs.Entity, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map9 has no relation") + } + m.world.Relations().Exchange(entity, m.ids, nil, m.relation, target[0]) + } else { + m.world.Add(entity, m.ids...) + } +} + +// AddBatch adds the Map9's components to multiple entities. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map9's [ecs.Relation]. +// +// See also [Batch.Add]. +func (m *Map9[A, B, C, D, E, F, G, H, I]) AddBatch(filter ecs.Filter, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map9 has no relation") + } + m.world.Batch().ExchangeRelation(filter, m.ids, nil, m.relation, target[0]) + } else { + m.world.Batch().Exchange(filter, m.ids, nil) + } +} + +// AddBatchQ adds the Map9's components to multiple entities and returns a query over them. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map9's [ecs.Relation]. +// +// See also [Batch.AddQ]. +func (m *Map9[A, B, C, D, E, F, G, H, I]) AddBatchQ(filter ecs.Filter, target ...ecs.Entity) Query9[A, B, C, D, E, F, G, H, I] { + var query ecs.Query + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map9 has no relation") + } + query = m.world.Batch().ExchangeRelationQ(filter, m.ids, nil, m.relation, target[0]) + } else { + query = m.world.Batch().ExchangeQ(filter, m.ids, nil) + } + 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, + } } // Assign the Map9's components to the given entity, using the supplied values. @@ -1603,9 +2449,63 @@ func (m *Map9[A, B, C, D, E, F, G, H, I]) Assign(entity ecs.Entity, a *A, b *B, // Remove the Map9's components from the given entity. // +// The optional argument can be used to set the target [ecs.Entity] for the Map9's [ecs.Relation]. +// // 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...) +func (m *Map9[A, B, C, D, E, F, G, H, I]) Remove(entity ecs.Entity, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map9 has no relation") + } + m.world.Relations().Exchange(entity, nil, m.ids, m.relation, target[0]) + } else { + m.world.Remove(entity, m.ids...) + } +} + +// RemoveBatch removes the Map9's components to multiple entities. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map9's [ecs.Relation]. +// +// See also [Batch.Remove]. +func (m *Map9[A, B, C, D, E, F, G, H, I]) RemoveBatch(filter ecs.Filter, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map9 has no relation") + } + m.world.Batch().ExchangeRelation(filter, nil, m.ids, m.relation, target[0]) + } else { + m.world.Batch().Exchange(filter, nil, m.ids) + } +} + +// RemoveBatchQ adds the Map9's components to multiple entities and returns a query over them. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map9's [ecs.Relation]. +// +// See also [Batch.RemoveQ]. +func (m *Map9[A, B, C, D, E, F, G, H, I]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query9[A, B, C, D, E, F, G, H, I] { + var query ecs.Query + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map9 has no relation") + } + query = m.world.Batch().ExchangeRelationQ(filter, nil, m.ids, m.relation, target[0]) + } else { + query = m.world.Batch().ExchangeQ(filter, nil, m.ids) + } + 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, + } } // RemoveEntities removes all entities from the world that match the Map9's components. @@ -1615,7 +2515,7 @@ func (m *Map9[A, B, C, D, E, F, G, H, I]) Remove(entity ecs.Entity) { // // Returns the number of removed entities. // -// See also [ecs.World.NewEntityWith]. +// See also [Batch.RemoveEntities]. func (m *Map9[A, B, C, D, E, F, G, H, I]) RemoveEntities(exclusive bool) int { if exclusive { filter := m.mask.Exclusive() @@ -1798,9 +2698,64 @@ func (m *Map10[A, B, C, D, E, F, G, H, I, J]) NewWith(a *A, b *B, c *C, d *D, e // Add the Map10's components to the given entity. // +// The optional argument can be used to set the target [ecs.Entity] for the Map10's [ecs.Relation]. +// // 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...) +func (m *Map10[A, B, C, D, E, F, G, H, I, J]) Add(entity ecs.Entity, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map10 has no relation") + } + m.world.Relations().Exchange(entity, m.ids, nil, m.relation, target[0]) + } else { + m.world.Add(entity, m.ids...) + } +} + +// AddBatch adds the Map10's components to multiple entities. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map10's [ecs.Relation]. +// +// See also [Batch.Add]. +func (m *Map10[A, B, C, D, E, F, G, H, I, J]) AddBatch(filter ecs.Filter, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map10 has no relation") + } + m.world.Batch().ExchangeRelation(filter, m.ids, nil, m.relation, target[0]) + } else { + m.world.Batch().Exchange(filter, m.ids, nil) + } +} + +// AddBatchQ adds the Map10's components to multiple entities and returns a query over them. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map10's [ecs.Relation]. +// +// See also [Batch.AddQ]. +func (m *Map10[A, B, C, D, E, F, G, H, I, J]) AddBatchQ(filter ecs.Filter, target ...ecs.Entity) Query10[A, B, C, D, E, F, G, H, I, J] { + var query ecs.Query + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map10 has no relation") + } + query = m.world.Batch().ExchangeRelationQ(filter, m.ids, nil, m.relation, target[0]) + } else { + query = m.world.Batch().ExchangeQ(filter, m.ids, nil) + } + 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, + } } // Assign the Map10's components to the given entity, using the supplied values. @@ -1823,9 +2778,64 @@ func (m *Map10[A, B, C, D, E, F, G, H, I, J]) Assign(entity ecs.Entity, a *A, b // Remove the Map10's components from the given entity. // +// The optional argument can be used to set the target [ecs.Entity] for the Map10's [ecs.Relation]. +// // 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...) +func (m *Map10[A, B, C, D, E, F, G, H, I, J]) Remove(entity ecs.Entity, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map10 has no relation") + } + m.world.Relations().Exchange(entity, nil, m.ids, m.relation, target[0]) + } else { + m.world.Remove(entity, m.ids...) + } +} + +// RemoveBatch removes the Map10's components to multiple entities. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map10's [ecs.Relation]. +// +// See also [Batch.Remove]. +func (m *Map10[A, B, C, D, E, F, G, H, I, J]) RemoveBatch(filter ecs.Filter, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map10 has no relation") + } + m.world.Batch().ExchangeRelation(filter, nil, m.ids, m.relation, target[0]) + } else { + m.world.Batch().Exchange(filter, nil, m.ids) + } +} + +// RemoveBatchQ adds the Map10's components to multiple entities and returns a query over them. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map10's [ecs.Relation]. +// +// See also [Batch.RemoveQ]. +func (m *Map10[A, B, C, D, E, F, G, H, I, J]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query10[A, B, C, D, E, F, G, H, I, J] { + var query ecs.Query + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map10 has no relation") + } + query = m.world.Batch().ExchangeRelationQ(filter, nil, m.ids, m.relation, target[0]) + } else { + query = m.world.Batch().ExchangeQ(filter, nil, m.ids) + } + 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, + } } // RemoveEntities removes all entities from the world that match the Map10's components. @@ -1835,7 +2845,7 @@ func (m *Map10[A, B, C, D, E, F, G, H, I, J]) Remove(entity ecs.Entity) { // // Returns the number of removed entities. // -// See also [ecs.World.NewEntityWith]. +// See also [Batch.RemoveEntities]. func (m *Map10[A, B, C, D, E, F, G, H, I, J]) RemoveEntities(exclusive bool) int { if exclusive { filter := m.mask.Exclusive() @@ -2025,9 +3035,65 @@ func (m *Map11[A, B, C, D, E, F, G, H, I, J, K]) NewWith(a *A, b *B, c *C, d *D, // Add the Map11's components to the given entity. // +// The optional argument can be used to set the target [ecs.Entity] for the Map11's [ecs.Relation]. +// // 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...) +func (m *Map11[A, B, C, D, E, F, G, H, I, J, K]) Add(entity ecs.Entity, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map11 has no relation") + } + m.world.Relations().Exchange(entity, m.ids, nil, m.relation, target[0]) + } else { + m.world.Add(entity, m.ids...) + } +} + +// AddBatch adds the Map11's components to multiple entities. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map11's [ecs.Relation]. +// +// See also [Batch.Add]. +func (m *Map11[A, B, C, D, E, F, G, H, I, J, K]) AddBatch(filter ecs.Filter, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map11 has no relation") + } + m.world.Batch().ExchangeRelation(filter, m.ids, nil, m.relation, target[0]) + } else { + m.world.Batch().Exchange(filter, m.ids, nil) + } +} + +// AddBatchQ adds the Map11's components to multiple entities and returns a query over them. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map11's [ecs.Relation]. +// +// See also [Batch.AddQ]. +func (m *Map11[A, B, C, D, E, F, G, H, I, J, K]) AddBatchQ(filter ecs.Filter, target ...ecs.Entity) Query11[A, B, C, D, E, F, G, H, I, J, K] { + var query ecs.Query + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map11 has no relation") + } + query = m.world.Batch().ExchangeRelationQ(filter, m.ids, nil, m.relation, target[0]) + } else { + query = m.world.Batch().ExchangeQ(filter, m.ids, nil) + } + 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, + } } // Assign the Map11's components to the given entity, using the supplied values. @@ -2051,9 +3117,65 @@ func (m *Map11[A, B, C, D, E, F, G, H, I, J, K]) Assign(entity ecs.Entity, a *A, // Remove the Map11's components from the given entity. // +// The optional argument can be used to set the target [ecs.Entity] for the Map11's [ecs.Relation]. +// // 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...) +func (m *Map11[A, B, C, D, E, F, G, H, I, J, K]) Remove(entity ecs.Entity, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map11 has no relation") + } + m.world.Relations().Exchange(entity, nil, m.ids, m.relation, target[0]) + } else { + m.world.Remove(entity, m.ids...) + } +} + +// RemoveBatch removes the Map11's components to multiple entities. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map11's [ecs.Relation]. +// +// See also [Batch.Remove]. +func (m *Map11[A, B, C, D, E, F, G, H, I, J, K]) RemoveBatch(filter ecs.Filter, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map11 has no relation") + } + m.world.Batch().ExchangeRelation(filter, nil, m.ids, m.relation, target[0]) + } else { + m.world.Batch().Exchange(filter, nil, m.ids) + } +} + +// RemoveBatchQ adds the Map11's components to multiple entities and returns a query over them. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map11's [ecs.Relation]. +// +// See also [Batch.RemoveQ]. +func (m *Map11[A, B, C, D, E, F, G, H, I, J, K]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query11[A, B, C, D, E, F, G, H, I, J, K] { + var query ecs.Query + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map11 has no relation") + } + query = m.world.Batch().ExchangeRelationQ(filter, nil, m.ids, m.relation, target[0]) + } else { + query = m.world.Batch().ExchangeQ(filter, nil, m.ids) + } + 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, + } } // RemoveEntities removes all entities from the world that match the Map11's components. @@ -2063,7 +3185,7 @@ func (m *Map11[A, B, C, D, E, F, G, H, I, J, K]) Remove(entity ecs.Entity) { // // Returns the number of removed entities. // -// See also [ecs.World.NewEntityWith]. +// See also [Batch.RemoveEntities]. func (m *Map11[A, B, C, D, E, F, G, H, I, J, K]) RemoveEntities(exclusive bool) int { if exclusive { filter := m.mask.Exclusive() @@ -2260,9 +3382,66 @@ func (m *Map12[A, B, C, D, E, F, G, H, I, J, K, L]) NewWith(a *A, b *B, c *C, d // Add the Map12's components to the given entity. // +// The optional argument can be used to set the target [ecs.Entity] for the Map12's [ecs.Relation]. +// // 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...) +func (m *Map12[A, B, C, D, E, F, G, H, I, J, K, L]) Add(entity ecs.Entity, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map12 has no relation") + } + m.world.Relations().Exchange(entity, m.ids, nil, m.relation, target[0]) + } else { + m.world.Add(entity, m.ids...) + } +} + +// AddBatch adds the Map12's components to multiple entities. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map12's [ecs.Relation]. +// +// See also [Batch.Add]. +func (m *Map12[A, B, C, D, E, F, G, H, I, J, K, L]) AddBatch(filter ecs.Filter, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map12 has no relation") + } + m.world.Batch().ExchangeRelation(filter, m.ids, nil, m.relation, target[0]) + } else { + m.world.Batch().Exchange(filter, m.ids, nil) + } +} + +// AddBatchQ adds the Map12's components to multiple entities and returns a query over them. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map12's [ecs.Relation]. +// +// See also [Batch.AddQ]. +func (m *Map12[A, B, C, D, E, F, G, H, I, J, K, L]) AddBatchQ(filter ecs.Filter, target ...ecs.Entity) Query12[A, B, C, D, E, F, G, H, I, J, K, L] { + var query ecs.Query + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map12 has no relation") + } + query = m.world.Batch().ExchangeRelationQ(filter, m.ids, nil, m.relation, target[0]) + } else { + query = m.world.Batch().ExchangeQ(filter, m.ids, nil) + } + 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, + } } // Assign the Map12's components to the given entity, using the supplied values. @@ -2287,9 +3466,66 @@ func (m *Map12[A, B, C, D, E, F, G, H, I, J, K, L]) Assign(entity ecs.Entity, a // Remove the Map12's components from the given entity. // +// The optional argument can be used to set the target [ecs.Entity] for the Map12's [ecs.Relation]. +// // 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...) +func (m *Map12[A, B, C, D, E, F, G, H, I, J, K, L]) Remove(entity ecs.Entity, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map12 has no relation") + } + m.world.Relations().Exchange(entity, nil, m.ids, m.relation, target[0]) + } else { + m.world.Remove(entity, m.ids...) + } +} + +// RemoveBatch removes the Map12's components to multiple entities. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map12's [ecs.Relation]. +// +// See also [Batch.Remove]. +func (m *Map12[A, B, C, D, E, F, G, H, I, J, K, L]) RemoveBatch(filter ecs.Filter, target ...ecs.Entity) { + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map12 has no relation") + } + m.world.Batch().ExchangeRelation(filter, nil, m.ids, m.relation, target[0]) + } else { + m.world.Batch().Exchange(filter, nil, m.ids) + } +} + +// RemoveBatchQ adds the Map12's components to multiple entities and returns a query over them. +// +// The optional argument can be used to set the target [ecs.Entity] for the Map12's [ecs.Relation]. +// +// See also [Batch.RemoveQ]. +func (m *Map12[A, B, C, D, E, F, G, H, I, J, K, L]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query12[A, B, C, D, E, F, G, H, I, J, K, L] { + var query ecs.Query + if len(target) > 0 { + if !m.hasRelation { + panic("can't set target entity: Map12 has no relation") + } + query = m.world.Batch().ExchangeRelationQ(filter, nil, m.ids, m.relation, target[0]) + } else { + query = m.world.Batch().ExchangeQ(filter, nil, m.ids) + } + 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, + } } // RemoveEntities removes all entities from the world that match the Map12's components. @@ -2299,7 +3535,7 @@ func (m *Map12[A, B, C, D, E, F, G, H, I, J, K, L]) Remove(entity ecs.Entity) { // // Returns the number of removed entities. // -// See also [ecs.World.NewEntityWith]. +// See also [Batch.RemoveEntities]. 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() diff --git a/generic/query_generated.go b/generic/query_generated.go index 7e39a9dc..02100d5c 100644 --- a/generic/query_generated.go +++ b/generic/query_generated.go @@ -75,7 +75,7 @@ func (q *Filter0) WithRelation(comp Comp, target ...ecs.Entity) *Filter0 { return q } -// Query builds a [Query0] query for iteration, with an optional relation target. +// Filter builds an [ecs.Filter], with an optional relation target. // // A relation target can't be used: // - if [Filter0.WithRelation] was not called @@ -83,7 +83,7 @@ func (q *Filter0) WithRelation(comp Comp, target ...ecs.Entity) *Filter0 { // - if the filter is registered for caching // // Panics in these cases. -func (q *Filter0) Query(w *ecs.World, target ...ecs.Entity) Query0 { +func (q *Filter0) Filter(w *ecs.World, target ...ecs.Entity) ecs.Filter { q.compiled.Compile(w, q.include, q.optional, q.exclude, q.targetType, q.target, q.hasTarget) filter := q.compiled.filter @@ -99,6 +99,19 @@ func (q *Filter0) Query(w *ecs.World, target ...ecs.Entity) Query0 { filter = &q.compiled.relationFilter } + return filter +} + +// Query builds a [Query0] query for iteration, with an optional relation target. +// +// A relation target can't be used: +// - if [Filter0.WithRelation] was not called +// - if the target was already set via [Filter0.WithRelation] +// - if the filter is registered for caching +// +// Panics in these cases. +func (q *Filter0) Query(w *ecs.World, target ...ecs.Entity) Query0 { + filter := q.Filter(w, target...) return Query0{ Query: w.Query(filter), target: q.compiled.TargetComp, @@ -241,7 +254,7 @@ func (q *Filter1[A]) WithRelation(comp Comp, target ...ecs.Entity) *Filter1[A] { return q } -// Query builds a [Query1] query for iteration, with an optional relation target. +// Filter builds an [ecs.Filter], with an optional relation target. // // A relation target can't be used: // - if [Filter1.WithRelation] was not called @@ -249,7 +262,7 @@ func (q *Filter1[A]) WithRelation(comp Comp, target ...ecs.Entity) *Filter1[A] { // - if the filter is registered for caching // // Panics in these cases. -func (q *Filter1[A]) Query(w *ecs.World, target ...ecs.Entity) Query1[A] { +func (q *Filter1[A]) Filter(w *ecs.World, target ...ecs.Entity) ecs.Filter { q.compiled.Compile(w, q.include, q.optional, q.exclude, q.targetType, q.target, q.hasTarget) filter := q.compiled.filter @@ -265,6 +278,19 @@ func (q *Filter1[A]) Query(w *ecs.World, target ...ecs.Entity) Query1[A] { filter = &q.compiled.relationFilter } + return filter +} + +// Query builds a [Query1] query for iteration, with an optional relation target. +// +// A relation target can't be used: +// - if [Filter1.WithRelation] was not called +// - if the target was already set via [Filter1.WithRelation] +// - if the filter is registered for caching +// +// Panics in these cases. +func (q *Filter1[A]) Query(w *ecs.World, target ...ecs.Entity) Query1[A] { + filter := q.Filter(w, target...) return Query1[A]{ Query: w.Query(filter), target: q.compiled.TargetComp, @@ -417,7 +443,7 @@ func (q *Filter2[A, B]) WithRelation(comp Comp, target ...ecs.Entity) *Filter2[A return q } -// Query builds a [Query2] query for iteration, with an optional relation target. +// Filter builds an [ecs.Filter], with an optional relation target. // // A relation target can't be used: // - if [Filter2.WithRelation] was not called @@ -425,7 +451,7 @@ func (q *Filter2[A, B]) WithRelation(comp Comp, target ...ecs.Entity) *Filter2[A // - if the filter is registered for caching // // Panics in these cases. -func (q *Filter2[A, B]) Query(w *ecs.World, target ...ecs.Entity) Query2[A, B] { +func (q *Filter2[A, B]) Filter(w *ecs.World, target ...ecs.Entity) ecs.Filter { q.compiled.Compile(w, q.include, q.optional, q.exclude, q.targetType, q.target, q.hasTarget) filter := q.compiled.filter @@ -441,6 +467,19 @@ func (q *Filter2[A, B]) Query(w *ecs.World, target ...ecs.Entity) Query2[A, B] { filter = &q.compiled.relationFilter } + return filter +} + +// Query builds a [Query2] query for iteration, with an optional relation target. +// +// A relation target can't be used: +// - if [Filter2.WithRelation] was not called +// - if the target was already set via [Filter2.WithRelation] +// - if the filter is registered for caching +// +// Panics in these cases. +func (q *Filter2[A, B]) Query(w *ecs.World, target ...ecs.Entity) Query2[A, B] { + filter := q.Filter(w, target...) return Query2[A, B]{ Query: w.Query(filter), target: q.compiled.TargetComp, @@ -597,7 +636,7 @@ func (q *Filter3[A, B, C]) WithRelation(comp Comp, target ...ecs.Entity) *Filter return q } -// Query builds a [Query3] query for iteration, with an optional relation target. +// Filter builds an [ecs.Filter], with an optional relation target. // // A relation target can't be used: // - if [Filter3.WithRelation] was not called @@ -605,7 +644,7 @@ func (q *Filter3[A, B, C]) WithRelation(comp Comp, target ...ecs.Entity) *Filter // - if the filter is registered for caching // // Panics in these cases. -func (q *Filter3[A, B, C]) Query(w *ecs.World, target ...ecs.Entity) Query3[A, B, C] { +func (q *Filter3[A, B, C]) Filter(w *ecs.World, target ...ecs.Entity) ecs.Filter { q.compiled.Compile(w, q.include, q.optional, q.exclude, q.targetType, q.target, q.hasTarget) filter := q.compiled.filter @@ -621,6 +660,19 @@ func (q *Filter3[A, B, C]) Query(w *ecs.World, target ...ecs.Entity) Query3[A, B filter = &q.compiled.relationFilter } + return filter +} + +// Query builds a [Query3] query for iteration, with an optional relation target. +// +// A relation target can't be used: +// - if [Filter3.WithRelation] was not called +// - if the target was already set via [Filter3.WithRelation] +// - if the filter is registered for caching +// +// Panics in these cases. +func (q *Filter3[A, B, C]) Query(w *ecs.World, target ...ecs.Entity) Query3[A, B, C] { + filter := q.Filter(w, target...) return Query3[A, B, C]{ Query: w.Query(filter), target: q.compiled.TargetComp, @@ -781,7 +833,7 @@ func (q *Filter4[A, B, C, D]) WithRelation(comp Comp, target ...ecs.Entity) *Fil return q } -// Query builds a [Query4] query for iteration, with an optional relation target. +// Filter builds an [ecs.Filter], with an optional relation target. // // A relation target can't be used: // - if [Filter4.WithRelation] was not called @@ -789,7 +841,7 @@ func (q *Filter4[A, B, C, D]) WithRelation(comp Comp, target ...ecs.Entity) *Fil // - if the filter is registered for caching // // Panics in these cases. -func (q *Filter4[A, B, C, D]) Query(w *ecs.World, target ...ecs.Entity) Query4[A, B, C, D] { +func (q *Filter4[A, B, C, D]) Filter(w *ecs.World, target ...ecs.Entity) ecs.Filter { q.compiled.Compile(w, q.include, q.optional, q.exclude, q.targetType, q.target, q.hasTarget) filter := q.compiled.filter @@ -805,6 +857,19 @@ func (q *Filter4[A, B, C, D]) Query(w *ecs.World, target ...ecs.Entity) Query4[A filter = &q.compiled.relationFilter } + return filter +} + +// Query builds a [Query4] query for iteration, with an optional relation target. +// +// A relation target can't be used: +// - if [Filter4.WithRelation] was not called +// - if the target was already set via [Filter4.WithRelation] +// - if the filter is registered for caching +// +// Panics in these cases. +func (q *Filter4[A, B, C, D]) Query(w *ecs.World, target ...ecs.Entity) Query4[A, B, C, D] { + filter := q.Filter(w, target...) return Query4[A, B, C, D]{ Query: w.Query(filter), target: q.compiled.TargetComp, @@ -969,7 +1034,7 @@ func (q *Filter5[A, B, C, D, E]) WithRelation(comp Comp, target ...ecs.Entity) * return q } -// Query builds a [Query5] query for iteration, with an optional relation target. +// Filter builds an [ecs.Filter], with an optional relation target. // // A relation target can't be used: // - if [Filter5.WithRelation] was not called @@ -977,7 +1042,7 @@ func (q *Filter5[A, B, C, D, E]) WithRelation(comp Comp, target ...ecs.Entity) * // - if the filter is registered for caching // // Panics in these cases. -func (q *Filter5[A, B, C, D, E]) Query(w *ecs.World, target ...ecs.Entity) Query5[A, B, C, D, E] { +func (q *Filter5[A, B, C, D, E]) Filter(w *ecs.World, target ...ecs.Entity) ecs.Filter { q.compiled.Compile(w, q.include, q.optional, q.exclude, q.targetType, q.target, q.hasTarget) filter := q.compiled.filter @@ -993,6 +1058,19 @@ func (q *Filter5[A, B, C, D, E]) Query(w *ecs.World, target ...ecs.Entity) Query filter = &q.compiled.relationFilter } + return filter +} + +// Query builds a [Query5] query for iteration, with an optional relation target. +// +// A relation target can't be used: +// - if [Filter5.WithRelation] was not called +// - if the target was already set via [Filter5.WithRelation] +// - if the filter is registered for caching +// +// Panics in these cases. +func (q *Filter5[A, B, C, D, E]) Query(w *ecs.World, target ...ecs.Entity) Query5[A, B, C, D, E] { + filter := q.Filter(w, target...) return Query5[A, B, C, D, E]{ Query: w.Query(filter), target: q.compiled.TargetComp, @@ -1161,7 +1239,7 @@ func (q *Filter6[A, B, C, D, E, F]) WithRelation(comp Comp, target ...ecs.Entity return q } -// Query builds a [Query6] query for iteration, with an optional relation target. +// Filter builds an [ecs.Filter], with an optional relation target. // // A relation target can't be used: // - if [Filter6.WithRelation] was not called @@ -1169,7 +1247,7 @@ func (q *Filter6[A, B, C, D, E, F]) WithRelation(comp Comp, target ...ecs.Entity // - if the filter is registered for caching // // Panics in these cases. -func (q *Filter6[A, B, C, D, E, F]) Query(w *ecs.World, target ...ecs.Entity) Query6[A, B, C, D, E, F] { +func (q *Filter6[A, B, C, D, E, F]) Filter(w *ecs.World, target ...ecs.Entity) ecs.Filter { q.compiled.Compile(w, q.include, q.optional, q.exclude, q.targetType, q.target, q.hasTarget) filter := q.compiled.filter @@ -1185,6 +1263,19 @@ func (q *Filter6[A, B, C, D, E, F]) Query(w *ecs.World, target ...ecs.Entity) Qu filter = &q.compiled.relationFilter } + return filter +} + +// Query builds a [Query6] query for iteration, with an optional relation target. +// +// A relation target can't be used: +// - if [Filter6.WithRelation] was not called +// - if the target was already set via [Filter6.WithRelation] +// - if the filter is registered for caching +// +// Panics in these cases. +func (q *Filter6[A, B, C, D, E, F]) Query(w *ecs.World, target ...ecs.Entity) Query6[A, B, C, D, E, F] { + filter := q.Filter(w, target...) return Query6[A, B, C, D, E, F]{ Query: w.Query(filter), target: q.compiled.TargetComp, @@ -1357,7 +1448,7 @@ func (q *Filter7[A, B, C, D, E, F, G]) WithRelation(comp Comp, target ...ecs.Ent return q } -// Query builds a [Query7] query for iteration, with an optional relation target. +// Filter builds an [ecs.Filter], with an optional relation target. // // A relation target can't be used: // - if [Filter7.WithRelation] was not called @@ -1365,7 +1456,7 @@ func (q *Filter7[A, B, C, D, E, F, G]) WithRelation(comp Comp, target ...ecs.Ent // - if the filter is registered for caching // // Panics in these cases. -func (q *Filter7[A, B, C, D, E, F, G]) Query(w *ecs.World, target ...ecs.Entity) Query7[A, B, C, D, E, F, G] { +func (q *Filter7[A, B, C, D, E, F, G]) Filter(w *ecs.World, target ...ecs.Entity) ecs.Filter { q.compiled.Compile(w, q.include, q.optional, q.exclude, q.targetType, q.target, q.hasTarget) filter := q.compiled.filter @@ -1381,6 +1472,19 @@ func (q *Filter7[A, B, C, D, E, F, G]) Query(w *ecs.World, target ...ecs.Entity) filter = &q.compiled.relationFilter } + return filter +} + +// Query builds a [Query7] query for iteration, with an optional relation target. +// +// A relation target can't be used: +// - if [Filter7.WithRelation] was not called +// - if the target was already set via [Filter7.WithRelation] +// - if the filter is registered for caching +// +// Panics in these cases. +func (q *Filter7[A, B, C, D, E, F, G]) Query(w *ecs.World, target ...ecs.Entity) Query7[A, B, C, D, E, F, G] { + filter := q.Filter(w, target...) return Query7[A, B, C, D, E, F, G]{ Query: w.Query(filter), target: q.compiled.TargetComp, @@ -1557,7 +1661,7 @@ func (q *Filter8[A, B, C, D, E, F, G, H]) WithRelation(comp Comp, target ...ecs. return q } -// Query builds a [Query8] query for iteration, with an optional relation target. +// Filter builds an [ecs.Filter], with an optional relation target. // // A relation target can't be used: // - if [Filter8.WithRelation] was not called @@ -1565,7 +1669,7 @@ func (q *Filter8[A, B, C, D, E, F, G, H]) WithRelation(comp Comp, target ...ecs. // - if the filter is registered for caching // // Panics in these cases. -func (q *Filter8[A, B, C, D, E, F, G, H]) Query(w *ecs.World, target ...ecs.Entity) Query8[A, B, C, D, E, F, G, H] { +func (q *Filter8[A, B, C, D, E, F, G, H]) Filter(w *ecs.World, target ...ecs.Entity) ecs.Filter { q.compiled.Compile(w, q.include, q.optional, q.exclude, q.targetType, q.target, q.hasTarget) filter := q.compiled.filter @@ -1581,6 +1685,19 @@ func (q *Filter8[A, B, C, D, E, F, G, H]) Query(w *ecs.World, target ...ecs.Enti filter = &q.compiled.relationFilter } + return filter +} + +// Query builds a [Query8] query for iteration, with an optional relation target. +// +// A relation target can't be used: +// - if [Filter8.WithRelation] was not called +// - if the target was already set via [Filter8.WithRelation] +// - if the filter is registered for caching +// +// Panics in these cases. +func (q *Filter8[A, B, C, D, E, F, G, H]) Query(w *ecs.World, target ...ecs.Entity) Query8[A, B, C, D, E, F, G, H] { + filter := q.Filter(w, target...) return Query8[A, B, C, D, E, F, G, H]{ Query: w.Query(filter), target: q.compiled.TargetComp, @@ -1761,7 +1878,7 @@ func (q *Filter9[A, B, C, D, E, F, G, H, I]) WithRelation(comp Comp, target ...e return q } -// Query builds a [Query9] query for iteration, with an optional relation target. +// Filter builds an [ecs.Filter], with an optional relation target. // // A relation target can't be used: // - if [Filter9.WithRelation] was not called @@ -1769,7 +1886,7 @@ func (q *Filter9[A, B, C, D, E, F, G, H, I]) WithRelation(comp Comp, target ...e // - 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] { +func (q *Filter9[A, B, C, D, E, F, G, H, I]) Filter(w *ecs.World, target ...ecs.Entity) ecs.Filter { q.compiled.Compile(w, q.include, q.optional, q.exclude, q.targetType, q.target, q.hasTarget) filter := q.compiled.filter @@ -1785,6 +1902,19 @@ func (q *Filter9[A, B, C, D, E, F, G, H, I]) Query(w *ecs.World, target ...ecs.E filter = &q.compiled.relationFilter } + return filter +} + +// 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] { + filter := q.Filter(w, target...) return Query9[A, B, C, D, E, F, G, H, I]{ Query: w.Query(filter), target: q.compiled.TargetComp, @@ -1969,7 +2099,7 @@ func (q *Filter10[A, B, C, D, E, F, G, H, I, J]) WithRelation(comp Comp, target return q } -// Query builds a [Query10] query for iteration, with an optional relation target. +// Filter builds an [ecs.Filter], with an optional relation target. // // A relation target can't be used: // - if [Filter10.WithRelation] was not called @@ -1977,7 +2107,7 @@ func (q *Filter10[A, B, C, D, E, F, G, H, I, J]) WithRelation(comp Comp, target // - 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] { +func (q *Filter10[A, B, C, D, E, F, G, H, I, J]) Filter(w *ecs.World, target ...ecs.Entity) ecs.Filter { q.compiled.Compile(w, q.include, q.optional, q.exclude, q.targetType, q.target, q.hasTarget) filter := q.compiled.filter @@ -1993,6 +2123,19 @@ func (q *Filter10[A, B, C, D, E, F, G, H, I, J]) Query(w *ecs.World, target ...e filter = &q.compiled.relationFilter } + return filter +} + +// 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] { + filter := q.Filter(w, target...) return Query10[A, B, C, D, E, F, G, H, I, J]{ Query: w.Query(filter), target: q.compiled.TargetComp, @@ -2181,7 +2324,7 @@ func (q *Filter11[A, B, C, D, E, F, G, H, I, J, K]) WithRelation(comp Comp, targ return q } -// Query builds a [Query11] query for iteration, with an optional relation target. +// Filter builds an [ecs.Filter], with an optional relation target. // // A relation target can't be used: // - if [Filter11.WithRelation] was not called @@ -2189,7 +2332,7 @@ func (q *Filter11[A, B, C, D, E, F, G, H, I, J, K]) WithRelation(comp Comp, targ // - 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] { +func (q *Filter11[A, B, C, D, E, F, G, H, I, J, K]) Filter(w *ecs.World, target ...ecs.Entity) ecs.Filter { q.compiled.Compile(w, q.include, q.optional, q.exclude, q.targetType, q.target, q.hasTarget) filter := q.compiled.filter @@ -2205,6 +2348,19 @@ func (q *Filter11[A, B, C, D, E, F, G, H, I, J, K]) Query(w *ecs.World, target . filter = &q.compiled.relationFilter } + return filter +} + +// 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] { + filter := q.Filter(w, target...) return Query11[A, B, C, D, E, F, G, H, I, J, K]{ Query: w.Query(filter), target: q.compiled.TargetComp, @@ -2397,7 +2553,7 @@ func (q *Filter12[A, B, C, D, E, F, G, H, I, J, K, L]) WithRelation(comp Comp, t return q } -// Query builds a [Query12] query for iteration, with an optional relation target. +// Filter builds an [ecs.Filter], with an optional relation target. // // A relation target can't be used: // - if [Filter12.WithRelation] was not called @@ -2405,7 +2561,7 @@ func (q *Filter12[A, B, C, D, E, F, G, H, I, J, K, L]) WithRelation(comp Comp, t // - 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] { +func (q *Filter12[A, B, C, D, E, F, G, H, I, J, K, L]) Filter(w *ecs.World, target ...ecs.Entity) ecs.Filter { q.compiled.Compile(w, q.include, q.optional, q.exclude, q.targetType, q.target, q.hasTarget) filter := q.compiled.filter @@ -2421,6 +2577,19 @@ func (q *Filter12[A, B, C, D, E, F, G, H, I, J, K, L]) Query(w *ecs.World, targe filter = &q.compiled.relationFilter } + return filter +} + +// 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] { + filter := q.Filter(w, target...) return Query12[A, B, C, D, E, F, G, H, I, J, K, L]{ Query: w.Query(filter), target: q.compiled.TargetComp, From f071dce16a03b02297564cf83f402e178e077bd3 Mon Sep 17 00:00:00 2001 From: mlange-42 Date: Thu, 18 Jan 2024 21:56:07 +0100 Subject: [PATCH 10/12] add batch operations to generic MapX, with optional target --- CHANGELOG.md | 1 + generic/exchange_test.go | 25 + generic/generate/map.go.txt | 12 +- generic/map_generated.go | 165 ++---- generic/map_generated_test.go | 1040 +++++++++++++++++++++++++++++++++ 5 files changed, 1122 insertions(+), 121 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 01a39484..c4f6121b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ This change was necessary to get the same performance as before, despite the mor * Generic API adds `MapX.AddBatch()`, `MapX.AddBatchQ()`, `MapX.RemoveBatch()`and `MapX.RemoveBatchQ()` (#342) * Generic API adds optional relation target argument to most `MapX` methods (#342) * Generic API adds `FilterX.Filter()` to get an `ecs.Filter` from a generic one (#342) +* Generic API adds `MapX.AddBatch()`, `MapX.AddBatchQ()`, `MapX.RemoveBatch()` and `MapX.RemoveBatchQ()` (#342) ### Performance diff --git a/generic/exchange_test.go b/generic/exchange_test.go index ca5568ad..97430a70 100644 --- a/generic/exchange_test.go +++ b/generic/exchange_test.go @@ -130,4 +130,29 @@ func TestExchangeRelationPanics(t *testing.T) { assert.Panics(t, func() { ex1.Exchange(e0, e0) }) + + assert.Panics(t, func() { + ex1.ExchangeBatch(ecs.All(), e0) + }) +} + +func TestExchangeBatch(t *testing.T) { + w := ecs.NewWorld() + + posID := ecs.ComponentID[Position](&w) + + ex1 := NewExchange(&w). + Adds(T3[Position, Velocity, testRelationA]()...). + WithRelation(T[testRelationA]()) + + parent1 := w.NewEntity(posID) + + b := ecs.NewBuilder(&w) + b.NewBatch(10) + + filter := ecs.All().Exclusive() + ex1.ExchangeBatch(&filter, parent1) + + b.NewBatch(10) + ex1.ExchangeBatch(&filter) } diff --git a/generic/generate/map.go.txt b/generic/generate/map.go.txt index a1c5b7f1..7bec7a11 100644 --- a/generic/generate/map.go.txt +++ b/generic/generate/map.go.txt @@ -139,7 +139,8 @@ func (m *Map{{ .Index }}{{ .Types }}) AddBatch(filter ecs.Filter, target ...ecs. } } -// AddBatchQ adds the Map{{ .Index }}'s components to multiple entities and returns a query over them. +// AddBatchQ adds the Map{{ .Index }}'s components to multiple entities and returns a query over them +// and the newly added Map{{ .Index }}'s components. // // The optional argument can be used to set the target [ecs.Entity] for the Map{{ .Index }}'s [ecs.Relation]. // @@ -186,7 +187,7 @@ func (m *Map{{ .Index }}{{ .Types }}) Remove(entity ecs.Entity, target ...ecs.En } } -// RemoveBatch removes the Map{{ .Index }}'s components to multiple entities. +// RemoveBatch removes the Map{{ .Index }}'s components from multiple entities. // // The optional argument can be used to set the target [ecs.Entity] for the Map{{ .Index }}'s [ecs.Relation]. // @@ -202,12 +203,12 @@ func (m *Map{{ .Index }}{{ .Types }}) RemoveBatch(filter ecs.Filter, target ...e } } -// RemoveBatchQ adds the Map{{ .Index }}'s components to multiple entities and returns a query over them. +// RemoveBatchQ adds the Map{{ .Index }}'s components from multiple entities and returns a query over them. // // The optional argument can be used to set the target [ecs.Entity] for the Map{{ .Index }}'s [ecs.Relation]. // // See also [Batch.RemoveQ]. -func (m *Map{{ .Index }}{{ .Types }}) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query{{ .Index }}{{ .Types }} { +func (m *Map{{ .Index }}{{ .Types }}) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query0 { var query ecs.Query if len(target) > 0 { if !m.hasRelation { @@ -217,9 +218,8 @@ func (m *Map{{ .Index }}{{ .Types }}) RemoveBatchQ(filter ecs.Filter, target ... } else { query = m.world.Batch().ExchangeQ(filter, nil, m.ids) } - return Query{{ .Index }}{{ .Types }}{ + return Query0{ Query: query, - {{ .IDAssign2 }} } } diff --git a/generic/map_generated.go b/generic/map_generated.go index 2e9d5215..36b8f6dc 100644 --- a/generic/map_generated.go +++ b/generic/map_generated.go @@ -145,7 +145,8 @@ func (m *Map1[A]) AddBatch(filter ecs.Filter, target ...ecs.Entity) { } } -// AddBatchQ adds the Map1's components to multiple entities and returns a query over them. +// AddBatchQ adds the Map1's components to multiple entities and returns a query over them +// and the newly added Map1's components. // // The optional argument can be used to set the target [ecs.Entity] for the Map1's [ecs.Relation]. // @@ -207,12 +208,13 @@ func (m *Map1[A]) RemoveBatch(filter ecs.Filter, target ...ecs.Entity) { } } -// RemoveBatchQ adds the Map1's components to multiple entities and returns a query over them. +// RemoveBatchQ adds the Map1's components to multiple entities and returns a query over them, +// without any queries components. // // The optional argument can be used to set the target [ecs.Entity] for the Map1's [ecs.Relation]. // // See also [Batch.RemoveQ]. -func (m *Map1[A]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query1[A] { +func (m *Map1[A]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query0 { var query ecs.Query if len(target) > 0 { if !m.hasRelation { @@ -222,9 +224,8 @@ func (m *Map1[A]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query1[A } else { query = m.world.Batch().ExchangeQ(filter, nil, m.ids) } - return Query1[A]{ + return Query0{ Query: query, - id0: m.id0, } } @@ -392,7 +393,8 @@ func (m *Map2[A, B]) AddBatch(filter ecs.Filter, target ...ecs.Entity) { } } -// AddBatchQ adds the Map2's components to multiple entities and returns a query over them. +// AddBatchQ adds the Map2's components to multiple entities and returns a query over them +// and the newly added Map2's components. // // The optional argument can be used to set the target [ecs.Entity] for the Map2's [ecs.Relation]. // @@ -461,7 +463,7 @@ func (m *Map2[A, B]) RemoveBatch(filter ecs.Filter, target ...ecs.Entity) { // The optional argument can be used to set the target [ecs.Entity] for the Map2's [ecs.Relation]. // // See also [Batch.RemoveQ]. -func (m *Map2[A, B]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query2[A, B] { +func (m *Map2[A, B]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query0 { var query ecs.Query if len(target) > 0 { if !m.hasRelation { @@ -471,10 +473,8 @@ func (m *Map2[A, B]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query } else { query = m.world.Batch().ExchangeQ(filter, nil, m.ids) } - return Query2[A, B]{ + return Query0{ Query: query, - id0: m.id0, - id1: m.id1, } } @@ -649,7 +649,8 @@ func (m *Map3[A, B, C]) AddBatch(filter ecs.Filter, target ...ecs.Entity) { } } -// AddBatchQ adds the Map3's components to multiple entities and returns a query over them. +// AddBatchQ adds the Map3's components to multiple entities and returns a query over them +// and the newly added Map3's components. // // The optional argument can be used to set the target [ecs.Entity] for the Map3's [ecs.Relation]. // @@ -720,7 +721,7 @@ func (m *Map3[A, B, C]) RemoveBatch(filter ecs.Filter, target ...ecs.Entity) { // The optional argument can be used to set the target [ecs.Entity] for the Map3's [ecs.Relation]. // // See also [Batch.RemoveQ]. -func (m *Map3[A, B, C]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query3[A, B, C] { +func (m *Map3[A, B, C]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query0 { var query ecs.Query if len(target) > 0 { if !m.hasRelation { @@ -730,11 +731,8 @@ func (m *Map3[A, B, C]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Qu } else { query = m.world.Batch().ExchangeQ(filter, nil, m.ids) } - return Query3[A, B, C]{ + return Query0{ Query: query, - id0: m.id0, - id1: m.id1, - id2: m.id2, } } @@ -916,7 +914,8 @@ func (m *Map4[A, B, C, D]) AddBatch(filter ecs.Filter, target ...ecs.Entity) { } } -// AddBatchQ adds the Map4's components to multiple entities and returns a query over them. +// AddBatchQ adds the Map4's components to multiple entities and returns a query over them +// and the newly added Map4's components. // // The optional argument can be used to set the target [ecs.Entity] for the Map4's [ecs.Relation]. // @@ -989,7 +988,7 @@ func (m *Map4[A, B, C, D]) RemoveBatch(filter ecs.Filter, target ...ecs.Entity) // The optional argument can be used to set the target [ecs.Entity] for the Map4's [ecs.Relation]. // // See also [Batch.RemoveQ]. -func (m *Map4[A, B, C, D]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query4[A, B, C, D] { +func (m *Map4[A, B, C, D]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query0 { var query ecs.Query if len(target) > 0 { if !m.hasRelation { @@ -999,12 +998,8 @@ func (m *Map4[A, B, C, D]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) } else { query = m.world.Batch().ExchangeQ(filter, nil, m.ids) } - return Query4[A, B, C, D]{ + return Query0{ Query: query, - id0: m.id0, - id1: m.id1, - id2: m.id2, - id3: m.id3, } } @@ -1193,7 +1188,8 @@ func (m *Map5[A, B, C, D, E]) AddBatch(filter ecs.Filter, target ...ecs.Entity) } } -// AddBatchQ adds the Map5's components to multiple entities and returns a query over them. +// AddBatchQ adds the Map5's components to multiple entities and returns a query over them +// and the newly added Map5's components. // // The optional argument can be used to set the target [ecs.Entity] for the Map5's [ecs.Relation]. // @@ -1268,7 +1264,7 @@ func (m *Map5[A, B, C, D, E]) RemoveBatch(filter ecs.Filter, target ...ecs.Entit // The optional argument can be used to set the target [ecs.Entity] for the Map5's [ecs.Relation]. // // See also [Batch.RemoveQ]. -func (m *Map5[A, B, C, D, E]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query5[A, B, C, D, E] { +func (m *Map5[A, B, C, D, E]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query0 { var query ecs.Query if len(target) > 0 { if !m.hasRelation { @@ -1278,13 +1274,8 @@ func (m *Map5[A, B, C, D, E]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Enti } else { query = m.world.Batch().ExchangeQ(filter, nil, m.ids) } - return Query5[A, B, C, D, E]{ + return Query0{ Query: query, - id0: m.id0, - id1: m.id1, - id2: m.id2, - id3: m.id3, - id4: m.id4, } } @@ -1480,7 +1471,8 @@ func (m *Map6[A, B, C, D, E, F]) AddBatch(filter ecs.Filter, target ...ecs.Entit } } -// AddBatchQ adds the Map6's components to multiple entities and returns a query over them. +// AddBatchQ adds the Map6's components to multiple entities and returns a query over them +// and the newly added Map6's components. // // The optional argument can be used to set the target [ecs.Entity] for the Map6's [ecs.Relation]. // @@ -1557,7 +1549,7 @@ func (m *Map6[A, B, C, D, E, F]) RemoveBatch(filter ecs.Filter, target ...ecs.En // The optional argument can be used to set the target [ecs.Entity] for the Map6's [ecs.Relation]. // // See also [Batch.RemoveQ]. -func (m *Map6[A, B, C, D, E, F]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query6[A, B, C, D, E, F] { +func (m *Map6[A, B, C, D, E, F]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query0 { var query ecs.Query if len(target) > 0 { if !m.hasRelation { @@ -1567,14 +1559,8 @@ func (m *Map6[A, B, C, D, E, F]) RemoveBatchQ(filter ecs.Filter, target ...ecs.E } else { query = m.world.Batch().ExchangeQ(filter, nil, m.ids) } - return Query6[A, B, C, D, E, F]{ + return Query0{ Query: query, - id0: m.id0, - id1: m.id1, - id2: m.id2, - id3: m.id3, - id4: m.id4, - id5: m.id5, } } @@ -1777,7 +1763,8 @@ func (m *Map7[A, B, C, D, E, F, G]) AddBatch(filter ecs.Filter, target ...ecs.En } } -// AddBatchQ adds the Map7's components to multiple entities and returns a query over them. +// AddBatchQ adds the Map7's components to multiple entities and returns a query over them +// and the newly added Map7's components. // // The optional argument can be used to set the target [ecs.Entity] for the Map7's [ecs.Relation]. // @@ -1856,7 +1843,7 @@ func (m *Map7[A, B, C, D, E, F, G]) RemoveBatch(filter ecs.Filter, target ...ecs // The optional argument can be used to set the target [ecs.Entity] for the Map7's [ecs.Relation]. // // See also [Batch.RemoveQ]. -func (m *Map7[A, B, C, D, E, F, G]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query7[A, B, C, D, E, F, G] { +func (m *Map7[A, B, C, D, E, F, G]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query0 { var query ecs.Query if len(target) > 0 { if !m.hasRelation { @@ -1866,15 +1853,8 @@ func (m *Map7[A, B, C, D, E, F, G]) RemoveBatchQ(filter ecs.Filter, target ...ec } else { query = m.world.Batch().ExchangeQ(filter, nil, m.ids) } - return Query7[A, B, C, D, E, F, G]{ + return Query0{ Query: query, - id0: m.id0, - id1: m.id1, - id2: m.id2, - id3: m.id3, - id4: m.id4, - id5: m.id5, - id6: m.id6, } } @@ -2084,7 +2064,8 @@ func (m *Map8[A, B, C, D, E, F, G, H]) AddBatch(filter ecs.Filter, target ...ecs } } -// AddBatchQ adds the Map8's components to multiple entities and returns a query over them. +// AddBatchQ adds the Map8's components to multiple entities and returns a query over them +// and the newly added Map8's components. // // The optional argument can be used to set the target [ecs.Entity] for the Map8's [ecs.Relation]. // @@ -2165,7 +2146,7 @@ func (m *Map8[A, B, C, D, E, F, G, H]) RemoveBatch(filter ecs.Filter, target ... // The optional argument can be used to set the target [ecs.Entity] for the Map8's [ecs.Relation]. // // See also [Batch.RemoveQ]. -func (m *Map8[A, B, C, D, E, F, G, H]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query8[A, B, C, D, E, F, G, H] { +func (m *Map8[A, B, C, D, E, F, G, H]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query0 { var query ecs.Query if len(target) > 0 { if !m.hasRelation { @@ -2175,16 +2156,8 @@ func (m *Map8[A, B, C, D, E, F, G, H]) RemoveBatchQ(filter ecs.Filter, target .. } else { query = m.world.Batch().ExchangeQ(filter, nil, m.ids) } - return Query8[A, B, C, D, E, F, G, H]{ + return Query0{ 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, } } @@ -2401,7 +2374,8 @@ func (m *Map9[A, B, C, D, E, F, G, H, I]) AddBatch(filter ecs.Filter, target ... } } -// AddBatchQ adds the Map9's components to multiple entities and returns a query over them. +// AddBatchQ adds the Map9's components to multiple entities and returns a query over them +// and the newly added Map9's components. // // The optional argument can be used to set the target [ecs.Entity] for the Map9's [ecs.Relation]. // @@ -2484,7 +2458,7 @@ func (m *Map9[A, B, C, D, E, F, G, H, I]) RemoveBatch(filter ecs.Filter, target // The optional argument can be used to set the target [ecs.Entity] for the Map9's [ecs.Relation]. // // See also [Batch.RemoveQ]. -func (m *Map9[A, B, C, D, E, F, G, H, I]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query9[A, B, C, D, E, F, G, H, I] { +func (m *Map9[A, B, C, D, E, F, G, H, I]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query0 { var query ecs.Query if len(target) > 0 { if !m.hasRelation { @@ -2494,17 +2468,8 @@ func (m *Map9[A, B, C, D, E, F, G, H, I]) RemoveBatchQ(filter ecs.Filter, target } else { query = m.world.Batch().ExchangeQ(filter, nil, m.ids) } - return Query9[A, B, C, D, E, F, G, H, I]{ + return Query0{ 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, } } @@ -2728,7 +2693,8 @@ func (m *Map10[A, B, C, D, E, F, G, H, I, J]) AddBatch(filter ecs.Filter, target } } -// AddBatchQ adds the Map10's components to multiple entities and returns a query over them. +// AddBatchQ adds the Map10's components to multiple entities and returns a query over them +// and the newly added Map10's components. // // The optional argument can be used to set the target [ecs.Entity] for the Map10's [ecs.Relation]. // @@ -2813,7 +2779,7 @@ func (m *Map10[A, B, C, D, E, F, G, H, I, J]) RemoveBatch(filter ecs.Filter, tar // The optional argument can be used to set the target [ecs.Entity] for the Map10's [ecs.Relation]. // // See also [Batch.RemoveQ]. -func (m *Map10[A, B, C, D, E, F, G, H, I, J]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query10[A, B, C, D, E, F, G, H, I, J] { +func (m *Map10[A, B, C, D, E, F, G, H, I, J]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query0 { var query ecs.Query if len(target) > 0 { if !m.hasRelation { @@ -2823,18 +2789,8 @@ func (m *Map10[A, B, C, D, E, F, G, H, I, J]) RemoveBatchQ(filter ecs.Filter, ta } else { query = m.world.Batch().ExchangeQ(filter, nil, m.ids) } - return Query10[A, B, C, D, E, F, G, H, I, J]{ + return Query0{ 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, } } @@ -3065,7 +3021,8 @@ func (m *Map11[A, B, C, D, E, F, G, H, I, J, K]) AddBatch(filter ecs.Filter, tar } } -// AddBatchQ adds the Map11's components to multiple entities and returns a query over them. +// AddBatchQ adds the Map11's components to multiple entities and returns a query over them +// and the newly added Map11's components. // // The optional argument can be used to set the target [ecs.Entity] for the Map11's [ecs.Relation]. // @@ -3152,7 +3109,7 @@ func (m *Map11[A, B, C, D, E, F, G, H, I, J, K]) RemoveBatch(filter ecs.Filter, // The optional argument can be used to set the target [ecs.Entity] for the Map11's [ecs.Relation]. // // See also [Batch.RemoveQ]. -func (m *Map11[A, B, C, D, E, F, G, H, I, J, K]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query11[A, B, C, D, E, F, G, H, I, J, K] { +func (m *Map11[A, B, C, D, E, F, G, H, I, J, K]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query0 { var query ecs.Query if len(target) > 0 { if !m.hasRelation { @@ -3162,19 +3119,8 @@ func (m *Map11[A, B, C, D, E, F, G, H, I, J, K]) RemoveBatchQ(filter ecs.Filter, } else { query = m.world.Batch().ExchangeQ(filter, nil, m.ids) } - return Query11[A, B, C, D, E, F, G, H, I, J, K]{ + return Query0{ 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, } } @@ -3412,7 +3358,8 @@ func (m *Map12[A, B, C, D, E, F, G, H, I, J, K, L]) AddBatch(filter ecs.Filter, } } -// AddBatchQ adds the Map12's components to multiple entities and returns a query over them. +// AddBatchQ adds the Map12's components to multiple entities and returns a query over them +// and the newly added Map12's components. // // The optional argument can be used to set the target [ecs.Entity] for the Map12's [ecs.Relation]. // @@ -3501,7 +3448,7 @@ func (m *Map12[A, B, C, D, E, F, G, H, I, J, K, L]) RemoveBatch(filter ecs.Filte // The optional argument can be used to set the target [ecs.Entity] for the Map12's [ecs.Relation]. // // See also [Batch.RemoveQ]. -func (m *Map12[A, B, C, D, E, F, G, H, I, J, K, L]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query12[A, B, C, D, E, F, G, H, I, J, K, L] { +func (m *Map12[A, B, C, D, E, F, G, H, I, J, K, L]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query0 { var query ecs.Query if len(target) > 0 { if !m.hasRelation { @@ -3511,20 +3458,8 @@ func (m *Map12[A, B, C, D, E, F, G, H, I, J, K, L]) RemoveBatchQ(filter ecs.Filt } else { query = m.world.Batch().ExchangeQ(filter, nil, m.ids) } - return Query12[A, B, C, D, E, F, G, H, I, J, K, L]{ + return Query0{ 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, } } diff --git a/generic/map_generated_test.go b/generic/map_generated_test.go index 1d129ccc..0150fdf8 100644 --- a/generic/map_generated_test.go +++ b/generic/map_generated_test.go @@ -74,6 +74,87 @@ func TestMap1Generated(t *testing.T) { mapper := NewMap[testRelationA](&w) assert.Equal(t, target, mapper.GetRelation(e)) assert.Equal(t, target, mapper.GetRelationUnchecked(e)) + + // === Batch operations ==== + + w.Batch().RemoveEntities(ecs.All()) + + mut3 := NewMap1[testRelationA](&w) + mut4 := NewMap1[testStruct0](&w, T[testRelationB]()) + rel2ID := ecs.ComponentID[testRelationB](&w) + + e1 := w.NewEntity() + e2 := w.NewEntity() + e3 := w.NewEntity() + assert.Panics(t, func() { + mut3.Add(e1, e2) + }) + mut2.Add(e1, e2) + + assert.Panics(t, func() { + mut3.Remove(e1, e2) + }) + + mut.Add(e3) + w.Add(e3, rel2ID) + mut4.Remove(e3, e1) + + w.Batch().RemoveEntities(ecs.All()) + + e1 = w.NewEntity() + e2 = w.NewEntity() + _ = w.NewEntity() + assert.Panics(t, func() { + mut3.AddBatch(ecs.All(), e2) + }) + assert.Panics(t, func() { + _ = mut3.AddBatchQ(ecs.All(), e2) + }) + mut2.AddBatch(ecs.All(), e2) + mut2.RemoveBatch(ecs.All()) + query := mut2.AddBatchQ(ecs.All(), e2) + assert.Equal(t, 3, query.Count()) + query.Close() + + assert.Panics(t, func() { + mut3.RemoveBatch(ecs.All(), e2) + }) + + assert.Panics(t, func() { + _ = mut3.RemoveBatchQ(ecs.All(), e2) + }) + + w.Batch().RemoveEntities(ecs.All()) + e1 = w.NewEntity() + _ = w.NewEntity() + _ = w.NewEntity() + + mut.AddBatch(ecs.All()) + w.Batch().Add(ecs.All(), rel2ID) + mut4.RemoveBatch(ecs.All(), e1) + + w.Batch().RemoveEntities(ecs.All()) + e1 = w.NewEntity() + _ = w.NewEntity() + _ = w.NewEntity() + + query2 := mut.AddBatchQ(ecs.All()) + assert.Equal(t, 3, query2.Count()) + query2.Close() + + w.Batch().Add(ecs.All(), rel2ID) + + query3 := mut4.RemoveBatchQ(ecs.All(), e1) + assert.Equal(t, 3, query3.Count()) + query2.Close() + + query2 = mut.AddBatchQ(ecs.All()) + assert.Equal(t, 3, query2.Count()) + query2.Close() + + query3 = mut4.RemoveBatchQ(ecs.All()) + assert.Equal(t, 3, query3.Count()) + query2.Close() } func TestMap2Generated(t *testing.T) { @@ -152,6 +233,91 @@ func TestMap2Generated(t *testing.T) { mapper := NewMap[testRelationA](&w) assert.Equal(t, target, mapper.GetRelation(e)) assert.Equal(t, target, mapper.GetRelationUnchecked(e)) + + // === Batch operations ==== + + w.Batch().RemoveEntities(ecs.All()) + + mut3 := NewMap2[ + testRelationA, testStruct1, + ](&w) + mut4 := NewMap2[ + testStruct0, testStruct1, + ](&w, T[testRelationB]()) + rel2ID := ecs.ComponentID[testRelationB](&w) + + e1 := w.NewEntity() + e2 := w.NewEntity() + e3 := w.NewEntity() + assert.Panics(t, func() { + mut3.Add(e1, e2) + }) + mut2.Add(e1, e2) + + assert.Panics(t, func() { + mut3.Remove(e1, e2) + }) + + mut.Add(e3) + w.Add(e3, rel2ID) + mut4.Remove(e3, e1) + + w.Batch().RemoveEntities(ecs.All()) + + e1 = w.NewEntity() + e2 = w.NewEntity() + _ = w.NewEntity() + assert.Panics(t, func() { + mut3.AddBatch(ecs.All(), e2) + }) + assert.Panics(t, func() { + _ = mut3.AddBatchQ(ecs.All(), e2) + }) + mut2.AddBatch(ecs.All(), e2) + mut2.RemoveBatch(ecs.All()) + query := mut2.AddBatchQ(ecs.All(), e2) + assert.Equal(t, 3, query.Count()) + query.Close() + + assert.Panics(t, func() { + mut3.RemoveBatch(ecs.All(), e2) + }) + + assert.Panics(t, func() { + _ = mut3.RemoveBatchQ(ecs.All(), e2) + }) + + w.Batch().RemoveEntities(ecs.All()) + e1 = w.NewEntity() + _ = w.NewEntity() + _ = w.NewEntity() + + mut.AddBatch(ecs.All()) + w.Batch().Add(ecs.All(), rel2ID) + mut4.RemoveBatch(ecs.All(), e1) + + w.Batch().RemoveEntities(ecs.All()) + e1 = w.NewEntity() + _ = w.NewEntity() + _ = w.NewEntity() + + query2 := mut.AddBatchQ(ecs.All()) + assert.Equal(t, 3, query2.Count()) + query2.Close() + + w.Batch().Add(ecs.All(), rel2ID) + + query3 := mut4.RemoveBatchQ(ecs.All(), e1) + assert.Equal(t, 3, query3.Count()) + query2.Close() + + query2 = mut.AddBatchQ(ecs.All()) + assert.Equal(t, 3, query2.Count()) + query2.Close() + + query3 = mut4.RemoveBatchQ(ecs.All()) + assert.Equal(t, 3, query3.Count()) + query2.Close() } func TestMap3Generated(t *testing.T) { @@ -234,6 +400,91 @@ func TestMap3Generated(t *testing.T) { mapper := NewMap[testRelationA](&w) assert.Equal(t, target, mapper.GetRelation(e)) assert.Equal(t, target, mapper.GetRelationUnchecked(e)) + + // === Batch operations ==== + + w.Batch().RemoveEntities(ecs.All()) + + mut3 := NewMap3[ + testRelationA, testStruct1, testStruct2, + ](&w) + mut4 := NewMap3[ + testStruct0, testStruct1, testStruct2, + ](&w, T[testRelationB]()) + rel2ID := ecs.ComponentID[testRelationB](&w) + + e1 := w.NewEntity() + e2 := w.NewEntity() + e3 := w.NewEntity() + assert.Panics(t, func() { + mut3.Add(e1, e2) + }) + mut2.Add(e1, e2) + + assert.Panics(t, func() { + mut3.Remove(e1, e2) + }) + + mut.Add(e3) + w.Add(e3, rel2ID) + mut4.Remove(e3, e1) + + w.Batch().RemoveEntities(ecs.All()) + + e1 = w.NewEntity() + e2 = w.NewEntity() + _ = w.NewEntity() + assert.Panics(t, func() { + mut3.AddBatch(ecs.All(), e2) + }) + assert.Panics(t, func() { + _ = mut3.AddBatchQ(ecs.All(), e2) + }) + mut2.AddBatch(ecs.All(), e2) + mut2.RemoveBatch(ecs.All()) + query := mut2.AddBatchQ(ecs.All(), e2) + assert.Equal(t, 3, query.Count()) + query.Close() + + assert.Panics(t, func() { + mut3.RemoveBatch(ecs.All(), e2) + }) + + assert.Panics(t, func() { + _ = mut3.RemoveBatchQ(ecs.All(), e2) + }) + + w.Batch().RemoveEntities(ecs.All()) + e1 = w.NewEntity() + _ = w.NewEntity() + _ = w.NewEntity() + + mut.AddBatch(ecs.All()) + w.Batch().Add(ecs.All(), rel2ID) + mut4.RemoveBatch(ecs.All(), e1) + + w.Batch().RemoveEntities(ecs.All()) + e1 = w.NewEntity() + _ = w.NewEntity() + _ = w.NewEntity() + + query2 := mut.AddBatchQ(ecs.All()) + assert.Equal(t, 3, query2.Count()) + query2.Close() + + w.Batch().Add(ecs.All(), rel2ID) + + query3 := mut4.RemoveBatchQ(ecs.All(), e1) + assert.Equal(t, 3, query3.Count()) + query2.Close() + + query2 = mut.AddBatchQ(ecs.All()) + assert.Equal(t, 3, query2.Count()) + query2.Close() + + query3 = mut4.RemoveBatchQ(ecs.All()) + assert.Equal(t, 3, query3.Count()) + query2.Close() } func TestMap4Generated(t *testing.T) { @@ -319,6 +570,91 @@ func TestMap4Generated(t *testing.T) { mapper := NewMap[testRelationA](&w) assert.Equal(t, target, mapper.GetRelation(e)) assert.Equal(t, target, mapper.GetRelationUnchecked(e)) + + // === Batch operations ==== + + w.Batch().RemoveEntities(ecs.All()) + + mut3 := NewMap4[ + testRelationA, testStruct1, testStruct2, testStruct3, + ](&w) + mut4 := NewMap4[ + testStruct0, testStruct1, testStruct2, testStruct3, + ](&w, T[testRelationB]()) + rel2ID := ecs.ComponentID[testRelationB](&w) + + e1 := w.NewEntity() + e2 := w.NewEntity() + e3 := w.NewEntity() + assert.Panics(t, func() { + mut3.Add(e1, e2) + }) + mut2.Add(e1, e2) + + assert.Panics(t, func() { + mut3.Remove(e1, e2) + }) + + mut.Add(e3) + w.Add(e3, rel2ID) + mut4.Remove(e3, e1) + + w.Batch().RemoveEntities(ecs.All()) + + e1 = w.NewEntity() + e2 = w.NewEntity() + _ = w.NewEntity() + assert.Panics(t, func() { + mut3.AddBatch(ecs.All(), e2) + }) + assert.Panics(t, func() { + _ = mut3.AddBatchQ(ecs.All(), e2) + }) + mut2.AddBatch(ecs.All(), e2) + mut2.RemoveBatch(ecs.All()) + query := mut2.AddBatchQ(ecs.All(), e2) + assert.Equal(t, 3, query.Count()) + query.Close() + + assert.Panics(t, func() { + mut3.RemoveBatch(ecs.All(), e2) + }) + + assert.Panics(t, func() { + _ = mut3.RemoveBatchQ(ecs.All(), e2) + }) + + w.Batch().RemoveEntities(ecs.All()) + e1 = w.NewEntity() + _ = w.NewEntity() + _ = w.NewEntity() + + mut.AddBatch(ecs.All()) + w.Batch().Add(ecs.All(), rel2ID) + mut4.RemoveBatch(ecs.All(), e1) + + w.Batch().RemoveEntities(ecs.All()) + e1 = w.NewEntity() + _ = w.NewEntity() + _ = w.NewEntity() + + query2 := mut.AddBatchQ(ecs.All()) + assert.Equal(t, 3, query2.Count()) + query2.Close() + + w.Batch().Add(ecs.All(), rel2ID) + + query3 := mut4.RemoveBatchQ(ecs.All(), e1) + assert.Equal(t, 3, query3.Count()) + query2.Close() + + query2 = mut.AddBatchQ(ecs.All()) + assert.Equal(t, 3, query2.Count()) + query2.Close() + + query3 = mut4.RemoveBatchQ(ecs.All()) + assert.Equal(t, 3, query3.Count()) + query2.Close() } func TestMap5Generated(t *testing.T) { @@ -416,6 +752,93 @@ func TestMap5Generated(t *testing.T) { mapper := NewMap[testRelationA](&w) assert.Equal(t, target, mapper.GetRelation(e)) assert.Equal(t, target, mapper.GetRelationUnchecked(e)) + + // === Batch operations ==== + + w.Batch().RemoveEntities(ecs.All()) + + mut3 := NewMap5[ + testRelationA, testStruct1, testStruct2, testStruct3, + testStruct4, + ](&w) + mut4 := NewMap5[ + testStruct0, testStruct1, testStruct2, testStruct3, + testStruct4, + ](&w, T[testRelationB]()) + rel2ID := ecs.ComponentID[testRelationB](&w) + + e1 := w.NewEntity() + e2 := w.NewEntity() + e3 := w.NewEntity() + assert.Panics(t, func() { + mut3.Add(e1, e2) + }) + mut2.Add(e1, e2) + + assert.Panics(t, func() { + mut3.Remove(e1, e2) + }) + + mut.Add(e3) + w.Add(e3, rel2ID) + mut4.Remove(e3, e1) + + w.Batch().RemoveEntities(ecs.All()) + + e1 = w.NewEntity() + e2 = w.NewEntity() + _ = w.NewEntity() + assert.Panics(t, func() { + mut3.AddBatch(ecs.All(), e2) + }) + assert.Panics(t, func() { + _ = mut3.AddBatchQ(ecs.All(), e2) + }) + mut2.AddBatch(ecs.All(), e2) + mut2.RemoveBatch(ecs.All()) + query := mut2.AddBatchQ(ecs.All(), e2) + assert.Equal(t, 3, query.Count()) + query.Close() + + assert.Panics(t, func() { + mut3.RemoveBatch(ecs.All(), e2) + }) + + assert.Panics(t, func() { + _ = mut3.RemoveBatchQ(ecs.All(), e2) + }) + + w.Batch().RemoveEntities(ecs.All()) + e1 = w.NewEntity() + _ = w.NewEntity() + _ = w.NewEntity() + + mut.AddBatch(ecs.All()) + w.Batch().Add(ecs.All(), rel2ID) + mut4.RemoveBatch(ecs.All(), e1) + + w.Batch().RemoveEntities(ecs.All()) + e1 = w.NewEntity() + _ = w.NewEntity() + _ = w.NewEntity() + + query2 := mut.AddBatchQ(ecs.All()) + assert.Equal(t, 3, query2.Count()) + query2.Close() + + w.Batch().Add(ecs.All(), rel2ID) + + query3 := mut4.RemoveBatchQ(ecs.All(), e1) + assert.Equal(t, 3, query3.Count()) + query2.Close() + + query2 = mut.AddBatchQ(ecs.All()) + assert.Equal(t, 3, query2.Count()) + query2.Close() + + query3 = mut4.RemoveBatchQ(ecs.All()) + assert.Equal(t, 3, query3.Count()) + query2.Close() } func TestMap6Generated(t *testing.T) { @@ -513,6 +936,93 @@ func TestMap6Generated(t *testing.T) { mapper := NewMap[testRelationA](&w) assert.Equal(t, target, mapper.GetRelation(e)) assert.Equal(t, target, mapper.GetRelationUnchecked(e)) + + // === Batch operations ==== + + w.Batch().RemoveEntities(ecs.All()) + + mut3 := NewMap6[ + testRelationA, testStruct1, testStruct2, testStruct3, + testStruct4, testStruct5, + ](&w) + mut4 := NewMap6[ + testStruct0, testStruct1, testStruct2, testStruct3, + testStruct4, testStruct5, + ](&w, T[testRelationB]()) + rel2ID := ecs.ComponentID[testRelationB](&w) + + e1 := w.NewEntity() + e2 := w.NewEntity() + e3 := w.NewEntity() + assert.Panics(t, func() { + mut3.Add(e1, e2) + }) + mut2.Add(e1, e2) + + assert.Panics(t, func() { + mut3.Remove(e1, e2) + }) + + mut.Add(e3) + w.Add(e3, rel2ID) + mut4.Remove(e3, e1) + + w.Batch().RemoveEntities(ecs.All()) + + e1 = w.NewEntity() + e2 = w.NewEntity() + _ = w.NewEntity() + assert.Panics(t, func() { + mut3.AddBatch(ecs.All(), e2) + }) + assert.Panics(t, func() { + _ = mut3.AddBatchQ(ecs.All(), e2) + }) + mut2.AddBatch(ecs.All(), e2) + mut2.RemoveBatch(ecs.All()) + query := mut2.AddBatchQ(ecs.All(), e2) + assert.Equal(t, 3, query.Count()) + query.Close() + + assert.Panics(t, func() { + mut3.RemoveBatch(ecs.All(), e2) + }) + + assert.Panics(t, func() { + _ = mut3.RemoveBatchQ(ecs.All(), e2) + }) + + w.Batch().RemoveEntities(ecs.All()) + e1 = w.NewEntity() + _ = w.NewEntity() + _ = w.NewEntity() + + mut.AddBatch(ecs.All()) + w.Batch().Add(ecs.All(), rel2ID) + mut4.RemoveBatch(ecs.All(), e1) + + w.Batch().RemoveEntities(ecs.All()) + e1 = w.NewEntity() + _ = w.NewEntity() + _ = w.NewEntity() + + query2 := mut.AddBatchQ(ecs.All()) + assert.Equal(t, 3, query2.Count()) + query2.Close() + + w.Batch().Add(ecs.All(), rel2ID) + + query3 := mut4.RemoveBatchQ(ecs.All(), e1) + assert.Equal(t, 3, query3.Count()) + query2.Close() + + query2 = mut.AddBatchQ(ecs.All()) + assert.Equal(t, 3, query2.Count()) + query2.Close() + + query3 = mut4.RemoveBatchQ(ecs.All()) + assert.Equal(t, 3, query3.Count()) + query2.Close() } func TestMap7Generated(t *testing.T) { @@ -610,6 +1120,93 @@ func TestMap7Generated(t *testing.T) { mapper := NewMap[testRelationA](&w) assert.Equal(t, target, mapper.GetRelation(e)) assert.Equal(t, target, mapper.GetRelationUnchecked(e)) + + // === Batch operations ==== + + w.Batch().RemoveEntities(ecs.All()) + + mut3 := NewMap7[ + testRelationA, testStruct1, testStruct2, testStruct3, + testStruct4, testStruct5, testStruct6, + ](&w) + mut4 := NewMap7[ + testStruct0, testStruct1, testStruct2, testStruct3, + testStruct4, testStruct5, testStruct6, + ](&w, T[testRelationB]()) + rel2ID := ecs.ComponentID[testRelationB](&w) + + e1 := w.NewEntity() + e2 := w.NewEntity() + e3 := w.NewEntity() + assert.Panics(t, func() { + mut3.Add(e1, e2) + }) + mut2.Add(e1, e2) + + assert.Panics(t, func() { + mut3.Remove(e1, e2) + }) + + mut.Add(e3) + w.Add(e3, rel2ID) + mut4.Remove(e3, e1) + + w.Batch().RemoveEntities(ecs.All()) + + e1 = w.NewEntity() + e2 = w.NewEntity() + _ = w.NewEntity() + assert.Panics(t, func() { + mut3.AddBatch(ecs.All(), e2) + }) + assert.Panics(t, func() { + _ = mut3.AddBatchQ(ecs.All(), e2) + }) + mut2.AddBatch(ecs.All(), e2) + mut2.RemoveBatch(ecs.All()) + query := mut2.AddBatchQ(ecs.All(), e2) + assert.Equal(t, 3, query.Count()) + query.Close() + + assert.Panics(t, func() { + mut3.RemoveBatch(ecs.All(), e2) + }) + + assert.Panics(t, func() { + _ = mut3.RemoveBatchQ(ecs.All(), e2) + }) + + w.Batch().RemoveEntities(ecs.All()) + e1 = w.NewEntity() + _ = w.NewEntity() + _ = w.NewEntity() + + mut.AddBatch(ecs.All()) + w.Batch().Add(ecs.All(), rel2ID) + mut4.RemoveBatch(ecs.All(), e1) + + w.Batch().RemoveEntities(ecs.All()) + e1 = w.NewEntity() + _ = w.NewEntity() + _ = w.NewEntity() + + query2 := mut.AddBatchQ(ecs.All()) + assert.Equal(t, 3, query2.Count()) + query2.Close() + + w.Batch().Add(ecs.All(), rel2ID) + + query3 := mut4.RemoveBatchQ(ecs.All(), e1) + assert.Equal(t, 3, query3.Count()) + query2.Close() + + query2 = mut.AddBatchQ(ecs.All()) + assert.Equal(t, 3, query2.Count()) + query2.Close() + + query3 = mut4.RemoveBatchQ(ecs.All()) + assert.Equal(t, 3, query3.Count()) + query2.Close() } func TestMap8Generated(t *testing.T) { @@ -707,6 +1304,93 @@ func TestMap8Generated(t *testing.T) { mapper := NewMap[testRelationA](&w) assert.Equal(t, target, mapper.GetRelation(e)) assert.Equal(t, target, mapper.GetRelationUnchecked(e)) + + // === Batch operations ==== + + w.Batch().RemoveEntities(ecs.All()) + + mut3 := NewMap8[ + testRelationA, testStruct1, testStruct2, testStruct3, + testStruct4, testStruct5, testStruct6, testStruct7, + ](&w) + mut4 := NewMap8[ + testStruct0, testStruct1, testStruct2, testStruct3, + testStruct4, testStruct5, testStruct6, testStruct7, + ](&w, T[testRelationB]()) + rel2ID := ecs.ComponentID[testRelationB](&w) + + e1 := w.NewEntity() + e2 := w.NewEntity() + e3 := w.NewEntity() + assert.Panics(t, func() { + mut3.Add(e1, e2) + }) + mut2.Add(e1, e2) + + assert.Panics(t, func() { + mut3.Remove(e1, e2) + }) + + mut.Add(e3) + w.Add(e3, rel2ID) + mut4.Remove(e3, e1) + + w.Batch().RemoveEntities(ecs.All()) + + e1 = w.NewEntity() + e2 = w.NewEntity() + _ = w.NewEntity() + assert.Panics(t, func() { + mut3.AddBatch(ecs.All(), e2) + }) + assert.Panics(t, func() { + _ = mut3.AddBatchQ(ecs.All(), e2) + }) + mut2.AddBatch(ecs.All(), e2) + mut2.RemoveBatch(ecs.All()) + query := mut2.AddBatchQ(ecs.All(), e2) + assert.Equal(t, 3, query.Count()) + query.Close() + + assert.Panics(t, func() { + mut3.RemoveBatch(ecs.All(), e2) + }) + + assert.Panics(t, func() { + _ = mut3.RemoveBatchQ(ecs.All(), e2) + }) + + w.Batch().RemoveEntities(ecs.All()) + e1 = w.NewEntity() + _ = w.NewEntity() + _ = w.NewEntity() + + mut.AddBatch(ecs.All()) + w.Batch().Add(ecs.All(), rel2ID) + mut4.RemoveBatch(ecs.All(), e1) + + w.Batch().RemoveEntities(ecs.All()) + e1 = w.NewEntity() + _ = w.NewEntity() + _ = w.NewEntity() + + query2 := mut.AddBatchQ(ecs.All()) + assert.Equal(t, 3, query2.Count()) + query2.Close() + + w.Batch().Add(ecs.All(), rel2ID) + + query3 := mut4.RemoveBatchQ(ecs.All(), e1) + assert.Equal(t, 3, query3.Count()) + query2.Close() + + query2 = mut.AddBatchQ(ecs.All()) + assert.Equal(t, 3, query2.Count()) + query2.Close() + + query3 = mut4.RemoveBatchQ(ecs.All()) + assert.Equal(t, 3, query3.Count()) + query2.Close() } func TestMap9Generated(t *testing.T) { @@ -811,6 +1495,95 @@ func TestMap9Generated(t *testing.T) { mapper := NewMap[testRelationA](&w) assert.Equal(t, target, mapper.GetRelation(e)) assert.Equal(t, target, mapper.GetRelationUnchecked(e)) + + // === Batch operations ==== + + w.Batch().RemoveEntities(ecs.All()) + + mut3 := NewMap9[ + testRelationA, testStruct1, testStruct2, testStruct3, + testStruct4, testStruct5, testStruct6, testStruct7, + testStruct8, + ](&w) + mut4 := NewMap9[ + testStruct0, testStruct1, testStruct2, testStruct3, + testStruct4, testStruct5, testStruct6, testStruct7, + testStruct8, + ](&w, T[testRelationB]()) + rel2ID := ecs.ComponentID[testRelationB](&w) + + e1 := w.NewEntity() + e2 := w.NewEntity() + e3 := w.NewEntity() + assert.Panics(t, func() { + mut3.Add(e1, e2) + }) + mut2.Add(e1, e2) + + assert.Panics(t, func() { + mut3.Remove(e1, e2) + }) + + mut.Add(e3) + w.Add(e3, rel2ID) + mut4.Remove(e3, e1) + + w.Batch().RemoveEntities(ecs.All()) + + e1 = w.NewEntity() + e2 = w.NewEntity() + _ = w.NewEntity() + assert.Panics(t, func() { + mut3.AddBatch(ecs.All(), e2) + }) + assert.Panics(t, func() { + _ = mut3.AddBatchQ(ecs.All(), e2) + }) + mut2.AddBatch(ecs.All(), e2) + mut2.RemoveBatch(ecs.All()) + query := mut2.AddBatchQ(ecs.All(), e2) + assert.Equal(t, 3, query.Count()) + query.Close() + + assert.Panics(t, func() { + mut3.RemoveBatch(ecs.All(), e2) + }) + + assert.Panics(t, func() { + _ = mut3.RemoveBatchQ(ecs.All(), e2) + }) + + w.Batch().RemoveEntities(ecs.All()) + e1 = w.NewEntity() + _ = w.NewEntity() + _ = w.NewEntity() + + mut.AddBatch(ecs.All()) + w.Batch().Add(ecs.All(), rel2ID) + mut4.RemoveBatch(ecs.All(), e1) + + w.Batch().RemoveEntities(ecs.All()) + e1 = w.NewEntity() + _ = w.NewEntity() + _ = w.NewEntity() + + query2 := mut.AddBatchQ(ecs.All()) + assert.Equal(t, 3, query2.Count()) + query2.Close() + + w.Batch().Add(ecs.All(), rel2ID) + + query3 := mut4.RemoveBatchQ(ecs.All(), e1) + assert.Equal(t, 3, query3.Count()) + query2.Close() + + query2 = mut.AddBatchQ(ecs.All()) + assert.Equal(t, 3, query2.Count()) + query2.Close() + + query3 = mut4.RemoveBatchQ(ecs.All()) + assert.Equal(t, 3, query3.Count()) + query2.Close() } func TestMap10Generated(t *testing.T) { @@ -915,6 +1688,95 @@ func TestMap10Generated(t *testing.T) { mapper := NewMap[testRelationA](&w) assert.Equal(t, target, mapper.GetRelation(e)) assert.Equal(t, target, mapper.GetRelationUnchecked(e)) + + // === Batch operations ==== + + w.Batch().RemoveEntities(ecs.All()) + + mut3 := NewMap10[ + testRelationA, testStruct1, testStruct2, testStruct3, + testStruct4, testStruct5, testStruct6, testStruct7, + testStruct8, testStruct9, + ](&w) + mut4 := NewMap10[ + testStruct0, testStruct1, testStruct2, testStruct3, + testStruct4, testStruct5, testStruct6, testStruct7, + testStruct8, testStruct9, + ](&w, T[testRelationB]()) + rel2ID := ecs.ComponentID[testRelationB](&w) + + e1 := w.NewEntity() + e2 := w.NewEntity() + e3 := w.NewEntity() + assert.Panics(t, func() { + mut3.Add(e1, e2) + }) + mut2.Add(e1, e2) + + assert.Panics(t, func() { + mut3.Remove(e1, e2) + }) + + mut.Add(e3) + w.Add(e3, rel2ID) + mut4.Remove(e3, e1) + + w.Batch().RemoveEntities(ecs.All()) + + e1 = w.NewEntity() + e2 = w.NewEntity() + _ = w.NewEntity() + assert.Panics(t, func() { + mut3.AddBatch(ecs.All(), e2) + }) + assert.Panics(t, func() { + _ = mut3.AddBatchQ(ecs.All(), e2) + }) + mut2.AddBatch(ecs.All(), e2) + mut2.RemoveBatch(ecs.All()) + query := mut2.AddBatchQ(ecs.All(), e2) + assert.Equal(t, 3, query.Count()) + query.Close() + + assert.Panics(t, func() { + mut3.RemoveBatch(ecs.All(), e2) + }) + + assert.Panics(t, func() { + _ = mut3.RemoveBatchQ(ecs.All(), e2) + }) + + w.Batch().RemoveEntities(ecs.All()) + e1 = w.NewEntity() + _ = w.NewEntity() + _ = w.NewEntity() + + mut.AddBatch(ecs.All()) + w.Batch().Add(ecs.All(), rel2ID) + mut4.RemoveBatch(ecs.All(), e1) + + w.Batch().RemoveEntities(ecs.All()) + e1 = w.NewEntity() + _ = w.NewEntity() + _ = w.NewEntity() + + query2 := mut.AddBatchQ(ecs.All()) + assert.Equal(t, 3, query2.Count()) + query2.Close() + + w.Batch().Add(ecs.All(), rel2ID) + + query3 := mut4.RemoveBatchQ(ecs.All(), e1) + assert.Equal(t, 3, query3.Count()) + query2.Close() + + query2 = mut.AddBatchQ(ecs.All()) + assert.Equal(t, 3, query2.Count()) + query2.Close() + + query3 = mut4.RemoveBatchQ(ecs.All()) + assert.Equal(t, 3, query3.Count()) + query2.Close() } func TestMap11Generated(t *testing.T) { @@ -1019,6 +1881,95 @@ func TestMap11Generated(t *testing.T) { mapper := NewMap[testRelationA](&w) assert.Equal(t, target, mapper.GetRelation(e)) assert.Equal(t, target, mapper.GetRelationUnchecked(e)) + + // === Batch operations ==== + + w.Batch().RemoveEntities(ecs.All()) + + mut3 := NewMap11[ + testRelationA, testStruct1, testStruct2, testStruct3, + testStruct4, testStruct5, testStruct6, testStruct7, + testStruct8, testStruct9, testStruct10, + ](&w) + mut4 := NewMap11[ + testStruct0, testStruct1, testStruct2, testStruct3, + testStruct4, testStruct5, testStruct6, testStruct7, + testStruct8, testStruct9, testStruct10, + ](&w, T[testRelationB]()) + rel2ID := ecs.ComponentID[testRelationB](&w) + + e1 := w.NewEntity() + e2 := w.NewEntity() + e3 := w.NewEntity() + assert.Panics(t, func() { + mut3.Add(e1, e2) + }) + mut2.Add(e1, e2) + + assert.Panics(t, func() { + mut3.Remove(e1, e2) + }) + + mut.Add(e3) + w.Add(e3, rel2ID) + mut4.Remove(e3, e1) + + w.Batch().RemoveEntities(ecs.All()) + + e1 = w.NewEntity() + e2 = w.NewEntity() + _ = w.NewEntity() + assert.Panics(t, func() { + mut3.AddBatch(ecs.All(), e2) + }) + assert.Panics(t, func() { + _ = mut3.AddBatchQ(ecs.All(), e2) + }) + mut2.AddBatch(ecs.All(), e2) + mut2.RemoveBatch(ecs.All()) + query := mut2.AddBatchQ(ecs.All(), e2) + assert.Equal(t, 3, query.Count()) + query.Close() + + assert.Panics(t, func() { + mut3.RemoveBatch(ecs.All(), e2) + }) + + assert.Panics(t, func() { + _ = mut3.RemoveBatchQ(ecs.All(), e2) + }) + + w.Batch().RemoveEntities(ecs.All()) + e1 = w.NewEntity() + _ = w.NewEntity() + _ = w.NewEntity() + + mut.AddBatch(ecs.All()) + w.Batch().Add(ecs.All(), rel2ID) + mut4.RemoveBatch(ecs.All(), e1) + + w.Batch().RemoveEntities(ecs.All()) + e1 = w.NewEntity() + _ = w.NewEntity() + _ = w.NewEntity() + + query2 := mut.AddBatchQ(ecs.All()) + assert.Equal(t, 3, query2.Count()) + query2.Close() + + w.Batch().Add(ecs.All(), rel2ID) + + query3 := mut4.RemoveBatchQ(ecs.All(), e1) + assert.Equal(t, 3, query3.Count()) + query2.Close() + + query2 = mut.AddBatchQ(ecs.All()) + assert.Equal(t, 3, query2.Count()) + query2.Close() + + query3 = mut4.RemoveBatchQ(ecs.All()) + assert.Equal(t, 3, query3.Count()) + query2.Close() } func TestMap12Generated(t *testing.T) { @@ -1123,4 +2074,93 @@ func TestMap12Generated(t *testing.T) { mapper := NewMap[testRelationA](&w) assert.Equal(t, target, mapper.GetRelation(e)) assert.Equal(t, target, mapper.GetRelationUnchecked(e)) + + // === Batch operations ==== + + w.Batch().RemoveEntities(ecs.All()) + + mut3 := NewMap12[ + testRelationA, testStruct1, testStruct2, testStruct3, + testStruct4, testStruct5, testStruct6, testStruct7, + testStruct8, testStruct9, testStruct10, testStruct11, + ](&w) + mut4 := NewMap12[ + testStruct0, testStruct1, testStruct2, testStruct3, + testStruct4, testStruct5, testStruct6, testStruct7, + testStruct8, testStruct9, testStruct10, testStruct11, + ](&w, T[testRelationB]()) + rel2ID := ecs.ComponentID[testRelationB](&w) + + e1 := w.NewEntity() + e2 := w.NewEntity() + e3 := w.NewEntity() + assert.Panics(t, func() { + mut3.Add(e1, e2) + }) + mut2.Add(e1, e2) + + assert.Panics(t, func() { + mut3.Remove(e1, e2) + }) + + mut.Add(e3) + w.Add(e3, rel2ID) + mut4.Remove(e3, e1) + + w.Batch().RemoveEntities(ecs.All()) + + e1 = w.NewEntity() + e2 = w.NewEntity() + _ = w.NewEntity() + assert.Panics(t, func() { + mut3.AddBatch(ecs.All(), e2) + }) + assert.Panics(t, func() { + _ = mut3.AddBatchQ(ecs.All(), e2) + }) + mut2.AddBatch(ecs.All(), e2) + mut2.RemoveBatch(ecs.All()) + query := mut2.AddBatchQ(ecs.All(), e2) + assert.Equal(t, 3, query.Count()) + query.Close() + + assert.Panics(t, func() { + mut3.RemoveBatch(ecs.All(), e2) + }) + + assert.Panics(t, func() { + _ = mut3.RemoveBatchQ(ecs.All(), e2) + }) + + w.Batch().RemoveEntities(ecs.All()) + e1 = w.NewEntity() + _ = w.NewEntity() + _ = w.NewEntity() + + mut.AddBatch(ecs.All()) + w.Batch().Add(ecs.All(), rel2ID) + mut4.RemoveBatch(ecs.All(), e1) + + w.Batch().RemoveEntities(ecs.All()) + e1 = w.NewEntity() + _ = w.NewEntity() + _ = w.NewEntity() + + query2 := mut.AddBatchQ(ecs.All()) + assert.Equal(t, 3, query2.Count()) + query2.Close() + + w.Batch().Add(ecs.All(), rel2ID) + + query3 := mut4.RemoveBatchQ(ecs.All(), e1) + assert.Equal(t, 3, query3.Count()) + query2.Close() + + query2 = mut.AddBatchQ(ecs.All()) + assert.Equal(t, 3, query2.Count()) + query2.Close() + + query3 = mut4.RemoveBatchQ(ecs.All()) + assert.Equal(t, 3, query3.Count()) + query2.Close() } From a5127eca281f314e5857acffe6ef04ba872f20d9 Mon Sep 17 00:00:00 2001 From: mlange-42 Date: Thu, 18 Jan 2024 22:26:49 +0100 Subject: [PATCH 11/12] move batch exchange with target from Batch to Relations, tweak docs --- CHANGELOG.md | 3 +- ecs/batch.go | 33 +---- ecs/batch_test.go | 54 ------- ecs/relations.go | 31 ++++ ecs/relations_test.go | 49 +++++++ generic/exchange.go | 12 +- generic/generate/map.go.txt | 21 +-- generic/map_generated.go | 277 +++++++++++++++++++----------------- 8 files changed, 244 insertions(+), 236 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c4f6121b..db2f8b3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,12 +29,11 @@ This change was necessary to get the same performance as before, despite the mor * Adds functions `ComponentIDs(*World)` and `ResourceIDs(*World)` to get all registered IDs (#329) * Adds methods `Mask.And`, `Mask.Or` and `Mask.Xor` (#335) * Adds build tag `tiny` to restrict to 64 components for an extra bit of performance (#338) -* Adds methods `Relations.Exchange()`, `Batch.ExchangeRelation()`, `Batch.ExchangeRelationQ()` for exchange with relation target (#342) +* Adds methods `Relations.Exchange()`, `Relations.ExchangeBatch()`, `Relations.ExchangeBatchQ()` for exchange with relation target (#342) * Generic API adds `Exchange.WithRelation()` and optional target argument for operations with relation target (#342) * Generic API adds `MapX.AddBatch()`, `MapX.AddBatchQ()`, `MapX.RemoveBatch()`and `MapX.RemoveBatchQ()` (#342) * Generic API adds optional relation target argument to most `MapX` methods (#342) * Generic API adds `FilterX.Filter()` to get an `ecs.Filter` from a generic one (#342) -* Generic API adds `MapX.AddBatch()`, `MapX.AddBatchQ()`, `MapX.RemoveBatch()` and `MapX.RemoveBatchQ()` (#342) ### Performance diff --git a/ecs/batch.go b/ecs/batch.go index 0b477561..9953cb19 100644 --- a/ecs/batch.go +++ b/ecs/batch.go @@ -94,6 +94,7 @@ func (b *Batch) SetRelationQ(filter Filter, comp ID, target Entity) Query { // - when called on a locked world. Do not use during [Query] iteration! // // See also [Batch.ExchangeQ] and [World.Exchange]. +// For batch-exchange with a relation target, see [Relations.ExchangeBatch]. func (b *Batch) Exchange(filter Filter, add []ID, rem []ID) { b.world.exchangeBatch(filter, add, rem, ID{}, false, Entity{}) } @@ -109,41 +110,11 @@ func (b *Batch) Exchange(filter Filter, add []ID, rem []ID) { // - when called on a locked world. Do not use during [Query] iteration! // // See also [Batch.Exchange] and [World.Exchange]. +// For batch-exchange with a relation target, see [Relations.ExchangeBatchQ]. func (b *Batch) ExchangeQ(filter Filter, add []ID, rem []ID) Query { return b.world.exchangeBatchQuery(filter, add, rem, ID{}, false, Entity{}) } -// ExchangeRelation exchanges components for many entities, matching a filter. -// In contrast to [Batch.Exchange], it allows to also set a relation target. -// -// Panics: -// - when called with components that can't be added or removed because they are already present/not present, respectively. -// - when called for a missing relation component. -// - when called for a component that is not a relation. -// - when called without any components to add or remove. Use [Batch.SetRelation] instead. -// - when called on a locked world. Do not use during [Query] iteration! -// -// See also [Batch.Exchange], [Batch.ExchangeQ], [Batch.ExchangeRelationQ] and [World.Exchange]. -func (b *Batch) ExchangeRelation(filter Filter, add []ID, rem []ID, relation ID, target Entity) { - b.world.exchangeBatch(filter, add, rem, relation, true, target) -} - -// ExchangeRelationQ exchanges components for many entities, matching a filter. -// It returns a query over the affected entities. -// In contrast to [Batch.ExchangeQ], it allows to also set a relation target. -// -// Panics: -// - when called with components that can't be added or removed because they are already present/not present, respectively. -// - when called for a missing relation component. -// - when called for a component that is not a relation. -// - when called without any components to add or remove. Use [Batch.SetRelationQ] instead. -// - when called on a locked world. Do not use during [Query] iteration! -// -// See also [Batch.Exchange], [Batch.ExchangeQ], [Batch.ExchangeRelation] and [World.Exchange]. -func (b *Batch) ExchangeRelationQ(filter Filter, add []ID, rem []ID, relation ID, target Entity) Query { - return b.world.exchangeBatchQuery(filter, add, rem, relation, true, target) -} - // RemoveEntities removes and recycles all entities matching a filter. // // Returns the number of removed entities. diff --git a/ecs/batch_test.go b/ecs/batch_test.go index 833b68e5..e85b45a6 100644 --- a/ecs/batch_test.go +++ b/ecs/batch_test.go @@ -1,55 +1 @@ package ecs_test - -import ( - "testing" - - "github.com/mlange-42/arche/ecs" - "github.com/stretchr/testify/assert" -) - -func TestBatchExchangeRelation(t *testing.T) { - w := ecs.NewWorld() - posID := ecs.ComponentID[Position](&w) - velID := ecs.ComponentID[Velocity](&w) - childID := ecs.ComponentID[ChildOf](&w) - - parent1 := w.NewEntity(posID) - parent2 := w.NewEntity(posID) - - builder := ecs.NewBuilder(&w, posID, childID).WithRelation(childID) - builder.NewBatch(10, parent1) - - relFilter := ecs.NewRelationFilter(ecs.All(posID, childID), parent1) - query := w.Query(&relFilter) - assert.Equal(t, 10, query.Count()) - query.Close() - - filter := ecs.All(posID, childID) - assert.Panics(t, func() { - w.Batch().ExchangeRelation(filter, nil, []ecs.ID{posID}, velID, parent2) - }) - assert.Panics(t, func() { - w.Batch().ExchangeRelation(filter, []ecs.ID{velID}, []ecs.ID{posID}, velID, parent2) - }) - assert.Panics(t, func() { - w.Batch().ExchangeRelation(filter, nil, nil, childID, parent2) - }) - assert.Panics(t, func() { - _ = w.Batch().ExchangeRelationQ(filter, nil, nil, childID, parent2) - }) - - w.Batch().ExchangeRelation(filter, []ecs.ID{velID}, nil, childID, parent2) - - relFilter = ecs.NewRelationFilter(ecs.All(posID, velID, childID), parent2) - query = w.Query(&relFilter) - assert.Equal(t, 10, query.Count()) - query.Close() - - w.Batch().ExchangeRelation(filter, nil, []ecs.ID{velID}, childID, parent1) - - relFilter = ecs.NewRelationFilter(ecs.All(posID, childID), parent1) - query = w.Query(&relFilter) - assert.Equal(t, 10, query.Count()) - query.Close() - -} diff --git a/ecs/relations.go b/ecs/relations.go index c5d1d2d0..11c62e4f 100644 --- a/ecs/relations.go +++ b/ecs/relations.go @@ -82,3 +82,34 @@ func (r *Relations) SetBatchQ(filter Filter, comp ID, target Entity) Query { func (r *Relations) Exchange(entity Entity, add []ID, rem []ID, relation ID, target Entity) { r.world.exchange(entity, add, rem, relation, true, target) } + +// ExchangeBatch exchanges components for many entities, matching a filter. +// In contrast to [Batch.Exchange], it allows to also set a relation target. +// +// Panics: +// - when called with components that can't be added or removed because they are already present/not present, respectively. +// - when called for a missing relation component. +// - when called for a component that is not a relation. +// - when called without any components to add or remove. Use [Batch.SetRelation] instead. +// - when called on a locked world. Do not use during [Query] iteration! +// +// See also [Batch.Exchange], [Batch.ExchangeQ], [Relations.ExchangeBatch] and [World.Exchange]. +func (r *Relations) ExchangeBatch(filter Filter, add []ID, rem []ID, relation ID, target Entity) { + r.world.exchangeBatch(filter, add, rem, relation, true, target) +} + +// ExchangeBatchQ exchanges components for many entities, matching a filter. +// It returns a query over the affected entities. +// In contrast to [Batch.ExchangeQ], it allows to also set a relation target. +// +// Panics: +// - when called with components that can't be added or removed because they are already present/not present, respectively. +// - when called for a missing relation component. +// - when called for a component that is not a relation. +// - when called without any components to add or remove. Use [Batch.SetRelationQ] instead. +// - when called on a locked world. Do not use during [Query] iteration! +// +// See also [Batch.Exchange], [Batch.ExchangeQ], [Relations.ExchangeBatch] and [World.Exchange]. +func (r *Relations) ExchangeBatchQ(filter Filter, add []ID, rem []ID, relation ID, target Entity) Query { + return r.world.exchangeBatchQuery(filter, add, rem, relation, true, target) +} diff --git a/ecs/relations_test.go b/ecs/relations_test.go index 4461e9f4..44756cea 100644 --- a/ecs/relations_test.go +++ b/ecs/relations_test.go @@ -2,10 +2,59 @@ package ecs_test import ( "fmt" + "testing" "github.com/mlange-42/arche/ecs" + "github.com/stretchr/testify/assert" ) +func TestRelationsExchange(t *testing.T) { + w := ecs.NewWorld() + posID := ecs.ComponentID[Position](&w) + velID := ecs.ComponentID[Velocity](&w) + childID := ecs.ComponentID[ChildOf](&w) + + parent1 := w.NewEntity(posID) + parent2 := w.NewEntity(posID) + + builder := ecs.NewBuilder(&w, posID, childID).WithRelation(childID) + builder.NewBatch(10, parent1) + + relFilter := ecs.NewRelationFilter(ecs.All(posID, childID), parent1) + query := w.Query(&relFilter) + assert.Equal(t, 10, query.Count()) + query.Close() + + filter := ecs.All(posID, childID) + assert.Panics(t, func() { + w.Relations().ExchangeBatch(filter, nil, []ecs.ID{posID}, velID, parent2) + }) + assert.Panics(t, func() { + w.Relations().ExchangeBatch(filter, []ecs.ID{velID}, []ecs.ID{posID}, velID, parent2) + }) + assert.Panics(t, func() { + w.Relations().ExchangeBatch(filter, nil, nil, childID, parent2) + }) + assert.Panics(t, func() { + _ = w.Relations().ExchangeBatchQ(filter, nil, nil, childID, parent2) + }) + + w.Relations().ExchangeBatch(filter, []ecs.ID{velID}, nil, childID, parent2) + + relFilter = ecs.NewRelationFilter(ecs.All(posID, velID, childID), parent2) + query = w.Query(&relFilter) + assert.Equal(t, 10, query.Count()) + query.Close() + + w.Relations().ExchangeBatch(filter, nil, []ecs.ID{velID}, childID, parent1) + + relFilter = ecs.NewRelationFilter(ecs.All(posID, childID), parent1) + query = w.Query(&relFilter) + assert.Equal(t, 10, query.Count()) + query.Close() + +} + func ExampleRelations() { world := ecs.NewWorld() diff --git a/generic/exchange.go b/generic/exchange.go index 4a294a65..1828b6f0 100644 --- a/generic/exchange.go +++ b/generic/exchange.go @@ -64,7 +64,7 @@ func (m *Exchange) Removes(remove ...Comp) *Exchange { // NewEntity creates a new [ecs.Entity] with the components set via [Exchange.Adds]. // -// The optional argument can be used to set the target [Entity] for the Exchange's [Relation]. +// The optional argument can be used to set the target [ecs.Entity] for the Exchange's [ecs.Relation]. // See [Exchange.WithRelation]. // // See also [ecs.World.NewEntity]. @@ -80,7 +80,7 @@ func (m *Exchange) NewEntity(target ...ecs.Entity) ecs.Entity { // Add the components set via [Exchange.Adds] to the given entity. // -// The optional argument can be used to set the target [Entity] for the Exchange's [Relation]. +// The optional argument can be used to set the target [ecs.Entity] for the Exchange's [ecs.Relation]. // See [Exchange.WithRelation]. // // See also [ecs.World.Add]. @@ -97,7 +97,7 @@ func (m *Exchange) Add(entity ecs.Entity, target ...ecs.Entity) { // Remove the components set via [Exchange.Removes] from the given entity. // -// The optional argument can be used to set the target [Entity] for the Exchange's [Relation]. +// The optional argument can be used to set the target [ecs.Entity] for the Exchange's [ecs.Relation]. // See [Exchange.WithRelation]. // // See also [ecs.World.Remove]. @@ -117,10 +117,10 @@ func (m *Exchange) Remove(entity ecs.Entity, target ...ecs.Entity) { // Removes the components set via [Exchange.Removes]. // Adds the components set via [Exchange.Adds]. // -// When a [Relation] component is removed and another one is added, +// When a [ecs.Relation] component is removed and another one is added, // the target entity of the relation is set to zero. // -// The optional argument can be used to set the target [Entity] for the Exchange's [Relation]. +// The optional argument can be used to set the target [ecs.Entity] for the Exchange's [ecs.Relation]. // See [Exchange.WithRelation]. // // See also [ecs.World.Exchange]. @@ -152,7 +152,7 @@ func (m *Exchange) ExchangeBatch(filter ecs.Filter, target ...ecs.Entity) { if !m.hasRelation { panic("can't set target entity: Exchange has no relation") } - m.world.Batch().ExchangeRelation(filter, m.add, m.remove, m.relationID, target[0]) + m.world.Relations().ExchangeBatch(filter, m.add, m.remove, m.relationID, target[0]) } else { m.world.Batch().Exchange(filter, m.add, m.remove) } diff --git a/generic/generate/map.go.txt b/generic/generate/map.go.txt index 7bec7a11..5c1a3eb5 100644 --- a/generic/generate/map.go.txt +++ b/generic/generate/map.go.txt @@ -127,13 +127,13 @@ func (m *Map{{ .Index }}{{ .Types }}) Add(entity ecs.Entity, target ...ecs.Entit // // The optional argument can be used to set the target [ecs.Entity] for the Map{{ .Index }}'s [ecs.Relation]. // -// See also [Batch.Add]. +// See also [ecs.Batch.Add]. func (m *Map{{ .Index }}{{ .Types }}) AddBatch(filter ecs.Filter, target ...ecs.Entity) { if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map{{ .Index }} has no relation") } - m.world.Batch().ExchangeRelation(filter, m.ids, nil, m.relation, target[0]) + m.world.Relations().ExchangeBatch(filter, m.ids, nil, m.relation, target[0]) } else { m.world.Batch().Exchange(filter, m.ids, nil) } @@ -144,14 +144,14 @@ func (m *Map{{ .Index }}{{ .Types }}) AddBatch(filter ecs.Filter, target ...ecs. // // The optional argument can be used to set the target [ecs.Entity] for the Map{{ .Index }}'s [ecs.Relation]. // -// See also [Batch.AddQ]. +// See also [ecs.Batch.AddQ]. func (m *Map{{ .Index }}{{ .Types }}) AddBatchQ(filter ecs.Filter, target ...ecs.Entity) Query{{ .Index }}{{ .Types }} { var query ecs.Query if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map{{ .Index }} has no relation") } - query = m.world.Batch().ExchangeRelationQ(filter, m.ids, nil, m.relation, target[0]) + query = m.world.Relations().ExchangeBatchQ(filter, m.ids, nil, m.relation, target[0]) } else { query = m.world.Batch().ExchangeQ(filter, m.ids, nil) } @@ -191,30 +191,31 @@ func (m *Map{{ .Index }}{{ .Types }}) Remove(entity ecs.Entity, target ...ecs.En // // The optional argument can be used to set the target [ecs.Entity] for the Map{{ .Index }}'s [ecs.Relation]. // -// See also [Batch.Remove]. +// See also [ecs.Batch.Remove]. func (m *Map{{ .Index }}{{ .Types }}) RemoveBatch(filter ecs.Filter, target ...ecs.Entity) { if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map{{ .Index }} has no relation") } - m.world.Batch().ExchangeRelation(filter, nil, m.ids, m.relation, target[0]) + m.world.Relations().ExchangeBatch(filter, nil, m.ids, m.relation, target[0]) } else { m.world.Batch().Exchange(filter, nil, m.ids) } } -// RemoveBatchQ adds the Map{{ .Index }}'s components from multiple entities and returns a query over them. +// RemoveBatchQ adds the Map{{ .Index }}'s components from multiple entities and returns a query over them, +// with no components. // // The optional argument can be used to set the target [ecs.Entity] for the Map{{ .Index }}'s [ecs.Relation]. // -// See also [Batch.RemoveQ]. +// See also [ecs.Batch.RemoveQ]. func (m *Map{{ .Index }}{{ .Types }}) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query0 { var query ecs.Query if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map{{ .Index }} has no relation") } - query = m.world.Batch().ExchangeRelationQ(filter, nil, m.ids, m.relation, target[0]) + query = m.world.Relations().ExchangeBatchQ(filter, nil, m.ids, m.relation, target[0]) } else { query = m.world.Batch().ExchangeQ(filter, nil, m.ids) } @@ -230,7 +231,7 @@ func (m *Map{{ .Index }}{{ .Types }}) RemoveBatchQ(filter ecs.Filter, target ... // // Returns the number of removed entities. // -// See also [Batch.RemoveEntities]. +// See also [ecs.Batch.RemoveEntities]. func (m *Map{{ .Index }}{{ .Types }}) RemoveEntities(exclusive bool) int { if exclusive { filter := m.mask.Exclusive() diff --git a/generic/map_generated.go b/generic/map_generated.go index 36b8f6dc..a02369fc 100644 --- a/generic/map_generated.go +++ b/generic/map_generated.go @@ -133,13 +133,13 @@ func (m *Map1[A]) Add(entity ecs.Entity, target ...ecs.Entity) { // // The optional argument can be used to set the target [ecs.Entity] for the Map1's [ecs.Relation]. // -// See also [Batch.Add]. +// See also [ecs.Batch.Add]. func (m *Map1[A]) AddBatch(filter ecs.Filter, target ...ecs.Entity) { if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map1 has no relation") } - m.world.Batch().ExchangeRelation(filter, m.ids, nil, m.relation, target[0]) + m.world.Relations().ExchangeBatch(filter, m.ids, nil, m.relation, target[0]) } else { m.world.Batch().Exchange(filter, m.ids, nil) } @@ -150,14 +150,14 @@ func (m *Map1[A]) AddBatch(filter ecs.Filter, target ...ecs.Entity) { // // The optional argument can be used to set the target [ecs.Entity] for the Map1's [ecs.Relation]. // -// See also [Batch.AddQ]. +// See also [ecs.Batch.AddQ]. func (m *Map1[A]) AddBatchQ(filter ecs.Filter, target ...ecs.Entity) Query1[A] { var query ecs.Query if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map1 has no relation") } - query = m.world.Batch().ExchangeRelationQ(filter, m.ids, nil, m.relation, target[0]) + query = m.world.Relations().ExchangeBatchQ(filter, m.ids, nil, m.relation, target[0]) } else { query = m.world.Batch().ExchangeQ(filter, m.ids, nil) } @@ -192,35 +192,35 @@ func (m *Map1[A]) Remove(entity ecs.Entity, target ...ecs.Entity) { } } -// RemoveBatch removes the Map1's components to multiple entities. +// RemoveBatch removes the Map1's components from multiple entities. // // The optional argument can be used to set the target [ecs.Entity] for the Map1's [ecs.Relation]. // -// See also [Batch.Remove]. +// See also [ecs.Batch.Remove]. func (m *Map1[A]) RemoveBatch(filter ecs.Filter, target ...ecs.Entity) { if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map1 has no relation") } - m.world.Batch().ExchangeRelation(filter, nil, m.ids, m.relation, target[0]) + m.world.Relations().ExchangeBatch(filter, nil, m.ids, m.relation, target[0]) } else { m.world.Batch().Exchange(filter, nil, m.ids) } } -// RemoveBatchQ adds the Map1's components to multiple entities and returns a query over them, -// without any queries components. +// RemoveBatchQ adds the Map1's components from multiple entities and returns a query over them, +// with no components. // // The optional argument can be used to set the target [ecs.Entity] for the Map1's [ecs.Relation]. // -// See also [Batch.RemoveQ]. +// See also [ecs.Batch.RemoveQ]. func (m *Map1[A]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query0 { var query ecs.Query if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map1 has no relation") } - query = m.world.Batch().ExchangeRelationQ(filter, nil, m.ids, m.relation, target[0]) + query = m.world.Relations().ExchangeBatchQ(filter, nil, m.ids, m.relation, target[0]) } else { query = m.world.Batch().ExchangeQ(filter, nil, m.ids) } @@ -236,7 +236,7 @@ func (m *Map1[A]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query0 { // // Returns the number of removed entities. // -// See also [Batch.RemoveEntities]. +// See also [ecs.Batch.RemoveEntities]. func (m *Map1[A]) RemoveEntities(exclusive bool) int { if exclusive { filter := m.mask.Exclusive() @@ -381,13 +381,13 @@ func (m *Map2[A, B]) Add(entity ecs.Entity, target ...ecs.Entity) { // // The optional argument can be used to set the target [ecs.Entity] for the Map2's [ecs.Relation]. // -// See also [Batch.Add]. +// See also [ecs.Batch.Add]. func (m *Map2[A, B]) AddBatch(filter ecs.Filter, target ...ecs.Entity) { if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map2 has no relation") } - m.world.Batch().ExchangeRelation(filter, m.ids, nil, m.relation, target[0]) + m.world.Relations().ExchangeBatch(filter, m.ids, nil, m.relation, target[0]) } else { m.world.Batch().Exchange(filter, m.ids, nil) } @@ -398,14 +398,14 @@ func (m *Map2[A, B]) AddBatch(filter ecs.Filter, target ...ecs.Entity) { // // The optional argument can be used to set the target [ecs.Entity] for the Map2's [ecs.Relation]. // -// See also [Batch.AddQ]. +// See also [ecs.Batch.AddQ]. func (m *Map2[A, B]) AddBatchQ(filter ecs.Filter, target ...ecs.Entity) Query2[A, B] { var query ecs.Query if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map2 has no relation") } - query = m.world.Batch().ExchangeRelationQ(filter, m.ids, nil, m.relation, target[0]) + query = m.world.Relations().ExchangeBatchQ(filter, m.ids, nil, m.relation, target[0]) } else { query = m.world.Batch().ExchangeQ(filter, m.ids, nil) } @@ -442,34 +442,35 @@ func (m *Map2[A, B]) Remove(entity ecs.Entity, target ...ecs.Entity) { } } -// RemoveBatch removes the Map2's components to multiple entities. +// RemoveBatch removes the Map2's components from multiple entities. // // The optional argument can be used to set the target [ecs.Entity] for the Map2's [ecs.Relation]. // -// See also [Batch.Remove]. +// See also [ecs.Batch.Remove]. func (m *Map2[A, B]) RemoveBatch(filter ecs.Filter, target ...ecs.Entity) { if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map2 has no relation") } - m.world.Batch().ExchangeRelation(filter, nil, m.ids, m.relation, target[0]) + m.world.Relations().ExchangeBatch(filter, nil, m.ids, m.relation, target[0]) } else { m.world.Batch().Exchange(filter, nil, m.ids) } } -// RemoveBatchQ adds the Map2's components to multiple entities and returns a query over them. +// RemoveBatchQ adds the Map2's components from multiple entities and returns a query over them, +// with no components. // // The optional argument can be used to set the target [ecs.Entity] for the Map2's [ecs.Relation]. // -// See also [Batch.RemoveQ]. +// See also [ecs.Batch.RemoveQ]. func (m *Map2[A, B]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query0 { var query ecs.Query if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map2 has no relation") } - query = m.world.Batch().ExchangeRelationQ(filter, nil, m.ids, m.relation, target[0]) + query = m.world.Relations().ExchangeBatchQ(filter, nil, m.ids, m.relation, target[0]) } else { query = m.world.Batch().ExchangeQ(filter, nil, m.ids) } @@ -485,7 +486,7 @@ func (m *Map2[A, B]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query // // Returns the number of removed entities. // -// See also [Batch.RemoveEntities]. +// See also [ecs.Batch.RemoveEntities]. func (m *Map2[A, B]) RemoveEntities(exclusive bool) int { if exclusive { filter := m.mask.Exclusive() @@ -637,13 +638,13 @@ func (m *Map3[A, B, C]) Add(entity ecs.Entity, target ...ecs.Entity) { // // The optional argument can be used to set the target [ecs.Entity] for the Map3's [ecs.Relation]. // -// See also [Batch.Add]. +// See also [ecs.Batch.Add]. func (m *Map3[A, B, C]) AddBatch(filter ecs.Filter, target ...ecs.Entity) { if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map3 has no relation") } - m.world.Batch().ExchangeRelation(filter, m.ids, nil, m.relation, target[0]) + m.world.Relations().ExchangeBatch(filter, m.ids, nil, m.relation, target[0]) } else { m.world.Batch().Exchange(filter, m.ids, nil) } @@ -654,14 +655,14 @@ func (m *Map3[A, B, C]) AddBatch(filter ecs.Filter, target ...ecs.Entity) { // // The optional argument can be used to set the target [ecs.Entity] for the Map3's [ecs.Relation]. // -// See also [Batch.AddQ]. +// See also [ecs.Batch.AddQ]. func (m *Map3[A, B, C]) AddBatchQ(filter ecs.Filter, target ...ecs.Entity) Query3[A, B, C] { var query ecs.Query if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map3 has no relation") } - query = m.world.Batch().ExchangeRelationQ(filter, m.ids, nil, m.relation, target[0]) + query = m.world.Relations().ExchangeBatchQ(filter, m.ids, nil, m.relation, target[0]) } else { query = m.world.Batch().ExchangeQ(filter, m.ids, nil) } @@ -700,34 +701,35 @@ func (m *Map3[A, B, C]) Remove(entity ecs.Entity, target ...ecs.Entity) { } } -// RemoveBatch removes the Map3's components to multiple entities. +// RemoveBatch removes the Map3's components from multiple entities. // // The optional argument can be used to set the target [ecs.Entity] for the Map3's [ecs.Relation]. // -// See also [Batch.Remove]. +// See also [ecs.Batch.Remove]. func (m *Map3[A, B, C]) RemoveBatch(filter ecs.Filter, target ...ecs.Entity) { if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map3 has no relation") } - m.world.Batch().ExchangeRelation(filter, nil, m.ids, m.relation, target[0]) + m.world.Relations().ExchangeBatch(filter, nil, m.ids, m.relation, target[0]) } else { m.world.Batch().Exchange(filter, nil, m.ids) } } -// RemoveBatchQ adds the Map3's components to multiple entities and returns a query over them. +// RemoveBatchQ adds the Map3's components from multiple entities and returns a query over them, +// with no components. // // The optional argument can be used to set the target [ecs.Entity] for the Map3's [ecs.Relation]. // -// See also [Batch.RemoveQ]. +// See also [ecs.Batch.RemoveQ]. func (m *Map3[A, B, C]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query0 { var query ecs.Query if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map3 has no relation") } - query = m.world.Batch().ExchangeRelationQ(filter, nil, m.ids, m.relation, target[0]) + query = m.world.Relations().ExchangeBatchQ(filter, nil, m.ids, m.relation, target[0]) } else { query = m.world.Batch().ExchangeQ(filter, nil, m.ids) } @@ -743,7 +745,7 @@ func (m *Map3[A, B, C]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Qu // // Returns the number of removed entities. // -// See also [Batch.RemoveEntities]. +// See also [ecs.Batch.RemoveEntities]. func (m *Map3[A, B, C]) RemoveEntities(exclusive bool) int { if exclusive { filter := m.mask.Exclusive() @@ -902,13 +904,13 @@ func (m *Map4[A, B, C, D]) Add(entity ecs.Entity, target ...ecs.Entity) { // // The optional argument can be used to set the target [ecs.Entity] for the Map4's [ecs.Relation]. // -// See also [Batch.Add]. +// See also [ecs.Batch.Add]. func (m *Map4[A, B, C, D]) AddBatch(filter ecs.Filter, target ...ecs.Entity) { if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map4 has no relation") } - m.world.Batch().ExchangeRelation(filter, m.ids, nil, m.relation, target[0]) + m.world.Relations().ExchangeBatch(filter, m.ids, nil, m.relation, target[0]) } else { m.world.Batch().Exchange(filter, m.ids, nil) } @@ -919,14 +921,14 @@ func (m *Map4[A, B, C, D]) AddBatch(filter ecs.Filter, target ...ecs.Entity) { // // The optional argument can be used to set the target [ecs.Entity] for the Map4's [ecs.Relation]. // -// See also [Batch.AddQ]. +// See also [ecs.Batch.AddQ]. func (m *Map4[A, B, C, D]) AddBatchQ(filter ecs.Filter, target ...ecs.Entity) Query4[A, B, C, D] { var query ecs.Query if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map4 has no relation") } - query = m.world.Batch().ExchangeRelationQ(filter, m.ids, nil, m.relation, target[0]) + query = m.world.Relations().ExchangeBatchQ(filter, m.ids, nil, m.relation, target[0]) } else { query = m.world.Batch().ExchangeQ(filter, m.ids, nil) } @@ -967,34 +969,35 @@ func (m *Map4[A, B, C, D]) Remove(entity ecs.Entity, target ...ecs.Entity) { } } -// RemoveBatch removes the Map4's components to multiple entities. +// RemoveBatch removes the Map4's components from multiple entities. // // The optional argument can be used to set the target [ecs.Entity] for the Map4's [ecs.Relation]. // -// See also [Batch.Remove]. +// See also [ecs.Batch.Remove]. func (m *Map4[A, B, C, D]) RemoveBatch(filter ecs.Filter, target ...ecs.Entity) { if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map4 has no relation") } - m.world.Batch().ExchangeRelation(filter, nil, m.ids, m.relation, target[0]) + m.world.Relations().ExchangeBatch(filter, nil, m.ids, m.relation, target[0]) } else { m.world.Batch().Exchange(filter, nil, m.ids) } } -// RemoveBatchQ adds the Map4's components to multiple entities and returns a query over them. +// RemoveBatchQ adds the Map4's components from multiple entities and returns a query over them, +// with no components. // // The optional argument can be used to set the target [ecs.Entity] for the Map4's [ecs.Relation]. // -// See also [Batch.RemoveQ]. +// See also [ecs.Batch.RemoveQ]. func (m *Map4[A, B, C, D]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query0 { var query ecs.Query if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map4 has no relation") } - query = m.world.Batch().ExchangeRelationQ(filter, nil, m.ids, m.relation, target[0]) + query = m.world.Relations().ExchangeBatchQ(filter, nil, m.ids, m.relation, target[0]) } else { query = m.world.Batch().ExchangeQ(filter, nil, m.ids) } @@ -1010,7 +1013,7 @@ func (m *Map4[A, B, C, D]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) // // Returns the number of removed entities. // -// See also [Batch.RemoveEntities]. +// See also [ecs.Batch.RemoveEntities]. func (m *Map4[A, B, C, D]) RemoveEntities(exclusive bool) int { if exclusive { filter := m.mask.Exclusive() @@ -1176,13 +1179,13 @@ func (m *Map5[A, B, C, D, E]) Add(entity ecs.Entity, target ...ecs.Entity) { // // The optional argument can be used to set the target [ecs.Entity] for the Map5's [ecs.Relation]. // -// See also [Batch.Add]. +// See also [ecs.Batch.Add]. func (m *Map5[A, B, C, D, E]) AddBatch(filter ecs.Filter, target ...ecs.Entity) { if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map5 has no relation") } - m.world.Batch().ExchangeRelation(filter, m.ids, nil, m.relation, target[0]) + m.world.Relations().ExchangeBatch(filter, m.ids, nil, m.relation, target[0]) } else { m.world.Batch().Exchange(filter, m.ids, nil) } @@ -1193,14 +1196,14 @@ func (m *Map5[A, B, C, D, E]) AddBatch(filter ecs.Filter, target ...ecs.Entity) // // The optional argument can be used to set the target [ecs.Entity] for the Map5's [ecs.Relation]. // -// See also [Batch.AddQ]. +// See also [ecs.Batch.AddQ]. func (m *Map5[A, B, C, D, E]) AddBatchQ(filter ecs.Filter, target ...ecs.Entity) Query5[A, B, C, D, E] { var query ecs.Query if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map5 has no relation") } - query = m.world.Batch().ExchangeRelationQ(filter, m.ids, nil, m.relation, target[0]) + query = m.world.Relations().ExchangeBatchQ(filter, m.ids, nil, m.relation, target[0]) } else { query = m.world.Batch().ExchangeQ(filter, m.ids, nil) } @@ -1243,34 +1246,35 @@ func (m *Map5[A, B, C, D, E]) Remove(entity ecs.Entity, target ...ecs.Entity) { } } -// RemoveBatch removes the Map5's components to multiple entities. +// RemoveBatch removes the Map5's components from multiple entities. // // The optional argument can be used to set the target [ecs.Entity] for the Map5's [ecs.Relation]. // -// See also [Batch.Remove]. +// See also [ecs.Batch.Remove]. func (m *Map5[A, B, C, D, E]) RemoveBatch(filter ecs.Filter, target ...ecs.Entity) { if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map5 has no relation") } - m.world.Batch().ExchangeRelation(filter, nil, m.ids, m.relation, target[0]) + m.world.Relations().ExchangeBatch(filter, nil, m.ids, m.relation, target[0]) } else { m.world.Batch().Exchange(filter, nil, m.ids) } } -// RemoveBatchQ adds the Map5's components to multiple entities and returns a query over them. +// RemoveBatchQ adds the Map5's components from multiple entities and returns a query over them, +// with no components. // // The optional argument can be used to set the target [ecs.Entity] for the Map5's [ecs.Relation]. // -// See also [Batch.RemoveQ]. +// See also [ecs.Batch.RemoveQ]. func (m *Map5[A, B, C, D, E]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query0 { var query ecs.Query if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map5 has no relation") } - query = m.world.Batch().ExchangeRelationQ(filter, nil, m.ids, m.relation, target[0]) + query = m.world.Relations().ExchangeBatchQ(filter, nil, m.ids, m.relation, target[0]) } else { query = m.world.Batch().ExchangeQ(filter, nil, m.ids) } @@ -1286,7 +1290,7 @@ func (m *Map5[A, B, C, D, E]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Enti // // Returns the number of removed entities. // -// See also [Batch.RemoveEntities]. +// See also [ecs.Batch.RemoveEntities]. func (m *Map5[A, B, C, D, E]) RemoveEntities(exclusive bool) int { if exclusive { filter := m.mask.Exclusive() @@ -1459,13 +1463,13 @@ func (m *Map6[A, B, C, D, E, F]) Add(entity ecs.Entity, target ...ecs.Entity) { // // The optional argument can be used to set the target [ecs.Entity] for the Map6's [ecs.Relation]. // -// See also [Batch.Add]. +// See also [ecs.Batch.Add]. func (m *Map6[A, B, C, D, E, F]) AddBatch(filter ecs.Filter, target ...ecs.Entity) { if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map6 has no relation") } - m.world.Batch().ExchangeRelation(filter, m.ids, nil, m.relation, target[0]) + m.world.Relations().ExchangeBatch(filter, m.ids, nil, m.relation, target[0]) } else { m.world.Batch().Exchange(filter, m.ids, nil) } @@ -1476,14 +1480,14 @@ func (m *Map6[A, B, C, D, E, F]) AddBatch(filter ecs.Filter, target ...ecs.Entit // // The optional argument can be used to set the target [ecs.Entity] for the Map6's [ecs.Relation]. // -// See also [Batch.AddQ]. +// See also [ecs.Batch.AddQ]. func (m *Map6[A, B, C, D, E, F]) AddBatchQ(filter ecs.Filter, target ...ecs.Entity) Query6[A, B, C, D, E, F] { var query ecs.Query if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map6 has no relation") } - query = m.world.Batch().ExchangeRelationQ(filter, m.ids, nil, m.relation, target[0]) + query = m.world.Relations().ExchangeBatchQ(filter, m.ids, nil, m.relation, target[0]) } else { query = m.world.Batch().ExchangeQ(filter, m.ids, nil) } @@ -1528,34 +1532,35 @@ func (m *Map6[A, B, C, D, E, F]) Remove(entity ecs.Entity, target ...ecs.Entity) } } -// RemoveBatch removes the Map6's components to multiple entities. +// RemoveBatch removes the Map6's components from multiple entities. // // The optional argument can be used to set the target [ecs.Entity] for the Map6's [ecs.Relation]. // -// See also [Batch.Remove]. +// See also [ecs.Batch.Remove]. func (m *Map6[A, B, C, D, E, F]) RemoveBatch(filter ecs.Filter, target ...ecs.Entity) { if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map6 has no relation") } - m.world.Batch().ExchangeRelation(filter, nil, m.ids, m.relation, target[0]) + m.world.Relations().ExchangeBatch(filter, nil, m.ids, m.relation, target[0]) } else { m.world.Batch().Exchange(filter, nil, m.ids) } } -// RemoveBatchQ adds the Map6's components to multiple entities and returns a query over them. +// RemoveBatchQ adds the Map6's components from multiple entities and returns a query over them, +// with no components. // // The optional argument can be used to set the target [ecs.Entity] for the Map6's [ecs.Relation]. // -// See also [Batch.RemoveQ]. +// See also [ecs.Batch.RemoveQ]. func (m *Map6[A, B, C, D, E, F]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query0 { var query ecs.Query if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map6 has no relation") } - query = m.world.Batch().ExchangeRelationQ(filter, nil, m.ids, m.relation, target[0]) + query = m.world.Relations().ExchangeBatchQ(filter, nil, m.ids, m.relation, target[0]) } else { query = m.world.Batch().ExchangeQ(filter, nil, m.ids) } @@ -1571,7 +1576,7 @@ func (m *Map6[A, B, C, D, E, F]) RemoveBatchQ(filter ecs.Filter, target ...ecs.E // // Returns the number of removed entities. // -// See also [Batch.RemoveEntities]. +// See also [ecs.Batch.RemoveEntities]. func (m *Map6[A, B, C, D, E, F]) RemoveEntities(exclusive bool) int { if exclusive { filter := m.mask.Exclusive() @@ -1751,13 +1756,13 @@ func (m *Map7[A, B, C, D, E, F, G]) Add(entity ecs.Entity, target ...ecs.Entity) // // The optional argument can be used to set the target [ecs.Entity] for the Map7's [ecs.Relation]. // -// See also [Batch.Add]. +// See also [ecs.Batch.Add]. func (m *Map7[A, B, C, D, E, F, G]) AddBatch(filter ecs.Filter, target ...ecs.Entity) { if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map7 has no relation") } - m.world.Batch().ExchangeRelation(filter, m.ids, nil, m.relation, target[0]) + m.world.Relations().ExchangeBatch(filter, m.ids, nil, m.relation, target[0]) } else { m.world.Batch().Exchange(filter, m.ids, nil) } @@ -1768,14 +1773,14 @@ func (m *Map7[A, B, C, D, E, F, G]) AddBatch(filter ecs.Filter, target ...ecs.En // // The optional argument can be used to set the target [ecs.Entity] for the Map7's [ecs.Relation]. // -// See also [Batch.AddQ]. +// See also [ecs.Batch.AddQ]. func (m *Map7[A, B, C, D, E, F, G]) AddBatchQ(filter ecs.Filter, target ...ecs.Entity) Query7[A, B, C, D, E, F, G] { var query ecs.Query if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map7 has no relation") } - query = m.world.Batch().ExchangeRelationQ(filter, m.ids, nil, m.relation, target[0]) + query = m.world.Relations().ExchangeBatchQ(filter, m.ids, nil, m.relation, target[0]) } else { query = m.world.Batch().ExchangeQ(filter, m.ids, nil) } @@ -1822,34 +1827,35 @@ func (m *Map7[A, B, C, D, E, F, G]) Remove(entity ecs.Entity, target ...ecs.Enti } } -// RemoveBatch removes the Map7's components to multiple entities. +// RemoveBatch removes the Map7's components from multiple entities. // // The optional argument can be used to set the target [ecs.Entity] for the Map7's [ecs.Relation]. // -// See also [Batch.Remove]. +// See also [ecs.Batch.Remove]. func (m *Map7[A, B, C, D, E, F, G]) RemoveBatch(filter ecs.Filter, target ...ecs.Entity) { if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map7 has no relation") } - m.world.Batch().ExchangeRelation(filter, nil, m.ids, m.relation, target[0]) + m.world.Relations().ExchangeBatch(filter, nil, m.ids, m.relation, target[0]) } else { m.world.Batch().Exchange(filter, nil, m.ids) } } -// RemoveBatchQ adds the Map7's components to multiple entities and returns a query over them. +// RemoveBatchQ adds the Map7's components from multiple entities and returns a query over them, +// with no components. // // The optional argument can be used to set the target [ecs.Entity] for the Map7's [ecs.Relation]. // -// See also [Batch.RemoveQ]. +// See also [ecs.Batch.RemoveQ]. func (m *Map7[A, B, C, D, E, F, G]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query0 { var query ecs.Query if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map7 has no relation") } - query = m.world.Batch().ExchangeRelationQ(filter, nil, m.ids, m.relation, target[0]) + query = m.world.Relations().ExchangeBatchQ(filter, nil, m.ids, m.relation, target[0]) } else { query = m.world.Batch().ExchangeQ(filter, nil, m.ids) } @@ -1865,7 +1871,7 @@ func (m *Map7[A, B, C, D, E, F, G]) RemoveBatchQ(filter ecs.Filter, target ...ec // // Returns the number of removed entities. // -// See also [Batch.RemoveEntities]. +// See also [ecs.Batch.RemoveEntities]. func (m *Map7[A, B, C, D, E, F, G]) RemoveEntities(exclusive bool) int { if exclusive { filter := m.mask.Exclusive() @@ -2052,13 +2058,13 @@ func (m *Map8[A, B, C, D, E, F, G, H]) Add(entity ecs.Entity, target ...ecs.Enti // // The optional argument can be used to set the target [ecs.Entity] for the Map8's [ecs.Relation]. // -// See also [Batch.Add]. +// See also [ecs.Batch.Add]. func (m *Map8[A, B, C, D, E, F, G, H]) AddBatch(filter ecs.Filter, target ...ecs.Entity) { if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map8 has no relation") } - m.world.Batch().ExchangeRelation(filter, m.ids, nil, m.relation, target[0]) + m.world.Relations().ExchangeBatch(filter, m.ids, nil, m.relation, target[0]) } else { m.world.Batch().Exchange(filter, m.ids, nil) } @@ -2069,14 +2075,14 @@ func (m *Map8[A, B, C, D, E, F, G, H]) AddBatch(filter ecs.Filter, target ...ecs // // The optional argument can be used to set the target [ecs.Entity] for the Map8's [ecs.Relation]. // -// See also [Batch.AddQ]. +// See also [ecs.Batch.AddQ]. func (m *Map8[A, B, C, D, E, F, G, H]) AddBatchQ(filter ecs.Filter, target ...ecs.Entity) Query8[A, B, C, D, E, F, G, H] { var query ecs.Query if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map8 has no relation") } - query = m.world.Batch().ExchangeRelationQ(filter, m.ids, nil, m.relation, target[0]) + query = m.world.Relations().ExchangeBatchQ(filter, m.ids, nil, m.relation, target[0]) } else { query = m.world.Batch().ExchangeQ(filter, m.ids, nil) } @@ -2125,34 +2131,35 @@ func (m *Map8[A, B, C, D, E, F, G, H]) Remove(entity ecs.Entity, target ...ecs.E } } -// RemoveBatch removes the Map8's components to multiple entities. +// RemoveBatch removes the Map8's components from multiple entities. // // The optional argument can be used to set the target [ecs.Entity] for the Map8's [ecs.Relation]. // -// See also [Batch.Remove]. +// See also [ecs.Batch.Remove]. func (m *Map8[A, B, C, D, E, F, G, H]) RemoveBatch(filter ecs.Filter, target ...ecs.Entity) { if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map8 has no relation") } - m.world.Batch().ExchangeRelation(filter, nil, m.ids, m.relation, target[0]) + m.world.Relations().ExchangeBatch(filter, nil, m.ids, m.relation, target[0]) } else { m.world.Batch().Exchange(filter, nil, m.ids) } } -// RemoveBatchQ adds the Map8's components to multiple entities and returns a query over them. +// RemoveBatchQ adds the Map8's components from multiple entities and returns a query over them, +// with no components. // // The optional argument can be used to set the target [ecs.Entity] for the Map8's [ecs.Relation]. // -// See also [Batch.RemoveQ]. +// See also [ecs.Batch.RemoveQ]. func (m *Map8[A, B, C, D, E, F, G, H]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query0 { var query ecs.Query if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map8 has no relation") } - query = m.world.Batch().ExchangeRelationQ(filter, nil, m.ids, m.relation, target[0]) + query = m.world.Relations().ExchangeBatchQ(filter, nil, m.ids, m.relation, target[0]) } else { query = m.world.Batch().ExchangeQ(filter, nil, m.ids) } @@ -2168,7 +2175,7 @@ func (m *Map8[A, B, C, D, E, F, G, H]) RemoveBatchQ(filter ecs.Filter, target .. // // Returns the number of removed entities. // -// See also [Batch.RemoveEntities]. +// See also [ecs.Batch.RemoveEntities]. func (m *Map8[A, B, C, D, E, F, G, H]) RemoveEntities(exclusive bool) int { if exclusive { filter := m.mask.Exclusive() @@ -2362,13 +2369,13 @@ func (m *Map9[A, B, C, D, E, F, G, H, I]) Add(entity ecs.Entity, target ...ecs.E // // The optional argument can be used to set the target [ecs.Entity] for the Map9's [ecs.Relation]. // -// See also [Batch.Add]. +// See also [ecs.Batch.Add]. func (m *Map9[A, B, C, D, E, F, G, H, I]) AddBatch(filter ecs.Filter, target ...ecs.Entity) { if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map9 has no relation") } - m.world.Batch().ExchangeRelation(filter, m.ids, nil, m.relation, target[0]) + m.world.Relations().ExchangeBatch(filter, m.ids, nil, m.relation, target[0]) } else { m.world.Batch().Exchange(filter, m.ids, nil) } @@ -2379,14 +2386,14 @@ func (m *Map9[A, B, C, D, E, F, G, H, I]) AddBatch(filter ecs.Filter, target ... // // The optional argument can be used to set the target [ecs.Entity] for the Map9's [ecs.Relation]. // -// See also [Batch.AddQ]. +// See also [ecs.Batch.AddQ]. func (m *Map9[A, B, C, D, E, F, G, H, I]) AddBatchQ(filter ecs.Filter, target ...ecs.Entity) Query9[A, B, C, D, E, F, G, H, I] { var query ecs.Query if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map9 has no relation") } - query = m.world.Batch().ExchangeRelationQ(filter, m.ids, nil, m.relation, target[0]) + query = m.world.Relations().ExchangeBatchQ(filter, m.ids, nil, m.relation, target[0]) } else { query = m.world.Batch().ExchangeQ(filter, m.ids, nil) } @@ -2437,34 +2444,35 @@ func (m *Map9[A, B, C, D, E, F, G, H, I]) Remove(entity ecs.Entity, target ...ec } } -// RemoveBatch removes the Map9's components to multiple entities. +// RemoveBatch removes the Map9's components from multiple entities. // // The optional argument can be used to set the target [ecs.Entity] for the Map9's [ecs.Relation]. // -// See also [Batch.Remove]. +// See also [ecs.Batch.Remove]. func (m *Map9[A, B, C, D, E, F, G, H, I]) RemoveBatch(filter ecs.Filter, target ...ecs.Entity) { if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map9 has no relation") } - m.world.Batch().ExchangeRelation(filter, nil, m.ids, m.relation, target[0]) + m.world.Relations().ExchangeBatch(filter, nil, m.ids, m.relation, target[0]) } else { m.world.Batch().Exchange(filter, nil, m.ids) } } -// RemoveBatchQ adds the Map9's components to multiple entities and returns a query over them. +// RemoveBatchQ adds the Map9's components from multiple entities and returns a query over them, +// with no components. // // The optional argument can be used to set the target [ecs.Entity] for the Map9's [ecs.Relation]. // -// See also [Batch.RemoveQ]. +// See also [ecs.Batch.RemoveQ]. func (m *Map9[A, B, C, D, E, F, G, H, I]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query0 { var query ecs.Query if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map9 has no relation") } - query = m.world.Batch().ExchangeRelationQ(filter, nil, m.ids, m.relation, target[0]) + query = m.world.Relations().ExchangeBatchQ(filter, nil, m.ids, m.relation, target[0]) } else { query = m.world.Batch().ExchangeQ(filter, nil, m.ids) } @@ -2480,7 +2488,7 @@ func (m *Map9[A, B, C, D, E, F, G, H, I]) RemoveBatchQ(filter ecs.Filter, target // // Returns the number of removed entities. // -// See also [Batch.RemoveEntities]. +// See also [ecs.Batch.RemoveEntities]. func (m *Map9[A, B, C, D, E, F, G, H, I]) RemoveEntities(exclusive bool) int { if exclusive { filter := m.mask.Exclusive() @@ -2681,13 +2689,13 @@ func (m *Map10[A, B, C, D, E, F, G, H, I, J]) Add(entity ecs.Entity, target ...e // // The optional argument can be used to set the target [ecs.Entity] for the Map10's [ecs.Relation]. // -// See also [Batch.Add]. +// See also [ecs.Batch.Add]. func (m *Map10[A, B, C, D, E, F, G, H, I, J]) AddBatch(filter ecs.Filter, target ...ecs.Entity) { if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map10 has no relation") } - m.world.Batch().ExchangeRelation(filter, m.ids, nil, m.relation, target[0]) + m.world.Relations().ExchangeBatch(filter, m.ids, nil, m.relation, target[0]) } else { m.world.Batch().Exchange(filter, m.ids, nil) } @@ -2698,14 +2706,14 @@ func (m *Map10[A, B, C, D, E, F, G, H, I, J]) AddBatch(filter ecs.Filter, target // // The optional argument can be used to set the target [ecs.Entity] for the Map10's [ecs.Relation]. // -// See also [Batch.AddQ]. +// See also [ecs.Batch.AddQ]. func (m *Map10[A, B, C, D, E, F, G, H, I, J]) AddBatchQ(filter ecs.Filter, target ...ecs.Entity) Query10[A, B, C, D, E, F, G, H, I, J] { var query ecs.Query if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map10 has no relation") } - query = m.world.Batch().ExchangeRelationQ(filter, m.ids, nil, m.relation, target[0]) + query = m.world.Relations().ExchangeBatchQ(filter, m.ids, nil, m.relation, target[0]) } else { query = m.world.Batch().ExchangeQ(filter, m.ids, nil) } @@ -2758,34 +2766,35 @@ func (m *Map10[A, B, C, D, E, F, G, H, I, J]) Remove(entity ecs.Entity, target . } } -// RemoveBatch removes the Map10's components to multiple entities. +// RemoveBatch removes the Map10's components from multiple entities. // // The optional argument can be used to set the target [ecs.Entity] for the Map10's [ecs.Relation]. // -// See also [Batch.Remove]. +// See also [ecs.Batch.Remove]. func (m *Map10[A, B, C, D, E, F, G, H, I, J]) RemoveBatch(filter ecs.Filter, target ...ecs.Entity) { if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map10 has no relation") } - m.world.Batch().ExchangeRelation(filter, nil, m.ids, m.relation, target[0]) + m.world.Relations().ExchangeBatch(filter, nil, m.ids, m.relation, target[0]) } else { m.world.Batch().Exchange(filter, nil, m.ids) } } -// RemoveBatchQ adds the Map10's components to multiple entities and returns a query over them. +// RemoveBatchQ adds the Map10's components from multiple entities and returns a query over them, +// with no components. // // The optional argument can be used to set the target [ecs.Entity] for the Map10's [ecs.Relation]. // -// See also [Batch.RemoveQ]. +// See also [ecs.Batch.RemoveQ]. func (m *Map10[A, B, C, D, E, F, G, H, I, J]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query0 { var query ecs.Query if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map10 has no relation") } - query = m.world.Batch().ExchangeRelationQ(filter, nil, m.ids, m.relation, target[0]) + query = m.world.Relations().ExchangeBatchQ(filter, nil, m.ids, m.relation, target[0]) } else { query = m.world.Batch().ExchangeQ(filter, nil, m.ids) } @@ -2801,7 +2810,7 @@ func (m *Map10[A, B, C, D, E, F, G, H, I, J]) RemoveBatchQ(filter ecs.Filter, ta // // Returns the number of removed entities. // -// See also [Batch.RemoveEntities]. +// See also [ecs.Batch.RemoveEntities]. func (m *Map10[A, B, C, D, E, F, G, H, I, J]) RemoveEntities(exclusive bool) int { if exclusive { filter := m.mask.Exclusive() @@ -3009,13 +3018,13 @@ func (m *Map11[A, B, C, D, E, F, G, H, I, J, K]) Add(entity ecs.Entity, target . // // The optional argument can be used to set the target [ecs.Entity] for the Map11's [ecs.Relation]. // -// See also [Batch.Add]. +// See also [ecs.Batch.Add]. func (m *Map11[A, B, C, D, E, F, G, H, I, J, K]) AddBatch(filter ecs.Filter, target ...ecs.Entity) { if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map11 has no relation") } - m.world.Batch().ExchangeRelation(filter, m.ids, nil, m.relation, target[0]) + m.world.Relations().ExchangeBatch(filter, m.ids, nil, m.relation, target[0]) } else { m.world.Batch().Exchange(filter, m.ids, nil) } @@ -3026,14 +3035,14 @@ func (m *Map11[A, B, C, D, E, F, G, H, I, J, K]) AddBatch(filter ecs.Filter, tar // // The optional argument can be used to set the target [ecs.Entity] for the Map11's [ecs.Relation]. // -// See also [Batch.AddQ]. +// See also [ecs.Batch.AddQ]. func (m *Map11[A, B, C, D, E, F, G, H, I, J, K]) AddBatchQ(filter ecs.Filter, target ...ecs.Entity) Query11[A, B, C, D, E, F, G, H, I, J, K] { var query ecs.Query if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map11 has no relation") } - query = m.world.Batch().ExchangeRelationQ(filter, m.ids, nil, m.relation, target[0]) + query = m.world.Relations().ExchangeBatchQ(filter, m.ids, nil, m.relation, target[0]) } else { query = m.world.Batch().ExchangeQ(filter, m.ids, nil) } @@ -3088,34 +3097,35 @@ func (m *Map11[A, B, C, D, E, F, G, H, I, J, K]) Remove(entity ecs.Entity, targe } } -// RemoveBatch removes the Map11's components to multiple entities. +// RemoveBatch removes the Map11's components from multiple entities. // // The optional argument can be used to set the target [ecs.Entity] for the Map11's [ecs.Relation]. // -// See also [Batch.Remove]. +// See also [ecs.Batch.Remove]. func (m *Map11[A, B, C, D, E, F, G, H, I, J, K]) RemoveBatch(filter ecs.Filter, target ...ecs.Entity) { if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map11 has no relation") } - m.world.Batch().ExchangeRelation(filter, nil, m.ids, m.relation, target[0]) + m.world.Relations().ExchangeBatch(filter, nil, m.ids, m.relation, target[0]) } else { m.world.Batch().Exchange(filter, nil, m.ids) } } -// RemoveBatchQ adds the Map11's components to multiple entities and returns a query over them. +// RemoveBatchQ adds the Map11's components from multiple entities and returns a query over them, +// with no components. // // The optional argument can be used to set the target [ecs.Entity] for the Map11's [ecs.Relation]. // -// See also [Batch.RemoveQ]. +// See also [ecs.Batch.RemoveQ]. func (m *Map11[A, B, C, D, E, F, G, H, I, J, K]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query0 { var query ecs.Query if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map11 has no relation") } - query = m.world.Batch().ExchangeRelationQ(filter, nil, m.ids, m.relation, target[0]) + query = m.world.Relations().ExchangeBatchQ(filter, nil, m.ids, m.relation, target[0]) } else { query = m.world.Batch().ExchangeQ(filter, nil, m.ids) } @@ -3131,7 +3141,7 @@ func (m *Map11[A, B, C, D, E, F, G, H, I, J, K]) RemoveBatchQ(filter ecs.Filter, // // Returns the number of removed entities. // -// See also [Batch.RemoveEntities]. +// See also [ecs.Batch.RemoveEntities]. func (m *Map11[A, B, C, D, E, F, G, H, I, J, K]) RemoveEntities(exclusive bool) int { if exclusive { filter := m.mask.Exclusive() @@ -3346,13 +3356,13 @@ func (m *Map12[A, B, C, D, E, F, G, H, I, J, K, L]) Add(entity ecs.Entity, targe // // The optional argument can be used to set the target [ecs.Entity] for the Map12's [ecs.Relation]. // -// See also [Batch.Add]. +// See also [ecs.Batch.Add]. func (m *Map12[A, B, C, D, E, F, G, H, I, J, K, L]) AddBatch(filter ecs.Filter, target ...ecs.Entity) { if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map12 has no relation") } - m.world.Batch().ExchangeRelation(filter, m.ids, nil, m.relation, target[0]) + m.world.Relations().ExchangeBatch(filter, m.ids, nil, m.relation, target[0]) } else { m.world.Batch().Exchange(filter, m.ids, nil) } @@ -3363,14 +3373,14 @@ func (m *Map12[A, B, C, D, E, F, G, H, I, J, K, L]) AddBatch(filter ecs.Filter, // // The optional argument can be used to set the target [ecs.Entity] for the Map12's [ecs.Relation]. // -// See also [Batch.AddQ]. +// See also [ecs.Batch.AddQ]. func (m *Map12[A, B, C, D, E, F, G, H, I, J, K, L]) AddBatchQ(filter ecs.Filter, target ...ecs.Entity) Query12[A, B, C, D, E, F, G, H, I, J, K, L] { var query ecs.Query if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map12 has no relation") } - query = m.world.Batch().ExchangeRelationQ(filter, m.ids, nil, m.relation, target[0]) + query = m.world.Relations().ExchangeBatchQ(filter, m.ids, nil, m.relation, target[0]) } else { query = m.world.Batch().ExchangeQ(filter, m.ids, nil) } @@ -3427,34 +3437,35 @@ func (m *Map12[A, B, C, D, E, F, G, H, I, J, K, L]) Remove(entity ecs.Entity, ta } } -// RemoveBatch removes the Map12's components to multiple entities. +// RemoveBatch removes the Map12's components from multiple entities. // // The optional argument can be used to set the target [ecs.Entity] for the Map12's [ecs.Relation]. // -// See also [Batch.Remove]. +// See also [ecs.Batch.Remove]. func (m *Map12[A, B, C, D, E, F, G, H, I, J, K, L]) RemoveBatch(filter ecs.Filter, target ...ecs.Entity) { if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map12 has no relation") } - m.world.Batch().ExchangeRelation(filter, nil, m.ids, m.relation, target[0]) + m.world.Relations().ExchangeBatch(filter, nil, m.ids, m.relation, target[0]) } else { m.world.Batch().Exchange(filter, nil, m.ids) } } -// RemoveBatchQ adds the Map12's components to multiple entities and returns a query over them. +// RemoveBatchQ adds the Map12's components from multiple entities and returns a query over them, +// with no components. // // The optional argument can be used to set the target [ecs.Entity] for the Map12's [ecs.Relation]. // -// See also [Batch.RemoveQ]. +// See also [ecs.Batch.RemoveQ]. func (m *Map12[A, B, C, D, E, F, G, H, I, J, K, L]) RemoveBatchQ(filter ecs.Filter, target ...ecs.Entity) Query0 { var query ecs.Query if len(target) > 0 { if !m.hasRelation { panic("can't set target entity: Map12 has no relation") } - query = m.world.Batch().ExchangeRelationQ(filter, nil, m.ids, m.relation, target[0]) + query = m.world.Relations().ExchangeBatchQ(filter, nil, m.ids, m.relation, target[0]) } else { query = m.world.Batch().ExchangeQ(filter, nil, m.ids) } @@ -3470,7 +3481,7 @@ func (m *Map12[A, B, C, D, E, F, G, H, I, J, K, L]) RemoveBatchQ(filter ecs.Filt // // Returns the number of removed entities. // -// See also [Batch.RemoveEntities]. +// See also [ecs.Batch.RemoveEntities]. 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() From 9f0e266967a7066f169c2557d123a47bd2031ddb Mon Sep 17 00:00:00 2001 From: mlange-42 Date: Thu, 18 Jan 2024 23:23:41 +0100 Subject: [PATCH 12/12] tweak docs for new methods --- ecs/event/event.go | 28 +++++++++++----------- ecs/relations.go | 11 +++++++++ generic/doc.go | 1 + generic/exchange.go | 16 +++++-------- generic/generate/map.go.txt | 4 ++-- generic/map_generated.go | 48 ++++++++++++++++++------------------- 6 files changed, 58 insertions(+), 50 deletions(-) diff --git a/ecs/event/event.go b/ecs/event/event.go index 812cd4ed..513cf7ce 100644 --- a/ecs/event/event.go +++ b/ecs/event/event.go @@ -1,8 +1,21 @@ // Package event contains a mask type and bit switches for listener subscriptions. // -// See also ecs.Listener and ecs.EntityEvent. +// See also [github.com/mlange-42/arche/ecs.Listener] and [github.com/mlange-42/arche/ecs.EntityEvent]. package event +// Subscription bits for an [github.com/mlange-42/arche/ecs.Listener] +type Subscription uint8 + +// Contains checks whether all the argument's bits are contained in this Subscription. +func (s Subscription) Contains(bits Subscription) bool { + return (bits & s) == bits +} + +// ContainsAny checks whether any of the argument's bits are contained in this Subscription. +func (s Subscription) ContainsAny(bits Subscription) bool { + return (bits & s) != 0 +} + // Subscription bits for individual events. const ( // EntityCreated subscription bit. @@ -75,16 +88,3 @@ const ( // All subscriptions All Subscription = Entities | Components | Relations ) - -// Subscription bits for an ecs.Listener -type Subscription uint8 - -// Contains checks whether all the argument's bits are contained in this Subscription. -func (s Subscription) Contains(bits Subscription) bool { - return (bits & s) == bits -} - -// ContainsAny checks whether any of the argument's bits are contained in this Subscription. -func (s Subscription) ContainsAny(bits Subscription) bool { - return (bits & s) != 0 -} diff --git a/ecs/relations.go b/ecs/relations.go index 11c62e4f..8d5cb9e5 100644 --- a/ecs/relations.go +++ b/ecs/relations.go @@ -23,6 +23,8 @@ func (r *Relations) Get(entity Entity, comp ID) Entity { // // GetUnchecked is an optimized version of [Relations.Get]. // Does not check if the entity is alive or that the component ID is applicable. +// +// Panics when called for a removed entity, but not for a recycled entity. func (r *Relations) GetUnchecked(entity Entity, comp ID) Entity { return r.world.getRelationUnchecked(entity, comp) } @@ -70,6 +72,9 @@ func (r *Relations) SetBatchQ(filter Filter, comp ID, target Entity) Query { // This is more efficient than subsequent use of [World.Add] and [World.Remove]. // In contrast to [World.Exchange], it allows to also set a relation target. // +// When a [Relation] component is removed and another one is added, +// the target entity of the relation is set to zero if no target is given. +// // Panics: // - when called for a removed (and potentially recycled) entity. // - when called with components that can't be added or removed because they are already present/not present, respectively. @@ -86,6 +91,9 @@ func (r *Relations) Exchange(entity Entity, add []ID, rem []ID, relation ID, tar // ExchangeBatch exchanges components for many entities, matching a filter. // In contrast to [Batch.Exchange], it allows to also set a relation target. // +// When a [Relation] component is removed and another one is added, +// the target entity of the relation is set to zero if no target is given. +// // Panics: // - when called with components that can't be added or removed because they are already present/not present, respectively. // - when called for a missing relation component. @@ -102,6 +110,9 @@ func (r *Relations) ExchangeBatch(filter Filter, add []ID, rem []ID, relation ID // It returns a query over the affected entities. // In contrast to [Batch.ExchangeQ], it allows to also set a relation target. // +// When a [Relation] component is removed and another one is added, +// the target entity of the relation is set to zero if no target is given. +// // Panics: // - when called with components that can't be added or removed because they are already present/not present, respectively. // - when called for a missing relation component. diff --git a/generic/doc.go b/generic/doc.go index 8e9f10ae..7244ff6d 100644 --- a/generic/doc.go +++ b/generic/doc.go @@ -10,5 +10,6 @@ // - [Map] provides generic access to a single component using world access, like [Map.Get] and [Map.Set]. // - [Map1], [Map2], etc. provide generic access to multiple components using world access, // like [Map1.Get], [Map1.Add], [Map1.Remove], etc. +// - [Exchange] allows to add, remove and exchange components, incl. as batch operations. // - [Resource] provides generic access to a resource from [ecs.Resources]. package generic diff --git a/generic/exchange.go b/generic/exchange.go index 1828b6f0..897ac939 100644 --- a/generic/exchange.go +++ b/generic/exchange.go @@ -116,12 +116,10 @@ func (m *Exchange) Remove(entity ecs.Entity, target ...ecs.Entity) { // // Removes the components set via [Exchange.Removes]. // Adds the components set via [Exchange.Adds]. -// -// When a [ecs.Relation] component is removed and another one is added, -// the target entity of the relation is set to zero. -// // The optional argument can be used to set the target [ecs.Entity] for the Exchange's [ecs.Relation]. // See [Exchange.WithRelation]. +// When a [ecs.Relation] component is removed and another one is added, +// the target entity of the relation is set to zero if no target is given. // // See also [ecs.World.Exchange]. func (m *Exchange) Exchange(entity ecs.Entity, target ...ecs.Entity) { @@ -135,18 +133,16 @@ func (m *Exchange) Exchange(entity ecs.Entity, target ...ecs.Entity) { } } -// ExchangeBatch exchanges components on many entities. +// ExchangeBatch exchanges components on many entities, matching a filter. // // Removes the components set via [Exchange.Removes]. // Adds the components set via [Exchange.Adds]. -// -// When a [Relation] component is removed and another one is added, -// the target entity of the relation is set to zero. -// // The optional argument can be used to set the target [ecs.Entity] for the Exchange's [ecs.Relation]. // See [Exchange.WithRelation]. +// When a [ecs.Relation] component is removed and another one is added, +// the target entity of the relation is set to zero if no target is given. // -// See also [Batch.Exchange] and [Batch.ExchangeQ]. +// See also [ecs.Batch.Exchange] and [ecs.Batch.ExchangeQ]. func (m *Exchange) ExchangeBatch(filter ecs.Filter, target ...ecs.Entity) { if len(target) > 0 { if !m.hasRelation { diff --git a/generic/generate/map.go.txt b/generic/generate/map.go.txt index 5c1a3eb5..e2eef105 100644 --- a/generic/generate/map.go.txt +++ b/generic/generate/map.go.txt @@ -123,7 +123,7 @@ func (m *Map{{ .Index }}{{ .Types }}) Add(entity ecs.Entity, target ...ecs.Entit } } -// AddBatch adds the Map{{ .Index }}'s components to multiple entities. +// AddBatch adds the Map{{ .Index }}'s components to many entities, matching a filter. // // The optional argument can be used to set the target [ecs.Entity] for the Map{{ .Index }}'s [ecs.Relation]. // @@ -187,7 +187,7 @@ func (m *Map{{ .Index }}{{ .Types }}) Remove(entity ecs.Entity, target ...ecs.En } } -// RemoveBatch removes the Map{{ .Index }}'s components from multiple entities. +// RemoveBatch removes the Map{{ .Index }}'s components from many entities, matching a filter. // // The optional argument can be used to set the target [ecs.Entity] for the Map{{ .Index }}'s [ecs.Relation]. // diff --git a/generic/map_generated.go b/generic/map_generated.go index a02369fc..ea42912d 100644 --- a/generic/map_generated.go +++ b/generic/map_generated.go @@ -129,7 +129,7 @@ func (m *Map1[A]) Add(entity ecs.Entity, target ...ecs.Entity) { } } -// AddBatch adds the Map1's components to multiple entities. +// AddBatch adds the Map1's components to many entities, matching a filter. // // The optional argument can be used to set the target [ecs.Entity] for the Map1's [ecs.Relation]. // @@ -192,7 +192,7 @@ func (m *Map1[A]) Remove(entity ecs.Entity, target ...ecs.Entity) { } } -// RemoveBatch removes the Map1's components from multiple entities. +// RemoveBatch removes the Map1's components from many entities, matching a filter. // // The optional argument can be used to set the target [ecs.Entity] for the Map1's [ecs.Relation]. // @@ -377,7 +377,7 @@ func (m *Map2[A, B]) Add(entity ecs.Entity, target ...ecs.Entity) { } } -// AddBatch adds the Map2's components to multiple entities. +// AddBatch adds the Map2's components to many entities, matching a filter. // // The optional argument can be used to set the target [ecs.Entity] for the Map2's [ecs.Relation]. // @@ -442,7 +442,7 @@ func (m *Map2[A, B]) Remove(entity ecs.Entity, target ...ecs.Entity) { } } -// RemoveBatch removes the Map2's components from multiple entities. +// RemoveBatch removes the Map2's components from many entities, matching a filter. // // The optional argument can be used to set the target [ecs.Entity] for the Map2's [ecs.Relation]. // @@ -634,7 +634,7 @@ func (m *Map3[A, B, C]) Add(entity ecs.Entity, target ...ecs.Entity) { } } -// AddBatch adds the Map3's components to multiple entities. +// AddBatch adds the Map3's components to many entities, matching a filter. // // The optional argument can be used to set the target [ecs.Entity] for the Map3's [ecs.Relation]. // @@ -701,7 +701,7 @@ func (m *Map3[A, B, C]) Remove(entity ecs.Entity, target ...ecs.Entity) { } } -// RemoveBatch removes the Map3's components from multiple entities. +// RemoveBatch removes the Map3's components from many entities, matching a filter. // // The optional argument can be used to set the target [ecs.Entity] for the Map3's [ecs.Relation]. // @@ -900,7 +900,7 @@ func (m *Map4[A, B, C, D]) Add(entity ecs.Entity, target ...ecs.Entity) { } } -// AddBatch adds the Map4's components to multiple entities. +// AddBatch adds the Map4's components to many entities, matching a filter. // // The optional argument can be used to set the target [ecs.Entity] for the Map4's [ecs.Relation]. // @@ -969,7 +969,7 @@ func (m *Map4[A, B, C, D]) Remove(entity ecs.Entity, target ...ecs.Entity) { } } -// RemoveBatch removes the Map4's components from multiple entities. +// RemoveBatch removes the Map4's components from many entities, matching a filter. // // The optional argument can be used to set the target [ecs.Entity] for the Map4's [ecs.Relation]. // @@ -1175,7 +1175,7 @@ func (m *Map5[A, B, C, D, E]) Add(entity ecs.Entity, target ...ecs.Entity) { } } -// AddBatch adds the Map5's components to multiple entities. +// AddBatch adds the Map5's components to many entities, matching a filter. // // The optional argument can be used to set the target [ecs.Entity] for the Map5's [ecs.Relation]. // @@ -1246,7 +1246,7 @@ func (m *Map5[A, B, C, D, E]) Remove(entity ecs.Entity, target ...ecs.Entity) { } } -// RemoveBatch removes the Map5's components from multiple entities. +// RemoveBatch removes the Map5's components from many entities, matching a filter. // // The optional argument can be used to set the target [ecs.Entity] for the Map5's [ecs.Relation]. // @@ -1459,7 +1459,7 @@ func (m *Map6[A, B, C, D, E, F]) Add(entity ecs.Entity, target ...ecs.Entity) { } } -// AddBatch adds the Map6's components to multiple entities. +// AddBatch adds the Map6's components to many entities, matching a filter. // // The optional argument can be used to set the target [ecs.Entity] for the Map6's [ecs.Relation]. // @@ -1532,7 +1532,7 @@ func (m *Map6[A, B, C, D, E, F]) Remove(entity ecs.Entity, target ...ecs.Entity) } } -// RemoveBatch removes the Map6's components from multiple entities. +// RemoveBatch removes the Map6's components from many entities, matching a filter. // // The optional argument can be used to set the target [ecs.Entity] for the Map6's [ecs.Relation]. // @@ -1752,7 +1752,7 @@ func (m *Map7[A, B, C, D, E, F, G]) Add(entity ecs.Entity, target ...ecs.Entity) } } -// AddBatch adds the Map7's components to multiple entities. +// AddBatch adds the Map7's components to many entities, matching a filter. // // The optional argument can be used to set the target [ecs.Entity] for the Map7's [ecs.Relation]. // @@ -1827,7 +1827,7 @@ func (m *Map7[A, B, C, D, E, F, G]) Remove(entity ecs.Entity, target ...ecs.Enti } } -// RemoveBatch removes the Map7's components from multiple entities. +// RemoveBatch removes the Map7's components from many entities, matching a filter. // // The optional argument can be used to set the target [ecs.Entity] for the Map7's [ecs.Relation]. // @@ -2054,7 +2054,7 @@ func (m *Map8[A, B, C, D, E, F, G, H]) Add(entity ecs.Entity, target ...ecs.Enti } } -// AddBatch adds the Map8's components to multiple entities. +// AddBatch adds the Map8's components to many entities, matching a filter. // // The optional argument can be used to set the target [ecs.Entity] for the Map8's [ecs.Relation]. // @@ -2131,7 +2131,7 @@ func (m *Map8[A, B, C, D, E, F, G, H]) Remove(entity ecs.Entity, target ...ecs.E } } -// RemoveBatch removes the Map8's components from multiple entities. +// RemoveBatch removes the Map8's components from many entities, matching a filter. // // The optional argument can be used to set the target [ecs.Entity] for the Map8's [ecs.Relation]. // @@ -2365,7 +2365,7 @@ func (m *Map9[A, B, C, D, E, F, G, H, I]) Add(entity ecs.Entity, target ...ecs.E } } -// AddBatch adds the Map9's components to multiple entities. +// AddBatch adds the Map9's components to many entities, matching a filter. // // The optional argument can be used to set the target [ecs.Entity] for the Map9's [ecs.Relation]. // @@ -2444,7 +2444,7 @@ func (m *Map9[A, B, C, D, E, F, G, H, I]) Remove(entity ecs.Entity, target ...ec } } -// RemoveBatch removes the Map9's components from multiple entities. +// RemoveBatch removes the Map9's components from many entities, matching a filter. // // The optional argument can be used to set the target [ecs.Entity] for the Map9's [ecs.Relation]. // @@ -2685,7 +2685,7 @@ func (m *Map10[A, B, C, D, E, F, G, H, I, J]) Add(entity ecs.Entity, target ...e } } -// AddBatch adds the Map10's components to multiple entities. +// AddBatch adds the Map10's components to many entities, matching a filter. // // The optional argument can be used to set the target [ecs.Entity] for the Map10's [ecs.Relation]. // @@ -2766,7 +2766,7 @@ func (m *Map10[A, B, C, D, E, F, G, H, I, J]) Remove(entity ecs.Entity, target . } } -// RemoveBatch removes the Map10's components from multiple entities. +// RemoveBatch removes the Map10's components from many entities, matching a filter. // // The optional argument can be used to set the target [ecs.Entity] for the Map10's [ecs.Relation]. // @@ -3014,7 +3014,7 @@ func (m *Map11[A, B, C, D, E, F, G, H, I, J, K]) Add(entity ecs.Entity, target . } } -// AddBatch adds the Map11's components to multiple entities. +// AddBatch adds the Map11's components to many entities, matching a filter. // // The optional argument can be used to set the target [ecs.Entity] for the Map11's [ecs.Relation]. // @@ -3097,7 +3097,7 @@ func (m *Map11[A, B, C, D, E, F, G, H, I, J, K]) Remove(entity ecs.Entity, targe } } -// RemoveBatch removes the Map11's components from multiple entities. +// RemoveBatch removes the Map11's components from many entities, matching a filter. // // The optional argument can be used to set the target [ecs.Entity] for the Map11's [ecs.Relation]. // @@ -3352,7 +3352,7 @@ func (m *Map12[A, B, C, D, E, F, G, H, I, J, K, L]) Add(entity ecs.Entity, targe } } -// AddBatch adds the Map12's components to multiple entities. +// AddBatch adds the Map12's components to many entities, matching a filter. // // The optional argument can be used to set the target [ecs.Entity] for the Map12's [ecs.Relation]. // @@ -3437,7 +3437,7 @@ func (m *Map12[A, B, C, D, E, F, G, H, I, J, K, L]) Remove(entity ecs.Entity, ta } } -// RemoveBatch removes the Map12's components from multiple entities. +// RemoveBatch removes the Map12's components from many entities, matching a filter. // // The optional argument can be used to set the target [ecs.Entity] for the Map12's [ecs.Relation]. //