AlliedModders

AlliedModders (https://forums.alliedmods.net/index.php)
-   Snippets and Tutorials (https://forums.alliedmods.net/forumdisplay.php?f=112)
-   -   [ANY] How to properly switch team (https://forums.alliedmods.net/showthread.php?t=314271)

Benoist3012 02-11-2019 03:26

[ANY] How to properly switch team
 
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 :crab:
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 :bacon:

ThatOneGuy 02-13-2019 01:15

Re: [ANY] How to properly switch team
 
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.

Benoist3012 02-13-2019 03:55

Re: [ANY] How to properly switch team
 
Quote:

Originally Posted by ThatOneGuy (Post 2639398)
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 (Post 2639134)
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

PC Gamer 02-13-2019 12:54

Re: [ANY] How to properly switch team
 
Benoist3012, tried your code. Works great. Thanks!

ThatOneGuy 02-13-2019 23:34

Re: [ANY] How to properly switch team
 
Quote:

Originally Posted by Benoist3012 (Post 2639423)

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).

Benoist3012 02-14-2019 05:25

Re: [ANY] How to properly switch team
 
Quote:

Originally Posted by PC Gamer (Post 2639492)
Benoist3012, tried your code. Works great. Thanks!

You're very much welcome.:bacon!:

Benoist3012 02-14-2019 05:32

Re: [ANY] How to properly switch team
 
Quote:

Originally Posted by ThatOneGuy (Post 2639540)
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. :nono:

ThatOneGuy 02-15-2019 13:51

Re: [ANY] How to properly switch team
 
Quote:

Originally Posted by Benoist3012 (Post 2639565)
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. :nono:

I had thought that avoiding death was the primary goal. Great work finding a solution.

Benoist3012 02-15-2019 20:58

Re: [ANY] How to properly switch team
 
Quote:

Originally Posted by ThatOneGuy (Post 2639749)
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. :bee:

And yeah that's fine, perhaps I should have made the first post clearer.

Whai 03-13-2019 10:43

Re: [ANY] How to properly switch team
 
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 (Post 2639398)
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.



All times are GMT -4. The time now is 02:51.

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