doodle/pkg/scripting/pubsub.go
Noah Petherbridge 7b3aec0fef Fix Two-State Blocks & Collision Detection
* Two-state Buttons now also subscribe to the state change message, so
  other on/off buttons in the same level update to match the state of
  the button that was hit.
* Add lock mutexes around the scripting engine to protect from
  concurrent event handlers.
2020-01-02 17:58:22 -08:00

79 lines
1.9 KiB
Go

package scripting
import (
"git.kirsle.net/apps/doodle/pkg/log"
"github.com/robertkrimen/otto"
)
// Message holds data being published from one script VM with information sent
// to the linked VMs.
type Message struct {
Name string
Args []interface{}
}
/*
RegisterPublishHooks adds the pub/sub hooks to a JavaScript VM.
This adds the global methods `Message.Subscribe(name, func)` and
`Message.Publish(name, args)` to the JavaScript VM's scope.
*/
func RegisterPublishHooks(s *Supervisor, vm *VM) {
// Goroutine to watch the VM's inbound channel and invoke Subscribe handlers
// for any matching messages received.
go func() {
for msg := range vm.Inbound {
vm.muSubscribe.RLock()
defer vm.muSubscribe.RUnlock()
if _, ok := vm.subscribe[msg.Name]; ok {
for _, callback := range vm.subscribe[msg.Name] {
callback.Call(otto.Value{}, msg.Args...)
}
}
}
}()
// Register the Message.Subscribe and Message.Publish functions.
vm.vm.Set("Message", map[string]interface{}{
"Subscribe": func(name string, callback otto.Value) {
vm.muSubscribe.Lock()
defer vm.muSubscribe.Unlock()
if !callback.IsFunction() {
log.Error("SUBSCRIBE(%s): callback is not a function", name)
return
}
if _, ok := vm.subscribe[name]; !ok {
vm.subscribe[name] = []otto.Value{}
}
vm.subscribe[name] = append(vm.subscribe[name], callback)
},
"Publish": func(name string, v ...interface{}) {
for _, channel := range vm.Outbound {
channel <- Message{
Name: name,
Args: v,
}
}
},
"Broadcast": func(name string, v ...interface{}) {
// Send the message to all actor VMs.
for _, toVM := range s.scripts {
if vm.Name == toVM.Name {
log.Debug("Broadcast(%s): skip to vm '%s' cuz it is the sender", name, toVM.Name)
continue
}
toVM.Inbound <- Message{
Name: name,
Args: v,
}
}
},
})
}