Staredit Network > Forums > SC1 UMS Mapmaking Assistance > Topic: Are triggers guaranteed to execute in order?
Are triggers guaranteed to execute in order?
Mar 7 2019, 11:33 pm
By: sethmachine  

Mar 7 2019, 11:33 pm sethmachine Post #1



Hi,

When a trigger cycle happens, does the game take a snapshot of the current state (units, deaths, resources, etc.) and execute all triggers based on that state of the game OR during the "frames" of trigger execution, if the game state changes, would each trigger in the trigger cycle check for this?

E.g. suppose I have these 2 triggers who have the same conditions, but if one them goes first, the other won't run:

Trigger 1:
Code
if Commands(Player 1, AT LEAST, 10, Terran Marine):
 DisplayTextMessage("Player 1 commands at least 10 marines!")


Trigger 2:
Code
if Commands(Player 1, AT LEAST, 10, Terran Marine):
  KillUnits(Player 1, ALL, Terran Marine)


Assume these triggers are put in this order (each owned by Player 1). Both triggers have the same conditions, but Trigger 2 removes the state where the conditions are true.

Is there a scenario where Trigger 2 will execute before Trigger 1, thus preventing the DisplayTextMessage from showing?

Which of these scenarios is true?

A. If the game takes a snapshot of the current state when executing the trigger cycle, then Trigger 1 should always execute followed by Trigger 2.

B. If each trigger reads the game state as it's executed, then it's possible during the "frame" Trigger 1 executes, Player 1 only commanded 9 marines but in the next "frame", Player 1 commands 10 marines and this is the "frame" where Trigger 2 would execute. I would call this undesirable, because it becomes harder to reason about triggers and it's almost like introducing fake parallelism/dining philosopher problem for seemingly no gain (since Starcraft presumably is single threaded).



None.

Mar 8 2019, 12:33 am Swampfox Post #2



From http://www.staredit.net/wiki/index.php?title=Triggers

Quote
During a trigger cycle, only trigger actions happen -- nothing else occurs in the game. We cannot see the game stop, however, because a single trigger cycle can occur during the span of a few milliseconds. This means that if you are, for example, trying to detect the presence of a unit in a given location, you do not have to worry about it exiting the location during a trigger cycle (and being missed by your trigger). It will only move if a trigger action moves it.

StarCraft checks this trigger list in a very particular order. It starts at the top of the list and begins to search for Player 1 triggers. (This includes triggers owned by All Players and triggers owned by whichever Force contains Player 1.) When it finds one of these triggers, it checks the conditions for that trigger, and if the conditions are all true, it executes the trigger. Once StarCraft has reached the end of the trigger list, it returns to the top of the list and repeats the process for Players 2, 3, 4, 5, 6, 7, and 8. The trigger cycle ends when StarCraft finishes checking triggers for Player 8. Note that the game will not run triggers owned by players who have left (or were never in) the game. (Triggers owned by multiple players will still be checked, but not for any owners that are no longer present.)




None.

Mar 8 2019, 12:42 am sethmachine Post #3



Thanks, so it looks like Scenario A applies (both triggers would run assuming Trigger 1 came before Trigger 2 in the trigger list).



None.

Mar 8 2019, 2:11 am NudeRaider Post #4

We can't explain the universe, just describe it; and we don't know whether our theories are true, we just know they're not wrong. >Harald Lesch

During the trigger phase gameplay is halted, so it's impossible that a unit is killed (or created, or moves) "on the map" (not by a trigger) between the checking of 2 triggers. This holds true for triggers owned by other players as well. However, as you would expect, triggers creating, or moving, etc. units come into effect immediatly and will be recognized by follow-up triggers.

But there's several quirks one has to account for:

- The bring exception: There's some actions that won't immediately update the unit array that is checked by bring, so depending on the actions used, the bring condition might behave unexpectedly. Bring will always update at the beginning of a new trigger loop.

- Commands lags behind by one trigger loop (or frame?) so it might fire 1 trigger loop later than the same condition that uses brings.

