Facepunch / garrysmod-issues

Garry's Mod issue tracker
145 stars 56 forks source link

SourceTV bot seems to be human in PlayerInitialSpawn and PlayerSpawn on the first time #5575

Closed JustPlayerDE closed 1 year ago

JustPlayerDE commented 1 year ago

Details

local testHooks = {
    "CheckPassword",
    "PlayerConnect",
    "NetworkIDValidated",
    "PlayerInitialSpawn",
    "PlayerAuthed",
    "PlayerSpawn"
}

for i,hookName in pairs(testHooks) do
    hook.Add(hookName, "TestSourceTVStuff", function(ply)
        print(hookName, ply, ply:Name(), (ply:IsBot() and "IsBot") or "IsHuman")
    end)
end

Output (when SourceTV spawns):

PlayerAuthed    Player [1][SourceTV]    SourceTV        IsHuman
PlayerInitialSpawn      Player [1][SourceTV]    SourceTV        IsHuman
PlayerSpawn     Player [1][SourceTV]    SourceTV        IsHuman

As you can see the server thinks it is not a Bot in these hooks when SourceTV spawns the first time, as soon as SourceTV dies and spawns again it will print IsBot on PlayerSpawn tho.

When i run it in the console it also says its a Bot:

> for i, ply in ipairs(player.GetAll()) do print("Console", ply, ply:Nick(), (ply:IsBot() and "IsBot") or "IsHuman") end...
Console      Player [1][SourceTV]    SourceTV        IsBot

Steps to reproduce

RaphaelIT7 commented 1 year ago

The SourceTV client is based off the CGameClient and because of this treated as a normal client.

// engine/sv_main.cpp (SV_ActivateServer)
// create new HLTV client
CGameClient *pClient = (CGameClient*)sv.CreateFakeClient( tv_name.GetString() );
hltv->StartMaster( pClient );

One way to solve this would probably be to have something like (CGameClient)pClient->SetBot(true) to force the client to be treated as a bot. (The function and all its logic CGameClient::IsBot would need to be implemented) Or you can let all clients created by CGameServer::CreateFakeClient be treated as a bot idk.

neico commented 1 year ago

I've run into this via mod code a while ago, since I don't have access to the engine code the earliest point to fix it was in ClientActivate (game/server/client.cpp, or a game specific one) using a simple trick:

void ClientActive( edict_t* pEdict, bool bLoadGame )
{
    CBasePlayer* pPlayer = nullptr;
    #if _DEBUG
        Assert( dynamic_cast<CBasePlayer*>( CBaseEntity::Instance( pEdict ) ) );
    #endif

    pPlayer = static_cast<CBasePlayer*>( CBaseEntity::Instance( pEdict ) );
    if( !pPlayer ) ClientPutInServer( pEdict );
    // Note: we have no player name set in rare cases where no proper `ClientPutInServer` was called, like for HLTV
    else if( V_strlen( pPlayer->GetPlayerName() ) <= 0 ) pPlayer->SetPlayerName( engine->GetClientConVarValue( pPlayer->entindex(), "name" ) );

    // Note: make sure we detect HLTV as a bot early
    ConVarRef hHLTVName( "tv_name" );
    if( hHLTVName.IsValid() && FStrEq( hHLTVName.GetString(), pPlayer->GetPlayerName() ) ) pPlayer->AddFlag( FL_FAKECLIENT );

    FinishClientPutInServer( pPlayer );
}

for gmod it can be properly set inside the hltv class just before ClientActive is being called