• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[Reforged] Nested "Value of Real Variable" events is bugged

Hello,

Some well-known systems for your custom maps use "Value of Real Variable" events as API you can use to get notified of some game actions. For example a well known is GUI Unit Event by @Bribe :

For example, the system executes the code below:

JASS:
Set VariableSet Event1 = 0.00
Set VariableSet Event1 = 1.00
Set VariableSet Event1 = 0.00

And you can create triggers with event "Value of Real Variable" Event1 equals 1.00. The 2 times you set it to zero is to be sure is modified, and to clean it afterward.

I discovered a limitation of those events through a bug in my map : Blizzard coded the engine behavior for such events soo poorly than a nested "Value of Real Variable" event will change the variable value of the caller/parent, thus if you have a listener later on it won't work properly. But only in the listener contexts, in GUI or JASS. I haven't tested a vJASS listener.

Le me show you through an example (map attached to this topic):
  • We have a map with 2 real variables "Event1" & "Event2".
  • Let's fire "Event1" = 0.5 on map initialization, and let's have a trigger listening Event1 that emits a second event, Event2 = 1. For a better visibility, instead of reseting Event2 to 0.00 I'll reset it to 0.33.
  • Fire event1 on init
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Set VariableSet Event1 = 0.00
      • Set VariableSet Event1 = 5.00
      • Set VariableSet Event1 = 0.00
  • ChainEvents
    • Events
      • Game - Event1 becomes Equal to 0.50
    • Conditions
    • Actions
      • Game - Display to (All players) the text: Chaining events!
      • Set VariableSet Event2 = 0.00
      • Set VariableSet Event2 = 10.00
      • Set VariableSet Event2 = 33.00
      • Game - Display to (All players) the text: (Check OK: Event1 still equal to + (String(Event1)))
- Now let's just add logs & triggers to display what happens.

And the result is WTF: it's the logger for Event1 = 33.00 that is called, instead of the logger for Event1 = 5.00. But when I check 5s later, Event1 is equal to 5.00 as expected.
1691488931052.png


So my guess is that Blizzard fucked its implementation of the event listener system, and uses a reference on a static variable that is the same for all "Value of Real Variable" events.

So for ex with GUI Unit Event by @Bribe, if you've listeners to UnitIndexEvent & UnitInActionEvent you may encounter the bug.
 

Attachments

  • Test.w3m
    17 KB · Views: 2

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,468
Your demo map appears to be out of sync with your unit tests described here. I've attached an updated version of your demo map that bridges the missing event links.

The results:
1691491224580.png


I find it hard to follow along with the logic you're using. What is the order you're expecting this stuff to print in?

Also, vJass is the same thing as JASS, which is the same thing as GUI when the map script is set to JASS. Lua would be the only other environment to test this in.
 

Attachments

  • Test.w3m
    16.9 KB · Views: 1
Your demo map appears to be out of sync with your unit tests described here. I've attached an updated version of your demo map that bridges the missing event links.

The results:
View attachment 442778

I find it hard to follow along with the logic you're using. What is the order you're expecting this stuff to print in?

Also, vJass is the same thing as JASS, which is the same thing as GUI when the map script is set to JASS. Lua would be the only other environment to test this in.
Oh sorry. It's not a matter of order, the execution order of the listeners is more or less a mix of the order of the trigger folders & the trigger orders when first created.

The issue is just that Event1 should never be equal to 33 in this exemple. In my ex, the issue is that "Event1 = 5" listener is never called for ex. I discovered this bug because with your system my listener to UnitInActionEvent was provocking it.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,468
Your findings seem to be validated from a test I've found here:


I'd say the best solution is for me to hook TriggerRegisterVariableEvent from within UnitEvent, since it has so many varieties of events that spawn off of it. I already do this in Damage Engine 5, but it wouldn't need to be as complicated for Unit Event. This solution can obviously only work for vJass.

Lua doesn't really suffer the same problem, because the systems that support GUI use Event, which changes the behavior of registering and running GUI events entirely.
 
Your findings seem to be validated from a test I've found here:


