[Trigger] Desync in multiplayer without obvious reason

Level 6
Joined
Mar 27, 2019
Messages
56
Hi there,

if I disable the Tutorial Trigger the Game no longer desyncs at the 30s - 60s mark.
Down below is every trigger related to the Tutorial Trigger.
I dont see a obvious reason for a desync, can anyone help me out here?

  • Game l Tutorial
    • Events
      • Time - Elapsed game time is 5.00 seconds
    • Conditions
    • Actions
      • Trigger - Turn on Unit l End Boss <gen>
      • -------- - --------
      • Neutral Building - Add |cffFFFFDELevel 1 Boss (Dummy) to all marketplaces with 0 in stock and a max stock of 1
      • Cinematic - Clear the screen of text messages for (All players).
      • Game - Display to (All players) for 13.32 seconds the text: |cffff9619» This i...
      • Game - Display to (All players) for 13.32 seconds the text:
      • Sound - Play Hint <gen>
      • For each (Integer A) from 1 to 12, do (Actions)
        • Loop - Actions
          • Unit - Unpause Player_Builder[(Integer A)]
          • Selection - Select Player_Headquarter[(Integer A)] for (Player((Integer A)))
      • For each (Integer A) from 1 to 12, do (Actions)
        • Loop - Actions
          • Camera - Lock camera target for (Owner of Player_Headquarter[(Integer A)]) to Player_Headquarter[(Integer A)], offset by (0.00, 0.00) using Default rotation
      • Wait 3.33 seconds
      • Cinematic - Clear the screen of text messages for (All players).
      • Game - Display to (All players) for 9.99 seconds the text: |cffff9619» This i...
      • Game - Display to (All players) for 9.99 seconds the text:
      • Sound - Play Hint <gen>
      • For each (Integer A) from 1 to 12, do (Actions)
        • Loop - Actions
          • Set VariableSet Temp_Point = (Position of Player_Barrack[(Integer A)])
          • Camera - Pan camera for (Player((Integer A))) to Temp_Point over 1.00 seconds
          • Selection - Select Player_Barrack[(Integer A)] for (Player((Integer A)))
          • Custom script: RemoveLocation(udg_Temp_Point)
      • Wait 1.00 seconds
      • For each (Integer A) from 1 to 12, do (Actions)
        • Loop - Actions
          • Camera - Lock camera target for (Owner of Player_Barrack[(Integer A)]) to Player_Barrack[(Integer A)], offset by (0.00, 0.00) using Default rotation
      • Wait 2.33 seconds
      • Cinematic - Clear the screen of text messages for (All players).
      • Game - Display to (All players) for 6.66 seconds the text: |cffff9619» This i...
      • Game - Display to (All players) for 6.66 seconds the text:
      • Sound - Play Hint <gen>
      • For each (Integer A) from 1 to 12, do (Actions)
        • Loop - Actions
          • Set VariableSet Temp_Point = (Position of Player_Builder[(Integer A)])
          • Camera - Pan camera for (Player((Integer A))) to Temp_Point over 1.00 seconds
          • Selection - Select Player_Builder[(Integer A)] for (Player((Integer A)))
          • Custom script: RemoveLocation(udg_Temp_Point)
      • Wait 1.00 seconds
      • Set VariableSet Temp_Group = (Units of type |cffFFFF00Void Aura)
      • Unit Group - Pick every unit in Temp_Group and do (Actions)
        • Loop - Actions
          • Unit - Unhide (Picked unit)
      • Custom script: DestroyGroup(udg_Temp_Group)
      • For each (Integer A) from 1 to 12, do (Actions)
        • Loop - Actions
          • Set VariableSet Temp_Point = (Position of Player_Builder[(Integer A)])
          • Set VariableSet Temp_Group = (Units within 1024.00 of Temp_Point matching ((((Matching unit) is A sapper) Equal to Wahr) and ((Matching unit) Not equal to Player_Builder[(Integer A)])).)
          • Unit Group - Pick every unit in Temp_Group and do (Actions)
            • Loop - Actions
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • ((Owner of Player_Builder[(Integer A)]) slot status) Equal to Is playing
                  • ((Owner of Player_Builder[(Integer A)]) slot status) Not equal to Has left the game
                • Then - Actions
                  • Unit - Add Tower Range Indicator (Aura) to (Picked unit)
                  • Unit - Change ownership of (Picked unit) to (Owner of Player_Builder[(Integer A)]) and Change color
                • Else - Actions
                  • Unit - Remove (Picked unit) from the game
          • Custom script: RemoveLocation(udg_Temp_Point)
          • Custom script: DestroyGroup(udg_Temp_Group)
          • Camera - Lock camera target for (Owner of Player_Builder[(Integer A)]) to Player_Builder[(Integer A)], offset by (0.00, 0.00) using Default rotation
      • Set VariableSet Temp_Group = (Units of type |cffFFFF00Void Aura)
      • Unit Group - Pick every unit in Temp_Group and do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Owner of (Picked unit)) Equal to Neutral Passive
            • Then - Actions
              • Unit - Remove (Picked unit) from the game
            • Else - Actions
              • Set VariableSet Temp_Point = (Position of (Picked unit))
              • Special Effect - Create a special effect at Temp_Point using Abilities\Spells\NightElf\Blink\BlinkTarget.mdl
              • Special Effect - Set Scale of (Last created special effect) to 1.25
              • Special Effect - Set Position - Z of (Last created special effect) to ((Position - Z of (Picked unit)) + 128.00)
              • Special Effect - Destroy (Last created special effect)
              • Custom script: RemoveLocation(udg_Temp_Point)
      • Custom script: DestroyGroup(udg_Temp_Group)
      • Wait 2.33 seconds
      • Countdown Timer - Start Game_Save_Timer as a Repeating timer that will expire in 60.00 seconds
      • Leaderboard - Change the title of Game_Leaderboard to (Income in + (String(Game_Count)))
      • Cinematic - Clear the screen of text messages for (All players).
      • Game - Display to (All players) for 3.33 seconds the text: |cffff9619» The Ga...
      • Game - Display to (All players) for 3.33 seconds the text:
      • Sound - Play GameFound <gen>
      • Countdown Timer - Start Game_Timer as a Repeating timer that will expire in 1.00 seconds
      • For each (Integer A) from 1 to 12, do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Player_Builder[(Integer A)] is alive) Equal to Wahr
            • Then - Actions
              • Unit - Set Player_Builder[(Integer A)] movement speed to 400.00
              • Special Effect - Create a special effect attached to the origin of Player_Builder[(Integer A)] using Abilities\Spells\Human\Resurrect\ResurrectTarget.mdl
              • Special Effect - Set Scale of (Last created special effect) to 0.50
              • Special Effect - Destroy (Last created special effect)
              • Set VariableSet Temp_Point = (Position of Player_Builder[(Integer A)])
              • Camera - Pan camera for (Owner of Player_Builder[(Integer A)]) to Temp_Point over 0.00 seconds
              • Custom script: RemoveLocation(udg_Temp_Point)
            • Else - Actions
      • Neutral Building - Remove |cffFFFFDELevel 1 Boss (Dummy) from all marketplaces
      • Neutral Building - Add |cffFFFFDELevel 1 Boss to all marketplaces with 0 in stock and a max stock of 1

  • Unit l End Boss
    • Events
      • Unit - A unit enters (Entire map)
    • Conditions
      • (Unit-type of (Triggering unit)) Equal to |cff520808Bloodheaven
    • Actions
      • Game - Display to (All players) for 5.00 seconds the text: (|cffffffc8The first |cffff6464Bloodheaven|r |cffffffc8has been sent!|r + <Empty String>)
      • Set VariableSet Temp_Unit = |cffFFA07AStronghold Clash|r 0056 <gen>
      • Set VariableSet Temp_String = Sound/Interface/Rescue.wav
      • Sound - Set pitch of Game_lastCreatedSound to 1.25
      • Custom script: AttachSoundUnit(udg_Temp_Unit, udg_Temp_String, 155, 1, 2000, false, false, 0, 1000000000)
      • Sound - Destroy Game_lastCreatedSound
      • Trigger - Turn off (This trigger)

  • Game l Global Income
    • Events
      • Time - Game_Timer expires
    • Conditions
    • Actions
      • Set VariableSet Game_Count = (Game_Count - 1)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Game_Count Equal to 8
        • Then - Actions
          • Trigger - Run Aura l Level Auto <gen> (ignoring conditions)
          • For each (Integer A) from 1 to 12, do (Actions)
            • Loop - Actions
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • Send_Toggle[(Integer A)] Equal to Wahr
                  • Send_Amount[(Integer A)] Greater than 0
                • Then - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • Send_Amount[(Integer A)] Greater than 5
                    • Then - Actions
                      • For each (Integer B) from 1 to 5, do (Actions)
                        • Loop - Actions
                          • Unit - Order Player_Barrack[(Integer A)] to train/upgrade to a Send_Type[(Integer A)]
                      • Unit - Order Player_Barrack[(Integer A)] to train/upgrade to a Send_Type[(Integer A)]
                    • Else - Actions
                      • For each (Integer B) from 1 to Send_Amount[(Integer A)], do (Actions)
                        • Loop - Actions
                          • Unit - Order Player_Barrack[(Integer A)] to train/upgrade to a Send_Type[(Integer A)]
                • Else - Actions
        • Else - Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Game_Count Less than or equal to 0
        • Then - Actions
          • Sound - Play IncomeGold <gen>
          • For each (Integer A) from 1 to 12, do (Actions)
            • Loop - Actions
              • Player - Add Player_Income[(Integer A)] to (Player((Integer A))).Current gold
              • Player - Add (Player_Income[(Integer A)] / 10) to (Player((Integer A))).Current lumber
          • Set VariableSet Game_Count = Game_Income_Display
          • Trigger - Run Game l Boss Replacement <gen> (ignoring conditions)
        • Else - Actions
      • Leaderboard - Change the title of Game_Leaderboard to (Income in + (String(Game_Count)))

  • Aura l Level Auto
    • Events
    • Conditions
    • Actions
      • Unit Group - Pick every unit in Group_Aura and do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Point-value of (Picked unit)) Greater than or equal to 9
            • Then - Actions
              • Unit Group - Remove (Picked unit) from Group_Aura.
            • Else - Actions
              • Set VariableSet Temp_Integer = (Load 0 of (Key (Picked unit).) from Game_Hashtable.)
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (Level of Game_Aura[Temp_Integer] for (Picked unit)) Greater than 0
                  • (Point-value of (Picked unit)) Less than (Load (5 + Temp_Integer) of (Key (Owner of (Picked unit)).) from Game_Hashtable.)
                • Then - Actions
                  • Unit - Order (Picked unit) to train/upgrade to a Game_Aura_Type[(((Temp_Integer - 1) x 9) + ((Point-value of (Picked unit)) + 1))]
                • Else - Actions

  • Game l Boss Replacement
    • Events
    • Conditions
    • Actions
      • Set VariableSet Temp_Integer = (Load 0 of 0 from Game_Hashtable.)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Temp_Integer Less than or equal to 0
        • Then - Actions
          • Neutral Building - Remove |cffFFFFDELevel 1 Boss (Dummy) from all marketplaces
          • Neutral Building - Remove |cffFFFFDELevel 1 Boss from all marketplaces
          • For each (Integer A) from 1 to 12, do (Actions)
            • Loop - Actions
              • Player - Make |cffFFFFDELevel 1 Boss Available for training/construction by (Player((Integer A)))
          • Custom script: DestroyTrigger(GetTriggeringTrigger())
        • Else - Actions
          • Hashtable - Save ((Load 0 of 0 from Game_Hashtable.) - 1) as 0 of 0 in Game_Hashtable.
 
