Thread Tools Display Modes
04-08-21, 02:00 PM   #1
LudiusMaximus
A Rage Talon Dragon Guard
 
LudiusMaximus's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2018
Posts: 320
AceEvent-3.0 tricked into registering non-existent events??

In my addon DynamicCam, the user can create custom situations including lists of events when to check the situation conditions.

If the user enters non-existent events, I want to show them my own error message. So I am using pcall() around RegisterEvent() to catch the errors of non-existent events.

This seemed to work quite well. But during the addon startup the event-registering function may be called more than once. It seems that with a second try, the non-existing events are registered!

Here is a minimal working example:

Lua Code:
  1. local folderName = ...
  2. DynamicCam = LibStub("AceAddon-3.0"):NewAddon(folderName, "AceEvent-3.0")
  3.  
  4. function DynamicCam:EventHandler(event)
  5.   print("EventHandler not doing anything", event)
  6. end
  7.  
  8.  
  9. local event = "nonExistingEvent"
  10.  
  11. -- First try fails with error message as expected.
  12. local result = {pcall(function() DynamicCam:RegisterEvent(event, "EventHandler") end)}
  13. if result[1] == false then
  14.   print("Did not register event:", event)
  15.   print("Error:", result[2])
  16. else
  17.   print("Registered event:", event)
  18. end
  19.  
  20. -- Second try succeeds!!
  21. print("Try again!")
  22. result = {pcall(function() DynamicCam:RegisterEvent(event, "EventHandler") end)}
  23. if result[1] == false then
  24.   print("Did not register event:", event)
  25.   print("Error:", result[2])
  26. else
  27.   print("Registered event:", event)
  28. end
  29.  
  30. -- Will lead to error, trying to unregister nonExistingEvent:
  31. DynamicCam:UnregisterAllEvents()


Why is the second try not exactly like the first one?
If I use RegisterEvent(event) instead of RegisterEvent(event, "EventHandler"), it is like that!
__________________
~ Be the change you want to see in the world... of warcraft interface! ~
  Reply With Quote
04-09-21, 05:19 PM   #2
LudiusMaximus
A Rage Talon Dragon Guard
 
LudiusMaximus's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2018
Posts: 320
If nobody has an answer to this strange behaviour of AceEvent-3.0,
maybe someone can tell me how to let my code check itself if an event exists?
Thanks!
__________________
~ Be the change you want to see in the world... of warcraft interface! ~
  Reply With Quote
04-09-21, 08:56 PM   #3
kurapica.igas
A Chromatic Dragonspawn
Join Date: Aug 2011
Posts: 152
When use a frame to register non-existed event, it'd throw an error, that's why the first try failed.

I guess the AceEvents handle it like

Lua Code:
  1. local events = {}
  2. local frame = CreateFrame("Frame")
  3.  
  4. frame:SetScript("OnEvent", function(self, event, ...)
  5.     for frm, handler in pairs(events[event]) do
  6.         frm[handler](frm, event, ...)
  7.     end
  8. end)
  9.  
  10. function addon:RegisterEvent(event, handler)
  11.     if not events[event] then
  12.         events[event] = {}
  13.  
  14.         -- Where the error raised when event not existed
  15.         -- But the events[event] already existed, so no error next time
  16.         frame:RegisterEvent(event)
  17.     end
  18.  
  19.     -- register the handler to the addon
  20.     events[event][self] = handler
  21.    
  22.     return true
  23. end

So, you can make a version of your own, it's not complicated
  Reply With Quote
04-10-21, 03:14 AM   #4
LudiusMaximus
A Rage Talon Dragon Guard
 
LudiusMaximus's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2018
Posts: 320
OK, it seems that UnregisterAllEvents() tries to unregister every event that was ever given to RegisterEvent(), regardless of whether it is an existing event or not.

So even when a non-existing event is tried to be registered only once, I get the error when UnregisterAllEvents() is called...

So I would really need a way to check if an event is valid before I call RegisterEvent().
__________________
~ Be the change you want to see in the world... of warcraft interface! ~
  Reply With Quote
04-10-21, 03:49 AM   #5
LudiusMaximus
A Rage Talon Dragon Guard
 
