Files

206 lines
4.3 KiB
Go

package runtime
import (
"slices"
"aego/contracts/scriptevents"
"aego/core/ids"
"aego/core/log"
"aego/engine"
"aego/engine/recorder"
"aego/scene"
"aego/script/abi"
"aego/script/bind"
"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)
_ scriptevents.Strings = (*Runtime)(nil)
_ scriptevents.Broadcaster = (*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.evNames = make([]string, len(list))
r.evRef = make([]bool, len(list))
r.listen = make([]bool, len(list))
for i, e := range list {
r.evIndex[e.Name] = i
r.evNames[i] = e.Name
r.evRef[i] = len(e.Params) > 0 && e.Params[0].Kind == abi.NodeRef
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) record(node scene.NodeHandle, i int, args []vm.Value, handled bool) {
rec := r.rec
if rec == nil || r.scene == nil {
return
}
id := r.scene.ID(node)
if !handled && !rec.Watching(id) {
rec.Count(recorder.Event)
return
}
var other ids.NodeID
if r.evRef[i] && len(args) > 0 {
if h := bind.NodeOf(args[0]); r.scene.IsAlive(h) {
other = r.scene.ID(h)
}
}
rec.Add(recorder.Event, id, other, r.evNames[i], "")
}
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) || len(args) != r.evSlots[i] {
return
}
_, owned := r.byNode[node]
if !owned {
owned = slices.Contains(r.spawn, node)
}
r.record(node, i, args, owned && r.listen[i])
if !owned || !r.listen[i] {
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()
}
func (r *Runtime) Intern(s string) vm.Value {
if r.m == nil {
return vm.Value{}
}
return r.m.Intern(s)
}
func (r *Runtime) Broadcast(ev scriptevents.EventID, args []vm.Value) {
if r.scene == nil || !r.Listens(ev) {
return
}
for _, h := range r.scene.PreOrder() {
if r.ListensOn(h, ev) {
r.Emit(h, ev, args)
}
}
}