From ee0867e45271c6864d9c505d343018ba2fa992ea Mon Sep 17 00:00:00 2001 From: /dev/humancontroller Date: Mon, 14 Aug 2017 14:10:11 +0200 Subject: implement the placeholder client shit TODO: to review, debug, etc this change; and amend this description --- src/game/g_public.h | 5 ++- src/server/server.h | 5 +++ src/server/sv_ccmds.c | 19 +++++++--- src/server/sv_client.c | 93 ++++++++++++++++++++++++++++++++++++++++++++---- src/server/sv_game.c | 25 +++++++++++++ src/server/sv_init.c | 20 ++++++++++- src/server/sv_main.c | 41 +++++++++++++-------- src/server/sv_snapshot.c | 9 +++++ 8 files changed, 190 insertions(+), 27 deletions(-) diff --git a/src/game/g_public.h b/src/game/g_public.h index 2e280a2d..c9fb046e 100644 --- a/src/game/g_public.h +++ b/src/game/g_public.h @@ -227,7 +227,10 @@ typedef enum { G_SEND_GAMESTAT, G_ADDCOMMAND, - G_REMOVECOMMAND + G_REMOVECOMMAND, + + G_INSTALL_PLACEHOLDER_CLIENT, + G_SET_CLIENT_VIEW_ENTITY, } gameImport_t; diff --git a/src/server/server.h b/src/server/server.h index 0e676e94..d07c2c94 100644 --- a/src/server/server.h +++ b/src/server/server.h @@ -134,6 +134,7 @@ typedef struct netchan_buffer_s { typedef struct client_s { clientState_t state; + qboolean isPlaceholder; char userinfo[MAX_INFO_STRING]; // name, etc char reliableCommands[MAX_RELIABLE_COMMANDS][MAX_STRING_CHARS]; @@ -150,6 +151,7 @@ typedef struct client_s { int lastClientCommand; // reliable client message sequence char lastClientCommandString[MAX_STRING_CHARS]; sharedEntity_t *gentity; // SV_GentityNum(clientnum) + struct client_s *view; char name[MAX_NAME_LENGTH]; // extracted from userinfo, high bits masked // downloading @@ -361,6 +363,9 @@ int SV_WriteDownloadToClient(client_t *cl , msg_t *msg); int SV_SendDownloadMessages(void); int SV_SendQueuedMessages(void); +int sv_install_placeholder_client(const char *userinfo); +void sv_set_client_view_entity(client_t *cortex, client_t *eye); + // // sv_ccmds.c diff --git a/src/server/sv_ccmds.c b/src/server/sv_ccmds.c index 21a5f0e5..688f20b3 100644 --- a/src/server/sv_ccmds.c +++ b/src/server/sv_ccmds.c @@ -162,7 +162,10 @@ static void SV_MapRestart_f( void ) { // to give them the correct time so that when they finish loading // they don't violate the backwards time check in cl_cgame.c for (i=0 ; iinteger ; i++) { + svs.clients[i].view = NULL; if (svs.clients[i].state == CS_PRIMED) { + //if (svs.clients[i].isPlaceholder) + //continue; svs.clients[i].oldServerTime = sv.restartTime; } } @@ -195,11 +198,14 @@ static void SV_MapRestart_f( void ) { continue; } - // add the map_restart command - SV_AddServerCommand( client, "map_restart\n" ); + if (!client->isPlaceholder) + { + // add the map_restart command + SV_AddServerCommand( client, "map_restart\n" ); + } // connect the client again, without the firstTime flag - denied = VM_ExplicitArgPtr( gvm, VM_Call( gvm, GAME_CLIENT_CONNECT, i, qfalse ) ); + denied = VM_ExplicitArgPtr( gvm, VM_Call( gvm, GAME_CLIENT_CONNECT, i, qfalse, client->isPlaceholder ) ); if ( denied ) { // this generally shouldn't happen, because the client // was connected before the level change @@ -208,7 +214,12 @@ static void SV_MapRestart_f( void ) { continue; } - if(client->state == CS_ACTIVE) + if (client->isPlaceholder) + { + client->gentity = SV_GentityNum(i); + client->gentity->s.number = i; + } + else if (client->state == CS_ACTIVE) SV_ClientEnterWorld(client, &client->lastUsercmd); else { diff --git a/src/server/sv_client.c b/src/server/sv_client.c index f3a0494f..9c0f842d 100644 --- a/src/server/sv_client.c +++ b/src/server/sv_client.c @@ -167,6 +167,8 @@ void SV_DirectConnect( netadr_t from ) { if ( cl->state == CS_FREE ) { continue; } + if (cl->isPlaceholder) + continue; if ( NET_CompareBaseAdr( from, cl->netchan.remoteAddress ) && ( cl->netchan.qport == qport || from.port == cl->netchan.remoteAddress.port ) ) { @@ -250,6 +252,8 @@ void SV_DirectConnect( netadr_t from ) { if ( cl->state == CS_FREE ) { continue; } + if (cl->isPlaceholder) + continue; if ( NET_CompareBaseAdr( from, cl->netchan.remoteAddress ) && ( cl->netchan.qport == qport || from.port == cl->netchan.remoteAddress.port ) ) { @@ -333,7 +337,7 @@ gotnewcl: Q_strncpyz( newcl->userinfo, userinfo, sizeof(newcl->userinfo) ); // get the game a chance to reject this connection or modify the userinfo - denied = VM_Call( gvm, GAME_CLIENT_CONNECT, clientNum, qtrue ); // firstTime = qtrue + denied = VM_Call( gvm, GAME_CLIENT_CONNECT, clientNum, qtrue, qfalse ); // firstTime = qtrue if ( denied ) { // we can't just use VM_ArgPtr, because that is only valid inside a VM_Call char *str = VM_ExplicitArgPtr( gvm, denied ); @@ -373,6 +377,57 @@ gotnewcl: } } +int sv_install_placeholder_client(const char *const ui) +{ + for (int n = (sv_privateClients->integer > 0 && strcmp(Info_ValueForKey(ui, "password"), sv_privatePassword->string) + ? sv_privateClients->integer : 0); + n < sv_maxclients->integer; ++n) + { + client_t *cl = &svs.clients[n]; + if (cl->state != CS_FREE) + continue; + + memset(cl, 0, sizeof(*cl)); + + cl->state = CS_ACTIVE; + cl->isPlaceholder = qtrue; + + cl->gentity = SV_GentityNum(n); + cl->gentity->s.number = n; + + Q_strncpyz(cl->userinfo, ui, sizeof(cl->userinfo)); + Q_strncpyz(cl->name, Info_ValueForKey(ui, "name"), sizeof(cl->name)); + cl->ping = atoi(Info_ValueForKey(ui, "ping")); + + cl->gotCP = qtrue; + cl->pureAuthentic = qtrue; + + Cvar_Set(va("sv_clAltProto%i", n), "0"); + + int count = 0; + for (int i = 0; i < sv_maxclients->integer; ++i) + { + if (svs.clients[i].state >= CS_CONNECTED) + ++count; + } + if (count == 1 || count == sv_maxclients->integer) + SV_Heartbeat_f(); + + return n; + } + + return -1; +} + +void sv_set_client_view_entity(client_t *const c, client_t *const e) +{ + if (e == c) + c->view = NULL; + else + c->view = e; +} + + /* ===================== SV_FreeClient @@ -382,6 +437,9 @@ Destructor for data allocated in a client structure */ void SV_FreeClient(client_t *client) { + if (client->isPlaceholder) + return; + #ifdef USE_VOIP int index; @@ -416,13 +474,16 @@ void SV_DropClient( client_t *drop, const char *reason ) { return; // already dropped } - // see if we already have a challenge for this ip - challenge = &svs.challenges[0]; + if (!drop->isPlaceholder) + { + // see if we already have a challenge for this ip + challenge = &svs.challenges[0]; - for (i = 0 ; i < MAX_CHALLENGES ; i++, challenge++) { - if ( NET_CompareAdr( drop->netchan.remoteAddress, challenge->adr ) ) { - Com_Memset(challenge, 0, sizeof(*challenge)); - break; + for (i = 0 ; i < MAX_CHALLENGES ; i++, challenge++) { + if ( NET_CompareAdr( drop->netchan.remoteAddress, challenge->adr ) ) { + Com_Memset(challenge, 0, sizeof(*challenge)); + break; + } } } @@ -444,6 +505,18 @@ void SV_DropClient( client_t *drop, const char *reason ) { Com_DPrintf( "Going to CS_ZOMBIE for %s\n", drop->name ); drop->state = CS_ZOMBIE; // become free in a few seconds + if (drop->isPlaceholder) + drop->state = CS_FREE; + + for (int i = 0; i < sv_maxclients->integer; ++i) + { + client_t *cl = &svs.clients[i]; + if (cl->state == CS_FREE) + continue; + + if (cl->view == drop) + cl->view = NULL; + } // if this was the last client on the server, send a heartbeat // to the master so it is known the server is empty @@ -886,6 +959,8 @@ int SV_SendQueuedMessages(void) { cl = &svs.clients[i]; + if (cl->isPlaceholder) + continue; if(cl->state) { nextFragT = SV_RateMsec(cl); @@ -921,6 +996,8 @@ int SV_SendDownloadMessages(void) { cl = &svs.clients[i]; + if (cl->isPlaceholder) + continue; if(cl->state && *cl->downloadName) { MSG_Init(&msg, msgBuffer, sizeof(msgBuffer)); @@ -1594,6 +1671,8 @@ void SV_UserVoip(client_t *cl, msg_t *msg, qboolean ignoreData) for (i = 0, client = svs.clients; i < sv_maxclients->integer ; i++, client++) { if (client->state != CS_ACTIVE) continue; // not in the game yet, don't send to this guy. + if (client->isPlaceholder) + continue; else if (i == sender) continue; // don't send voice packet back to original author. else if (!client->hasVoip) diff --git a/src/server/sv_game.c b/src/server/sv_game.c index 734d4559..0cf5eb4d 100644 --- a/src/server/sv_game.c +++ b/src/server/sv_game.c @@ -271,6 +271,24 @@ void SV_GetUsercmd( int clientNum, usercmd_t *cmd ) { *cmd = svs.clients[clientNum].lastUsercmd; } +static int svg_install_placeholder_client(const char *ui) +{ + if (!ui) + Com_Error(ERR_DROP, "install_placeholder_client(): NULL userinfo argument"); + return sv_install_placeholder_client(ui); +} + +static void svg_set_client_view_entity(int c, int e) +{ + if (c < 0 || c >= sv_maxclients->integer || svs.clients[c].state < CS_PRIMED + || (c != e && (e < 0 || e >= sv_maxclients->integer || svs.clients[e].state < CS_ACTIVE))) + { + Com_Error(ERR_DROP, "set_client_view_entity(): invalid arguments"); + } + + sv_set_client_view_entity(&svs.clients[c], &svs.clients[e]); +} + //============================================== static int FloatAsInt( float f ) { @@ -441,6 +459,13 @@ intptr_t SV_GameSystemCalls( intptr_t *args ) { Cmd_RemoveCommand( VMA(1) ); return 0; + case G_INSTALL_PLACEHOLDER_CLIENT: + return svg_install_placeholder_client(VMA(1)); + + case G_SET_CLIENT_VIEW_ENTITY: + svg_set_client_view_entity(args[1], args[2]); + return 0; + case TRAP_MEMSET: Com_Memset( VMA(1), args[2], args[3] ); return 0; diff --git a/src/server/sv_init.c b/src/server/sv_init.c index 236c3d98..316c71a1 100644 --- a/src/server/sv_init.c +++ b/src/server/sv_init.c @@ -196,6 +196,8 @@ void SV_SetConfigstring (int index, const char *val) { // send the data to all relevent clients for (i = 0, client = svs.clients; i < sv_maxclients->integer ; i++, client++) { + if (client->isPlaceholder) + continue; if ( index <= CS_SYSTEMINFO && !modified[ client->netchan.alternateProtocol ] ) { continue; } @@ -250,6 +252,8 @@ void SV_SetConfigstringRestrictions (int index, const clientList_t* clientList) for ( i = 0 ; i < sv_maxclients->integer ; i++ ) { if ( svs.clients[i].state >= CS_CONNECTED ) { + if (svs.clients[i].isPlaceholder) + continue; if ( Com_ClientListContains( &oldClientList, i ) != Com_ClientListContains( clientList, i ) ) { // A client has left or joined the restricted list, so update @@ -277,6 +281,9 @@ void SV_SetUserinfo( int index, const char *val ) { Q_strncpyz( svs.clients[index].userinfo, val, sizeof( svs.clients[ index ].userinfo ) ); Q_strncpyz( svs.clients[index].name, Info_ValueForKey( val, "name" ), sizeof(svs.clients[index].name) ); + + if (svs.clients[index].isPlaceholder) + svs.clients[index].ping = atoi(Info_ValueForKey(val, "ping")); } @@ -542,8 +549,11 @@ void SV_SpawnServer( char *server, qboolean killBots ) { svs.snapFlagServerBit ^= SNAPFLAG_SERVERCOUNT; for (i=0 ; iinteger ; i++) { + svs.clients[i].view = NULL; // save when the server started for each client already connected if (svs.clients[i].state >= CS_CONNECTED) { + if (svs.clients[i].isPlaceholder) + continue; svs.clients[i].oldServerTime = sv.time; } } @@ -610,7 +620,7 @@ void SV_SpawnServer( char *server, qboolean killBots ) { char *denied; // connect the client again - denied = VM_ExplicitArgPtr( gvm, VM_Call( gvm, GAME_CLIENT_CONNECT, i, qfalse ) ); // firstTime = qfalse + denied = VM_ExplicitArgPtr( gvm, VM_Call( gvm, GAME_CLIENT_CONNECT, i, qfalse, svs.clients[i].isPlaceholder ) ); // firstTime = qfalse if ( denied ) { // this generally shouldn't happen, because the client // was connected before the level change @@ -619,6 +629,12 @@ void SV_SpawnServer( char *server, qboolean killBots ) { // when we get the next packet from a connected client, // the new gamestate will be sent svs.clients[i].state = CS_CONNECTED; + if (svs.clients[i].isPlaceholder) + { + svs.clients[i].state = CS_ACTIVE; + svs.clients[i].gentity = SV_GentityNum(i); + svs.clients[i].gentity->s.number = i; + } } } } @@ -787,6 +803,8 @@ void SV_FinalMessage( char *message ) { for ( j = 0 ; j < 2 ; j++ ) { for (i=0, cl = svs.clients ; i < sv_maxclients->integer ; i++, cl++) { if (cl->state >= CS_CONNECTED) { + if (cl->isPlaceholder) + continue; // don't send a disconnect to a local client if ( cl->netchan.remoteAddress.type != NA_LOOPBACK ) { SV_SendServerCommand( cl, "print \"%s\n\"\n", message ); diff --git a/src/server/sv_main.c b/src/server/sv_main.c index 8d75d55f..860a0cdb 100644 --- a/src/server/sv_main.c +++ b/src/server/sv_main.c @@ -176,6 +176,9 @@ void QDECL SV_SendServerCommand(client_t *cl, const char *fmt, ...) { client_t *client; int j; + if (cl && cl->isPlaceholder) + return; + va_start (argptr,fmt); Q_vsnprintf ((char *)message, sizeof(message), fmt,argptr); va_end (argptr); @@ -200,6 +203,8 @@ void QDECL SV_SendServerCommand(client_t *cl, const char *fmt, ...) { // send the data to all relevent clients for (j = 0, client = svs.clients; j < sv_maxclients->integer ; j++, client++) { + if (client->isPlaceholder) + continue; SV_AddServerCommand( client, (char *)message ); } } @@ -851,6 +856,8 @@ void SV_PacketEvent( netadr_t from, msg_t *msg ) { if (cl->state == CS_FREE) { continue; } + if (cl->isPlaceholder) + continue; if ( !NET_CompareBaseAdr( from, cl->netchan.remoteAddress ) ) { continue; } @@ -908,22 +915,25 @@ static void SV_CalcPings( void ) { continue; } - total = 0; - count = 0; - for ( j = 0 ; j < PACKET_BACKUP ; j++ ) { - if ( cl->frames[j].messageAcked <= 0 ) { - continue; + if (!cl->isPlaceholder) + { + total = 0; + count = 0; + for ( j = 0 ; j < PACKET_BACKUP ; j++ ) { + if ( cl->frames[j].messageAcked <= 0 ) { + continue; + } + delta = cl->frames[j].messageAcked - cl->frames[j].messageSent; + count++; + total += delta; } - delta = cl->frames[j].messageAcked - cl->frames[j].messageSent; - count++; - total += delta; - } - if (!count) { - cl->ping = 999; - } else { - cl->ping = total/count; - if ( cl->ping > 999 ) { + if (!count) { cl->ping = 999; + } else { + cl->ping = total/count; + if ( cl->ping > 999 ) { + cl->ping = 999; + } } } @@ -956,6 +966,9 @@ static void SV_CheckTimeouts( void ) { zombiepoint = svs.time - 1000 * sv_zombietime->integer; for (i=0,cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++) { + if (cl->isPlaceholder) + continue; + // message times may be wrong across a changelevel if (cl->lastPacketTime > svs.time) { cl->lastPacketTime = svs.time; diff --git a/src/server/sv_snapshot.c b/src/server/sv_snapshot.c index 09633eb7..0bde3d51 100644 --- a/src/server/sv_snapshot.c +++ b/src/server/sv_snapshot.c @@ -473,6 +473,12 @@ static void SV_BuildClientSnapshot( client_t *client ) { return; } + if (client->view) + { + client = client->view; + clent = client->gentity; + } + // grab the current playerState_t ps = SV_GameClientNum( client - svs.clients ); frame->ps = *ps; @@ -657,6 +663,9 @@ void SV_SendClientMessages(void) if(!c->state) continue; // not connected + if (c->isPlaceholder) + continue; + if(*c->downloadName) continue; // Client is downloading, don't send snapshots -- cgit