Jump to content
jordanbache97

Script MP/JIP Compatibility

Recommended Posts

waitUntil {!isNull player};      

 

_unit = _this select 0;

removeallweapons _unit;

removeallassigneditems _unit;

removeallcontainers _unit;

removeheadgear _unit;

 

_unit adduniform "UK3CB_BAF_U_CombatUniform_MTP";

_unit addmagazines ["UK3CB_BAF_30Rnd", 2];    

_unit addweapon "UK3CB_BAF_L85A2"; 

_unit addPrimaryWeaponItem "UK3CB_BAF_SUSAT"; 

 

_Unit linkitem "ItemMap";

_Unit linkitem "ItemCompass";

_Unit linkitem "ItemWatch";

_Unit linkitem "tf_anprc152_2";

 

if(true) exitWith{}; 

 

Above I have been trying to use this script for when My unit loads into a mission they will just have clothing and a basic rifle. But when someone joins all the gear they pick up dissapears and goes straight back to this script.

I thought having  "waitUntil {!isNull player};" at the start stops this and makes it JIP compatible and I was also told I wouldnt need to bother with adding Global to the end of each line is that correct?

 

Thanks in advance,

 

Jordan   

Share this post


Link to post
Share on other sites

Execute the script in the initPlayerLocal.sqf instead of the init.sqf or the init field of each unit..

Share this post


Link to post
Share on other sites

Sorry I should of been clear it is running in the Playable units Init field through the init.sqf, so what your suggesting is just copy and paste the script into each of their init fields and it should work without problems.

Share this post


Link to post
Share on other sites

No, I'm saying move the script call to the initPlayerLocal.sqf instead of the init.sqf or the init fields of each playable character.

 

Event Scripts

Share this post


Link to post
Share on other sites

This is what I use

if (!isServer && isNull player) then {
	waitUntil {sleep 1;!(isNull player)};
	// do stuff
};

Share this post


Link to post
Share on other sites
if (!local _unit) exitWith {};

this is how i do it.

players init:

this call compile preprocessFileLineNumbers "loadouts\leader.sqf";

leader.sqf

private ["_unit", "_backpack", "_uniform", "_vest"];

/* Replace _this below if you want to call this script differently */
_unit = _this;
if (!local _unit) exitWith {};

if (!isNull _unit) then {
    removeAllWeapons _unit;
    removeAllItems _unit;
    removeBackpack _unit;
    removeAllAssignedItems _unit;
    removeUniform _unit;
    removeVest _unit;
    removeHeadgear _unit;
    removeGoggles _unit;

    /* Other gear, goggles, vest, uniform, backpack */
    _unit forceAddUniform "rhs_uniform_m88_patchless";
    _unit addVest "V_TacVest_brn";
    _unit addHeadgear "rhs_beanie_green";
    _unit addBackpackGlobal "rhs_sidor";

    /* Magazines and weapons in main inventory */
    _unit addWeaponGlobal "CUP_arifle_AK74";

    /* Weapons attachments and magazines */
    _unit addPrimaryWeaponItem "CUP_optic_Kobra";
    _unit addPrimaryWeaponItem "CUP_30Rnd_545x39_AK_M";

    /* Assigned items (maps, radios, NV Goggles, etc */
    _unit linkItem "ItemMap";
    _unit linkItem "ItemCompass";
    _unit linkItem "ItemWatch";
    _unit linkItem "ItemRadio";
    _unit linkItem "ItemGPS";

    /* Magazines and weapons in vest */
    _vest = vestContainer _unit;
    _vest addMagazineCargoGlobal ["CUP_30Rnd_TE1_White_Tracer_545x39_AK_M", 6];
    _vest addMagazineCargoGlobal ["HandGrenade", 2];
    _vest addMagazineCargoGlobal ["SmokeShellYellow", 2];

    /* Magazines and weapons in uniform */
    _uniform = uniformContainer _unit;
    _uniform addItemCargoGlobal ["FirstAidKit", 5];

    _unit selectWeapon "this";
    reload _unit;

    /* Backpack */
    _backpack = unitBackPack _unit;
    clearMagazineCargoGlobal _backpack;
    clearWeaponCargoGlobal _backpack;

    _backpack addMagazineCargoGlobal ["rhs_mag_nspn_yellow", 3];
};