If the Save/Load thing suggested above is not your problem, here's another idea: I have a suspicion that the Wait 2.33 seconds function was created for campaign missions and single player events. We don't usually think of something so basic as being "a problem." If we were building Warcraft 3 today, surely we would synchronize the wait function across all computers for use in multiplayer game events.

But, if it's possible that the "Wait" function is what I'm saying, then it might be better that you do not use it. And there is evidence for what I am saying -- there is a second "Wait" action that says it will wait in "game time seconds." What is the use for that? When are 2.00 seconds not equal to 2.00 seconds of gameplay? One possibility is the "game speed," wherein the game might be running at a rate accelerated versus its original programming. If the game is "FAST" then maybe 1.00 second is really 0.6 second or something, so the whole universe operates more quickly.

But if the synchronized game engine universe is operating on a faster clock, then what clock is the generic "Wait" function using? What if it was the local computer clock? Ouch, that might desync!

So what do we do about it? You could change all your waits to the "Wait 2.33 Game time seconds" wait function. But we can look under the hood at what that function does, because it's not a part of the game code but instead exposed in the default map script libraries defined in userspace that your map can replace. And it turns out, this function is junk! It uses the "Wait 2.33 seconds" generic function, and spins waiting and checking a Timer object in the hopes that the amount of time that passed is almost roughly the same as what passed in one of the lock-step network synchronized Timer objects. Ouch again! So if the "Wait" function has a desync, then the "Wait game time seconds" also has the same desync.

