WoWWiki:Sandbox/661
From WoWWiki
{{wowapievent}} [[Category:API events| Events]]
Events are messages sent by the WoW client to the scripts, mostly in reaction to messages sent by the game server to the game client.
The World of Warcraft scripting framework is entirely event driven. You cannot run code without being called by an event. If your code keeps running for an extended time, nothing new will happen; you freeze the entire game client. So, everything you do has to be in response to something happening, e.g.:
- Someone saying something
- Combat events
- Someone's stats changing
- The player interacting with a UI element (though this is not delivered as events; that's done via Widget Handlers)
Events are always sent to Frame objects and their derivates (Buttons, EditBoxes, etc).
| |||||||||||||||||||||
Contents |
Registering for Event reception
In order for your frame to receive an event, you have to do two things:
- Your frame needs to have an "OnEvent" handler where it can receive the events.
- You need to tell the game client which events you want to receive, using the myFrame:RegisterEvent("EVENT_NAME") API.
When processing an event, there are three arguments that are passed to the handler:
- self
- A reference to the frame that registered for receiving the event (e.g. myFrame above). "self" is always set for code that gets run in an "<OnSomething>" handler, and events are no exception.
- event
- The name of the event (e.g. "EVENT_NAME" above)
- ...
- A tuple containing the event-specific arguments. See the event reference for more information.
You can reference arguments individually by accessing the tuple as a table, as in the following code:
local arg1 = ({...})[1]
local arg2 = ({...})[2]
-- repeat ad infinitum...
To find out what the arguments of a given event are, you can print them out to the chat frame using the following code:
local args = { }
for i = 1, select('#', ...) do
args[i] = tostring(select(i, ...))
end
DEFAULT_CHAT_FRAME:AddMessage(event..": "..table.concat(args, ", "))
Then, when the event is triggered, it will output the event name followed by the arguments separated by commas. ex: TIME_PLAYED_MSG: 4147246, 419631
Using XML, calling Lua functions
MyAddOn.xml
<script file="MyAddOn.lua">
<Frame name="MyAddOn_MainFrame">
<Scripts>
<OnLoad>
MyAddOn_MainFrame_OnLoad(self)
</OnLoad>
<OnEvent>
MyAddOn_MainFrame_OnEvent(self, event, ...)
</OnEvent>
</Scripts>
</Frame>
MyAddOn.lua
function MyAddOn_MainFrame_OnLoad(self)
self:RegisterEvent("ADDON_LOADED");
self:RegisterEvent("SOME_OTHER_EVENT");
end
function MyAddOn_MainFrame_OnEvent(self, event, ...)
if (event == "ADDON_LOADED" and ({...})[1] == "MyAddOn") then
-- initialize variables, safety check saved variables....
elseif (event == "SOME_OTHER_EVENT") then
-- do other stuff...
end
end
Using XML only
This is perhaps not so suitable for those big functions where you initialize variables and all that, but it's definitely handy for small snippets of code.
MyAddOn.xml
<Frame name="MyAddOn_MainFrame" [...]>
<Scripts>
<OnLoad>
self:RegisterEvent("ZONE_UNDER_ATTACK")
</OnLoad>
<OnEvent>
-- We're not even checking the event here; we know that ZONE_UNDER_ATTACK is the only one we'll get
self:Show()
</OnEvent>
</Scripts>
</Frame>
Using Lua only
If you have a small snippet of Lua code that doesn't really have a user interface, but just want it to run on certain events, you can use CreateFrame() to create an invisible frame and then use Frame:SetScript() to set the "OnEvent" handler:
MyAddOn.lua:
function MyAddOn_OnEvent(self, event, ...)
if event=="SOME_EVENT" then
-- do stuff...
elseif event=="SOME_OTHER_EVENT" then
-- do other stuff...
end
end
local frame = CreateFrame("Frame", "MyAddOnFrame")
frame:SetScript("OnEvent", MyAddOn_OnEvent)
frame:RegisterEvent("SOME_EVENT")
frame:RegisterEvent("SOME_OTHER_EVENT")
Notice in the OnEvent function, your frame is passed to MyAddOn_OnEvent as the first argument (self), the event is passed as the second argument (event), and the tuple containing the event-specific arguments is passed at the end (...). Keep in mind that all things you can do in xml can also be done in Lua. In many ways it's better to do them in Lua because it offers you more power over how your interfaces are made.
Variations Seen in the Wild
The examples above all use the global variables to access event data. Though in some addons, you may see event handler functions accepting regular function arguments instead. This works, as long as you actually pass the global variables in as function arguments:
MyAddOn.xml
...
<OnLoad>
this:RegisterEvent("CHAT_MSG_SAY");
this:RegisterEvent("CHAT_MSG_WHISPER");
</OnLoad>
<OnEvent>
MyAddOn_OnComms(event, arg1, arg2);
</OnEvent>
...
MyAddOn.lua
function MyAddOn_OnComms(chattype, text, author)
if(event == "CHAT_MSG_SAY") then
ChatFrame2:AddMessage(author.." says: "..text);
elseif(event == "CHAT_MSG_WHISPER") then
ChatFrame2:AddMessage(author.." whispers to you: "..text);
end
end
This code assumes that arg1 is the text of the message, and arg2 is the author. This is the case for both CHAT_MSG_SAY and CHAT_MSG_WHISPER.
With this method, you are creating a local copy of the global variables within your lua function.
Reasons to do this include:
- Clean Code - It clarifies where variables come from.
- General Code - You can easily "emulate" an event by just calling the function, and often this function will have to distinguish between some events. This way you don't have to create an "event" variable in the calling function. Also, many include arg1, arg2, etc. in the function's arguments for the same reasons.
Caution
If you do pass either the event name or the args, be careful with your naming. If your function's signature looks like this
function YourMod_OnEvent(event, arg1, arg2) -- dangerous
... you are hiding the global variables from your function. This is fine, as long as you remember to pass them in the script handler!
If you are getting a lot of nils that you aren't expecting, it's probably because you forgot to pass your variables into your function.
An all Lua-approach
If you are using an all-Lua approach, it is still possible to pass the event name and arguments in as function parameters, using the following method. Note that the (...) arguments are ellipses. Since World of Warcraft 2.0 they are part of the required function wrapper for altering OnEvent with SetScript.
MyAddOn.lua:
function MyAddOn_OnEvent(self, eventName, ...)
...
end
...
someFrame:SetScript("OnEvent", MyAddOn_OnEvent);
An alternate method, using Tables
A method I picked up from a coding tips page on wowace.com, and have noticed many people using makes use of a table of events (or you can use your addon's table object if you define your addons in such a way):
MyAddOn.lua:
local events = {
SOME_EVENT = function(arg1)
...
end,
}
function events.SOME_OTHER_EVENT(arg1) -- or you can define functions outside of the table
...
end
...
local eventFrame = CreateFrame("Frame")
eventFrame:SetScript("OnEvent", function(this, event, ...)
events[event](...)
end)
You can then register all your needed events to the frame "eventFrame". And as a neat bonus, when using several events, this table method is also faster than running a long if-then-else chain.
Unregistering Event reception
<myFrame>:UnregisterEvent(nameOfEvent);
Unregister an event, when it's no longer needed.
<myFrame>:UnregisterAllEvents();
Unregisters all events on the frame. Useful if the addon can be disabled in game.