Raised This Month: $12 Target: $400
 3% 

[ANY] How to properly switch team


Post New Thread Reply   
 
Thread Tools Display Modes
Author Message
Benoist3012
Veteran Member
Join Date: Mar 2014
Location: CWave::ForceFinish()
Old 02-11-2019 , 03:26   [ANY] How to properly switch team
Reply With Quote #1

Introduction:
This snippet is aimed at those not willing to use ChangeClientTeam due to undesired side effects, but still wish to change a player team. And without any crash!

How to find out if you're concerned:
If you're doing
Code:
SetEntProp(iClient, Prop_Send, "m_iTeamNum", iNewTeam);
somewhere in your plugin then this is monstrously wrong and you're gonna crash the server hosting your plugin in following minutes.
I was myself doing it and that's how I found how to fix it as well why I'm making this snippet
Here's a list of plugins I can think of that are/were affected by the crash(all from tf2 but I'm sure it's happening on other games):
How to fix:
Luckily the solution is very simple:
Code:
#define TEAM_CLASSNAME "tf_team" Handle g_hSDKTeamAddPlayer; Handle g_hSDKTeamRemovePlayer; public void OnPluginStart() {     Handle hGameData = LoadGameConfigFile("yourgamedata");         StartPrepSDKCall(SDKCall_Entity);     PrepSDKCall_SetFromConf(hGameData, SDKConf_Virtual, "CTeam::AddPlayer");     PrepSDKCall_AddParameter(SDKType_CBasePlayer, SDKPass_Pointer);     g_hSDKTeamAddPlayer = EndPrepSDKCall();     if(g_hSDKTeamAddPlayer == INVALID_HANDLE)         SetFailState("Could not find CTeam::AddPlayer!");         StartPrepSDKCall(SDKCall_Entity);     PrepSDKCall_SetFromConf(hGameData, SDKConf_Virtual, "CTeam::RemovePlayer");     PrepSDKCall_AddParameter(SDKType_CBasePlayer, SDKPass_Pointer);     g_hSDKTeamRemovePlayer = EndPrepSDKCall();     if(g_hSDKTeamRemovePlayer == INVALID_HANDLE)         SetFailState("Could not find CTeam::RemovePlayer!");         delete hGameData; } void ChangeClientTeamEx(iClient, int iNewTeamNum) {     int iTeamNum = GetEntProp(iClient, Prop_Send, "m_iTeamNum");         // Safely swap team     int iTeam = MaxClients+1;     while ((iTeam = FindEntityByClassname(iTeam, TEAM_CLASSNAME)) != -1)     {         int iAssociatedTeam = GetEntProp(iTeam, Prop_Send, "m_iTeamNum");         if (iAssociatedTeam == iTeamNum)             SDK_Team_RemovePlayer(iTeam, iClient);         else if (iAssociatedTeam == iNewTeamNum)             SDK_Team_AddPlayer(iTeam, iClient);     }         SetEntProp(iClient, Prop_Send, "m_iTeamNum", iNewTeamNum); } void SDK_Team_AddPlayer(int iTeam, int iClient) {     if (g_hSDKTeamAddPlayer != INVALID_HANDLE)     {         SDKCall(g_hSDKTeamAddPlayer, iTeam, iClient);     } } void SDK_Team_RemovePlayer(int iTeam, int iClient) {     if (g_hSDKTeamRemovePlayer != INVALID_HANDLE)     {         SDKCall(g_hSDKTeamRemovePlayer, iTeam, iClient);     } }

Gamedata (for tf2, I leave you find the correct team entity classname and offsets for other games)
Code:
"Games" {     "tf"     {         "Offsets"         {             "CTeam::AddPlayer"             {                 "linux"  "202"                 "windows"   "201"             }             "CTeam::RemovePlayer"             {                 "linux"  "203"                 "windows"   "202"             }         }     } }

