Thread Tools Display Modes
05/25/14, 06:32 PM   #1
lyravega
AddOn Author - Click to view addons
Join Date: Apr 2014
Posts: 93
Event Help Needed

Hello. I was trying to come up with a way to disable some of the features in my little add-on whenever the user is in Cyrodiil.

In my add-on, I register the events IF they are selected, otherwise they don't register. So basically, my RegisterEvents() function has if statements in it. The event "EVENT_ADDON_LOADED" triggers an Initialize() function, which in turn runs this RegisterEvents() function. This event is then unregistered later on.

As far as I can tell, whenever the UI is reloaded, game basically "cleans" itself, and runs these initialize functions and such all over again. However, if the map has changed (you see a loading screen that is not caused by reloadUI so to speak), this is not the case. So I was trying to find a way to make this happen.

I know I can put a check in the functions, but that means less efficiency; less performance as it checks whether or not you are in Cyrodiil everytime. That is why I was trying to put this check in the RegisterEvents() function. However it only checks when the add-on is loaded or UI is reloaded and leaves map changes / loading screens alone.

Is it wise to register events with "EVENT_PLAYER_ACTIVATED"? I am asking this as it may be of help to me. Other suggestions are also welcome. Aim is to keep the actual function codes short and/or efficient, and avoiding registering any event if it is not desired.
  Reply With Quote
05/25/14, 08:17 PM   #2
Garkin
 
Garkin's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2014
Posts: 832
So you use something like custom events (callback object) and which functions are registered to certain event is based on player location? It sounds interesting.

I have tested which events occurs when you enter/leave campaign and it seems that mentioned "EVENT_PLAYER_ACTIVATED" is the best choice. There is no campaign event that you can use and "EVENT_ZONE_CHANGED" is triggered way too often (for every point of interest on the map).

To find out if you are in campaign I'd probably use:
Lua Code:
  1. if GetCurrentCampaignId() ~= 0 then
  2.    d("You are in Cyrodiil.")
  3. end
  Reply With Quote
05/25/14, 09:22 PM   #3
Seerah
Fishing Trainer
 
