Page 1 of 1

Simple and dynamic event system

Posted: Thu Jul 03, 2014 1:31 pm
by Julxzs
Inspired by the 'hook' library in Garry's Mod, my event system allows adding and removing of event listeners which store a function to be called when an event happens.

Code: Select all

if event then return end
event = {}
event.events = {}

-- Add an event listener
function event.listen(eventType, name, func)
	if not event.events[eventType] then
		event.events[eventType] = {}
	end
	if type(func) == "function" then
		event.events[eventType][name] = func
	end
end
-- Call all functions for an event
function event.call(eventType, ...)
	if event.events[eventType] then
		for k, v in pairs(event.events[eventType]) do
			v(...)
		end
	end
end
-- Remove an event listener
function event.remove(eventType, name)
	event.events[eventType][name] = nil
end

Re: Simple and dynamic event system

Posted: Thu Jul 03, 2014 1:33 pm
by Julxzs
The main.lua code to create events for all Löve callback functions:

Code: Select all

require("event")
function love.load(args) event.call("load", args) end
function love.update(dt) event.call("update", dt) end
function love.draw() event.call("draw") end
function love.keypressed(key, isRepeat) event.call("keypressed", key, isRepeat) end
function love.keyreleased(key) event.call("keyreleased", key) end
function love.textinput(text) event.call("textinput", text) end
function love.gamepadpressed(joystick, button) event.call("gamepadpressed", joystick, button) end
function love.joystickpressed(joystick, button) event.call("joystickpressed", joystick, button) end
function love.gamepadreleased(joystick, button) event.call("gamepadreleased", joystick, button) end
function love.joystickreleased(joystick, button) event.call("joystickreleased", joystick, button) end
function love.gamepadaxis(joystick, axis, value) event.call("gamepadaxis", joystick, axis, value) end
function love.joystickaxis(joystick, axis, value) event.call("joystickaxis", joystick, axis, value) end
function love.joystickhat(joystick, hat, direction) event.call("joystickhat", joystick, hat, direction) end
function love.mousepressed(x, y, button) event.call("mousepressed", x, y, button) end
function love.mousereleased(x, y, button) event.call("mousereleased", x, y, button) end
function love.mousefocus(focus) event.call("mousefocus", focus) end
function love.focus(focus) event.call("focus", focus) end
function love.resize(w, h) event.call("resize", w, h) end
function love.visible(visible) event.call("visible", visible) end
function love.joystickadded(joystick) event.call("joystickadded", joystick) end
function love.joystickremoved(joystick) event.call("joystickremoved", joystick) end
function love.quit() event.call("quit") end
function love.threaderror(thread, errorstr) event.call("threaderror", thread, errorstr) end

local errhand = love.errhand
function love.errhand(msg)
	errhand(msg)
	event.call("errhand", msg)
end
local run = love.run
function love.run()
	run()
	event.call("run")
end

Re: Simple and dynamic event system

Posted: Thu Jul 03, 2014 8:16 pm
by dusoft
Julxzs wrote:The main.lua code to create events for all Löve callback functions:

[snip...]
I am not sure function overloading is way to go...

Re: Simple and dynamic event system

Posted: Fri Jul 04, 2014 9:29 am
by ArchAngel075
Hmm i have a similar event system but i focus on making adding and removal easier by handling events and hooks as OOP object...
---

Code: Select all

EvCo = {
 Events = {},
}

local event = {}
function event:new()
 local o = {}
 setmetatable(o, self)
 self.__index = self
 return o
end

local Hook = {}
function Hook:new()
 local o = {}
 setmetatable(o, self)
 self.__index = self
 return o
end

function Hook:setFunction(func,selfed)
 self.selfed = selfed
 self.func = func
end

function Hook:setEvent(event)
 self.event = event
end

--[[
 Ignore the 
  CuCo._RESERVED.MOD_DATABASE_Host.BUILD
  CuCo._RESERVED.MOD_DATABASE_PROXY.BUILD
 code as its related to my assetModule and Engine...
--]]


function event:fire(...)
 local returns = {}
 for k,v in pairs(self.Hooks) do
  local ret
  if v.selfed then
   --check if the asset still exists, for auto cleaning dead assets!
   if not CuCo._RESERVED.MOD_DATABASE_PROXY.BUILD[v.selfed.address] and not
CuCo._RESERVED.MOD_DATABASE_HOST.BUILD[v.selfed.address] then
    self:removeHook(k)
   else
    ret = v.func(v.selfed,...)
   end
  else
   ret = v.func(...)
  end
  if ret then
   table.insert(returns,ret)
  end
 end
 return returns
end

function event:getOpenSlot()
 local slot
 local Address = self.Hooks
 local lowerLimit = 1
 local upperLimit = 2^32 --can really make this anything to 2^256 (scary though)
 for i = 1,upperLimit do
  if not Address[tostring(i)] then
   slot = tostring(i)
   break
  end
 end
 return tostring(slot)
end

function event:newHook(func,selfed)
 local slot = self:getOpenSlot()
 self.Hooks[slot] = Hook:new()
 self.Hooks[slot]:setFunction(func,selfed)
 self.Hooks[slot]:setEvent(self)
 return slot
end

function event:removeHook(hook)
 if self.Hooks[hook] then
  self.Hooks[hook] = nil
 end
end

function EvCo.getOpenEventSlot()
 local slot
 local Address = EvCo.Events
 local lowerLimit = 1
 local upperLimit = 2^32 --again, van make it very high
 for i = 1,upperLimit do
  if not Address[tostring(i)] then
   slot = tostring(i)
   break
  end
 end
 return tostring(slot)
end

function EvCo.newEvent(eventName)
 local slot = EvCo.getOpenEventSlot()
 local newEvent = event:new()
 newEvent.address = slot
 newEvent.Hooks = {}
 EvCo.Events[slot] = newEvent
 return newEvent
end
The way i handle events is make it so you can reference them directly, ie
Hook onto the default love.update() and create an event that fires when love.Update is called... thus you can throw functions into love.update at anytime?

Code: Select all

myEvent =  EvCo.newEvent()
function printThis(this)
 print(this)
end

myHook = myEvent:newHook(printThis)

love.update(dt)
 myEvent:fire(dt)
end
Also note that if you have ANOTHER OOP object and are hooking one of its functions to an event then you MUST pass the object aswell :

Code: Select all

---object definition :
local object = {}
function object:new()
 local o = {}
 setmetatable(o, self)
 self.__index = self
 return o
end

function object:printMyName()
 if self.name then 
  print("My name is '"..self.name.."'")
 else 
  print("I dont have a name!")
 end
end

---love functions
love.update(dt)
 myEvent:fire(dt)
end

function love.load()
 --create event
 myEvent =  EvCo.newEvent()

 --create objects :
 myObject = object:new()
 myObject.name = "SnoweeJohn"

 myOtherObject = object:new()
 myOtherObject.name = "YankAlWierd"

 --create hooks :
 myObjectsHook = myEvent:newHook(myObject.printMyName,myObject)
 myOtherObjectsHook = myEvent:newHook(myOtherObject.printMyName) --notice how we dont pass the object aswell!
end
Now update will cause both objects to call printMyName, EXCEPT :
myObject output --> "My name is 'SnoweeJohn'"
myOtherObject output --> "I dont have a name"

Im pretty sure i explained well?

Also id be happy to strip away any CurseEngine bits from my event system for sharing :)