Jump to content
ribera1945e

[solved]PiP UAV camera script in Multiplayer

Recommended Posts

Hello. I am making a PiP script that runs on a dedicated server.
It works as intended in the preview, but not in multiplayer.
Below is the code:

 

Spoiler

/*-----------------------------------------------------------------------------------
Script: liveFeedUAV
Description:
	UAVのカメラフィードバックをモニターオブジェクトに行う。
Parameters:
	_monitor	- <OBJECT> フィードバック再生とaddActionを行うオブジェクト
Return:
	Nothing
Examples:
	(in your monitor init)
	nul = [this] execVM "scripts\liveFeedUAV1.sqf";
	(in init.sqf)
	[missionNamespace, "arsenalClosed", {
		[yourMonitor] execVM "scripts\liveFeedUAV1.sqf";
	}] call BIS_fnc_addScriptedEventHandler;
Author:
	ribera1945eBP
-----------------------------------------------------------------------------------*/
if (!isServer) exitWith {};

params ["_monitor"];

if (isNull _monitor) exitWith {};
sleep 1;

if (missionNamespace getVariable ["liveFeedUAV1done", false]) exitWith {
	// cam1 cameraEffect ["TERMINATE", "BACK"];
	// camDestroy cam1;
	
	cam1 = "camera" camCreate [0,0,0];
	cam1 cameraEffect ["Internal", "Back", "uavrtt1"];
	cam1 camSetFov 0.46599999;
	"uavrtt1" setPiPEffect [0];
	cam1 attachTo [MQ_1, [0,0,0], "PiP0_pos"];
};

_monitor setObjectTextureGlobal [0, "#(argb,512,512,1)r2t(uavrtt1,1)"];
cam1 = objNull;
missionNamespace setVariable ["cam1", cam1];

cam1 = "camera" camCreate [0,0,0];
cam1 cameraEffect ["Internal", "Back", "uavrtt1"];
cam1 camSetFov 0.46599999;
"uavrtt1" setPiPEffect [0];

cam1 attachTo [MQ_1, [0,0,0], "PiP0_pos"];

fnc_selectZoom = {
	private _sZoom = _this select 0;
	switch (_sZoom) do {
		case "ULTRA WIDE":
		{
			cam1 camSetFov 0.46599999;
		};
		case "WIDE":
		{
			cam1 camSetFov 0.2;
		};
		case "MEDIUM":
		{
			cam1 camSetFov 0.1;
		};
		case "NARROW":
		{
			cam1 camSetFov 0.02;
		};
		case "NARROWER":
		{
			cam1 camSetFov 0.01;
		};
	};
};

fnc_selectMode = {
	private _sMode = _this select 0;
	switch (_sMode) do {
		case "DTV":
		{
			["uavrtt1", [0]] remoteExec ["setPiPEffect", 0];
		};
		case "NV":
		{
			["uavrtt1", [1]] remoteExec ["setPiPEffect", 0];
		};
		case "WHOT":
		{
			["uavrtt1", [2]] remoteExec ["setPiPEffect", 0];
		};
		case "BHOT":
		{
			["uavrtt1", [7]] remoteExec ["setPiPEffect", 0];
		};
	};
};


{
	private _actionTitle = format ["Select Zoom - %1",_x];
	[_monitor, [
		_actionTitle,
		{
			private _Value = _this select 3 select 0;
			[_Value] call fnc_selectZoom;
		},
		[_x],3.3,true,false,"","",8
	]] remoteExec ["addAction", 0];
} forEach ["ULTRA WIDE", "WIDE", "MEDIUM", "NARROW", "NARROWER"];

{
	private _actionTitle = format ["Select Mode - %1",_x];
	[_monitor, [
		_actionTitle,
		{
			private _Value = _this select 3 select 0;
			[_Value] call fnc_selectMode;
		},
		[_x],3.3,true,false,"","",8
	]] remoteExec ["addAction", 0];
} forEach ["DTV", "NV", "WHOT", "BHOT"];

missionNamespace setVariable ["liveFeedUAV1done", true];

