WoWInterface

WoWInterface (https://www.wowinterface.com/forums/index.php)
-   General Authoring Discussion (https://www.wowinterface.com/forums/forumdisplay.php?f=20)
-   -   Need event when world is loaded, game is playable (https://www.wowinterface.com/forums/showthread.php?t=55060)

Tuhljin 02-07-17 08:59 PM

Need event when world is loaded, game is playable
 
My addon needs to display some information to the player when they log in using the alert system, similar to how you get notices about achievements being earned on log in or garrison/order missions being completed. The problem is that all the methods I've found to do this are unreliable.

I'm looking for an event that is called when the game is actually playable: The player sees their character in the world and can now start playing (moving the character around, etc.). It should preferably be called after any loading screen has finished its job, but I will settle for one that triggers A) when the character "enters the world" for the first time from the character selection screen, and B) when the UI is reloaded (as from a /reload command or the "Reload UI" button on the in-game AddOn List).

The events I've been using all trigger too soon, while the game world is still loading. I've tried using C_Timer to add a delay, but this only works sometimes: It never works after a /reload, but it usually does when entering the world from character selection (but not always! the extra long loading screens in Dalaran seem to sometimes break it, for one). Frustratingly, the C_Timer functions apparently start counting down at some point while the loading screen is still visible (and that point seems to happen sooner when you do a /reload than it does when entering from character selection).

For my purposes, the event I'm looking for needn't happen at the exact moment the player character is playable, just so long as it is reliably triggered within a couple of seconds after that point.

Seerah 02-07-17 09:44 PM

Neither PLAYER_LOGIN nor PLAYER_ENTERING_WORLD work for you?

Tuhljin 02-08-17 12:05 AM

I've tried both of them individually and they don't work consistently. It's as described above: Usually on entering the game, never on /reload.

The alerts involve the in-game calendar, so I thought I'd try using OpenCalendar() inside PLAYER_ENTERING_WORLD and watching for CALENDAR_UPDATE_EVENT_LIST, but I end up with mostly the same problem, with one (rather odd) difference: The alerts do appear as intended after the first /reload... but not on any subsequent /reload (until you go back to character selection). I can tell the code that makes the alerts appear is called (since I added print messages), but the frames do not appear unless I put in so many alerts that there are too many to display at once, in which case the first batch are not shown but the rest (which would have gone to the queue) are.

I should note that the alerts I'm using are created using AlertFrame:AddQueuedAlertFrameSubSystem() with a custom template.

If I set the C_Timer to something especially long, the alerts will appear then, as well -- but that's not a good solution because the ideal wait time would vary from PC to PC and also can vary depending on what is being loaded, and we'd have an especially long delay in those cases where C_Timer wasn't counting down early (e.g. when entering the world from character select). (I really don't understand why C_Timer is counting down during the /reload process. This seems like a bug.) It would be much easier if I had an event that reliably triggered when the game world is fully ready.

MunkDev 02-08-17 05:56 AM

Lua Code:
  1. local addonName, addonTable = ...
  2.  
  3. local EventFrame = CreateFrame('Frame')
  4. EventFrame:RegisterEvent('ADDON_LOADED')
  5. EventFrame:SetScript('OnEvent', function(self, event, ...)
  6.     if self[event] then
  7.         self[event](self, ...)
  8.     end
  9. end)
  10.  
  11. function EventFrame:ADDON_LOADED(name)
  12.     if name == addonName then
  13.         -- do the thing
  14.  
  15.         -- remove event
  16.         self:UnregisterEvent('ADDON_LOADED')
  17.         self.ADDON_LOADED = nil
  18.     end
  19. end

ADDON_LOADED will always fire on login and on reload, but not on loading screens or any subsequent loading in the game. While this might not be the best basis for showing your toast, you can use it as backbone to determine when to start things off.

Fizzlemizz 02-08-17 09:44 AM

Quote:

The alerts involve the in-game calendar, so
The calendar is LoadOnDemand so won't actually be loaded and it's functions/data available until after the user clicks the calendar button.

You could try at PLAYER_ENTERING_WORLD:

Code:

if not IsAddOnLoaded("Blizzard_Calendar") then
        UIParentLoadAddOn("Blizzard_Calendar")
end
--- Your code goes here


jeruku 02-08-17 10:11 AM

In most cases the events prescribed do not load in any significant order. The way I have found, that works for me, is to wait until all three, replacing PLAYER_LOGIN since this occurs only once during a session, have been triggered and most information is available. This should include on ReloadUI but I am unsure if it will work for things like dungeon in/out. However, I am unaware if this will remedy your issue so I apologize in advance if this has wasted your time.


Lua Code:
  1. local frame = CreateFrame('Frame')
  2. local VariableLoadCount = 0
  3. local function LoadVariables()
  4.     VariableLoadCount = VariableLoadCount +1
  5.     if VariableLoadCount == 3 then
  6.         --you have reached the point where most information is now available
  7.         --safest place to create and populate frames
  8.         --unregister the events if no longer needed
  9.     end
  10. end
  11.  
  12. frame:RegisterEvent('PLAYER_ENTERING_WORLD')
  13. local function PLAYER_ENTERING_WORLD()
  14.     LoadVariables()
  15.     --self:UnregisterEvent('PLAYER_ENTERING_WORLD')
  16. end
  17.  
  18. frame:RegisterEvent('ADDON_LOADED')
  19. local function ADDON_LOADED(addon, ...)
  20.     if addon == addonName then
  21.         --safe place to create frames
  22.         LoadVariables()
  23.     end
  24. end
  25.  
  26. frame:RegisterEvent('VARIABLES_LOADED')
  27. local function VARIABLES_LOADED()
  28.     --safe place to populate frames
  29.     LoadVariables()
  30. end
  31.  
  32. frame:HookScript('OnEvent', function(self, event, ...)
  33.     if event == 'VARIABLES_LOADED' then
  34.         VARIABLES_LOADED()
  35.     elseif event == 'ADDON_LOADED' then
  36.         ADDON_LOADED(...)
  37.     elseif event == 'PLAYER_ENTERING_WORLD' then
  38.         PLAYER_ENTERING_WORLD()
  39.     end
  40. end)

Seerah 02-08-17 03:58 PM

Ah, getting info from the calendar. That's helpful to know. ;)

In my Bear in Mind addon, I have the Blizzard_Calendar listed as a dependency. I then looked through the UI source code and found how they update and gather calendar info.

You can look in BiM.lua if you like to see how I gather the day's events (just don't straight up copy it ;) ). The relevant code is at the end of the BiM.PlayerLogin function, which then also calls the CheckForCalendarEvents function above that.

