Difference between revisions of "Arma 3: Functions Library"

From Bohemia Interactive Community
Jump to navigation Jump to search
m (Text replacement - "Category:Arma 3: Editing" to "{{GameCategory|arma3|Editing}}")
m (Fix typo)
 
(31 intermediate revisions by 2 users not shown)
Line 1: Line 1:
Arma 3 '''Functions Library''' is pack of routine script functions available from anywhere in game. Main difference from older [[Functions Library]] is that it runs automatically and doesn't require Functions manager to be present.
+
{{TOC|side||3}}
 +
{{arma3}}'s '''Functions Library''' is the way to declare mission, campaign or addon's [[Function]]s. The main difference from older [[:Category:Functions Library|Function Libraries]] is that it runs automatically and does not require a Functions module.
  
 +
The advantages of using the Functions Library over e.g [[execVM]] an [[SQF Syntax|SQF]] file are as follow:
 +
# '''Automatic compilation upon mission start''' into a global variable - no need to remember direct paths to files.
 +
# '''Anti-hack protection''' using [[compileFinal]] (see {{HashLink|#Recompiling}})
 +
# '''Listing in the [[Arma 3: Functions Viewer|Functions Viewer]]'''
 +
# '''Advanced debugging options'''
 +
# '''Optional immediate execution upon mission start''', without need for manual call
 +
# '''Potential performance improvements'''
  
== Finding a Function ==
 
 
[[File:A3_functionwViewer.png|300px|thumb|right|Functions Viewer]]
 
Before you can use a function, you first need to find it. The easiest way is to access the '''Functions Viewer''':
 
 
* In editor, click on [[File:icon editor functions.png|20px]] icon or press Ctrl + F
 
* In mission, access the [[Arma 3 Debug Console|debug console]] (automatically visible in pause menu of an editor mission) and click on '''FUNCTIONS''' button.
 
 
Once in the '''Functions Viewer''', you can filter all available functions by location, projects and categories.
 
 
When you find the desired function, look at the code preview on the right. Every function has a header where you can find basic description of its functionality including required arguments, returned values and sometimes examples of use.
 
 
{{note|Some functions, especially the older ones, may have missing or incomplete headers.<br>We do our best to update them, but if you find some issues, feel free to report them in the [http://feedback.arma3.com/ Feedback Tracker].}}
 
 
 
All functions are also listed in [[:Category:Arma_3:_Functions|Arma 3 Functions]] category.
 
 
 
== Calling a Function ==
 
 
Functions can be launched in mission, intro and outro using this [[call]] or [[spawn]] commands:
 
_returnedValue = ''arguments'' [[call]] ''functionName'';
 
''arguments'' [[spawn]] ''functionName'';
 
 
=== Arguments ===
 
 
Arguments are data sent into the function, affecting its behavior.
 
 
They can be '''mandatory''' or '''optional'''.
 
* Mandatory arguments are required for function to run. When missing, the function usually stops and throws an error.
 
* Optional arguments allows more detailed configuration. If you dont send them, the function will use pre-defined default values.
 
 
{{note|Within the function, '''arguments''' are called '''parameters''', or simply params.}}
 
 
 
For example, let's take a look at [[BIS_fnc_endMission]], a function which ends a mission with animated closing shot. This is what the header says:
 
/*
 
Author: Karel Moricky
 
 
Description:
 
Ends mission with specific ending.
 
 
{{Color|DarkCyan|Parameter(s):
 
0 (Optional):
 
STRING - end name (default: "end1")
 
ARRAY in format [endName,ID], will be composed to "endName_ID" string
 
1 (Optional): BOOL - true to end mission, false to fail mission (default: true)
 
2 (Optional):
 
BOOL - true for signature closing shot (default: true)
 
NUMBER - duration of a simple fade out to black}}
 
 
Returns:
 
BOOL
 
*/
 
 
As you can see, all arguments are marked optional and you can call the function without them.
 
 
[] [[call]] [[BIS_fnc_endMission]];
 
:This will result in successfull ending of type "end1", preceeded with the signature [[Debriefing|closing shot]].
 
 
["end2"] [[call]] [[BIS_fnc_endMission]];
 
:Set the ending type to "end2", while keeping the other arguments intact.
 
 
["end2", [[false]], [[false]]] [[call]] [[BIS_fnc_endMission]];
 
:Fail the mission without any effect, using "end2" type.
 
 
 
However, what should you do if you want to set the '''only''' last argument without affecting the previous ones? The solution is simple - put an empty variable [[nil]] on their place.
 
 
[<nowiki/>[[nil]], [[nil]], [[false]]] [[call]] [[BIS_fnc_endMission]];
 
:Disable the closing effects, but keep the other aguments intact (successful "end1").
 
 
{{note|Some older functions are incompatible with this syntax and using nil would break them. See their header for more information.}}
 
 
=== Returned Value ===
 
 
Functions executed by [[call]] command can return back a value. Let's take a look at [[BIS_fnc_sideName]]:
 
 
/*
 
Author: Karel Moricky
 
 
Description:
 
Returns side name
 
 
Parameter(s):
 
0: SIDE or NUMBER - either side or side ID
 
 
{{Color|DarkCyan|Returns:
 
STRING}}
 
*/
 
The function returns a [[String]] - localized name of a side.
 
_westName = [[west]] [[call]] [[BIS_fnc_sideName]];
 
:Variable _westName will now be "BLUFOR" (or other name, based on selected language)
 
  
=== Multiplayer ===
+
{{Feature|important|This page is about {{arma3}} Functions Library.
 +
* For {{arma2}}/{{Name|arma2oa|short}}, see [[Arma 2: Functions Library]].
 +
* For {{tkoh}}, see [[Take On Helicopters: Functions Library]].
 +
}}
  
Functions executed using [[call]] or [[spawn]] command will run only on the computer which triggered them. If you'd wish to execute a function remotely on specific clients, use either [[remoteExec]] or [[remoteExecCall]].<br>
 
[arguments] [[remoteExec]] ["functionName",target,isPersistent];
 
  
=== User Interface ===
+
== Function Declaration ==
  
Anywhere outside of running mission, refer to the functions stored in [[uiNamespace]].
+
Functions are configured within the <tt>CfgFunctions</tt> class.
''arguments'' [[call]] ([[uiNamespace]] [[getVariable]] ''"functionName"'');
 
  
== Adding a Function ==
+
Mission and campaign specific functions can be configured in [[Description.ext]]/[[Campaign Description.ext]], while addon functions are defined in [[Config.cpp]].
When writing a script, consider registering it into the Functions Library.
+
Configuration structure is the same in both cases.
 
 
Main benefits:
 
# '''Automatic compilation upon mission start''' into a global variable - no need to remember direct paths to files.
 
# '''Anti-hack protection''' using [[compileFinal]] (see [[#Recompiling|Recompiling ]] for more info)
 
# '''Listing in the Functions Viewer'''
 
# '''Advanced debugging options'''
 
# '''Optional immediate execution upon mission start''', without need for manual call
 
# '''Potential performance improvements'''
 
  
  
Mission and campaign specific functions can be configured in [[Description.ext]], while addon functions are defined in [[Config.cpp]]. Configuration structure is the same in both cases.
+
{{Feature|informative|The root directory (noted as '''&lt;ROOT&gt;''' on this page) is the origin from which the game will try to load function files. It depends on <tt>CfgFunctions</tt>' location:
 +
* if [[Description.ext]], '''&lt;ROOT&gt;''' means the [[Mission Editor: External#Mission Folder|mission's root]] directory
 +
* if [[Campaign Description.ext]], '''&lt;ROOT&gt;''' means the campaign's root directory
 +
* if in config, '''&lt;ROOT&gt;''' means the '''game'''<nowiki/>'s root directory. The addon path needs to be set manually.
 +
}}
  
=== Tag ===
 
  
Functions are configured within CfgFunctions class. To prevent duplicities, every author must create a subclass with unique ''tag'' and place functions inside it. The tag name will be used when composing a function name.
+
See a basic example config:
class CfgFunctions
+
class '''CfgFunctions'''
 
  {
 
  {
  class {{Color|green|myTag}}
+
  class {{Color|purple|TAG}}
 
  {
 
  {
  class Anything
+
  class {{Color|darkorange|Category}}
 
  {
 
  {
  tag = "{{Color|green|myTag}}"; {{cc|Custom tag name}}
+
  class {{Color|teal|functionName}} {};
requiredAddons[] = {"A3_Data_F"}; {{cc|Optional requirements of CfgPatches classes. When some addons are missing, functions won't be compiled.}}
 
 
  };
 
  };
 
  };
 
  };
 
  };
 
  };
 +
* The function's name will be '''{{Color|purple|TAG}}'''_fnc_'''{{Color|teal|functionName}}'''
 +
* The function will be loaded:
 +
** from config: <tt>'''&lt;ROOT&gt;'''\{{Color|darkorange|Category}}\'''fn_'''{{Color|teal|functionName}}'''.sqf'''</tt>
 +
