Jump to content
red62

Randomizing roles for players in MP

Recommended Posts

Good morning all. I have been working on a mission for a week or so where 80% of the players slot a generic "civilian" role, and then an array of the actual roles are randomized in a script I wrote (called by a simple trigger on the map) and distributed to each player through systemChat when they load in. The script works perfectly fine in SP and about half the time in MP, with a seemingly random probability. When I test it in MP with 3-4 people on our dedicated server, some of us get a systemChat with our role and some do not. I am wondering if this has to do with the VM trying to systemChat the player before they load in, or some other issue with how I have implemented my script, but I appreciate any help. My script, roles.sqf, follows:

 

Spoiler


//if (hasInterface) exitWith {}; //use this once i'm done with the script so it doesn't run on HC
if (!isServer) exitWith {};

//define a fisher-yates shuffle implementation
//credit to https://forums.bistudio.com/forums/topic/188713-bis_fnc_arrayshuffle-broken/
//every possible permutation of the input array has an equal probability after the shuffle
FY_shuffle = {
	private ["_j", "_t"];

	for "_i" from count _this - 1 to 1 step -1 do
	{
		//Select random index
		_j = floor random (_i + 1);
		
		//Swap 
		if (_i != _j) then
		{
			_t = _this select _i;
			_this set [_i, _this select _j];
			_this set [_j, _t]; 
		};
	};

	_this
};

//3 suspects - high priority roles get filled first by people that slotted aiello, hayes, amit
_highPrioRoles = ["You are the culprit. Use your agents to get the heat off of yourself, and get rid of the old lock as soon as possibe. Maybe you can frame someone...", "You are a suspect but you did not do the crime. Use your agents to find out who did it and make sure you don't get wrongfully accused.", "You are a suspect but you did not do the crime. Use your agents to find out who did it and make sure you don't get wrongfully accused."] call FY_shuffle;

//these do not have to be randomly selected, just ran on each supporter
_aielloSupporterRole = "You are on Aiello's side. Help him deflect suspicion and bribe, kill, and persuade others to make sure he lives.";
_amitSupporterRole = "You are on Amit's side. Help him deflect suspicion and bribe, kill, and persuade others to make sure he lives.";
_hayesSupporterRole = "You are on Hayes's side. Help him deflect suspicion and bribe, kill, and persuade others to make sure he lives.";

//medium prio roles
//hope these newlines don't break the interpreter
_mediumPrioRoles =  [
					"You are an agent of chaos. Fuel conflicts, steal, cheat, and lie to cause general issues with the group (secretly, of course).", "You are an agent of chaos. Fuel conflicts, steal, cheat, and lie to cause general issues with the group (secretly, of course).", 
					"You are a hoarder. Your job is to secretly leech and hoard as much loot as you can to your room, just like in Rust.", "You are a hoarder. Your job is to secretly leech and hoard as much loot as you can to your room, just like in Rust.",
					"You are an agent of law. Try to de-escalate conflicts, keep people calm, and find the culprit. You are kind of like the JANFU rust police.", "You are an agent of law. Try to de-escalate conflicts, keep people calm, and find the culprit. You are kind of like the JANFU rust police.", "You are an agent of law. Try to de-escalate conflicts, keep people calm, and find the culprit. You are kind of like the JANFU rust police.", 
					"You are the tavern owner. Maintain a nice place for JANFU members to relax and socialize.", "You are the bartender. Serve JANFU members at the tavern and resolve bar fights.",
					"You are an armory guard. Get to the armory as soon as possible so you can regulate who gets what guns. As a result, you can have some of the guns yourself. There may be one other guard with you.", "You are an armory guard. Get to the armory as soon as possible so you can regulate who gets what guns. As a result, you can have some of the guns yourself. There may be one other guard with you.",
					"You are a doctor. Take medical care of JANFU members and make sure people don't abuse/steal the medical supplies in the hospital. There may be another doctor with you.", "You are a doctor. Take medical care of JANFU members and make sure people don't abuse/steal the medical supplies in the hospital. There may be another doctor with you.",
					"You are a jailer. You are the warden for anyone that the group decides to jail. You may have another jailer with you.", "You are a jailer. You are the warden for anyone that the group decides to jail. You may have another jailer with you.",
					"You are a witness. You saw the incident happen, but you didn't quite catch the culprit's name... You can blame it on someone you don't like, or accept a bribe, or just keep quiet.", "You are a witness. You saw the incident happen, but you didn't quite catch the culprit's name... You can blame it on someone you don't like, or accept a bribe, or just keep quiet.",
					"ACCOMPLICE_PLACEHOLDER",
					"FREEAGENT_PLACEHOLDER","FREEAGENT_PLACEHOLDER"
					] call FY_shuffle;