Then what can you do? Well, the solution is to not use any waits at all. If you take this trigger, chop it into pieces, and instead of each "Wait" behavior, use a new trigger with a new event, then you could have this same startup tutorial without wait functions! How might that look? Well, your first trigger would be the same up until "Wait 3.33 seconds" action. Then, at that point, the trigger would end.

Then, you would have another trigger to follow after, which would have the event, "Time - Elapsed game time is 8.33 seconds" using the network synchronized game time subsystem to define when actions fire.

  • Game l Tutorial
  • Events
    • Time - Elapsed game time is 5.00 seconds
  • Conditions
  • Actions
    • Trigger - Turn on Unit l End Boss <gen>
    • -------- - --------
    • Neutral Building - Add |cffFFFFDELevel 1 Boss (Dummy) to all marketplaces with 0 in stock and a max stock of 1
    • Cinematic - Clear the screen of text messages for (All players).
    • Game - Display to (All players) for 13.32 seconds the text: |cffff9619» This i...
    • Game - Display to (All players) for 13.32 seconds the text:
    • Sound - Play Hint <gen>
    • For each (Integer A) from 1 to 12, do (Actions)
      • Loop - Actions
        • Unit - Unpause Player_Builder[(Integer A)]
        • Selection - Select Player_Headquarter[(Integer A)] for (Player((Integer A)))
    • For each (Integer A) from 1 to 12, do (Actions)
      • Loop - Actions
        • Camera - Lock camera target for (Owner of Player_Headquarter[(Integer A)]) to Player_Headquarter[(Integer A)], offset by (0.00, 0.00) using Default rotation
  • Game l Tutorial (Part 2)
    • Events
      • Time - Elapsed game time is 8.33 seconds
    • Conditions
    • Actions
      • Cinematic - Clear the screen of text messages for (All players).
      • Game - Display to (All players) for 9.99 seconds the text: |cffff9619» This i...
      • Game - Display to (All players) for 9.99 seconds the text:
      • Sound - Play Hint <gen>
      • For each (Integer A) from 1 to 12, do (Actions)
        • Loop - Actions
          • Set VariableSet Temp_Point = (Position of Player_Barrack[(Integer A)])
          • Camera - Pan camera for (Player((Integer A))) to Temp_Point over 1.00 seconds
          • Selection - Select Player_Barrack[(Integer A)] for (Player((Integer A)))
          • Custom script: RemoveLocation(udg_Temp_Point)