Share this post


Link to post
Share on other sites

Hey guys,

Since this is about MP functionality, I guess there is no need of making another similar post.

I have spent weeks studying KK's teachings of MP scripting, as well as many other helpful pages in this community. However, I do not quite understand where and why I should put isServer or isDedicated, when they mean different for the client. (As well as the functionality of initPlayerLocal, initServer, etc) :huh:

How do I turn my code, which is originally SP, into an MP compatible script? I have only specialised in the SP side of coding, so please forgive me as I am fairly new to MP coding. (I have yet to not host a dedicated server, due to technical problems)

Kind Regards,

Rawner135

Share this post


Link to post
Share on other sites

If you're designing strictly for dedicated servers, use isDedicated, however that will prevent you from testing the code in SP. A local TADST session can allow you to code in a dedicated environment though, which I very strongly recommend.

 

Stuff running on the server within behind an isDedicated condition, won't be evaluated if someone loads the content as a local host.

 

An example of a time when I'd use isDedicated is when I call callExtension command. You might not want callExtension being called when you're testing the code in SP, but when its hooked up to your dedi server which has DB dll, then its appropriate.

 

There are only a few cases in A3 where you really need to use isServer/isDedicated anyways.

 

1. Functions called in preInit/postInit. These are called by both server and client so you have to filter with isServer/isDedicated.

 

2. mission.sqm. Code in mission SQM is called on all machines in the session, so need to filter. This one trips up a lot of the SP scenario designers when they make MP content, with frequent complaints like "my loadout disappeared when someone joined the server"

 

3. MP Event Handlers. Executed globally, should filter if appropriate.

 

4. setWaypointStatements. Executed globally, should filter if appropriate.

 

5. Remote execution frameworks, good to filter. If your remoteExec function target is 0, should probably filter if its appropriate.

 

Might be a couple others.

 

Other than those few examples, you can avoid locality issues from the start.

 

 

As for the rest, just trial and error.

 

Lots of little things simply don't work by themselves in MP.

 

Example is setting the position and direction of a remote object.

 

This code does not work as it should:

if (!local _unit) then {
     _unit setDir (random 360);
};

Despite the BIKI saying setDir has global effect, it does not. Even Moricky did not account for this when designing Zeus. You cannot setDir remote objects with vanilla Zeus, to this day.

 

Instead we have to do silly things like this:

if (!local _unit) then {
     [_unit,(random 360)] remoteExec ['setDir',_unit,FALSE];
} else {
     _unit setDir (random 360);
};
  • Like 2

Share this post


Link to post
Share on other sites

If you're designing strictly for dedicated servers, use isDedicated, however that will prevent you from testing the code in SP. A local TADST session can allow you to code in a dedicated environment though, which I very strongly recommend.

 

Stuff running on the server within behind an isDedicated condition, won't be evaluated if someone loads the content as a local host.

 

An example of a time when I'd use isDedicated is when I call callExtension command. You might not want callExtension being called when you're testing the code in SP, but when its hooked up to your dedi server which has DB dll, then its appropriate.

 

There are only a few cases in A3 where you really need to use isServer/isDedicated anyways.

 

1. Functions called in preInit/postInit. These are called by both server and client so you have to filter with isServer/isDedicated.

 

2. mission.sqm. Code in mission SQM is called on all machines in the session, so need to filter. This one trips up a lot of the SP scenario designers when they make MP content, with frequent complaints like "my loadout disappeared when someone joined the server"

 

3. MP Event Handlers. Executed globally, should filter if appropriate.

 

4. setWaypointStatements. Executed globally, should filter if appropriate.

 

5. Remote execution frameworks, good to filter. If your remoteExec function target is 0, should probably filter if its appropriate.

 

Might be a couple others.

 

Other than those few examples, you can avoid locality issues from the start.

 

 

As for the rest, just trial and error.

 