** from description.ext: <tt>'''&lt;ROOT&gt;'''\'''Functions'''\{{Color|darkorange|Category}}\'''fn_'''{{Color|teal|functionName}}'''.sqf'''</tt>
  
=== Path ===
+
{{Feature|informative|'''config.cpp only''': Anywhere outside of running mission, refer to the functions stored in [[uiNamespace]]. e.g:
 +
<code>''arguments'' [[call]] ([[uiNamespace]] [[getVariable]] ''"{{Color|purple|TAG}}_fnc_{{Color|teal|functionName}}"'');</code>}}
  
==== File Path ====
+
=== Config Levels ===
The easiest and the most transparent way is to set path for each function.
+
 
 +
A '''CfgFunctions''' config is made of three levels: Tag, Category, and Function.
 +
 
 +
==== Tag ====
 +
To prevent duplicates, every author must create a subclass with a unique {{Color|purple|[[OFPEC tags|tag]]}} and create functions under it.
 +
The tag name will be used when composing a function name (e.g [[BIS_fnc_spawnGroup|'''BIS'''_fnc_spawnGroup]]).
 
  class CfgFunctions
 
  class CfgFunctions
 
  {
 
  {
  class {{Color|green|myTag}}
+
  class {{Color|purple|TAG}}
 
  {
 
  {
  class myCategory
+
  class Category
 
  {
 
  {
  class {{Color|teal|myFunction}} {file = "{{Color|DarkOrange|myFile.sqf}}";};
+
  class myFunction {};
 
  };
 
  };
 
  };
 
  };
  };
+
   
This will try to compile function '''<big>{{Color|green|myTag}}_fnc_{{Color|teal|myFunction}}</big>''' from the following file:
+
  class TAG_WeaponManagement
''%ROOT%''\{{Color|DarkOrange|myFile.sqf}}
 
Where ''%ROOT%'' is either '''[[Mission_Editor:_External#Mission_Folder|mission root]]''' (where mission.sqm file is), or the '''game root''' (path to an addon is not included and has to be part of the file path, e.g., {{Color|DarkOrange|myAddon\myFile.sqf}}).
 
 
 
==== Folder Path ====
 
You can set folder path and leave the function paths undefined. The functions will then be loaded from the folder.
 
class CfgFunctions
 
{
 
  class {{Color|green|myTag}}
 
 
  {
 
  {
  class myCategory
+
'''tag''' = {{Color|purple|"TAG"}}; {{cc|the function will be named '''{{Color|purple|TAG}}_fnc_myOtherFunction'''}}
 +
  class Category
 
  {
 
  {
file = "{{Color|DarkOrange|myPath}}";
+
  class myOtherFunction {};
  class {{Color|teal|myFunction}} {};
 
 
  };
 
  };
 
  };
 
  };
 
  };
 
  };
