Thread Tools Display Modes
07/19/22, 01:54 AM   #1
Masteroshi430
 
Masteroshi430's Avatar
AddOn Author - Click to view addons
Join Date: Dec 2020
Posts: 185
[notabug] Is EVENT_MANAGER:RegisterForUpdate still working?

Hi all,
Since yesterday's (18/07) patch I have function calls through EVENT_MANAGER:RegisterForUpdate not working anymore, replacing them by direct function calls or zo_callLater works.

Lua Code:
  1. local function DelayedPOIPins() -- WORKAROUND 18/07/2022
  2.        zo_callLater(FyrMM.POIPins, 300)
  3.     --EVENT_MANAGER:RegisterForUpdate("FyrMiniMapPOIPinsD", 300,  FyrMM.POIPins)
  4. end

Can any other dev confirm/disconfirm ?
  Reply With Quote
07/19/22, 02:41 AM   #2
Baertram
Super Moderator
 
Baertram's Avatar
WoWInterface Super Mod
AddOn Author - Click to view addons
Join Date: Mar 2014
Posts: 4,912
Seems to work normal for me.



You can simply test it with a blank addon and disable ALL other addons and libraries:

Lua Code:
  1. local updaterNameUnique = "FyrMiniMapPOIPinsD"
  2.     local unregisterAfterCalls = 0
  3.     local function FyrMM_POIPins()
  4.        d("EVENT_MANAGER:RegisterForUpdate called: FyrMM_POIPins")
  5.         unregisterAfterCalls = unregisterAfterCalls + 1
  6.         if unregisterAfterCalls == 20 then
  7.             EVENT_MANAGER:UnregisterForUpdate(updaterNameUnique)
  8.         end
  9.     end
  10.     EVENT_MANAGER:UnregisterForUpdate(updaterNameUnique)
  11.     EVENT_MANAGER:RegisterForUpdate(updaterNameUnique, 300,  FyrMM_POIPins)

Last edited by Baertram : 07/19/22 at 02:47 AM.
  Reply With Quote
07/19/22, 02:47 AM   #3
shadowcep
 
shadowcep's Avatar
Join Date: Jul 2020
Posts: 1
Was just preparing this reply, then Baertram replied.

@Masteroshi430 it's interesting that you say zo_callLater works but EVENT_MANAGER:RegisterForUpdate doesn't: in the esoui-8.0.8 source code, file libraries\globals\globalapi.lua shows zo_callLater is implemented using EVENT_MANAGER:RegisterForUpdate.
Lua Code:
  1. function zo_callLater(func, ms)
  2.     local id = ZO_CallLaterId
  3.     local name = "CallLaterFunction"..id
  4.     ZO_CallLaterId = ZO_CallLaterId + 1
  5.  
  6.     EVENT_MANAGER:RegisterForUpdate(name, ms,
  7.         function()
  8.             EVENT_MANAGER:UnregisterForUpdate(name)
  9.             func(id)
  10.         end)
  11.     return id
  12. end
  Reply With Quote
07/19/22, 02:51 AM   #4
Baertram
Super Moderator
 
Baertram's Avatar
WoWInterface Super Mod
AddOn Author - Click to view addons
Join Date: Mar 2014
Posts: 4,912
Yeah, that too. Would have been a very big suprise if the one works and the others doesn't

You probably have a bug in your code then which will unregister the eventUpdater (seach your code for "FyrMiniMapPOIPinsD")
or some other error occurs which will make your function never be called.
If not doing this already enable LibDebugLogger, DebugLogViewer and check messages that show after login/load of the addon/after reloadui that won't show with normal d() messages (as the d() messages will only show after event_player_activated as the chat was initialized and addons like pChat or LibDebugLogger also catch those messages).
  Reply With Quote
07/19/22, 03:02 AM   #5
Masteroshi430
 
Masteroshi430's Avatar
AddOn Author - Click to view addons
Join Date: Dec 2020
Posts: 185
Originally Posted by Baertram View Post
You probably have a bug in your code then which will unregister the eventUpdater (seach your code for "FyrMiniMapPOIPinsD")
It's probably that but how was everything working on Live but didn't work on PTS, then after the Live update doesn't work on live anymore, without me pushing an update in between?

I will check code with unregisterforupdate.
  Reply With Quote
07/19/22, 03:24 AM   #6
Baertram
Super Moderator
 