As a note, I did not test this and I am not sure of my theory above. Desyncs are often mysterious and vicious beasts. But what I am saying makes logical sense to me as a possible cause of a problem. That's not a guarantee that it is the problem, but I'm saying, I think it might be worth trying.
 
Level 6
Joined
Mar 27, 2019
Messages
56
try disabling this in your tutorial trigger:
  • Countdown Timer - Start Game_Save_Timer as a Repeating timer that will expire in 60.00 seconds
I am just assuming from the variable name that this is related to some save/load system? If so, that could be the cause of desync.
It used to be for a Save/Load System but I disabled it.

Im pretty baffled what the issue is:

I just disabled this function along with the Global Income Trigger:

  • Countdown Timer - Start Game_Timer as a Repeating timer that will expire in 1.00 seconds
And now no desyncs happen.
But if I enable the Start Game_Timer once again while keeping Global Income Trigger disabled it will still desync.

I dont know what the cause of this could be, arent Countdown Timer a safe function to use????
I'm using LUA instead of JASS in the Map setting.

I even have a example map down below.
 

Attachments

  • Stronghold Test Map.w3m
    154.7 KB · Views: 3
Level 6
Joined
Mar 27, 2019
Messages
56
If the Save/Load thing suggested above is not your problem, here's another idea: I have a suspicion that the Wait 2.33 seconds function was created for campaign missions and single player events. We don't usually think of something so basic as being "a problem." If we were building Warcraft 3 today, surely we would synchronize the wait function across all computers for use in multiplayer game events.

But, if it's possible that the "Wait" function is what I'm saying, then it might be better that you do not use it. And there is evidence for what I am saying -- there is a second "Wait" action that says it will wait in "game time seconds." What is the use for that? When are 2.00 seconds not equal to 2.00 seconds of gameplay? One possibility is the "game speed," wherein the game might be running at a rate accelerated versus its original programming. If the game is "FAST" then maybe 1.00 second is really 0.6 second or something, so the whole universe operates more quickly.

But if the synchronized game engine universe is operating on a faster clock, then what clock is the generic "Wait" function using? What if it was the local computer clock? Ouch, that might desync!

