2
===========================================================================
3
Copyright (C) 1999-2005 Id Software, Inc.
5
This file is part of Quake III Arena source code.
7
Quake III Arena source code is free software; you can redistribute it
8
and/or modify it under the terms of the GNU General Public License as
9
published by the Free Software Foundation; either version 2 of the License,
10
or (at your option) any later version.
12
Quake III Arena source code is distributed in the hope that it will be
13
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
GNU General Public License for more details.
17
You should have received a copy of the GNU General Public License
18
along with Quake III Arena source code; if not, write to the Free Software
19
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20
===========================================================================
22
// sv_client.c -- server code for dealing with clients
26
static void SV_CloseDownload( client_t *cl );
32
A "getchallenge" OOB command has been received
33
Returns a challenge number that can be used
34
in a subsequent connectResponse command.
35
We do this to prevent denial of service attacks that
36
flood the server with invalid connection IPs. With a
37
challenge, they must give a valid IP address.
39
If we are authorizing, a challenge request will cause a packet
40
to be sent to the authorize server.
42
When an authorizeip is returned, a challenge response will be
46
void SV_GetChallenge( netadr_t from ) {
50
challenge_t *challenge;
52
// ignore if we are in single player
53
if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER || Cvar_VariableValue("ui_singlePlayerActive")) {
58
oldestTime = 0x7fffffff;
60
// see if we already have a challenge for this ip
61
challenge = &svs.challenges[0];
62
for (i = 0 ; i < MAX_CHALLENGES ; i++, challenge++) {
63
if ( !challenge->connected && NET_CompareAdr( from, challenge->adr ) ) {
66
if ( challenge->time < oldestTime ) {
67
oldestTime = challenge->time;
72
if (i == MAX_CHALLENGES) {
73
// this is the first time this client has asked for a challenge
74
challenge = &svs.challenges[oldest];
76
challenge->challenge = ( (rand() << 16) ^ rand() ) ^ svs.time;
77
challenge->adr = from;
78
challenge->firstTime = svs.time;
79
challenge->time = svs.time;
80
challenge->connected = qfalse;
84
// if they are on a lan address, send the challengeResponse immediately
85
if ( Sys_IsLANAddress( from ) ) {
86
challenge->pingTime = svs.time;
87
NET_OutOfBandPrint( NS_SERVER, from, "challengeResponse %i", challenge->challenge );
91
// if there's no authorize server defined, just let them in
92
if(strlen(AUTHORIZE_SERVER_NAME) < 1)
94
Com_Printf("Not authorizing client due to lack of auth server\n");
95
challenge->pingTime = svs.time;
96
NET_OutOfBandPrint( NS_SERVER, from, "challengeResponse %i", challenge->challenge );
100
// look up the authorize server's IP
101
if ( !svs.authorizeAddress.ip[0] && svs.authorizeAddress.type != NA_BAD ) {
102
Com_Printf( "Resolving %s\n", AUTHORIZE_SERVER_NAME );
103
if ( !NET_StringToAdr( AUTHORIZE_SERVER_NAME, &svs.authorizeAddress ) ) {
104
Com_Printf( "Couldn't resolve address\n" );
107
svs.authorizeAddress.port = BigShort( PORT_AUTHORIZE );
108
Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", AUTHORIZE_SERVER_NAME,
109
svs.authorizeAddress.ip[0], svs.authorizeAddress.ip[1],
110
svs.authorizeAddress.ip[2], svs.authorizeAddress.ip[3],
111
BigShort( svs.authorizeAddress.port ) );
114
// if they have been challenging for a long time and we
115
// haven't heard anything from the authorize server, go ahead and
116
// let them in, assuming the id server is down
117
if ( svs.time - challenge->firstTime > AUTHORIZE_TIMEOUT ) {
118
Com_DPrintf( "authorize server timed out\n" );
120
challenge->pingTime = svs.time;
121
NET_OutOfBandPrint( NS_SERVER, challenge->adr,
122
"challengeResponse %i", challenge->challenge );
126
// otherwise send their ip to the authorize server
127
if ( svs.authorizeAddress.type != NA_BAD ) {
131
Com_DPrintf( "sending getIpAuthorize for %s\n", NET_AdrToString( from ));
133
strcpy(game, BASEGAME);
134
fs = Cvar_Get ("fs_game", "", CVAR_INIT|CVAR_SYSTEMINFO );
135
if (fs && fs->string[0] != 0) {
136
strcpy(game, fs->string);
139
// the 0 is for backwards compatibility with obsolete sv_allowanonymous flags
140
// getIpAuthorize <challenge> <IP> <game> 0 <auth-flag>
141
NET_OutOfBandPrint( NS_SERVER, svs.authorizeAddress,
142
"getIpAuthorize %i %i.%i.%i.%i %s 0 %s", svs.challenges[i].challenge,
143
from.ip[0], from.ip[1], from.ip[2], from.ip[3], game, sv_strictAuth->string );
151
A packet has been returned from the authorize server.
152
If we have a challenge adr for that ip, send the
153
challengeResponse to it
156
void SV_AuthorizeIpPacket( netadr_t from ) {
163
if ( !NET_CompareBaseAdr( from, svs.authorizeAddress ) ) {
164
Com_Printf( "SV_AuthorizeIpPacket: not from authorize server\n" );
168
challenge = atoi( Cmd_Argv( 1 ) );
170
for (i = 0 ; i < MAX_CHALLENGES ; i++) {
171
if ( svs.challenges[i].challenge == challenge ) {
175
if ( i == MAX_CHALLENGES ) {
176
Com_Printf( "SV_AuthorizeIpPacket: challenge not found\n" );
180
// send a packet back to the original client
181
svs.challenges[i].pingTime = svs.time;
183
r = Cmd_Argv( 3 ); // reason
185
if ( !Q_stricmp( s, "demo" ) ) {
186
if ( Cvar_VariableValue( "fs_restrict" ) ) {
187
// a demo client connecting to a demo server
188
NET_OutOfBandPrint( NS_SERVER, svs.challenges[i].adr,
189
"challengeResponse %i", svs.challenges[i].challenge );
192
// they are a demo client trying to connect to a real server
193
NET_OutOfBandPrint( NS_SERVER, svs.challenges[i].adr, "print\nServer is not a demo server\n" );
194
// clear the challenge record so it won't timeout and let them through
195
Com_Memset( &svs.challenges[i], 0, sizeof( svs.challenges[i] ) );
198
if ( !Q_stricmp( s, "accept" ) ) {
199
NET_OutOfBandPrint( NS_SERVER, svs.challenges[i].adr,
200
"challengeResponse %i", svs.challenges[i].challenge );
203
if ( !Q_stricmp( s, "unknown" ) ) {
205
NET_OutOfBandPrint( NS_SERVER, svs.challenges[i].adr, "print\nAwaiting CD key authorization\n" );
207
sprintf(ret, "print\n%s\n", r);
208
NET_OutOfBandPrint( NS_SERVER, svs.challenges[i].adr, ret );
210
// clear the challenge record so it won't timeout and let them through
211
Com_Memset( &svs.challenges[i], 0, sizeof( svs.challenges[i] ) );
215
// authorization failed
217
NET_OutOfBandPrint( NS_SERVER, svs.challenges[i].adr, "print\nSomeone is using this CD Key\n" );
219
sprintf(ret, "print\n%s\n", r);
220
NET_OutOfBandPrint( NS_SERVER, svs.challenges[i].adr, ret );
223
// clear the challenge record so it won't timeout and let them through
224
Com_Memset( &svs.challenges[i], 0, sizeof( svs.challenges[i] ) );
231
A "connect" OOB command has been received
235
#define PB_MESSAGE "PunkBuster Anti-Cheat software must be installed " \
236
"and Enabled in order to join this server. An updated game patch can be downloaded from " \
239
void SV_DirectConnect( netadr_t from ) {
240
char userinfo[MAX_INFO_STRING];
242
client_t *cl, *newcl;
254
Com_DPrintf ("SVC_DirectConnect ()\n");
256
Q_strncpyz( userinfo, Cmd_Argv(1), sizeof(userinfo) );
258
version = atoi( Info_ValueForKey( userinfo, "protocol" ) );
259
if ( version != PROTOCOL_VERSION ) {
260
NET_OutOfBandPrint( NS_SERVER, from, "print\nServer uses protocol version %i.\n", PROTOCOL_VERSION );
261
Com_DPrintf (" rejected connect from version %i\n", version);
265
challenge = atoi( Info_ValueForKey( userinfo, "challenge" ) );
266
qport = atoi( Info_ValueForKey( userinfo, "qport" ) );
269
for (i=0,cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++) {
270
if ( cl->state == CS_FREE ) {
273
if ( NET_CompareBaseAdr( from, cl->netchan.remoteAddress )
274
&& ( cl->netchan.qport == qport
275
|| from.port == cl->netchan.remoteAddress.port ) ) {
276
if (( svs.time - cl->lastConnectTime)
277
< (sv_reconnectlimit->integer * 1000)) {
278
Com_DPrintf ("%s:reconnect rejected : too soon\n", NET_AdrToString (from));
285
// see if the challenge is valid (LAN clients don't need to challenge)
286
if ( !NET_IsLocalAddress (from) ) {
289
for (i=0 ; i<MAX_CHALLENGES ; i++) {
290
if (NET_CompareAdr(from, svs.challenges[i].adr)) {
291
if ( challenge == svs.challenges[i].challenge ) {
296
if (i == MAX_CHALLENGES) {
297
NET_OutOfBandPrint( NS_SERVER, from, "print\nNo or bad challenge for address.\n" );
300
// force the IP key/value pair so the game can filter based on ip
301
Info_SetValueForKey( userinfo, "ip", NET_AdrToString( from ) );
303
ping = svs.time - svs.challenges[i].pingTime;
304
Com_Printf( "Client %i connecting with %i challenge ping\n", i, ping );
305
svs.challenges[i].connected = qtrue;
307
// never reject a LAN client based on ping
308
if ( !Sys_IsLANAddress( from ) ) {
309
if ( sv_minPing->value && ping < sv_minPing->value ) {
310
// don't let them keep trying until they get a big delay
311
NET_OutOfBandPrint( NS_SERVER, from, "print\nServer is for high pings only\n" );
312
Com_DPrintf ("Client %i rejected on a too low ping\n", i);
313
// reset the address otherwise their ping will keep increasing
314
// with each connect message and they'd eventually be able to connect
315
svs.challenges[i].adr.port = 0;
318
if ( sv_maxPing->value && ping > sv_maxPing->value ) {
319
NET_OutOfBandPrint( NS_SERVER, from, "print\nServer is for low pings only\n" );
320
Com_DPrintf ("Client %i rejected on a too high ping\n", i);
325
// force the "ip" info key to "localhost"
326
Info_SetValueForKey( userinfo, "ip", "localhost" );
330
Com_Memset (newcl, 0, sizeof(client_t));
332
// if there is already a slot for this ip, reuse it
333
for (i=0,cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++) {
334
if ( cl->state == CS_FREE ) {
337
if ( NET_CompareBaseAdr( from, cl->netchan.remoteAddress )
338
&& ( cl->netchan.qport == qport
339
|| from.port == cl->netchan.remoteAddress.port ) ) {
340
Com_Printf ("%s:reconnect\n", NET_AdrToString (from));
343
// this doesn't work because it nukes the players userinfo
345
// // disconnect the client from the game first so any flags the
346
// // player might have are dropped
347
// VM_Call( gvm, GAME_CLIENT_DISCONNECT, newcl - svs.clients );
353
// find a client slot
354
// if "sv_privateClients" is set > 0, then that number
355
// of client slots will be reserved for connections that
356
// have "password" set to the value of "sv_privatePassword"
357
// Info requests will report the maxclients as if the private
358
// slots didn't exist, to prevent people from trying to connect
360
// This is to allow us to reserve a couple slots here on our
361
// servers so we can play without having to kick people.
363
// check for privateClient password
364
password = Info_ValueForKey( userinfo, "password" );
365
if ( !strcmp( password, sv_privatePassword->string ) ) {
368
// skip past the reserved slots
369
startIndex = sv_privateClients->integer;
373
for ( i = startIndex; i < sv_maxclients->integer ; i++ ) {
374
cl = &svs.clients[i];
375
if (cl->state == CS_FREE) {
382
if ( NET_IsLocalAddress( from ) ) {
384
for ( i = startIndex; i < sv_maxclients->integer ; i++ ) {
385
cl = &svs.clients[i];
386
if (cl->netchan.remoteAddress.type == NA_BOT) {
390
// if they're all bots
391
if (count >= sv_maxclients->integer - startIndex) {
392
SV_DropClient(&svs.clients[sv_maxclients->integer - 1], "only bots on server");
393
newcl = &svs.clients[sv_maxclients->integer - 1];
396
Com_Error( ERR_FATAL, "server is full on local connect\n" );
401
NET_OutOfBandPrint( NS_SERVER, from, "print\nServer is full.\n" );
402
Com_DPrintf ("Rejected a connection.\n");
407
// we got a newcl, so reset the reliableSequence and reliableAcknowledge
408
cl->reliableAcknowledge = 0;
409
cl->reliableSequence = 0;
412
// build a new connection
413
// accept the new client
414
// this is the only place a client_t is ever initialized
416
clientNum = newcl - svs.clients;
417
ent = SV_GentityNum( clientNum );
418
newcl->gentity = ent;
420
// save the challenge
421
newcl->challenge = challenge;
424
Netchan_Setup (NS_SERVER, &newcl->netchan , from, qport);
425
// init the netchan queue
426
newcl->netchan_end_queue = &newcl->netchan_start_queue;
429
Q_strncpyz( newcl->userinfo, userinfo, sizeof(newcl->userinfo) );
431
// get the game a chance to reject this connection or modify the userinfo
432
denied = VM_Call( gvm, GAME_CLIENT_CONNECT, clientNum, qtrue, qfalse ); // firstTime = qtrue
434
// we can't just use VM_ArgPtr, because that is only valid inside a VM_Call
435
char *str = VM_ExplicitArgPtr( gvm, denied );
437
NET_OutOfBandPrint( NS_SERVER, from, "print\n%s\n", str );
438
Com_DPrintf ("Game rejected a connection: %s.\n", str);
442
SV_UserinfoChanged( newcl );
444
// send the connect packet to the client
445
NET_OutOfBandPrint( NS_SERVER, from, "connectResponse" );
447
Com_DPrintf( "Going from CS_FREE to CS_CONNECTED for %s\n", newcl->name );
449
newcl->state = CS_CONNECTED;
450
newcl->nextSnapshotTime = svs.time;
451
newcl->lastPacketTime = svs.time;
452
newcl->lastConnectTime = svs.time;
454
// when we receive the first packet from the client, we will
455
// notice that it is from a different serverid and that the
456
// gamestate message was not just sent, forcing a retransmit
457
newcl->gamestateMessageNum = -1;
459
// if this was the first client on the server, or the last client
460
// the server can hold, send a heartbeat to the master.
462
for (i=0,cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++) {
463
if ( svs.clients[i].state >= CS_CONNECTED ) {
467
if ( count == 1 || count == sv_maxclients->integer ) {
474
=====================
477
Called when the player is totally leaving the server, either willingly
478
or unwillingly. This is NOT called if the entire server is quiting
479
or crashing -- SV_FinalMessage() will handle that
480
=====================
482
void SV_DropClient( client_t *drop, const char *reason ) {
484
challenge_t *challenge;
486
if ( drop->state == CS_ZOMBIE ) {
487
return; // already dropped
490
if (drop->netchan.remoteAddress.type != NA_BOT) {
491
// see if we already have a challenge for this ip
492
challenge = &svs.challenges[0];
494
for (i = 0 ; i < MAX_CHALLENGES ; i++, challenge++) {
495
if ( NET_CompareAdr( drop->netchan.remoteAddress, challenge->adr ) ) {
496
challenge->connected = qfalse;
503
SV_CloseDownload( drop );
505
// tell everyone why they got dropped
506
SV_SendServerCommand( NULL, "print \"%s" S_COLOR_WHITE " %s\n\"", drop->name, reason );
508
Com_DPrintf( "Going to CS_ZOMBIE for %s\n", drop->name );
509
drop->state = CS_ZOMBIE; // become free in a few seconds
511
if (drop->download) {
512
FS_FCloseFile( drop->download );
516
// call the prog function for removing a client
517
// this will remove the body, among other things
518
VM_Call( gvm, GAME_CLIENT_DISCONNECT, drop - svs.clients );
520
// add the disconnect command
521
SV_SendServerCommand( drop, "disconnect \"%s\"", reason);
523
if ( drop->netchan.remoteAddress.type == NA_BOT ) {
524
SV_BotFreeClient( drop - svs.clients );
528
SV_SetUserinfo( drop - svs.clients, "" );
530
// if this was the last client on the server, send a heartbeat
531
// to the master so it is known the server is empty
532
// send a heartbeat now so the master will get up to date info
533
// if there is already a slot for this ip, reuse it
534
for (i=0 ; i < sv_maxclients->integer ; i++ ) {
535
if ( svs.clients[i].state >= CS_CONNECTED ) {
539
if ( i == sv_maxclients->integer ) {
546
SV_SendClientGameState
548
Sends the first message from the server to a connected client.
549
This will be sent on the initial connection and upon each new map load.
551
It will be resent if the client acknowledges a later message but has
555
void SV_SendClientGameState( client_t *client ) {
557
entityState_t *base, nullstate;
559
byte msgBuffer[MAX_MSGLEN];
561
Com_DPrintf ("SV_SendClientGameState() for %s\n", client->name);
562
Com_DPrintf( "Going from CS_CONNECTED to CS_PRIMED for %s\n", client->name );
563
client->state = CS_PRIMED;
564
client->pureAuthentic = 0;
565
client->gotCP = qfalse;
567
// when we receive the first packet from the client, we will
568
// notice that it is from a different serverid and that the
569
// gamestate message was not just sent, forcing a retransmit
570
client->gamestateMessageNum = client->netchan.outgoingSequence;
572
MSG_Init( &msg, msgBuffer, sizeof( msgBuffer ) );
574
// NOTE, MRE: all server->client messages now acknowledge
575
// let the client know which reliable clientCommands we have received
576
MSG_WriteLong( &msg, client->lastClientCommand );
578
// send any server commands waiting to be sent first.
579
// we have to do this cause we send the client->reliableSequence
580
// with a gamestate and it sets the clc.serverCommandSequence at
582
SV_UpdateServerCommandsToClient( client, &msg );
584
// send the gamestate
585
MSG_WriteByte( &msg, svc_gamestate );
586
MSG_WriteLong( &msg, client->reliableSequence );
588
// write the configstrings
589
for ( start = 0 ; start < MAX_CONFIGSTRINGS ; start++ ) {
590
if (sv.configstrings[start][0]) {
591
MSG_WriteByte( &msg, svc_configstring );
592
MSG_WriteShort( &msg, start );
593
MSG_WriteBigString( &msg, sv.configstrings[start] );
597
// write the baselines
598
Com_Memset( &nullstate, 0, sizeof( nullstate ) );
599
for ( start = 0 ; start < MAX_GENTITIES; start++ ) {
600
base = &sv.svEntities[start].baseline;
601
if ( !base->number ) {
604
MSG_WriteByte( &msg, svc_baseline );
605
MSG_WriteDeltaEntity( &msg, &nullstate, base, qtrue );
608
MSG_WriteByte( &msg, svc_EOF );
610
MSG_WriteLong( &msg, client - svs.clients);
612
// write the checksum feed
613
MSG_WriteLong( &msg, sv.checksumFeed);
615
// deliver this to the client
616
SV_SendMessageToClient( &msg, client );
625
void SV_ClientEnterWorld( client_t *client, usercmd_t *cmd ) {
629
Com_DPrintf( "Going from CS_PRIMED to CS_ACTIVE for %s\n", client->name );
630
client->state = CS_ACTIVE;
632
// resend all configstrings using the cs commands since these are
633
// no longer sent when the client is CS_PRIMED
634
SV_UpdateConfigstrings( client );
636
// set up the entity for the client
637
clientNum = client - svs.clients;
638
ent = SV_GentityNum( clientNum );
639
ent->s.number = clientNum;
640
client->gentity = ent;
642
client->deltaMessage = -1;
643
client->nextSnapshotTime = svs.time; // generate a snapshot immediately
644
client->lastUsercmd = *cmd;
646
// call the game begin function
647
VM_Call( gvm, GAME_CLIENT_BEGIN, client - svs.clients );
651
============================================================
653
CLIENT COMMAND EXECUTION
655
============================================================
662
clear/free any download vars
665
static void SV_CloseDownload( client_t *cl ) {
670
FS_FCloseFile( cl->download );
673
*cl->downloadName = 0;
675
// Free the temporary buffer space
676
for (i = 0; i < MAX_DOWNLOAD_WINDOW; i++) {
677
if (cl->downloadBlocks[i]) {
678
Z_Free( cl->downloadBlocks[i] );
679
cl->downloadBlocks[i] = NULL;
689
Abort a download if in progress
692
void SV_StopDownload_f( client_t *cl ) {
693
if (*cl->downloadName)
694
Com_DPrintf( "clientDownload: %d : file \"%s\" aborted\n", cl - svs.clients, cl->downloadName );
696
SV_CloseDownload( cl );
703
Downloads are finished
706
void SV_DoneDownload_f( client_t *cl ) {
707
Com_DPrintf( "clientDownload: %s Done\n", cl->name);
708
// resend the game state to update any clients that entered during the download
709
SV_SendClientGameState(cl);
716
The argument will be the last acknowledged block from the client, it should be
717
the same as cl->downloadClientBlock
720
void SV_NextDownload_f( client_t *cl )
722
int block = atoi( Cmd_Argv(1) );
724
if (block == cl->downloadClientBlock) {
725
Com_DPrintf( "clientDownload: %d : client acknowledge of block %d\n", cl - svs.clients, block );
727
// Find out if we are done. A zero-length block indicates EOF
728
if (cl->downloadBlockSize[cl->downloadClientBlock % MAX_DOWNLOAD_WINDOW] == 0) {
729
Com_Printf( "clientDownload: %d : file \"%s\" completed\n", cl - svs.clients, cl->downloadName );
730
SV_CloseDownload( cl );
734
cl->downloadSendTime = svs.time;
735
cl->downloadClientBlock++;
738
// We aren't getting an acknowledge for the correct block, drop the client
739
// FIXME: this is bad... the client will never parse the disconnect message
740
// because the cgame isn't loaded yet
741
SV_DropClient( cl, "broken download" );
749
void SV_BeginDownload_f( client_t *cl ) {
751
// Kill any existing download
752
SV_CloseDownload( cl );
754
// cl->downloadName is non-zero now, SV_WriteDownloadToClient will see this and open
756
Q_strncpyz( cl->downloadName, Cmd_Argv(1), sizeof(cl->downloadName) );
761
SV_WriteDownloadToClient
763
Check to see if the client wants a file, open it if needed and start pumping the client
764
Fill up msg with data
767
void SV_WriteDownloadToClient( client_t *cl , msg_t *msg )
772
int idPack = 0, missionPack = 0, unreferenced = 1;
773
char errorMessage[1024];
774
char pakbuf[MAX_QPATH], *pakptr;
777
if (!*cl->downloadName)
778
return; // Nothing being downloaded
781
// Chop off filename extension.
782
Com_sprintf(pakbuf, sizeof(pakbuf), "%s", cl->downloadName);
783
pakptr = Q_strrchr(pakbuf, '.');
789
// Check for pk3 filename extension
790
if(!Q_stricmp(pakptr + 1, "pk3"))
792
const char *referencedPaks = FS_ReferencedPakNames();
794
// Check whether the file appears in the list of referenced
795
// paks to prevent downloading of arbitrary files.
796
Cmd_TokenizeStringIgnoreQuotes(referencedPaks);
797
numRefPaks = Cmd_Argc();
799
for(curindex = 0; curindex < numRefPaks; curindex++)
801
if(!FS_FilenameCompare(Cmd_Argv(curindex), pakbuf))
805
// now that we know the file is referenced,
806
// check whether it's legal to download it.
807
missionPack = FS_idPak(pakbuf, "missionpack");
808
idPack = missionPack || FS_idPak(pakbuf, BASEGAME);
816
// We open the file here
817
if ( !(sv_allowDownload->integer & DLF_ENABLE) ||
818
(sv_allowDownload->integer & DLF_NO_UDP) ||
819
idPack || unreferenced ||
820
( cl->downloadSize = FS_SV_FOpenFileRead( cl->downloadName, &cl->download ) ) <= 0 ) {
821
// cannot auto-download file
824
Com_Printf("clientDownload: %d : \"%s\" is not referenced and cannot be downloaded.\n", cl - svs.clients, cl->downloadName);
825
Com_sprintf(errorMessage, sizeof(errorMessage), "File \"%s\" is not referenced and cannot be downloaded.", cl->downloadName);
828
Com_Printf("clientDownload: %d : \"%s\" cannot download id pk3 files\n", cl - svs.clients, cl->downloadName);
830
Com_sprintf(errorMessage, sizeof(errorMessage), "Cannot autodownload Team Arena file \"%s\"\n"
831
"The Team Arena mission pack can be found in your local game store.", cl->downloadName);
834
Com_sprintf(errorMessage, sizeof(errorMessage), "Cannot autodownload id pk3 file \"%s\"", cl->downloadName);
837
else if ( !(sv_allowDownload->integer & DLF_ENABLE) ||
838
(sv_allowDownload->integer & DLF_NO_UDP) ) {
840
Com_Printf("clientDownload: %d : \"%s\" download disabled", cl - svs.clients, cl->downloadName);
841
if (sv_pure->integer) {
842
Com_sprintf(errorMessage, sizeof(errorMessage), "Could not download \"%s\" because autodownloading is disabled on the server.\n\n"
843
"You will need to get this file elsewhere before you "
844
"can connect to this pure server.\n", cl->downloadName);
846
Com_sprintf(errorMessage, sizeof(errorMessage), "Could not download \"%s\" because autodownloading is disabled on the server.\n\n"
847
"The server you are connecting to is not a pure server, "
848
"set autodownload to No in your settings and you might be "
849
"able to join the game anyway.\n", cl->downloadName);
852
// NOTE TTimo this is NOT supposed to happen unless bug in our filesystem scheme?
853
// if the pk3 is referenced, it must have been found somewhere in the filesystem
854
Com_Printf("clientDownload: %d : \"%s\" file not found on server\n", cl - svs.clients, cl->downloadName);
855
Com_sprintf(errorMessage, sizeof(errorMessage), "File \"%s\" not found on server for autodownloading.\n", cl->downloadName);
857
MSG_WriteByte( msg, svc_download );
858
MSG_WriteShort( msg, 0 ); // client is expecting block zero
859
MSG_WriteLong( msg, -1 ); // illegal file size
860
MSG_WriteString( msg, errorMessage );
862
*cl->downloadName = 0;
866
Com_Printf( "clientDownload: %d : beginning \"%s\"\n", cl - svs.clients, cl->downloadName );
869
cl->downloadCurrentBlock = cl->downloadClientBlock = cl->downloadXmitBlock = 0;
870
cl->downloadCount = 0;
871
cl->downloadEOF = qfalse;
874
// Perform any reads that we need to
875
while (cl->downloadCurrentBlock - cl->downloadClientBlock < MAX_DOWNLOAD_WINDOW &&
876
cl->downloadSize != cl->downloadCount) {
878
curindex = (cl->downloadCurrentBlock % MAX_DOWNLOAD_WINDOW);
880
if (!cl->downloadBlocks[curindex])
881
cl->downloadBlocks[curindex] = Z_Malloc( MAX_DOWNLOAD_BLKSIZE );
883
cl->downloadBlockSize[curindex] = FS_Read( cl->downloadBlocks[curindex], MAX_DOWNLOAD_BLKSIZE, cl->download );
885
if (cl->downloadBlockSize[curindex] < 0) {
887
cl->downloadCount = cl->downloadSize;
891
cl->downloadCount += cl->downloadBlockSize[curindex];
893
// Load in next block
894
cl->downloadCurrentBlock++;
897
// Check to see if we have eof condition and add the EOF block
898
if (cl->downloadCount == cl->downloadSize &&
900
cl->downloadCurrentBlock - cl->downloadClientBlock < MAX_DOWNLOAD_WINDOW) {
902
cl->downloadBlockSize[cl->downloadCurrentBlock % MAX_DOWNLOAD_WINDOW] = 0;
903
cl->downloadCurrentBlock++;
905
cl->downloadEOF = qtrue; // We have added the EOF block
908
// Loop up to window size times based on how many blocks we can fit in the
909
// client snapMsec and rate
911
// based on the rate, how many bytes can we fit in the snapMsec time of the client
912
// normal rate / snapshotMsec calculation
914
if ( sv_maxRate->integer ) {
915
if ( sv_maxRate->integer < 1000 ) {
916
Cvar_Set( "sv_MaxRate", "1000" );
918
if ( sv_maxRate->integer < rate ) {
919
rate = sv_maxRate->integer;
922
if ( sv_minRate->integer ) {
923
if ( sv_minRate->integer < 1000 )
924
Cvar_Set( "sv_minRate", "1000" );
925
if ( sv_minRate->integer > rate )
926
rate = sv_minRate->integer;
932
blockspersnap = ( (rate * cl->snapshotMsec) / 1000 + MAX_DOWNLOAD_BLKSIZE ) /
933
MAX_DOWNLOAD_BLKSIZE;
936
if (blockspersnap < 0)
939
while (blockspersnap--) {
941
// Write out the next section of the file, if we have already reached our window,
942
// automatically start retransmitting
944
if (cl->downloadClientBlock == cl->downloadCurrentBlock)
945
return; // Nothing to transmit
947
if (cl->downloadXmitBlock == cl->downloadCurrentBlock) {
948
// We have transmitted the complete window, should we start resending?
950
//FIXME: This uses a hardcoded one second timeout for lost blocks
951
//the timeout should be based on client rate somehow
952
if (svs.time - cl->downloadSendTime > 1000)
953
cl->downloadXmitBlock = cl->downloadClientBlock;
958
// Send current block
959
curindex = (cl->downloadXmitBlock % MAX_DOWNLOAD_WINDOW);
961
MSG_WriteByte( msg, svc_download );
962
MSG_WriteShort( msg, cl->downloadXmitBlock );
964
// block zero is special, contains file size
965
if ( cl->downloadXmitBlock == 0 )
966
MSG_WriteLong( msg, cl->downloadSize );
968
MSG_WriteShort( msg, cl->downloadBlockSize[curindex] );
971
if ( cl->downloadBlockSize[curindex] ) {
972
MSG_WriteData( msg, cl->downloadBlocks[curindex], cl->downloadBlockSize[curindex] );
975
Com_DPrintf( "clientDownload: %d : writing block %d\n", cl - svs.clients, cl->downloadXmitBlock );
977
// Move on to the next block
978
// It will get sent with next snap shot. The rate will keep us in line.
979
cl->downloadXmitBlock++;
981
cl->downloadSendTime = svs.time;
989
The client is going to disconnect, so remove the connection immediately FIXME: move to game?
992
static void SV_Disconnect_f( client_t *cl ) {
993
SV_DropClient( cl, "disconnected" );
1000
If we are pure, disconnect the client if they do no meet the following conditions:
1002
1. the first two checksums match our view of cgame and ui
1003
2. there are no any additional checksums that we do not have
1005
This routine would be a bit simpler with a goto but i abstained
1009
static void SV_VerifyPaks_f( client_t *cl ) {
1010
int nChkSum1, nChkSum2, nClientPaks, nServerPaks, i, j, nCurArg;
1011
int nClientChkSum[1024];
1012
int nServerChkSum[1024];
1013
const char *pPaks, *pArg;
1014
qboolean bGood = qtrue;
1016
// if we are pure, we "expect" the client to load certain things from
1017
// certain pk3 files, namely we want the client to have loaded the
1018
// ui and cgame that we think should be loaded based on the pure setting
1020
if ( sv_pure->integer != 0 ) {
1023
nChkSum1 = nChkSum2 = 0;
1024
// we run the game, so determine which cgame and ui the client "should" be running
1025
bGood = (FS_FileIsInPAK("vm/cgame.qvm", &nChkSum1) == 1);
1027
bGood = (FS_FileIsInPAK("vm/ui.qvm", &nChkSum2) == 1);
1029
nClientPaks = Cmd_Argc();
1031
// start at arg 2 ( skip serverId cl_paks )
1034
pArg = Cmd_Argv(nCurArg++);
1040
// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=475
1041
// we may get incoming cp sequences from a previous checksumFeed, which we need to ignore
1042
// since serverId is a frame count, it always goes up
1043
if (atoi(pArg) < sv.checksumFeedServerId)
1045
Com_DPrintf("ignoring outdated cp command from client %s\n", cl->name);
1050
// we basically use this while loop to avoid using 'goto' :)
1053
// must be at least 6: "cl_paks cgame ui @ firstref ... numChecksums"
1054
// numChecksums is encoded
1055
if (nClientPaks < 6) {
1059
// verify first to be the cgame checksum
1060
pArg = Cmd_Argv(nCurArg++);
1061
if (!pArg || *pArg == '@' || atoi(pArg) != nChkSum1 ) {
1065
// verify the second to be the ui checksum
1066
pArg = Cmd_Argv(nCurArg++);
1067
if (!pArg || *pArg == '@' || atoi(pArg) != nChkSum2 ) {
1071
// should be sitting at the delimeter now
1072
pArg = Cmd_Argv(nCurArg++);
1077
// store checksums since tokenization is not re-entrant
1078
for (i = 0; nCurArg < nClientPaks; i++) {
1079
nClientChkSum[i] = atoi(Cmd_Argv(nCurArg++));
1082
// store number to compare against (minus one cause the last is the number of checksums)
1083
nClientPaks = i - 1;
1085
// make sure none of the client check sums are the same
1086
// so the client can't send 5 the same checksums
1087
for (i = 0; i < nClientPaks; i++) {
1088
for (j = 0; j < nClientPaks; j++) {
1091
if (nClientChkSum[i] == nClientChkSum[j]) {
1096
if (bGood == qfalse)
1099
if (bGood == qfalse)
1102
// get the pure checksums of the pk3 files loaded by the server
1103
pPaks = FS_LoadedPakPureChecksums();
1104
Cmd_TokenizeString( pPaks );
1105
nServerPaks = Cmd_Argc();
1106
if (nServerPaks > 1024)
1109
for (i = 0; i < nServerPaks; i++) {
1110
nServerChkSum[i] = atoi(Cmd_Argv(i));
1113
// check if the client has provided any pure checksums of pk3 files not loaded by the server
1114
for (i = 0; i < nClientPaks; i++) {
1115
for (j = 0; j < nServerPaks; j++) {
1116
if (nClientChkSum[i] == nServerChkSum[j]) {
1120
if (j >= nServerPaks) {
1125
if ( bGood == qfalse ) {
1129
// check if the number of checksums was correct
1130
nChkSum1 = sv.checksumFeed;
1131
for (i = 0; i < nClientPaks; i++) {
1132
nChkSum1 ^= nClientChkSum[i];
1134
nChkSum1 ^= nClientPaks;
1135
if (nChkSum1 != nClientChkSum[nClientPaks]) {
1147
cl->pureAuthentic = 1;
1150
cl->pureAuthentic = 0;
1151
cl->nextSnapshotTime = -1;
1152
cl->state = CS_ACTIVE;
1153
SV_SendClientSnapshot( cl );
1154
SV_DropClient( cl, "Unpure client detected. Invalid .PK3 files referenced!" );
1161
SV_ResetPureClient_f
1164
static void SV_ResetPureClient_f( client_t *cl ) {
1165
cl->pureAuthentic = 0;
1173
Pull specific info from a newly changed userinfo string
1174
into a more C friendly form.
1177
void SV_UserinfoChanged( client_t *cl ) {
1182
Q_strncpyz( cl->name, Info_ValueForKey (cl->userinfo, "name"), sizeof(cl->name) );
1186
// if the client is on the same subnet as the server and we aren't running an
1187
// internet public server, assume they don't need a rate choke
1188
if ( Sys_IsLANAddress( cl->netchan.remoteAddress ) && com_dedicated->integer != 2 && sv_lanForceRate->integer == 1) {
1189
cl->rate = 99999; // lans should not rate limit
1191
val = Info_ValueForKey (cl->userinfo, "rate");
1195
if (cl->rate < 1000) {
1197
} else if (cl->rate > 90000) {
1204
val = Info_ValueForKey (cl->userinfo, "handicap");
1207
if (i<=0 || i>100 || strlen(val) > 4) {
1208
Info_SetValueForKey( cl->userinfo, "handicap", "100" );
1213
val = Info_ValueForKey (cl->userinfo, "snaps");
1218
} else if ( i > sv_fps->integer ) {
1219
i = sv_fps->integer;
1221
cl->snapshotMsec = 1000/i;
1223
cl->snapshotMsec = 50;
1227
// maintain the IP information
1228
// this is set in SV_DirectConnect (directly on the server, not transmitted), may be lost when client updates it's userinfo
1229
// the banning code relies on this being consistently present
1230
val = Info_ValueForKey (cl->userinfo, "ip");
1233
//Com_DPrintf("Maintain IP in userinfo for '%s'\n", cl->name);
1234
if ( !NET_IsLocalAddress(cl->netchan.remoteAddress) )
1235
Info_SetValueForKey( cl->userinfo, "ip", NET_AdrToString( cl->netchan.remoteAddress ) );
1237
// force the "ip" info key to "localhost" for local clients
1238
Info_SetValueForKey( cl->userinfo, "ip", "localhost" );
1248
static void SV_UpdateUserinfo_f( client_t *cl ) {
1249
Q_strncpyz( cl->userinfo, Cmd_Argv(1), sizeof(cl->userinfo) );
1251
SV_UserinfoChanged( cl );
1252
// call prog code to allow overrides
1253
VM_Call( gvm, GAME_CLIENT_USERINFO_CHANGED, cl - svs.clients );
1258
void (*func)( client_t *cl );
1261
static ucmd_t ucmds[] = {
1262
{"userinfo", SV_UpdateUserinfo_f},
1263
{"disconnect", SV_Disconnect_f},
1264
{"cp", SV_VerifyPaks_f},
1265
{"vdr", SV_ResetPureClient_f},
1266
{"download", SV_BeginDownload_f},
1267
{"nextdl", SV_NextDownload_f},
1268
{"stopdl", SV_StopDownload_f},
1269
{"donedl", SV_DoneDownload_f},
1276
SV_ExecuteClientCommand
1278
Also called by bot code
1281
void SV_ExecuteClientCommand( client_t *cl, const char *s, qboolean clientOK ) {
1283
qboolean bProcessed = qfalse;
1285
Cmd_TokenizeString( s );
1287
// see if it is a server level command
1288
for (u=ucmds ; u->name ; u++) {
1289
if (!strcmp (Cmd_Argv(0), u->name) ) {
1297
// pass unknown strings to the game
1298
if (!u->name && sv.state == SS_GAME) {
1299
VM_Call( gvm, GAME_CLIENT_COMMAND, cl - svs.clients );
1302
else if (!bProcessed)
1303
Com_DPrintf( "client text ignored for %s: %s\n", cl->name, Cmd_Argv(0) );
1311
static qboolean SV_ClientCommand( client_t *cl, msg_t *msg ) {
1314
qboolean clientOk = qtrue;
1316
seq = MSG_ReadLong( msg );
1317
s = MSG_ReadString( msg );
1319
// see if we have already executed it
1320
if ( cl->lastClientCommand >= seq ) {
1324
Com_DPrintf( "clientCommand: %s : %i : %s\n", cl->name, seq, s );
1326
// drop the connection if we have somehow lost commands
1327
if ( seq > cl->lastClientCommand + 1 ) {
1328
Com_Printf( "Client %s lost %i clientCommands\n", cl->name,
1329
seq - cl->lastClientCommand + 1 );
1330
SV_DropClient( cl, "Lost reliable commands" );
1334
// malicious users may try using too many string commands
1335
// to lag other players. If we decide that we want to stall
1336
// the command, we will stop processing the rest of the packet,
1337
// including the usercmd. This causes flooders to lag themselves
1338
// but not other people
1339
// We don't do this when the client hasn't been active yet since its
1340
// normal to spam a lot of commands when downloading
1341
if ( !com_cl_running->integer &&
1342
cl->state >= CS_ACTIVE &&
1343
sv_floodProtect->integer &&
1344
svs.time < cl->nextReliableTime ) {
1345
// ignore any other text messages from this client but let them keep playing
1346
// TTimo - moved the ignored verbose to the actual processing in SV_ExecuteClientCommand, only printing if the core doesn't intercept
1350
// don't allow another command for one second
1351
cl->nextReliableTime = svs.time + 1000;
1353
SV_ExecuteClientCommand( cl, s, clientOk );
1355
cl->lastClientCommand = seq;
1356
Com_sprintf(cl->lastClientCommandString, sizeof(cl->lastClientCommandString), "%s", s);
1358
return qtrue; // continue procesing
1362
//==================================================================================
1369
Also called by bot code
1372
void SV_ClientThink (client_t *cl, usercmd_t *cmd) {
1373
cl->lastUsercmd = *cmd;
1375
if ( cl->state != CS_ACTIVE ) {
1376
return; // may have been kicked during the last usercmd
1379
VM_Call( gvm, GAME_CLIENT_THINK, cl - svs.clients );
1386
The message usually contains all the movement commands
1387
that were in the last three packets, so that the information
1388
in dropped packets can be recovered.
1390
On very fast clients, there may be multiple usercmd packed into
1391
each of the backup packets.
1394
static void SV_UserMove( client_t *cl, msg_t *msg, qboolean delta ) {
1398
usercmd_t cmds[MAX_PACKET_USERCMDS];
1399
usercmd_t *cmd, *oldcmd;
1402
cl->deltaMessage = cl->messageAcknowledge;
1404
cl->deltaMessage = -1;
1407
cmdCount = MSG_ReadByte( msg );
1409
if ( cmdCount < 1 ) {
1410
Com_Printf( "cmdCount < 1\n" );
1414
if ( cmdCount > MAX_PACKET_USERCMDS ) {
1415
Com_Printf( "cmdCount > MAX_PACKET_USERCMDS\n" );
1419
// use the checksum feed in the key
1420
key = sv.checksumFeed;
1421
// also use the message acknowledge
1422
key ^= cl->messageAcknowledge;
1423
// also use the last acknowledged server command in the key
1424
key ^= Com_HashKey(cl->reliableCommands[ cl->reliableAcknowledge & (MAX_RELIABLE_COMMANDS-1) ], 32);
1426
Com_Memset( &nullcmd, 0, sizeof(nullcmd) );
1428
for ( i = 0 ; i < cmdCount ; i++ ) {
1430
MSG_ReadDeltaUsercmdKey( msg, key, oldcmd, cmd );
1434
// save time for ping calculation
1435
cl->frames[ cl->messageAcknowledge & PACKET_MASK ].messageAcked = svs.time;
1438
// catch the no-cp-yet situation before SV_ClientEnterWorld
1439
// if CS_ACTIVE, then it's time to trigger a new gamestate emission
1440
// if not, then we are getting remaining parasite usermove commands, which we should ignore
1441
if (sv_pure->integer != 0 && cl->pureAuthentic == 0 && !cl->gotCP) {
1442
if (cl->state == CS_ACTIVE)
1444
// we didn't get a cp yet, don't assume anything and just send the gamestate all over again
1445
Com_DPrintf( "%s: didn't get cp command, resending gamestate\n", cl->name, cl->state );
1446
SV_SendClientGameState( cl );
1451
// if this is the first usercmd we have received
1452
// this gamestate, put the client into the world
1453
if ( cl->state == CS_PRIMED ) {
1454
SV_ClientEnterWorld( cl, &cmds[0] );
1455
// the moves can be processed normaly
1458
// a bad cp command was sent, drop the client
1459
if (sv_pure->integer != 0 && cl->pureAuthentic == 0) {
1460
SV_DropClient( cl, "Cannot validate pure client!");
1464
if ( cl->state != CS_ACTIVE ) {
1465
cl->deltaMessage = -1;
1469
// usually, the first couple commands will be duplicates
1470
// of ones we have previously received, but the servertimes
1471
// in the commands will cause them to be immediately discarded
1472
for ( i = 0 ; i < cmdCount ; i++ ) {
1473
// if this is a cmd from before a map_restart ignore it
1474
if ( cmds[i].serverTime > cmds[cmdCount-1].serverTime ) {
1477
// extremely lagged or cmd from before a map_restart
1478
//if ( cmds[i].serverTime > svs.time + 3000 ) {
1481
// don't execute if this is an old cmd which is already executed
1482
// these old cmds are included when cl_packetdup > 0
1483
if ( cmds[i].serverTime <= cl->lastUsercmd.serverTime ) {
1486
SV_ClientThink (cl, &cmds[ i ]);
1492
===========================================================================
1496
===========================================================================
1501
SV_ExecuteClientMessage
1503
Parse a client packet
1506
void SV_ExecuteClientMessage( client_t *cl, msg_t *msg ) {
1512
serverId = MSG_ReadLong( msg );
1513
cl->messageAcknowledge = MSG_ReadLong( msg );
1515
if (cl->messageAcknowledge < 0) {
1516
// usually only hackers create messages like this
1517
// it is more annoying for them to let them hanging
1519
SV_DropClient( cl, "DEBUG: illegible client message" );
1524
cl->reliableAcknowledge = MSG_ReadLong( msg );
1526
// NOTE: when the client message is fux0red the acknowledgement numbers
1527
// can be out of range, this could cause the server to send thousands of server
1528
// commands which the server thinks are not yet acknowledged in SV_UpdateServerCommandsToClient
1529
if (cl->reliableAcknowledge < cl->reliableSequence - MAX_RELIABLE_COMMANDS) {
1530
// usually only hackers create messages like this
1531
// it is more annoying for them to let them hanging
1533
SV_DropClient( cl, "DEBUG: illegible client message" );
1535
cl->reliableAcknowledge = cl->reliableSequence;
1538
// if this is a usercmd from a previous gamestate,
1539
// ignore it or retransmit the current gamestate
1541
// if the client was downloading, let it stay at whatever serverId and
1542
// gamestate it was at. This allows it to keep downloading even when
1543
// the gamestate changes. After the download is finished, we'll
1544
// notice and send it a new game state
1546
// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=536
1547
// don't drop as long as previous command was a nextdl, after a dl is done, downloadName is set back to ""
1548
// but we still need to read the next message to move to next download or send gamestate
1549
// I don't like this hack though, it must have been working fine at some point, suspecting the fix is somewhere else
1550
if ( serverId != sv.serverId && !*cl->downloadName && !strstr(cl->lastClientCommandString, "nextdl") ) {
1551
if ( serverId >= sv.restartedServerId && serverId < sv.serverId ) { // TTimo - use a comparison here to catch multiple map_restart
1552
// they just haven't caught the map_restart yet
1553
Com_DPrintf("%s : ignoring pre map_restart / outdated client message\n", cl->name);
1556
// if we can tell that the client has dropped the last
1557
// gamestate we sent them, resend it
1558
if ( cl->messageAcknowledge > cl->gamestateMessageNum ) {
1559
Com_DPrintf( "%s : dropped gamestate, resending\n", cl->name );
1560
SV_SendClientGameState( cl );
1565
// this client has acknowledged the new gamestate so it's
1566
// safe to start sending it the real time again
1567
if( cl->oldServerTime && serverId == sv.serverId ){
1568
Com_DPrintf( "%s acknowledged gamestate\n", cl->name );
1569
cl->oldServerTime = 0;
1572
// read optional clientCommand strings
1574
c = MSG_ReadByte( msg );
1575
if ( c == clc_EOF ) {
1578
if ( c != clc_clientCommand ) {
1581
if ( !SV_ClientCommand( cl, msg ) ) {
1582
return; // we couldn't execute it because of the flood protection
1584
if (cl->state == CS_ZOMBIE) {
1585
return; // disconnect command
1589
// read the usercmd_t
1590
if ( c == clc_move ) {
1591
SV_UserMove( cl, msg, qtrue );
1592
} else if ( c == clc_moveNoDelta ) {
1593
SV_UserMove( cl, msg, qfalse );
1594
} else if ( c != clc_EOF ) {
1595
Com_Printf( "WARNING: bad command byte for client %i\n", cl - svs.clients );
1597
// if ( msg->readcount != msg->cursize ) {
1598
// Com_Printf( "WARNING: Junk at end of packet for client %i\n", cl - svs.clients );