PDA

View Full Version : ClientDisconnect() is driving me NUTS!!!


Pierre-Marie Baty
02-01-2004, 20:03
Okay, here's again one of the weirdest bugs ever - those who drive you mad for hours bunking your face on walls until it turns into an obnoxious red/grey mixture.

I use ClientDisconnect() in my bot code to keep track of bots and players disconnecting, in order to reset their structures and/or adapt the players and bot counts, and other stuff, etc, etc.

Here's a typical ClientDisconnect() function from one of my bot DLLs (in this case, it's a The Specialists version, but it's the same for the other MODs)

void ClientDisconnect (edict_t *pEntity)
{
// this function is called whenever a client is VOLUNTARILY disconnected from the server,
// either because the client dropped the connection, or because the server dropped him from
// the game (latency timeout). The effect is the freeing of a client slot on the server. Note
// that clients and bots disconnected because of a level change NOT NECESSARILY call this
// function, because in case of a level change, it's a server shutdown, and not a normal
// disconnection. I find that completely stupid, but that's it. Anyway it's time to update
// the bots and players counts, and in case the client disconnecting is a bot, to back its
// brain(s) up to disk. We also try to notice when a listenserver client disconnects, so as
// to reset his entity pointer for safety. There are still a few server frames to go once a
// listen server client disconnects, and we don't want to send him any sort of message then.
printf ("RACC BOT DISCONNECTING\n");
// only process bots if we are in multiplayer mode
if (server.is_multiplayer && !DebugLevel.is_paused)
{
int player_index;

player_index = ENTINDEX (pEntity) - 1; // get this player's index
bots[player_index].is_active = FALSE; // mark this bot slot as free
// was this client a bot ?
if (players[player_index].is_racc_bot)
{
if (!FNullEnt (bots[player_index].pIllumination))
bots[player_index].pIllumination->v.flags |= FL_KILLME; // kill its light entity
BotShutdownPathMachine (&bots[player_index]); // shutdown our bot's pathmachine
BotNavSaveBrain (&bots[player_index]); // save our bot's nav brain
BotHALSaveBrain (&bots[player_index]); // save our bot's HAL brain
bot_count--; // decrement the bot count as we know this bot is disconnecting
}
memset (&players[player_index], 0, sizeof (players[player_index])); // reset his structure
players[player_index].pEntity = NULL; // clear his entity pointer
player_count--; // decrement the player count as we know this client is disconnected
}
(*other_gFunctionTable.pfnClientDisconnect) (pEntity);
}

....ah yes, because perhaps I never annoucned it yet, but the next release of my bot will probably support The Specialists :D

Now THE PROBLEM, is that, as the commenting says, ClientDisconnect() doesn't seem to be called each time someone is disconnected.

It looks like if you use the 'kick' command, ClientDisconnect() is not called. Neither is it on map changes (but NOT all cases, and THAT's the weirdest thing !!!)

I am completely puzzled. Is there a reason and eventually a fix to apply somewhere or do I need to move all the connecting/disconnecting stuff to the beginning of StartFrame ??? That would be more portable perhaps but since these functions are provided for this purpose it would be a shame not to use them.. ???:(

stefanhendriks
02-01-2004, 20:33
perhaps you can somehow intercept the 'kick' and 'map' command. THen you force your own function being called to make sure your bot is kicked in your bot.dll as well.

Its an ugly hack, but it should work.

Lazy
02-01-2004, 20:52
Just a thought - but maybe FreeEntPrivateData gets called on a disconnect.
I'd look into it but I'm eating a sandwich now.

Pierre-Marie Baty
03-01-2004, 05:11
Nevermind...

I moved all my connect/disconnect stuff to StartFrame.

Took me a helluva time (especially when you try to get things working and a pesky dutchman keeps sending you rows of PMs for IRC every minute ;)), but I did it!!!!!

/me bows to avoid the flying dutchman :D

botmeister
03-01-2004, 10:33
ClientDisconnect gets called only for players that actually spawned into the game, but I suppose you already know this. I use the same function call to test for disconnects, and as far as I know it works all the time.

stefanhendriks
03-01-2004, 11:34
glad it works now pmb. I will try to send 2 pm's within a minute now instead of 1 ;)

Cheeseh
07-01-2004, 18:57
I am also needing to store the amount of players. But I am in deeper crap because I want to store the amount of players trying to connect as well.

I need this so that my max bots command will work correctly.

What does the server use to get the amount of players when you use the "status" command??

Cheeseh
07-01-2004, 19:16
btw PM, you can sort the problem you have by doing something like this if you want to get the number of clients playing.

getPlayerUserId will tell you if the player is in the server

but not if its joining, which is what I really need to know

:o hold on.. i think it returns 0 when a player is not in the server, maybe its -1 if connecting.. gonna check.

int UTIL_GetNumClients ( void )
{
int i = 0;

edict_t *pPlayer;
int iNum;

iNum = 0;

for ( i = 1; i <= gpGlobals->maxClients; i ++ )
{
pPlayer = INDEXENT(i);

if ( pPlayer == NULL )
continue;
if ( pPlayer->free )
continue;

if ( (*g_engfuncs.pfnGetPlayerUserId)(pPlayer) > 0 )
iNum++;
}

return iNum;
}

Pierre-Marie Baty
07-01-2004, 19:28
All I can say, is that as soon as the engine is notified that a new player tries to connect, it calls ClientConnect() in the game DLL ; and in the case where the connection established successfully, when the new player finished downloading all the resources and stuff, then the engine allows the new player in game and immediately calls ClientPutInServer() in the game DLL.

Unfortunately if the equivalent to ClientPutInServer() is ClientDisconnect(), there is no equivalent for ClientConnect() and thus you can't know if a user who attempted to connect has been dropped by the server or cancelled the connection himself. You're left to put timeouts yourself for each of your players in ClientConnect(), and inhibite these timeouts once ClientPutInServer() has been called for them.

Cheeseh
07-01-2004, 19:40
Although I don't think the time outs will work if a player is still downloading resources or maps or whatever, you really need to check this some how I do'n't think you can :|

koraX
07-01-2004, 22:07
Now THE PROBLEM, is that, as the commenting says, ClientDisconnect() doesn't seem to be called each time someone is disconnected.

It looks like ...can this be reproduced or does it happen randomly ?
If it can be reproduced, we can log every engine and mod function that is called, check edict number and see if something is called when this occurs

botmeister
07-01-2004, 22:24
I'd like to know to since I'm relying on it working 100% for some things. So far I've seen no indication that is is not working perfectly, and I've been using this function hook for a long time now.

Pierre-Marie Baty
07-01-2004, 22:56
Sorry, I couldn't give you guys much more info, since now all my connecting/disconnecting stuff has been moved into StartFrame(). Anyway I'm quite sure it DOES NOT happen at random, there must be a reason for it... but so far, I have not been able to find out what.