Lou Montana/Sandbox – User

From Bohemia Interactive Community
Jump to navigation Jump to search
m (Some page presentation / copy-paste introduction from Code Optimisation)
m (Update w/ links)
 
(53 intermediate revisions by the same user not shown)
Line 1: Line 1:
__NOTOC__
[[Category: Sandbox]]
[[Category: Sandbox]]
{{Informative | Future [[Code Best Practices]] page}}
{{Feature|informative|To go on [[Initialisation Order]].}}
{{Important | This is at the moment '''only a list of topics''' and absolutely not the final render. Many single entries will result in a chapter.}}


{| class="wikitable sortable align-center align-left-col-1"
|+ 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


----
|-
| [[Arma 3: Functions Library|Functions]] with <syntaxhighlight lang="cpp" inline>recompile</syntaxhighlight> {{Link|Arma 3: Functions Library#Attributes 3|attribute}} are recompiled
| {{n/a}}
| 1 <!-- Single Player -->
| 1 <!-- Dedicated Server -->
| 1 <!-- Hosted Server -->
| 1 <!-- Multiplayer Client -->
| 1 <!-- JIP MP Client -->


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


[[:Category: Scripting Topics]]
|-
| Object Init Event Handlers are called
| [[Scheduler#Unscheduled Environment|Unscheduled]]
| 3 <!-- Single Player -->
| 3 <!-- Dedicated Server -->
| 3 <!-- Hosted Server -->
| 3 <!-- Multiplayer Client -->
| {{Icon|unchecked}} <!-- JIP MP Client -->


== Getting started ==
|-
| 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 -->


=== Prerequisites ===
|-
| Object initialisation fields are called
| [[Scheduler#Unscheduled Environment|Unscheduled]]
| 5 <!-- Single Player -->
| 5 <!-- Dedicated Server -->
| 5 <!-- Hosted Server -->
| 4 <!-- Multiplayer Client -->
| 3 <!-- JIP MP Client -->


* An Arma game
|- style="background-color: #95F0AD"
* Passion
| [[Event Scripts#init.sqs|init.sqs]] is executed
* Some English knowledge, helped if needed by a translator tool like [https://www.google.com/translate Google Translate] or [https://www.deepl.com/ DeepL]
|
* Access to this wiki is a plus (the Swiss flag is, too)
| 6 <!-- Single Player -->
* Tutorial/example article (YouTube tutorials, )
| <!-- Dedicated Server -->
* Google-Fu (a.k.a search engine skills)
| <!-- Hosted Server -->
* Advanced text editor (Such as Notepad++ or Visual Studio Code)
| <!-- Multiplayer Client -->
| <!-- JIP MP Client -->


|- style="background-color: #95F0AD"
| [[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 -->


== Rules ==
|-
| Expressions of [[Eden Editor: Configuring Attributes|Eden Editor scenario attributes]] are called<ref name="playerCommandNotAvailable">[[player]] is not available immediately.</ref>
| [[Scheduler#Unscheduled Environment|Unscheduled]]
| <!-- Single Player -->
| <!-- Dedicated Server -->
| <!-- Hosted Server -->
| <!-- Multiplayer Client -->
| <!-- JIP MP Client -->


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.
|- style="background-color: #95DEF0"
| Persistent functions are called
|
| <!-- Single Player -->
| <!-- Dedicated Server -->
| <!-- Hosted Server -->
| <!-- Multiplayer Client -->
| <!-- JIP MP Client -->


With that being said, here are the three basic rules to get yourself in the clear:
|-
| [[Modules]] are initialised
|
| <!-- Single Player -->
| <!-- Dedicated Server -->
| <!-- Hosted Server -->
| <!-- Multiplayer Client -->
| {{Icon|unchecked}} <!-- JIP MP Client -->


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


=== Make it work ===
|- 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 -->


{{quote|Premature optimization is the root of all evil.|[https://en.wikipedia.org/wiki/Donald_Knuth Donald Knuth]}}
|- style="background-color: #DEF0AD"
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.
| [[Event Scripts#initPlayerServer.sqf|initPlayerServer.sqf]] is executed on the server
| [[Scheduler#Scheduled Environment|Scheduled]]
| <!-- Single Player -->
| {{n/a}} <!-- Dedicated Server -->
| ?? <!-- Hosted Server -->
| <!-- Multiplayer Client -->
| <!-- JIP MP Client -->


* 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 [[allUnits|all the units]] [[distance|near]] [[locationPosition|the city]], and [[forEach|for each]] [[side|west]] soldier in them, [[setDamage|add 30% damage]]''.
|-
* Use [[Arma_3_Startup_Parameters#Developer_Options|-showScriptErrors]] startup parameter and '''make sure your code doesn't throw errors.''' Not only will your code run slower but it may also ''not work at all''. Be sure to read the error, isolate the issue and sort it out [[:Category:Scripting Commands|thanks to this Wiki]].
| [[Arma 3: Functions Library|Functions]] with <syntaxhighlight lang="cpp" inline>postInit</syntaxhighlight> {{Link|Arma 3: Functions Library#Attributes 3|attribute}} are called
* Read your [[Crash_Files|Arma RPT]] (report) to read more details about the error that happened in your code.
| [[Scheduler#Scheduled Environment|Scheduled]]<ref name="enginewaits"/>
| <!-- Single Player -->
| <!-- Dedicated Server -->
| <!-- Hosted Server -->
| <!-- Multiplayer Client -->
| <!-- JIP MP Client -->


=== Make it readable ===
|- style="background-color: #95DEF0"
| [[Event Scripts#init.sqs|init.sqs]] is executed
| [[Scheduler#Scheduled Environment|Scheduled]]
| <!-- Single Player -->
| <!-- Dedicated Server -->
| <!-- Hosted Server -->
| <!-- Multiplayer Client -->
| <!-- JIP MP Client -->


Whether you are cleaning your code or a different person's, you must understand the code without twisting your brain:
|- style="background-color: #95DEF0"
| [[Event Scripts#init.sqf|init.sqf]] is executed
| [[Scheduler#Scheduled Environment|Scheduled]]
| <!-- Single Player -->
| <!-- Dedicated Server -->
| <!-- Hosted Server -->
| <!-- Multiplayer Client -->
| <!-- JIP MP Client -->


* While [[SQF syntax|SQF]] ''is'' (non-noticeably) 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 most of the time not worth the headache it gives when trying to read it. Don't overuse it.
| [[remoteExec]]'s [[Multiplayer Scripting#Join In Progress|JIP]] queue
* Indentation is important for the human mind, and space is too. Space is free, use it.
| {{n/a}}
* Same goes for line return; it helps to see a code block wrapping multiple common instructions instead of having to guess where it starts and stops.
| {{n/a}} <!-- Single Player -->
* Do you see the same code multiple times, only with different parameters? Now is the time to write a function!
| {{n/a}} <!-- Dedicated Server -->
* 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.
| {{n/a}} <!-- Hosted Server -->
* Is your function code far too long? Break it in understandable-sized bites for your own sanity.
| {{n/a}} <!-- Multiplayer Client -->
* Finally, camel-casing (namingLikeThis) your variables and commands will naturally make the code more readable, especially for long names.
| 42 <!-- JIP MP Client -->


{{Informative| '''_i''' is an accepted variable standard for a [[for]]..[[do]] iteration}}
|- style="background-color: #EEE"
| ''Scenario going''
| {{n/a}}
| <!-- Single Player -->
| <!-- Dedicated Server -->
| <!-- Hosted Server -->
| <!-- Multiplayer Client -->
| <!-- JIP MP Client -->


See the following code:
|-
_w=[]; {_w pushbackunique primaryweapon _x} foreach((allunits+alldeadmen) select{_x call bis_fnc_objectside==east});
| [[Event Scripts#exit.sqf|exit.sqf]]
|
| <!-- 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:
|-
_weaponNames = [];
| [[Event Scripts#exit.sqs|exit.sqs]]
_allUnitsAliveAndDead = allUnits + allDeadMen;
|
_allEastAliveAndDead = _allUnitsAliveAndDead select { _x call BIS_fnc_objectSide == east };
| <!-- Single Player -->
{ _weaponNames pushBackUnique primaryWeapon _x } forEach _allEastAliveAndDead;
| <!-- Dedicated Server -->
<!--
| <!-- Hosted Server -->
EDITOR'S NOTE: ^ code examples are not linking commands on purpose! This allows for a fair comparison of both syntaxes' readability.
| <!-- Multiplayer Client -->
-->
| <!-- JIP MP Client -->


==== Constants ====
|-
Using a hard-coded constant more than once? Use preprocessor directives rather than storing it in memory or cluttering your code with numbers. Such as:
| {{Link|Arma 3: Mission Event Handlers#Ended|"Ended" Mission Event Handler}}
a = _x + 1.053;
|
b = _y + 1.053;
| <!-- Single Player -->
And
| <!-- Dedicated Server -->
_buffer = 1.053;
| <!-- Hosted Server -->
a = _x + _buffer;
| <!-- Multiplayer Client -->
b = _y + _buffer;
| <!-- JIP MP Client -->
Becomes
<span style="color: purple; font-weight: bold;">#define BUFFER 1.053</span> {{codecomment|// note: no semicolon}}
_a = _x + BUFFER;
_b = _y + BUFFER;
{{Informative | Using the '''#define''' macro only works within the current [[SQF]] ''file''. Such definition will not propagate anywhere else.}}
'''Global''' "constants" can be defined ''via'' a [[Description.ext]] declaration, though, and accessed using [[getMissionConfigValue]] command:


Declaration in [[Description.ext]]:
|-
<syntaxhighlight lang="cpp">
|  {{Link|Arma 3: Mission Event Handlers#MPEnded|"MPEnded" Mission Event Handler}}
var1 = 123;
|
var2 = "123";
| <!-- Single Player -->
var3[] = {1,2,3};
| <!-- Dedicated Server -->
rand = __EVAL(random 999);
| <!-- Hosted Server -->
</syntaxhighlight>
| <!-- Multiplayer Client -->
| <!-- JIP MP Client -->


Usage in code:
|}
[[hint]] [[str]] [[getMissionConfigValue]] "var1"; // 123 {{codecomment|// 0.0007 ms}}
[[hint]] [[str]] [[getMissionConfigValue]] "var2"; // "123" {{codecomment|// 0.0008 ms}}
[[hint]] [[str]] [[getMissionConfigValue]] "var3"; // [1,2,3] {{codecomment|// 0.0017 ms}}
[[hint]] [[str]] [[getMissionConfigValue]] "rand"; // constant random, for example 935.038 {{codecomment|// 0.0007 ms}}


The [[getMissionConfigValue]] command searching [[Description.ext]] from top to bottom,
it is better for a matter of performance to put all your definitions at the top of the file.


=== Optimise then ===
== See Also ==


Once you know what is what, you can understand your code better.
* [[Arma 3: Functions Library]]<!--
* Use private variables instead of global variables (preceded with an underscore) as much as possible
* [[Arma 2: Functions Library]] -->
* You were iterating multiple times on the same array?
* [[Arma 3: Remote Execution]], [[BIS_fnc_MP]] <!-- keep? -->
** You should be able to spot your issue now.
* [[Eden Editor: Configuring Attributes|Eden Editor: Configuring Attributes]]
* Are you using [[execVM]] on the same file, many times?
* [[Event Scripts]]
** Store your function in memory to avoid file reading every call with {{Inline code|_myFunction {{=}} [[compile]] [[preprocessFileLineNumbers]] "myFile.sqf";}}
* [[Scheduler]]
* 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;
 
{{Informative | See [[Code Optimisation]] for more information.}}
 
 
== Best practices ==
 
=== Code format === // move that in Make it Readable maybe - it's too late, brb later (:
 
* Some general coding tips from [https://www.topcoder.com/blog/coding-best-practices/ here]: standards, line lengths, etc
** Variable names should indicate what they store / are used for.
 
* Format, indentation, no one-line, spacing, line returns
* Be consistent (space/tab indentation, (camel)casing, [https://en.wikipedia.org/wiki/Indentation_style#K&R_style K&R style] / [https://en.wikipedia.org/wiki/Indentation_style#Allman_style Allman style] indenting)
* Use comments frequently to explain ''not'' what the code does, but ''why'' it is done this way.
 
=== Make reusable functions ===
 
* Don't copy and paste the same code, make functions
* Don't make macros to replace SQF code
 
=== Variables ===
 
* Prefix your public variables and [[setVariable]] with your tag
* PRIVATE (or params) your variables
* Use {{Inline code|#define SOME_CONST}} for constant values instead of variables
 
=== Code location ===
 
* '''Nothing''' in init box ''but'' [[local]] commands for this specific unit - '''all''' the init boxes are run client-side on client connection
* {{Inline code|<span style{{=}}"background-color: #FCC">'''0 {{=}}''' </span>''myCommand''}} is "useful" only for editor fields that for no apparent reason refuse commands returning a value.
 
 
== Final words ==
 
* Learn from others' scripts but don't steal code and pretend it's yours — be a decent human being.
* Don't try to obfuscate your code: it is considered rude, especially since you learnt from others.
* Have fun!

Latest revision as of 01: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.