I'd say the best solution is for me to hook TriggerRegisterVariableEvent from within UnitEvent, since it has so many varieties of events that spawn off of it. I already do this in Damage Engine 5, but it wouldn't need to be as complicated for Unit Event. This solution can obviously only work for vJass.

Lua doesn't really suffer the same problem, because the systems that support GUI use Event, which changes the behavior of registering and running GUI events entirely.
First, do you know if there is a place I could report this bug/limitation to Blizzard ?

Also I started playing with the code to see if I can do something (shadowing variables, adding a trigger, ...), but haven't found an elegant solution yet.

Also if players know it, they can at least change their design pattern to a chain of responsability, or a visitor maybe ?
 
Your findings seem to be validated from a test I've found here:


I'd say the best solution is for me to hook TriggerRegisterVariableEvent from within UnitEvent, since it has so many varieties of events that spawn off of it. I already do this in Damage Engine 5, but it wouldn't need to be as complicated for Unit Event. This solution can obviously only work for vJass.

Lua doesn't really suffer the same problem, because the systems that support GUI use Event, which changes the behavior of registering and running GUI events entirely.

After my testings this would work (but is not very elegant):
- Adding a "Wait 0.00 seconds" before emiting Event2 (postpones Event2 to the next frame).

Otherwise, yes a hook in vJass could do allow doing sort of a homemade observer pattern I guess, but I haven't tested doing it.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,468
After my testings this would work (but is not very elegant):
- Adding a "Wait 0.00 seconds" before emiting Event2 (postpones Event2 to the next frame).

Otherwise, yes a hook in vJass could do allow doing sort of a homemade observer pattern I guess, but I haven't tested doing it.
I did a zero second timer to solve an issue with UnitEvent during several events: unit reincarnation detection, transform detection, unit creation detection, maybe more. The thing is that those were needed for actual in-game expected behavior and not as workarounds for event execution.

The "hook" thing is definitely the right solution, because a lot of these events need to be handled at those exact instants.
 
I did a zero second timer to solve an issue with UnitEvent during several events: unit reincarnation detection, transform detection, unit creation detection, maybe more. The thing is that those were needed for actual in-game expected behavior and not as workarounds for event execution.

The "hook" thing is definitely the right solution, because a lot of these events need to be handled at those exact instants.
But it will be a pain to handle the different cases:
  • Operators other than "="
  • The listening trigger may read the value of UnitEvent
  • The listening trigger may turn itself OFF during execution
  • The listening trigger may cast new UnitEvents in the meanwhile
...

For example you'll need a little boring behavior like this when you cast an event no ? :
1 - Iterate over all the detected listening triggers. Check the variable name, check if it is the proper variable name, check if the trigger is ON, check is the assertion with operator & value is true. If yes, put it in a temporary trigger list.
2 - Turn OFF all the triggers of the list temporary (because a trigger may need the value of "UnitEvent" but you don't want the fired "Value of Real Variable" event to be triggered).
3 - Set the variable UnitEvent to the proper value.
4 - Turn ON again the triggers of the list
5 - Manualy execute the triggers of the list with condition checking.
6 - Save is the triggers of the list are now ON or OFF. Turn them OFF again.
7 - Reset the variable UnitEvent to 0.
8 - Restore the ON/OFF status of the trigger.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,468
I've already handled all of those edge cases in Damage Engine in both vJass and Lua. Doing it for Unit Event is much cleaner because there is only "Equal to" to account for.

Reading the value of the real variable is incorrect. You want separate triggers to handle separate logic. The real variable is just to trigger the event, but not to measure which event was triggered. And subsequent triggers should absolutely not be changing the value of the real variable regardless, unless those triggers merely re-triggered Unit Event itself.

The real variable event should only be used for registering a trigger, but not for use within a condition. Separate conditional logic should be used in those scenarios, such as adding an eventId to Damage Engine or Unit Event. If such conditional logic is wanted by the community, then I am happy to add it in, but it could get confusing.
 
It may be useless, but I reported the issue to Blizzard. It suxx that I can't link them a map to reproduce.

 
Top