Seerah's Avatar
WoWInterface Super Mod
AddOn Author - Click to view addons
Join Date: Feb 2014
Posts: 648
EVENT_PLAYER_ACTIVATED fires after all of the EVENT_ADDON_LOADED events at login/reload. It also fires for every loading screen. (It is the equivalent of WoW's "PLAYER_ENTERING_WORLD" event.)

There is nothing wrong with using this event, especially if you want to check the value of something after each loading screen.
  Reply With Quote
05/26/14, 12:27 AM   #4
lyravega
AddOn Author - Click to view addons
Join Date: Apr 2014
Posts: 93
Thanks for the replies. I am using this API function: "IsPlayerInAvAWorld()". I don't know if this returns true for anywhere else than Cyrodiil, I don't think so.

Right now I decided to use "EVENT_PLAYER_ACTIVATED" to register some other events; it will check the function above (IsPlayerInAvAWorld) and the user's choice (whether or not to have some features on or off in Cyrodiil), then register the events if they haven't been registered, or unregister them if they have been registered. Otherwise, I'll have to do the same check in the handler functions, which I'm trying to avoid (why do it over and over instead of doing it once)

Something like this (do not mind the order):
Lua Code:
  1. EVENT_MANAGER:RegisterForEvent( "QB" , EVENT_ADD_ON_LOADED , QB_Initialize )
  2.  
  3. local function QB_Initialize()
  4. ...
  5. EVENT_MANAGER:RegisterForEvent( "QB" , EVENT_PLAYER_ACTIVATED , QB_Cyrodiil )
  6. end
  7.  
  8. local function QB_Cyrodiil()
  9. if IsPlayerInAvAWorld()
  10. and QB_DisableFeaturesInCyrodiil
  11. and QB_EventsRegisteredBefore
  12. then UnregisterEvents()
  13. elseif not QB_EventsRegisteredBefore
  14. then RegisterEvents()
  15. else d("DERP, call 911") end
  16. end

I'm thinking this would suffice. Instead of doing the if-checks in the handler functions everytime their events fire, I'll be doing this check whenever "EVENT_PLAYER_ACTIVATED" fires. Wish there were two events called as "EVENT_CAMPAIGN_JOINED" and "EVENT_CAMPAIGN_LEFT" or something similar, that way I could save even more if-checks. Actually, going to add it to the wish list, who knows.

Meanwhile, can I create such a custom event? (Garkin and Seerah, you may now remember my name as I've asked you two how to create custom events, thanks to your helps but I still have no clue for some reason )
  Reply With Quote
05/26/14, 01:51 AM   #5
Wobin
 
Wobin's Avatar
AddOn Author - Click to view addons
Join Date: Apr 2014
Posts: 78
If all you're doing is registering and unregistering event handlers, don't stress the EVENT_PLAYER_ACTIVATED repetitiveness. It's definitely not fired as often as an OnUpdate, and isn't too hard on the system in any sense of the matter.
  Reply With Quote
05/26/14, 05:46 AM   #6
Garkin
 
Garkin's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2014
Posts: 832
Originally Posted by lyravega View Post
Meanwhile, can I create such a custom event? (Garkin and Seerah, you may now remember my name as I've asked you two how to create custom events, thanks to your helps but I still have no clue for some reason )
You can create something like "EVENT_CAMPAIGN_JOINED"/"EVENT_CAMPAIGN_LEFT". But it is more like function for library, so other addons/objects can listen to that event too.

wiki: How do I generate my own "events" in Lua?

If you want to use global object for events, use CALLBACK_MANAGER. If you want to create your own callback object use yourObject = ZO_CallbackObject:New()
Methods you can use:
Lua Code:
  1. object:RegisterCallback(eventName, callback, arg)
  2. object:UnregisterCallback(eventName, callback)
  3. object:FireCallbacks(eventName, ...)

A good example of callback object is ingame scene. Every time when scene changes its state (SCENE_SHOWN, SCENE_HIDDEN, SCENE_SHOWING, SCENE_HIDING), custom event "StateChange" is fired:
Lua Code:
  1. scene:FireCallbacks("StateChange", oldState, newState)
You can register for that event to do something in your addon:
Lua Code:
  1. scene = SCENE_MANAGER:GetScene("worldMap")
  2. scene:RegisterCallback("StateChange",
  3.    function(oldState, newState)
  4.       if(newState == SCENE_SHOWING) then
  5.          MyAddon:RefreshData()
  6.       end
  7.    end)

Another good example could be inventory. There is just one global event "EVENT_INVENTORY_SINGLE_SLOT_UPDATE" for all inventory types (worn, backpack, bank, guildbank, buyback). All objects working with any inventory needs to do the same check over and over. Inventory manager listents to the event and then using the callback it passes processed information to other objects, so they do not need to that again - eg.:
Lua Code:
  1. CALLBACK_MANAGER:FireCallbacks("BackpackSlotUpdate", slotIndex)
  2. CALLBACK_MANAGER:FireCallbacks("InventorySlotUpdate", GetControl(slot.slotControl, "Button"))
  Reply With Quote
05/26/14, 10:36 AM   #7
Seerah
Fishing Trainer
 
Seerah's Avatar
WoWInterface Super Mod
AddOn Author - Click to view addons
Join Date: Feb 2014
Posts: 648
Originally Posted by lyravega;847
[Highlight="Lua"
EVENT_MANAGER:RegisterForEvent( "QB" , EVENT_ADD_ON_LOADED , QB_Initialize )[/highlight]
The first parameter for RegisterForEvent is supposed to be a unique identifier. "QB" is not unique.

And, yes, for custom "events" you want callbacks. So follow what Garkin said above.
  Reply With Quote
05/26/14, 01:34 PM   #8
Garkin
 
Garkin's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2014
Posts: 832
Originally Posted by Seerah View Post
The first parameter for RegisterForEvent is supposed to be a unique identifier. "QB" is not unique.
The first parameter is supposed to be unique per event. If you are registering for different events, you can use the same identifier.
  Reply With Quote
05/26/14, 01:46 PM   #9
zgrssd
AddOn Author - Click to view addons
Join Date: May 2014
Posts: 280
Originally Posted by Garkin View Post
I have some questions about that section actually. It seems counter intuitive:
Code:
local function OnTrackStateChanged(questTracker, tracked, trackType, arg1, arg2)
    if tracked then
        d("A quest was tracked")
    else
        d("A quest was untracked")
    end
end
 
CALLBACK_MANAGER:RegisterCallback("QuestTrackerTrackingStateChanged", OnTrackStateChanged)
Why do I write 'OnTrackStateChanged' before regsitering the Callback? Is it's parameter list supposed to be the "siganture" of the Event?
If it is only the protoype for the Events Signature, why does it also contain code? Was somebody just lazy and wanted to use the same function as Prototype for Registering the Event and as the Callback that is later registerd to the event?
  Reply With Quote
05/26/14, 01:57 PM   #10
stjobe
 
stjobe's Avatar
AddOn Author - Click to view addons
Join Date: Apr 2014
Posts: 60
Originally Posted by zgrssd View Post
Why do I write 'OnTrackStateChanged' before regsitering the Callback? Is it's parameter list supposed to be the "siganture" of the Event?
If it is only the protoype for the Events Signature, why does it also contain code? Was somebody just lazy and wanted to use the same function as Prototype for Registering the Event and as the Callback that is later registerd to the event?
http://www.luafaq.org/gotchas.html#T3.2

Edit: In Lua, you can't call a function before it's declared.
  Reply With Quote
05/26/14, 02:36 PM   #11
zgrssd
AddOn Author - Click to view addons
Join Date: May 2014
Posts: 280
Originally Posted by stjobe View Post
http://www.luafaq.org/gotchas.html#T3.2

Edit: In Lua, you can't call a function before it's declared.
I know. But why is he writing:
Code:
local function OnTrackStateChanged(questTracker, tracked, trackType, arg1, arg2)
    if tracked then
        d("A quest was tracked")
    else
        d("A quest was untracked")
    end
end

CALLBACK_MANAGER:RegisterCallback("QuestTrackerTrackingStateChanged", OnTrackStateChanged)
isntead of:
Code:
local function OnTrackStateChanged(questTracker, tracked, trackType, arg1, arg2)
end

CALLBACK_MANAGER:RegisterCallback("QuestTrackerTrackingStateChanged", OnTrackStateChanged)
  Reply With Quote
05/26/14, 03:19 PM   #12
Xrystal
caritas omnia vincit
 
Xrystal's Avatar
Premium Member
AddOn Author - Click to view addons
Join Date: Feb 2014
Posts: 369
As far as I know there is no prototyping requirement in lua. You simply set up the function you want to call or you call a function that already exists.
  Reply With Quote
05/26/14, 03:35 PM   #13
zgrssd
AddOn Author - Click to view addons
Join Date: May 2014
Posts: 280
Originally Posted by Xrystal View Post
As far as I know there is no prototyping requirement in lua. You simply set up the function you want to call or you call a function that already exists.
Now I get it. I thought we first had to "add" our custom event before others could register for it. But that was wrong.
Any handlers regsitered to the callback manager are raised by the call of:
Code:
CALLBACK_MANAGER:FireCallbacks("QuestTrackerTrackingStateChanged", self, true, TRACK_TYPE_QUEST, 0)
And this was registering one handler
Code:
local function OnTrackStateChanged(questTracker, tracked, trackType, arg1, arg2)
    if tracked then
        d("A quest was tracked")
    else
        d("A quest was untracked")
    end
end
 
CALLBACK_MANAGER:RegisterCallback("QuestTrackerTrackingStateChanged", OnTrackStateChanged)
I think I am going to rework that FAQ part for better understandability, as I jsut rewrote the Saved Var part.
  Reply With Quote
05/26/14, 04:19 PM   #14
Seerah
Fishing Trainer
 
Seerah's Avatar
WoWInterface Super Mod
AddOn Author - Click to view addons
Join Date: Feb 2014
Posts: 648
Originally Posted by Garkin View Post
The first parameter is supposed to be unique per event. If you are registering for different events, you can use the same identifier.
And what happens when another addon wants to register for that event and only uses "QB"?


For callbacks, when you register for the callback you are listening for, you assign what function you want to call when it fires. This works the same way as regular events.
  Reply With Quote
05/26/14, 06:02 PM   #15
lyravega
AddOn Author - Click to view addons
Join Date: Apr 2014
Posts: 93
Originally Posted by Seerah View Post
And what happens when another addon wants to register for that event and only uses "QB"?
I am not aware of any other add-on that has a name with two words that start with Q and B, but I'll change those to avoid conflict and stuff nonetheless.

As for my problem, the solution looks ugly. Very ugly in fact. But functionality wise, it is much better than doing the checks on handler functions. I just hope I didn't miss anything - I just hope it won't register same stuff over and over.

If anyone is interested, this is the solution that I come up with:

Lua Code:
  1. local function QB_isEnabledInAvA()
  2.     if IsPlayerInAvAWorld() then return QB_vars.isEnabledInAvA
  3.     --QB_vars.isEnabledInAvA is saved variable, aka user choice
  4.     else return true end
  5. end
  6.  
  7. local function QB_RegisterEvents_OnActivated()
  8.     --QB_internal.eventsInitialized is a variable to avoid registering same stuff over and over
  9.     if QB_isEnabledInAvA() and not QB_internal.eventsInitialized then QB_internal.eventsInitialized = true
  10.     --if not registered, check if features are enabled in Cyrodiil, if it is enabled or player is not in Cyrodiil, register stuff
  11.         registerStuff()
  12.     elseif not QB_isEnabledInAvA() and QB_internal.eventsInitialized then QB_internal.eventsInitialized = false
  13.     --if stuff are registered, check if features are enabled in Cyrodiil, if it is not enabled there then unregister stuff
  14.         unregisterStuff()
  15.     else return end
  16.     --if stuff are not registered, and features are disabled in Cyrodiil, do not register anything in there
  17. end

Last edited by lyravega : 05/26/14 at 06:42 PM.
  Reply With Quote
05/26/14, 06:11 PM   #16
Xrystal
caritas omnia vincit
 
Xrystal's Avatar
Premium Member
AddOn Author - Click to view addons
Join Date: Feb 2014
Posts: 369
Wow, thanks Garkin. I totally missed this post and just assumed the CallbackManager was for accessing ESO's built in functions etc. Didn't realise it is how you can generate your own events. Will have to see if it is something that can be utilised in gatherer to improve its interaction functionality, which is still glitching out if the mouse moves at the wrong millisecond rofl.

Originally Posted by Garkin View Post
You can create something like "EVENT_CAMPAIGN_JOINED"/"EVENT_CAMPAIGN_LEFT". But it is more like function for library, so other addons/objects can listen to that event too.

wiki: How do I generate my own "events" in Lua?

If you want to use global object for events, use CALLBACK_MANAGER. If you want to create your own callback object use yourObject = ZO_CallbackObject:New()
Methods you can use:
Lua Code:
  1. object:RegisterCallback(eventName, callback, arg)
  2. object:UnregisterCallback(eventName, callback)
  3. object:FireCallbacks(eventName, ...)

A good example of callback object is ingame scene. Every time when scene changes its state (SCENE_SHOWN, SCENE_HIDDEN, SCENE_SHOWING, SCENE_HIDING), custom event "StateChange" is fired:
Lua Code:
  1. scene:FireCallbacks("StateChange", oldState, newState)
You can register for that event to do something in your addon:
Lua Code:
  1. scene = SCENE_MANAGER:GetScene("worldMap")
  2. scene:RegisterCallback("StateChange",
  3.    function(oldState, newState)
  4.       if(newState == SCENE_SHOWING) then
  5.          MyAddon:RefreshData()
  6.       end
  7.    end)

Another good example could be inventory. There is just one global event "EVENT_INVENTORY_SINGLE_SLOT_UPDATE" for all inventory types (worn, backpack, bank, guildbank, buyback). All objects working with any inventory needs to do the same check over and over. Inventory manager listents to the event and then using the callback it passes processed information to other objects, so they do not need to that again - eg.:
Lua Code:
  1. CALLBACK_MANAGER:FireCallbacks("BackpackSlotUpdate", slotIndex)
  2. CALLBACK_MANAGER:FireCallbacks("InventorySlotUpdate", GetControl(slot.slotControl, "Button"))
  Reply With Quote
05/26/14, 06:46 PM   #17
lyravega
AddOn Author - Click to view addons
Join Date: Apr 2014
Posts: 93
What I really don't understand about creating custom events is, what do you watch? On examples, all custom events that I've seen are linked to "OnSomethingHappened" stuff. Since I am not working with UI (at all), I cannot grasp what else you can use instead of "OnSomethingHappened" stuff.

And don't bother, I won't be able to till I see some other examples, I learn better with dissecting stuff and inspecting them bit by bit
  Reply With Quote
05/26/14, 08:51 PM   #18
Seerah
Fishing Trainer
 
Seerah's Avatar
WoWInterface Super Mod
AddOn Author - Click to view addons
Join Date: Feb 2014
Posts: 648
You would create a custom "event" - a callback - when an event isn't available for what you are looking for.

In ZAM_Stats, I use a callback to tell modules that the frame/label has been created for that module so that it may set any scripts or event handlers on that module. There is no event built into ESO to say "hey, that ZAM_Stats module you want to create is ready to customize".


@Xrystal: ESO basically has WoW's CallbackHandler library baked in.
  Reply With Quote
05/26/14, 09:25 PM   #19
lyravega
AddOn Author - Click to view addons
Join Date: Apr 2014
Posts: 93
Originally Posted by Garkin View Post
The first parameter is supposed to be unique per event. If you are registering for different events, you can use the same identifier.
Hm, some questions popped up in my mind. edit: got opportunity to check it for myself

-Can I unregister an event just by passing its identifier name? no
-More on that topic, lets say I've registered a few different events under the same identifier. Can I unregister them all at once by just giving that identifier name? no
-Would it throw an error if it tries to unregister something that isn't registered? no

Last edited by lyravega : 05/26/14 at 09:39 PM.
  Reply With Quote
05/27/14, 04:35 AM   #20
Garkin
 
Garkin's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2014
Posts: 832
Originally Posted by lyravega View Post
Hm, some questions popped up in my mind. edit: got opportunity to check it for myself

-Can I unregister an event just by passing its identifier name? no
-More on that topic, lets say I've registered a few different events under the same identifier. Can I unregister them all at once by just giving that identifier name? no
-Would it throw an error if it tries to unregister something that isn't registered? no
I believe that event manager works the same way as callback object. It uses registry with structure similar to:
Lua Code:
  1. eventRegistry = {
  2.    [event1] = {
  3.       [identifier1] = callback1,
  4.       [identifier2] = callback2,
  5.    },
  6.    [event2] = {
  7.       [identifier1] = callback3,
  8.    },
  9. }
- you need to specify both event and identifier if you want to unregister event
- there is no function that can unregister more events at once, but you can write your own
- there is no error and afaik no return code if you try to unregister something that wasn't registered

Very simple example how it can look like:
Lua Code:
  1. local EventManager = ZO_Object:Subclass()
  2.  
  3. function EventManager:New()
  4.    local o = ZO_Object.New(self)
  5.    o.registry = {}
  6.    return o
  7. end
  8.  
  9. function EventManager:RegisterForEvent(identifier, event, callback)
  10.    self.registry[event] = self.registry[event] or {}
  11.    self.registry[event][identifier] = callback
  12. end
  13.  
  14. function EventManager:UnregisterForEvent(identifier, event)
  15.    if self.registry[event] then
  16.       self.registry[event][identifier] = nil
  17.    end
  18. end
  19.  
  20. function EventManager:FireEvent(event, ...)
  21.    if self.registry[event] then
  22.       for identifier, callback in pairs(self.registry[event]) do
  23.          callback(event, ...)
  24.       end
  25.    end
  26. end
  27.  
  28. MyEventManager = EventManager:New()
  Reply With Quote

ESOUI » Developer Discussions » General Authoring Discussion » Event Help Needed

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