00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "server.h"
00024
00025 serverStatic_t svs;
00026 server_t sv;
00027 vm_t *gvm = NULL;
00028
00029 cvar_t *sv_fps;
00030 cvar_t *sv_timeout;
00031 cvar_t *sv_zombietime;
00032 cvar_t *sv_rconPassword;
00033 cvar_t *sv_privatePassword;
00034 cvar_t *sv_allowDownload;
00035 cvar_t *sv_maxclients;
00036
00037 cvar_t *sv_privateClients;
00038 cvar_t *sv_hostname;
00039 cvar_t *sv_master[MAX_MASTER_SERVERS];
00040 cvar_t *sv_reconnectlimit;
00041 cvar_t *sv_showloss;
00042 cvar_t *sv_padPackets;
00043 cvar_t *sv_killserver;
00044 cvar_t *sv_mapname;
00045 cvar_t *sv_mapChecksum;
00046 cvar_t *sv_serverid;
00047 cvar_t *sv_maxRate;
00048 cvar_t *sv_minPing;
00049 cvar_t *sv_maxPing;
00050 cvar_t *sv_gametype;
00051 cvar_t *sv_pure;
00052 cvar_t *sv_floodProtect;
00053 cvar_t *sv_lanForceRate;
00054 cvar_t *sv_strictAuth;
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071 char *SV_ExpandNewlines( char *in ) {
00072 static char string[1024];
00073 int l;
00074
00075 l = 0;
00076 while ( *in && l < sizeof(string) - 3 ) {
00077 if ( *in == '\n' ) {
00078 string[l++] = '\\';
00079 string[l++] = 'n';
00080 } else {
00081 string[l++] = *in;
00082 }
00083 in++;
00084 }
00085 string[l] = 0;
00086
00087 return string;
00088 }
00089
00090
00091
00092
00093
00094
00095
00096
00097 int SV_ReplacePendingServerCommands( client_t *client, const char *cmd ) {
00098 int i, index, csnum1, csnum2;
00099
00100 for ( i = client->reliableSent+1; i <= client->reliableSequence; i++ ) {
00101 index = i & ( MAX_RELIABLE_COMMANDS - 1 );
00102
00103 if ( !Q_strncmp(cmd, client->reliableCommands[ index ], strlen("cs")) ) {
00104 sscanf(cmd, "cs %i", &csnum1);
00105 sscanf(client->reliableCommands[ index ], "cs %i", &csnum2);
00106 if ( csnum1 == csnum2 ) {
00107 Q_strncpyz( client->reliableCommands[ index ], cmd, sizeof( client->reliableCommands[ index ] ) );
00108
00109
00110
00111
00112
00113 return qtrue;
00114 }
00115 }
00116 }
00117 return qfalse;
00118 }
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128 void SV_AddServerCommand( client_t *client, const char *cmd ) {
00129 int index, i;
00130
00131
00132
00133
00134
00135
00136
00137 client->reliableSequence++;
00138
00139
00140
00141
00142 if ( client->reliableSequence - client->reliableAcknowledge == MAX_RELIABLE_COMMANDS + 1 ) {
00143 Com_Printf( "===== pending server commands =====\n" );
00144 for ( i = client->reliableAcknowledge + 1 ; i <= client->reliableSequence ; i++ ) {
00145 Com_Printf( "cmd %5d: %s\n", i, client->reliableCommands[ i & (MAX_RELIABLE_COMMANDS-1) ] );
00146 }
00147 Com_Printf( "cmd %5d: %s\n", i, cmd );
00148 SV_DropClient( client, "Server command overflow" );
00149 return;
00150 }
00151 index = client->reliableSequence & ( MAX_RELIABLE_COMMANDS - 1 );
00152 Q_strncpyz( client->reliableCommands[ index ], cmd, sizeof( client->reliableCommands[ index ] ) );
00153 }
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165 void QDECL SV_SendServerCommand(client_t *cl, const char *fmt, ...) {
00166 va_list argptr;
00167 byte message[MAX_MSGLEN];
00168 client_t *client;
00169 int j;
00170
00171 va_start (argptr,fmt);
00172 Q_vsnprintf ((char *)message, sizeof(message), fmt,argptr);
00173 va_end (argptr);
00174
00175 if ( cl != NULL ) {
00176 SV_AddServerCommand( cl, (char *)message );
00177 return;
00178 }
00179
00180
00181 if ( com_dedicated->integer && !strncmp( (char *)message, "print", 5) ) {
00182 Com_Printf ("broadcast: %s\n", SV_ExpandNewlines((char *)message) );
00183 }
00184
00185
00186 for (j = 0, client = svs.clients; j < sv_maxclients->integer ; j++, client++) {
00187 if ( client->state < CS_PRIMED ) {
00188 continue;
00189 }
00190 SV_AddServerCommand( client, (char *)message );
00191 }
00192 }
00193
00194
00195
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214 #define HEARTBEAT_MSEC 300*1000
00215 #define HEARTBEAT_GAME "QuakeArena-1"
00216 void SV_MasterHeartbeat( void ) {
00217 static netadr_t adr[MAX_MASTER_SERVERS];
00218 int i;
00219
00220
00221 if ( !com_dedicated || com_dedicated->integer != 2 ) {
00222 return;
00223 }
00224
00225
00226 if ( svs.time < svs.nextHeartbeatTime ) {
00227 return;
00228 }
00229 svs.nextHeartbeatTime = svs.time + HEARTBEAT_MSEC;
00230
00231
00232
00233 for ( i = 0 ; i < MAX_MASTER_SERVERS ; i++ ) {
00234 if ( !sv_master[i]->string[0] ) {
00235 continue;
00236 }
00237
00238
00239
00240
00241 if ( sv_master[i]->modified ) {
00242 sv_master[i]->modified = qfalse;
00243
00244 Com_Printf( "Resolving %s\n", sv_master[i]->string );
00245 if ( !NET_StringToAdr( sv_master[i]->string, &adr[i] ) ) {
00246
00247
00248 Com_Printf( "Couldn't resolve address: %s\n", sv_master[i]->string );
00249 Cvar_Set( sv_master[i]->name, "" );
00250 sv_master[i]->modified = qfalse;
00251 continue;
00252 }
00253 if ( !strstr( ":", sv_master[i]->string ) ) {
00254 adr[i].port = BigShort( PORT_MASTER );
00255 }
00256 Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", sv_master[i]->string,
00257 adr[i].ip[0], adr[i].ip[1], adr[i].ip[2], adr[i].ip[3],
00258 BigShort( adr[i].port ) );
00259 }
00260
00261
00262 Com_Printf ("Sending heartbeat to %s\n", sv_master[i]->string );
00263
00264
00265 NET_OutOfBandPrint( NS_SERVER, adr[i], "heartbeat %s\n", HEARTBEAT_GAME );
00266 }
00267 }
00268
00269
00270
00271
00272
00273
00274
00275
00276 void SV_MasterShutdown( void ) {
00277
00278 svs.nextHeartbeatTime = -9999;
00279 SV_MasterHeartbeat();
00280
00281
00282 svs.nextHeartbeatTime = -9999;
00283 SV_MasterHeartbeat();
00284
00285
00286
00287 }
00288
00289
00290
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305
00306
00307 void SVC_Status( netadr_t from ) {
00308 char player[1024];
00309 char status[MAX_MSGLEN];
00310 int i;
00311 client_t *cl;
00312 playerState_t *ps;
00313 int statusLength;
00314 int playerLength;
00315 char infostring[MAX_INFO_STRING];
00316
00317
00318 if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER ) {
00319 return;
00320 }
00321
00322 strcpy( infostring, Cvar_InfoString( CVAR_SERVERINFO ) );
00323
00324
00325
00326 Info_SetValueForKey( infostring, "challenge", Cmd_Argv(1) );
00327
00328
00329 if ( Cvar_VariableValue( "fs_restrict" ) ) {
00330 char keywords[MAX_INFO_STRING];
00331
00332 Com_sprintf( keywords, sizeof( keywords ), "demo %s",
00333 Info_ValueForKey( infostring, "sv_keywords" ) );
00334 Info_SetValueForKey( infostring, "sv_keywords", keywords );
00335 }
00336
00337 status[0] = 0;
00338 statusLength = 0;
00339
00340 for (i=0 ; i < sv_maxclients->integer ; i++) {
00341 cl = &svs.clients[i];
00342 if ( cl->state >= CS_CONNECTED ) {
00343 ps = SV_GameClientNum( i );
00344 Com_sprintf (player, sizeof(player), "%i %i \"%s\"\n",
00345 ps->persistant[PERS_SCORE], cl->ping, cl->name);
00346 playerLength = strlen(player);
00347 if (statusLength + playerLength >= sizeof(status) ) {
00348 break;
00349 }
00350 strcpy (status + statusLength, player);
00351 statusLength += playerLength;
00352 }
00353 }
00354
00355 NET_OutOfBandPrint( NS_SERVER, from, "statusResponse\n%s\n%s", infostring, status );
00356 }
00357
00358
00359
00360
00361
00362
00363
00364
00365
00366 void SVC_Info( netadr_t from ) {
00367 int i, count;
00368 char *gamedir;
00369 char infostring[MAX_INFO_STRING];
00370
00371
00372 if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER || Cvar_VariableValue("ui_singlePlayerActive")) {
00373 return;
00374 }
00375
00376
00377 count = 0;
00378 for ( i = sv_privateClients->integer ; i < sv_maxclients->integer ; i++ ) {
00379 if ( svs.clients[i].state >= CS_CONNECTED ) {
00380 count++;
00381 }
00382 }
00383
00384 infostring[0] = 0;
00385
00386
00387
00388 Info_SetValueForKey( infostring, "challenge", Cmd_Argv(1) );
00389
00390 Info_SetValueForKey( infostring, "protocol", va("%i", PROTOCOL_VERSION) );
00391 Info_SetValueForKey( infostring, "hostname", sv_hostname->string );
00392 Info_SetValueForKey( infostring, "mapname", sv_mapname->string );
00393 Info_SetValueForKey( infostring, "clients", va("%i", count) );
00394 Info_SetValueForKey( infostring, "sv_maxclients",
00395 va("%i", sv_maxclients->integer - sv_privateClients->integer ) );
00396 Info_SetValueForKey( infostring, "gametype", va("%i", sv_gametype->integer ) );
00397 Info_SetValueForKey( infostring, "pure", va("%i", sv_pure->integer ) );
00398
00399 if( sv_minPing->integer ) {
00400 Info_SetValueForKey( infostring, "minPing", va("%i", sv_minPing->integer) );
00401 }
00402 if( sv_maxPing->integer ) {
00403 Info_SetValueForKey( infostring, "maxPing", va("%i", sv_maxPing->integer) );
00404 }
00405 gamedir = Cvar_VariableString( "fs_game" );
00406 if( *gamedir ) {
00407 Info_SetValueForKey( infostring, "game", gamedir );
00408 }
00409
00410 NET_OutOfBandPrint( NS_SERVER, from, "infoResponse\n%s", infostring );
00411 }
00412
00413
00414
00415
00416
00417
00418
00419 void SV_FlushRedirect( char *outputbuf ) {
00420 NET_OutOfBandPrint( NS_SERVER, svs.redirectAddress, "print\n%s", outputbuf );
00421 }
00422
00423
00424
00425
00426
00427
00428
00429
00430
00431
00432 void SVC_RemoteCommand( netadr_t from, msg_t *msg ) {
00433 qboolean valid;
00434 unsigned int time;
00435 char remaining[1024];
00436
00437
00438 #define SV_OUTPUTBUF_LENGTH (1024 - 16)
00439 char sv_outputbuf[SV_OUTPUTBUF_LENGTH];
00440 static unsigned int lasttime = 0;
00441 char *cmd_aux;
00442
00443
00444 time = Com_Milliseconds();
00445 if (time<(lasttime+500)) {
00446 return;
00447 }
00448 lasttime = time;
00449
00450 if ( !strlen( sv_rconPassword->string ) ||
00451 strcmp (Cmd_Argv(1), sv_rconPassword->string) ) {
00452 valid = qfalse;
00453 Com_Printf ("Bad rcon from %s:\n%s\n", NET_AdrToString (from), Cmd_Argv(2) );
00454 } else {
00455 valid = qtrue;
00456 Com_Printf ("Rcon from %s:\n%s\n", NET_AdrToString (from), Cmd_Argv(2) );
00457 }
00458
00459
00460 svs.redirectAddress = from;
00461 Com_BeginRedirect (sv_outputbuf, SV_OUTPUTBUF_LENGTH, SV_FlushRedirect);
00462
00463 if ( !strlen( sv_rconPassword->string ) ) {
00464 Com_Printf ("No rconpassword set on the server.\n");
00465 } else if ( !valid ) {
00466 Com_Printf ("Bad rconpassword.\n");
00467 } else {
00468 remaining[0] = 0;
00469
00470
00471
00472
00473
00474 cmd_aux = Cmd_Cmd();
00475 cmd_aux+=4;
00476 while(cmd_aux[0]==' ')
00477 cmd_aux++;
00478 while(cmd_aux[0] && cmd_aux[0]!=' ')
00479 cmd_aux++;
00480 while(cmd_aux[0]==' ')
00481 cmd_aux++;
00482
00483 Q_strcat( remaining, sizeof(remaining), cmd_aux);
00484
00485 Cmd_ExecuteString (remaining);
00486
00487 }
00488
00489 Com_EndRedirect ();
00490 }
00491
00492
00493
00494
00495
00496
00497
00498
00499
00500
00501
00502 void SV_ConnectionlessPacket( netadr_t from, msg_t *msg ) {
00503 char *s;
00504 char *c;
00505
00506 MSG_BeginReadingOOB( msg );
00507 MSG_ReadLong( msg );
00508
00509 if (!Q_strncmp("connect", &msg->data[4], 7)) {
00510 Huff_Decompress(msg, 12);
00511 }
00512
00513 s = MSG_ReadStringLine( msg );
00514 Cmd_TokenizeString( s );
00515
00516 c = Cmd_Argv(0);
00517 Com_DPrintf ("SV packet %s : %s\n", NET_AdrToString(from), c);
00518
00519 if (!Q_stricmp(c, "getstatus")) {
00520 SVC_Status( from );
00521 } else if (!Q_stricmp(c, "getinfo")) {
00522 SVC_Info( from );
00523 } else if (!Q_stricmp(c, "getchallenge")) {
00524 SV_GetChallenge( from );
00525 } else if (!Q_stricmp(c, "connect")) {
00526 SV_DirectConnect( from );
00527 } else if (!Q_stricmp(c, "ipAuthorize")) {
00528 SV_AuthorizeIpPacket( from );
00529 } else if (!Q_stricmp(c, "rcon")) {
00530 SVC_RemoteCommand( from, msg );
00531 } else if (!Q_stricmp(c, "disconnect")) {
00532
00533
00534
00535 } else {
00536 Com_DPrintf ("bad connectionless packet from %s:\n%s\n"
00537 , NET_AdrToString (from), s);
00538 }
00539 }
00540
00541
00542
00543
00544
00545
00546
00547
00548 void SV_PacketEvent( netadr_t from, msg_t *msg ) {
00549 int i;
00550 client_t *cl;
00551 int qport;
00552
00553
00554 if ( msg->cursize >= 4 && *(int *)msg->data == -1) {
00555 SV_ConnectionlessPacket( from, msg );
00556 return;
00557 }
00558
00559
00560
00561 MSG_BeginReadingOOB( msg );
00562 MSG_ReadLong( msg );
00563 qport = MSG_ReadShort( msg ) & 0xffff;
00564
00565
00566 for (i=0, cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++) {
00567 if (cl->state == CS_FREE) {
00568 continue;
00569 }
00570 if ( !NET_CompareBaseAdr( from, cl->netchan.remoteAddress ) ) {
00571 continue;
00572 }
00573
00574
00575 if (cl->netchan.qport != qport) {
00576 continue;
00577 }
00578
00579
00580
00581
00582 if (cl->netchan.remoteAddress.port != from.port) {
00583 Com_Printf( "SV_PacketEvent: fixing up a translated port\n" );
00584 cl->netchan.remoteAddress.port = from.port;
00585 }
00586
00587
00588 if (SV_Netchan_Process(cl, msg)) {
00589
00590
00591
00592 if (cl->state != CS_ZOMBIE) {
00593 cl->lastPacketTime = svs.time;
00594 SV_ExecuteClientMessage( cl, msg );
00595 }
00596 }
00597 return;
00598 }
00599
00600
00601
00602 NET_OutOfBandPrint( NS_SERVER, from, "disconnect" );
00603 }
00604
00605
00606
00607
00608
00609
00610
00611
00612
00613 void SV_CalcPings( void ) {
00614 int i, j;
00615 client_t *cl;
00616 int total, count;
00617 int delta;
00618 playerState_t *ps;
00619
00620 for (i=0 ; i < sv_maxclients->integer ; i++) {
00621 cl = &svs.clients[i];
00622 if ( cl->state != CS_ACTIVE ) {
00623 cl->ping = 999;
00624 continue;
00625 }
00626 if ( !cl->gentity ) {
00627 cl->ping = 999;
00628 continue;
00629 }
00630 if ( cl->gentity->r.svFlags & SVF_BOT ) {
00631 cl->ping = 0;
00632 continue;
00633 }
00634
00635 total = 0;
00636 count = 0;
00637 for ( j = 0 ; j < PACKET_BACKUP ; j++ ) {
00638 if ( cl->frames[j].messageAcked <= 0 ) {
00639 continue;
00640 }
00641 delta = cl->frames[j].messageAcked - cl->frames[j].messageSent;
00642 count++;
00643 total += delta;
00644 }
00645 if (!count) {
00646 cl->ping = 999;
00647 } else {
00648 cl->ping = total/count;
00649 if ( cl->ping > 999 ) {
00650 cl->ping = 999;
00651 }
00652 }
00653
00654
00655 ps = SV_GameClientNum( i );
00656 ps->ping = cl->ping;
00657 }
00658 }
00659
00660
00661
00662
00663
00664
00665
00666
00667
00668
00669
00670
00671
00672
00673 void SV_CheckTimeouts( void ) {
00674 int i;
00675 client_t *cl;
00676 int droppoint;
00677 int zombiepoint;
00678
00679 droppoint = svs.time - 1000 * sv_timeout->integer;
00680 zombiepoint = svs.time - 1000 * sv_zombietime->integer;
00681
00682 for (i=0,cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++) {
00683
00684 if (cl->lastPacketTime > svs.time) {
00685 cl->lastPacketTime = svs.time;
00686 }
00687
00688 if (cl->state == CS_ZOMBIE
00689 && cl->lastPacketTime < zombiepoint) {
00690
00691 Com_DPrintf( "Going from CS_ZOMBIE to CS_FREE for client %d\n", i );
00692 cl->state = CS_FREE;
00693 continue;
00694 }
00695 if ( cl->state >= CS_CONNECTED && cl->lastPacketTime < droppoint) {
00696
00697
00698 if ( ++cl->timeoutCount > 5 ) {
00699 SV_DropClient (cl, "timed out");
00700 cl->state = CS_FREE;
00701 }
00702 } else {
00703 cl->timeoutCount = 0;
00704 }
00705 }
00706 }
00707
00708
00709
00710
00711
00712
00713
00714 qboolean SV_CheckPaused( void ) {
00715 int count;
00716 client_t *cl;
00717 int i;
00718
00719 if ( !cl_paused->integer ) {
00720 return qfalse;
00721 }
00722
00723
00724 count = 0;
00725 for (i=0,cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++) {
00726 if ( cl->state >= CS_CONNECTED && cl->netchan.remoteAddress.type != NA_BOT ) {
00727 count++;
00728 }
00729 }
00730
00731 if ( count > 1 ) {
00732
00733 if (sv_paused->integer)
00734 Cvar_Set("sv_paused", "0");
00735 return qfalse;
00736 }
00737
00738 if (!sv_paused->integer)
00739 Cvar_Set("sv_paused", "1");
00740 return qtrue;
00741 }
00742
00743
00744
00745
00746
00747
00748
00749
00750
00751 void SV_Frame( int msec ) {
00752 int frameMsec;
00753 int startTime;
00754
00755
00756 if ( sv_killserver->integer ) {
00757 SV_Shutdown ("Server was killed.\n");
00758 Cvar_Set( "sv_killserver", "0" );
00759 return;
00760 }
00761
00762 if ( !com_sv_running->integer ) {
00763 return;
00764 }
00765
00766
00767 if ( SV_CheckPaused() ) {
00768 return;
00769 }
00770
00771
00772 if ( sv_fps->integer < 1 ) {
00773 Cvar_Set( "sv_fps", "10" );
00774 }
00775 frameMsec = 1000 / sv_fps->integer ;
00776
00777 sv.timeResidual += msec;
00778
00779 if (!com_dedicated->integer) SV_BotFrame( svs.time + sv.timeResidual );
00780
00781 if ( com_dedicated->integer && sv.timeResidual < frameMsec ) {
00782
00783
00784 NET_Sleep(frameMsec - sv.timeResidual);
00785 return;
00786 }
00787
00788
00789
00790
00791
00792 if ( svs.time > 0x70000000 ) {
00793 SV_Shutdown( "Restarting server due to time wrapping" );
00794 Cbuf_AddText( "vstr nextmap\n" );
00795 return;
00796 }
00797
00798 if ( svs.nextSnapshotEntities >= 0x7FFFFFFE - svs.numSnapshotEntities ) {
00799 SV_Shutdown( "Restarting server due to numSnapshotEntities wrapping" );
00800 Cbuf_AddText( "vstr nextmap\n" );
00801 return;
00802 }
00803
00804 if( sv.restartTime && svs.time >= sv.restartTime ) {
00805 sv.restartTime = 0;
00806 Cbuf_AddText( "map_restart 0\n" );
00807 return;
00808 }
00809
00810
00811 if ( cvar_modifiedFlags & CVAR_SERVERINFO ) {
00812 SV_SetConfigstring( CS_SERVERINFO, Cvar_InfoString( CVAR_SERVERINFO ) );
00813 cvar_modifiedFlags &= ~CVAR_SERVERINFO;
00814 }
00815 if ( cvar_modifiedFlags & CVAR_SYSTEMINFO ) {
00816 SV_SetConfigstring( CS_SYSTEMINFO, Cvar_InfoString_Big( CVAR_SYSTEMINFO ) );
00817 cvar_modifiedFlags &= ~CVAR_SYSTEMINFO;
00818 }
00819
00820 if ( com_speeds->integer ) {
00821 startTime = Sys_Milliseconds ();
00822 } else {
00823 startTime = 0;
00824 }
00825
00826
00827 SV_CalcPings();
00828
00829 if (com_dedicated->integer) SV_BotFrame( svs.time );
00830
00831
00832 while ( sv.timeResidual >= frameMsec ) {
00833 sv.timeResidual -= frameMsec;
00834 svs.time += frameMsec;
00835
00836
00837 VM_Call( gvm, GAME_RUN_FRAME, svs.time );
00838 }
00839
00840 if ( com_speeds->integer ) {
00841 time_game = Sys_Milliseconds () - startTime;
00842 }
00843
00844
00845 SV_CheckTimeouts();
00846
00847
00848 SV_SendClientMessages();
00849
00850
00851 SV_MasterHeartbeat();
00852 }
00853
00854
00855