• 🏆 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!

[Trigger] Stuttering Map Lag Problem

Status
Not open for further replies.
Level 1
Joined
Sep 23, 2018
Messages
2
Hello there. In map project I am participating, Crusade Over Europe, has stuttering map lag which seems to happen after game has run for around 45 minutes or more. The lag seems to get worse the more there are units and the lag ususally causes players to time out or even crash eventually. There is second map project I am creating and it shares same triggers with Crusade Over Europe but the lag could not be replicated there, as the Crusade Over Europe lag even with 8 players while second map Could handle 24 players at ease with maybe 1-2 timeouts after 1-2h of game.

There has been tests with different kind of scenarios like removing all custom models or disabling all triggers. When triggers are remove the lag is gone but the exact leaking trigger could not be found.

Example of triggers of the secondary map project with same triggers as Crusade Over Europe in attached map file.
 

Attachments

  • Conquest - Factions At War 1.13 24P.w3x
    5.3 MB · Views: 63
Level 1
Joined
Sep 23, 2018
Messages
2
But what could be the possible reason that the triggers work in other map but not on another. The Crusade Over Europe also uses lots of attachment based custom models for example peasant archer unit is vanilla citizen model with bow attachments while the Conquest Factions At War uses mostly vanilla models and textures and some downloaded custom models.

Edit. Could it be possible that lots of custom models in map cause game run out of memory sooner than map with no custom models?
 
Level 13
Joined
Jul 15, 2007
Messages
763
Is the "second map project that doesn't lag" the one you attached? You're not really helping yourself if so. Anyway, I browsed over the triggers in the attached file and didn't find anything sinister, in fact from what i could tell all the avoidable leaks were dealt with appropriately.

The fact that you say that map performance degrades after 45 minutes suggests an accumulation of memory leaks, though there are other possible causes.
 
Level 39
Joined
Feb 27, 2007
Messages
5,024
Be aware many GUI actions and functions have implicit leaks due to the local declared local handle (agent?) variable reference counter leak on return bug that has not yet been fixed.
I found that there are also ones that leak local handles/agents that aren't returned. All the multiboard ones do, so if the map has a very active multiboard that could cause problems in the same way. Example:
JASS:
function MultiboardSetItemValueBJ takes multiboard mb, integer col, integer row, string val returns nothing
    local integer curRow = 0
    local integer curCol = 0
    local integer numRows = MultiboardGetRowCount(mb)
    local integer numCols = MultiboardGetColumnCount(mb)
    local multiboarditem mbitem = null  //this motherfucker right here isn't nulled

   // Loop over rows, using 1-based index
    loop
        set curRow = curRow + 1
        exitwhen curRow > numRows

       // Apply setting to the requested row, or all rows (if row is 0)
        if (row == 0 or row == curRow) then
           // Loop over columns, using 1-based index
            set curCol = 0
            loop
                set curCol = curCol + 1
                exitwhen curCol > numCols

               // Apply setting to the requested column, or all columns (if col is 0)
                if (col == 0 or col == curCol) then
                    set mbitem = MultiboardGetItem(mb, curRow - 1, curCol - 1)
                    call MultiboardSetItemValue(mbitem, val)
                    call MultiboardReleaseItem(mbitem)
                endif
            endloop
        endif
    endloop
endfunction
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,202
Also be aware that consume of pickup items are not removed when they are used. Rather the item is killed and left permanently on the map. This applies to items like bundle of lumber, gold coin and all TFT tomes. The solution is a trigger that periodically removes all dead items. These can have a major performance impact unless removed as their model still exists in the game world.
 
Level 11
Joined
Apr 9, 2013
Messages
710
Due to being a new account, Oranki's replies seem to have been delayed to moderator approval and will continue to be until some time, being the Co-Creator of both maps i'll take his place to respond.

As some may be aware I have raised this issue before but with few resolves. After some more time and consideration I have taken to realise that the map can not continue in it's current state as the game simply can not be played with the lag present. Thus I have a keen interest in trying to find a solution to this problem.

Edit: Oranki message seems to have been approved by the Moderators now.
 
