Lou Montana/Sandbox – User

From Bohemia Interactive Community
Jump to navigation Jump to search
m (Add some examples)
m (Update w/ links)
 
(50 intermediate revisions by the same user not shown)
Line 1: Line 1:
[[Category: Sandbox]]
[[Category: Sandbox]]
{{Feature|informative|To go on [[Initialisation Order]].}}


{{Informative | Future [[Code Best Practices]] page}}
{| class="wikitable sortable align-center align-left-col-1"
{{wip}}
|+ 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 -->


{{SideTOC}}
|-
{{Stub}}
| [[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 -->


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.
| Object initialisation fields are called
If you have a good structure, '''do not''' change your code to enforce a single arbitrary rule.
| [[Scheduler#Unscheduled Environment|Unscheduled]]
If you break many of them, you may have to change something; again, this is according to your judgement.
| 5 <!-- Single Player -->
| 5 <!-- Dedicated Server -->
| 5 <!-- Hosted Server -->
| 4 <!-- Multiplayer Client -->
| 3 <!-- JIP MP Client -->


With that being said, let's go!
|- style="background-color: #95F0AD"
| [[Event Scripts#init.sqs|init.sqs]] is executed
|
| 6 <!-- Single Player -->
| <!-- Dedicated Server -->
| <!-- Hosted Server -->
| <!-- Multiplayer Client -->
| <!-- JIP MP Client -->


=== Prerequisites ===
|- 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 -->


* An advanced text editor such as Visual Studio Code or Notepad++ (see [[Debugging Techniques#Code Edition|Debugging Techniques' Code Edition chapter]] for useful plugins)
|-
* Some English knowledge, helped if needed by a translator tool such as [https://www.google.com/translate Google Translate] or [https://www.deepl.com/ DeepL]
| Expressions of [[Eden Editor: Configuring Attributes|Eden Editor scenario attributes]] are called<ref name="playerCommandNotAvailable">[[player]] is not available immediately.</ref>
* Access to this wiki is a plus (the Swiss flag is, too)
| [[Scheduler#Unscheduled Environment|Unscheduled]]
* Tutorials/examples (YouTube tutorials, community tutorials, [[:Category:Example_Code|Example Code]])
| <!-- Single Player -->
* Google-Fu (a.k.a search engine skills)
| <!-- Dedicated Server -->
* {{arma3}} Discord channel: [https://discord.gg/arma/ ARMA], channel <tt>#scripting</tt>
| <!-- Hosted Server -->
| <!-- Multiplayer Client -->
| <!-- JIP MP Client -->


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


== Best practices ==
|-
| [[Modules]] are initialised
|
| <!-- Single Player -->
| <!-- Dedicated Server -->
| <!-- Hosted Server -->
| <!-- Multiplayer Client -->
| {{Icon|unchecked}} <!-- JIP MP Client -->


=== Code format ===
|- 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 -->


* Whatever you do about your code's format, '''be consistent'''.
|- style="background-color: #DEF0AD"
* Choose an indentation format '''and stick to it'''. There is not especially one indent better than another (''but there sure are terrible ones''), again the important point here being ''consistency''.
| [[Event Scripts#initPlayerLocal.sqf|initPlayerLocal.sqf]] is executed
** Common indentation styles are:
| [[Scheduler#Scheduled Environment|Scheduled]]
*** [https://en.wikipedia.org/wiki/Indentation_style#K&R_style K&R style] indenting
| <!-- Single Player -->
*** [https://en.wikipedia.org/wiki/Indentation_style#Allman_style Allman style] indenting
| {{n/a}} <!-- Dedicated Server -->
** Use empty space. Line return, spaces before and after brackets, if this improves readability, use it: space is free.
| <!-- Hosted Server -->
** indent with two/four spaces '''or''' one tab. Do not mix these.
| <!-- Multiplayer Client -->
** ''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.
| <!-- JIP MP Client -->
* {{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.<br><!--
--> You do '''not''' need it in script files.


=== Variable format ===
|- style="background-color: #DEF0AD"
| [[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 -->


* Name your variables and functions properly:<!--
|-
--> 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.
| [[Arma 3: Functions Library|Functions]] with <syntaxhighlight lang="cpp" inline>postInit</syntaxhighlight> {{Link|Arma 3: Functions Library#Attributes 3|attribute}} are called
** Your variable names must have a meaning: variables like '''_u''' instead of '''_uniform''' should not exist. '''_i''' is an accepted iteration variable name (e.g in [[for]] loops).
| [[Scheduler#Scheduled Environment|Scheduled]]<ref name="enginewaits"/>
** It is recommended to use [https://en.wikipedia.org/wiki/Camel_case camel-case] your variables;<!--
| <!-- Single Player -->
--> camel-casing (namingLikeThis) your variables and functions make the code naturally more readable, especially for long names.
| <!-- Dedicated Server -->
* Prefix your public variables and [[setVariable]] with your tag in order to avoid any conflict about other mods, scripts or mission variables.
| <!-- Hosted Server -->
* Make your variables '''private''' thanks to the [[private]] or [[params]] keyword in order to avoid accidental upper-scope overwriting of variables of the same name.
| <!-- Multiplayer Client -->
* Defined constants must be UPPERCASE_WITH_UNDERSCORES (e.g {{Inline code|#define SOME_CONST}}).
| <!-- JIP MP Client -->


|- 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 -->


=== Code structuration ===
|- 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 -->


* '''DRY:''' '''D'''on't '''R'''epeat '''Y'''ourself. If you write the same code block or the same logic many times, export this code as a function and use parameters with it.
|-
** If your code has too many levels, it is time to split and rethink it<!--
| [[remoteExec]]'s [[Multiplayer Scripting#Join In Progress|JIP]] queue
--> (e.g {{Inline code|[[if]] (a) [[then]] { [[if]] (b) [[then]] { [[if]] (c) [[then]] { {{codecomment|/* etc */ }} }; }; };}}...)
| {{n/a}}
** Do NOT use [[PreProcessor Commands#Macros|macros]] as functions - these hinder readability. Use functions instead.
| {{n/a}} <!-- Single Player -->
* Using {{codecomment|comments}} in your code must not explain ''what'' the code does, but ''why'' it is done this way (if needed).<!--
| {{n/a}} <!-- Dedicated Server -->
--> Your code organisation combined to your variable names must be enough to be read by a human.
| {{n/a}} <!-- Hosted Server -->
| {{n/a}} <!-- Multiplayer Client -->
| 42 <!-- JIP MP Client -->


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


* Use [[Arma 3 Functions Library#Adding a Function|CfgFunctions]] to declare the functions that will be called frequently.
|-
** One Functions directory, with sub-directories if needed.
| [[Event Scripts#exit.sqf|exit.sqf]]
* Use [[Event Scripts]] as needed.
|
* Don't put '''any code''' in a unit's init box ''but eventually'' [[local]] commands for this specific unit - '''all''' unit's init boxes are run client-side on client connection.
| <!-- Single Player -->
| <!-- Dedicated Server -->
| <!-- Hosted Server -->
| <!-- Multiplayer Client -->
| <!-- JIP MP Client -->


== Examples ==
<!--
EDITOR'S NOTE: Formatting Code examples are not linking commands on purpose! This allows for a fair comparison of both syntaxes' readability.
-->
{| class="bikitable"
! <big>Bad Example</big>
! <big>Good Example</big>
|-
! colspan="2" |
<small>
=== Good Practice examples ===
</small>
|-
| <code>_unit = player;</code>
| <code>'''private''' _unit = player;</code>
|-
| <code>private _uB = allUnits select { side _x == blufor };</code>
| <code>private '''_bluforUnits''' = allUnits select { side _x == blufor };</code>
|-
| <code>private _plead = leader player;</code>
| <code>private '''_playersLeader''' = leader player;</code>
|-
| <code>finalAssault = true; publicVariable "finalAssault";</code>
| <code>'''PREFIX_'''finalAssault = true; publicVariable "'''PREFIX_'''finalAssault";</code>
|-
|-
| <code>player setVariable ["MoneyInPocket", 250, true];</code>
| [[Event Scripts#exit.sqs|exit.sqs]]
| <code>player setVariable ["'''PREFIX_'''MoneyInPocket", 250, true];</code>
|- style="vertical-align: top"
|
|
<code>{{cc|if player has less than 3/4 health}}
| <!-- Single Player -->
if (damage player > 0.25) then
| <!-- Dedicated Server -->
{
| <!-- Hosted Server -->
{{cc|if the player has no first aid kit}}
| <!-- Multiplayer Client -->
if (not ("FirstAidKit" in items player))
| <!-- JIP MP Client -->
{
 
{{cc|if player has room for first aid kit}}
if (player canAdd "FirstAidKit") then
{
{{cc|add first aid kit to the player}}
player addItem "FirstAidKit";
}
else
{
{{cc|set player's damage to 1/4}}
player setDamage 0.25;
};
};
};</code>
|
<code>{{cc|player will need health at this stage of the mission}}
if (
damage player > 0.25 &&
not ("FirstAidKit" in items player)) then
{
if (player canAdd "FirstAidKit") then
{
player addItem "FirstAidKit";
}
else {{cc|let's help him anyway}}
{
player setDamage 0.25;
};
};</code>
|-
|-
! colspan="2" |
| {{Link|Arma 3: Mission Event Handlers#Ended|"Ended" Mission Event Handler}}
<small>
=== Flow Logic examples ===
</small>
|- style="vertical-align: top"
|
|
<code>if (alive player && damage player >= 0.9) then {
| <!-- Single Player -->
hint "very damaged";
| <!-- Dedicated Server -->
};
| <!-- Hosted Server -->
if (alive player && damage player >= 0.5 && damage player < 0.9) then {
| <!-- Multiplayer Client -->
hint "quite damaged";
| <!-- JIP MP Client -->
};
 
if (alive player && damage player > 0 && damage player < 0.5) then {
hint "slightly damaged";
};
if (alive player && damage player == 0) then {
hint "pristine state";
};
if (not alive player) then {
hint "dead";
};</code>
|
<code>if (not alive player) exitWith { hint "dead"; };<br>
private _playerDamage = damage player;
switch (true) do {
case (_playerDamage >= 0.9): { hint "very damaged"; };
case (_playerDamage >= 0.5): { hint "quite damaged"; };
case (_playerDamage > 0)  : { hint "slightly damaged"; };
default { hint "pristine"; };
};</code>
|-
|-
! colspan="2" |
| {{Link|Arma 3: Mission Event Handlers#MPEnded|"MPEnded" Mission Event Handler}}
<small>
=== Format examples ===
</small>
|- style="vertical-align: top"
|
|
<code>if (not alive player) exitWith
| <!-- Single Player -->
{ hint "dead"; };<br>
| <!-- Dedicated Server -->
private _playerDamage = damage player;
| <!-- Hosted Server -->
switch (true) do {
| <!-- Multiplayer Client -->
case (_playerDamage >= 0.9): {hint "very damaged";};
| <!-- JIP MP Client -->
case (_playerDamage >= 0.5): {
 
hint  "quite damaged";};<br>
case (_playerDamage > 0): { hint "slightly damaged"; };
<nowiki>  </nowiki>default
{<br>
<nowiki>    </nowiki>hint "pristine";
<nowiki>  </nowiki>};
};</code>
|
<code>if (not alive player) exitWith { hint "dead"; };<br>
private _playerDamage = damage player;
switch (true) do {
case (_playerDamage >= 0.9): { hint "very damaged"; };
case (_playerDamage >= 0.5): { hint "quite damaged"; };
case (_playerDamage > 0)  : { hint "slightly damaged"; };
default { hint "pristine"; };
};</code>
|}
|}




== Final words ==
== See Also ==


* Learn from others' scripts but don't steal code and pretend it's yours — be a decent human being. Stealing code and its consequences:
* [[Arma 3: Functions Library]]<!--
** It soils your reputation and devaluates your actions once it is found out — and it ''always'' get found out. DMCA's are filled on Steam every day.
* [[Arma 2: Functions Library]] -->
** It makes people in the community get less helpful and more reluctant in giving advices. It can also prevent them to ''release'' an interesting feature!
* [[Arma 3: Remote Execution]], [[BIS_fnc_MP]] <!-- keep? -->
* Don't try to obfuscate your code: it is considered rude, especially since you learnt from others.
* [[Eden Editor: Configuring Attributes|Eden Editor: Configuring Attributes]]
** Obfuscated code only makes it ''harder'' to get, but does not make it ''protected''.
* [[Event Scripts]]
** Obfuscated code is also slower on compilation (and, depending on the quality of code and obfuscation, on execution too).
* [[Scheduler]]
* Have fun!

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.