Lots of little things simply don't work by themselves in MP.

 

Example is setting the position and direction of a remote object.

 

This code does not work as it should:

if (!local _unit) then {
     _unit setDir (random 360);
};

Despite the BIKI saying setDir has global effect, it does not. Even Moricky did not account for this when designing Zeus. You cannot setDir remote objects with vanilla Zeus, to this day.

 

Instead we have to do silly things like this:

if (!local _unit) then {
     [_unit,(random 360)] remoteExec ['setDir',_unit,FALSE];
} else {
     _unit setDir (random 360);
};

Thanks a tonne, mdcclxxvi! :D (Damn, wish I could owe you a beer :P )

I had no idea that isServer and isDedicated were used for those purposes, thanks again! :D

I guess KK might have to update setDir's information on the BIKI, before it confuses more that use the command.

One more question, though. Is isServer necessary for filtering commands such as compile or preprocessFileLineNumbers? (I usually have a .sqf file that remembers all known functions for future executions)

Thanks Again,

Rawner135

Share this post


Link to post
Share on other sites

I thought isServer and isDedicated were pretty much the same thing till I tried running my mission I've been coding for about 2-3 weeks now on a dedicated platform.

 

Found out the hard way, but it was a pretty easy fix to get everything working again, just gotta find out which scripts require which locality. Being only 3 options, client, server and dedicated, it was fairly quick to get everything back to working order.

Share this post


Link to post
Share on other sites

@Rawner, it honestly depends on where you need to execute those functions, on the clients (need to ensure you run it locally on all clients), the server only (make sure to run server side only), or both client and server.

Share this post


Link to post
Share on other sites

@Rawner, it honestly depends on where you need to execute those functions, on the clients (need to ensure you run it locally on all clients), the server only (make sure to run server side only), or both client and server.

 

Well basically I want human interactive functions (including my custom modules) to work on clients, especially the host (not server). However, at the same time, I don't want conflicts upon JIP players such as vehicle creation duplicates, or global uniform removals.

 

 

Currently, here is a list on what happened upon testing my mod:

 

+ = Works; + = Does not work

 

 

Server (first Arma 3 running. Acts as Host):

 

+ System messages duplicate, which means compiles and functions are duping.

+ Seems to run MP smoothly, but stays on a black screen (with logo as well) which is supposed to fade, using cutText. (beyond this start-up issue, I had no clue what was going on behind the black screen)

 

 

Client (second Arma 3):

 

+ Start-up works perfectly.

+ Spawns on map perfectly with no black screen.

+ Custom modules do not work, except for Ambient Spawner.