Level 39
Joined
Feb 27, 2007
Messages
5,024
I didn't look at any of the disabled triggers, only the ones that were enabled. Here's what I found; a couple of these are definite problems that would build up over time

  • You can remove literally every Do Nothing in your entire map. They... actually do nothing and you don't need to have them. Empty Elses are fine, though empty Ifs are (sometimes?) not.

  • "Removing Empty Players Slots" and "Settle" both leak a unit group each. But they only happen once so it's not big.

  • Anything that uses "Replace X unit with..." has the returned-handle and non-nulled handle leaks Dr Super Good and I mentioned. Two per replace. You can avoid this by manually replacing the unit yourself but that is... well, a lot work for each replace. Chaos and 'passive bear form morph' can be used to transform one unit into another, which may solve your problem here but it's more OE work and Chaos is weird with stat boosts from items. The Berserker Orc upgrade may also be applicable here to automatically morph units in triggers like "Elite Troop Upgrade"

  • Everything but "Trade" in the "Trading Ship" and "Trading horses" categories leaks locations in the minimap pings.

  • "Multiple Units" is... wonky. The 1-6 loop always exits on the first time so why even have it loop in the first place? Furthermore, the exitwhen true happens before the locations are you removed, so every time this trigger runs it leaks 2 locations.

  • "No Riot lvl 5", "No Riot lvl 6", and "No Riot lvl 7 Stabilize" in "Public Order Good Fixed" all leak a group on the Set RiotPoint = ... line, two groups in the Number of units in... lines (in conditions), and the location removal CS needs to be moved to within the Then block where RiotPoint is set. As of now it only removes the last thing RiotPoint was set to, but it can be set as many times as there are units in UnhappinessZ!

  • All the "Riot lvl 1-6" triggers in "Public Order Bad Fixed" leak a group on the Set RiotPoint = ... line and improperly clean the location leak outside of the Then block just like the ones I listed above.

  • "Settlement CAPTURE1" in "Capturing Town Hall/Capitol FIXED" leaks up to 3 unit groups, one each time the Number of units in... condition is checked in the nested If-blocks. "Settlement Upgrade FIX" has a similar leak of up to 3 groups in its Or block in the main trigger conditions.

  • Every trigger except "Gate Opening" in "Gates Not Working" probably leaks a region. You create one and set either GVertDetectCenter or GHoriDetectCenter = to the new region, but you don't seem to do anything with it or remove the region?

  • In "Disabled units at beginning" in "Elite Unit Upgrade" you don't actually need to create a Player Group (force) of All Players. The All Players group is actually automatically created by the game on map init so it doesn't create a new Player Group every time you use All Players.

  • "Warp Gate Killed" and "Tunnel Killed" in "Warp Gate" needs to have each of their location removal lines moved inside of their respective Loop - Actions so they actually removeall the location leaks instead of just the last one.

  • "Wonder Victory 2", "Wonder Building 24p", and "Wonder Ping 24p" all leak locations in their Cinematic - Ping... actions.

  • "Wonder Victory 3" leaks a unit group and probably a lot of player groups, but it doesn't really matter because that trigger ends the game.

  • "Wonder Cancelled 24p" leaks a unit group in its condition.

  • "Player Leaves 24p" leaks a Player Group.

  • In "Player Defeated" you do Set PlayerGold[(Player number of (Owner of (Dying unit)))] = 1000 but you don't actually set the player's gold to 1000...?

  • "Fortress Range Check Human" and "Fortress Range Check Dwarf" in "Building System" have an exploit where players can gain infinite resources (and for dwarves infinite workers) because the group loop doesn't stop after one matching Fortress unit is found. If multiple are found within range of the Fortress that's being built, the player will be refunded the gold and lumber for each one. Imagine a player that builds 2 Fortresses that are 2001.00 units apart (allowed by this trigger), then attempts to build a third one that's between those 2. It will find both existing Fortresses, try to kill the one being built twice, and refunds the gold/lumber twice. To fix this is a little complicated because you can't use an exitwhen true because group loops aren't actually loops, they're ForForceBJ()s. Instead I would do things this way:
    • Set BuildingRangeCheck = (Units within 2000.00 of BuildingRangeCheckPoint matching ((Unit-type of (Matching Unit) equal to Fortress (Europe)) and (Owner of (Matching Unit) equal to (Owner of (Triggering Unit))))
    • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
      • If - Conditions
        • (Number of units in BuildingRangeCheck) greater than 1
      • Then - Actions
        • Unit - Kill (Triggering unit)
        • Game - Display to (Player group((Owner of (Triggering unit)))) the text: Your Fortress is to...
        • Player - Add 5000 to (Owner of (Triggering unit)) Current gold
        • Player - Add 3500 to (Owner of (Triggering unit)) Current lumber
      • Else - Actions
  • This same exploit is also present in every trigger in the "Churches" and "Settlement Foundation" categories. The same fix can be applied to them but you may have to use slightly different logic for the unit group setting line since those triggers seem not to care which player owns the nearby units that invalidate building churches/settlements.
 
Level 11
Joined
Apr 9, 2013
Messages
710
Well, first of all thank you for putting time in to dissecting each trigger in part.

Now I must tell you that the map attached isn't the one that stutters, it actually runs quite well considering now how many leaky trigger it has.

While reluctant, I do realise now that I'll have to send you the troublesome map if I want to fix this issue, so if you would be so kind to have a look at it if you are still willing. I'll send it to you in a PM, currently however I am not at home so i'll send it when I'm able if you please.
The map does posses the same triggers as the attached one. It is apparent though that something must be different in order to have such climactic stutters and lag.

I thank all of those that replied and helped once more and I only hope that we'll be able to find a reasonable solution for this troublesome problem.
 
Level 39
Joined
Feb 27, 2007
Messages
5,024
If the maps share many triggers as Oranki implies, then the things I found are probably still relevant to the CoE map. How about you look for analogous triggers in CoE and clean up the analogous leaks, then test the map again to see if it still becomes laggy and drops players. If it does, send me the map. I would rather not go through the triggers and find lots of the same things I already suggested you should fix.

CoE could be more prone to being affected by the same leaks just because of the number of players. More players means more units moving, being built, being upgraded, being killed; it means more pings and distance checks; it means more code executed in Player Group loops.
 
Level 11
Joined
Apr 9, 2013
Messages
710
CoE could be more prone to being affected by the same leaks just because of the number of players. More players means more units moving, being built, being upgraded, being killed; it means more pings and distance checks; it means more code executed in Player Group loops.

Well the thing is that COE has been played with fewer players than Conquest. While Conquest has been consistently played with 20+ players COE has only had 1 game with more than 12 players, most of the time having only 12 slots and even fewer actual players. The map consistently lags even with only 5 players and does so just as bad.
Not to mention that triggers such as Wonder, Warpgate or the barbaric horse attacks for trade have been removed since early times so that's basically no pings to speak of.

So i'm quite baffled on what exactly is causing this massive lag.
 
Level 39
Joined
Feb 27, 2007
Messages
5,024
  • "Set Town Name Trigger 12P Copy" improperly cleans the floating text location; move the removal line inside the loop

  • "Player leaves Correct 12P" improperly destroys the group multiple times; move the removal line outside the loop.

  • Riot level 1-6 could all be compiled into 1 trigger. Also levels 1 and 2 run every 180s but 3-6 run every 120s? You could set the diplomacy group once on map init (since I assume the diplo units are permanent) and then just use it every time you need to loop through them all so you aren't creating and destroying the groups every time one of these triggers runs. Not a leak, just efficiency. Why can't you just use an (All Players) loop instead of the group loop btw? (Later note: actually a lot of your very similar triggers could be combined into 1 for each group of them... but I guess since you've already set it up to use so many different triggers where 1 with creative conditions/variable setting would suffice, there's no reason to change now. The riot triggers are no more egregious than the tax triggers are, they were just the first ones I encountered before I decided to stop commenting on this.)

  • "Church Unrest Reduction 50" and "Church Unrest Destruction 50" both leak a unit group in the Number of... line.

  • The "Capturing Ore XXg" and "Capturing Mine XXg" triggers all have the variable reference 'leak' from replacing units that I mentioned before. It seems like there's no reason to use the replace command here because the unit is replaced with a copy of itself at full health. You could just set its health to full (and if necessary order the attacking unit to stop) and change its owner. At the very least removing the old unit and replacing it with a new unit doesn't leak in the same way.

  • "Income Command 12P" and leaks a player group (force) you created dynamically to display the message.

  • "Trade Iron Horse" and "Trade Iron Ship" both leak 2 locations in their minimap pings. They also leak dynamically created forces for the pings as with my above note.

  • "Castle Tier 1 Built" and "Destroy Castle DUMMY TAX LOW/MED/HIGH", and all triggers in the "Tax City Hall Upgrades" folder leak 1-4 groups in the Number of units... lines. Most of the triggers in these 2 folders have the replace-unit-leak.

  • "UNREST SYSTEM LOW/MED/HIGH TAX Copy" all leak up to 3 unit groups in the Number of units... lines. "Low/Medium/High Tax FIX Copy" all have the replace-unit-leaks.

  • "Stats Command 12P" leaks the same dynamically created player group 6 times.

  • "upkeep" and "upkeep 2" have duplicate conditions checking if the unit isn't a structure. "Trebuchet Dies" seems to have an error where you used "Entering Unit" where you meant "Dying Unit".

  • "Grant Independence", "Take Independence", "CH CAP Low/Med/High Tax", and "TH Cap Low/Med/High Tax" all leak up to 5 unit groups each in their Number of units... lines. Each of these triggers also has one of these group leaks inside the main trigger conditions so you will need to move the condition that checks that number of units in the trigger's actions instead (where you can properly clean the leak). You should be aware of the bj_wantDestroyGroup method for cleaning group leaks as it could simplify things here. Some of these triggers have replace-unit-leaks.

  • Everything in the "Gates" folder has replaced-unit-leaks. As I said in my other post you may or may not be leaking regions here too because I have no idea what you're using the GVert/HoriDetectCenter variables for.

  • Everything in the "Trading horses" and "Trading Ship" folders leaks dynamically created forces for the pings.

  • Everything but the disable-at-beginning triggers in the "European Base Unit Tier Upgrade" and "Byzantine Unit Tier Upgrade" has the replaced-unit-leak.

  • "Settle" leaks a unit group and has the replaced-unit-leak.

  • The Or condition in the If-block in "Settlement Range Check" doesn't actually have the 2 appropriate conditions inside it (they're outside) so it will fail every time.

  • Reread what I said about there being an exploit in this range check for "Settlement Range Check" and "Church Range Check".
 