addMissionEventHandler ["Draw3D", {
    _dir = 
        (MQ_1 selectionPosition "laser_start") 
            vectorFromTo 
        (MQ_1 selectionPosition "laser_end");
    cam1 setVectorDirAndUp [
        _dir, 
        _dir vectorCrossProduct [-(_dir select 1), _dir select 0, 0]
    ];
}];

 

I have implemented zoom and PiPEffect change functions using addAction, but changes to the monitor are not synchronized in multiplayer.

 

Thanks.

 

Share this post


Link to post
Share on other sites

This should help you: https://community.bistudio.com/wiki/Multiplayer_Scripting. You have to keep in mind where your commands execute. To explain it on the first few lines of your script:

if (!isServer) exitWith {};

// [...]

if (missionNamespace getVariable ["liveFeedUAV1done", false]) exitWith {
	cam1 = "camera" camCreate [0,0,0];

This main script is only getting executed on the SERVER. the camCreate command has local effect, the camera is only available on the SERVER.

fnc_selectZoom = {

Your function is also only declared on the SERVER, the CLIENTs don't know about them. therefore...

cam1 camSetFov 0.46599999;

will not work as cam1 does not exist nor can you call the function from an addAction that was added via...

	[_monitor, [
		_actionTitle,
		{
			private _Value = _this select 3 select 0;
			[_Value] call fnc_selectZoom;
		},
		[_x],3.3,true,false,"","",8
	]] remoteExec ["addAction", 0];

from the SERVER on all CLIENTS and the SERVER (target: 0, aka everyone).

 

tl;dr: Muliplayer scripting is harder than SP

 

Also: missionNamespace setVariable ["cam1", cam1]; is redundant. No need for that line as cam1 = ... is equivalent to missionNamespace setVariable ["cam1", ...

  • Thanks 1

Share this post


Link to post
Share on other sites

@7erra

Thank you for your reply.
I wrote !IsServer at the beginning to prevent addAction from running on all machines.
I modified the code based on your point:

 

Spoiler

/*-----------------------------------------------------------------------------------
Script: liveFeedUAV
Description:
	UAVのカメラフィードバックをモニターオブジェクトに行う。
Parameters:
	_monitor	- <OBJECT> フィードバック再生とaddActionを行うオブジェクト
Return:
	Nothing
Examples:
	(in your monitor init)
	nul = [this] execVM "scripts\liveFeedUAV1.sqf";
	(in init.sqf)
	[missionNamespace, "arsenalClosed", {
		[yourMonitor] execVM "scripts\liveFeedUAV1.sqf";
	}] call BIS_fnc_addScriptedEventHandler;
	["ace_arsenal_displayClosed", {
		[monitor_1] execVM "scripts\liveFeedUAV1.sqf";
	}] call CBA_fnc_addEventHandler;
Author:
	ribera1945eBP
-----------------------------------------------------------------------------------*/


params ["_monitor"];

if (isNull _monitor) exitWith {};
sleep 1;

if (missionNamespace getVariable ["liveFeedUAV1done", false]) exitWith {
	// cam1 cameraEffect ["TERMINATE", "BACK"];
	// camDestroy cam1;
	
	cam1 = "camera" camCreate [0,0,0];
	cam1 cameraEffect ["Internal", "Back", "uavrtt1"];
	cam1 camSetFov 0.46599999;
	"uavrtt1" setPiPEffect [0];
	cam1 attachTo [MQ_1, [0,0,0], "PiP0_pos"];
};

_monitor setObjectTextureGlobal [0, "#(argb,512,512,1)r2t(uavrtt1,1)"];

cam1 = objNull;
missionNamespace setVariable ["cam1", cam1];

cam1 = "camera" camCreate [0,0,0];
cam1 cameraEffect ["Internal", "Back", "uavrtt1"];
cam1 camSetFov 0.46599999;
"uavrtt1" setPiPEffect [0];

cam1 attachTo [MQ_1, [0,0,0], "PiP0_pos"];

fnc_selectZoom = {
	private _sZoom = _this select 0;
	switch (_sZoom) do {
		case "ULTRA WIDE":
		{
			[missionNamespace getVariable "cam1", 0.46599999] remoteExec ["camSetFov", 0];
		};
		case "WIDE":
		{
			[missionNamespace getVariable "cam1", 0.2] remoteExec ["camSetFov", 0];
		};
		case "MEDIUM":
		{
			[missionNamespace getVariable "cam1", 0.1] remoteExec ["camSetFov", 0];
		};
		case "NARROW":
		{
			[missionNamespace getVariable "cam1", 0.02] remoteExec ["camSetFov", 0];
		};
		case "NARROWER":
		{
			[missionNamespace getVariable "cam1", 0.01] remoteExec ["camSetFov", 0];
		};
	};
};

fnc_selectMode = {
	private _sMode = _this select 0;
	switch (_sMode) do {
		case "DTV":
		{
			["uavrtt1", [0]] remoteExec ["setPiPEffect", 0];
		};
		case "NV":
		{
			["uavrtt1", [1]] remoteExec ["setPiPEffect", 0];
		};
		case "WHOT":
		{
			["uavrtt1", [2]] remoteExec ["setPiPEffect", 0];
		};
		case "BHOT":
		{
			["uavrtt1", [7]] remoteExec ["setPiPEffect", 0];
		};
	};
};


{
	private _actionTitle = format ["Select Zoom - %1",_x];
	[_monitor, [
		_actionTitle,
		{
			private _Value = _this select 3 select 0;
			[_Value] call fnc_selectZoom;
		},
		[_x],3.3,true,false,"","",8
	]] remoteExec ["addAction", 2];
} forEach ["ULTRA WIDE", "WIDE", "MEDIUM", "NARROW", "NARROWER"];

{
	private _actionTitle = format ["Select Mode - %1",_x];
	[_monitor, [
		_actionTitle,
		{
			private _Value = _this select 3 select 0;
			[_Value] call fnc_selectMode;
		},
		[_x],3.3,true,false,"","",8
	]] remoteExec ["addAction", 2];
} forEach ["DTV", "NV", "WHOT", "BHOT"];

missionNamespace setVariable ["liveFeedUAV1done", true];

addMissionEventHandler ["Draw3D", {
    _dir = 
        (MQ_1 selectionPosition "laser_start") 
            vectorFromTo 
        (MQ_1 selectionPosition "laser_end");
    cam1 setVectorDirAndUp [
        _dir, 
        _dir vectorCrossProduct [-(_dir select 1), _dir select 0, 0]
    ];
}];

 

But fnc_selectZoom stopped working in preview.
I don't get an error, but camSetFov doesn't seem to recognize cam1.
What should I do?

Share this post


Link to post
Share on other sites

Your problem, IMHO, is that you want to synchronize UAV displays for all players: they must have the same picture, no matter who set the zoom and mode.

For evident reason of broadcast, the displays are usually local. The best way to display the same thing on all PCs, even for JIP, is to run all possible things locally, then broadcast only little variables (like your _sMode or _sZoom) making them public instead of local.

In such cases, use global sMode and publicVariable "sMode"; sZoom and publicVariable "sZoom"...  and, as you are running a code on each PC,  stop using setObjectTextureGlobal, prefer setObjectTexture. No need to broadcast such global command (it's even counter-productive: each JIP reset the texture for everyone!).

  • Like 1

Share this post


Link to post
Share on other sites

That's exactly what I'm looking for. Everyone should have the same picture regardless of who set it.
fnc_selectZoom is currently only run by the server, does that mean cam1 doesn't match that of the local machines?
Also, does cam1 need to be private?

 

Thanks.

Share this post


Link to post
Share on other sites

wow! some basics:

- local machine: player's PC (or server) but local variables are variables defined in a scope (so locally on PC and locally on (part of) script.

- global can refer to all PCs (like commands ending by global as setTextureGlobal) but global variables (like your cam1) means "known by all scripts of the scenario on the PC(s)". If multiple PCs, each global variable, like cam1 has its own "life", i.e. there is no broadcast for value

- public is a term for variables (roughly) and that's a way for making a variable "common". There are 2 ways:

  * using setVariable command with the 3rd parameter set tot true (see BIKI)

  * using publicVariable command EACH TIME the variable changes and must be updated for everyone.

 

For script/code, we are speaking about remote execution or not (remoteExec command). Usually, it's easy to remote exec a command if needed. That depends on:

- its argument(s). They can be local, global, server only... The need depends on the locality they apply. An example: allowDamage is a AL EG command. That means: must be applied where the unit is local, effects are global, so for everyone. As you can test, the (poor) option for disabling damage in editor worth in SP , or in MP on players. Just because such units, once played, change their locality (played unit is local to a client) , so your invincible unit will be killed. Such AL EG command "dislike" the shift of unit's owner.

- on the other hand, a EL command like camSetFOV (AL EL) will give effects only locally. (Not a reason for remoteExecuting it, just think about same settings produce same effects)

- there are other possibilities for commands: AG EG.. AL EG SE...  Always refer to BIKI. For example it's a bad idea remoteExecuting setObjectTextureGlobal, already shared.Read BIKI links for details.

- Last but not least, do not remote execute sqf or function unless that meets every argument and effect requirements (usually not the case).

 

So, I suggest you to script yours codes in initPLayerLocal.sqf (means for each player)

when a player sets something via addAction (or else). Make public some variables, so these values change for all players. As your MEH "draw3D" is a kind of loop, if you refer to these variables, your display can be updated (locally but everywhere).

 

 

 

 

  • Like 2

Share this post


Link to post
Share on other sites

Thank you for the detailed lecture.
I thought that everything would work if remoteExec was done. Please forgive the script rookie.
Now I'm running camSetFov and setPiPEffect in Draw3D. This works as intended in multiplayer.

Spoiler

/*-----------------------------------------------------------------------------------
Script: liveFeedUAV
Description:
	UAVのカメラフィードバックをモニターオブジェクトに行う。
Parameters:
	_monitor	- <OBJECT> フィードバック再生とaddActionを行うオブジェクト
Return:
	Nothing
Examples:
	(in initPlayerLocal.sqf)
	nul = [yourMonitor] execVM "scripts\liveFeedUAV1.sqf";
	(in init.sqf)
	[missionNamespace, "arsenalClosed", {
		[yourMonitor] execVM "scripts\liveFeedUAV1.sqf";
	}] call BIS_fnc_addScriptedEventHandler;
	["ace_arsenal_displayClosed", {
		[yourMonitor] execVM "scripts\liveFeedUAV1.sqf";
	}] call CBA_fnc_addEventHandler;
Author:
	ribera1945eBP
-----------------------------------------------------------------------------------*/


params ["_monitor"];

if (isNull _monitor) exitWith {};
sleep 1;

if (localNamespace getVariable ["liveFeedUAV1done", false]) exitWith {
	_cam1 = (localNamespace getVariable "cam1");
	_cam1 cameraEffect ["Internal", "Back", "uavrtt1"];
};

if (missionNamespace getVariable ["liveFeedUAV1doneNoJIP", true]) then {
	missionNamespace setVariable ["zoomLevel", 0.46599999];
	missionNamespace setVariable ["camMode", [0]];
};

_monitor setObjectTexture [0, "#(argb,512,512,1)r2t(uavrtt1,1)"];

_cam1 = "camera" camCreate [0,0,0];
_cam1 cameraEffect ["Internal", "Back", "uavrtt1"];
_cam1 attachTo [MQ_1, [0,0,0], "PiP0_pos"];

localNamespace setVariable ["cam1", _cam1];
liveFeedUAV1cams = [];

fnc_selectZoom = {
	private _sZoom = _this select 0;
	private _cam1 = _this select 1;
	switch (_sZoom) do {
		case "ULTRA WIDE":
		{
			missionNamespace setVariable ["zoomLevel", 0.46599999, true];
		};
		case "WIDE":
		{
			missionNamespace setVariable ["zoomLevel", 0.2, true];
		};
		case "MEDIUM":
		{
			missionNamespace setVariable ["zoomLevel", 0.1, true];
		};
		case "NARROW":
		{
			missionNamespace setVariable ["zoomLevel", 0.02, true];
		};
		case "NARROWER":
		{
			missionNamespace setVariable ["zoomLevel", 0.01, true];
		};
	};
};

fnc_selectMode = {
	private _sMode = _this select 0;
	switch (_sMode) do {
		case "DTV":
		{
			missionNamespace setVariable ["camMode", [0], true];
		};
		case "NV":
		{
			missionNamespace setVariable ["camMode", [1], true];
		};
		case "WHOT":
		{
			missionNamespace setVariable ["camMode", [2], true];
		};
		case "BHOT":
		{
			missionNamespace setVariable ["camMode", [7], true];
		};
	};
};


{
	private _actionTitle = format ["Select Zoom - %1",_x];
	_monitor addAction [
		_actionTitle,
		{
			private _Value = _this select 3 select 0;
			[_Value, (localNamespace getVariable "cam1")] call fnc_selectZoom;
		},
		[_x],3.3,true,false,"","",8
	];
} forEach ["ULTRA WIDE", "WIDE", "MEDIUM", "NARROW", "NARROWER"];

{
	private _actionTitle = format ["Select Mode - %1",_x];
	_monitor addAction [
		_actionTitle,
		{
			private _Value = _this select 3 select 0;
			[_Value] call fnc_selectMode;
		},
		[_x],3.3,true,false,"","",8
	];
} forEach ["DTV", "NV", "WHOT", "BHOT"];

localNamespace setVariable ["liveFeedUAV1done", true];
missionNamespace setVariable ["liveFeedUAV1doneNoJIP", false];

0 = liveFeedUAV1cams pushBack [localNamespace getVariable "cam1"];

addMissionEventHandler ["Draw3D", {
	{
		_cam1 = _x select 0;
		_dir = 
			(MQ_1 selectionPosition "laser_start") 
				vectorFromTo 
			(MQ_1 selectionPosition "laser_end");
		_cam1 setVectorDirAndUp [
			_dir, 
			_dir vectorCrossProduct [-(_dir select 1), _dir select 0, 0]
		];
		_cam1 camSetFov (missionNamespace getVariable ["zoomLevel", 0.46599999]);
		"uavrtt1" setPiPEffect (missionNamespace getVariable ["camMode", [0]]);
	} count liveFeedUAV1cams;
}];

 


However, JIP still seems to be loading the initial values of zoomLevel and camMode.
I want to borrow your wisdom once again.

 

Thanks.

Share this post


Link to post
Share on other sites

The initial value(s) can be on server (initServer.sqf) and publicVariable for clients. This way, the initial value(s) are not reset by JIP (they don't run initServer.sqf)

On the other hand, when a player changes for other values (addAction or menu), these new values must be updated by publicVariable command also. In this case, the values are updated everywhere, so for JIP also.

missionNameSpace setVariable ["yourVar", yourValue ,TRUE]  is also a good way for broadcasting yourVar variable everywhere (so make it public and JIP compatible)

  • Thanks 1

Share this post


Link to post
Share on other sites

Apparently I was writing unnecessary processing.
If you change the processing of the initial value, JIP will also be synchronized.
I've finally got the code to work as intended in JIP-enabled multiplayer, so I'll share it:

/*-----------------------------------------------------------------------------------
Script: liveFeedUAV
Description:
	UAVのカメラフィードバックをモニターオブジェクトに行う。
Parameters:
	_monitor	- <OBJECT> フィードバック再生とaddActionを行うオブジェクト
Return:
	Nothing
Examples:
	(in initPlayerLocal.sqf)
	nul = [yourMonitor] execVM "scripts\liveFeedUAV1.sqf";
	(in init.sqf)
	[missionNamespace, "arsenalClosed", {
		[yourMonitor] execVM "scripts\liveFeedUAV1.sqf";
	}] call BIS_fnc_addScriptedEventHandler;
	["ace_arsenal_displayClosed", {
		[yourMonitor] execVM "scripts\liveFeedUAV1.sqf";
	}] call CBA_fnc_addEventHandler;
Author:
	ribera1945eBP
-----------------------------------------------------------------------------------*/


params ["_monitor"];

if (isNull _monitor) exitWith {};
sleep 1;

if (localNamespace getVariable ["liveFeedUAV1done", false]) exitWith {
	_cam1 = (localNamespace getVariable "cam1");
	_cam1 cameraEffect ["Internal", "Back", "uavrtt1"];
};

_monitor setObjectTexture [0, "#(argb,512,512,1)r2t(uavrtt1,1)"];

_cam1 = "camera" camCreate [0,0,0];
_cam1 cameraEffect ["Internal", "Back", "uavrtt1"];
_cam1 attachTo [MQ_1, [0,0,0], "PiP0_pos"];

localNamespace setVariable ["cam1", _cam1];
liveFeedUAV1cams = [];

fnc_selectZoom = {
	private _sZoom = _this select 0;
	private _cam1 = _this select 1;
	switch (_sZoom) do {
		case "ULTRA WIDE":
		{
			missionNamespace setVariable ["zoomLevel", 0.46599999, true];
		};
		case "WIDE":
		{
			missionNamespace setVariable ["zoomLevel", 0.2, true];
		};
		case "MEDIUM":
		{
			missionNamespace setVariable ["zoomLevel", 0.1, true];
		};
		case "NARROW":
		{
			missionNamespace setVariable ["zoomLevel", 0.02, true];
		};
		case "NARROWER":
		{
			missionNamespace setVariable ["zoomLevel", 0.01, true];
		};
	};
};

fnc_selectMode = {
	private _sMode = _this select 0;
	switch (_sMode) do {
		case "DTV":
		{
			missionNamespace setVariable ["camMode", [0], true];
		};
		case "NV":
		{
			missionNamespace setVariable ["camMode", [1], true];
		};
		case "WHOT":
		{
			missionNamespace setVariable ["camMode", [2], true];
		};
		case "BHOT":
		{
			missionNamespace setVariable ["camMode", [7], true];
		};
	};
};


{
	private _actionTitle = format ["Select Zoom - %1",_x];
	_monitor addAction [
		_actionTitle,
		{
			private _Value = _this select 3 select 0;
			[_Value, (localNamespace getVariable "cam1")] call fnc_selectZoom;
		},
		[_x],3.3,true,false,"","",8
	];
} forEach ["ULTRA WIDE", "WIDE", "MEDIUM", "NARROW", "NARROWER"];

{
	private _actionTitle = format ["Select Mode - %1",_x];
	_monitor addAction [
		_actionTitle,
		{
			private _Value = _this select 3 select 0;
			[_Value] call fnc_selectMode;
		},
		[_x],3.3,true,false,"","",8
	];
} forEach ["DTV", "NV", "WHOT", "BHOT"];

localNamespace setVariable ["liveFeedUAV1done", true];

0 = liveFeedUAV1cams pushBack [localNamespace getVariable "cam1"];

addMissionEventHandler ["Draw3D", {
	{
		_cam1 = _x select 0;
		_dir = 
			(MQ_1 selectionPosition "laser_start") 
				vectorFromTo 
			(MQ_1 selectionPosition "laser_end");
		_cam1 setVectorDirAndUp [
			_dir, 
			_dir vectorCrossProduct [-(_dir select 1), _dir select 0, 0]
		];
		_cam1 camSetFov (missionNamespace getVariable ["zoomLevel", 0.46599999]);
		"uavrtt1" setPiPEffect (missionNamespace getVariable ["camMode", [0]]);
	} count liveFeedUAV1cams;
}];

There is still a possibility of optimization, but this is the end for the time being.

@7erra

@pierremgi

Thanks for teaching me a lot!

  • Like 4

Share this post


Link to post
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now

×