So what do we do about it? You could change all your waits to the "Wait 2.33 Game time seconds" wait function. But we can look under the hood at what that function does, because it's not a part of the game code but instead exposed in the default map script libraries defined in userspace that your map can replace. And it turns out, this function is junk! It uses the "Wait 2.33 seconds" generic function, and spins waiting and checking a Timer object in the hopes that the amount of time that passed is almost roughly the same as what passed in one of the lock-step network synchronized Timer objects. Ouch again! So if the "Wait" function has a desync, then the "Wait game time seconds" also has the same desync.

Then what can you do? Well, the solution is to not use any waits at all. If you take this trigger, chop it into pieces, and instead of each "Wait" behavior, use a new trigger with a new event, then you could have this same startup tutorial without wait functions! How might that look? Well, your first trigger would be the same up until "Wait 3.33 seconds" action. Then, at that point, the trigger would end.

Then, you would have another trigger to follow after, which would have the event, "Time - Elapsed game time is 8.33 seconds" using the network synchronized game time subsystem to define when actions fire.

  • Game l Tutorial
  • Events
    • Time - Elapsed game time is 5.00 seconds
  • Conditions
  • Actions
    • Trigger - Turn on Unit l End Boss <gen>
    • -------- - --------
    • Neutral Building - Add |cffFFFFDELevel 1 Boss (Dummy) to all marketplaces with 0 in stock and a max stock of 1
    • Cinematic - Clear the screen of text messages for (All players).
    • Game - Display to (All players) for 13.32 seconds the text: |cffff9619» This i...
    • Game - Display to (All players) for 13.32 seconds the text:
    • Sound - Play Hint <gen>
    • For each (Integer A) from 1 to 12, do (Actions)
      • Loop - Actions
        • Unit - Unpause Player_Builder[(Integer A)]
        • Selection - Select Player_Headquarter[(Integer A)] for (Player((Integer A)))
    • For each (Integer A) from 1 to 12, do (Actions)
      • Loop - Actions
        • Camera - Lock camera target for (Owner of Player_Headquarter[(Integer A)]) to Player_Headquarter[(Integer A)], offset by (0.00, 0.00) using Default rotation
  • Game l Tutorial (Part 2)
    • Events
      • Time - Elapsed game time is 8.33 seconds
    • Conditions
    • Actions
      • Cinematic - Clear the screen of text messages for (All players).
      • Game - Display to (All players) for 9.99 seconds the text: |cffff9619» This i...
      • Game - Display to (All players) for 9.99 seconds the text:
      • Sound - Play Hint <gen>
      • For each (Integer A) from 1 to 12, do (Actions)
        • Loop - Actions
          • Set VariableSet Temp_Point = (Position of Player_Barrack[(Integer A)])
          • Camera - Pan camera for (Player((Integer A))) to Temp_Point over 1.00 seconds
          • Selection - Select Player_Barrack[(Integer A)] for (Player((Integer A)))
          • Custom script: RemoveLocation(udg_Temp_Point)
As a note, I did not test this and I am not sure of my theory above. Desyncs are often mysterious and vicious beasts. But what I am saying makes logical sense to me as a possible cause of a problem. That's not a guarantee that it is the problem, but I'm saying, I think it might be worth trying.
Tried this, this still desyncs, somehow my Countdown Timer function is the reason for a desync.
Comment above is a further explanation... im very confused.
 
So, when I was rewriting my remake of Warcraft 3 to emulate it -- which doesn't have a desync detector yet by the way, and is probably riddled with desyncs -- we might be able to use that to imagine the sort of programming pitfalls Microsoft Activision could fall into even if obviously the programming pitfalls that I fall into will be different ones.

I recently had an issue where after I rewrote much of the Jass VM to work in a way that supported "wait" functions and the mechanism for pausing and resuming the actions of a trigger to accommodate a wait function, it turned out that during that refactor I mucked up the functions such as ForGroup and friends that run Jass code from within native(non-JASS game engine code) by trying to slap a "queueThread(...)" call on everything. And ForGroup actually has to be better than queueing up the next behavior to run, because it is inline even in Blizzard.j for that to work correctly, and expects that when call ForGroup(...) terminates, the state of the machine at that point will be after the execution of the code handler per each unit in the group, not before.

So I started refactoring things that would create a thread context to run a function, and changed them to immediately run that context as far as it would go (until the function returned or hit a wait function and shut off). In my code, again which technically has nothing to do with Microsoft Activision's code and we might say is my attempt at a "reference implementation" but in no way knows the original, within that context I used this "queueThread" function in the jass Timer fire implementation, which was the same one that I replaced with runThreadUntilCompletion when trying to fix ForGroup. So, one possible mistake I might have made would have been if I slapped the same change on this code as ForGroup. We could imagine it might be changed to immediately run the code when the timer expires.

