-
Notifications
You must be signed in to change notification settings - Fork 28
Description
It appears that sometimes the callback for a query is called twice when dynamically adding and removing components from an entity. Is it perhaps a bug, or caused by misuse of the API?
Self-contained example
The self-contained example below hopefully demonstrates the problem. I've also put the example on Go Playground.
package main
import (
"log"
"github.com/yohamta/donburi"
"github.com/yohamta/donburi/filter"
)
type PositionData struct {
X int
Y int
}
var Position = donburi.NewComponentType[PositionData]()
var Player = donburi.NewTag()
type DoActionData struct {
action string
}
var DoAction = donburi.NewComponentType[DoActionData]()
func main() {
world := donburi.NewWorld()
playerEntity := world.Create(Player, Position)
log.Println("=== ROUND 1")
receivePlayerInput(world)
processActions(world, playerEntity)
log.Println("=== ROUND 2")
receivePlayerInput(world)
processActions(world, playerEntity)
}
func receivePlayerInput(world donburi.World) {
log.Println("receivePlayerInput")
query := donburi.NewQuery(filter.Contains(Player, Position))
log.Printf("receivePlayerInput query count: %d\n", query.Count(world))
query.Each(world, func(e *donburi.Entry) {
log.Printf("receivePlayerInput query callback for %s\n", e)
log.Printf("adding DoAction to %s\n", e)
donburi.Add(e, DoAction, &DoActionData{
action: "do thing",
})
})
}
func processActions(world donburi.World, playerEntity donburi.Entity) {
playerEntry := world.Entry(playerEntity)
log.Printf("removing DoAction from %s\n", playerEntry)
donburi.Remove[DoActionData](playerEntry, DoAction)
}Output
2024/01/23 00:03:30 === ROUND 1
2024/01/23 00:03:30 receivePlayerInput
2024/01/23 00:03:30 receivePlayerInput query count: 1
2024/01/23 00:03:30 receivePlayerInput query callback for Entry: {Entity: {id: 1, version: 0}, Layout: {, PositionData}, Valid: true}
2024/01/23 00:03:30 adding DoAction to Entry: {Entity: {id: 1, version: 0}, Layout: {, PositionData}, Valid: true}
2024/01/23 00:03:30 removing DoAction from Entry: {Entity: {id: 1, version: 0}, Layout: {, PositionData, DoActionData}, Valid: true}
2024/01/23 00:03:30 === ROUND 2
2024/01/23 00:03:30 receivePlayerInput
2024/01/23 00:03:30 receivePlayerInput query count: 1
2024/01/23 00:03:30 receivePlayerInput query callback for Entry: {Entity: {id: 1, version: 0}, Layout: {, PositionData}, Valid: true}
2024/01/23 00:03:30 adding DoAction to Entry: {Entity: {id: 1, version: 0}, Layout: {, PositionData}, Valid: true}
2024/01/23 00:03:30 receivePlayerInput query callback for Entry: {Entity: {id: 1, version: 0}, Layout: {, PositionData, DoActionData}, Valid: true}
2024/01/23 00:03:30 adding DoAction to Entry: {Entity: {id: 1, version: 0}, Layout: {, PositionData, DoActionData}, Valid: true}
2024/01/23 00:03:30 removing DoAction from Entry: {Entity: {id: 1, version: 0}, Layout: {, PositionData, DoActionData}, Valid: true}
Actual behavior
In ROUND 1 the query behaves as I would expect. There is 1 entity that has both the Player and Position components, the callback is called once and the DoAction component is added. It is then processed and the DoAction component is removed from the entity.
In ROUND 2 the entity should be back in the state it was before with only the Player and Position components. The query .Count() returns 1, which is correct. But the query now appears to call the callback twice for the same entity, but with different layouts. Once without the DoAction and once without the DoAction.
Expected behavior
I would expect ROUND 1 and ROUND 2 to behave the same because the component is added and removed in each round.