Repository: mlange-42/arche
Pull request #15 (2023-02-06 23:52:23)

Panic on too many component types


Patch:
diff --git a/ecs/registry.go b/ecs/registry.go
index e646824..8e8befa 100644
--- a/ecs/registry.go
+++ b/ecs/registry.go
@@ -15,9 +15,12 @@ func newComponentRegistry() componentRegistry {
 	}
 }
 
-// RegisterComponent registers a components and assigns an ID for it
-func (r *componentRegistry) RegisterComponent(tp reflect.Type) ID {
+// registerComponent registers a components and assigns an ID for it
+func (r *componentRegistry) registerComponent(tp reflect.Type, totalBits int) ID {
 	id := ID(len(r.components))
+	if int(id) >= totalBits {
+		panic("maximum of 64 component types exceeded")
+	}
 	r.components[tp] = id
 	r.types[id] = tp
 	return id
@@ -28,7 +31,7 @@ func (r *componentRegistry) ComponentID(tp reflect.Type) ID {
 	if id, ok := r.components[tp]; ok {
 		return id
 	}
-	return r.RegisterComponent(tp)
+	return r.registerComponent(tp, MaskTotalBits)
 }
 
 // ComponentType returns the type of a component by ID
diff --git a/ecs/world.go b/ecs/world.go
index 037859f..02647f0 100644
--- a/ecs/world.go
+++ b/ecs/world.go
@@ -196,11 +196,6 @@ func (w *World) Alive(entity Entity) bool {
 	return w.entityPool.Alive(entity)
 }
 
-// RegisterComponent registers a components and assigns an ID for it
-func (w *World) RegisterComponent(tp reflect.Type) ID {
-	return w.registry.RegisterComponent(tp)
-}
-
 // ComponentID returns the ID for a component type, and registers it if not already registered
 func (w *World) ComponentID(tp reflect.Type) ID {
 	return w.registry.ComponentID(tp)
@@ -220,12 +215,6 @@ func (w *World) Query(comps ...ID) Query {
 	return newQuery(arches)
 }
 
-// RegisterComponent provides a way to register components via generics
-func RegisterComponent[T any](w *World) ID {
-	tp := reflect.TypeOf((*T)(nil)).Elem()
-	return w.RegisterComponent(tp)
-}
-
 // ComponentID provides a way to get a component's ID via generics
 func ComponentID[T any](w *World) ID {
 	tp := reflect.TypeOf((*T)(nil)).Elem()
diff --git a/profile/main/profile.go b/profile/main/profile.go
index e0a7e68..d36709f 100644
--- a/profile/main/profile.go
+++ b/profile/main/profile.go
@@ -39,8 +39,8 @@ func run(rounds, iters, entities int) {
 	for i := 0; i < rounds; i++ {
 		world := ecs.NewWorld()
 
-		posID := ecs.RegisterComponent[position](&world)
-		rotID := ecs.RegisterComponent[rotation](&world)
+		posID := ecs.ComponentID[position](&world)
+		rotID := ecs.ComponentID[rotation](&world)
 
 		for j := 0; j < entities; j++ {
 			entity := world.NewEntity()


Test patch:
diff --git a/benchmark/add/arche_test.go b/benchmark/add/arche_test.go
index 906d555..79728b8 100644
--- a/benchmark/add/arche_test.go
+++ b/benchmark/add/arche_test.go
@@ -11,8 +11,8 @@ func addArcheWorld(b *testing.B, count int) {
 		b.StopTimer()
 		world := ecs.NewWorld()
 
-		posID := ecs.RegisterComponent[position](&world)
-		rotID := ecs.RegisterComponent[rotation](&world)
+		posID := ecs.ComponentID[position](&world)
+		rotID := ecs.ComponentID[rotation](&world)
 		b.StartTimer()
 
 		for i := 0; i < count; i++ {
diff --git a/benchmark/iterate/arche_test.go b/benchmark/iterate/arche_test.go
index be55db5..44eee6e 100644
--- a/benchmark/iterate/arche_test.go
+++ b/benchmark/iterate/arche_test.go
@@ -10,8 +10,8 @@ func runArcheQuery(b *testing.B, count int) {
 	b.StopTimer()
 	world := ecs.NewWorld()
 
-	posID := ecs.RegisterComponent[position](&world)
-	rotID := ecs.RegisterComponent[rotation](&world)
+	posID := ecs.ComponentID[position](&world)
+	rotID := ecs.ComponentID[rotation](&world)
 
 	for i := 0; i < count; i++ {
 		entity := world.NewEntity()
@@ -34,8 +34,8 @@ func runArcheWorld(b *testing.B, count int) {
 	b.StopTimer()
 	world := ecs.NewWorld()
 
-	posID := ecs.RegisterComponent[position](&world)
-	rotID := ecs.RegisterComponent[rotation](&world)
+	posID := ecs.ComponentID[position](&world)
+	rotID := ecs.ComponentID[rotation](&world)
 
 	for i := 0; i < count; i++ {
 		entity := world.NewEntity()
diff --git a/ecs/query_test.go b/ecs/query_test.go
index b0f885b..212b864 100644
--- a/ecs/query_test.go
+++ b/ecs/query_test.go
@@ -9,8 +9,8 @@ import (
 func TestQuery(t *testing.T) {
 	w := NewWorld()
 
-	posID := RegisterComponent[position](&w)
-	rotID := RegisterComponent[rotation](&w)
+	posID := ComponentID[position](&w)
+	rotID := ComponentID[rotation](&w)
 
 	e0 := w.NewEntity()
 	e1 := w.NewEntity()
diff --git a/ecs/registry_test.go b/ecs/registry_test.go
index 64fdbbc..eb9c242 100644
--- a/ecs/registry_test.go
+++ b/ecs/registry_test.go
@@ -13,7 +13,7 @@ func TestComponentRegistry(t *testing.T) {
 	posType := reflect.TypeOf((*position)(nil)).Elem()
 	rotType := reflect.TypeOf((*rotation)(nil)).Elem()
 
-	reg.RegisterComponent(posType)
+	reg.registerComponent(posType, MaskTotalBits)
 
 	assert.Equal(t, ID(0), reg.ComponentID(posType))
 	assert.Equal(t, ID(1), reg.ComponentID(rotType))
@@ -21,3 +21,13 @@ func TestComponentRegistry(t *testing.T) {
 	assert.Equal(t, posType, reg.ComponentType(ID(0)))
 	assert.Equal(t, rotType, reg.ComponentType(ID(1)))
 }
+
+func TestComponentRegistryOverflow(t *testing.T) {
+	reg := newComponentRegistry()
+
+	reg.registerComponent(reflect.TypeOf((*position)(nil)).Elem(), 1)
+
+	assert.Panics(t, func() {
+		reg.registerComponent(reflect.TypeOf((*rotation)(nil)).Elem(), 1)
+	})
+}
diff --git a/ecs/world_test.go b/ecs/world_test.go
index 9ac5893..8d43d3a 100644
--- a/ecs/world_test.go
+++ b/ecs/world_test.go
@@ -39,8 +39,8 @@ func TestWorldEntites(t *testing.T) {
 func TestWorldComponents(t *testing.T) {
 	w := NewWorld()
 
-	posID := RegisterComponent[position](&w)
-	rotID := RegisterComponent[rotation](&w)
+	posID := ComponentID[position](&w)
+	rotID := ComponentID[rotation](&w)
 
 	e0 := w.NewEntity()
 	e1 := w.NewEntity()
@@ -96,8 +96,8 @@ func TestWorldComponents(t *testing.T) {
 func TestWorldGetComponents(t *testing.T) {
 	w := NewWorld()
 
-	posID := RegisterComponent[position](&w)
-	rotID := RegisterComponent[rotation](&w)
+	posID := ComponentID[position](&w)
+	rotID := ComponentID[rotation](&w)
 
 	e0 := w.NewEntity()
 	e1 := w.NewEntity()
@@ -133,8 +133,8 @@ func TestWorldGetComponents(t *testing.T) {
 func TestWorldIter(t *testing.T) {
 	world := NewWorld()
 
-	posID := RegisterComponent[position](&world)
-	rotID := RegisterComponent[rotation](&world)
+	posID := ComponentID[position](&world)
+	rotID := ComponentID[rotation](&world)
 
 	for i := 0; i < 1000; i++ {
 		entity := world.NewEntity()
@@ -151,7 +151,7 @@ func TestWorldIter(t *testing.T) {
 func TestRegisterComponents(t *testing.T) {
 	world := NewWorld()
 
-	RegisterComponent[position](&world)
+	ComponentID[position](&world)
 
 	assert.Equal(t, ID(0), ComponentID[position](&world))
 	assert.Equal(t, ID(1), ComponentID[rotation](&world))