LudiusMaximus's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2018
Posts: 320
I found a way to get a list of all events:

Lua Code:
  1. if not APIDocumentation then
  2.   LoadAddOn("Blizzard_APIDocumentation");
  3. end
  4.  
  5. local allEvents = APIDocumentation:GetAPITableByTypeName("event")
  6.  
  7. for k, v in pairs(allEvents) do
  8.   print(k, v.LiteralName)
  9. end


So now I can check my events before I call RegisterEvent() :-)
__________________
~ Be the change you want to see in the world... of warcraft interface! ~

Last edited by LudiusMaximus : 04-10-21 at 03:54 AM.
  Reply With Quote
04-10-21, 05:31 AM   #6
myrroddin
A Pyroguard Emberseer
 
myrroddin's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2008
Posts: 1,240
AceEvent registers or unregisters Blizzard events; it does not process user-defined events. For that, you want Callbackhandler-1.0.

UnregisterAllEvents iterates through all possible Blizzard events and unregisters them from the frame. It does not matter if an event was actually registered or not. Internally, AceEvent will catch if an event was not registered. Therefore, you do not need to do that.

For example, if I write code and never register UNIT_HEALTH, UnregisterAllEvents will still check for UNIT_HEALTH, find it wasn't used, and ignore it.

If that isn't exactly the process, the other method AceEvent could be using is storing a table of events that really, truly, were registered on the frame, and iterates that table when you use UnregisterAllEvents. No matter which method is used, you are safe, and won't get errors.
  Reply With Quote
04-10-21, 05:40 AM   #7
LudiusMaximus
A Rage Talon Dragon Guard
 
LudiusMaximus's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2018
Posts: 320
Originally Posted by myrroddin View Post
No matter which method is used, you are safe, and won't get errors.
It must be something about pcall() then.
Another even more minimal working example:

Lua Code:
  1. local folderName = ...
  2. local myAddon = LibStub("AceAddon-3.0"):NewAddon(folderName, "AceEvent-3.0")
  3.  
  4. function myAddon:EventHandler(event)
  5.   print("EventHandler not doing anything", event)
  6. end
  7.  
  8. pcall(function() myAddon:RegisterEvent("Nonsense_Event", "EventHandler") end)
  9. myAddon:UnregisterAllEvents()

Gives me this unregister error:

Code:
AceEvent-3.0-4.lua:37: Attempt to unregister unknown event "Nonsense_Event"
__________________
~ Be the change you want to see in the world... of warcraft interface! ~
  Reply With Quote
04-11-21, 06:22 AM   #8
sezz
A Chromatic Dragonspawn
AddOn Author - Click to view addons
Join Date: Apr 2008
Posts: 158
Originally Posted by LudiusMaximus View Post
If nobody has an answer to this strange behaviour of AceEvent-3.0,
maybe someone can tell me how to let my code check itself if an event exists?
Thanks!
did you try if it works without using acevent which itself uses callbackhandler and that's propably why it doesn's work as you expect it?

Lua Code:
  1. local f = CreateFrame("Frame");
  2.  
  3. local EventExists = function(event)
  4.     if (pcall(function() f:RegisterEvent(event); end)) then
  5.         f:UnregisterEvent(event);
  6.         return true;
  7.     end
  8. end
  9.  
  10. print(EventExists("MEH"));
  11. print(EventExists("PLAYER_LOGIN"));
  Reply With Quote
04-11-21, 03:12 PM   #9
LudiusMaximus
A Rage Talon Dragon Guard
 
LudiusMaximus's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2018
Posts: 320
Originally Posted by sezz View Post
did you try if it works without using acevent which itself uses callbackhandler and that's propably why it doesn's work as you expect it?

Lua Code:
  1. local f = CreateFrame("Frame");
  2.  
  3. local EventExists = function(event)
  4.     if (pcall(function() f:RegisterEvent(event); end)) then
  5.         f:UnregisterEvent(event);
  6.         return true;
  7.     end
  8. end
