Skip to content

Commit

Permalink
Merge pull request #196 from oakmound/feature/v4-events
Browse files Browse the repository at this point in the history
event: overhaul api
  • Loading branch information
200sc committed Apr 2, 2022
2 parents 135b087 + 419b016 commit fc32cea
Show file tree
Hide file tree
Showing 144 changed files with 2,701 additions and 3,194 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/go.yml
Expand Up @@ -6,10 +6,10 @@ jobs:
runs-on: [self-hosted, linux, ARM64]
steps:

- name: Set up Go 1.17
- name: Set up Go 1.18
uses: actions/setup-go@v1
with:
go-version: 1.17
go-version: 1.18
id: go

- name: Check out code into the Go module directory
Expand All @@ -27,10 +27,10 @@ jobs:
runs-on: ubuntu-latest
steps:

- name: Set up Go 1.17
- name: Set up Go 1.18
uses: actions/setup-go@v1
with:
go-version: 1.17
go-version: 1.18
id: go

- name: Check out code into the Go module directory
Expand Down
5 changes: 4 additions & 1 deletion .gitignore
Expand Up @@ -14,4 +14,7 @@
# Output of the go coverage tool, specifically when used with LiteIDE
*.out

coverage.txt
coverage.txt

# Workspace configuration
.vscode
42 changes: 24 additions & 18 deletions collision/attachSpace.go
Expand Up @@ -19,27 +19,40 @@ type AttachSpace struct {
aSpace **Space
tree *Tree
offX, offY float64
binding event.Binding
}

func (as *AttachSpace) getAttachSpace() *AttachSpace {
return as
}

func (as *AttachSpace) CID() event.CallerID {
return (*as.aSpace).CID
}

var _ attachSpace = &AttachSpace{}

type attachSpace interface {
event.Caller
getAttachSpace() *AttachSpace
}

