KC Grimes 79 Posted May 9, 2014 Came across a little conflict and am curious what y'all make of it, and if anyone has any definite answers. So, considering BIS_fnc_MP. My question is, what is the difference between the following lines? [["Hello World"],"BIS_fnc_guiMessage",true,true,true] spawn BIS_fnc_MP; [["Hello World"],"BIS_fnc_guiMessage",true,true] call BIS_fnc_MP; Naturally, I am confused by the isCall parameter being used in conjunction with the spawn or call command. Does anyone know how this is working exactly? Share this post Link to post Share on other sites
das attorney 858 Posted May 9, 2014 Not sure mate. I used to think that you called when you wanted to run called code and used spawn if you wanted to spawn a new thread on the target machine(s), but I realised I was wrong when I saw there was a parameter to do that anyway. Now I have no idea to be honest! Share this post Link to post Share on other sites
KC Grimes 79 Posted May 9, 2014 Not sure mate. I used to think that you called when you wanted to run called code and used spawn if you wanted to spawn a new thread on the target machine(s), but I realised I was wrong when I saw there was a parameter to do that anyway.Now I have no idea to be honest! Exactly what I thought, and was about to try, but then noticed the optional param. Guess I'll setup a little "test chamber" and do some timing, and figure out what combinations do what. Share this post Link to post Share on other sites
brians200 51 Posted May 9, 2014 That fifth parameter is relativelynew to the wiki. (March 31st) Is there anything in the dev branch logs indicating a change? Share this post Link to post Share on other sites
KC Grimes 79 Posted May 9, 2014 (edited) So I did some quick testing. The following code in init.sqf: waitUntil {player == player}; G_fnc_Test = { _test = _this select 0; sleep 3; player sideChat format["%1 for %2",time,_test]; }; Testvar = false; [] spawn { player sideChat "Ready 1"; _test = "1"; waitUntil {Testvar}; [[_test], "G_fnc_Test", true,true,true] call BIS_fnc_MP; player sideChat "Ready 1"; }; [] spawn { player sideChat "Ready 2"; _test = "2"; waitUntil {Testvar}; [[_test], "G_fnc_Test", true,true] spawn BIS_fnc_MP; player sideChat "Ready 2"; }; [] spawn { player sideChat "Ready 3"; _test = "3"; waitUntil {Testvar}; [[_test], "G_fnc_Test", true,true] call BIS_fnc_MP; player sideChat "Ready 3"; }; [] spawn { player sideChat "Ready 4"; _test = "4"; waitUntil {Testvar}; [[_test], "G_fnc_Test", true,true,true] spawn BIS_fnc_MP; player sideChat "Ready 4"; }; Code to be executed via debug console: testvar = true; Results: On init, as expected, the initial Readys came up 1-4. This is due to chronological order. Upon defining testvar as true, all sessions fired immediately, and in order of 1-4. My guess is that this is due to the millisecond differences in the cycle start time for the waitUntils. Here's the interesting part. Upon the sleep being finished, the results were: 14.244 for 1 14.268 for 3 14.268 for 4 14.291 for 2 The number itself should be zeroed, as it is relative to when I hit testvar = true. What matters is the difference and range. After multiple tests, the order was always 1 3 4 2, with 3 and 4 being very close, and 2 always being a great distance apart, even as far as 100 ms. Now, unfortunately none of that really solves our question of what actually "calls" and what actually "spawns". Can't use scriptDone because the handle for BIS_fnc_MP is the packet, not a return variable (unfortunately). Personally, I believe that no matter what setting is used of the 4, it will always spawn instead of call. I say this because a "call" would be indicated by a time difference of about 3 seconds, indicating the script was waiting for the called code to complete. Edited May 9, 2014 by Grimes [3rd ID] Share this post Link to post Share on other sites
brians200 51 Posted May 9, 2014 (edited) You could follow the code and see where it is going after the publicVariableServer in BIS_fnc_MP ... Which finally ends up in BIS_fnc_MPexec. See the if statement towards the bottom. /* Author: Karel Moricky Description: Execute received remote execution Parameter(s): _this select 0: STRING - Packet variable name (always "BIS_fnc_MP_packet") _this select 1: ARRAY - Packet value (sent by BIS_fnc_MP function; see it's description for more details) Returns: BOOL - true if function was executed successfuly */ private ["_params","_functionName","_target","_isPersistent","_isCall","_varName","_varValue","_function"]; _varName = _this select 0; _varValue = _this select 1; _mode = [_varValue,0,[0]] call bis_fnc_param; _params = [_varValue,1,[]] call bis_fnc_param; _functionName = [_varValue,2,"",[""]] call bis_fnc_param; _target = [_varValue,3,true,[objnull,true,0,[],sideUnknown,grpnull,""]] call bis_fnc_param; _isPersistent = [_varValue,4,false,[false]] call bis_fnc_param; _isCall = [_varValue,5,false,[false]] call bis_fnc_param; if (typename _target == typename []) then { //--- Multi execution { [_varName,[_mode,_params,_functionName,_x,_isPersistent,_isCall]] call bis_fnc_MPexec; } foreach _target; } else{ //--- Single execution if (ismultiplayer && _mode == 0) then { private ["_ownerID","_serverID"]; _serverID = owner (missionnamespace getvariable ["bis_functions_mainscope",objnull]); //--- Server ID is not always 0 //--- Server process switch (typename _target) do { case (typename ""): { _ownerID = owner (missionnamespace getvariable [_target,objnull]); }; case (typename objnull): { private ["_targetCuratorUnit"]; _targetCuratorUnit = getassignedcuratorunit _target; if !(isnull _targetCuratorUnit) then {_target = _targetCuratorUnit;}; _ownerID = owner _target; }; case (typename true): { _ownerID = [_serverID,-1] select _target; }; case (typename 0): { _ownerID = _target; }; case (typename grpnull); case (typename sideUnknown): { _ownerID = -1; }; }; BIS_fnc_MP_packet = [1,_params,_functionName,_target,_isPersistent,_isCall]; //--- Send to clients if (_ownerID < 0) then { //--- Everyone publicvariable "BIS_fnc_MP_packet"; } else { if (_ownerID != _serverID) then { //--- Client _ownerID publicvariableclient "BIS_fnc_MP_packet"; }; }; //--- Server execution (for all or server only) if (_ownerID == -1 || _ownerID == _serverID) then { ["BIS_fnc_MP_packet",BIS_fnc_MP_packet] spawn BIS_fnc_MPexec; }; //--- Persistent call (for all or clients) if (_isPersistent) then { if (typename _target != typename 0) then { private ["_logic","_queue"]; _logic = missionnamespace getvariable ["bis_functions_mainscope",objnull]; _queue = _logic getvariable ["BIS_fnc_MP_queue",[]]; _queue set [ count _queue, +BIS_fnc_MP_packet ]; _logic setvariable ["BIS_fnc_MP_queue",_queue,true]; } else { ["Persistent execution is not allowed when target is %1. Use %2 or %3 instead.",typename 0,typename objnull,typename false] call bis_fnc_error; }; }; } else { //--- Local execution private ["_canExecute"]; _canExecute = switch (typename _target) do { case (typename grpnull): {player in units _target}; case (typename sideUnknown): {(player call bis_fnc_objectside) == _target}; default {true}; }; if (_canExecute) then { _function = missionnamespace getvariable _functionName; if (!isnil "_function") then { if (_isCall) then { _params call _function; } else { _params spawn _function; }; true } else { ["Function '%1' does not exist",_functionName] call bis_fnc_error; false }; }; }; }; Edited May 9, 2014 by brians200 Share this post Link to post Share on other sites
KC Grimes 79 Posted May 9, 2014 Thanks for looking at that. Didn't bother to look because I feared it just would be a bit much, but a quick search seemed to point out how it worked. It's basically a function within a function, all the mini-functions aside. The executing client can choose to spawn or call the function BIS_fnc_MP, but the difference there is essentially no difference because within it the function that was included in the parameter (now the function within a function) is then being called/spawned into parallel. In looking at it all again, I think I just answered my question with the above. Because the function BIS_fnc_MP doesn't wait for the functions it is executing to be completing, it executes them and ends. That is why the 2nd set of Ready lines are not being delayed, but instead appear soon after hitting testvar = true. So, my conclusion: The non-param call/spawn (the command for BIS_fnc_MP) is as functional as usual. If you have a LOT of clients, you'll notice a delay when using call. Otherwise, the time delay between call and spawn is negligible. The only handle you can return on a line for BIS_fnc_MP is the sent packet, which is... useless. It's just the params of the BIS_fnc_MP line, with a 0 in the beginning. Nothing that can be used. If you ask me, in order to best control your functions and their return values you'll need to call a function in a function in a function. Basically, make a function that is only an execVM line with a handle, and call/spawn it via BIS_fnc_MP. That execVM/call line will then execute another function, which will be controllable because you had a _handle = at the beginning to receive the return. Because all values will be different in some cases, for instance, when adding an addAction when JIPers are involved, I suggest either managing the ID on the client side or adding it to a PV'd array that is then managed by the server as needed. Anybody please feel free to provide further feedback about the original question, as a lot of my reasoning is somewhat speculation. Share this post Link to post Share on other sites