So, on my "reference implementation" I have really bad Timer granularity and only have Jass and not Lua because I set the scope at mostly trying to emulate Warcraft III and not the Reforged (except for as an occasional test for entertainment in passing). But, if we consider the possibility of a system with better Timer granularity, maybe the only way that the Timers would sync would be if they queue their firing onto the lock-step synchronized ticks instead of the exact "time" that they expire. This assumes a different implementation than mine, wherein Timers themselves are high resolution and then are rounded off, maybe to the nearest lock-step synchronized tick corresponding to when they fire. If somebody went into there and caused these timers to immediately execute their code handler callback when they fire -- maybe in the lua universe -- instead of dumping the "what to do" portion onto a delay that actuates later when a lock-step network synchronization tick happens -- maybe that would cause this?

If so, shouldn't you be able to solve the problem by changing your trigger actions when the timer fires, to then call the Sync natives to send data in a Sync event, and then make a second trigger that fires on the Sync event, which would happen at the same time on each computer corresponding to the lock-step network synchronization? If that is too time consuming and you don't want to test what I'm saying, here's a better test:
  • try editing these timers so that the only Action in the Actions section when they fire is something that we know is OK to do in local code. Maybe something as simple as DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "this is a test")
  • If the above code desyncs, despite being local-safe, then the desync bug in the game (or lua vm) is the result of using timers at all. That's pretty egregious. Is the problem only related to repeating timers? What happens if you make a series of non-repeating timers? If you can accomplish it all with non-repeating timers, then maybe stick to those.
  • If the above code does not desync, then the desync bug in the game (or lua vm) is the result of timers firing at slightly different times per computer, which could be trivially solved by having the host computer fire a sync event to the clients when his timer fires, and writing your trigger actions into the sync event trigger instead of the timer event trigger
 
Hi there,

