Files
aego/script/runtime/events.go
T

153 lines
3.0 KiB
Go

package runtime
import (
"aego/contracts/scriptevents"
"aego/core/log"
"aego/engine"
"aego/scene"
"aego/script/vm"
)
const eventQueue = 1024
type queued struct {
node scene.NodeHandle
ev uint16
off int32
n int32
}
var (
_ scriptevents.Sink = (*Runtime)(nil)
_ scriptevents.Listener = (*Runtime)(nil)
)
func (t *scriptType) event(i int) *vm.Func {
if i >= len(t.events) {
return nil
}
return t.events[i]
}
func (r *Runtime) linkEvents() {
tab := r.opt.Bindings.Table()
list := tab.EventList()
r.evIndex = make(map[string]int, len(list))
r.evSlots = make([]int, len(list))
r.listen = make([]bool, len(list))
for i, e := range list {
r.evIndex[e.Name] = i
n := 0
for _, p := range e.Params {
n += tab.Slots(p)
}
r.evSlots[i] = n
}
if cap(r.evq) == 0 {
r.evq = make([]queued, 0, eventQueue)
r.evargs = make([]vm.Value, 0, eventQueue*4)
}
}
func (r *Runtime) relisten() {
clear(r.listen)
for _, t := range r.types {
for i, f := range t.events {
if f != nil && i < len(r.listen) {
r.listen[i] = true
}
}
}
}
func (r *Runtime) Listens(ev scriptevents.EventID) bool {
i := int(ev)
return i < len(r.listen) && r.listen[i]
}
func (r *Runtime) ListensOn(node scene.NodeHandle, ev scriptevents.EventID) bool {
i := int(ev)
if i >= len(r.listen) || !r.listen[i] {
return false
}
for _, id := range r.byNode[node] {
in := &r.inst[id-1]
if in.state == Running && in.typ.event(i) != nil {
return true
}
}
return false
}
func (r *Runtime) Emit(node scene.NodeHandle, ev scriptevents.EventID, args []vm.Value) {
i := int(ev)
if r.m == nil || i >= len(r.listen) || !r.listen[i] || len(args) != r.evSlots[i] {
return
}
if _, ok := r.byNode[node]; !ok {
return
}
if (len(r.evq) == cap(r.evq) || len(r.evargs)+len(args) > cap(r.evargs)) && !r.evWarned {
r.evWarned = true
r.log.Warn("script event queue grew past its preallocated size", log.F("events", len(r.evq)))
}
off := len(r.evargs)
r.evargs = append(r.evargs, args...)
r.evq = append(r.evq, queued{node: node, ev: uint16(ev), off: int32(off), n: int32(len(args))})
}
func (r *Runtime) Flush() {
if r.m == nil || r.scene == nil || r.m.Busy() || len(r.evq) == 0 {
return
}
n := len(r.evq)
for k := 0; k < n; k++ {
q := r.evq[k]
args := r.evargs[q.off : q.off+q.n]
for _, id := range r.byNode[q.node] {
in := &r.inst[id-1]
if in.state != Running {
continue
}
if f := in.typ.event(int(q.ev)); f != nil {
r.evCount++
r.call(id, f, args)
}
}
}
r.dropEvents(n)
}
func (r *Runtime) dropEvents(n int) {
rest := len(r.evq) - n
if rest <= 0 {
r.evq, r.evargs = r.evq[:0], r.evargs[:0]
return
}
base := r.evq[n].off
k := copy(r.evargs, r.evargs[base:])
r.evargs = r.evargs[:k]
copy(r.evq, r.evq[n:])
r.evq = r.evq[:rest]
for i := range r.evq {
r.evq[i].off -= base
}
}
func (r *Runtime) Pending() int {
return len(r.evq)
}
func (r *Runtime) EventCalls() uint64 {
return r.evCount
}
func (r *Runtime) DispatchEvents(ec *engine.Context) {
if !r.begin(ec) {
return
}
r.ctx.Dt = float32(ec.Dt)
r.Flush()
r.end()
}