What you suggest is a good way to check if an event exists! Do you think it is more efficient than my solution:
Lua Code:
  1. if not APIDocumentation then
  2.     LoadAddOn("Blizzard_APIDocumentation");
  3. end
  4. local allEvents = APIDocumentation:GetAPITableByTypeName("event")
  5.  
  6. local EventExists = function(event)
  7.     local eventExists = false
  8.     for k, v in pairs(allEvents) do
  9.         if event == v.LiteralName then
  10.             eventExists = true
  11.             break
  12.         end
  13.     end
  14.     return eventExists
  15. end

My problem was really AceEvent's UnregisterAllEvents() function that tries to unregister non-existing events that were tried to be registered using pcall(). Maybe AceEvent assumes that a script will never continue after its RegisterEvent() fails. So it does not bother to remove a non-existent event from its own table of (putatively) registered events...?
__________________
~ Be the change you want to see in the world... of warcraft interface! ~
  Reply With Quote
04-11-21, 03:53 PM   #10
sezz
A Chromatic Dragonspawn
AddOn Author - Click to view addons
Join Date: Apr 2008
Posts: 158
Originally Posted by LudiusMaximus View Post
What you suggest is a good way to check if an event exists! Do you think it is more efficient than my solution:
Lua Code:
  1. if not APIDocumentation then
  2.     LoadAddOn("Blizzard_APIDocumentation");
  3. end
  4. local allEvents = APIDocumentation:GetAPITableByTypeName("event")
  5.  
  6. local EventExists = function(event)
  7.     local eventExists = false
  8.     for k, v in pairs(allEvents) do
  9.         if event == v.LiteralName then
  10.             eventExists = true
  11.             break
  12.         end
  13.     end
  14.     return eventExists
  15. end