More details on the crash:
In source MP games, each team is governed by an entity. Those entities contains an array of CBasePlayer *, of course when ChangeClientTeam is called everything is properly handled and the player pointer is removed from one, and added to the other. However some plugins want to move players to spec team but keep them alive, which is impossible to do with ChangeClientTeam even by changing the player alive state, as they will be forcibly be put in observer mode.
So most will do:
PHP Code:
SetEntProp(iClientProp_Send"m_iTeamNum"0); 
Which doesn't notify the previous team entity that the player is now gone, and doesn't notify the receiving team of the new player. And so when the player leaves the server, the game code will clear out the stored ptr in the last assigned team entity, but not in the team you forcibly removed the player from. And that's where the issue is as now the server will play with a garbage pointer.

Here's a piece code I reversed from TF2 server.
Code:
CTeam *pTeam = pOwner->GetTeam(); if ( pTeam ) {     for (int i = 0; i < pTeam->GetNumPlayers(); ++i )     {         CBaseCombatCharacter *pPlayer = pTeam->GetPlayer(i);         if ( pPlayer ) // This check passes "fine"         {             if ( pPlayer->IsPlayer() ) // Uh oh, we have a problem => crash             {                 if ( pPlayer->IsPlayerClass(TFClass_Medic) && pPlayer != pOwner )                 {                 }             }         }     } }
You can easily see why the server will crash as CTeam contains a ptr to a destroyed CBasePlayer object.



That's all, thanks for reading, hopefully you learned something or you knew already either way my job is done here
__________________
Benoist3012 is offline
ThatOneGuy
Veteran Member
Join Date: Jul 2012
Location: Oregon, USA
Old 02-13-2019 , 01:15   Re: [ANY] How to properly switch team
Reply With Quote #2

If the undesired affects you are referring to is the suicide, you can avoid that simply by doing something like the following:

PHP Code:
void ChangeTeam_NoKill(int clientint iTeam)
{
    if(
IsValidClient(client))
    {
        
int iEntProp GetEntProp(clientProp_Send"m_lifeState");
        
SetEntProp(clientProp_Send"m_lifeState"2);
        
ChangeClientTeam(clientiTeam);
        
SetEntProp(clientProp_Send"m_lifeState"iEntProp);
    }

Edit: I see that you state you cannot do it by changing the alive state. It has always worked for me.
__________________

Last edited by ThatOneGuy; 02-13-2019 at 01:18.
ThatOneGuy is offline
Benoist3012
Veteran Member
Join Date: Mar 2014
Location: CWave::ForceFinish()
Old 02-13-2019 , 03:55   Re: [ANY] How to properly switch team
Reply With Quote #3

Quote:
Originally Posted by ThatOneGuy View Post
If the undesired affects you are referring to is the suicide, you can avoid that simply by doing something like the following:

PHP Code:
code 
Edit: I see that you state you cannot do it by changing the alive state. It has always worked for me.
Quote:
Originally Posted by Benoist3012 View Post
However some plugins want to move players to spec team but keep them alive, which is impossible to do with ChangeClientTeam even by changing the player alive state, as they will be forcibly be put in observer mode.
So most will do:
PHP Code:
SetEntProp(iClientProp_Send"m_iTeamNum"0); 
Edit:
code reference #1
code reference #2
__________________

Last edited by Benoist3012; 02-13-2019 at 03:59.
Benoist3012 is offline
PC Gamer
Veteran Member
Join Date: Mar 2014
Old 02-13-2019 , 12:54   Re: [ANY] How to properly switch team
Reply With Quote #4

Benoist3012, tried your code. Works great. Thanks!
PC Gamer is offline
ThatOneGuy
Veteran Member
Join Date: Jul 2012
Location: Oregon, USA
Old 02-13-2019 , 23:34   Re: [ANY] How to properly switch team
Reply With Quote #5

Quote:
Originally Posted by Benoist3012 View Post
Your code:
Code:
if ( iTeamNum == TEAM_UNASSIGNED )
	{
		StateTransition( TF_STATE_OBSERVER );
	}
may not be the same as what I put. Perhaps consider testing what has worked for me in several plugins before stating it untrue. Ignorance is not always bliss... (though we all eat those words at times).
__________________
ThatOneGuy is offline
Benoist3012
Veteran Member
Join Date: Mar 2014
Location: CWave::ForceFinish()
Old 02-14-2019 , 05:25   Re: [ANY] How to properly switch team
Reply With Quote #6

Quote:
Originally Posted by PC Gamer View Post
Benoist3012, tried your code. Works great. Thanks!
You're very much welcome.
__________________
Benoist3012 is offline
Benoist3012
Veteran Member
Join Date: Mar 2014
Location: CWave::ForceFinish()
Old 02-14-2019 , 05:32   Re: [ANY] How to properly switch team
Reply With Quote #7

Quote:
Originally Posted by ThatOneGuy View Post
may not be the same as what I put. Perhaps consider testing what has worked for me in several plugins before stating it untrue. Ignorance is not always bliss... (though we all eat those words at times).
Staying polite, and fully reading what the snippet is about would show you why I made this snippet.
It's aimed at people who can't use ChangeClientTeam due to undesired side effects. Force death is something you can avoid with the code you sent but it is not enough to avoid forced observer mode or whatever else the game code might have.
__________________
Benoist3012 is offline
ThatOneGuy
Veteran Member
Join Date: Jul 2012
Location: Oregon, USA
Old 02-15-2019 , 13:51   Re: [ANY] How to properly switch team
Reply With Quote #8

Quote:
Originally Posted by Benoist3012 View Post
Staying polite, and fully reading what the snippet is about would show you why I made this snippet.
It's aimed at people who can't use ChangeClientTeam due to undesired side effects. Force death is something you can avoid with the code you sent but it is not enough to avoid forced observer mode or whatever else the game code might have.
I had thought that avoiding death was the primary goal. Great work finding a solution.
__________________
ThatOneGuy is offline
Benoist3012
Veteran Member
Join Date: Mar 2014
Location: CWave::ForceFinish()
Old 02-15-2019 , 20:58   Re: [ANY] How to properly switch team
Reply With Quote #9

Quote:
Originally Posted by ThatOneGuy View Post
I had thought that avoiding death was the primary goal. Great work finding a solution.
Oh I just took this code part https://github.com/alliedmodders/hl2...pp#L7505#L7515
to be frank.

And yeah that's fine, perhaps I should have made the first post clearer.
__________________
Benoist3012 is offline
Whai
Senior Member
Join Date: Jul 2018
Old 03-13-2019 , 10:43   Re: [ANY] How to properly switch team
Reply With Quote #10

For switch to spectator team, you can just use that :

PHP Code:
TF2_ChangeClientTeam(clientTFTeam_Spectator);
SetEntProp(clientProp_Send"m_iTeamNum"0); //Change client team to Unassigned team, you can choose any team exept spectator if you want
TF2_RespawnPlayer(client);
TeleportEntity(clientfOriginfAnglesNULL_VECTOR); //fOrigin from GetClientAbsOrigin and fAngles from GetClientEyeAngles
SetEntProp(clientProp_Send"m_iTeamNum"1); 
because with your method, we have to check offsets every updates

If you want to change team but not in spectator, just use this

Quote:
Originally Posted by ThatOneGuy View Post
If the undesired affects you are referring to is the suicide, you can avoid that simply by doing something like the following:

PHP Code:
void ChangeTeam_NoKill(int clientint iTeam)
{
    if(
IsValidClient(client))
    {
        
int iEntProp GetEntProp(clientProp_Send"m_lifeState");
        
SetEntProp(clientProp_Send"m_lifeState"2);
        
ChangeClientTeam(clientiTeam);
        
SetEntProp(clientProp_Send"m_lifeState"iEntProp);
    }

Edit: I see that you state you cannot do it by changing the alive state. It has always worked for me.
__________________

Last edited by Whai; 03-13-2019 at 10:50.
Whai is offline
Reply


Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -4. The time now is 11:41.


Powered by vBulletin®
Copyright ©2000 - 2024, vBulletin Solutions, Inc.
Theme made by Freecode