// Attach attaches v to the given space with optional x,y offsets. See AttachSpace.
func Attach(v physics.Vector, s *Space, tree *Tree, offsets ...float64) error {
if t, ok := s.CID.E().(attachSpace); ok {
return AttachWithBus(v, s, tree, event.DefaultBus, offsets...)
}

func AttachWithBus(v physics.Vector, s *Space, tree *Tree, bus event.Handler, offsets ...float64) error {
en := bus.GetCallerMap().GetEntity(s.CID)
if t, ok := en.(attachSpace); ok {
as := t.getAttachSpace()
as.aSpace = &s
as.follow = v
as.tree = tree
if as.tree == nil {
as.tree = DefaultTree
}
s.CID.Bind(event.Enter, attachSpaceEnter)
as.binding = event.Bind(bus, event.Enter, t, attachSpaceEnter)
if len(offsets) > 0 {
as.offX = offsets[0]
if len(offsets) > 1 {
Expand All @@ -54,30 +67,23 @@ func Attach(v physics.Vector, s *Space, tree *Tree, offsets ...float64) error {
// Detach removes the attachSpaceEnter binding from an entity composed with
// AttachSpace
func Detach(s *Space) error {
en := s.CID.E()
if _, ok := en.(attachSpace); ok {
event.UnbindBindable(
event.UnbindOption{
Event: event.Event{
Name: event.Enter,
CallerID: s.CID,
},
Fn: attachSpaceEnter,
},
)
return DetachWithBus(s, event.DefaultBus)
}

func DetachWithBus(s *Space, bus event.Handler) error {
en := bus.GetCallerMap().GetEntity(s.CID)
if as, ok := en.(attachSpace); ok {
as.getAttachSpace().binding.Unbind()
return nil
}
return errors.New("this space's entity is not composed of AttachSpace")
}

func attachSpaceEnter(id event.CID, _ interface{}) int {
as := id.E().(attachSpace).getAttachSpace()
func attachSpaceEnter(asIface attachSpace, _ event.EnterPayload) event.Response {
as := asIface.(attachSpace).getAttachSpace()
x, y := as.follow.X()+as.offX, as.follow.Y()+as.offY
if x != (*as.aSpace).X() ||
y != (*as.aSpace).Y() {

// If this was a nil pointer it would have already crashed but as of release 2.2.0
// this could error from the space to delete not existing in the rtree.
as.tree.UpdateSpace(x, y, (*as.aSpace).GetW(), (*as.aSpace).GetH(), *as.aSpace)
}
return 0
Expand Down
23 changes: 11 additions & 12 deletions collision/attachSpace_test.go
@@ -1,6 +1,7 @@
package collision

import (
"fmt"
"testing"
"time"

Expand All @@ -12,24 +13,22 @@ type aspace struct {
AttachSpace
}

func (as *aspace) Init() event.CID {
return event.NextID(as)
}

func TestAttachSpace(t *testing.T) {
Clear()
go event.ResolveChanges()
b := event.NewBus(event.NewCallerMap())
go func() {
for {
<-time.After(5 * time.Millisecond)
<-event.TriggerBack(event.Enter, nil)
<-event.TriggerOn(b, event.Enter, event.EnterPayload{})
}
}()
as := aspace{}
as := &aspace{}
cid := b.GetCallerMap().Register(as)
v := physics.NewVector(0, 0)
s := NewSpace(100, 100, 10, 10, as.Init())
s := NewSpace(100, 100, 10, 10, cid)
Add(s)
err := Attach(v, s, nil, 4, 4)
fmt.Println(s.CID)
err := AttachWithBus(v, s, nil, b, 4, 4)
if err != nil {
t.Fatalf("attach failed: %v", err)
}
Expand All @@ -42,7 +41,7 @@ func TestAttachSpace(t *testing.T) {
t.Fatalf("expected attached space to have y of 9, was %v", s.Y())
}

err = Detach(s)
err = DetachWithBus(s, b)
if err != nil {
t.Fatalf("detach failed: %v", err)
}
Expand All @@ -60,10 +59,10 @@ func TestAttachSpace(t *testing.T) {
s = NewUnassignedSpace(0, 0, 1, 1)
err = Attach(v, s, nil)
if err == nil {
t.Fatalf("unassinged space attach should have failed: %v", err)
t.Fatalf("unassigned space attach should have failed: %v", err)
}
err = Detach(s)
if err == nil {
t.Fatalf("unassinged space detach should have failed: %v", err)
t.Fatalf("unassigned space detach should have failed: %v", err)
}
}
2 changes: 1 addition & 1 deletion collision/filter.go
Expand Up @@ -43,7 +43,7 @@ func Without(tossFn func(*Space) bool) Filter {
}

// WithoutCIDs will return no spaces with a CID in the input
func WithoutCIDs(cids ...event.CID) Filter {
func WithoutCIDs(cids ...event.CallerID) Filter {
return Without(func(s *Space) bool {
for _, c := range cids {
if s.CID == c {
Expand Down
64 changes: 32 additions & 32 deletions collision/onCollision.go
Expand Up @@ -22,6 +22,10 @@ func (cp *Phase) getCollisionPhase() *Phase {
return cp
}

func (cp *Phase) CID() event.CallerID {
return cp.OnCollisionS.CID
}

type collisionPhase interface {
getCollisionPhase() *Phase
}
Expand All @@ -31,13 +35,12 @@ type collisionPhase interface {
// entities begin to collide or stop colliding with the space.
// If tree is nil, it uses DefTree
func PhaseCollision(s *Space, tree *Tree) error {
return PhaseCollisionWithBus(s, tree, event.DefaultBus, event.DefaultCallerMap)
return PhaseCollisionWithBus(s, tree, event.DefaultBus)
}

// PhaseCollisionWithBus allows for a non-default bus and non-default entity mapping
// in a phase collision binding.
func PhaseCollisionWithBus(s *Space, tree *Tree, bus event.Handler, entities *event.CallerMap) error {
en := entities.GetEntity(s.CID)
// PhaseCollisionWithBus allows for a non-default bus in a phase collision binding.
func PhaseCollisionWithBus(s *Space, tree *Tree, bus event.Handler) error {
en := bus.GetCallerMap().GetEntity(s.CID)
if cp, ok := en.(collisionPhase); ok {
oc := cp.getCollisionPhase()
oc.OnCollisionS = s
Expand All @@ -46,46 +49,43 @@ func PhaseCollisionWithBus(s *Space, tree *Tree, bus event.Handler, entities *ev
if oc.tree == nil {
oc.tree = DefaultTree
}
bus.Bind(event.Enter, s.CID, phaseCollisionEnter(entities))
bus.UnsafeBind(event.Enter.UnsafeEventID, s.CID, phaseCollisionEnter)
return nil
}
return errors.New("This space's entity does not implement collisionPhase")
}

// CollisionStart/Stop: when a PhaseCollision entity starts/stops touching some label.
// Payload: (Label) the label the entity has started/stopped touching
const (
Start = "CollisionStart"
Stop = "CollisionStop"
var (
Start = event.RegisterEvent[Label]()
Stop = event.RegisterEvent[Label]()
)

func phaseCollisionEnter(entities *event.CallerMap) func(id event.CID, nothing interface{}) int {
return func(id event.CID, nothing interface{}) int {
e := entities.GetEntity(id).(collisionPhase)
oc := e.getCollisionPhase()
func phaseCollisionEnter(id event.CallerID, handler event.Handler, _ interface{}) event.Response {
e := handler.GetCallerMap().GetEntity(id).(collisionPhase)
oc := e.getCollisionPhase()

// check hits
hits := oc.tree.Hits(oc.OnCollisionS)
newTouching := map[Label]bool{}
// check hits
hits := oc.tree.Hits(oc.OnCollisionS)
newTouching := map[Label]bool{}

// if any are new, trigger on collision
for _, h := range hits {
l := h.Label
if _, ok := oc.Touching[l]; !ok {
id.TriggerBus(Start, l, oc.bus)
}
newTouching[l] = true
// if any are new, trigger on collision
for _, h := range hits {
l := h.Label
if _, ok := oc.Touching[l]; !ok {
event.TriggerForCallerOn(oc.bus, id, Start, l)
}
newTouching[l] = true
}

// if we lost any, trigger off collision
for l := range oc.Touching {
if _, ok := newTouching[l]; !ok {
id.TriggerBus(Stop, l, oc.bus)
}
// if we lost any, trigger off collision
for l := range oc.Touching {
if _, ok := newTouching[l]; !ok {
event.TriggerForCallerOn(handler, id, Stop, l)
}
}

oc.Touching = newTouching
oc.Touching = newTouching

return 0
}
return 0
}
41 changes: 18 additions & 23 deletions collision/onCollision_test.go
Expand Up @@ -12,55 +12,50 @@ type cphase struct {
callers *event.CallerMap
}

func (cp *cphase) Init() event.CID {
return cp.callers.NextID(cp)
}

func TestCollisionPhase(t *testing.T) {
callers := event.NewCallerMap()
bus := event.NewBus(callers)
go bus.ResolveChanges()
b := event.NewBus(event.NewCallerMap())
go func() {
for {
<-time.After(5 * time.Millisecond)
<-bus.TriggerBack(event.Enter, nil)
<-event.TriggerOn(b, event.Enter, event.EnterPayload{})
}
}()
cp := cphase{
callers: callers,
}
cid := cp.Init()
cp := &cphase{}
cid := b.GetCallerMap().Register(cp)
s := NewSpace(10, 10, 10, 10, cid)
tree := NewTree()
err := PhaseCollisionWithBus(s, tree, bus, callers)
err := PhaseCollisionWithBus(s, tree, b)
if err != nil {
t.Fatalf("phase collision failed: %v", err)
}
var active bool
bus.Bind("CollisionStart", cid, func(event.CID, interface{}) int {
active = true
activeCh := make(chan bool, 5)
b1 := event.Bind(b, Start, cp, func(_ *cphase, _ Label) event.Response {
activeCh <- true
return 0
})
bus.Bind("CollisionStop", cid, func(event.CID, interface{}) int {
active = false
b2 := event.Bind(b, Stop, cp, func(_ *cphase, _ Label) event.Response {
activeCh <- false
return 0
})

<-b1.Bound
<-b2.Bound
s2 := NewLabeledSpace(15, 15, 10, 10, 5)
tree.Add(s2)
time.Sleep(200 * time.Millisecond)
if !active {
if active := <-activeCh; !active {
t.Fatalf("collision should be active")
}

tree.Remove(s2)
time.Sleep(200 * time.Millisecond)
if active {
if active := <-activeCh; active {
t.Fatalf("collision should be inactive")
}
}

func TestPhaseCollision_Unembedded(t *testing.T) {
t.Parallel()
s3 := NewSpace(10, 10, 10, 10, 5)
err = PhaseCollision(s3, nil)
err := PhaseCollision(s3, nil)
if err == nil {
t.Fatalf("phase collision should have failed")
}
Expand Down
4 changes: 2 additions & 2 deletions collision/ray/castFilter.go
Expand Up @@ -46,7 +46,7 @@ func IgnoreLabels(ls ...collision.Label) CastOption {
}

// AcceptIDs is equivalent to AcceptLabels, but for CIDs.
func AcceptIDs(ids ...event.CID) CastOption {
func AcceptIDs(ids ...event.CallerID) CastOption {
return AddFilter(func(s *collision.Space) bool {
for _, id := range ids {
if s.CID == id {
Expand All @@ -58,7 +58,7 @@ func AcceptIDs(ids ...event.CID) CastOption {
}

// IgnoreIDs is equivalent to IgnoreLabels, but for CIDs.
func IgnoreIDs(ids ...event.CID) CastOption {
func IgnoreIDs(ids ...event.CallerID) CastOption {
return AddFilter(func(s *collision.Space) bool {
for _, id := range ids {
if s.CID == id {
Expand Down
2 changes: 1 addition & 1 deletion collision/ray/castLimit.go
Expand Up @@ -47,7 +47,7 @@ func StopAtLabel(ls ...collision.Label) CastOption {

// StopAtID will cause a caster to cease casting as soon as it
// hits one of the input CIDs.
func StopAtID(ids ...event.CID) CastOption {
func StopAtID(ids ...event.CallerID) CastOption {
return AddLimit(func(ps []collision.Point) bool {
z := ps[len(ps)-1].Zone
for _, id := range ids {
Expand Down

0 comments on commit fc32cea

Please sign in to comment.