+ Loot spawns in buildings. (Na_Palm's script)

+ Upon unit spawns in towns, they are wearing nothing but underpants.

 

 

These issues were tested in my newly released mod, DSS: The Mod. (Steam Version)

 

 

Feel free to unpbo dss_code, as well as my mission to find out what I have done wrong. (I have only been used to the SP environment of arma 3, but I hope its something simple such as adding isServer or isDedicated somewhere along the scripts.  :( )

 

 

Thank you and I appreciate all your help greatly,

 

 

 

Rawner135

  • Like 1

Share this post


Link to post
Share on other sites

At top of 'init\compiles.sqf' is a:

if (!isServer) exitWith {};

Which means only a server (dedicated or local host) will be able to read the file. That means this file will work as intended in singleplayer or local-host environment, where the client is also the server, but won't work as intended when connecting as a client in a dedicated environment, where the client is not the server.

 

It looks to be all client code (assumption), so you'd likely want it to run if you are the host of a local MP and also in SP.

 

Consider something like this instead

if ((isMultiplayer) && (isDedicated)) exitWith {}; // If we are in multiplayer, we don't want the dedicated machine to read this code, but we do want to allow the local MP host to read it if he is also a client or in singleplayer.

__

 

Then for the UI stuff .. Line 31 of 'UI\init.sqf' is I believe the same issue.

 

change the 

if (!isServer) exitWith {};

to 

if ((isMultiplayer) && (isDedicated)) exitWith {};

Consider moving that check closer to the top of the file, as it looks like there is some code above line 31 that is only intended to be read by the clients machine. Probably somewhere above where "Humanity_init.sqf" is compiled since the contents of that file seem to be client code as well.

 

 

__

 

And in 'Humanity_init.sqf'

 

Those two functions look like client code, so do the same as above. change

if (!isServer) exitWith {};

to 

if ((isMultiplayer) && (isDedicated)) exitWith {};

__

 

This also seems to be occurring in some of the 'UI\functions\....sqf' files. The ismultiplayer&&isdedicated check above should probably replace all the instances of !isServer in those files, and any other client files you want to run in both MP, SP and local Host MP, but dont want the dedicated server to read.

 

Same thing for the ambient music init file.

 

__

 

For the nudie units,

 

in 'compiles\man_q_randomizer.sqf'

 

Is that supposed to run on the client or server? If on the server then its okay. (!isServer) exitwith is appropriate in that situation.

 

Down bottom of the file, change:

_man addUniform _randomUniform;

to 

_man forceAddUniform _randomUniform;

https://community.bistudio.com/wiki/forceAddUniform

 

Same thing for the 'system\fn_actionBrain.sqf' file, find the addUniform and change to forceAddUniform. If the unit is not in a civilian group, adding civilian clothing will only work with forceAddUniform, same thing applies for all factions. If there are any other instances of 'addUniform' in the mod, probably apply this change across the board.

 

 

__

 

Re the black screen, im not sure what this supposed to be for, but loop + lack of an exitWith raised some question marks.

while {true} do
     {
          cutText ["","BLACK IN", 0.5];
          sleep 0.3;
          cutRsc ["DSS_Logo_MainMenu", "PLAIN"];
          playMusic "DSS_Intro_MainMenu";
          sleep 168;
          cutText ["","BLACK FADED", 0.5];
          sleep 1;
};

 

 

 

Congrats on the release BTW :)

  • Like 1

Share this post


Link to post
Share on other sites

Certain scripts might not function right due to locality, they might run perfect on singleplayer/LAN multiplayer sessions, but won't work right on dedicated enviroments.

 

Just try them on different clients using initServer, initPlayerLocal and through the actual init(only when needed)

Make sure all functions still work in-game when testing on a dedicated enviroment, as some might and some might not. If they don't, switch their locality and try again.

  • Like 1

Share this post


Link to post
Share on other sites

Thank you so much guys for your great help! :D


I will open up an offline dedi server with those changes (as well as a couple of improvements to some functions), and will let you guys know if any thing happens. ;)

 

 

Edit: Okay, I reviewed the code and I realized man_q_randomizer.sqf is for randomizing the clothing of Quest units (which are dead, scattered throughout the map to acquire Quests). The zombies that spawn in towns are the ones which are naked: (I believe it has got to do with it being uniformClass-less :blink: )

 

 

Full config:

class CfgPatches
{
	class dss_zombies
	{
		units[]=
		{
			"dss_zombie1"
		};
		weapons[]={};
		requiredVersion=1;
		requiredAddons[]={"A3_Characters_F"};
		author[]=
		{
			"Ranwer135"
		};
	};
};
class CfgFactionClasses
{
	class DSS_ZMB_FCT
	{
		icon="dss_zombies\icon\dss_zmb_faction_ca.paa";
		displayName="Zombies";
		priority=10;
		side=2;
	};
};
class CfgVehicleClasses
{
	class DSS_ZMB
	{
		displayName="Zombies";
	};
	class DSS_ZMB_I
	{
		displayName="Zombies";
	};
};
class CfgFaces
{
	class Default;
	class Man_A3: Default
	{
		class Default;
		class DSS_Zed1: Default
		{
			name = "Zombie";
			displayname = "Zed";
			identityTypes[] = {"DSS_id_Zed1"};
			head = "KerryHead_A3";
			texture = "\dss_zombies\faces\z_white_01_co.paa";
			disabled = 0;
			textureHL = "dss_zombies\faces\hl_white_z_co.paa";
		};
		class DSS_ShadowZed1: Default
		{
			name = "Shadow Zombie";
			displayname = "Shadow Zed";
			identityTypes[] = {"DSS_id_NTZed1"};
			head = "KerryHead_A3";
			texture = "\dss_zombies\faces\nt_white_01_co.paa";
			disabled = 0;
			textureHL = "dss_zombies\faces\hl_white_nt_co.paa";
		};
	};
};
class CfgIdentities
{
	class DSS_id_Zed1
	{
		name = "Zombie";
		face = "DSS_Zed1";
		speaker = "Male09_F";
		pitch = 1.0;
		glasses = "none";
	};
	class DSS_id_NTZed1
	{
		name = "Shadow Zombie";
		face = "DSS_ShadowZed1";
		speaker = "Male09_F";
		pitch = 1.0;
		glasses = "none";
	};
};
class CfgVehicles
{
	class C_man_p_beggar_F;
	class DSS_zombie1: C_man_p_beggar_F
	{
		faction="DSS_ZMB_FCT";
		scope=2;
		side=2;
		vehicleClass="DSS_ZMB";
		identityTypes[]=
		{
			"Language_EN_EP1",
			"Head_NATO",
			"NoGlasses"
		};
		faceType="Man_A3";
		magazines[]={};
		respawnmagazines[]={};
		moves="DSS_CfgMovesMaleZombie";
		genericNames="NATOMen";
		armor=15;
     	uniformClass = ""; //I believe this could be the reason why he is naked
		displayName="Zombie";
		author="Rawner135";
		hiddenSelections[]={"camo"};
		hiddenSelectionsTextures[]=
		{"\dss_zombies\textures\c_clothz_co.paa"};
		class eventHandlers
		{
			init = "_this execVM ""\dss_code\zeds\zed_follow.sqf""; _this execVM ""\dss_code\zeds\zed_sounds.sqf""; _this execVM ""\dss_code\zeds\zed_init.sqf"";";
		};
	};
	class i_soldier_unarmed_f;
	class DSS_zombie_a1: i_soldier_unarmed_f
	{
		faction="DSS_ZMB_FCT";
		scope=2;
		side=2;
		vehicleClass="DSS_ZMB";
		identityTypes[]=
		{
			"Language_EN_EP1",
			"Head_NATO",
			"NoGlasses"
		};
		faceType="Man_A3";
		magazines[]={};
		respawnmagazines[]={};
		moves="DSS_CfgMovesMaleZombie";
		genericNames="NATOMen";
		armor=20;
     	uniformClass = ""; //Same goes here
		displayName="Military Zombie";
		author="Rawner135";
		linkedItems[] = {"V_PlateCarrierIA1_dgtl","H_HelmetIA"};
		respawnLinkedItems[] = {"V_PlateCarrierIA1_dgtl","H_HelmetIA"};
		class eventHandlers
		{
			init = "_this execVM ""\dss_code\zeds\zed_follow.sqf""; _this execVM ""\dss_code\zeds\zed_sounds.sqf""; _this execVM ""\dss_code\zeds\zed_init.sqf"";";
		};
	};
	class DSS_zombie_n1: C_man_p_beggar_F
	{
		faction="DSS_ZMB_FCT";
		scope=2;
		side=2;
		vehicleClass="DSS_ZMB";
		identityTypes[]=
		{
			"Language_EN_EP1",
			"Head_NATO",
			"NoGlasses"
		};
		faceType="Man_A3";
		magazines[]={};
		respawnmagazines[]={};
		moves = "CfgMovesMaleSdr";
		genericNames="NATOMen";
		armor=30;
     	uniformClass = ""; //And here
		displayName="Shadow Zombie";
		author="Rawner135";
		hiddenSelections[]={"camo"};
		hiddenSelectionsTextures[]=
		{"\dss_zombies\textures\c_clothz_co.paa"};
		linkedItems[] = {};
		respawnLinkedItems[] = {};
		class eventHandlers
		{
			init = "_this execVM ""\dss_code\zeds\nt_follow.sqf""; _this execVM ""\dss_code\zeds\nt_sounds.sqf""; _this execVM ""\dss_code\zeds\nt_init.sqf"";";
		};
	};
};