Baertram's Avatar
WoWInterface Super Mod
AddOn Author - Click to view addons
Join Date: Mar 2014
Posts: 4,912
Sounds like your code on live was "buggy" before the live update then already and the bug triggers "only sometimes".
I know these kind of bugs. Either it's a typo somewhere , or a really annoyingly hard one to catch... Good luck!
  Reply With Quote
07/19/22, 09:46 AM   #7
ZOS_DanBatson
ZOS Staff!
 
ZOS_DanBatson's Avatar
Yes this person is from ZeniMax!
Join Date: Jul 2015
Posts: 171
Are you calling DelayedPOIPins repeatedly? Cause if so, you'd be re-registering repeatedly, which would keep reseting the delay clock, making it never ultimately fire.

There was a change to RegisterForUpdate recently (to account for a crash) that could result in some changed behavior if you were previously relying on subsequent calls to RegisterForUpdate (without an unregister first) to simply "drop on the ground silently." Now calling RegisterForUpdate again "replaces" whatever is currently registered (including replacing the delay time), rather than silently dropping it on the ground if something is already registered. This can mainly cause weirdness for anyone who was doing the register on a loop, which is not really something you should be doing anyway.
  Reply With Quote
07/19/22, 11:40 AM   #8
Baertram
Super Moderator
 
Baertram's Avatar
WoWInterface Super Mod
AddOn Author - Click to view addons
Join Date: Mar 2014
Posts: 4,912
With this changes done, maybe I totally missunderstand your post, but what happens differently now?

If I got a function that was using RegisterForUpdate to e.g. register an inventory refresh via updaterCallbackFunction() after 50ms with unique identifer "UpdateInv1".
And after 10ms another RegisterForUpdate for the same inventory refresh using same unique update identifier "UpdateInv1" with 50ms comes in, without unregistering the first updater:

Will it overwrite the updater "UpdateInv1" and reset the timer to 50ms again, so that the registered updaterCallbackFunction() is called 50ms after the 2nd RegisterForUpdate was done?
Or will the 1st registered "UpdateInv1" call the updaterCallbackFunction() after the 40ms left have passed then, and the 2nd registered "UpdateInv1" will fire after 50ms too?


In the past "UpdateInv1" was overwritten and the timer was reset to 50ms. The 1st registered "UpdateInv1" was never triggered, and the 2nd "UpdateInv1" was triggering updaterCallbackFunction() after 50ms then.
  Reply With Quote
07/19/22, 12:08 PM   #9
Masteroshi430
 
Masteroshi430's Avatar
AddOn Author - Click to view addons
Join Date: Dec 2020
Posts: 185
Originally Posted by ZOS_DanBatson View Post
Are you calling DelayedPOIPins repeatedly? Cause if so, you'd be re-registering repeatedly, which would keep reseting the delay clock, making it never ultimately fire.

There was a change to RegisterForUpdate recently (to account for a crash) that could result in some changed behavior if you were previously relying on subsequent calls to RegisterForUpdate (without an unregister first) to simply "drop on the ground silently." Now calling RegisterForUpdate again "replaces" whatever is currently registered (including replacing the delay time), rather than silently dropping it on the ground if something is already registered. This can mainly cause weirdness for anyone who was doing the register on a loop, which is not really something you should be doing anyway.
It's an addon I am maintaining, there are some sort of loops of RegisterForUpdate exiting to an unregisterForUpdate by a condition.

So the new rule for RegisterForUpdate is :
- Always unregister before registering again otherwise the new register replaces the previous one with it's own milliseconds value restarting from scratch.
Am I right?
I will try to see what I can do.
Thanks Dan!
  Reply With Quote
07/19/22, 04:19 PM   #10
ZOS_DanBatson
ZOS Staff!
 
ZOS_DanBatson's Avatar
Yes this person is from ZeniMax!
Join Date: Jul 2015
Posts: 171
Originally Posted by Baertram View Post
With this changes done, maybe I totally missunderstand your post, but what happens differently now?

If I got a function that was using RegisterForUpdate to e.g. register an inventory refresh via updaterCallbackFunction() after 50ms with unique identifer "UpdateInv1".
And after 10ms another RegisterForUpdate for the same inventory refresh using same unique update identifier "UpdateInv1" with 50ms comes in, without unregistering the first updater:

Will it overwrite the updater "UpdateInv1" and reset the timer to 50ms again, so that the registered updaterCallbackFunction() is called 50ms after the 2nd RegisterForUpdate was done?
Or will the 1st registered "UpdateInv1" call the updaterCallbackFunction() after the 40ms left have passed then, and the 2nd registered "UpdateInv1" will fire after 50ms too?


