First of all, originally this was Karel Moricky‘s idea so all credit goes to him. You might have noticed on the very recent DEV branch update, you could remote execute scripting commands, currently WIP. So I took my own stab at it to satisfy my curiousity. Basically the main idea is that some scripting commands are limited in locality. You must have heard “this command is global”, “this command is local” etc. I have touched on this in previous posts, but let’s refresh the memory.

Some commands only have effect if executed where the unit you are preforming this command on is local to the computer that executes this command. For example, addPrimaryWeaponItem command to add accessories to your rifle, will only work when executed on your PC. So if I want to do this from the server directly, I cannot. For this I need to set up a function on my PC that will add weapon item on my PC then trigger this function from the server.

Some commands have local effect. For example switchMove will change the animation of target unit but only where it was executed. If you execute it on PC that owns the unit, the change will sync over network, but not immediately. So for immediate change and global effect you need to have this executed on every PC. Again, this means having functions on every PC that will be triggered all at once.

So as you can see on one hand there is direct benefit of being able to execute commands globally. On the other hand if you allow all commands execute globally you are opening a can of worms and this could be exploited by script kiddies. The solution is to be able to regulate what commands can and what cannot be executed globally. You might only need a handful commands for your mission, why have those you don’t need?

I did this via mission config file, you can define commands and their behaviour in class Commands {}. Each command contains templates and input is verified against templates twice, locally and again remotely. If verification fails, exec function returns false, true otherwise. This is how commands config looks for example:

class Commands { class addPrimaryWeaponItem { type = 0; templates[] = {{"OBJECT","STRING","STRING"}}; }; class hint { type = 1; templates[] = {{"NULL","STRING","STRING"},{"NULL","STRING","TEXT"}}; }; };

So to add accessory to remote unit:

remoteUnit addPrimaryWeaponItem "muzzle_snds_H";

will not work. Using the ExG function:

[remoteUnit, "addPrimaryWeaponItem", "muzzle_snds_H"] call KK_fnc_ExG;

will do the trick. As you can see function arguments follow command syntax and template follows arguments order: OBJECT, STRING, STRING. Another example. Hint is local command and:

hint "Show to everyone!";

will obviously show nothing to other players, but:

[nil, "hint", "Show to everyone!"] call KK_fnc_ExG;

will show the hint globally.

There are 3 types of command execution. Type 0 will execute command where the argument is local, this would normally send request to the server to find owner of the argument object and then send request to owner’s client to execute command. Type 1 will trigger execution globally, this is mainly for commands that have local effect. You don’t want to use this with global effect commands as each one of them will broadcast the change making an avalanche of network packets. Type 2 is for custom execution, in case you want to limit execution to 1 client. You can make your own commands and define their behaviour. Look up hintTarget in commands.hpp config for example.

You can examine the whole code, which is surprisingly quite compact, in the download package. The content is this:

  • commands.hpp – commands config
  • defines.hqf – defines the name of the public variable used as transport
  • fn_ExG.sqf – main function code
  • fn_setPVHandler.sqf – public variable event handler

Installation

All files should be put in:

  • MPMissions \<yourmisionname>\Functions\ExG

folder. In description.ext add the following:

#include "Functions\ExG\commands.hpp" class CfgFunctions { class KK { class ExG { class ExG {}; class setPVHandler {preInit = 1;}; }; }; };

or if you have CfgFunctions defined already, just merge it in.

One thing I want to share here is procedure from the main code I turned in a separate function. This function takes an array of values and turns it into array of data types. For example [1,”2″,[true]] will result in [“SCALAR”,”STRING”,[“BOOL”]]. This is how I implemented check against command templates:

KK_fnc_typesNamesArray = { private "_fnc"; _this =+ _this; _fnc = { { if (isNil "_x") then { _this set [_forEachIndex, "NULL"]; } else { if (typeName _x == "ARRAY") then { _x call _fnc; } else { _this set [_forEachIndex, typeName _x]; }; }; } forEach _this; }; _this call _fnc; _this }; [1,"2",{3},[objNull,scriptNull,nil]] call KK_fnc_typesNamesArray; //["SCALAR","STRING","CODE",["OBJECT","SCRIPT","NULL"]]

Enjoy,
KK