_lowPrioRoles = [
					"FREEAGENT_PLACEHOLDER",
					"LAW_PLACEHOLDER", "LAW_PLACEHOLDER",
					"FREEAGENT_PLACEHOLDER", "FREEAGENT_PLACEHOLDER", 
					"FREEAGENT_PLACEHOLDER"
					] call FY_shuffle;
					

//concatenate all roles into one array such that the higher priority ones are at the start
_allRoles = _mediumPrioRoles + _lowPrioRoles; 

_highPrioIndex = 0;
_currIndex = 0;
_culprit = nil;
_accomplice = nil;

//actually assign the roles here
{
	
	
	switch (str _x) do
	{	
		//wait until player is loaded in
		waitUntil {!isNull _x};
		waitUntil {time > 0};
		//assign suspect and main culprits
		case "hayes":
		{
			(_highPrioRoles select _highPrioIndex) remoteExec ["systemChat", _x];
			if ((_highPrioRoles select _highPrioIndex) isEqualTo "CULPRIT_PLACEHOLDER") then 
			{
				_culprit = _x;
			};
			_highPrioIndex = _highPrioIndex + 1;
		};
		case "aiello":
		{
			(_highPrioRoles select _highPrioIndex) remoteExec ["systemChat", _x];
			_highPrioIndex = _highPrioIndex + 1;
			if ((_highPrioRoles select _highPrioIndex) isEqualTo "CULPRIT_PLACEHOLDER") then 
			{
				_culprit = _x;
			};
		};
		case "amit":
		{
			(_highPrioRoles select _highPrioIndex) remoteExec ["systemChat", _x];
			_highPrioIndex = _highPrioIndex + 1;
			if ((_highPrioRoles select _highPrioIndex) isEqualTo "CULPRIT_PLACEHOLDER") then 
			{
				_culprit = _x;
			};
		};
		
		case "aiello_agent_1":
		{
			_aielloSupporterRole remoteExec ["systemChat", aiello_agent_1];
		};

		case "aiello_agent_2":
		{
			_aielloSupporterRole remoteExec ["systemChat", aiello_agent_2];
		};
		
		case "hayes_agent_1":
		{
			_hayesSupporterRole remoteExec ["systemChat", hayes_agent_1];
		};
		
		case "hayes_agent_2":
		{
			_hayesSupporterRole remoteExec ["systemChat", hayes_agent_2];
		};
		
		case "amit_agent_1":
		{
			_amitSupporterRole remoteExec ["systemChat", amit_agent_1];
		};
		
		case "amit_agent_2":
		{
			_amitSupporterRole remoteExec ["systemChat", amit_agent_2];
		};
		
		//assign all other roles
		default
		{
			//systemChat to the current user his role
			(_allRoles select _currIndex) remoteExec ["systemChat", _x];
			if ((_allRoles select _currIndex) isEqualTo "ACCOMPLICE_PLACEHOLDER") then 
			{
				_accomplice = _x;
			};
			_currIndex = _currIndex + 1;
		};
	};
} forEach allPlayers - (entities "HeadlessClient_F"); 

sleep 5;
if (not isNil "_culprit") then
{
	_culprit addItem "MCC_metalwire";
};


if (not isNil "_accomplice") then
{
	_accomplice addItem "MCC_screwdriver";
};











 

 

Share this post


Link to post
Share on other sites

Update to this, I spent another 5 hours on it and decided to split the work into client and server to see if I could make that work. Now, inside my initPlayerLocal I call a script roles_client.sqf which calls this code for each person with their role:

	player addAction ["Show Role", 
			"hint (_this select 3); 
			systemChat (_this select 3); 
			(_this select 1) removeAction (_this select 2)", 
		_roleDesc];
		

I thought this should work fine since it is being executed on each player for their unit, and it does work fine in SP. It adds the action to the player in multiplayer but the problem is neither the hint or the systemchat show up once you select it (it does get removed afterwards though). I have also tried using remoteExec like so:

	[player, ["Show Role", 
		"hint (_this select 3); 
		systemChat (_this select 3); 
		(_this select 1) removeAction (_this select 2)", 
		_roleDesc]] 
	remoteExec ["addAction", player];

 