Compile function '''<big>{{Color|green|myTag}}_fnc_{{Color|teal|myFunction}}</big>''' from the following file:
 
''%ROOT%''\{{Color|DarkOrange|myPath}}\fn_{{Color|teal|myFunction}}.sqf
 
{{Color|DarkOrange|myPath}} can be a folder or multiple folders, e.g., {{Color|DarkOrange|myFolder\mySubfolder}}
 
  
==== Default Path (Mission Only) ====
+
===== Attributes =====
In a mission, you can leave also the folder path undefined and let functions be loaded from the default directory.
+
====== tag ======
 +
The <tt>tag</tt> attribute can override the config {{Color|green|tag}} set differently.
 +
 
 +
==== Category ====
 +
The category name changes the function's category in the [[Arma 3: Functions Viewer|Functions Viewer]]. It does not change the function's name, only the loading path.
 
  class CfgFunctions
 
  class CfgFunctions
 
  {
 
  {
  class {{Color|green|myTag}}
+
  class TAG
 
  {
 
  {
  class {{Color|crimson|myCategory}}
+
  class {{Color|darkorange|Category}}
 
  {
 
  {
  class {{Color|teal|myFunction}} {};
+
  class myFunction {};
 +
};
 +
 +
class OtherCategory
 +
{
 +
'''file''' = "{{Color|darkorange|My}}\{{Color|darkorange|Category}}\{{Color|darkorange|Path}}";
 +
class myFunction {}; {{cc|file path will be '''&lt;ROOT&gt;'''\{{Color|darkorange|My}}\{{Color|darkorange|Category}}\{{Color|darkorange|Path}}\fn_myFunction.sqf";}}
 +
};
 +
 +
class DataCategory
 +
{
 +
'''requiredAddons[]''' = {{Color|darkorange|{ "A3_Data_F" }<nowiki/>}}; {{cc|Optional requirements of [[CfgPatches]] classes. If some addons are missing, category functions will not be compiled.}}
 +
class myDataFunction {};
 
  };
 
  };
 
  };
 
  };
 
  };
 
  };
Compile function '''<big>{{Color|green|myTag}}_fnc_{{Color|teal|myFunction}}</big>''' from the following file:
 
''%ROOT%''\functions\{{Color|crimson|myCategory}}\fn_{{Color|teal|myFunction}}.sqf
 
  
=== Attributes ===
+
===== Attributes =====
 +
====== file ======
 +
The <tt>file</tt> attribute can override the category's loading path segment.
  
Apart from already mentioned ''file'', function class can have additional attributes:
+
====== requiredAddons ======
 +
The category can skip loading if a required addon is missing by setting its dependency with the <tt>requiredAddons</tt> attribute.
 +
 
 +
==== Function ====
 +
{{Feature|informative|By convention, the function class should be named in '''camelCase''' and not have its first letter capitalised (e.g PascalCase):
 +
* {{Icon|checked}} TAG_fnc_{{Color|teal|myFunction}}
 +
* {{Icon|unchecked}} TAG_fnc_{{Color|teal|MyFunction}}
 +
* {{Icon|checked}} TAG_fnc_{{Color|teal|POWRescue}}
 +
}}
 
  class CfgFunctions
 
  class CfgFunctions
 
  {
 
  {
  class {{Color|green|myTag}}
+
  class TAG
 
  {
 
  {
  class myCategory
+
  class Category1
 
  {
 
  {
  class {{Color|teal|myFunction}}
+
  class {{Color|teal|myFunction}} {};
 +
};
 +
 +
class Category2
 +
{
 +
file = "Path\To\Category";
 +
class myFunction
 +
{
 +
'''file''' = "{{Color|teal|My}}\{{Color|teal|Function}}\{{Color|teal|Filepath.sqf}}"; {{cc|file path will be '''&lt;ROOT&gt;'''\{{Color|teal|My}}\{{Color|teal|Function}}\{{Color|teal|Filepath.sqf}}", ignoring "Path\To\Category"}}
 +
};
 +
 +
class {{Color|teal|myFSMFunction}}
 
  {
 
  {
  preInit = 1; {{cc|(formerly known as "forced") 1 to call the function upon mission start, <u>before</u> objects are initialized. Passed arguments are ["preInit"]}}
+
  '''preInit''' = 1;
  postInit = 1; {{cc|1 to call the function upon mission start, <u>after</u> objects are initialized. Passed arguments are ["postInit", didJIP]}}
+
  '''postInit''' = 1;
preStart = 1; {{cc|1 to call the function upon game start, before title screen, but after all addons are loaded (config.cpp only)}}
+
  '''ext''' = ".fsm";
  ext = ".fsm"; {{cc|Set file type, can be ".sqf" or ".fsm" (meaning scripted FSM). Default is ".sqf".}}
+
  '''preStart''' = 1;
  headerType = -1; {{cc|Set function header type: -1 - no header; 0 - default header; 1 - system header.}}
+
  '''recompile''' = 1;
  recompile = 1; {{cc|1 to recompile the function upon mission start (config.cpp only; functions in description.ext are compiled upon mission start already)}}
 
 
  };
 
  };
 
  };
 
  };
 
  };
 
  };
 
  };
 
  };
All of these attributes are case sensitive.
 
  
==== Pre and Post Init ====
+
===== Attributes =====
''preInit'' and ''postInit'' attributes are truly powerful ones, as they let you execute your function at the beginning of '''every''' mission. Use them with caution!.<br>
+
{| class="wikitable sortable"
''preInit'' are '''called''' [[Scheduler#Unscheduled_Environment|unscheduled]] so suspension is not allowed. Parameters passed are [ "preInit" ].<br>
+
! rowspan="2" | Attribute
''postInit'' are '''called''' [[Scheduler#Scheduled_Environment|scheduled]] so suspension is allowed '''but any long term suspension will halt the mission loading until suspension has finished.''' Parameters passed are [ "postInit", [[didJIP]] ].<br>
+
! rowspan="2" | Description
 
+
! colspan="2" | Available in
* Any scripting error will prevent the mission from being loaded correctly
+
|- style="font-size: 0.9em"
* Server admins might blacklist your addon if they find out you're using the function for hacking.
+
! [[Description.ext]]
<!-- HIDDEN as per note in talk page by STR
+
! [[config.cpp]]
 
+
|-
==== Header Types ====
+
| file
The different header types add code to the begining of your functions that can..
+
| the <tt>file</tt> attribute can be used to manually set the file path.
*Set up the [[#Meta Variables|Meta Variables]] _fnc_scriptParent and _fnc_scriptName
+
{{Feature|important|The '''file''' entry '''overrides''' Category's set path and ignores it entirely (they are not merged).}}
*Name the current scope via the command [[scriptName]]
+
| style="text-align: center" | {{Icon|checked}}
*Adds debug information by saving the current execution order in the [[Array]] _fnc_scriptMap.
+
| style="text-align: center" | {{Icon|checked}}
 
+
|-
 
+
| preInit
:'''''No Header'''''<br>
+
| the <tt>preInit</tt> attribute (formerly known as "forced") can be set to 1 to call the function upon mission start, before objects are initialized.<br>Passed arguments are {{ic|["preInit"]}}. The function is run in an '''[[Scheduler#Unscheduled Environment|unscheduled environment]]'''.
:Adds nothing, literally adds a blank [[String]] to the begining of your function. The function will have no debug or [[#Meta Variables|Meta Variables]] assigned for it and the scope will not be named.
+
| style="text-align: center" | {{Icon|checked}}
 
+
| style="text-align: center" | {{Icon|checked}}
 
+
|-
:'''''System Header'''''<br>
+
| postInit
:Incorporates the [[#Meta Variables|Meta Variables]] _fnc_scriptParent. Also names the current scope.
+
| the <tt>postInit</tt> attribute can be set to 1 to call the function upon mission start, after objects are initialized.<br>Passed arguments are {{ic|["postInit", [[didJIP]]]}}. The function is run in a '''[[Scheduler#Scheduled Environment|scheduled environment]]''' so suspension is allowed, but '''any long term suspension will halt the mission loading until suspension is done'''.
:{|
+
| style="text-align: center" | {{Icon|checked}}
|
+
| style="text-align: center" | {{Icon|checked}}
private _fnc_scriptNameParent = if (isNil '_fnc_scriptName') then {'%1'} else {_fnc_scriptName};
+
|-
scriptName '%1';
+
| ext
|}
+
| the <tt>ext</tt> attribute can be used to set function file's type; it can be one of:
 
+
* ".sqf" (default)
 
+
* ".fsm" (see [[FSM]])
:'''''Default Header'''''<br>
+
| style="text-align: center" | {{Icon|checked}}
:The default header changes based on the current [[#Debug Mode:Debug Mode]]
+
| style="text-align: center" | {{Icon|checked}}
 
+
|-
::''Debug Mode 0'' - default<br>
+
| preStart
::Incorporates the [[#Meta Variables|Meta Variables]] _fnc_scriptParent and _fnc_scriptName and names the current scope.
+
| the <tt>preStart</tt> attribute can be set to 1 to call the function upon game start, before title screen, but after all addons are loaded.
::{|
+
| style="text-align: center" | {{Icon|unchecked}}
|
+
| style="text-align: center" | {{Icon|checked}}
private _fnc_scriptNameParent = if (isNil '_fnc_scriptName') then {'%1'} else {_fnc_scriptName};
+
|-
private _fnc_scriptName = '%1';
+
| recompile
scriptName _fnc_scriptName;
+
| the <tt>recompile</tt> attribute can be set to 1 to recompile the function upon mission start (functions in [[Description.ext]] are always compiled upon mission (re)start)
|}
+
| style="text-align: center" | {{Icon|unchecked}}
 
+
| style="text-align: center" | {{Icon|checked}}
::''Debug Mode 1''<br>
 
::As per ''Debug Mode 0'' and also saves the current execution order in the [[Array]] _fnc_scriptMap.
 
::{|
 
|
 
private _fnc_scriptNameParent = if (isNil '_fnc_scriptName') then {'%1'} else {_fnc_scriptName};
 
private _fnc_scriptName = '%1';
 
scriptName _fnc_scriptName;
 
private _fnc_scriptMap = if (isNil '_fnc_scriptMap') then {[_fnc_scriptName]} else {_fnc_scriptMap + [_fnc_scriptName]};
 
|}
 
 
 
::''Debug Mode 2''<br>
 
::As per ''Debug Mode 1'' and also outputs execution order using [[textLogFormat]] ( textLogFormat is not available in the retail version of Arma3 ) is it available in Diagnostics exe?
 
::{|
 
|
 
private _fnc_scriptNameParent = if (isNil '_fnc_scriptName') then {'%1'} else {_fnc_scriptName};
 
private _fnc_scriptName = '%1';
 
scriptName _fnc_scriptName;
 
private _fnc_scriptMap = if (isNil '_fnc_scriptMap') then {[_fnc_scriptName]} else {_fnc_scriptMap + [_fnc_scriptName]};
 
textLogFormat ['%1 : %2', _fnc_scriptMap joinString ' >> ', _this];
 
|}
 
END HIDDEN -->
 
 
 
=== Initialization Order ===
 
 
 
''See [[Initialization_Order]]''
 
 
 
 
 
== Writing a Function ==
 
 
 
The most important thing to remember when writing a function is that other people than you are going to use it. Most of them won't understand how does it work, expecting it to do its job without problems.
 
 
 
Your function must be '''robust'''. It shouldn't allow passing arguments of incorrect [[Data Types]] in. When some values are incorrect, it should throw an error explaining what went wrong and how to fix it. And above all, its header must provide complete explanation of usage.
 
{{Important | What is not documented does not exist!}}
 
 
 
=== Loading Parameters ===
 
 
 
Arguments are the only way how to interact with your function. Let's now see how to make sure they are loaded properly.
 
 
 
We have this very simple function which will let a unit watch a position:
 
[[params]] ["_unit", "_target"];
 
_unit [[doWatch]] _target;
 
 
 
Expected way how to call the function is by correctly defining all arguments:
 
{|
 
| [[File:Ico_ok.png]]
 
|
 
[<nowiki/>[[player]], [[position]] myCar] [[call]] myTag_fnc_myFunction;
 
|}
 
 
 
However, the function will break down when you try to send only one argument in:
 
{|
 
| [[File:Ico_none.png]]
 
|
 
[<nowiki/>[[player]]] [[call]] myTag_fnc_myFunction;
 
|}
 
 
 
Furthermore, using wrong data type will also lead to a problem:
 
{|
 
| [[File:Ico_none.png]]
 
|
 
[<nowiki/>[[player]], 0] [[call]] myTag_fnc_myFunction;
 
|}
 
 
 
Variabe ''_target'' expects position array in format [x,y,z]. Scripting error will appear when different number of elements is used:
 
{|
 
| [[File:Ico_none.png]]
 
|
 
[<nowiki/>[[player]], [1, 2, 3, 4]] [[call]] myTag_fnc_myFunction;
 
|}
 
 
 
 
 
As you can see there, the most common problems are:
 
# '''Param of wrong data type is sent'''
 
# '''Param is missing'''
 
# '''Param is an array expecting specific number of elements, but different number is sent'''
 
 
 
Rather than check for these exceptions yourself, you can use existing [[param]] command which will do it for you:
 
_unit = [[param]] [{{Color|darkorange|0}}, {{Color|teal|objNull}}, {{Color|crimson|[objNull]}}];
 
For multiple parameters, use the [[params]] command instead.
 
[[params]] [
 
[{{Color|green|"_unit"}}, {{Color|teal|objNull}}, {{Color|crimson|[objNull]}}],
 
[{{Color|green|"_target"}}, {{Color|teal|[0, 0, 0]}}, {{Color|crimson|[[], objNull]}}, {{Color|indigo|[2, 3]}}]
 
];
 
_unit [[doWatch]] _target;
 
* In a [[params]] array first argument is the name of the '''{{Color|green|private variable}}'''. In [[param]] it's the '''{{Color|darkorange|index}}''' number.
 
* Second argument is the '''{{Color|teal|default value}}'''. It will be used when the argument is missing, is [[nil]] or when wrong data type is used.
 
* Next is optional '''{{Color|crimson|array of compatible data types}}'''. They are defined by an example of the type, e.g. [[objNull]] will mean an object is allowed. When wrong data type is sent into your function, BIS_fnc_param will log an error message explaining what went wrong and use the default value.
 
* The last, also optional argument is an '''{{Color|indigo|array of required array sizes}}'''. [2,3] means only array with 2 or 3 elements are allowed. When incorrectly large array is sent into your function, BIS_fnc_param will log an error message explaining what went wrong and use the default value.
 
 
 
 
 
Let's see what will happen when you try to use the wrong examples now:
 
{|
 
| [[File:Ico_ok.png]]
 
|
 
[<nowiki/>[[player]]] [[call]] myTag_fnc_myFunction;
 
|}
 
: ''_target'' is undefined. Default {{Color|teal|[0, 0, 0]}} is used instead. No error message is logged.
 
 
 
{|
 
| [[File:Ico_ok.png]]
 
|
 
[<nowiki/>[[nil]], [[position]] myCar] [[call]] myTag_fnc_myFunction;
 
|}
 
: ''_unit'' is undefined (nil is passed instead). Default {{Color|teal|objNull}} is used instead. No error message is logged.
 
 
 
{|
 
| [[File:Ico_warning.png]]
 
|
 
[<nowiki/>[[player]], 0] [[call]] myTag_fnc_myFunction;
 
|}
 
: ''_target'' has wrong {{Color|crimson|type}}. Default {{Color|teal|[0, 0, 0]}} is used instead. Error message is logged.
 
 
 
{|
 
| [[File:Ico_warning.png]]
 
|
 
[<nowiki/>[[player]], [1, 2, 3, 4]] [[call]] myTag_fnc_myFunction;
 
|}
 
: ''_target'' has wrong {{Color|indigo|size}}. Default {{Color|teal|[0, 0, 0]}} is used instead. Error message is logged.
 
 
 
 
 
Additionally, when only one argument is used, you can send it into the function directly without need to have it in an array.
 
{|
 
| [[File:Ico_ok.png]]
 
|
 
[[player]] [[call]] myTag_fnc_myFunction;
 
|}
 
 
 
=== Returning Value ===
 
 
 
Users will often save result of your function to a variable. If no value is returned, the variable would be [[nil]] and could lead to script errors.
 
_myVar = [<nowiki/>[[player]], [[position]] myCar] [[call]] myTag_fnc_myFunction;
 
 
 
It's good practice to '''always''' return a value, even if it would be simple [[true]] marking the function as completed. Let's use the example function from above:
 
[[params]] [
 
["_unit", [[objNull]], [objNull]],
 
["_target", [0, 0, 0], [[], [[objNull]]], [2, 3]]
 
];
 
_unit [[doWatch]] _target;
 
'''[[true]]'''
 
 
 
=== Showing Errors ===
 
 
 
While [[param]] and [[params]] can filter out the most common issues, sometimes your function will have special rules which will need to be handled. Let's return back to our example function, where we'd want to terminate the function with error when ''_unit'' is dead:
 
[[params]] [["_unit", [[objNull]], [objNull]], ["_target", [0, 0, 0], [[], [[objNull]]], [2, 3]]];
 
'''[[if]] (![[alive]] _unit) [[exitwWth]] { ["Unit %1 must be alive.", _unit] [[call]] [[BIS_fnc_error]]; [[false]] };'''
 
_unit [[doWatch]] _target;
 
[[true]]
 
Notice that we're returning [[false]] at the end of [[exitWith]] code.
 
{{Important|Error states must always return value of the same type as when everything is fine ([[Boolean]] in this case).}}
 
 
 
 
 
[[BIS_fnc_error]] accepts [[String]] and [[Array]] of [[format]]ted ext. The error is logged into [[RPT]] and if the mission is previewd from the editor, it will also appear on screen.
 
{|
 
| [[RPT]]
 
|
 
"User1/log: ERROR: [BIS_fnc_respawnTickets] #0: 0 is type SCALAR, must be NAMESPACE, SIDE, GROUP, OBJECT, BOOL. true used instead."
 
 
|-
 
|-
| In-game
+
| headerType
| [[File:A3 functions error.jpg|800px|On-screen error]]
+
| the <tt>headerType</tt> adds code to the beginning of functions that can:
 +
* Set up the [[#Meta Variables|Meta Variables]] _fnc_scriptParent and _fnc_scriptName
 +
* Name the current scope via the command [[scriptName]]
 +
* Adds debug information by saving the current execution order in the [[Array]] _fnc_scriptMap.<br/>
 +
Possible values are:
 +
* -1: No header - Adds nothing, literally adds a blank [[String]] to the beginning of functions. The functions will have no debug or {{HashLink|#Meta Variables}} assigned for it and the scope will not be named. Useful to reduce the performance overhead or if custom debug system is used.
 +
* 0: Default header - The default header changes based on the current [[#Debug Modes|Debug Mode]]
 +
* 1: System header - Incorporates the [[#Meta Variables|Meta Variables]] _fnc_scriptParent. Also names the current scope.
 +
[[private]] _fnc_scriptNameParent = [[if]] ([[isNil]] '_fnc_scriptName') [[then]] { '%1' } [[else]] { _fnc_scriptName };
 +
[[scriptName]] '%1';
 +
See the following section for more information.
 +
| style="text-align: center" | {{Icon|checked}}
 +
| style="text-align: center" | {{Icon|checked}}
 
|}
 
|}
  
=== Logging ===
+
{{Feature|important|Notes about <tt>preInit</tt> and <tt>postInit</tt> defined in '''config.cpp''':
 +
* these attributes will make the function run '''on each, every and any mission start'''
 +
* Any scripting error will prevent the mission from being loaded correctly
 +
* Server admins might blacklist the whole addon if they find out the function is used for hacking
 +
}}
  
Apart from errors, you can print any debug message you need. Use one of the following functions:
 
* [[BIS_fnc_log]] - log a data of any type (e.g., [[String]], [[Number]], [[Object]], ...)
 
* [[BIS_fnc_logFormat]] - log [[format]]ted text
 
Profile name and function name will automatically appear in the output text, helping you identify the source.
 
  
 +
== Advanced Knowledge ==
  
Usage examples:
+
=== Meta Variables ===
{| class="wikitable"
 
! Expression
 
! [[RPT]] Output
 
|-
 
|
 
{{Color|green|"Hello World"}} [[call]] [[BIS_fnc_log]];
 
|
 
"User1/BIS_fnc_log: [myTag_fnc_myFunction] {{Color|green|Hello World}}"
 
|-
 
|
 
{{Color|green|42}} [[call]] [[BIS_fnc_log]];
 
|
 
"User1/BIS_fnc_log: [myTag_fnc_myFunction] {{Color|green|42}}"
 
|-
 
|
 
[{{Color|green|"I'm playing %1"}}, [[missionName]]] [[call]] [[BIS_fnc_logFormat]];
 
|
 
"User1/BIS_fnc_log: [myTag_fnc_myFunction] {{Color|green|I'm playing FalconWing}}"
 
|}
 
 
 
  
To prevent RPT spam, logging is by default enabled only when previewing a mission from the editor. To force it in the mission everywhere, use the following [[Description.ext]] attribute:
+
* '''_fnc_scriptName''': [[String]] - Name of the script
<syntaxhighlight lang="cpp">
+
* '''_fnc_scriptNameParent''': [[String]] - Name of the parent script
allowFunctionsLog = 1;
+
* '''_fnc_scriptMap''': [[Array]] - List of all parent scripts
</syntaxhighlight>
 
  
=== Recompiling ===
+
=== Debug Modes ===
  
Once compiled, functions remain unchanged and editing their file won't have any effect in the game. To adjust functions on the fly, you can manually trigger their recompilation.
+
Developers can access several debug modes using ''[[BIS_fnc_functionsDebug]]'' function. '''For this to work [[Description.ext#allowFunctionsRecompile|function recompiling]] has to be allowed!'''
1 [[call]] [[BIS_fnc_recompile]];
 
:Recompiles all functions. Can be also achieved by clicking on RECOMPILE button in the Functions Viewer
 
"''functionName''" [[call]] [[BIS_fnc_recompile]];
 
:Recompile the given function
 
  
As a security measure, functions are by default protected against rewriting during the mission. <u>This restriction does not apply in missions previewed from the editor and in missions with the following attribute in [[Description.ext]]</u>:
+
==== Debug Mode 0 ====
<syntaxhighlight lang="cpp">
+
default - incorporates the [[#Meta Variables|Meta Variables]] _fnc_scriptParent and _fnc_scriptName and names the current scope.
allowFunctionsRecompile = 1;
+
[[private]] _fnc_scriptNameParent = [[if]] ([[isNil]] '_fnc_scriptName') [[then]] {'%1'} [[else]] {_fnc_scriptName};
</syntaxhighlight>
+
[[private]] _fnc_scriptName = '%1';
 +
[[scriptName]] _fnc_scriptName;
  
"Recompile" button in the functions viewer will be enabled only when recompiling is allowed.
+
==== Debug Mode 1 ====
 +
As per ''Debug Mode 0'' and also saves the current execution order in the [[Array]] _fnc_scriptMap.
 +
[[private]] _fnc_scriptNameParent = [[if]] ([[isNil]] '_fnc_scriptName') [[then]] { '%1' } [[else]] { _fnc_scriptName };
 +
[[private]] _fnc_scriptName = '%1';
 +
[[scriptName]] _fnc_scriptName;
 +
[[private]] _fnc_scriptMap = [[if]] ([[isNil]] '_fnc_scriptMap') [[then]] {[_fnc_scriptName]} [[else]] {_fnc_scriptMap + [_fnc_scriptName]};
  
=== Meta Variables ===
+
==== Debug Mode 2 ====
 +
As per ''Debug Mode 1'' and also outputs execution order using [[textLogFormat]] ( textLogFormat is not available in the retail version of Arma3 ) is it available in Diagnostics exe?
 +
[[private]] _fnc_scriptNameParent = [[if]] ([[isNil]] '_fnc_scriptName') [[then]] { '%1' } [[else]] { _fnc_scriptName };
 +
[[private]] _fnc_scriptName = '%1';
 +
[[scriptName]] _fnc_scriptName;
 +
[[private]] _fnc_scriptMap = [[if]] ([[isNil]] '_fnc_scriptMap') [[then]] { [_fnc_scriptName] } [[else]] { _fnc_scriptMap + [_fnc_scriptName] };
 +
[[textLogFormat]] ['%1 : %2', _fnc_scriptMap [[joinString]] ' >> ', _this];
  
System is adding header with basic meta data to all functions. Following local variables are declared there:
 
* '''_fnc_scriptName''': [[String]] - Function name (e.g., myTag_fnc_myFunction)
 
* '''_fnc_scriptNameParent''': [[String]] - Name of q function from which the current one was called (_fnc_scriptName used when not defined)
 
<!--* '''_fnc_scriptMap''': ARRAY - List of all parent scripts (available only in debug mode 1 and higher, see [[#Debug_Mode|above]]).-->
 
{{Important|Do not modify these values!}}
 
  
<!--
+
== See Also ==
=== Debug Mode ===
 
Developers can access several debug modes using ''[[BIS_fnc_functionsDebug]]'' function.
 
# '''No debug'''
 
#* Default
 
# '''Save script map'''
 
#* Variable ''_fnc_scriptMap'' tracking script execution progress is stored in function header
 
# '''Save and log script map'''
 
#* Variable ''_fnc_scriptMap'' tracking script execution progress is stored in functions header and it's printed to debug log.
 
{{Important|Function recompiling has to be allowed!}}
 
-->
 
  
 +
* [[Initialization Order]]
 +
* [[Arma 3: Functions Viewer]]
 +
* [[:Category:Arma 3: Functions|Arma 3 Functions]]
 +
* [[Arma 3: Writing a Function|Writing a Function Tutorial]]
  
 
{{GameCategory|arma3|Editing}}
 
{{GameCategory|arma3|Editing}}
 +
[[Category: Functions Library]]

Latest revision as of 23:41, 1 September 2021

Arma 3's Functions Library is the way to declare mission, campaign or addon's Functions. The main difference from older Function Libraries is that it runs automatically and does not require a Functions module.

The advantages of using the Functions Library over e.g execVM an SQF file are as follow:

  1. Automatic compilation upon mission start into a global variable - no need to remember direct paths to files.
  2. Anti-hack protection using compileFinal (see Recompiling)
  3. Listing in the Functions Viewer
  4. Advanced debugging options
  5. Optional immediate execution upon mission start, without need for manual call
  6. Potential performance improvements


This page is about Arma 3 Functions Library.


Function Declaration

Functions are configured within the CfgFunctions class.

Mission and campaign specific functions can be configured in Description.ext/Campaign Description.ext, while addon functions are defined in Config.cpp. Configuration structure is the same in both cases.


The root directory (noted as <ROOT> on this page) is the origin from which the game will try to load function files. It depends on CfgFunctions' location:


See a basic example config:

class CfgFunctions
{
	class TAG
	{
		class Category
		{
			class functionName {};
		};
	};
};
  • The function's name will be TAG_fnc_functionName
  • The function will be loaded:
    • from config: <ROOT>\Category\fn_functionName.sqf
    • from description.ext: <ROOT>\Functions\Category\fn_functionName.sqf
config.cpp only: Anywhere outside of running mission, refer to the functions stored in uiNamespace. e.g: arguments call (uiNamespace getVariable "TAG_fnc_functionName");

Config Levels

A CfgFunctions config is made of three levels: Tag, Category, and Function.

Tag

To prevent duplicates, every author must create a subclass with a unique tag and create functions under it. The tag name will be used when composing a function name (e.g BIS_fnc_spawnGroup).

class CfgFunctions
{
	class TAG
	{
		class Category
		{
			class myFunction {};
		};
	};

	class TAG_WeaponManagement
	{
		tag = "TAG"; // the function will be named TAG_fnc_myOtherFunction
		class Category
		{
			class myOtherFunction {};
		};
	};
};
Attributes
tag

The tag attribute can override the config tag set differently.

Category

The category name changes the function's category in the Functions Viewer. It does not change the function's name, only the loading path.

class CfgFunctions
{
	class TAG
	{
		class Category
		{
			class myFunction {};
		};

		class OtherCategory
		{
			file = "My\Category\Path";
			class myFunction {}; // file path will be <ROOT>\My\Category\Path\fn_myFunction.sqf";
		};

		class DataCategory
		{
			requiredAddons[] = { "A3_Data_F" }; // Optional requirements of CfgPatches classes. If some addons are missing, category functions will not be compiled.
			class myDataFunction {};
		};
	};
};
Attributes
file

The file attribute can override the category's loading path segment.

requiredAddons

The category can skip loading if a required addon is missing by setting its dependency with the requiredAddons attribute.

Function

By convention, the function class should be named in camelCase and not have its first letter capitalised (e.g PascalCase):
  • Ico ok.png TAG_fnc_myFunction
  • Ico none.png TAG_fnc_MyFunction
  • Ico ok.png TAG_fnc_POWRescue
class CfgFunctions
{
	class TAG
	{
		class Category1
		{
			class myFunction {};
		};

		class Category2
		{
			file = "Path\To\Category";
			class myFunction
			{
				file = "My\Function\Filepath.sqf"; // file path will be <ROOT>\My\Function\Filepath.sqf", ignoring "Path\To\Category"
			};

			class myFSMFunction
			{
				preInit		= 1;
				postInit	= 1;
				ext			= ".fsm";
				preStart	= 1;
				recompile	= 1;
			};
		};
	};
};
Attributes
Attribute Description Available in
Description.ext config.cpp
file the file attribute can be used to manually set the file path.
The file entry overrides Category's set path and ignores it entirely (they are not merged).
Ico ok.png Ico ok.png
preInit the preInit attribute (formerly known as "forced") can be set to 1 to call the function upon mission start, before objects are initialized.
Passed arguments are ["preInit"]. The function is run in an unscheduled environment.
Ico ok.png Ico ok.png
postInit the postInit attribute can be set to 1 to call the function upon mission start, after objects are initialized.
Passed arguments are ["postInit", didJIP]. The function is run in a scheduled environment so suspension is allowed, but any long term suspension will halt the mission loading until suspension is done.
Ico ok.png Ico ok.png
ext the ext attribute can be used to set function file's type; it can be one of:
  • ".sqf" (default)
  • ".fsm" (see FSM)
Ico ok.png Ico ok.png
preStart the preStart attribute can be set to 1 to call the function upon game start, before title screen, but after all addons are loaded. Ico none.png Ico ok.png
recompile the recompile attribute can be set to 1 to recompile the function upon mission start (functions in Description.ext are always compiled upon mission (re)start) Ico none.png Ico ok.png
headerType the headerType adds code to the beginning of functions that can:
  • Set up the Meta Variables _fnc_scriptParent and _fnc_scriptName
  • Name the current scope via the command scriptName
  • Adds debug information by saving the current execution order in the Array _fnc_scriptMap.

Possible values are:

  • -1: No header - Adds nothing, literally adds a blank String to the beginning of functions. The functions will have no debug or Meta Variables assigned for it and the scope will not be named. Useful to reduce the performance overhead or if custom debug system is used.
  • 0: Default header - The default header changes based on the current Debug Mode
  • 1: System header - Incorporates the Meta Variables _fnc_scriptParent. Also names the current scope.
private _fnc_scriptNameParent = if (isNil '_fnc_scriptName') then { '%1' } else { _fnc_scriptName };
scriptName '%1';

See the following section for more information.

Ico ok.png Ico ok.png
Notes about preInit and postInit defined in config.cpp:
  • these attributes will make the function run on each, every and any mission start
  • Any scripting error will prevent the mission from being loaded correctly
  • Server admins might blacklist the whole addon if they find out the function is used for hacking


Advanced Knowledge

Meta Variables

  • _fnc_scriptName: String - Name of the script
  • _fnc_scriptNameParent: String - Name of the parent script
  • _fnc_scriptMap: Array - List of all parent scripts

Debug Modes

Developers can access several debug modes using BIS_fnc_functionsDebug function. For this to work function recompiling has to be allowed!

Debug Mode 0

default - incorporates the Meta Variables _fnc_scriptParent and _fnc_scriptName and names the current scope.

private _fnc_scriptNameParent = if (isNil '_fnc_scriptName') then {'%1'} else {_fnc_scriptName};
private _fnc_scriptName = '%1';
scriptName _fnc_scriptName;

Debug Mode 1

As per Debug Mode 0 and also saves the current execution order in the Array _fnc_scriptMap.

private _fnc_scriptNameParent = if (isNil '_fnc_scriptName') then { '%1' } else { _fnc_scriptName };
private _fnc_scriptName = '%1';
scriptName _fnc_scriptName;
private _fnc_scriptMap = if (isNil '_fnc_scriptMap') then {[_fnc_scriptName]} else {_fnc_scriptMap + [_fnc_scriptName]};

Debug Mode 2

As per Debug Mode 1 and also outputs execution order using textLogFormat ( textLogFormat is not available in the retail version of Arma3 ) is it available in Diagnostics exe?

private _fnc_scriptNameParent = if (isNil '_fnc_scriptName') then { '%1' } else { _fnc_scriptName };
private _fnc_scriptName = '%1';
scriptName _fnc_scriptName;
private _fnc_scriptMap = if (isNil '_fnc_scriptMap') then { [_fnc_scriptName] } else { _fnc_scriptMap + [_fnc_scriptName] };
textLogFormat ['%1 : %2', _fnc_scriptMap joinString ' >> ', _this];


See Also