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

Reply via email to