diff --git a/dev-assets/doodads/on-off/state-block-blue.js b/dev-assets/doodads/on-off/state-block-blue.js index 0b287e3..f09f5cc 100644 --- a/dev-assets/doodads/on-off/state-block-blue.js +++ b/dev-assets/doodads/on-off/state-block-blue.js @@ -7,15 +7,10 @@ function main() { Message.Subscribe("broadcast:state-change", function(newState) { state = !newState; - console.warn("BLUE BLOCK Received state=%+v, set mine to %+v", newState, state); // Layer 0: ON // Layer 1: OFF - if (state) { - Self.ShowLayer(0); - } else { - Self.ShowLayer(1); - } + Self.ShowLayer(state ? 0 : 1); }); Events.OnCollide(function(e) { diff --git a/dev-assets/doodads/on-off/state-block-orange.js b/dev-assets/doodads/on-off/state-block-orange.js index 634d5e8..dfea8b7 100644 --- a/dev-assets/doodads/on-off/state-block-orange.js +++ b/dev-assets/doodads/on-off/state-block-orange.js @@ -7,15 +7,10 @@ function main() { Message.Subscribe("broadcast:state-change", function(newState) { state = newState; - console.warn("ORANGE BLOCK Received state=%+v, set mine to %+v", newState, state); // Layer 0: OFF // Layer 1: ON - if (state) { - Self.ShowLayer(1); - } else { - Self.ShowLayer(0); - } + Self.ShowLayer(state ? 1 : 0); }); Events.OnCollide(function(e) { diff --git a/dev-assets/doodads/on-off/state-button.js b/dev-assets/doodads/on-off/state-button.js index 51a2af3..8770b08 100644 --- a/dev-assets/doodads/on-off/state-button.js +++ b/dev-assets/doodads/on-off/state-button.js @@ -1,14 +1,22 @@ // State Block Control Button + +// Button is "OFF" by default. +var state = false; + function main() { - console.log("%s initialized!", Self.Doodad.Title); + console.log("%s ID '%s' initialized!", Self.Doodad.Title, Self.ID()); Self.SetHitbox(0, 0, 33, 33); // When the button is activated, don't keep toggling state until we're not // being touched again. var colliding = false; - // Button is "OFF" by default. - var state = false; + // If we receive a state change event from a DIFFERENT on/off button, update + // ourself to match the state received. + Message.Subscribe("broadcast:state-change", function(value) { + state = value; + showSprite(); + }); Events.OnCollide(function(e) { if (colliding) { @@ -17,24 +25,16 @@ function main() { // Only trigger for mobile characters. if (e.Actor.IsMobile()) { - console.log("Mobile actor %s touched the on/off button!", e.Actor.Actor.Filename); - // Only activate if touched from the bottom or sides. if (e.Overlap.Y === 0) { - console.log("... but touched the top!"); return false; } colliding = true; - console.log(" -> emit state change"); state = !state; Message.Broadcast("broadcast:state-change", state); - if (state) { - Self.ShowLayer(1); - } else { - Self.ShowLayer(0); - } + showSprite(); } // Always a solid button. @@ -45,3 +45,12 @@ function main() { colliding = false; }) } + +// Update the active layer based on the current button state. +function showSprite() { + if (state) { + Self.ShowLayer(1); + } else { + Self.ShowLayer(0); + } +} diff --git a/pkg/doodle.go b/pkg/doodle.go index 7032a5e..b097e53 100644 --- a/pkg/doodle.go +++ b/pkg/doodle.go @@ -99,8 +99,6 @@ func (d *Doodle) Run() error { // Set up the default scene. if d.Scene == nil { - // d.Goto(&GUITestScene{}) - // d.NewMap() d.Goto(&MainScene{}) } diff --git a/pkg/scripting/events.go b/pkg/scripting/events.go index b6e725e..04eaf9f 100644 --- a/pkg/scripting/events.go +++ b/pkg/scripting/events.go @@ -2,6 +2,7 @@ package scripting import ( "errors" + "sync" "git.kirsle.net/go/render/event" "github.com/robertkrimen/otto" @@ -25,6 +26,7 @@ var ( // Events API for Doodad scripts. type Events struct { registry map[string][]otto.Value + lock sync.RWMutex } // NewEvents initializes the Events API. @@ -70,6 +72,9 @@ func (e *Events) register(name string, callback otto.Value) otto.Value { return otto.Value{} // TODO } + e.lock.Lock() + defer e.lock.Unlock() + if _, ok := e.registry[name]; !ok { e.registry[name] = []otto.Value{} } @@ -81,6 +86,9 @@ func (e *Events) register(name string, callback otto.Value) otto.Value { // Run an event handler. Returns an error only if there was a JavaScript error // inside the function. If there are no event handlers, just returns nil. func (e *Events) run(name string, args ...interface{}) error { + e.lock.RLock() + defer e.lock.RUnlock() + if _, ok := e.registry[name]; !ok { return nil } diff --git a/pkg/scripting/pubsub.go b/pkg/scripting/pubsub.go index 407f22e..8324698 100644 --- a/pkg/scripting/pubsub.go +++ b/pkg/scripting/pubsub.go @@ -23,7 +23,9 @@ func RegisterPublishHooks(s *Supervisor, vm *VM) { // for any matching messages received. go func() { for msg := range vm.Inbound { - log.Warn("vm: %s msg: %+v", vm.Name, msg) + 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...) @@ -35,6 +37,9 @@ func RegisterPublishHooks(s *Supervisor, vm *VM) { // 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 @@ -57,8 +62,13 @@ func RegisterPublishHooks(s *Supervisor, vm *VM) { "Broadcast": func(name string, v ...interface{}) { // Send the message to all actor VMs. - for _, vm := range s.scripts { - vm.Inbound <- Message{ + 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, } diff --git a/pkg/scripting/vm.go b/pkg/scripting/vm.go index 21f78f5..d3cfdef 100644 --- a/pkg/scripting/vm.go +++ b/pkg/scripting/vm.go @@ -3,6 +3,7 @@ package scripting import ( "fmt" "reflect" + "sync" "time" "git.kirsle.net/apps/doodle/pkg/log" @@ -25,9 +26,10 @@ type VM struct { // Each VM also has an array of Outbound channels which map to the Inbound // channel of the VMs it is linked to, for pushing out Message.Publish() // messages. - Inbound chan Message - Outbound []chan Message - subscribe map[string][]otto.Value // Subscribed message handlers by name. + Inbound chan Message + Outbound []chan Message + subscribe map[string][]otto.Value // Subscribed message handlers by name. + muSubscribe sync.RWMutex vm *otto.Otto diff --git a/pkg/uix/actor_collision.go b/pkg/uix/actor_collision.go index a2fda38..9a5f867 100644 --- a/pkg/uix/actor_collision.go +++ b/pkg/uix/actor_collision.go @@ -103,6 +103,12 @@ func (w *Canvas) loopActorCollision() error { var collidingActors = map[string]string{} for tuple := range collision.BetweenBoxes(boxes) { a, b := w.actors[tuple.A], w.actors[tuple.B] + + // If neither actor is mobile, don't run collision handlers. + if !(a.IsMobile() || b.IsMobile()) { + continue + } + collidingActors[a.ID()] = b.ID() // Call the OnCollide handler for A informing them of B's intersection. @@ -219,7 +225,7 @@ func (w *Canvas) loopActorCollision() error { InHitbox: info.Overlap.Intersects(a.Hitbox()), Settled: true, }); err != nil && err != scripting.ErrReturnFalse { - log.Error(err.Error()) + log.Error("VM(%s).RunCollide: %s", a.ID(), err.Error()) } } }