Tuhljin 02-08-17 04:13 PM

I already successfully grab the information I need from the calendar, and did so even if I was using only PLAYER_LOGIN or PLAYER_ENTERING_WORLD, so I don't think that's the problem.

According to this, PLAYER_ENTERING_WORLD fires after ADDON_LOADED and is considered the last startup event. But C_Timer starts ticking down while the loading screen is still visible when you do /reload (again, I think this is a bug), so it doesn't work properly with alert frames.

Is there not an event that fires when the game is actually playable, player in the world? Maybe one that actually signifies something besides "game now playable" but will reliably occur at that point? It could be one that triggers many times, even, just so long as the first time it triggers after PLAYER_ENTERING_WORLD is when the game world is shown.

Banknorris 02-08-17 05:03 PM

I had a long seach for this as well and the best I could do was this:

Lua Code:
  1. local f = CreateFrame("Frame")
  2.  
  3. f:SetScript("OnEvent",function(self,event,...)
  4.     if event=="PLAYER_LOGIN" then
  5.         self:RegisterEvent("PET_JOURNAL_LIST_UPDATE")
  6.     elseif event=="PET_JOURNAL_LIST_UPDATE" then
  7.         self:UnregisterEvent("PET_JOURNAL_LIST_UPDATE")
  8.         --CALL YOUR STUFF HERE
  9.     end
  10. end)
  11. f:RegisterEvent("PLAYER_LOGIN")

Tuhljin 02-08-17 11:07 PM

I just tried PET_JOURNAL_LIST_UPDATE and it was still called too soon. Any other ideas?

Fizzlemizz 02-09-17 12:30 AM

As far as I know, noone has found a silver bullet event after PLAYER_ENERING_WORLD to cover all situations. Even Blizzard uses multiple events to cover login, logout/login and /reload scenarios for different parts of the game.

Tuhljin 02-09-17 12:30 AM

Sorry for the double post, but I seem to have found a workaround.

This doesn't work reliably:

Code:

C_Timer.After(3, function()
  ToastForEvents()
end)

... but this seemingly does:

Code:

C_Timer.After(0, function()
  C_Timer.After(3, function()
    ToastForEvents()
  end)
end)

Weird. I have a theory as to why it works, and it makes some sense as a programmer, but that doesn't make it any less unintuitive -- and I still think it would be considered buggy that /reload handles this so differently.

Edit: Turns out it wasn't a double post. Thanks, Fizzlemizz! ;)

SDPhantom 02-09-17 01:24 AM

Quote:

Originally Posted by Tuhljin (Post 321954)
... but this seemingly does:

Code:

C_Timer.After(0, function()
  C_Timer.After(3, function()
    ToastForEvents()
  end)
end)


The first call is basically telling C_Timer to run it when it can. After the game has loaded, it actually runs and the second one schedules the final function for 3 additional seconds from then. The first example would vary depending if it took 3 seconds or longer to finish loading and push out the first rendered frame.

Trying to support /reload is a niche case and you should just make sure your code doesn't throw any errors. Ideally, nobody should need to use it in a normal session. It only exists because sometimes mistakes happen. You shouldn't need to complicate your code more covering other addons' problems.


All times are GMT -6. The time now is 06:34 PM.

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