- trigger sorting vs. actual execution order. Even when a trigger owned by P8 is above a trigger of P1 in the trigger list in Scmdraft, P1's trigger will fire first, because sc goes trough the triggers player by player, (forces and All Players are resolved into dedicated players) starting with P1, then P2, then P3, etc.

In practice this can create a scenario where a trigger A that is checked before trigger B fires delayed by 1 trigger loop when between those two there is a trigger C, making the condition true that both A and B are checking for.

- waits. When sc encounters a wait action
  • the current trigger loop is halted (and its state saved onto a stack)
  • The current trigger won't even finish (but will continue later)
  • a new trigger loop is immediately triggered (this new loop will skip the trigger that caused it)
  • after the "injected" trigger loop finishes and the time specified in the wait has passed the original trigger loop in its half-finished state will be fetched from the stack and continues after the encountered wait
  • this is used by hyper triggers to constantly induce trigger loops
This can lead to scenarios where one trigger is executed twice or more before a trigger below it is even checked once.

Post has been edited 1 time(s), last time on Mar 8 2019, 6:56 pm by NudeRaider. Reason: stupid typo




Mar 8 2019, 4:24 am Lanthanide Post #5



Quote from NudeRaider
- The bring exception: There's some actions that won't immediately update the unit array that is checked by bring, so depending on the actions used, the bring condition might behave unexpectedly. Bring will always update at the beginning of a new trigger loop.
Correct but incomplete.

Quote from NudeRaider
- Commands lacks behind by one trigger loop (or frame?) so it might fire 1 trigger loop later than the same condition that uses brings.
Wrong.

Here's the actual details about how Bring and Command work: http://www.staredit.net/topic/14321/#9



None.

Mar 8 2019, 6:54 pm NudeRaider Post #6

We can't explain the universe, just describe it; and we don't know whether our theories are true, we just know they're not wrong. >Harald Lesch

Quote from Lanthanide
Quote from NudeRaider
- The bring exception: There's some actions that won't immediately update the unit array that is checked by bring, so depending on the actions used, the bring condition might behave unexpectedly. Bring will always update at the beginning of a new trigger loop.
Correct but incomplete.
If you are referring to the this:
Quote
Move Location, Remove Unit, Remove Unit At, Kill Unit, Kill Unit At, Move Unit ---- Value is reset to 0.
The list of actions that update bring -- I omitted these on purpose to keep it simple. He can ask if he needs more info on that, in which case I usually refer to here, because it's a more practical explanation and states the one (? or just most common?) action that doesn't invalidate the array: Create Unit.
Otherwise I'm not sure what you think is missing.




Quote from Lanthanide
Quote from NudeRaider
- Commands lacks behind by one trigger loop (or frame?) so it might fire 1 trigger loop later than the same condition that uses brings.
Wrong.
Let's say it's not always true. ;)

I attached a test map that shows a case where Commands lags behind. Each trigger loop it checks if a marine is there with with "Brings" or "Commands" and both. When the unit is created Commands is "just as fast" as Brings (1st pic), but when I remove the unit, it's detected with Commands for another trigger cycle (2nd pic). I assume similar shenenigans going on as with bring update, as if you change the map to *kill* the unit instead of removing it, or kill it with the lurker, both update at the same time.
Here's screen shots of the Display Text I used to test it:


Attachments:
deaths.scx
Hits: 1 Size: 36.12kb

Post has been edited 1 time(s), last time on Mar 8 2019, 7:07 pm by NudeRaider.




Mar 9 2019, 7:08 am Lanthanide Post #7



Quote from NudeRaider
Let's say it's not always true. ;)

I attached a test map that shows a case where Commands lags behind. Each trigger loop it checks if a marine is there with with "Brings" or "Commands" and both. When the unit is created Commands is "just as fast" as Brings (1st pic), but when I remove the unit, it's detected with Commands for another trigger cycle (2nd pic). I assume similar shenenigans going on as with bring update, as if you change the map to *kill* the unit instead of removing it, or kill it with the lurker, both update at the same time.
Here's screen shots of the Display Text I used to test it:
The thread/post I linked to explains how to ensure that Command always registers correctly and it will never 'lag behind' by 1 trigger cycle if you follow those instructions.