My roles_server.sqf is below :

 

Spoiler


//if (hasInterface) exitWith {}; //use this once i'm done with the script so it doesn't run on HC
if (!isServer) exitWith {};
//if (!isDedicated) exitWith {};

/*
//EXTRA CODE THAT I'VE TRIED IN THE PAST BUT DOESN'T WORK. might be useful in future
//_id = _x addAction ["Show Role", "hint (_this select 3); systemChat (_this select 3)", (_highPrioRoles select _highPrioIndex)];
//_x addAction ["Remove Show Role Option", "(_this select 1) removeAction (_this select 3); (_this select 1) removeAction (_this select 2)",_id];
//[["Show Role", "hint (_this select 3); systemChat (_this select 3)", (_highPrioRoles select _highPrioIndex)], "addAction", _x] call BIS_fnc_MP;
//[["Remove Show Role Option", "(_this select 1) removeAction (_this select 3); (_this select 1) removeAction (_this select 2)",_id], "addAction", _x] call BIS_fnc_MP;

*/

//define a fisher-yates shuffle implementation
//credit to https://forums.bistudio.com/forums/topic/188713-bis_fnc_arrayshuffle-broken/
//every possible permutation of the input array has an equal probability after the shuffle
FY_shuffle = {
	private ["_j", "_t"];

	for "_i" from count _this - 1 to 1 step -1 do
	{
		//Select random index
		_j = floor random (_i + 1);
		
		//Swap 
		if (_i != _j) then
		{
			_t = _this select _i;
			_this set [_i, _this select _j];
			_this set [_j, _t]; 
		};
	};

	_this
};

//3 suspects - high priority roles get filled first by people that slotted aiello, hayes, amit
highPrioRoles = ["You are the culprit. Use your agents to get the heat off of yourself, and get rid of the old lock as soon as possibe. Maybe you can frame someone...", "You are a suspect but you did not do the crime. Use your agents to find out who did it and make sure you don't get wrongfully accused.", "You are a suspect but you did not do the crime. Use your agents to find out who did it and make sure you don't get wrongfully accused."] call FY_shuffle;

//these do not have to be randomly selected, just ran on each supporter
aielloSupporterRole = "You are on Aiello's side. Help him deflect suspicion and bribe, kill, and persuade others to make sure he lives.";
amitSupporterRole = "You are on Amit's side. Help him deflect suspicion and bribe, kill, and persuade others to make sure he lives.";
hayesSupporterRole = "You are on Hayes's side. Help him deflect suspicion and bribe, kill, and persuade others to make sure he lives.";

//medium prio roles
//hope these newlines don't break the interpreter
mediumPrioRoles =  [
					"You are an agent of chaos. Fuel conflicts, steal, cheat, and lie to cause general issues with the group (secretly, of course).", "You are an agent of chaos. Fuel conflicts, steal, cheat, and lie to cause general issues with the group (secretly, of course).", 
					"You are a hoarder. Your job is to secretly leech and hoard as much loot as you can to your room, just like in Rust.", "You are a hoarder. Your job is to secretly leech and hoard as much loot as you can to your room, just like in Rust.",
					"You are an agent of law. Try to de-escalate conflicts, keep people calm, and find the culprit. You are kind of like the JANFU rust police and the lawmaker rolled in to one.", "You are an agent of law. Try to de-escalate conflicts, keep people calm, and find the culprit. You are kind of like the JANFU rust police and the lawmaker rolled in to one.", "You are an agent of law. Try to de-escalate conflicts, keep people calm, and find the culprit. You are kind of like the JANFU rust police and the lawmaker rolled in to one.", 
					"You are the tavern owner. Maintain a nice place for JANFU members to relax and socialize.", "You are the bartender. Serve JANFU members at the tavern and resolve bar fights.",
					"You are an armory guard. Get to the armory as soon as possible so you can regulate who gets what guns. As a result, you can have some of the guns yourself. There may be another armory guard picked also. The good guns are on the upper floor of the armory, and the lower tier ones are on the bottom floor.", "You are an armory guard. Get to the armory as soon as possible so you can regulate who gets what guns. As a result, you can have some of the guns yourself. There may be another armory guard picked also. The good guns are on the upper floor of the armory, and the lower tier ones are on the bottom floor.",
					"You are a doctor. Take medical care of JANFU members and make sure people don't abuse/steal the medical supplies in the hospital. There may be another doctor with you.", "You are a doctor. Take medical care of JANFU members and make sure people don't abuse/steal the medical supplies in the hospital. There may be another doctor with you.",
					"You are a jailer. You are the warden for anyone that the group decides to jail. You may have another jailer with you.", "You are a jailer. You are the warden for anyone that the group decides to jail. You may have another jailer with you.",
					"You are a witness. You saw the incident happen, but you didn't quite catch the culprit's name... You can blame it on someone you don't like, or accept a bribe, or just keep quiet.", "You are a witness. You saw the incident happen, but you didn't quite catch the culprit's name... You can blame it on someone you don't like, or accept a bribe, or just keep quiet.",
					"You are an accomplice. You helped the culprit commit the crime, but he used the alias 'ROBLOX_LORD_69' and as a result you didn't catch his name. You have some evidence on you that you need to get rid of, and then you should blend in to the crowd. The culprit may try to blame you if they know who you are.",
					"You are a free agent. You can join any side that pays you, guarantees your protection, or just because you want to.","You are a free agent. You can join any side that pays you, guarantees your protection, or just because you want to."
					] call FY_shuffle;

