Skip to content

Commit

Permalink
Add a few minor helper functions on EntityCommand
Browse files Browse the repository at this point in the history
  • Loading branch information
unitoftime committed Mar 3, 2025
1 parent 48615c7 commit 8a8920a
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 9 deletions.
9 changes: 4 additions & 5 deletions bundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,10 @@ func readBundle[T Component](bun *Bundler) (T, bool) {
// return bun.Components[compId], true
// }

// func (bun *Bundler) Remove(comp Component) {
// compId := comp.id()
// bun.archMask.removeComponent(compId)
// bun.Set[compId] = true
// }
func (bun *Bundler) Remove(compId CompId) {
bun.archMask.removeComponent(compId)
bun.Set[compId] = false
}

// func WriteComponent[T any](bun *Bundler, comp T) {
// compId := nameTyped(comp)
Expand Down
54 changes: 50 additions & 4 deletions command.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const (
CmdTypeSpawn
CmdTypeWrite
CmdTypeTrigger
CmdTypeDelete
// CmdTypeCustom
)

Expand All @@ -29,17 +30,27 @@ type singleCmd struct {
event Event
}

func (c singleCmd) apply(world *World) {
func (c *singleCmd) apply(world *World) {
switch c.Type {
case CmdTypeNone:
// Do nothing, Command was probably cancelled
case CmdTypeSpawn:
// TODO: This could probably use a Spawn function which would be faster
c.bundler.Write(world, c.id)
if world.cmd.preWrite != nil {
world.cmd.preWrite(EntityCommand{c})
}
c.bundler.Write(world, c.id) // TODO: This could probably use a Spawn function which would be faster
case CmdTypeWrite:
if world.cmd.preWrite != nil {
world.cmd.preWrite(EntityCommand{c})
}
c.bundler.Write(world, c.id)
case CmdTypeTrigger:
world.Trigger(c.event, c.id)
case CmdTypeDelete:
if world.cmd.preDelete != nil {
world.cmd.preDelete(c.id)
}
Delete(world, c.id)
}
}

Expand All @@ -61,6 +72,13 @@ func (e EntityCommand) Cancel() {
e.cmd.Type = CmdTypeNone
}

// Removes the supplied component type from this entity command.
// TODO: Should this also remove it from the world? if it exists there?
func (e EntityCommand) Remove(comp Component) {
compId := comp.CompId()
e.cmd.bundler.Remove(compId)
}

func (e EntityCommand) Empty() bool {
return (e == EntityCommand{})
}
Expand Down Expand Up @@ -112,6 +130,8 @@ func ReadComp[T Component](e EntityCommand) (T, bool) {

type CommandQueue struct {
world *World
preWrite func(EntityCommand)
preDelete func(Id)
commands []singleCmd

currentBundlerIndex int
Expand Down Expand Up @@ -146,6 +166,11 @@ func unbundle(bundle Writer, bundler *Bundler) {
bundle.CompWrite(wd)
}

func remove(bundle Writer, bundler *Bundler) {
wd := W{bundler: bundler}
bundle.CompWrite(wd)
}

// func CmdSpawn[T Writer](c *CommandQueue, ub T) {
// bundler := c.NextBundler()
// unbundle(ub, bundler)
Expand Down Expand Up @@ -175,6 +200,14 @@ func (c *CommandQueue) SpawnEmpty() EntityCommand {
}
}

// // Pushes a command to delete the entity
// func (c *CommandQueue) Delete(id Id) {
// c.commands = append(c.commands, singleCmd{
// Type: CmdTypeDelete,
// id: id,
// })
// }

func (c *CommandQueue) Write(id Id) EntityCommand {
bundler := c.NextBundler()

Expand Down Expand Up @@ -209,9 +242,22 @@ func (c *CommandQueue) Trigger(event Event, ids ...Id) {
}
}

// Adds a prewrite function to be executed before every write or spawn command is executed
// Useful for ensuring entities are fully formed before pushing them into the ECS
func (c *CommandQueue) SetPrewrite(lambda func(EntityCommand)) {
c.preWrite = lambda
}

// // Adds a predelite function to be executed before every delete command is executed
// // Useful for ensuring any external datastructures get cleaned up when an entity is deleted
// func (c *CommandQueue) SetPredelete(lambda func(Id)) {
// c.preDelete = lambda
// }

func (c *CommandQueue) Execute() {
// Perform all commands
for i := range c.commands {
// Note: We must check length every time in case calling one command adds more commands
for i := 0; i < len(c.commands); i++ {
c.commands[i].apply(c.world)
}

Expand Down
7 changes: 7 additions & 0 deletions mask.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@ func (m *archetypeMask) addComponent(compId CompId) {
m[idx] |= (1 << offset)
}

func (m *archetypeMask) removeComponent(compId CompId) {
// Ranges: [0, 64), [64, 128), [128, 192), [192, 256)
idx := compId / 64
offset := compId - (64 * idx)
m[idx] &= ^(1 << offset)
}

// Performs a bitwise OR on the base mask `m` with the added mask `a`
func (m archetypeMask) bitwiseOr(a archetypeMask) archetypeMask {
for i := range m {
Expand Down
83 changes: 83 additions & 0 deletions mask_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,3 +175,86 @@ func TestBitwiseAnd(t *testing.T) {
}
}
}

func TestAddCompMask(t *testing.T) {
type testStruct struct {
input archetypeMask
add CompId
expected archetypeMask
}

tests := []testStruct{
{
input: buildArchMaskFromId(0),
add: 1,
expected: buildArchMaskFromId(0, 1),
},
{
input: buildArchMaskFromId(0, 1),
add: 3,
expected: buildArchMaskFromId(0, 1, 3),
},
{
input: buildArchMaskFromId(0, 1),
add: 63,
expected: buildArchMaskFromId(0, 1, 63),
},
{
input: buildArchMaskFromId(0, 1),
add: 1,
expected: buildArchMaskFromId(0, 1),
},
}

for _, test := range tests {
test.input.addComponent(test.add)
got := test.input
if got != test.expected {
t.Errorf("error")
}
}
}

func TestRemoveCompMask(t *testing.T) {
type testStruct struct {
input archetypeMask
remove CompId
expected archetypeMask
}

tests := []testStruct{
{
input: buildArchMaskFromId(0),
remove: 1,
expected: buildArchMaskFromId(0),
},
{
input: buildArchMaskFromId(0, 1),
remove: 63,
expected: buildArchMaskFromId(0, 1),
},
{
input: buildArchMaskFromId(0),
remove: 0,
expected: buildArchMaskFromId(),
},
{
input: buildArchMaskFromId(0, 1),
remove: 1,
expected: buildArchMaskFromId(0),
},
{
input: buildArchMaskFromId(0, 1),
remove: 0,
expected: buildArchMaskFromId(1),
},
}

for _, test := range tests {
test.input.removeComponent(test.remove)
got := test.input
if got != test.expected {
t.Errorf("error")
}
}
}

0 comments on commit 8a8920a

Please sign in to comment.