In the past "UpdateInv1" was overwritten and the timer was reset to 50ms. The 1st registered "UpdateInv1" was never triggered, and the 2nd "UpdateInv1" was triggering updaterCallbackFunction() after 50ms then.
It will do the first one. Example:

Register updaterCallbackFunction() to "UpdateInv1" with an interval of 1000
After 200 ms, Register updaterCallbackFunction() to "UpdateInv1" with an interval of 1000. (Original registration is now gone.)
After 1000 more ms (1200 ms total elapsed), updaterCallbackFunction() will get called for the first time.

Calling RegisterForUpdate again with the same identifier will replace everything and reset the timer.

You're incorrect though about the "in the past" behavior. In the past, the behavior used to be this:

Register updaterCallbackFunction() to "UpdateInv1" with an interval of 1000
After 200 ms, try to register updaterCallbackFunction() to "UpdateInv1" with an interval of 1000. This request just gets dropped on the ground completely, because "UpdateInv1" is already registered.
After 800 more ms (1000 ms total elapsed), updaterCallbackFunction() will get called for the first time.

In the past, you couldn't replace the function or reset the clock. Again, trying to register again would previously literally just abort because something is already registered. Now, it's a replace and reset.

In the past, if you wanted to replace the function or change/reset the interval, you had to manually UnregisterForUpdate the identifier first.
  Reply With Quote
07/19/22, 04:25 PM   #11
ZOS_DanBatson
ZOS Staff!
 
ZOS_DanBatson's Avatar
Yes this person is from ZeniMax!
Join Date: Jul 2015
Posts: 171
Originally Posted by Masteroshi430 View Post
It's an addon I am maintaining, there are some sort of loops of RegisterForUpdate exiting to an unregisterForUpdate by a condition.

So the new rule for RegisterForUpdate is :
- Always unregister before registering again otherwise the new register replaces the previous one with it's own milliseconds value restarting from scratch.
Am I right?
I will try to see what I can do.
Thanks Dan!
As of this change, there's literally no reason to unregister unless you're actually just trying to turn the update off. If you want to change what's currently registered (either do a different function, or change the interval, or reset the timer on the interval) you can register again as needed, no need to do the unregister first. You can do an unregister first if you want, but you don't need to and it provides no benefit.

Calling this every frame will result in myFunction() never getting called, because it will reset the clock every frame, and 50 ms never gets to elapse enough to fire the function.
Code:
EVENT_MANAGER:RegisterForUpdate("MyIdentifier", 50, myFunction())
Calling this every frame would be fine because the interval is 0, so it'll call it every frame anyway. But also calling this every frame is pretty pointless and indicative of potentially lazy code.
Code:
EVENT_MANAGER:RegisterForUpdate("MyIdentifier", 0, myFunction())
  Reply With Quote
07/20/22, 12:34 AM   #12
Masteroshi430
 
Masteroshi430's Avatar
AddOn Author - Click to view addons
Join Date: Dec 2020
Posts: 185
I think I understood the problem in that precise case:
A function called with EVENT_MANAGER:RegisterForUpdate was calling another function with EVENT_MANAGER:RegisterForUpdate so this made a loop inside the loop, calling the 2nd function with zo_callLater avoids the unnecessary loop.

I don't know if it is possible but can EVENT_MANAGER:RegisterForUpdate return something like a warning in case the timer of a function with the same identifier is repeatedly reset and never really triggers?
  Reply With Quote
07/20/22, 03:50 AM   #13
Baertram
Super Moderator
 
Baertram's Avatar
WoWInterface Super Mod
AddOn Author - Click to view addons
Join Date: Mar 2014
Posts: 4,912
Alright, thanks for the details, very appreciated!
I think I always used an unregister before registering a new update task, so it should work the same for my code
Originally Posted by ZOS_DanBatson View Post
It will do the first one. Example:

Register updaterCallbackFunction() to "UpdateInv1" with an interval of 1000
After 200 ms, Register updaterCallbackFunction() to "UpdateInv1" with an interval of 1000. (Original registration is now gone.)
After 1000 more ms (1200 ms total elapsed), updaterCallbackFunction() will get called for the first time.

Calling RegisterForUpdate again with the same identifier will replace everything and reset the timer.

You're incorrect though about the "in the past" behavior. In the past, the behavior used to be this:

Register updaterCallbackFunction() to "UpdateInv1" with an interval of 1000
After 200 ms, try to register updaterCallbackFunction() to "UpdateInv1" with an interval of 1000. This request just gets dropped on the ground completely, because "UpdateInv1" is already registered.
After 800 more ms (1000 ms total elapsed), updaterCallbackFunction() will get called for the first time.

In the past, you couldn't replace the function or reset the clock. Again, trying to register again would previously literally just abort because something is already registered. Now, it's a replace and reset.

In the past, if you wanted to replace the function or change/reset the interval, you had to manually UnregisterForUpdate the identifier first.
  Reply With Quote
07/20/22, 09:51 AM   #14
ZOS_DanBatson
ZOS Staff!
 
ZOS_DanBatson's Avatar
Yes this person is from ZeniMax!
Join Date: Jul 2015
Posts: 171
Originally Posted by Masteroshi430 View Post
I think I understood the problem in that precise case:
A function called with EVENT_MANAGER:RegisterForUpdate was calling another function with EVENT_MANAGER:RegisterForUpdate so this made a loop inside the loop, calling the 2nd function with zo_callLater avoids the unnecessary loop.

I don't know if it is possible but can EVENT_MANAGER:RegisterForUpdate return something like a warning in case the timer of a function with the same identifier is repeatedly reset and never really triggers?
At best it could return whether or not it was a new registration or overwriting a registration, but that still wouldn't tell you if it was repeating and never going to fire, because it can only tell you about that one single attempt to register. There's no record of the number of times you overwrote or any previous attempts or anything like that. So it still comes down to the fact that you have to be cognizant of what you're writing. The main reason I didn't return that information is because I would never really have a legitimate reason to act on it in the moment. Whether it was new or an overwrite wouldn't actually change anything. The addon would still need to manually keep track of a count, and if you're going to do that, you can just do that with already by adding whenever you register and subtracting whenever you unregister or some such.

The other reason I didn't add the return is because if you call RegisterForUpdate from within some other update function, we actually delay the registration until the end of the frame, because we don't want to insert an update registration in the middle of us looping through all our existing registrations, potentially causing some kind of mismatch or desync of behavior. So technically, when that happens, you haven't actually overwritten anything yet. Most of the time this wouldn't manifest in a way anyone would even notice. But you could set up a situation where it would matter:

We register UpdateA and UpdateB. This frame, UpdateA starts running its function. As part of that function, it happens to meet a condition to overwrite the registration for UpdateB. Since we're in the middle of processing all the update functions, we queue that overwrite up and keep going. We then run UpdateB, which is still the old UpdateB. Then, after that's all done, we overwrite UpdateB, and next frame, when UpdateB runs, it'll be the new overwritten behavior.
  Reply With Quote
07/20/22, 12:04 PM   #15
Masteroshi430
 
Masteroshi430's Avatar
AddOn Author - Click to view addons
Join Date: Dec 2020
Posts: 185
Originally Posted by ZOS_DanBatson View Post
At best it could return whether or not it was a new registration or overwriting a registration, but that still wouldn't tell you if it was repeating and never going to fire, because it can only tell you about that one single attempt to register. There's no record of the number of times you overwrote or any previous attempts or anything like that. So it still comes down to the fact that you have to be cognizant of what you're writing. The main reason I didn't return that information is because I would never really have a legitimate reason to act on it in the moment. Whether it was new or an overwrite wouldn't actually change anything. The addon would still need to manually keep track of a count, and if you're going to do that, you can just do that with already by adding whenever you register and subtracting whenever you unregister or some such.

The other reason I didn't add the return is because if you call RegisterForUpdate from within some other update function, we actually delay the registration until the end of the frame, because we don't want to insert an update registration in the middle of us looping through all our existing registrations, potentially causing some kind of mismatch or desync of behavior. So technically, when that happens, you haven't actually overwritten anything yet. Most of the time this wouldn't manifest in a way anyone would even notice. But you could set up a situation where it would matter:

We register UpdateA and UpdateB. This frame, UpdateA starts running its function. As part of that function, it happens to meet a condition to overwrite the registration for UpdateB. Since we're in the middle of processing all the update functions, we queue that overwrite up and keep going. We then run UpdateB, which is still the old UpdateB. Then, after that's all done, we overwrite UpdateB, and next frame, when UpdateB runs, it'll be the new overwritten behavior.
Understood, thank you for the detailed answer.
  Reply With Quote

ESOUI » Developer Discussions » Bug Reports » [notabug] Is EVENT_MANAGER:RegisterForUpdate still working?

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