Documentation
¶
Overview ¶
Package akara provides a typed, deterministic entity-component-system runtime.
Components are arbitrary Go data types registered through generic Store values. Subscriptions use opaque store handles and cache shared entity archetypes. Systems execute sequentially in a caller-driven update loop. Component masks and archetype transitions are private; the package does not expose or depend on a bit-set API.
Index ¶
- Variables
- type CommandBuffer
- func (buffer *CommandBuffer) AddDynamic(store *DynamicStore, entity Entity, values map[string]any)
- func (buffer *CommandBuffer) Apply() error
- func (buffer *CommandBuffer) CreateDynamic(world *World, components map[*DynamicStore]map[string]any)
- func (buffer *CommandBuffer) Destroy(world *World, entity Entity)
- func (buffer *CommandBuffer) Remove(component ComponentType, entity Entity)
- type ComponentKey
- type ComponentType
- type DynamicComponent
- type DynamicStore
- func (s *DynamicStore) Add(entity Entity) (*DynamicComponent, error)
- func (s *DynamicStore) Entities() []Entity
- func (s *DynamicStore) Get(entity Entity) (*DynamicComponent, bool)
- func (s *DynamicStore) Has(entity Entity) bool
- func (s *DynamicStore) Key() ComponentKey
- func (s *DynamicStore) Len() int
- func (s *DynamicStore) Migrate(next Schema, migrate Migration) error
- func (s *DynamicStore) Remove(entity Entity) bool
- func (s *DynamicStore) Schema() Schema
- func (s *DynamicStore) Set(entity Entity, values map[string]any) (*DynamicComponent, error)
- type Entity
- type Field
- type FieldKind
- type FilterOption
- type Migration
- type Schema
- type Store
- func (s *Store[T]) Add(entity Entity) (*T, error)
- func (s *Store[T]) Entities() []Entity
- func (s *Store[T]) Get(entity Entity) (*T, bool)
- func (s *Store[T]) Has(entity Entity) bool
- func (s *Store[T]) Key() ComponentKey
- func (s *Store[T]) Len() int
- func (s *Store[T]) Remove(entity Entity) bool
- func (s *Store[T]) Set(entity Entity, value T) (*T, error)
- func (s *Store[T]) TypeName() string
- type Subscription
- type System
- type SystemFunc
- type SystemID
- type World
- func (world *World) AddSystem(system System) (SystemID, error)
- func (world *World) ArchetypeCount() int
- func (world *World) Close() error
- func (world *World) Closed() bool
- func (world *World) CreateEntity() (Entity, error)
- func (world *World) CreateEntityWithID(entity Entity) error
- func (world *World) DestroyEntity(entity Entity) bool
- func (world *World) Done() <-chan struct{}
- func (world *World) Entities() []Entity
- func (world *World) EntityExists(entity Entity) bool
- func (world *World) MustCreateEntity() Entity
- func (world *World) RemoveSystem(id SystemID) bool
- func (world *World) Subscribe(options ...FilterOption) (*Subscription, error)
- func (world *World) Systems() []SystemID
- func (world *World) Update(delta time.Duration) error
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( ErrInvalidSchema = errors.New("akara: invalid component schema") ErrSchemaConflict = errors.New("akara: component schema conflicts with existing registration") ErrFieldNotFound = errors.New("akara: component field does not exist") ErrInvalidFieldValue = errors.New("akara: invalid component field value") ErrComponentNotFound = errors.New("akara: entity does not have component") ErrComponentReference = errors.New("akara: component reference is stale") ErrSchemaVersion = errors.New("akara: schema version must increase") )
var ( ErrWorldClosed = errors.New("akara: world is closed") ErrEntityNotFound = errors.New("akara: entity does not exist") ErrEntityExists = errors.New("akara: entity already exists") ErrForeignComponent = errors.New("akara: component belongs to another world") ErrNilComponent = errors.New("akara: component handle is nil") ErrNilSystem = errors.New("akara: system is nil") ErrEntityIDExhausted = errors.New("akara: entity ID space exhausted") ErrSystemIDExhausted = errors.New("akara: system ID space exhausted") )
var ErrCommandBufferApplied = errors.New("akara: command buffer already applied")
Functions ¶
This section is empty.
Types ¶
type CommandBuffer ¶
type CommandBuffer struct {
// contains filtered or unexported fields
}
CommandBuffer defers structural world mutations until a safe barrier. It is safe to populate concurrently. Apply executes commands once in submission order.
func NewCommandBuffer ¶
func NewCommandBuffer() *CommandBuffer
NewCommandBuffer creates an empty structural command buffer.
func (*CommandBuffer) AddDynamic ¶
func (buffer *CommandBuffer) AddDynamic(store *DynamicStore, entity Entity, values map[string]any)
AddDynamic queues addition or replacement of a runtime-defined component. The values map is copied at submission time.
func (*CommandBuffer) Apply ¶
func (buffer *CommandBuffer) Apply() error
Apply executes every queued mutation once. It stops at the first error.
func (*CommandBuffer) CreateDynamic ¶
func (buffer *CommandBuffer) CreateDynamic(world *World, components map[*DynamicStore]map[string]any)
CreateDynamic queues creation of an entity with runtime-defined components. The new entity becomes visible after Apply; systems that need to communicate its identity should assign a stable application identifier component.
func (*CommandBuffer) Destroy ¶
func (buffer *CommandBuffer) Destroy(world *World, entity Entity)
Destroy queues entity destruction.
func (*CommandBuffer) Remove ¶
func (buffer *CommandBuffer) Remove(component ComponentType, entity Entity)
Remove queues removal of a component type from an entity.
type ComponentKey ¶
type ComponentKey struct {
// contains filtered or unexported fields
}
ComponentKey is an opaque, world-local component type identifier. Keys are stable for the lifetime of a World but must not be persisted.
type ComponentType ¶
type ComponentType interface {
// contains filtered or unexported methods
}
ComponentType is implemented by Store[T] and accepted by filter builders. Its private method prevents forged component keys.
type DynamicComponent ¶
type DynamicComponent struct {
// contains filtered or unexported fields
}
DynamicComponent is a checked reference to one runtime component instance. Replacing or removing the component invalidates prior references.
type DynamicStore ¶
type DynamicStore struct {
// contains filtered or unexported fields
}
DynamicStore owns instances of one runtime-defined schema.
func DynamicStores ¶ added in v0.1.2
func DynamicStores(world *World) []*DynamicStore
DynamicStores returns the world's runtime-defined component stores ordered by stable schema name. The returned slice is detached from the registry; stores remain owned by the world.
func GetDynamicStore ¶
func GetDynamicStore(world *World, name string) (*DynamicStore, bool)
GetDynamicStore looks up an already registered runtime component by name.
func RegisterSchema ¶
func RegisterSchema(world *World, schema Schema) (*DynamicStore, error)
RegisterSchema returns the canonical runtime-defined store for schema.Name. Re-registering an identical schema is idempotent; incompatible declarations return ErrSchemaConflict.
func (*DynamicStore) Add ¶
func (s *DynamicStore) Add(entity Entity) (*DynamicComponent, error)
Add creates a component with schema defaults or returns the existing reference.
func (*DynamicStore) Entities ¶
func (s *DynamicStore) Entities() []Entity
func (*DynamicStore) Get ¶
func (s *DynamicStore) Get(entity Entity) (*DynamicComponent, bool)
Get returns a generation-checked reference to entity's component.
func (*DynamicStore) Has ¶
func (s *DynamicStore) Has(entity Entity) bool
func (*DynamicStore) Key ¶
func (s *DynamicStore) Key() ComponentKey
func (*DynamicStore) Len ¶
func (s *DynamicStore) Len() int
func (*DynamicStore) Migrate ¶
func (s *DynamicStore) Migrate(next Schema, migrate Migration) error
Migrate transactionally replaces this store's schema and all of its component instances. The name must remain stable and the version must increase. If any conversion or validation fails, neither schema nor instances are changed.
func (*DynamicStore) Remove ¶
func (s *DynamicStore) Remove(entity Entity) bool
func (*DynamicStore) Schema ¶
func (s *DynamicStore) Schema() Schema
Schema returns a defensive copy of the registered schema.
func (*DynamicStore) Set ¶
func (s *DynamicStore) Set(entity Entity, values map[string]any) (*DynamicComponent, error)
Set replaces a component using schema defaults overridden by values.
type Entity ¶
type Entity uint64
Entity is a world-local entity identifier. Zero is never a valid entity.
type Field ¶
Field defines one named field and its optional default value. A nil default selects the zero value for Kind.
type FieldKind ¶
type FieldKind uint8
FieldKind is the storage and validation type of a runtime-defined field.
type FilterOption ¶
type FilterOption interface {
// contains filtered or unexported methods
}
FilterOption contributes one component constraint to a subscription filter.
func All ¶
func All(components ...ComponentType) FilterOption
All requires every supplied component type.
func Any ¶
func Any(components ...ComponentType) FilterOption
Any requires at least one supplied component type. Any() with no arguments adds no constraint.
func None ¶
func None(components ...ComponentType) FilterOption
None forbids every supplied component type.
type Schema ¶
Schema defines a runtime component type. Name is the stable application-level identity; Version is available to adapters implementing schema migration.
type Store ¶
type Store[T any] struct { // contains filtered or unexported fields }
Store owns the components of one Go type in one World. Register returns the canonical Store[T] for that type and world.
func Register ¶
Register returns the world's canonical store for T. T does not implement an interface and its zero value is used when Add creates a component. Register panics if world is nil or closed.
Example ¶
package main
import (
"fmt"
"github.com/gravestench/akara"
)
type examplePosition struct{ X, Y float64 }
func main() {
world := akara.NewWorld()
defer func() { _ = world.Close() }()
positions := akara.Register[examplePosition](world)
entity := world.MustCreateEntity()
position, _ := positions.Set(entity, examplePosition{X: 12, Y: 4})
fmt.Println(position.X, position.Y)
}
Output: 12 4
func (*Store[T]) Get ¶
Get returns entity's component. The returned pointer remains valid until the component is removed or replaced by Set; callers synchronize concurrent field access.
func (*Store[T]) Key ¶
func (s *Store[T]) Key() ComponentKey
Key returns the opaque component key used by filters and diagnostics.
type Subscription ¶
type Subscription struct {
// contains filtered or unexported fields
}
Subscription caches the archetypes matching an immutable component filter. Entity membership follows archetype transitions without per-entity filter work.
func (*Subscription) Close ¶
func (subscription *Subscription) Close() error
Close unregisters the subscription. It is safe to call repeatedly.
func (*Subscription) Entities ¶
func (subscription *Subscription) Entities() []Entity
Entities returns matching entity IDs in ascending order.
func (*Subscription) Ignore ¶
func (subscription *Subscription) Ignore(entity Entity)
Ignore excludes entity from this subscription until Include is called.
func (*Subscription) Include ¶
func (subscription *Subscription) Include(entity Entity)
Include removes an explicit exclusion.
func (*Subscription) Len ¶
func (subscription *Subscription) Len() int
Len returns the current number of matching entities.
type System ¶
System advances one part of a simulation. World.Update invokes systems sequentially in registration order.
type SystemFunc ¶
SystemFunc adapts a function to System.
type World ¶
type World struct {
// contains filtered or unexported fields
}
World owns entities, component stores, subscriptions, and deterministic systems.
func NewWorldWithContext ¶
NewWorldWithContext closes its world when ctx is canceled.
func (*World) ArchetypeCount ¶
ArchetypeCount returns the number of component combinations interned by the world, including the empty archetype.
func (*World) Close ¶
Close permanently closes the world. Existing component data remains readable, while mutation, subscription creation, and updates are rejected.
func (*World) CreateEntity ¶
CreateEntity creates an entity with no components.
func (*World) CreateEntityWithID ¶ added in v0.1.4
CreateEntityWithID creates an entity with an explicit non-zero identity. This is intended for trusted snapshot restoration. Normal simulation should use CreateEntity so identity allocation remains encapsulated by the world.
func (*World) DestroyEntity ¶
DestroyEntity immediately removes an entity, all its components, and all its subscription memberships.
func (*World) EntityExists ¶
EntityExists reports whether entity is live.
func (*World) MustCreateEntity ¶
MustCreateEntity creates an entity or panics. It is convenient during trusted world construction; runtime code should generally use CreateEntity.
func (*World) RemoveSystem ¶
RemoveSystem removes a registration and reports whether it existed. A system removed during Update will not run on the next update.
func (*World) Subscribe ¶
func (world *World) Subscribe(options ...FilterOption) (*Subscription, error)
Subscribe creates an independent subscription. An empty filter matches every live entity. Component handles from another world return ErrForeignComponent.
Example ¶
package main
import (
"fmt"
"github.com/gravestench/akara"
)
type examplePosition struct{ X, Y float64 }
type exampleVelocity struct{ X, Y float64 }
func main() {
world := akara.NewWorld()
defer func() { _ = world.Close() }()
positions := akara.Register[examplePosition](world)
velocities := akara.Register[exampleVelocity](world)
moving, _ := world.Subscribe(akara.All(positions, velocities))
defer func() { _ = moving.Close() }()
entity := world.MustCreateEntity()
_, _ = positions.Add(entity)
_, _ = velocities.Set(entity, exampleVelocity{X: 2})
fmt.Println(moving.Entities())
}
Output: [1]
func (*World) Update ¶
Update runs a stable snapshot of systems sequentially. Concurrent Update calls are serialized.
Example ¶
package main
import (
"fmt"
"time"
"github.com/gravestench/akara"
)
func main() {
world := akara.NewWorld()
defer func() { _ = world.Close() }()
_, _ = world.AddSystem(akara.SystemFunc(func(_ *akara.World, delta time.Duration) error {
fmt.Println(delta)
return nil
}))
_ = world.Update(16 * time.Millisecond)
}
Output: 16ms