My goal for these guys is to have a uniform, but not equip-able. (Much like in DayZ SA, where dead zombies do not carry anything typically)

 

 

As for the black screen, here is the code, found under dss_missions:

/*
	Author: Rawner135

	Description:
	Custom load screen that reduces lag while loading live scripts.

	Parameter(s):
	NONE

	Returns:
	NOTHING
*/

//This jazzes up the custom load screen with a slow, blinking Text
dss_mission_textBlinker = {

	while {PreLoadState == "Running"} do {

		["Loading DSS...", 0.0, 0.7, 0.5, 0.5] spawn BIS_fnc_dynamicText;
		sleep 3;
	};
};

//Creates camera view, far away from Altis so players can fully load the map
disableSerialization;
enableRadio false;
cutText ["","BLACK FADED", 4.9];
_camera = "camera" camCreate [0,0,0]; 
_camera cameraEffect ["internal","back"];
_camera camPrepareTarget [8445.52,26290.08,95.18];
_camera camPreparePos [8445.34,25191.04,0.47];
_camera camPrepareFOV 0.700;
_camera camCommitPrepared 0;

PreLoadState = "Running";
[] spawn dss_mission_textBlinker;

//Loading Custom Screen
_ui = uiNamespace getVariable "PBarProgress_Load";
_progressBar = _ui displayCtrl 39302;
("DSS_LayerLogo" call BIS_fnc_rscLayer) cutRsc ["DSS_Logo_MainMission", "BLACK"];
_progressBar progressSetPosition 0.01;
5 cutRsc ["DSS_ProgressBar_Load","PLAIN"];
_progressBarCall = (uiNamespace getVariable "PBarProgress_Load") displayCtrl 39302;
sleep 2;

//READY

//Initialization

/*
The sleep commands mentioned below should not delay the player any further. However, it is required for future/present functions to work in a scheduled environment. (e.g. loading character's gear using iniDB)
*/

//Spawn
systemChat "Loading Spawn..";
[] spawn dss_fnc_player_spawn;
sleep 2;
_progressBarCall progressSetPosition (37 / 100);

//Survivors
systemChat "Loading Survivor's..";
[] spawn dss_fnc_survivorinit;
sleep 3;
_progressBarCall progressSetPosition (62 / 100);

//Client
systemChat "Loading Client..";
if (isMultiplayer) then {[] execVM "dss_missions\scenarios\dss_mission.Altis\Server\serverInit.sqf"}; //Checks if scenario is in Multiplayer. If true, it replaces default loadout above with his saved gear
"colorCorrections" ppEffectEnable true;
"colorCorrections" ppEffectAdjust [1, 1, 0, [0, 0, 0, 0], [0.3, 0.3, 0.3, 1.3], [1, 1, 1, 0]];
"colorCorrections" ppEffectCommit 0;
sleep 4;
_progressBarCall progressSetPosition (100 / 100);

//FINISHED!

//Launch

systemChat "Launching DSS..";
PreLoadState = "Finished";
sleep 3;
"dynamicBlur" ppEffectEnable true;   
"dynamicBlur" ppEffectAdjust [0];  
"dynamicBlur" ppEffectCommit 3;     
"dynamicBlur" ppEffectAdjust [6]; 
"dynamicBlur" ppEffectCommit 0;

//The below code appears to be the source of it not returning to 'PLAIN'
/******************************/
5 cutText ["","BLACK FADED"];
cutText ["","BLACK FADED", 0.5];
/******************************/

sleep 3;

//Ends Camera scene and returns to player view
player cameraEffect ["terminate","back"];
camDestroy _camera; //Destroys camera and returns to player view

sleep 2;
[] spawn dss_fnc_start; //Initializes scene where he wakes up
[] spawn dss_fnc_startfx; //Sound effects for waking up

//Screen remains black until map is fully loaded
cutText ["Please Wait..", "BLACK"];
enableRadio = true;

Line 91 and 92 are the lines of code, which I believe is the source of the screen staying black.  :huh:

 

 

Best Regards,



Rawner135

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

×