Thus it is not an inherent property of 'commands' that it will lag behind by 1 trigger cycle. It can if you don't use it properly, but the workaround to use it properly is very trivial that once you know it there's no reason not to use it.



None.

Mar 9 2019, 1:47 pm NudeRaider Post #8

We can't explain the universe, just describe it; and we don't know whether our theories are true, we just know they're not wrong. >Harald Lesch

That doesn't make anything I said wrong. In fact it confirms it as one of the quirks you have to be aware of.

Also which explanation are you referring to? If anything, Heinermans post states that the same should happen whether I use Kill Unit or Remove Unit (which it doesn't).
Also adding random Move Locations (like you suggested in the quoted post) seems to change nothing.
Please use the test map to verify and explain what you did to it if you can give any insights to my questions.




Mar 10 2019, 1:19 am Lanthanide Post #9



Quote from NudeRaider
That doesn't make anything I said wrong.
Yes it does. You claimed this:
Quote from NudeRaider
- Commands lags behind by one trigger loop (or frame?) so it might fire 1 trigger loop later than the same condition that uses brings.
You've stated this as an incontrovertible fact. It isn't.

Quote from NudeRaider
Also which explanation are you referring to? If anything, Heinermann'ss post states that the same should happen whether I use Kill Unit or Remove Unit (which it doesn't).
Heinermann's explanation.

I tested with your map and Remove Unit and Move Location does not appear to work in this instance so Heinermann's explanation appears incomplete.

However Giving the marine to P1 works as expected. Replace your first 2 triggers with this:

Quote
Trigger("Player 1"){
Conditions:
Switch("Switch1", not set);

Actions:
Display Text Message(Always Display, "\r\n----- NEW CYCLE -----");
Preserve Trigger();
}

//-----------------------------------------------------------------//

Trigger("Player 1"){
Conditions:
Command("Player 8", "Men", At least, 1);

Actions:
Set Switch("Switch1", set);
Give Units to Player("Player 8", "Player 1", "Terran Marine", All, "Anywhere");
Remove Unit("All players", "Terran Marine");
}

//-----------------------------------------------------------------//

My previous experience with the Commands condition has pretty much always been involving triggers that perform a Give action between players, which is likely why I never found any deficiency in Heinermann's explanation.



None.

Mar 10 2019, 2:00 am NudeRaider Post #10

We can't explain the universe, just describe it; and we don't know whether our theories are true, we just know they're not wrong. >Harald Lesch

Quote from Lanthanide
You've stated this as an incontrovertible fact. It isn't.
The "might" was supposed to make it conditional, but I can see how my wording is misleading.

Quote from Lanthanide
Quote from NudeRaider
Also which explanation are you referring to? If anything, Heinermann'ss post states that the same should happen whether I use Kill Unit or Remove Unit (which it doesn't).
Heinermann's explanation.
lol. Don't play dumb now. The problem is Heinermann doesn't really explain anything. All he states is that "some count" is going to get refreshed (or not), depending on ... well he doesn't really say. He gives lists of conditions and actions, but not how to apply them. And nothing explains what this has at all to do with how Commands works.
I'm missing the "Commands lags behind under circumstances xyz" part and the "But you can do abc, to avoid it" to really call it an "explanation" - more like a technical discussion that apparently needs the whole thread as context.

Quote from Lanthanide
I tested with your map and Remove Unit and Move Location does not appear to work in this instance so Heinermann's explanation appears incomplete.

However Giving the marine to P1 works as expected.
Interesting. So it appears to "react" to different actions as Bring. Would be interesting to know which those are. But I must admit, I do not care enough anymore to do such pioneering work. ;)




Mar 10 2019, 3:00 am Lanthanide Post #11



Quote from NudeRaider
lol. Don't play dumb now. The problem is Heinermann doesn't really explain anything. All he states is that "some count" is going to get refreshed (or not), depending on ... well he doesn't really say.
He does say. Each time any one of those actions occurs it compares the stored variable value to the value assigned to the condition. If the value matches then the unit counts are not updated before the condition is checked. If the value is different then the unit counts are updated, the variable set to the new value and the condition is checked.

