• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[Lua] [Needs Fix] eventDispatcher

Level 4
Joined
Jun 26, 2013
Messages
48
Event Dispatcher implemented in Lua. It can work in the normal object mode or in the WLPM module mode (detected automatically).
One of the fundamental modules when building achitecture application.
Solves the high сohesion problem. Based on the observer pattern.

Installation

Copy code from /src and use eventDispatcher global
OR use WLPM and import("eventDispatcher")


wlpm install Indaxia/lua-eventDispatcher

Usage


Lua:
-- Subscription on the event
-- The event param is an object with "data", "name" and "stopPropagation" properties
-- You can set event.stopPropagation = true inside the callback to break current dispatch loop
eventDispatcher.on("my.event.a", function(event)
   print("Callback A 1: " .. event.data)
end)

-- ... another file ...

-- Dispatch with any data
eventDispatcher.dispatch("my.event.a", "Dispatch A 1 Data")


Lua:
if(_G["WM"] == nil) then WM = (function(m,h) h(nil,(function() end), (function(e) _G[m] = e end)) end) end -- WLPM MM fallback
-- Warcraft 3 eventDispatcher module by ScorpioT1000 / 2020
WM("eventDispatcher", function(import, export, exportDefault)
  local handlers = {}
  exportDefault({
    -- Subscribe to an event with the callback function that takes an event param
    -- The event param is an object with "data", "name" and "stopPropagation" properties
    -- You can set event.stopPropagation = true inside the callback
    -- to break current dispatch loop
    --- @param eventName string
    --- @param callback function
    on = function(eventName, callback)
      if(handlers[eventName] == nil) then
        handlers[eventName] = {}
      end
      table.insert(handlers[eventName], callback)
    end,
    -- Unsubscribe from the event. Specify the same function ref from on() to avoid every subscription removal
    --- @param eventName string
    --- @param specialCallback function
    off = function(eventName, specialCallback)
      local callbacks = handlers[eventName]
      if(callbacks ~= nil) then
        if(specialCallback ~= nil) then
          local counter = #callbacks
          for i = #callbacks, 1, -1 do
            if(callbacks[i] == specialCallback) then
              table.remove(callbacks, i)
            end
          end
        else
          handlers[eventName] = nil
        end
      end
    end,
    -- Dispatch the event and pass a data to it (optional)
    --- @param eventName string
    --- @param data
    dispatch = function(eventName, data)
      local callbacks = handlers[eventName]
      local event = {
        name = eventName,
        data = data,
        stopPropagation = false
      }
      if(callbacks == nil) then
        return
      end
      for i,callback in ipairs(callbacks) do
        callback(event)
        if(event.stopPropagation) then
          return
        end
      end
    end,
    -- Removes all handlers
    clear = function()
      handlers = {}
    end
  })
end)


Version History​

1.2: fixed potential desync (different behavior on different machines) in case of multiple listeners of one event


Refer the test file for details
 
Last edited:

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
Thank you. I have added the source code to your first post.

Pros:
  • Surprisingly short code for custom event building and execution, thanks to modular function parameters (e.g. passing nil as the second parameter in the 'off' method in order to remove an event entirely).
  • Passing the event name via string, rather than requiring event object creation, allows the implementer to not have to create event objects prior to running the event. If nothing is registered, the 'dispatch' method does nothing at all.
Cons:
  • There is no ability to define a priority for events to run in.
  • Removal requires full iteration of the registered stack.
  • Callbacks are using 'pairs' which can cause desyncs due to its asynchronous behavior.
The last point is a priority fix, the others are "nice-to-have"s.
 
Level 4
Joined
Jun 26, 2013
Messages
48
Thank you. I have added the source code to your first post.

Pros:
  • Surprisingly short code for custom event building and execution, thanks to modular function parameters (e.g. passing nil as the second parameter in the 'off' method in order to remove an event entirely).
  • Passing the event name via string, rather than requiring event object creation, allows the implementer to not have to create event objects prior to running the event. If nothing is registered, the 'dispatch' method does nothing at all.
Cons:
  • There is no ability to define a priority for events to run in.
  • Removal requires full iteration of the registered stack.
  • Callbacks are using 'pairs' which can cause desyncs due to its asynchronous behavior.
The last point is a priority fix, the others are "nice-to-have"s.
Removal doesn't require full iteration, this is a hashmap. Just iteration in single event type scope. But I don't think you're gonna build some enterprise stuff based on it
 
Top