Level 39
Joined
Feb 27, 2007
Messages
5,024
Changing the replaced-unit-leak stuff may or may not be worth your time. I would ignore those until you fix all the others and then see how bad the map is. For everything else: Things That Leak. Most of these are the same type of leak from the 'number of units in...' and I would suggest fixing this way:

  • If (All conditions are true) then do (Then actions) else do (Else actions)
    • If - Conditions
      • (Number of units in (Units owned by (Triggering Player) of type SOME_TYPE)) greater than 0
    • Then - Actions
      • -------- do things --------
    • Else - Actions
      • -------- do other things --------
  • -------- --------
  • BECOMES THIS
  • -------- --------
    • Custom script: set bj_wantDestroyGroup = true //this automatically destroys the next dynamically created group, aka the one in the conditions
    • If - Conditions
      • (Number of units in (Units owned by (Triggering Player) of type SOME_TYPE)) greater than 0
    • Then - Actions
      • -------- do things --------
    • Else - Actions
      • -------- do other things --------
  • -------- --------
  • OR DO THIS
  • -------- --------
    • Set TempGroup = (Units owned by (Triggering Player) of type SOME_TYPE)
    • Set GroupCount = Number of units in (TempGroup)
    • Custom script: call DestroyGroup(udg_TempGroup)
    • If - Conditions
      • GroupCount greater than 0
    • Then - Actions
      • -------- do things --------
    • Else - Actions
      • -------- do other things --------
 
Status
Not open for further replies.
Top