My problem was really AceEvent's UnregisterAllEvents() function that tries to unregister non-existing events that were tried to be registered using pcall(). Maybe AceEvent assumes that a script will never continue after its RegisterEvent() fails. So it does not bother to remove a non-existent event from its own table of (putatively) registered events...?
Personally I'd would use my solution because it doesn't rely on Blizzard to provide a up-to-date and complete documentation (also you don't need to load the addon which itself takes about 2-3 times as long as it takes to check 10000 events for existance with the pcall approach).
  Reply With Quote
04-11-21, 06:48 PM   #11
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,313
Originally Posted by sezz View Post
did you try if it works without using acevent which itself uses callbackhandler and that's propably why it doesn's work as you expect it?

Lua Code:
  1. local f = CreateFrame("Frame");
  2.  
  3. local EventExists = function(event)
  4.     if (pcall(function() f:RegisterEvent(event); end)) then
  5.         f:UnregisterEvent(event);
  6.         return true;
  7.     end
  8. end
  9.  
  10. print(EventExists("MEH"));
  11. print(EventExists("PLAYER_LOGIN"));
If you check a lot of events, it'll create a new function each time. This'll work better.
Lua Code:
  1. local f=CreateFrame("Frame");
  2. local function EventExists(event)
  3.     local exists=pcall(f.RegisterEvent,f,event);--  This is the same as calling f:RegisterEvent(event)
  4.     if exists then f:UnregisterEvent(event); end
  5.     return exists;
  6. end
This calls f:RegisterEvent() directly instead of needlessly wrapping it in a dynamic function.
__________________
WoWInterface AddOns
"All I want is a pretty girl, a decent meal, and the right to shoot lightning at fools."
-Anders (Dragon Age: Origins - Awakening)
  Reply With Quote
04-12-21, 01:57 PM   #12
LudiusMaximus
A Rage Talon Dragon Guard
 
LudiusMaximus's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2018
Posts: 320
Cool! Thanks to both of you! :-D
__________________
~ Be the change you want to see in the world... of warcraft interface! ~
  Reply With Quote
04-14-21, 02:28 PM   #13
myrroddin
A Pyroguard Emberseer
 
myrroddin's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2008
Posts: 1,240
The reason NONSENSE_EVENT fires an error has nothing to do with pcall, and everything to do with that not being a Blizzard event. I did say that only Blizzard events are supported.

It sounds like you are letting users enter a string, which could be anything, and attempting to register the string as an event, valid or not. Why would you do that? Putting in some validation code to check if the event is real would be an excellent idea.

You could use strfind or strmatch to parse _G and if the event is found, then register the event. That would prevent users from entering FLUFFS_ARE_CUTE, or at least reduce the list to the two events that have FLU*, both of which are valid.
  Reply With Quote
04-14-21, 02:37 PM   #14
LudiusMaximus
A Rage Talon Dragon Guard
 
LudiusMaximus's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2018
Posts: 320
Originally Posted by myrroddin View Post
It sounds like you are letting users enter a string, which could be anything, and attempting to register the string as an event, valid or not. Why would you do that?
That's exactly what DynamicCam allows. Users are able to create their own custom situations in which certain camera settings should be applied. This includes the events at which the situation conditions should be checked.

Originally Posted by myrroddin View Post
You could use strfind or strmatch to parse _G and if the event is found, then register the event. That would prevent users from entering FLUFFS_ARE_CUTE, or at least reduce the list to the two events that have FLU*, both of which are valid.
Do you think this is more efficient than the EventExists() function by SDPhantom?
__________________
~ Be the change you want to see in the world... of warcraft interface! ~
  Reply With Quote
04-14-21, 02:39 PM   #15
myrroddin
A Pyroguard Emberseer
 
myrroddin's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2008
Posts: 1,240
Originally Posted by LudiusMaximus View Post
Code:
AceEvent-3.0-4.lua:37: Attempt to unregister unknown event "Nonsense_Event"
If that is what you see for unknown events, then AceEvent is registering by table, a table that has a list of events assigned to the frame. It is the second method I suggested, not the first.

Clearly, AceEvent does not validate events before adding them to the frame's table. As per the thread's title, no, AceEvent is NOT being tricked into registering non-existent events. It is up to the addon author using AceEvent to do the validation, or simply never use events that don't exist. Which is what 99.9999999999% of authors do in their projects.

I can see why there is no validation in AceEvent. Considering the ever-increasing quantity of events, that would bloat AceEvent's code for no gain.
  Reply With Quote
04-14-21, 02:44 PM   #16
myrroddin
A Pyroguard Emberseer
 
myrroddin's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2008
Posts: 1,240
Originally Posted by LudiusMaximus View Post
That's exactly what DynamicCam allows. Users are able to create their own custom situations in which certain camera settings should be applied. This includes the events at which the situation conditions should be checked.
Please provide a working example.

Originally Posted by LudiusMaximus View Post
Do you think this is more efficient than the EventExists() function by SDPhantom?
I can't answer that. Might be worth testing. If you parse _G, you can break the loop if an event is found, if that matters. That could save some computer power, maybe.
  Reply With Quote
04-14-21, 03:31 PM   #17
LudiusMaximus
A Rage Talon Dragon Guard
 
LudiusMaximus's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2018
Posts: 320
Originally Posted by myrroddin View Post
If that is what you see for unknown events, then AceEvent is registering by table, a table that has a list of events assigned to the frame. It is the second method I suggested, not the first.
That's what I supposed, too. But the implementation of AceEvent-3.0.lua is really beyond me...
I cannot spot any point where it would seem to maintain such a table of registered events.

What really puzzles me is why there is the unregister error when I call RegisterEvent() with callback:
Lua Code:
  1. local myAddon = LibStub("AceAddon-3.0"):NewAddon(..., "AceEvent-3.0")
  2. pcall(function() myAddon:RegisterEvent("NONSENSE_EVENT", function() end) end)
  3. myAddon:UnregisterAllEvents()
  4. -- Error: Attempt to unregister unknown event "NONSENSE_EVENT"

But no error message when I do it without callback:
Lua Code:
  1. local myAddon = LibStub("AceAddon-3.0"):NewAddon(..., "AceEvent-3.0")
  2. pcall(function() myAddon:RegisterEvent("NONSENSE_EVENT") end)
  3. myAddon:UnregisterAllEvents()
  4. -- No error.

Thanks! Your help is really appreciated.


Originally Posted by myrroddin View Post
Please provide a working example.
An example is just that a user mistypes an event they want to register. The GUI looks like this:
__________________
~ Be the change you want to see in the world... of warcraft interface! ~
  Reply With Quote

WoWInterface » Developer Discussions » Lua/XML Help » AceEvent-3.0 tricked into registering non-existent events??

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off