This is a multi-part message in MIME format.
--
[ Picked text/plain from multipart/alternative ]
Excellent! This was frustrating me to no end. I've simply been trying to
tell if a player is connected or not. As you have indicated, when a player
disconnects, the pointer is still valid or not null. I'll stick the
FREE_PRIVATE(pClient) in the clientDisconnect function and see if it doesn't
clear things up.
I think you may have misunderstood some finer points of my last message. First,
adding the FREE_PRIVATE instruction will not fix your problem. It just prevents
problems with bots which is what I assumed you were trying to use when you mentioned
the IsNetClient() call. Let me clarify by first saying that IsNetClient () has nothing
to do with finding out if a client is connected or not. IsNetClient () is a virtual
function that will return true when the player is a human player and return false when
the player is a bot (FakeClient). It has nothing to do with the player's connect
status.
In order to keep track of who's connected or not, you need to know a couple of
things about how the HL engine manages clients. That way you can infer the connect
status even though there is no way to simple ask the engine. First thing you need to
know everything in HL is an edict_t including players. There is an array of edicts
that represent everything in the world including players. The first edict slot (slot
0) is for the worldspawn (the game level) I think. The next 32 slots in the edict
table (slots 1 through 32) are reserved for storing player info. Now this is where
things get messy. Every edict structure has a pointer to a block of memory called
pvPrivateData. This is the memory that holds the class data that the edict has been
assigned to. The edicts themselves have no class. So when an edict has been assigned
to a player, memory is allocated like so:
MyEdict->pvPrivateData = malloc ( sizeof (CBasePlayer) );
The CBasePlayer pointer you get is a copy of the pvPrivateData pointer that has been
type cast to (CBasePlayer *). When you call FREE_PRIVATE(pEdict), you're freeing the
memory assigned to pvPrivateData. But the old pointers are still around which can
cause problems.
There are two things going on here that are hurting you. First is that the engine
sometimes tries to reuse the memory allocated to another client that disconnect
earlier when a new client connects. This can be a source of confusion. It's because of
this that I suggested using the FREE_PRIVATE call to make sure any old clients get
completely cleared out. The engine will allocate new memory as needed when a new
client connects so this call doesn't hurt anything and prevents old data from previous
clients from causing problems.
The second problem is that you still don't have a way to tell when a player is
connected. This involves some trickery. At the end of the
CHalflifeMultiplay:ClientDisconnect() add these two lines:
pClient->v.netname = MAKE_STRING("");
FREE_PRIVATE(pClient);
This frees the memory allocated to the CBasePlayerClass and set's the player's name
to a NULL string. Doing this will allow us to write a routine that will tell us if a
player is connected. Here's the code:
bool IsConnected ( edict_t *plr )
{
int i;
edict_t *pEdict = g_engfuncs.pfnEntityOfEntIndex(1);
CBasePlayer *pPlayer;
if (!plr)
return FALSE;
for (i=1; i < gpGlobals->maxClients; i++, pEdict++)
{
if ((pEdict->v.flags & FL_CLIENT) == 0)
continue; // not a client
if (pEdict->free)
continue; // edict not in use.
if (pEdict == plr)
{
// we found the edict slot that is/was assigned to the player.
// Make some tests to see
// this slot is still active (player connected).
if (strlen(STRING(pEdict->v.netname)) == 0)
{
break; // player has no name assigned. not connected.
}
pPlayer = (CBasePlayer *) pEdict->pvPrivateData;
if (!pPlayer)
{
// player does not have private data assigned to him so he can't be
connected.
break;
}
if (&pEdict->v != pPlayer->pev)
{
// if the pev pointers in the edict and the CBasePlayer class don't
match,
// it means that this player's data has been assigned to someone
else. So
// this player edict is not active, hence player is not connected.
break;
}
if (pPlayer->pev->pContainingEntity != pEdict)
{
// player's pev structure is pointing back to the wrong edict.
Therefore
// this player's pev is out-of-date, hence player is not connected.
break;
}
// all the tests past. we can assume that this player
// is fully connected.
return TRUE;
}
}
// Player failed connected tests.
return FALSE;
}
}
bool IsConnected ( CBasePlayer *plr )
{
int i;
edict_t *pEdict = g_engfuncs.pfnEntityOfEntIndex(1);
CBasePlayer *pPlayer;
if (!plr)
return FALSE;
for (i=1; i < gpGlobals->maxClients; i++, pEdict++)
{
if ((pEdict->v.flags & FL_CLIENT) == 0)
continue; // not a client
if (pEdict->free)
continue; // edict not in use.
pPlayer = (CBasePlayer *) pEdict->pvPrivateData;
if (!pPlayer)
{
// no private data.
continue;
}
if ( pPlayer == plr)
{
// we found the edict slot that is/was assigned
// to the player. Make some tests to see
// this slot is still active (player connected).
if (&pEdict->v != pPlayer->pev)
{
continue;
}
if (pPlayer->pev->pContainingEntity != pEdict)
{
continue;
}
if (strlen(STRING(pEdict->v.netname)) == 0)
{
// player has no name assigned. not connected
continue;
}
// all the tests past. we can assume that this player is fully connected.
return TRUE;
}
}
// Player failed connected tests.
return FALSE;
}
}
Yep. It's a bit cheesy, but it's very reliable. Just don't allow clients to connect
with blank names.
--
_______________________________________________
To unsubscribe, edit your list preferences, or view the list archives, please visit:
http://list.valvesoftware.com/mailman/listinfo/hlcoders