When testing Workshop scripts, the answer to the question “why did my server shut down?” is almost always due to what we call “server load”, which is a measurement we take to determine how much processing power a given game instance is consuming. In order to accommodate a large number of instances, we must shut down individual instances that have started to use too much of the available processing power.
So why does this happen? The short answer is that too many Workshop actions are executing. Sometimes it’s because a high number of actions have occurred over several seconds. Sometimes it’s because an extremely high number of actions happen on a single frame.
Not all actions impact server load the same amount, however. Creating and destroying dummy bots, for example, is very costly, while modifying variables is relatively inexpensive (especially starting in patch 1.45). The values you provide actions will affect server load as well. For example, the ray casting values are very expensive while values such as Event Player are practically free.
So how can server load be reduced? The best way is by adding this rule (also available in the “Server Load” preset) to your Workshop script and testing your mode to see when the numbers go up:
rule("Display server performance characteristics")
{
event
{
Ongoing - Global;
}
actions
{
Create HUD Text(All Players(All Teams), String("{0}: {1}", String("Server Load", Null, Null, Null), String("{0}%", Server Load,
Null, Null), Null), Null, Null, Left, 0, White, White, White, Visible To and String, Default Visibility);
Create HUD Text(All Players(All Teams), String("{0}: {1}", String("Server Load Average", Null, Null, Null), String("{0}%",
Server Load Average, Null, Null), Null), Null, Null, Left, 1, White, White, White, Visible To and String, Default Visibility);
Create HUD Text(All Players(All Teams), String("{0}: {1}", String("Server Load Peak", Null, Null, Null), String("{0}%",
Server Load Peak, Null, Null), Null), Null, Null, Left, 2, White, White, White, Visible To and String, Default Visibility);
}
}
As a general rule of thumb, you want the numbers to stay below 100 as much as possible. The higher the numbers are above 100, the more risk you run of the server load threshold triggering and your instance being shut down. Note that this measurement includes the base cost of running the game as well, so in heavy fire fights, the numbers can get up to 50 or higher even with no Workshop logic executing.
Another way server load can be reduced is by using conditions whenever possible instead of actions. For example, this…
rule("Kill players that leave the circle")
{
event
{
Ongoing - Each Player;
All;
All;
}
conditions
{
Distance Between(Event Player, Global Variable(A)) > 5;
}
actions
{
Kill(Event Player, Null);
}
}
…is much less expensive than this…
rule("Kill players that leave the circle")
{
event
{
Ongoing - Each Player;
All;
All;
}
actions
{
Wait(0.016, Ignore Condition);
Loop If(Compare(Distance Between(Event Player, Global Variable(A)), <=, 5));
Kill(Event Player, Null);
}
}
Another useful trick is to take a loop that causes too much server load on a single frame and spread its work over multiple frames:
rule("Spawn 100 effects in 10 frames")
{
event
{
Ongoing - Each Player;
All;
All;
}
conditions
{
Is Button Held(Event Player, Interact) == True;
}
actions
{
For Global Variable(I, 0, 100, 1);
Play Effect(All Players(All Teams), Good Explosion, White, Vector(Global Variable(I), 0, 0), 1);
If(Compare(Modulo(Global Variable(I), 10), ==, 0));
Wait(0.016, Ignore Condition); // Wait for a frame once every 10th effect
End;
End;
}
}
It can be challenging to realize your vision given a limited performance budget, but there are often ways to optimize your scripts. As you create, it might be worth asking yourself as you go “Wait, can I do this using fewer actions?” and “What happens when 12 players all try to do this at the same time?”
One thing that will absolutely shut down your instance every time is something that’s possible starting in the 1.45 patch: infinite loops.
So what is an infinite loop? Simply put, it’s when Workshop is given an endless series of actions to execute on a single frame. Because it can never finish, it eventually gives up. By that time, it has consumed too much of the available processing power, and the server must be shut down.
What does an infinite loop look like? It can take several forms, but the simplest is:
actions
{
While(True);
End;
}
Because the condition of the While action always passes and causes execution to continue down to the End action, and because the End action simply causes execution to return to the While action above it, Workshop continues to execute these two actions until enough real-world time has passed that the server is shut down.
Below is a more complex example that is only an infinite loop if there are more than 5 elements in the “targets” array. Can you spot the error?
variables
{
global:
0: index
1: targets
}
actions
{
Set Global Variable(index, 0);
While(Compare(Global Variable(index), <, Count Of(Global Variable(targets)))); // Consider each target
If(Compare(Global Variable(index), <, 5));
Heal(Value In Array(Global Variable(targets), Global Variable(index)), Null, 30); // Heal the first 5 targets by 30
Modify Global Variable(index, Add, 1); // Advance to the next target
Else;
Damage(Value In Array(Global Variable(targets), Global Variable(index)), Null, 30); // Damage all other targets by 30
End;
End;
}
Finally, here’s an example that involves subroutines (another new feature in 1.45). It might seem safe enough at first, but it’s an infinite loop because both rules use the same “index” variable:
variables
{
global:
0: index
}
rule("Call the subroutine 100 times")
{
event
{
Ongoing - Global;
}
actions
{
For Global Variable(index, 0, 100, 1);
Call Subroutine(Sub0); // index will always be 10 when the subroutine returns
End;
}
}
rule("The subroutine")
{
event
{
Subroutine;
Sub0;
}
actions
{
For Global Variable(index, 0, 10, 1);
Modify Global Variable(C, Add, 1);
End;
}
}
To summarize, game instances shut down due to server load, and one way to hit the server load limit immediately is with an infinite loop. So keep an eye on those server load values and watch out for logic that loops forever. Good luck!