lowPrioRoles = [
					"You are a free agent. You can join any side that pays you, guarantees your protection, or just because you want to.",
					"You are an agent of law. Try to de-escalate conflicts, keep people calm, and find the culprit. You are kind of like the JANFU rust police and the lawmaker rolled in to one.", "You are an agent of law. Try to de-escalate conflicts, keep people calm, and find the culprit. You are kind of like the JANFU rust police and the lawmaker rolled in to one.",
					"You are a free agent. You can join any side that pays you, guarantees your protection, or just because you want to.", "You are a free agent. You can join any side that pays you, guarantees your protection, or just because you want to.", 
					"You are a free agent. You can join any side that pays you, guarantees your protection, or just because you want to."
					] call FY_shuffle;
					

//concatenate all roles into one array such that the higher priority ones are at the start
allRoles = mediumPrioRoles + lowPrioRoles; 

highPrioIndex = 0;
currIndex = 0;
culprit = nil;
accomplice = nil;
tavernOwner = nil;

publicVariable "allRoles";
publicVariable "highPrioRoles";
publicVariable "aielloSupporterRole";
publicVariable "amitSupporterRole";
publicVariable "hayesSupporterRole";
publicVariable "allRoles";
publicVariable "highPrioIndex";
publicVariable "currIndex";


//hint format ["%1", currIndex];



 

 

 

And my full roles_client.sqf:

Spoiler

//if (isServer) exitWith {};

//show the players role to them and give them an action to show it again
fnc_addRoleAction = {
params ["_z", "_roleDesc"];
	
	player addAction ["Show Role", 
			"hint (_this select 3); 
			systemChat (_this select 3); 
			(_this select 1) removeAction (_this select 2)", 
		_roleDesc];
		
	/*
	[player, ["Show Role", 
		"hint (_this select 3); 
		systemChat (_this select 3); 
		(_this select 1) removeAction (_this select 2)", 
		_roleDesc]] 
	remoteExec ["addAction", player];
	
	
	systemChat _roleDesc;
	_z addAction ["Show Role", 
			"hint (_this select 3); 
			systemChat (_this select 3); 
			(_this select 1) removeAction (_this select 2)", 
		_roleDesc];
	*/
		
};

waitUntil {time > 0};

_x = _this;

