WoWInterface

WoWInterface (https://www.wowinterface.com/forums/index.php)
-   Lua/XML Help (https://www.wowinterface.com/forums/forumdisplay.php?f=16)
-   -   AceEvent-3.0 tricked into registering non-existent events?? (https://www.wowinterface.com/forums/showthread.php?t=58681)

LudiusMaximus 04-08-21 02:00 PM

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!

LudiusMaximus 04-09-21 05:19 PM

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!

kurapica.igas 04-09-21 08:56 PM

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

LudiusMaximus 04-10-21 03:14 AM

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().

LudiusMaximus 04-10-21 03:49 AM

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() :-)

myrroddin 04-10-21 05:31 AM

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.

LudiusMaximus 04-10-21 05:40 AM

Quote:

Originally Posted by myrroddin (Post 338848)
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"

sezz 04-11-21 06:22 AM

Quote:

Originally Posted by LudiusMaximus (Post 338843)
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"));

LudiusMaximus 04-11-21 03:12 PM

Quote:

Originally Posted by sezz (Post 338854)
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...?

sezz 04-11-21 03:53 PM

Quote:

Originally Posted by LudiusMaximus (Post 338862)
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).

SDPhantom 04-11-21 06:48 PM

Quote:

Originally Posted by sezz (Post 338854)
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.

LudiusMaximus 04-12-21 01:57 PM

Cool! Thanks to both of you! :-D

myrroddin 04-14-21 02:28 PM

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.

LudiusMaximus 04-14-21 02:37 PM

Quote:

Originally Posted by myrroddin (Post 338891)
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.

Quote:

Originally Posted by myrroddin (Post 338891)
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?

myrroddin 04-14-21 02:39 PM

Quote:

Originally Posted by LudiusMaximus (Post 338849)
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.

myrroddin 04-14-21 02:44 PM

Quote:

Originally Posted by LudiusMaximus (Post 338892)
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.

Quote:

Originally Posted by LudiusMaximus (Post 338892)
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.

LudiusMaximus 04-14-21 03:31 PM

Quote:

Originally Posted by myrroddin (Post 338893)
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.


Quote:

Originally Posted by myrroddin (Post 338894)
Please provide a working example.

An example is just that a user mistypes an event they want to register. The GUI looks like this:


All times are GMT -6. The time now is 01:37 PM.

vBulletin © 2022, Jelsoft Enterprises Ltd
© 2004 - 2021 MMOUI