General - Reducing Lag

Tutorial By Darthfett

Reducing Lag
Do you often have players complaining about a laggy map or have sudden pauses while your map is running?

This tutorial will tell you a bit about the causes of lag, and teach you how to avoid lag.

1. Map Initialization Lag

Map Initialization lag can be anything from long loading times to players lagging just after the map starts.

There are a few causes:

A. Unit & Hero Creation

Every time you create a new unit that the game has not loaded yet, it takes time. Putting these at map initialization will get rid of annoying in-game pauses, but it will increase the time it takes to load the map. If you don't want either of these, you could try making a short cinematic just after the game starts, so the player won't notice a long load time or any sudden pauses.

B. Massive Amounts of Code

If you have a lot of code being run at Map Initialization, it can create a long loading time. Sometimes, spreading this out just after game start, while playing a cinematic can help to make it a little more bearable.

C. Loading the Map

The other players lag may also be because of how large your map is. If your map is Epic, or Large sized, you may want to add a loading time at the beginning of the map, just so players can handle the heavy strain on their computer.

2. Game Lag

As the game progresses, does lag seem to get worse and worse? It could be from any of the following causes:

A. Memory Leaks

a. Removing GUI Leaks

Did you know that every time you use a "Pick every Unit in..." or a "Create Units at Location" that you create a memory leak? These are the biggest cause for in-game lag. There are many variables that leak:
Points, Unit Groups, Player Groups, Special Effects, Lightning Effects, Floating Text, Countdown Timers, and a number of others.

To remove these memory leaks, every time that you use any of these variable types in a line of GUI, you must set a variable to it, and then use custom script/JASS to remove them.

EX:
Trigger:
  • Untitled Trigger 001
    • Events
      • Unit - A unit enters Region 0001 <gen>
    • Conditions
    • Actions
      • Unit - Create 1 Footman for Player 1 (Red) at (Position of (Entering unit)) facing Default building facing degrees


Your first step is to identify all the values that you can change, and their variable type.

In this trigger I used 1 (An Integer), Footman (A Unit-Type), Player 1 (Red) (A player), Position of (Entering unit) (A Point), Entering unit (A Unit), and Default building facing degrees (A Real).

Your next step is to check to see which of these variables leaks.
1 (Integer) = Integers do not leak.
Footman (Unit-Type) = Unit-Types do not leak.
Player 1 (Red) (Player) = Players do not leak.
Position of (Entering unit) (Point)= Points do leak.
Entering unit (Unit)= Units do not leak.
Default buliding facing degrees (Real) = Reals do not leak

As you can see, I have found that one of my variables leaks; my point variable, Position of (Entering unit).

So what you must do, is you set a temporary variable to that point, BEFORE the line of text that has the memory leak, and use the variable as the point in the leaking line:

EX:
Trigger:
  • Untitled Trigger 001
    • Events
      • Unit - A unit enters Region 0001 <gen>
    • Conditions
    • Actions
      • Set TEMP_Point = (Position of (Entering unit))
      • Unit - Create 1 Footman for Player 1 (Red) at TEMP_Point facing Default building facing degrees


Now you have the leak stored in a variable, so you can remove it. Each leaking variable has its own custom script to destroy it however, so you must memorize these lines:

Points, Unit Groups, Player Groups, Special Effects, Lightning Effects, Floating Text, and Countdown Timers::
Custom script:    call RemoveLocation (udg_*Temp variable name here*)
Custom script:    call DestroyGroup (udg_*Temp variable name here*)
Custom script:    call DestroyForce (udg_*Temp variable name here*)
Custom script:    call DestroyEffect (udg_*Temp variable name here*)
Custom script:    call DestroyLightning (udg_*Temp variable name here*)
Custom script:    call DestroyTextTag (udg_*Temp variable name here*)
Custom script:    call DestroyTimer (udg_*Temp variable name here*)


For the Special Effect, Blizzard already created a Line that destroys it:

Special Effect - Destroy (Last created special effect)

You may want to watch out for the tricky leaks, such as this double point location leak:
Trigger:
  • Untitled Trigger 001
    • Events
      • Unit - A unit enters Region 0001 <gen>
    • Conditions
    • Actions
      • Unit - Create 1 Footman for Player 1 (Red) at ((Position of (Entering Unit)) offset by 50 towards 90 degrees) facing Default building facing degrees


It looks like one leak, right? But there are actually 2 leaks here; Position of (Entering unit) leaks, but so does the point created by offsetting it. You would take care of this kind of a leak like this:

Trigger:
  • Untitled Trigger 001
    • Events
      • Unit - A unit enters Region 0001 <gen>
    • Conditions
    • Actions
      • Set TEMP_Point = Position of (Entering unit)
      • Set TEMP_Point2 = (TEMP_Point) offset by 50 towards 90 degrees
      • Unit - Create 1 Footman for Player 1 (Red) at TEMP_Point2 facing Default building facing degrees
      • Custom Script: call RemoveLocation(udg_TEMP_Point)
      • Custom Script: call RemoveLocation(udg_TEMP_Point2)


Another type of variable that leaks is a Region, but unless the region is not a default region (Playable Map Area), or a Preplaced region (Region XXXX <gen>), it will not leak. However the custom script to destroy a region is:

Custom script:   call RemoveRect(udg_*Temp variable name here*)


This is what a region leak looks like:

Trigger:
  • Untitled Trigger 001
    • Events
      • Unit - A unit enters Region 0001 <gen>
    • Conditions
    • Actions
      • Unit - Create 1 Footman for Player 1 (Red) at (Center of (Region (100.00 200.00 300.00 400.00)))


As you can see this creates a region (Anything you have not already preplaced, leaks), AND a point. You would do the same thing as with the double point leak: set the variables to the leaks, use the variables instead of the leaks, and then remove the variables.

You may also be creating leaks if you are using local handle variables in Jass.

b. Nulling Local Handle Variables

Just like not removing a location can leak, leaving any local handle variable with a value will cause a leak. To fix this, you have to null it.

A handle variable includes every single type of variables, except boolean, integer, real, and string.

So, basically if you have any functions that have a part in them that starts out like this:

local VARIABLETYPE NAME , it is creating a local, and if it's not an integer, real, string, or boolean, it needs to be nulled like this:

set NAME = null


There are a few exceptions, however.

If you are not going to ever remove the value of the local handle variable, then there is no point in nulling it.

For example, triggers are almost never destroyed. If you create a trigger like this:

function InitTrig_Untitled_Trigger_001 takes nothing returns nothing
    local trigger t = CreateTrigger()
endfunction


and never destroy the trigger, then it does not need to be nulled.

Players are another great example, as they do not leak, and cannot be destroyed, so there is no need to ever null a player.

B. Trigger Enhanced Spells

This is usually a last resort, as most lag can be solved from simply preloading and removing memory leaks. However, there are exceptions.

Even if all of your leaks are taken care of, there still may be lag from triggered spells.

If it uses any Periodic events with a very small duration (such as 0.05 seconds or lower), it can get laggy.

Here are some tips to keep your map as lag-less as possible


• Avoid periodic events as often as you can. If it doesn't have to use it, then don't use it.
• Keep spells that use periodic events limited to units that are few in number, such as heroes.
• Try optimizing the spell in JASS/vJASS, as it is superior to GUI in terms of speed.
• Avoid large amounts of special effects, and try to use dummy units as replacements.
• Try to use slighly larger time intervals for periodic events, such as 0.03 - 0.05 instead of 0.02 or 0.01 the human eye can't see the different much of the time anyways.


D. Massive Amounts of Objects

Try to reduce the amount of objects you have on your map at one time, such as units, destructables, doodads, and special effects.

Instead of having hundreds of units, have only a few hundred, and use stronger units instead. You can also save on graphical power by using dummy units instead of special effects, and removing clumps of doodads (such as excessive wheat).

3. PreLoading

A. Units

Again, everytime a unit or hero is created, the game has to load the unit. You can reduce the in-game lag by preplacing all the units, and as soon as the game starts, remove the units. Even dummy casters should be preloaded, otherwise when the first time an ability is cast, it will lag.

B. Abilities

If you add any abilities to units, then you should also create a special unit, that already has the abilities, just for preloading. Just preplace this unit, and remove it as soon as the map initializes. Make sure to preload spellbooks as well.

4. Optimization Tools

A. Loading Times

All this preloading, and having everything already on the map will cause the map to take longer to load. This can be solved by using an optimization tool, such as the Widgetizer.

The Widgetizer is a tool specially designed to reduce loading time and make your map run a bit faster. All you must do is download the tool, open your map with it, and save it as the filename you will be using to host it, under the downloaded maps area. (Make sure your original map isn't under there, so you don't get confused when hosting it, as the maps will still have the same name). Your map may get bigger, but it will load faster.

What the widgetizer actually does (Thanks to SFilip for this section):

All Warcraft's original object data (such as a footman, knight etc.) is actually stored inside SLK files in the MPQ. When you create something custom you will add it to a special file inside your map which needs to be converted and initialized while the map is loading along with all the SLK files even if you don't use one of the objects. The widgetizer simply preconverts all the custom data intro SLK files thus removing any units/abilities/whatever you haven't used, which can greatly reduce your map's loading time. Yet the problem is that SLK files are larger and they need to contain units even if you haven't changed them at all, so this might increase your map's size.

B. Script Optimization

Thankfully, Vexorian created the Map Optimizer, which will automatically go through your script and clean it up for you. (By removing useless BJ functions, shortening, and optimizing scripts to improve performance)

This really helps increase the speed of your map, and it can also remove some of your memory leaks for you (Just don't rely on it to do everything for you).

Another useful tool to help identify and clean leaks is Im_On_56k's Leak Check tool. It is a tool that will help show you where some of your leaks are in your GUI script.

================

Thanks for reading, and good luck in your maps!

Are you still confused about memory leaks? Try a more in-depth tutorial:

http://www.thehelper.net/forums/showthread.php?t=27219

Click here to comment on this tutorial.
 
 
Blizzard Entertainment, Inc.
Silkroad Online Forums
Team Griffonrawl Trains Muay Thai and MMA fighters in Ohio.
Apex Steel Pipe - Buys and sells Steel Pipe.