153 lines
3.0 KiB
Go
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()
|
|
}
|