So you have a piece of code you want to execute on all computers for whatever reason. As a mission/mod maker you should be in charge of what is and is not allowed in your creation. One thing you definitely don’t want is to allow passing of a code over network and then letting all client machines to execute this code. Mainly because this is an invitation to all sorts of hacks and exploits and also because transferring chunks of code over network is pretty bad for bandwidth. What you really want to do is to create a custom function on each client, lock it and then be able to call it from anywhere with a set of parameters rather than passing over a bare code.

Creating function on each client is easy. When mission loads, init.sqf loads and every machine will run it once, including dedicated server. So defining function in init.sqf or any file that is called from it will do the trick. Calling it from anywhere is also easy, you just have to use a combination of publicVariable and respectful event handler. It all boils down to setting up a callback framework, i.e. the main function that will do all the calls on clients for us.

We will pass a custom function name as a string to our callback function. The string then needs to be somehow converted to actual variable holding custom function code. But whatever you do, do not use call compile format for this. I will show you why. Lets say we have constructed our bad callback framework function and defined it on every PC.

our_badfunction = { private ["_fnc","_params"]; _fnc = _this select 0; _params = _this select 1; call compile format ["%2 call fnc_%1", _fnc, _params]; };

We even attempted to make it secure by prefixing called name, so that no other functions may be called but those that start with “fnc_”. Now lets define our custom function we want to call on every machine.

fnc_lolfunc = { private "_params"; _params = _this select 0; hint str _params; };

To make it all work we need an event handler that will call our bad callback  framework, which in return will call our custom function.

our_publicvariable = []; "our_publicvariable" addPublicVariableEventHandler { (_this select 1) call our_badfunction; };

And to make event handler fire in the first place we need to define and broadcast our public variable from another machine.

our_publicvariable = ["lolfunc", ["LMAO!"]]; publicVariable "our_publicvariable";

So after initial call compile format in our bad function we will end up with:

call {["LMAO!"] call fnc_lolfunc};

So it looks like everything works.  Not bad, until a hacker comes and broadcasts own public variable like this:

our_publicvariable = ["lolfunc;...malicious code...", ["LMAO!"]]; publicVariable "our_publicvariable";

Now call compile format result will look like this:

call {["LMAO!"] call fnc_lolfunc;...malicious code...};

As you can see, our bad function will first finish the intended call, then will happily carry own executing malicious code injected by the hacker. So first thing is first, we never use call compile format combo, instead we use namespace.

_params call missionNamespace getVariable [format ["%1", _fnc], fallback_function];

You simply cannot inject code into this construction. Well, actually you can but it is no good since nothing gets compiled, so it is useless. On top we can define a fallback function that will be executed in case the passed function name is a non-existent variable, alerting us if someone is trying anything dodgy. We also have to make sure we cannot call our framework function from our framework function, creating endless loop. After all these considerations, this is what we have:

KK_fnc_CB_ERROR = { diag_log (format [ "CB_ERROR! Call to non-existent function '%1' (Passed params: %2)", _this select 0, _this ]) }; KK_fnc_CB = { private ["_fnc","_code"]; _fnc = _this select 0; if (_fnc != "KK_fnc_CB") then { _code = missionNamespace getVariable [ format ["%1", _fnc], KK_fnc_CB_ERROR ]; if ((_this select 2) == "call") then { _this call _code; } else { null = _this spawn _code; } } };

As before we need custom function and event handler.

fnc_lolfunc = { hint (_this select 1); }; KK_pv_CB = []; "KK_pv_CB" addPublicVariableEventHandler { (_this select 1) call KK_fnc_CB; };

And to send the call over network:

KK_pv_CB = ["fnc_lolfunc", "LMAO!", "spawn"]; publicVariable "KK_pv_CB";

Just remember, public variable event handler will not be triggered on the machine you call publicVariable from, so you will need to call your custom function directly on that PC.

After it all has been tested and working as intended it is time to lock the code, so that no one can overwrite our callback framework. We are going to use compileFinal command for it (is ArmA 3 feature for now). It simply compiles passed string into a code and makes it final. It will not be possible to alter the variable containing code after this. We can either convert our functions to strings by hand or use converter I’ve made. The final result will look like this:

KK_fnc_CB_ERROR = compileFinal " diag_log (format [ ""CB_ERROR! Call to non-existent function '%1' (Passed params: %2)"", _this select 0, _this ]) "; KK_fnc_CB = compileFinal " private [""_fnc"",""_code""]; _fnc = _this select 0; if (_fnc != ""KK_fnc_CB"") then { _code = missionNamespace getVariable [ format [""%1"", _fnc], KK_fnc_CB_ERROR ]; if ((_this select 2) == ""call"") then { _this call _code; } else { null = _this spawn _code; } } ";

Would be a good idea to stop our custom function from unauthorised overwrites too.

fnc_lolfunc = compileFinal " hint (_this select 1); ";

And now the Bonus Ball!

Pages: 1 2