switch (str _x) do
{	
	//wait until player is loaded in
	waitUntil {time > 0};
	waitUntil {!isNull _x};

	//assign suspect and main culprits
	case "hayes":
	{
		[_x, (highPrioRoles select highPrioIndex)] call fnc_addRoleAction;
		
		if ((highPrioRoles select highPrioIndex) isEqualTo "You are the culprit. Use your agents to get the heat off of yourself, and get rid of the old lock as soon as possibe. Maybe you can frame someone...") then 
		{
			_x addItem "MCC_metalwire";
		};
		highPrioIndex = highPrioIndex + 1;
		publicVariable "highPrioIndex";
	};
	case "aiello":
	{
		[_x, (highPrioRoles select highPrioIndex)] call fnc_addRoleAction;
		
		if ((highPrioRoles select highPrioIndex) isEqualTo "You are the culprit. Use your agents to get the heat off of yourself, and get rid of the old lock as soon as possibe. Maybe you can frame someone...") then 
		{
			_x addItem "MCC_metalwire";
		};
		highPrioIndex = highPrioIndex + 1;
		publicVariable "highPrioIndex";
	};
	case "Amit":
	{
		[_x, (highPrioRoles select highPrioIndex)] call fnc_addRoleAction;
		
		if ((highPrioRoles select highPrioIndex) isEqualTo "You are the culprit. Use your agents to get the heat off of yourself, and get rid of the old lock as soon as possibe. Maybe you can frame someone...") then 
		{
			_x addItem "MCC_metalwire";
		};
		highPrioIndex = highPrioIndex + 1;
		publicVariable "highPrioIndex";
	};
	
	case "aiello_agent_1":
	{
		[_x, aielloSupporterRole] call fnc_addRoleAction;
	};

	case "aiello_agent_2":
	{
		[_x, aielloSupporterRole] call fnc_addRoleAction;
	};
	
	case "hayes_agent_1":
	{
		[_x, hayesSupporterRole] call fnc_addRoleAction;
	};
	
	case "hayes_agent_2":
	{
		[_x, hayesSupporterRole] call fnc_addRoleAction;
	};
	
	case "amit_agent_1":
	{
		[_x, amitSupporterRole] call fnc_addRoleAction;
	};
	
	case "amit_agent_2":
	{
		[_x, amitSupporterRole] call fnc_addRoleAction;
	};
	
	//assign all other roles
	default
	{
		//add the actions for the user to check his role
		[_x, (allRoles select currIndex)] call fnc_addRoleAction;
		
		if ((allRoles select currIndex) isEqualTo "You are an accomplice. You helped the culprit commit the crime, but he used the alias 'ROBLOX_LORD_69' and as a result you didn't catch his name. You have some evidence on you that you need to get rid of, and then you should blend in to the crowd. The culprit may try to blame you if they know who you are.") then 
		{
			_x addItem "MCC_screwdriver";
		};
		
		if ((allRoles select currIndex) isEqualTo "You are the bartender. Serve JANFU members at the tavern and resolve bar fights.") then 
		{
			_x addItem "MCC_bottle_murky";
			_x addItem "MCC_bottle_murky";
			_x addItem "MCC_bottle_murky";

		};
		currIndex = currIndex + 1;
		publicVariable "currIndex"; //update the current index 
	};
};









 

 

Any help is much appreciated. I am at a loss

Share this post


Link to post
Share on other sites

That's a lot to take in all at once. But I see you're using publicVariable to send information across to clients. Theres a chance that those variables aren't being defined when the clientside code is running.

This would cause a few things to go wrong, including your addaction failing to show the role description and the code being unreliable on a dedicated,.

Try this at the beginning of your client side script. *I have not tested this but I think youll know how to fix it*

 

private _variables = [
	"allRoles", 
	"highPrioRoles", 
	"aielloSupporterRole", 
	"amitSupporterRole", 
	"hayesSupporterRole", 
	"allRoles", 
	"highPrioIndex", 
	"currIndex"
];

waituntil {
	
	sleep 1;

	( {isNil _x} count _variables == 0)

};


Also always put a sleep into the start of a waitUntil block or it will run as fast as possible.
 

  • Like 1

Share this post


Link to post
Share on other sites

It looks like that was it. Once I waited until the variables were actually defined, like you said, all functionality seems to work (at least for just me, in our dedicated server). Thank you so much for the help. Do you think there is going to be an issue with users claiming the same role because the new value isn't getting updated fast enough? I am imagining a situation where two players are executing the script at the same time and they both access the variable at the same time. The publicvariable instruction may not get executed in time and the two users would get the same role. Do you think this is possible?

Share this post


Link to post
Share on other sites

Definitely possible, likely even. You will need to store the variables on the server and have the clients ask for a role instead of the server sending all of the roles out. 

Share this post


Link to post
Share on other sites
On 12/11/2017 at 6:00 AM, MKD3-FHI said:

Definitely possible, likely even. You will need to store the variables on the server and have the clients ask for a role instead of the server sending all of the roles out. 

 

Alright, what I may do then is expose a public function fnc_getRole from the roles_server.sqf that the clients can use, and then the server hands out a role, increments the index, and pushes it back out to the mission namespace with publicvariable all in a couple instructions. Thanks for all the help, can I +rep you guys somehow?

Share this post


Link to post
Share on other sites

Click the like button on comments you like. It adds rep. No worries, hope you get it sorted

  • Like 1

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

×