if I disable the Tutorial Trigger the Game no longer desyncs at the 30s - 60s mark.
Down below is every trigger related to the Tutorial Trigger.
I dont see a obvious reason for a desync, can anyone help me out here?

  • Game l Tutorial
    • Events
      • Time - Elapsed game time is 5.00 seconds
    • Conditions
    • Actions
      • Trigger - Turn on Unit l End Boss <gen>
      • -------- - --------
      • Neutral Building - Add |cffFFFFDELevel 1 Boss (Dummy) to all marketplaces with 0 in stock and a max stock of 1
      • Cinematic - Clear the screen of text messages for (All players).
      • Game - Display to (All players) for 13.32 seconds the text: |cffff9619» This i...
      • Game - Display to (All players) for 13.32 seconds the text:
      • Sound - Play Hint <gen>
      • For each (Integer A) from 1 to 12, do (Actions)
        • Loop - Actions
          • Unit - Unpause Player_Builder[(Integer A)]
          • Selection - Select Player_Headquarter[(Integer A)] for (Player((Integer A)))
      • For each (Integer A) from 1 to 12, do (Actions)
        • Loop - Actions
          • Camera - Lock camera target for (Owner of Player_Headquarter[(Integer A)]) to Player_Headquarter[(Integer A)], offset by (0.00, 0.00) using Default rotation
      • Wait 3.33 seconds
      • Cinematic - Clear the screen of text messages for (All players).
      • Game - Display to (All players) for 9.99 seconds the text: |cffff9619» This i...
      • Game - Display to (All players) for 9.99 seconds the text:
      • Sound - Play Hint <gen>
      • For each (Integer A) from 1 to 12, do (Actions)
        • Loop - Actions
          • Set VariableSet Temp_Point = (Position of Player_Barrack[(Integer A)])
          • Camera - Pan camera for (Player((Integer A))) to Temp_Point over 1.00 seconds
          • Selection - Select Player_Barrack[(Integer A)] for (Player((Integer A)))
          • Custom script: RemoveLocation(udg_Temp_Point)
      • Wait 1.00 seconds
      • For each (Integer A) from 1 to 12, do (Actions)
        • Loop - Actions
          • Camera - Lock camera target for (Owner of Player_Barrack[(Integer A)]) to Player_Barrack[(Integer A)], offset by (0.00, 0.00) using Default rotation
      • Wait 2.33 seconds
      • Cinematic - Clear the screen of text messages for (All players).
      • Game - Display to (All players) for 6.66 seconds the text: |cffff9619» This i...
      • Game - Display to (All players) for 6.66 seconds the text:
      • Sound - Play Hint <gen>
      • For each (Integer A) from 1 to 12, do (Actions)
        • Loop - Actions
          • Set VariableSet Temp_Point = (Position of Player_Builder[(Integer A)])
          • Camera - Pan camera for (Player((Integer A))) to Temp_Point over 1.00 seconds
          • Selection - Select Player_Builder[(Integer A)] for (Player((Integer A)))
          • Custom script: RemoveLocation(udg_Temp_Point)
      • Wait 1.00 seconds
      • Set VariableSet Temp_Group = (Units of type |cffFFFF00Void Aura)
      • Unit Group - Pick every unit in Temp_Group and do (Actions)
        • Loop - Actions
          • Unit - Unhide (Picked unit)
      • Custom script: DestroyGroup(udg_Temp_Group)
      • For each (Integer A) from 1 to 12, do (Actions)
        • Loop - Actions
          • Set VariableSet Temp_Point = (Position of Player_Builder[(Integer A)])
          • Set VariableSet Temp_Group = (Units within 1024.00 of Temp_Point matching ((((Matching unit) is A sapper) Equal to Wahr) and ((Matching unit) Not equal to Player_Builder[(Integer A)])).)
          • Unit Group - Pick every unit in Temp_Group and do (Actions)
            • Loop - Actions
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • ((Owner of Player_Builder[(Integer A)]) slot status) Equal to Is playing
                  • ((Owner of Player_Builder[(Integer A)]) slot status) Not equal to Has left the game
                • Then - Actions
                  • Unit - Add Tower Range Indicator (Aura) to (Picked unit)
                  • Unit - Change ownership of (Picked unit) to (Owner of Player_Builder[(Integer A)]) and Change color
                • Else - Actions
                  • Unit - Remove (Picked unit) from the game
          • Custom script: RemoveLocation(udg_Temp_Point)
          • Custom script: DestroyGroup(udg_Temp_Group)
          • Camera - Lock camera target for (Owner of Player_Builder[(Integer A)]) to Player_Builder[(Integer A)], offset by (0.00, 0.00) using Default rotation
      • Set VariableSet Temp_Group = (Units of type |cffFFFF00Void Aura)
      • Unit Group - Pick every unit in Temp_Group and do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Owner of (Picked unit)) Equal to Neutral Passive
            • Then - Actions
              • Unit - Remove (Picked unit) from the game
            • Else - Actions
              • Set VariableSet Temp_Point = (Position of (Picked unit))
              • Special Effect - Create a special effect at Temp_Point using Abilities\Spells\NightElf\Blink\BlinkTarget.mdl
              • Special Effect - Set Scale of (Last created special effect) to 1.25
              • Special Effect - Set Position - Z of (Last created special effect) to ((Position - Z of (Picked unit)) + 128.00)
              • Special Effect - Destroy (Last created special effect)
              • Custom script: RemoveLocation(udg_Temp_Point)
      • Custom script: DestroyGroup(udg_Temp_Group)
      • Wait 2.33 seconds
      • Countdown Timer - Start Game_Save_Timer as a Repeating timer that will expire in 60.00 seconds
      • Leaderboard - Change the title of Game_Leaderboard to (Income in + (String(Game_Count)))
      • Cinematic - Clear the screen of text messages for (All players).
      • Game - Display to (All players) for 3.33 seconds the text: |cffff9619» The Ga...
      • Game - Display to (All players) for 3.33 seconds the text:
      • Sound - Play GameFound <gen>
      • Countdown Timer - Start Game_Timer as a Repeating timer that will expire in 1.00 seconds
      • For each (Integer A) from 1 to 12, do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Player_Builder[(Integer A)] is alive) Equal to Wahr
            • Then - Actions
              • Unit - Set Player_Builder[(Integer A)] movement speed to 400.00
              • Special Effect - Create a special effect attached to the origin of Player_Builder[(Integer A)] using Abilities\Spells\Human\Resurrect\ResurrectTarget.mdl
              • Special Effect - Set Scale of (Last created special effect) to 0.50
              • Special Effect - Destroy (Last created special effect)
              • Set VariableSet Temp_Point = (Position of Player_Builder[(Integer A)])
              • Camera - Pan camera for (Owner of Player_Builder[(Integer A)]) to Temp_Point over 0.00 seconds
              • Custom script: RemoveLocation(udg_Temp_Point)
            • Else - Actions
      • Neutral Building - Remove |cffFFFFDELevel 1 Boss (Dummy) from all marketplaces
      • Neutral Building - Add |cffFFFFDELevel 1 Boss to all marketplaces with 0 in stock and a max stock of 1

  • Unit l End Boss
    • Events
      • Unit - A unit enters (Entire map)
    • Conditions
      • (Unit-type of (Triggering unit)) Equal to |cff520808Bloodheaven
    • Actions
      • Game - Display to (All players) for 5.00 seconds the text: (|cffffffc8The first |cffff6464Bloodheaven|r |cffffffc8has been sent!|r + <Empty String>)
      • Set VariableSet Temp_Unit = |cffFFA07AStronghold Clash|r 0056 <gen>
      • Set VariableSet Temp_String = Sound/Interface/Rescue.wav
      • Sound - Set pitch of Game_lastCreatedSound to 1.25
      • Custom script: AttachSoundUnit(udg_Temp_Unit, udg_Temp_String, 155, 1, 2000, false, false, 0, 1000000000)
      • Sound - Destroy Game_lastCreatedSound
      • Trigger - Turn off (This trigger)

  • Game l Global Income
    • Events
      • Time - Game_Timer expires
    • Conditions
    • Actions
      • Set VariableSet Game_Count = (Game_Count - 1)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Game_Count Equal to 8
        • Then - Actions
          • Trigger - Run Aura l Level Auto <gen> (ignoring conditions)
          • For each (Integer A) from 1 to 12, do (Actions)
            • Loop - Actions
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • Send_Toggle[(Integer A)] Equal to Wahr
                  • Send_Amount[(Integer A)] Greater than 0
                • Then - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • Send_Amount[(Integer A)] Greater than 5
                    • Then - Actions
                      • For each (Integer B) from 1 to 5, do (Actions)
                        • Loop - Actions
                          • Unit - Order Player_Barrack[(Integer A)] to train/upgrade to a Send_Type[(Integer A)]
                      • Unit - Order Player_Barrack[(Integer A)] to train/upgrade to a Send_Type[(Integer A)]
                    • Else - Actions
                      • For each (Integer B) from 1 to Send_Amount[(Integer A)], do (Actions)
                        • Loop - Actions
                          • Unit - Order Player_Barrack[(Integer A)] to train/upgrade to a Send_Type[(Integer A)]
                • Else - Actions
        • Else - Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Game_Count Less than or equal to 0
        • Then - Actions
          • Sound - Play IncomeGold <gen>
          • For each (Integer A) from 1 to 12, do (Actions)
            • Loop - Actions
              • Player - Add Player_Income[(Integer A)] to (Player((Integer A))).Current gold
              • Player - Add (Player_Income[(Integer A)] / 10) to (Player((Integer A))).Current lumber
          • Set VariableSet Game_Count = Game_Income_Display
          • Trigger - Run Game l Boss Replacement <gen> (ignoring conditions)
        • Else - Actions
      • Leaderboard - Change the title of Game_Leaderboard to (Income in + (String(Game_Count)))

  • Aura l Level Auto
    • Events
    • Conditions
    • Actions
      • Unit Group - Pick every unit in Group_Aura and do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Point-value of (Picked unit)) Greater than or equal to 9
            • Then - Actions
              • Unit Group - Remove (Picked unit) from Group_Aura.
            • Else - Actions
              • Set VariableSet Temp_Integer = (Load 0 of (Key (Picked unit).) from Game_Hashtable.)
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (Level of Game_Aura[Temp_Integer] for (Picked unit)) Greater than 0
                  • (Point-value of (Picked unit)) Less than (Load (5 + Temp_Integer) of (Key (Owner of (Picked unit)).) from Game_Hashtable.)
                • Then - Actions
                  • Unit - Order (Picked unit) to train/upgrade to a Game_Aura_Type[(((Temp_Integer - 1) x 9) + ((Point-value of (Picked unit)) + 1))]
                • Else - Actions

  • Game l Boss Replacement
    • Events
    • Conditions
    • Actions
      • Set VariableSet Temp_Integer = (Load 0 of 0 from Game_Hashtable.)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Temp_Integer Less than or equal to 0
        • Then - Actions
          • Neutral Building - Remove |cffFFFFDELevel 1 Boss (Dummy) from all marketplaces
          • Neutral Building - Remove |cffFFFFDELevel 1 Boss from all marketplaces
          • For each (Integer A) from 1 to 12, do (Actions)
            • Loop - Actions
              • Player - Make |cffFFFFDELevel 1 Boss Available for training/construction by (Player((Integer A)))
          • Custom script: DestroyTrigger(GetTriggeringTrigger())
        • Else - Actions
          • Hashtable - Save ((Load 0 of 0 from Game_Hashtable.) - 1) as 0 of 0 in Game_Hashtable.
Because you are using lua, gui timers d/c in lua.
 
Top