Quote from NudeRaider
He gives lists of conditions and actions, but not how to apply them. And nothing explains what this has at all to do with how Commands works.
I'm missing the "Commands lags behind under circumstances xyz" part and the "But you can do abc, to avoid it" to really call it an "explanation" - more like a technical discussion that apparently needs the whole thread as context.
Initially I linked to the post saying it explains how command technically works - which it does. I then said to refer to the thread/post for how to avoid the lagging - I never claimed that Heinermann's post specifically says how to avoid the '1 trigger loop lag' behaviour you've highlighted.

The whole area of discussion in general has been about how subsequent bring or command conditions don't work correctly when units are Given between players, and Heinermann's post makes it clear why that it is - because the Give action is not one of the ones that updates the variable so Bring / Give / Bring doesn't update the unit count for the 2nd Bring condition.

The map you've come up with that shows that Command is somehow affected by Give in this situation is weird though and isn't explained by Heinermann's description, unless there's some other modifier like it depending on the player the action is affecting and the owner of the trigger somehow, since you see there's a clause in the second bullet point where a different location will also reset the variable value.



None.

Mar 10 2019, 11:38 am NudeRaider Post #12

We can't explain the universe, just describe it; and we don't know whether our theories are true, we just know they're not wrong. >Harald Lesch

Quote from Lanthanide
He does say.
You are technically correct (the best kind of correct) but what I'm saying is that it's not helpful. He doesn't explain it in a way that someone can use to understand the concept, if they don't already know what's going on.
What makes this worse is the many inconsistencies: No clear distinction between actions and conditions, confusing information mixed in (see red parts below), and putting 2 of the same and one different thing in 1 list (see yellow, green and blue groups below).

Quote from Lanthanide
Each time any one of those actions occurs it compares the stored variable [...]
Just take this part. An action does something. Indiscriminately. It does not check a variable before that. Now that I know what is going on, I understand what you actually meant with it, (grouping actions and conditions because both affect the variable) but it's not suited as a first sentence explain it to someone.

Quote from Lanthanide
[...]it compares the stored variable value to the value assigned to the condition. If the value matches then the unit counts are not updated before the condition is checked. If the value is different then the unit counts are updated, the variable set to the new value and the condition is checked.
See this is better. Here you state that I only should look for conditions (so I can infer to ignore the 3rd list item for now) to understand it. But if you already confused people with the beginnging of the sentence it's hard to find the middle part that makes sense.


Alright... so please don't take all of this as an offense, I'm glad you took the time to clear it up for me. I just wanted to explain in detail what I had trouble with, and why I think just linking to Heinermann's snippet is not enough to really explain the issue to someone. It's not even suited as a "HowTo" avoid the issue, because it's doesn't clearly state what you're supposed to do to avoid it without understanding it.


In any case, I think I got (most of) it now. Here's what I got out of this:
(Feel free to skip to the conclusions and notes part, I think those are worthy of further discussion. the inbetween is just my thought process.)

Raw data (colored for easier referencing in the analysis)
Quote
There is a variable involved. It is first set to 0 as the trigger cycle begins. If this value is different than the value assigned by certain conditions then the count is updated.

Command, Command the Most, Command the Least leaderboard sorting ---- Value is set to 1.
Bring, Command the Most At, Command the Least At, leaderboard sorting ---- Value is set to 2. Reset if location is different than previous call.
Move Location, Remove Unit, Remove Unit At, Kill Unit, Kill Unit At, Move Unit ---- Value is reset to 0.
Analysis
The first sentence refers to a procedure that is run before each condition check. SC first checks a variable and the outcome decides whether or not SC updates the unit counts *[1]. Below are the rules for this precheck-variable.

So initially the variable is 0. And whenever I run any of those blue actions it's reset (to 0) again.
(Variable 0) When a yellow condition is encountered, the variable is checked against 1 and returns FALSE, so the count is updated and the condition will behave correctly. It now sets the variable to 1 and the condition is checked

