garrysmod
garrysmod copied to clipboard
Implement Listen-Only Hooks
This pull request wants to add these 3 functions:
hook.Listen( string eventName, any name, bool isPostHook, function func )
hook.Forget( string eventName, any name, bool isPostHook )
hook.GetListenTable()
There is also a few micro-optimizations done to hook.Add
and hook.Remove
with their type checking.
Listen-only hooks are hooks that will call regardless if one had returned a value. These hooks will allow us to hook onto events without having to worry about a hook being ran before ours, returning a value, and completely skipping our logic, or about weird addons returning values when they shouldn't.
Example:
hook.Add('PlayerFootstep', 'SmallPuffOnStep', function(ply, pos)
local ef = EffectData()
ef:SetOrigin(pos)
util.Effect('ElectricSpark', ef)
end)
Let's say I wanted to have an effect happen every time I take a step. We use the PlayerFootstep event, and for this example, I made it create small puff balls of smoke at the position of every footstep you take. However, addons can return true to suppress the step sound, and unfortunately for us, the order in which hooks are called in is random. That means such hooks by those addons could run before ours, completely blocking our fancy little effect. Instead, we can use hook.Listen
like so:
hook.Listen('PlayerFootstep', 'SmallPuffOnStep', false, function(ply, pos)
local ef = EffectData()
ef:SetOrigin(pos)
util.Effect('ElectricSpark', ef)
end)
That way, even if an addon returns a value in their hook, we still have our fancy smoke effect on each step.
Setting the third argument to true will make the listen-only hook be called after all of the normal hooks are called. This can be used to make sure your logic is only called if a hook return didn't happen. For example, let's say I wanted to track how many times the player toggled their flashlight. I can do this like so
hook.Add('PlayerSwitchFlashlight', 'PlayerFlashlightTracker9000', function(ply)
ply.FlashlightToggleAmount = (ply.FlashlightToggleAmount or 0) + 1
end)
Addons can block us from toggling our flashlight by returning false. That mean's there is a chance our logic is completely skipped. So, we use hook.Listen
. However, listen-only hooks are called before normal hooks, so there's a chance we add onto that variable, even if the toggle was blocked or not. So, by setting the third argument to true, we can make that listen-only hook be called after the rest, that way we can make sure we are counting unblocked flashlight changes, with no worry of our logic being randomly skipped.
hook.Listen('PlayerSwitchFlashlight', 'PlayerFlashlightTracker9000', true, function(ply)
ply.FlashlightToggleAmount = (ply.FlashlightToggleAmount or 0) + 1
end)
Listen and post listen hooks are put in two separate tables. Listen only hooks can also have objects as identifiers, just like regular hooks.
You can remove listen-only hooks like so:
hook.Forget("EventName", "Identifier")
hook.Forget("EventName", "Identifier", true) -- removes post listen-hook
You can get the listen-only hook tables like so:
local listeners, listenersPost = hook.GetListenTable()
PrintTable(listeners)
PrintTable(listenersPost)
I am awaiting feedback.