summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/game/g_public.h5
-rw-r--r--src/server/server.h5
-rw-r--r--src/server/sv_ccmds.c19
-rw-r--r--src/server/sv_client.c93
-rw-r--r--src/server/sv_game.c25
-rw-r--r--src/server/sv_init.c20
-rw-r--r--src/server/sv_main.c41
-rw-r--r--src/server/sv_snapshot.c9
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 ; i<sv_maxclients->integer ; 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 ; i<sv_maxclients->integer ; 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