(Variable 1) If the next condition is a yellow one again, the precheck returns TRUE, so the count is NOT updated. So if any not blue action changed something, it will use an outdated game state! The variable is set to 1 again and the condition checked.

(Variable 1) But if the next condition is a green one instead, it will update the count, because it matches 1 vs 2 which is FALSE. Now it normally sets the variable to 2, but there's also a case where it resets the variable: If a different location was checked than by the previous condition. *[2]

(Variable 2) The result of going from green to yellow should be obvious by now: The count is updated and the variable is set to 1.

Conclusions
  • Conditions from different groups following each other are always "safe" (guaranteed to be up to date)
  • Conditions that follow blue actions, are also safe. *[3]
  • Green conditions following each other are "a bit safer" because they have fewer cases that lead to outdated game state, compared to yellow conditions.
  • Bring is in the green group, so it can be considered a bit safer than Commands.
  • Looking at the blue action list, it's notable that Create Unit is missing, like with the Bring anomaly DoA described in 2008. And Give Unit, which was where you (Lanth) stumbled over it.
  • WTF is red? And how can it appear in 2 different behavior groups at once?

Notes
*[1] "What unit counts?", you may ask. Good question! SC keeps track of the units inside a location with an array that is populated at the beginning of each trigger cycle. To reduce the performance impact this list is not updated after each trigger, but only under certain circumstances. Unfortunately they forgot to include some possible circumstances, which makes conditions checking against this array unreliable, which is why we're talking about it here in the first place. ;)

*[2] Am I correct in assuming this includes conditions within the same trigger?

*[3] This one is probably the biggest take-away because we can formulate a clear instruction with it to avoid running into conditions checking an outdated unit count inside a location:
In triggers, that rely on each other and being correctly executed within the same trigger loop, always include a dummy action from the blue list in the predecessor trigger. Heinermann recommends Move Unit to keep performance impact low.



Quote from Lanthanide
The map you've come up with that shows that Command is somehow affected by Give in this situation is weird though and isn't explained by Heinermann's description, unless there's some other modifier like it depending on the player the action is affecting and the owner of the trigger somehow, since you see there's a clause in the second bullet point where a different location will also reset the variable value.
Theory says that using Bring after Commands (or vice versa) should always force an update. It must be the part where it says "Reset if location is different than previous call." - which is only valid for Brings, but not Commands.
But this also means that conditions would not set the variable, but then the whole concept makes no sense, because what would set it to 1 or 2 then?

At this point I'd like to mention this post (part of the discussion that DoA resolved) that seems to draw the same conclusion about switching Commands and Brings up being safe.




Options
  Back to forum
Please log in to reply to this topic or to report it.
Members in this topic: None.
[07:45 pm]
Suicidal Insanity -- SEN? Secure? lawl never thought I'd see that
[06:32 pm]
MTiger156 -- Been playing around with the API's i was able to dig up in the javascript. Seems there's more to it than just forcing parameter values. Security pretty tight up in here.
[05:57 pm]
NudeRaider -- tbh thats the most likely way people got told it
[03:43 pm]
Suicidal Insanity -- Why would the FTP password be in the SQL DB? Unless somebody sent it via PM :X
[03:23 pm]
Vrael -- pearfishing*
[03:23 pm]
Vrael -- spearphishing**
[03:23 pm]
Vrael -- I accept the challenge, I will begin by discovering Moose's email and spearphising him and using social media tactics to learn about the mods lives, then I will use that leverage to hold the site ransom until they yield the password!
[02:27 pm]
MTiger156 -- NudeRaider
NudeRaider shouted: Vrael basically any mod has that power at least indirectly by rewarding a post. Anyone with SQL access can obviously do whatever. Not sure if an explicit "give this member x minerals"-button exists.
hmm... if i somehow successfully hack into the server DB, i could give myself minerals AND retrieve that FTP password for you mods :D (unless its not in DB then :( )
[01:45 pm]
NudeRaider -- probably cursed too
[01:33 pm]
NudeRaider -- modern pirate treasure
Please log in to shout.


Members Online: Roy, GGmano, Wing Zero