Lou Montana/Sandbox – User

From Bohemia Interactive Community
Jump to navigation Jump to search
m (WIP)
m (Update w/ links)
 
(133 intermediate revisions by 5 users not shown)
Line 1: Line 1:
{{Informative|This page is an intended replacement for '''[[Code Optimisation]]'''. It is obviously '''a WORK-IN-PROGRESS'''.}}
[[Category: Sandbox]]
[[Category:Sandbox]]
{{Feature|informative|To go on [[Initialisation Order]].}}
<!--
TODO: color code about importance of performance saving? (green / yellow / red)
-->
----
__NOEDITSECTION__
<div style="float: right; margin-left: 1.5em; margin-bottom: 1em; max-width: 25%;">__TOC__</div>
= Code Optimisation =


== Introduction ==
{| class="wikitable sortable align-center align-left-col-1"
This article will try to be a general guide about improving your code '''and''' its performance.
|+ Order of Initialisation (use column sorting for respective machine order)
! rowspan="2" class="unsortable" style="text-align: center" | Task
! rowspan="2" | Exec Environment
! rowspan="1" colspan="5" class="unsortable" | Machine
|-
! Single Player
! Dedicated Server
! Hosted Server
! Multiplayer Client
! [[Multiplayer Scripting#Join In Progress|JIP]] MP Client


== Rules ==
|-
In the domain of development, any rule is a rule of thumb. If a rule states for example that it is ''better'' that a line of code doesn't go over 80 characters, it doesn't mean that any line '''''must not''''' go over 80 characters; sometimes, the situation needs it. If you have a good structure, '''do not''' change your code to enforce a single arbitrary rule. If you break many of them, you may have to change something. Again, this is according to your judgement.<br />
| [[Arma 3: Functions Library|Functions]] with <syntaxhighlight lang="cpp" inline>recompile</syntaxhighlight> {{Link|Arma 3: Functions Library#Attributes 3|attribute}} are recompiled
<br />
| {{n/a}}
With that being said, here are the three basic rules to get yourself in the clear:
| 1 <!-- Single Player -->
| 1 <!-- Dedicated Server -->
| 1 <!-- Hosted Server -->
| 1 <!-- Multiplayer Client -->
| 1 <!-- JIP MP Client -->


# [[#Make it work|Make it work]]
|-
# [[#Make it readable|Make it readable]]
| [[Arma 3: Functions Library|Functions]] with <syntaxhighlight lang="cpp" inline>preInit</syntaxhighlight> {{Link|Arma 3: Functions Library#Attributes 3|attribute}} are called
# [[#Optimise then|Optimise then]]
| [[Scheduler#Unscheduled Environment|Unscheduled]]
| 2 <!-- Single Player -->
| 2 <!-- Dedicated Server -->
| 2 <!-- Hosted Server -->
| 2 <!-- Multiplayer Client -->
| 2 <!-- JIP MP Client -->


=== Make it work ===
|-
Your first goal when coding is to make your code do what you want it does. A good way to reach this objective is to read and getting inspired by other people's code. If you understand it by reading it once, it is probably a good source of inspiration.<br />
| Object Init Event Handlers are called
When starting from scratch if you know what you want but miss the specific steps to get to your point, it is a good practice to write down in your native language what you want to do. E.g ''Get all the units near the city, and [[forEach|for each]] west soldier in them, add damage''.
| [[Scheduler#Unscheduled Environment|Unscheduled]]
| 3 <!-- Single Player -->
| 3 <!-- Dedicated Server -->
| 3 <!-- Hosted Server -->
| 3 <!-- Multiplayer Client -->
| {{Icon|unchecked}} <!-- JIP MP Client -->


=== Make it readable ===
|-
Whether you are cleaning your code or a different person's, you must understand the code without twisting your brain:
| Expressions of [[Eden Editor: Configuring Attributes|Eden Editor entity attributes]] are called<ref name="isPlayer"><sqf inline>isPlayer _entity</sqf> does not return [[true]] immediately. Once the entity has become a [[player]], it is transferred to the client.</ref>
| [[Scheduler#Unscheduled Environment|Unscheduled]]
| 4 <!-- Single Player -->
| 4 <!-- Dedicated Server -->
| 4 <!-- Hosted Server -->
| {{Icon|unchecked}} <!-- Multiplayer Client -->
| {{Icon|unchecked}} <!-- JIP MP Client -->


* While [[SQF syntax|SQF]] ''is'' impacted by variable name length, this should not take precedence on the fact that code must be readable by a human being. Variables like '''_u''' instead of '''_uniform''' should not be present.
|-
* ''One-lining'' (putting everything in one statement) memory improvement is not worth the headache it gives when trying to read it. Don't overuse it.
| Object initialisation fields are called
* Indentation is important for the human mind, and space is too. Space is free, use it.
| [[Scheduler#Unscheduled Environment|Unscheduled]]
* Finally, camel-casing (namingLikeThis) your variables and commands will naturally make the code more readable, especially for long names.
| 5 <!-- Single Player -->
| 5 <!-- Dedicated Server -->
| 5 <!-- Hosted Server -->
| 4 <!-- Multiplayer Client -->
| 3 <!-- JIP MP Client -->


{{Informative| '''_i''' is an accepted variable standard for a [[for]]..[[do]] iteration}}
|- style="background-color: #95F0AD"
| [[Event Scripts#init.sqs|init.sqs]] is executed
|
| 6 <!-- Single Player -->
| <!-- Dedicated Server -->
| <!-- Hosted Server -->
| <!-- Multiplayer Client -->
| <!-- JIP MP Client -->


See the following code:
|- style="background-color: #95F0AD"
<code style="clear: right;">_w=[];{_w pushbackunique primaryweapon _x}foreach((allunits+alldeadmen)select{_x call bis_fnc_objectside==east});</code>
| [[Event Scripts#init.sqf|init.sqf]] is executed
| [[Scheduler#Scheduled Environment|Scheduled]]<ref name="enginewaits">Note '''in single player''' that while the environment is [[Scheduler#Scheduled Environment|Scheduled]] ([[canSuspend]] returns true), the engine seems to wait until the script is done executing, essentially behaving similarly to an [[Scheduler#Unscheduled Environment|Unscheduled environment]] - infinite loops will freeze the game, [[uiSleep]] may pause the game for up to ~20s (postInit), [[waitUntil]] can cause catastrophic issues, etc.</ref>
| <!-- Single Player -->
| <!-- Dedicated Server -->
| <!-- Hosted Server -->
| <!-- Multiplayer Client -->
| <!-- JIP MP Client -->


The same example is far more readable with proper spacing, good variable names and intermediate results:
|-
<code>_weaponNames = [];
| Expressions of [[Eden Editor: Configuring Attributes|Eden Editor scenario attributes]] are called<ref name="playerCommandNotAvailable">[[player]] is not available immediately.</ref>
_allUnitsAliveAndDead = allUnits + allDeadMen;
| [[Scheduler#Unscheduled Environment|Unscheduled]]
_allEastAliveAndDead = _allUnitsAliveAndDead select { _x call BIS_fnc_objectSide == east };
| <!-- Single Player -->
{ _weaponNames pushBackUnique primaryWeapon _x } forEach _allEastAliveAndDead;</code>
| <!-- Dedicated Server -->
| <!-- Hosted Server -->
| <!-- Multiplayer Client -->
| <!-- JIP MP Client -->


* if you have a lot of [[if]]..[[else]], you may want to look at a [[switch]] condition, or again break your code in smaller functions.
|- style="background-color: #95DEF0"
| Persistent functions are called
|
| <!-- Single Player -->
| <!-- Dedicated Server -->
| <!-- Hosted Server -->
| <!-- Multiplayer Client -->
| <!-- JIP MP Client -->


=== Optimise then ===
|-
Once you know what is what, you can understand your code better.
| [[Modules]] are initialised
* You were iterating multiple times on the same array?
|
** You should be able to spot your issue now.
| <!-- Single Player -->
* Do you see the same code, but with different parameters?
| <!-- Dedicated Server -->
** Time to write a function.
| <!-- Hosted Server -->
* Are you using [[execVM]] on the same file, many times?
| <!-- Multiplayer Client -->
** Store your function in memory to avoid file reading every call with <code>_myFunction = [[compile]] [[preprocessFileLineNumbers]] "myFile.sqf";</code>
| {{Icon|unchecked}} <!-- JIP MP Client -->
* Is your function code far too long?
** Split it in understandable-sized bites, for your own sanity.
* Is your variable name far too long?
** Find a smaller name, according to the variable scope:
e.g
{ _opforUnitUniform {{=}} uniform _x; systemChat _opforUnitUniform; } forEach _allOpforUnits
becomes
{ _uniform {{=}} uniform _x; systemChat _uniform; } forEach _allopforUnits


|- style="background-color: #DEF0AD"
| [[Event Scripts#initServer.sqf|initServer.sqf]] is executed
| [[Scheduler#Scheduled Environment|Scheduled]]
| <!-- Single Player -->
| <!-- Dedicated Server -->
| <!-- Hosted Server -->
| {{n/a}} <!-- Multiplayer Client -->
| {{n/a}} <!-- JIP MP Client -->


== Code optimisation ==
|- style="background-color: #DEF0AD"
| [[Event Scripts#initPlayerLocal.sqf|initPlayerLocal.sqf]] is executed
| [[Scheduler#Scheduled Environment|Scheduled]]
| <!-- Single Player -->
| {{n/a}} <!-- Dedicated Server -->
| <!-- Hosted Server -->
| <!-- Multiplayer Client -->
| <!-- JIP MP Client -->


=== Successive condition check ===
|- style="background-color: #DEF0AD"
In [[SQF syntax|SQF]] the following code will check '''all and every''' condition, even if one fail:
| [[Event Scripts#initPlayerServer.sqf|initPlayerServer.sqf]] is executed on the server
[[if]] (condition1 && condition2 && condition3) [[then]] { {{codecomment|/* thenCode */}} };
| [[Scheduler#Scheduled Environment|Scheduled]]
e.g:
| <!-- Single Player -->
[[if]] ([[alive]] unit1 && [[not]] [[alive]] aPvehicle && [[daytime]] > 5) [[then]] { {{codecomment|/* thenCode */}} };
| {{n/a}} <!-- Dedicated Server -->
This code will check if {{Inline code|unit1}} is alive, and if it is not {{Inline code|[[not]] [[alive]] aPvehicle && [[daytime]] > 5}} will execute anyway.
| ?? <!-- Hosted Server -->
To avoid this behaviour, you can either imbricate [[if|ifs]] or use '''lazy evaluation''' such as the following:
| <!-- Multiplayer Client -->
[[if]] ([[alive]] unit1 && { [[not]] [[alive]] aPvehicle } && {{ [[daytime]] > 5}}) [[then]] { {{codecomment|/* thenCode */}} };
| <!-- JIP MP Client -->
This lazy evaluation will stop code execution on the first [[false]] statement.


Using lazy evaluation is not always the best way as it could speed up the code as well as slow it down, depending on the current condition being evaluated:
|-
| [[Arma 3: Functions Library|Functions]] with <syntaxhighlight lang="cpp" inline>postInit</syntaxhighlight> {{Link|Arma 3: Functions Library#Attributes 3|attribute}} are called
| [[Scheduler#Scheduled Environment|Scheduled]]<ref name="enginewaits"/>
| <!-- Single Player -->
| <!-- Dedicated Server -->
| <!-- Hosted Server -->
| <!-- Multiplayer Client -->
| <!-- JIP MP Client -->


[true &nbsp;|| {false} || {false}] call BIS_fnc_codePerformance; {{codecomment|// fastest}}
|- style="background-color: #95DEF0"
[true &nbsp;|| &nbsp;false &nbsp;|| &nbsp;false&nbsp;] call BIS_fnc_codePerformance; {{codecomment|// normal}}
| [[Event Scripts#init.sqs|init.sqs]] is executed
[false || &nbsp;false &nbsp;|| &nbsp;false&nbsp;] call BIS_fnc_codePerformance; {{codecomment|// normal}}
| [[Scheduler#Scheduled Environment|Scheduled]]
[false || {false} || {false}] call BIS_fnc_codePerformance; {{codecomment|// slowest}}
| <!-- Single Player -->
| <!-- Dedicated Server -->
| <!-- Hosted Server -->
| <!-- Multiplayer Client -->
| <!-- JIP MP Client -->


==== Adding large strings together ====
|- style="background-color: #95DEF0"
{{Inline code|a = a + b}} works fine for small strings, however the bigger the string gets the slower this becomes:
| [[Event Scripts#init.sqf|init.sqf]] is executed
s = ""; [[for]] "_i" [[from]] 1 [[to]] 10000 [[do]] {s = s + "123"}; {{codecomment|// 30000 chars @ 290ms}}
| [[Scheduler#Scheduled Environment|Scheduled]]
| <!-- Single Player -->
| <!-- Dedicated Server -->
| <!-- Hosted Server -->
| <!-- Multiplayer Client -->
| <!-- JIP MP Client -->


The solution is to use a string array that you will concatenate later:
|-
strings = []; [[for]] "_i" [[from]] 1 [[to]] 10000 [[do]] {strings [[pushBack]] "123"}; strings = strings [[joinString]] ""; {{codecomment|// 30000 chars @ 30ms}}
| [[remoteExec]]'s [[Multiplayer Scripting#Join In Progress|JIP]] queue
| {{n/a}}
| {{n/a}} <!-- Single Player -->
| {{n/a}} <!-- Dedicated Server -->
| {{n/a}} <!-- Hosted Server -->
| {{n/a}} <!-- Multiplayer Client -->
| 42 <!-- JIP MP Client -->


|- style="background-color: #EEE"
| ''Scenario going''
| {{n/a}}
| <!-- Single Player -->
| <!-- Dedicated Server -->
| <!-- Hosted Server -->
| <!-- Multiplayer Client -->
| <!-- JIP MP Client -->


=== Multiplayer recommendations ===
|-
''TODO''
| [[Event Scripts#exit.sqf|exit.sqf]]
* no [[publicVariable]] or public [[setVariable]] at high frequency
|
* the server is supposed to have a lot of memory, use it
| <!-- Single Player -->
| <!-- Dedicated Server -->
| <!-- Hosted Server -->
| <!-- Multiplayer Client -->
| <!-- JIP MP Client -->


=== createSimpleObject vs createVehicle ===
|-
''TODO''
| [[Event Scripts#exit.sqs|exit.sqs]]
create at [0,0,0]
|
| <!-- Single Player -->
| <!-- Dedicated Server -->
| <!-- Hosted Server -->
| <!-- Multiplayer Client -->
| <!-- JIP MP Client -->


|-
| {{Link|Arma 3: Mission Event Handlers#Ended|"Ended" Mission Event Handler}}
|
| <!-- Single Player -->
| <!-- Dedicated Server -->
| <!-- Hosted Server -->
| <!-- Multiplayer Client -->
| <!-- JIP MP Client -->


== Equivalent commands performance ==
|-
|  {{Link|Arma 3: Mission Event Handlers#MPEnded|"MPEnded" Mission Event Handler}}
|
| <!-- Single Player -->
| <!-- Dedicated Server -->
| <!-- Hosted Server -->
| <!-- Multiplayer Client -->
| <!-- JIP MP Client -->


=== if ===
|}
Use:
[[if]] (condition1) [[then]] { {{codecomment|/* thenCode */}} } else { {{codecomment|/* elseCode */}} }; {{codecomment|// faster}}
Instead of:
[[if]] (condition1) [[then]] [{ {{codecomment|/* thenCode */}} }, { {{codecomment|/* elseCode */}} }]; {{codecomment|// slower}}


=== for ===
Use:
[[for]] "_i" [[from]] 0 [[to]] 100 [[do]] { {{codecomment|/* forCode */}} }; {{codecomment|// faster}}
Instead of:
[[for]] [{_i = 0}, {_i < 100}, {_i = _i + 1}] [[do]] { {{codecomment|/* forCode */}} }; {{codecomment|// slower}}


=== forEach vs count ===
== See Also ==
Both commands will step through supplied array of elements one by one and both commands will contain reference to current element in the [[_x]] variable.
However, [[count]] loop is a little faster than [[forEach]] loop, but it does not have [[_forEachIndex]] variable.<br />
Also, there is a limitation as the code inside [[count]] expects [[Boolean]] or [[Nothing]] while itself returns [[Number]].


<code>{[[diag_log]] _x} [[count]] [1,2,3,4,5,6,7,8,9]; {{codecomment|// faster}}</code>
* [[Arma 3: Functions Library]]<!--
<code>{[[diag_log]] _x} [[forEach]] [1,2,3,4,5,6,7,8,9]; {{codecomment|// slower}}</code>
* [[Arma 2: Functions Library]] -->
 
* [[Arma 3: Remote Execution]], [[BIS_fnc_MP]] <!-- keep? -->
Usage:
* [[Eden Editor: Configuring Attributes|Eden Editor: Configuring Attributes]]
<code>_someoneIsNear = {_x [[distance]] [0,0,0] < 1000} [[count]] [[allUnits]] > 0;</code>
* [[Event Scripts]]
_someoneIsNear = {
* [[Scheduler]]
[[if]] ([[_x]] [[distance]] [0,0,0] < 1000) [[exitWith]] { [[true]] };
[[false]]
} [[forEach]] [[allUnits]];
''TODO: findIf?''
 
 
 
=== + and format ===
When concatenating more than two strings, [[format]] is faster than [[valuea_plus_valueb|+]]. Use:
[[format]] ["%1%2%3%4%5", "string1", "string2", "string3", "string4", "string5"]; {{codecomment|// faster}}
Instead of:
"string1" + "string2" + "string3" + "string4" + "string5"; {{codecomment|// slower}}
 
=== select and if ===
Use:
result = ["false result", "true result"] [[select]] _condition; {{codecomment|// faster}}
Instead of the lazy-evaluated [[if]]:
result = [[if]] (_condition) [[then]] { "true result"; } [[else]] { "false result"; }; {{codecomment|// slower}}
 
=== private ===
Use:
private _a = 1;
private _b = 2;
private _c = 3;
private _d = 4;
{{codecomment|// 0.0023 ms}}
 
Instead of:
private ["_a", "_b", "_c", "_d"];
_a = 1;
_b = 2;
_c = 3;
_d = 4;
{{codecomment|// 0.0040 ms}}
 
However:
[[for]] "_i" from 1 to 100 do
{
private _a = 1; private _b = 2; private _c = 3; private _d = 4;
};
{{codecomment|// 0.186776 ms}}
 
is slower than:
private ["_a", "_b", "_c", "_d"];
for "_i" from 1 to 100 do
{
_a = 1;
_b = 2;
_c = 3;
_d = 4;
};
{{codecomment|// 0.146327 ms}}
The reason behind this is that in the first example variables are created, assigned and deleted in each loop.<br />
In the second example the variables are only created/deleted once and changed often.
 
=== objectParent and vehicle ===
<code>isNull objectParent player {{codecomment|// 0.0013 ms, slightly faster}}</code>
<code>vehicle player == player {{codecomment|// 0.0022 ms}}</code>
 
=== nearEntities and nearestObjects ===
* [[nearEntities]] is much faster than [[nearestObjects]] given on range and amount of object(s) which are within the given range.
If a range was set to more thean 100 meters it is highly recommend to use [[nearEntities]] instead of [[nearestObjects]].
 
Note: [[nearEntities]] only searches for objects which are alive.
Killed units, destroyed vehicles, static objects and buildings will be ignored by the [[nearEntities]] command.
 
=== Config path delimiter ===
{{Inline code|>>}} is slightly faster than {{Inline code|/}} when used in config path with [[configFile]] or [[missionConfigFile]]:
[[configFile]] >> "CfgVehicles" {{codecomment|// 0.0019 ms}}
is (very slighlty) faster than
[[configFile]] / "CfgVehicles" {{codecomment|// 0.0023 ms}}
 
=== getPos* and setPos* ===
 
 
[[getPosWorld]] {{codecomment|// 0.0015 ms}}
[[getPosASL]] {{codecomment|// 0.0016 ms}}
[[getPosATL]] {{codecomment|// 0.0016 ms}}
[[getPos]] {{codecomment|// 0.0020 ms}}
[[position]] {{codecomment|// 0.0020 ms}}
[[getPosVisual]] {{codecomment|// 0.0021 ms}}
[[visiblePosition]] {{codecomment|// 0.0021 ms}}
[[getPosASLW]] {{codecomment|// 0.0023 ms}}
 
[[setPosWorld]] {{codecomment|// 0.0060 ms}}
[[setPosASL]] {{codecomment|// 0.0060 ms}}
[[setPosATL]] {{codecomment|// 0.0060 ms}}
[[setPos]] {{codecomment|// 0.0063 ms}}
[[setPosASLW]] {{codecomment|// 0.0068 ms}}
[[setVehiclePosition]] {{codecomment|// 0.0077 ms "CAN_COLLIDE"}}
{{codecomment|// 0.0390 ms "NONE"}}
 
 
== Conversion from earlier versions ==
Each iteration of Bohemia games ({{ofp}}, {{arma}}, {{arma2}}, {{tkoh}}, {{arma3}}) brought their own new commands, especially {{arma2}} and {{arma3}}.<br />
For that, if you are converting scripts from older versions of the engine, some aspects should be reviewed:
 
=== Loops ===
* [[forEach]] loops, depending on the situation, can be replaced by:
** [[apply]]
** [[count]]
** [[findIf]]
** [[select]]
 
=== Array operations ===
* '''Adding an item:''' {{Inline code|myArray [[valuea_plus_valueb|+]] [element]}} and {{Inline code|myArray [[set]] <nowiki>[</nowiki>[[count]] myArray, element]}} have been replaced by [[pushBack]]
* '''Selecting a random item:''' [[BIS_fnc_selectRandom]] has been replaced by [[selectRandom]]
* '''Removing items:''' {{Inline code|myArray [[set]] [1, objNull]; myArray [[a_-_b|-]] [objNull]}} has been replaced by [[deleteAt]] and [[deleteRange]]
* '''Concatenating:''' {{Inline code|myArray {{=}} myArray [[valuea_plus_valueb|+]] [element]}} has been ''reinforced'' by [[append]]: if you don't need the original array to be modified, use +
* '''Comparing:''' use [[isEqualTo]] instead of [[BIS_fnc_areEqual]] or a custom comparison function
** {{Inline code|[[count]] myArray {{=}}{{=}} 0}} is pretty fast, but direct comparison with [[isEqualTo]] is still a little faster: {{Inline code|myArray [[isEqualTo]] []}}
* '''Finding common items:''' [[in]] [[forEach]] loop has been replaced by [[arrayIntersect]]
* '''Condition filtering:''' [[forEach]] can be replaced by [[select]] (alternative syntax)
<code>result = [];
{
[[if]] (_x % 2 == 0) [[then]]
{
result [[pushBack]] [[_x]];
};
} [[forEach]] arrayOfNumbers; {{codecomment|// 2.57 ms}}</code>
result = (arrayOfNumbers [[select]] { _x % 2 == 0 }); {{codecomment|// 1.55 ms}}
 
=== String operations ===
[[String]] manipulation has been simplified with the following commands:
* string [[select]] index
* [[toArray]] and [[toString]] have been reinforced with [[splitString]] and [[joinString]]
 
=== Multiplayer ===
* [[BIS_fnc_MP]] has been replaced by [[remoteExec]] and [[remoteExecCall]]

Latest revision as of 00:29, 18 March 2024

Order of Initialisation (use column sorting for respective machine order)
Task Exec Environment Machine
Single Player Dedicated Server Hosted Server Multiplayer Client JIP MP Client
Functions with recompile attribute are recompiled N/A 1 1 1 1 1
Functions with preInit attribute are called Unscheduled 2 2 2 2 2
Object Init Event Handlers are called Unscheduled 3 3 3 3 Unchecked
Expressions of Eden Editor entity attributes are called[1] Unscheduled 4 4 4 Unchecked Unchecked
Object initialisation fields are called Unscheduled 5 5 5 4 3
init.sqs is executed 6
init.sqf is executed Scheduled[2]
Expressions of Eden Editor scenario attributes are called[3] Unscheduled
Persistent functions are called
Modules are initialised Unchecked
initServer.sqf is executed Scheduled N/A N/A
initPlayerLocal.sqf is executed Scheduled N/A
initPlayerServer.sqf is executed on the server Scheduled N/A ??
Functions with postInit attribute are called Scheduled[2]
init.sqs is executed Scheduled
init.sqf is executed Scheduled
remoteExec's JIP queue N/A N/A N/A N/A N/A 42
Scenario going N/A
exit.sqf
exit.sqs
"Ended" Mission Event Handler
"MPEnded" Mission Event Handler


See Also

  1. isPlayer _entity does not return true immediately. Once the entity has become a player, it is transferred to the client.
  2. 2.0 2.1 Note in single player that while the environment is Scheduled (canSuspend returns true), the engine seems to wait until the script is done executing, essentially behaving similarly to an Unscheduled environment - infinite loops will freeze the game, uiSleep may pause the game for up to ~20s (postInit), waitUntil can cause catastrophic issues, etc.
  3. player is not available immediately.