]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/framework/async/AsyncServer.cpp
hello world
[icculus/iodoom3.git] / neo / framework / async / AsyncServer.cpp
1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. 
6
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).  
8
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code.  If not, see <http://www.gnu.org/licenses/>.
21
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code.  If not, please request a copy in writing from id Software at the address below.
23
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25
26 ===========================================================================
27 */
28
29 #include "../../idlib/precompiled.h"
30 #pragma hdrstop
31
32 #include "AsyncNetwork.h"
33
34 #include "../Session_local.h"
35
36 const int MIN_RECONNECT_TIME                    = 2000;
37 const int EMPTY_RESEND_TIME                             = 500;
38 const int PING_RESEND_TIME                              = 500;
39 const int NOINPUT_IDLE_TIME                             = 30000;
40
41 const int HEARTBEAT_MSEC                                = 5*60*1000;
42
43 // must be kept in sync with authReplyMsg_t
44 const char* authReplyMsg[] = {
45         //      "Waiting for authorization",
46         "#str_07204",
47         //      "Client unknown to auth",
48         "#str_07205",
49         //      "Access denied - CD Key in use",
50         "#str_07206",
51         //      "Auth custom message", // placeholder - we propagate a message from the master
52         "#str_07207",
53         //      "Authorize Server - Waiting for client"
54         "#str_07208"
55 };
56
57 const char* authReplyStr[] = {
58         "AUTH_NONE",
59         "AUTH_OK",
60         "AUTH_WAIT",
61         "AUTH_DENY"
62 };
63
64 /*
65 ==================
66 idAsyncServer::idAsyncServer
67 ==================
68 */
69 idAsyncServer::idAsyncServer( void ) {
70         int i;
71
72         active = false;
73         realTime = 0;
74         serverTime = 0;
75         serverId = 0;
76         serverDataChecksum = 0;
77         localClientNum = -1;
78         gameInitId = 0;
79         gameFrame = 0;
80         gameTime = 0;
81         gameTimeResidual = 0;
82         memset( challenges, 0, sizeof( challenges ) );
83         memset( userCmds, 0, sizeof( userCmds ) );
84         for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
85                 ClearClient( i );
86         }
87         serverReloadingEngine = false;
88         nextHeartbeatTime = 0;
89         nextAsyncStatsTime = 0;
90         noRconOutput = true;
91         lastAuthTime = 0;
92
93         memset( stats_outrate, 0, sizeof( stats_outrate ) );
94         stats_current = 0;
95         stats_average_sum = 0;
96         stats_max = 0;
97         stats_max_index = 0;
98 }
99
100 /*
101 ==================
102 idAsyncServer::InitPort
103 ==================
104 */
105 bool idAsyncServer::InitPort( void ) {
106         int lastPort;
107
108         // if this is the first time we have spawned a server, open the UDP port
109         if ( !serverPort.GetPort() ) {
110                 if ( cvarSystem->GetCVarInteger( "net_port" ) != 0 ) {
111                         if ( !serverPort.InitForPort( cvarSystem->GetCVarInteger( "net_port" ) ) ) {
112                                 common->Printf( "Unable to open server on port %d (net_port)\n", cvarSystem->GetCVarInteger( "net_port" ) );
113                                 return false;
114                         }
115                 } else {
116                         // scan for multiple ports, in case other servers are running on this IP already
117                         for ( lastPort = 0; lastPort < NUM_SERVER_PORTS; lastPort++ ) {
118                                 if ( serverPort.InitForPort( PORT_SERVER + lastPort ) ) {
119                                         break;
120                                 }
121                         }
122                         if ( lastPort >= NUM_SERVER_PORTS ) {
123                                 common->Printf( "Unable to open server network port.\n" );
124                                 return false;
125                         }
126                 }
127         }
128
129         return true;
130 }
131
132 /*
133 ==================
134 idAsyncServer::ClosePort
135 ==================
136 */
137 void idAsyncServer::ClosePort( void ) {
138         int i;
139
140         serverPort.Close();
141         for ( i = 0; i < MAX_CHALLENGES; i++ ) {
142                 challenges[ i ].authReplyPrint.Clear();
143         }
144 }
145
146 /*
147 ==================
148 idAsyncServer::Spawn
149 ==================
150 */
151 void idAsyncServer::Spawn( void ) {
152         int                     i, size;
153         byte            msgBuf[MAX_MESSAGE_SIZE];
154         netadr_t        from;
155
156         // shutdown any current game
157         session->Stop();
158
159         if ( active ) {
160                 return;
161         }
162
163         if ( !InitPort() ) {
164                 return;
165         }
166
167         // trash any currently pending packets
168         while( serverPort.GetPacket( from, msgBuf, size, sizeof( msgBuf ) ) ) {
169         }
170
171         // reset cheats cvars
172         if ( !idAsyncNetwork::allowCheats.GetBool() ) {
173                 cvarSystem->ResetFlaggedVariables( CVAR_CHEAT );
174         }
175
176         memset( challenges, 0, sizeof( challenges ) );
177         memset( userCmds, 0, sizeof( userCmds ) );
178         for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
179                 ClearClient( i );
180         }
181
182         common->Printf( "Server spawned on port %i.\n", serverPort.GetPort() );
183
184         // calculate a checksum on some of the essential data used
185         serverDataChecksum = declManager->GetChecksum();
186
187         // get a pseudo random server id, but don't use the id which is reserved for connectionless packets
188         serverId = Sys_Milliseconds() & CONNECTIONLESS_MESSAGE_ID_MASK;
189
190         active = true;
191
192         nextHeartbeatTime = 0;
193         nextAsyncStatsTime = 0;
194
195         ExecuteMapChange();
196 }
197
198 /*
199 ==================
200 idAsyncServer::Kill
201 ==================
202 */
203 void idAsyncServer::Kill( void ) {
204         int i, j;
205
206         if ( !active ) {
207                 return;
208         }
209
210         // drop all clients
211         for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
212                 DropClient( i, "#str_07135" );
213         }
214
215         // send some empty messages to the zombie clients to make sure they disconnect
216         for ( j = 0; j < 4; j++ ) {
217                 for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
218                         if ( clients[i].clientState == SCS_ZOMBIE ) {
219                                 if ( clients[i].channel.UnsentFragmentsLeft() ) {
220                                         clients[i].channel.SendNextFragment( serverPort, serverTime );
221                                 } else {
222                                         SendEmptyToClient( i, true );
223                                 }
224                         }
225                 }
226                 Sys_Sleep( 10 );
227         }
228
229         // reset any pureness
230         fileSystem->ClearPureChecksums();
231
232         active = false;
233
234         // shutdown any current game
235         session->Stop();
236 }
237
238 /*
239 ==================
240 idAsyncServer::ExecuteMapChange
241 ==================
242 */
243 void idAsyncServer::ExecuteMapChange( void ) {
244         int                     i;
245         idBitMsg        msg;
246         byte            msgBuf[MAX_MESSAGE_SIZE];
247         idStr           mapName;
248         findFile_t      ff;
249         bool            addonReload = false;
250         char            bestGameType[ MAX_STRING_CHARS ];
251
252         assert( active );
253
254         // reset any pureness
255         fileSystem->ClearPureChecksums();
256
257         // make sure the map/gametype combo is good
258         game->GetBestGameType( cvarSystem->GetCVarString("si_map"), cvarSystem->GetCVarString("si_gametype"), bestGameType );
259         cvarSystem->SetCVarString("si_gametype", bestGameType );
260
261         // initialize map settings
262         cmdSystem->BufferCommandText( CMD_EXEC_NOW, "rescanSI" );
263
264         sprintf( mapName, "maps/%s", sessLocal.mapSpawnData.serverInfo.GetString( "si_map" ) );
265         mapName.SetFileExtension( ".map" );
266         ff = fileSystem->FindFile( mapName, !serverReloadingEngine );
267         switch( ff ) {
268         case FIND_NO:
269                 common->Printf( "Can't find map %s\n", mapName.c_str() );
270                 cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "disconnect\n" );
271                 return;
272         case FIND_ADDON:
273                 // NOTE: we have no problem with addon dependencies here because if the map is in
274                 // an addon pack that's already on search list, then all it's deps are assumed to be on search as well
275                 common->Printf( "map %s is in an addon pak - reloading\n", mapName.c_str() );
276                 addonReload = true;
277                 break;
278         default:
279                 break;
280         }
281
282         // if we are asked to do a full reload, the strategy is completely different
283         if ( !serverReloadingEngine && ( addonReload || idAsyncNetwork::serverReloadEngine.GetInteger() != 0 ) ) {
284                 if ( idAsyncNetwork::serverReloadEngine.GetInteger() != 0 ) {
285                         common->Printf( "net_serverReloadEngine enabled - doing a full reload\n" );
286                 }
287                 // tell the clients to reconnect
288                 // FIXME: shouldn't they wait for the new pure list, then reload?
289                 // in a lot of cases this is going to trigger two reloadEngines for the clients
290                 // one to restart, the other one to set paks right ( with addon for instance )
291                 // can fix by reconnecting without reloading and waiting for the server to tell..
292                 for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
293                         if ( clients[ i ].clientState >= SCS_PUREWAIT && i != localClientNum ) {
294                                 msg.Init( msgBuf, sizeof( msgBuf ) );
295                                 msg.WriteByte( SERVER_RELIABLE_MESSAGE_RELOAD );
296                                 SendReliableMessage( i, msg );
297                                 clients[ i ].clientState = SCS_ZOMBIE; // so we don't bother sending a disconnect
298                         }
299                 }
300                 cmdSystem->BufferCommandText( CMD_EXEC_NOW, "reloadEngine" );
301                 serverReloadingEngine = true; // don't get caught in endless loop
302                 cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "spawnServer\n" );
303                 // decrease feature
304                 if ( idAsyncNetwork::serverReloadEngine.GetInteger() > 0 ) {
305                         idAsyncNetwork::serverReloadEngine.SetInteger( idAsyncNetwork::serverReloadEngine.GetInteger() - 1 );
306                 }
307                 return;
308         }
309         serverReloadingEngine = false;
310
311         serverTime = 0;
312
313         // initialize game id and time
314         gameInitId ^= Sys_Milliseconds();       // NOTE: make sure the gameInitId is always a positive number because negative numbers have special meaning
315         gameFrame = 0;
316         gameTime = 0;
317         gameTimeResidual = 0;
318         memset( userCmds, 0, sizeof( userCmds ) );
319
320         if ( idAsyncNetwork::serverDedicated.GetInteger() == 0 ) {
321                 InitLocalClient( 0 );
322         } else {
323                 localClientNum = -1;
324         }
325
326         // re-initialize all connected clients for the new map
327         for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
328                 if ( clients[i].clientState >= SCS_PUREWAIT && i != localClientNum ) {
329
330                         InitClient( i, clients[i].clientId, clients[i].clientRate );
331
332                         SendGameInitToClient( i );
333
334                         if ( sessLocal.mapSpawnData.serverInfo.GetBool( "si_pure" ) ) {
335                                 clients[ i ].clientState = SCS_PUREWAIT;
336                         }
337                 }
338         }
339
340         // setup the game pak checksums
341         // since this is not dependant on si_pure we catch anything bad before loading map
342         if ( sessLocal.mapSpawnData.serverInfo.GetInt( "si_pure" ) ) {
343                 if ( !fileSystem->UpdateGamePakChecksums( ) ) {
344                         session->MessageBox( MSG_OK, common->GetLanguageDict()->GetString ( "#str_04337" ), common->GetLanguageDict()->GetString ( "#str_04338" ), true );
345                         cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "disconnect\n" );
346                         return;
347                 }
348         }
349
350         // load map
351         sessLocal.ExecuteMapChange();
352
353         if ( localClientNum >= 0 ) {
354                 BeginLocalClient();
355         } else {
356                 game->SetLocalClient( -1 );
357         }
358
359         if ( sessLocal.mapSpawnData.serverInfo.GetInt( "si_pure" ) ) {
360                 // lock down the pak list
361                 fileSystem->UpdatePureServerChecksums( );
362                 // tell the clients so they can work out their pure lists
363                 for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
364                         if ( clients[ i ].clientState == SCS_PUREWAIT ) {
365                                 if ( !SendReliablePureToClient( i ) ) {
366                                         clients[ i ].clientState = SCS_CONNECTED;
367                                 }
368                         }
369                 }
370         }
371
372         // serverTime gets reset, force a heartbeat so timings restart
373         MasterHeartbeat( true );
374 }
375
376 /*
377 ==================
378 idAsyncServer::GetPort
379 ==================
380 */
381 int idAsyncServer::GetPort( void ) const {
382         return serverPort.GetPort();
383 }
384
385 /*
386 ===============
387 idAsyncServer::GetBoundAdr
388 ===============
389 */
390 netadr_t idAsyncServer::GetBoundAdr( void ) const {
391         return serverPort.GetAdr();
392 }
393
394 /*
395 ==================
396 idAsyncServer::GetOutgoingRate
397 ==================
398 */
399 int idAsyncServer::GetOutgoingRate( void ) const {
400         int i, rate;
401
402         rate = 0;
403         for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
404                 const serverClient_t &client = clients[i];
405
406                 if ( client.clientState >= SCS_CONNECTED ) {
407                         rate += client.channel.GetOutgoingRate();
408                 }
409         }
410         return rate;
411 }
412
413 /*
414 ==================
415 idAsyncServer::GetIncomingRate
416 ==================
417 */
418 int idAsyncServer::GetIncomingRate( void ) const {
419         int i, rate;
420
421         rate = 0;
422         for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
423                 const serverClient_t &client = clients[i];
424
425                 if ( client.clientState >= SCS_CONNECTED ) {
426                         rate += client.channel.GetIncomingRate();
427                 }
428         }
429         return rate;
430 }
431
432 /*
433 ==================
434 idAsyncServer::IsClientInGame
435 ==================
436 */
437 bool idAsyncServer::IsClientInGame( int clientNum ) const {
438         return ( clients[clientNum].clientState >= SCS_INGAME );
439 }
440
441 /*
442 ==================
443 idAsyncServer::GetClientPing
444 ==================
445 */
446 int idAsyncServer::GetClientPing( int clientNum ) const {
447         const serverClient_t &client = clients[clientNum];
448
449         if ( client.clientState < SCS_CONNECTED ) {
450                 return 99999;
451         } else {
452                 return client.clientPing;
453         }
454 }
455
456 /*
457 ==================
458 idAsyncServer::GetClientPrediction
459 ==================
460 */
461 int idAsyncServer::GetClientPrediction( int clientNum ) const {
462         const serverClient_t &client = clients[clientNum];
463
464         if ( client.clientState < SCS_CONNECTED ) {
465                 return 99999;
466         } else {
467                 return client.clientPrediction;
468         }
469 }
470
471 /*
472 ==================
473 idAsyncServer::GetClientTimeSinceLastPacket
474 ==================
475 */
476 int idAsyncServer::GetClientTimeSinceLastPacket( int clientNum ) const {
477         const serverClient_t &client = clients[clientNum];
478
479         if ( client.clientState < SCS_CONNECTED ) {
480                 return 99999;
481         } else {
482                 return serverTime - client.lastPacketTime;
483         }
484 }
485
486 /*
487 ==================
488 idAsyncServer::GetClientTimeSinceLastInput
489 ==================
490 */
491 int idAsyncServer::GetClientTimeSinceLastInput( int clientNum ) const {
492         const serverClient_t &client = clients[clientNum];
493
494         if ( client.clientState < SCS_CONNECTED ) {
495                 return 99999;
496         } else {
497                 return serverTime - client.lastInputTime;
498         }
499 }
500
501 /*
502 ==================
503 idAsyncServer::GetClientOutgoingRate
504 ==================
505 */
506 int idAsyncServer::GetClientOutgoingRate( int clientNum ) const {
507         const serverClient_t &client = clients[clientNum];
508
509         if ( client.clientState < SCS_CONNECTED ) {
510                 return -1;
511         } else {
512                 return client.channel.GetOutgoingRate();
513         }
514 }
515
516 /*
517 ==================
518 idAsyncServer::GetClientIncomingRate
519 ==================
520 */
521 int idAsyncServer::GetClientIncomingRate( int clientNum ) const {
522         const serverClient_t &client = clients[clientNum];
523
524         if ( client.clientState < SCS_CONNECTED ) {
525                 return -1;
526         } else {
527                 return client.channel.GetIncomingRate();
528         }
529 }
530
531 /*
532 ==================
533 idAsyncServer::GetClientOutgoingCompression
534 ==================
535 */
536 float idAsyncServer::GetClientOutgoingCompression( int clientNum ) const {
537         const serverClient_t &client = clients[clientNum];
538
539         if ( client.clientState < SCS_CONNECTED ) {
540                 return 0.0f;
541         } else {
542                 return client.channel.GetOutgoingCompression();
543         }
544 }
545
546 /*
547 ==================
548 idAsyncServer::GetClientIncomingCompression
549 ==================
550 */
551 float idAsyncServer::GetClientIncomingCompression( int clientNum ) const {
552         const serverClient_t &client = clients[clientNum];
553
554         if ( client.clientState < SCS_CONNECTED ) {
555                 return 0.0f;
556         } else {
557                 return client.channel.GetIncomingCompression();
558         }
559 }
560
561 /*
562 ==================
563 idAsyncServer::GetClientIncomingPacketLoss
564 ==================
565 */
566 float idAsyncServer::GetClientIncomingPacketLoss( int clientNum ) const {
567         const serverClient_t &client = clients[clientNum];
568
569         if ( client.clientState < SCS_CONNECTED ) {
570                 return 0.0f;
571         } else {
572                 return client.channel.GetIncomingPacketLoss();
573         }
574 }
575
576 /*
577 ==================
578 idAsyncServer::GetNumClients
579 ==================
580 */
581 int idAsyncServer::GetNumClients( void ) const {
582         int ret = 0;
583         for ( int i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
584                 if ( clients[ i ].clientState >= SCS_CONNECTED ) {
585                         ret++;
586                 }
587         }
588         return ret;
589 }
590
591 /*
592 ==================
593 idAsyncServer::GetNumIdleClients
594 ==================
595 */
596 int idAsyncServer::GetNumIdleClients( void ) const {
597         int ret = 0;
598         for ( int i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
599                 if ( clients[ i ].clientState >= SCS_CONNECTED ) {
600                         if ( serverTime - clients[ i ].lastInputTime > NOINPUT_IDLE_TIME ) {
601                                 ret++;
602                         }
603                 }
604         }
605         return ret;
606 }
607
608 /*
609 ==================
610 idAsyncServer::DuplicateUsercmds
611 ==================
612 */
613 void idAsyncServer::DuplicateUsercmds( int frame, int time ) {
614         int i, previousIndex, currentIndex;
615
616         previousIndex = ( frame - 1 ) & ( MAX_USERCMD_BACKUP - 1 );
617         currentIndex = frame & ( MAX_USERCMD_BACKUP - 1 );
618
619         // duplicate previous user commands if no new commands are available for a client
620         for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
621                 if ( clients[i].clientState == SCS_FREE ) {
622                         continue;
623                 }
624
625                 if ( idAsyncNetwork::DuplicateUsercmd( userCmds[previousIndex][i], userCmds[currentIndex][i], frame, time ) ) {
626                         clients[i].numDuplicatedUsercmds++;
627                 }
628         }
629 }
630
631 /*
632 ==================
633 idAsyncServer::ClearClient
634 ==================
635 */
636 void idAsyncServer::ClearClient( int clientNum ) {
637         serverClient_t &client = clients[clientNum];
638         client.clientId = 0;
639         client.clientState = SCS_FREE;
640         client.clientPrediction = 0;
641         client.clientAheadTime = 0;
642         client.clientRate = 0;
643         client.clientPing = 0;
644         client.gameInitSequence = 0;
645         client.gameFrame = 0;
646         client.gameTime = 0;
647         client.channel.Shutdown();
648         client.lastConnectTime = 0;
649         client.lastEmptyTime = 0;
650         client.lastPingTime = 0;
651         client.lastSnapshotTime = 0;
652         client.lastPacketTime = 0;
653         client.lastInputTime = 0;
654         client.snapshotSequence = 0;
655         client.acknowledgeSnapshotSequence = 0;
656         client.numDuplicatedUsercmds = 0;
657 }
658
659 /*
660 ==================
661 idAsyncServer::InitClient
662 ==================
663 */
664 void idAsyncServer::InitClient( int clientNum, int clientId, int clientRate ) {
665         int i;
666
667         // clear the user info
668         sessLocal.mapSpawnData.userInfo[ clientNum ].Clear();   // always start with a clean base
669
670         // clear the server client
671         serverClient_t &client = clients[clientNum];
672         client.clientId = clientId;
673         client.clientState = SCS_CONNECTED;
674         client.clientPrediction = 0;
675         client.clientAheadTime = 0;
676         client.gameInitSequence = -1;
677         client.gameFrame = 0;
678         client.gameTime = 0;
679         client.channel.ResetRate();
680         client.clientRate = clientRate ? clientRate : idAsyncNetwork::serverMaxClientRate.GetInteger();
681         client.channel.SetMaxOutgoingRate( Min( idAsyncNetwork::serverMaxClientRate.GetInteger(), client.clientRate ) );
682         client.clientPing = 0;
683         client.lastConnectTime = serverTime;
684         client.lastEmptyTime = serverTime;
685         client.lastPingTime = serverTime;
686         client.lastSnapshotTime = serverTime;
687         client.lastPacketTime = serverTime;
688         client.lastInputTime = serverTime;
689         client.acknowledgeSnapshotSequence = 0;
690         client.numDuplicatedUsercmds = 0;
691
692         // clear the user commands
693         for ( i = 0; i < MAX_USERCMD_BACKUP; i++ ) {
694                 memset( &userCmds[i][clientNum], 0, sizeof( userCmds[i][clientNum] ) );
695         }
696
697         // let the game know a player connected
698         game->ServerClientConnect( clientNum, client.guid );
699 }
700
701 /*
702 ==================
703 idAsyncServer::InitLocalClient
704 ==================
705 */
706 void idAsyncServer::InitLocalClient( int clientNum ) {
707         netadr_t badAddress;
708
709         localClientNum = clientNum;
710         InitClient( clientNum, 0, 0 );
711         memset( &badAddress, 0, sizeof( badAddress ) );
712         badAddress.type = NA_BAD;
713         clients[clientNum].channel.Init( badAddress, serverId );
714         clients[clientNum].clientState = SCS_INGAME;
715         sessLocal.mapSpawnData.userInfo[clientNum] = *cvarSystem->MoveCVarsToDict( CVAR_USERINFO );
716 }
717
718 /*
719 ==================
720 idAsyncServer::BeginLocalClient
721 ==================
722 */
723 void idAsyncServer::BeginLocalClient( void ) {
724         game->SetLocalClient( localClientNum );
725         game->SetUserInfo( localClientNum, sessLocal.mapSpawnData.userInfo[localClientNum], false, false );
726         game->ServerClientBegin( localClientNum );
727 }
728
729 /*
730 ==================
731 idAsyncServer::LocalClientInput
732 ==================
733 */
734 void idAsyncServer::LocalClientInput( void ) {
735         int index;
736
737         if ( localClientNum < 0 ) {
738                 return;
739         }
740
741         index = gameFrame & ( MAX_USERCMD_BACKUP - 1 );
742         userCmds[index][localClientNum] = usercmdGen->GetDirectUsercmd();
743         userCmds[index][localClientNum].gameFrame = gameFrame;
744         userCmds[index][localClientNum].gameTime = gameTime;
745         if ( idAsyncNetwork::UsercmdInputChanged( userCmds[( gameFrame - 1 ) & ( MAX_USERCMD_BACKUP - 1 )][localClientNum], userCmds[index][localClientNum] ) ) {
746                 clients[localClientNum].lastInputTime = serverTime;
747         }
748         clients[localClientNum].gameFrame = gameFrame;
749         clients[localClientNum].gameTime = gameTime;
750         clients[localClientNum].lastPacketTime = serverTime;
751 }
752
753 /*
754 ==================
755 idAsyncServer::DropClient
756 ==================
757 */
758 void idAsyncServer::DropClient( int clientNum, const char *reason ) {
759         int                     i;
760         idBitMsg        msg;
761         byte            msgBuf[MAX_MESSAGE_SIZE];
762
763         serverClient_t &client = clients[clientNum];
764
765         if ( client.clientState <= SCS_ZOMBIE ) {
766                 return;
767         }
768
769         if ( client.clientState >= SCS_PUREWAIT && clientNum != localClientNum ) {
770                 msg.Init( msgBuf, sizeof( msgBuf ) );
771                 msg.WriteByte( SERVER_RELIABLE_MESSAGE_DISCONNECT );
772                 msg.WriteLong( clientNum );
773                 msg.WriteString( reason );
774                 for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
775                         // clientNum so SCS_PUREWAIT client gets it's own disconnect msg
776                         if ( i == clientNum || clients[i].clientState >= SCS_CONNECTED ) {
777                                 SendReliableMessage( i, msg );
778                         }
779                 }
780         }
781
782         reason = common->GetLanguageDict()->GetString( reason );
783         common->Printf( "client %d %s\n", clientNum, reason );
784         cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "addChatLine \"%s^0 %s\"", sessLocal.mapSpawnData.userInfo[ clientNum ].GetString( "ui_name" ), reason ) );
785
786         // remove the player from the game
787         game->ServerClientDisconnect( clientNum );
788
789         client.clientState = SCS_ZOMBIE;
790 }
791
792 /*
793 ==================
794 idAsyncServer::SendReliableMessage
795 ==================
796 */
797 void idAsyncServer::SendReliableMessage( int clientNum, const idBitMsg &msg ) {
798         if ( clientNum == localClientNum ) {
799                 return;
800         }
801         if ( !clients[ clientNum ].channel.SendReliableMessage( msg ) ) {
802                 clients[ clientNum ].channel.ClearReliableMessages();
803                 DropClient( clientNum, "#str_07136" );
804         }
805 }
806
807 /*
808 ==================
809 idAsyncServer::CheckClientTimeouts
810 ==================
811 */
812 void idAsyncServer::CheckClientTimeouts( void ) {
813         int i, zombieTimeout, clientTimeout;
814
815         zombieTimeout = serverTime - idAsyncNetwork::serverZombieTimeout.GetInteger() * 1000;
816         clientTimeout = serverTime - idAsyncNetwork::serverClientTimeout.GetInteger() * 1000;
817
818         for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
819                 serverClient_t &client = clients[i];
820
821                 if ( i == localClientNum ) {
822                         continue;
823                 }
824
825                 if ( client.lastPacketTime > serverTime ) {
826                         client.lastPacketTime = serverTime;
827                         continue;
828                 }
829
830                 if ( client.clientState == SCS_ZOMBIE && client.lastPacketTime < zombieTimeout ) {
831                         client.channel.Shutdown();
832                         client.clientState = SCS_FREE;
833                         continue;
834                 }
835
836                 if ( client.clientState >= SCS_PUREWAIT && client.lastPacketTime < clientTimeout ) {
837                         DropClient( i, "#str_07137" );
838                         continue;
839                 }
840         }
841 }
842
843 /*
844 ==================
845 idAsyncServer::SendPrintBroadcast
846 ==================
847 */
848 void idAsyncServer::SendPrintBroadcast( const char *string ) {
849         int                     i;
850         idBitMsg        msg;
851         byte            msgBuf[MAX_MESSAGE_SIZE];
852
853         msg.Init( msgBuf, sizeof( msgBuf ) );
854         msg.WriteByte( SERVER_RELIABLE_MESSAGE_PRINT );
855         msg.WriteString( string );
856
857         for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
858                 if ( clients[i].clientState >= SCS_CONNECTED ) {
859                         SendReliableMessage( i, msg );
860                 }
861         }
862 }
863
864 /*
865 ==================
866 idAsyncServer::SendPrintToClient
867 ==================
868 */
869 void idAsyncServer::SendPrintToClient( int clientNum, const char *string ) {
870         idBitMsg        msg;
871         byte            msgBuf[MAX_MESSAGE_SIZE];
872
873         serverClient_t &client = clients[clientNum];
874
875         if ( client.clientState < SCS_CONNECTED ) {
876                 return;
877         }
878
879         msg.Init( msgBuf, sizeof( msgBuf ) );
880         msg.WriteByte( SERVER_RELIABLE_MESSAGE_PRINT );
881         msg.WriteString( string );
882
883         SendReliableMessage( clientNum, msg );
884 }
885
886 /*
887 ==================
888 idAsyncServer::SendUserInfoBroadcast
889 ==================
890 */
891 void idAsyncServer::SendUserInfoBroadcast( int userInfoNum, const idDict &info, bool sendToAll ) {
892         idBitMsg                msg;
893         byte                    msgBuf[MAX_MESSAGE_SIZE];
894         const idDict    *gameInfo;
895         bool                    gameModifiedInfo;
896
897         gameInfo = game->SetUserInfo( userInfoNum, info, false, true );
898         if ( gameInfo ) {
899                 gameModifiedInfo = true;
900         } else {
901                 gameModifiedInfo = false;
902                 gameInfo = &info;
903         }
904
905         if ( userInfoNum == localClientNum ) {
906                 common->DPrintf( "local user info modified by server\n" );
907                 cvarSystem->SetCVarsFromDict( *gameInfo );
908                 cvarSystem->ClearModifiedFlags( CVAR_USERINFO ); // don't emit back
909         }
910
911         msg.Init( msgBuf, sizeof( msgBuf ) );
912         msg.WriteByte( SERVER_RELIABLE_MESSAGE_CLIENTINFO );
913         msg.WriteByte( userInfoNum );
914         if ( gameModifiedInfo || sendToAll ) {
915                 msg.WriteBits( 0, 1 );
916         } else {
917                 msg.WriteBits( 1, 1 );
918         }
919
920 #if ID_CLIENTINFO_TAGS
921         msg.WriteLong( sessLocal.mapSpawnData.userInfo[userInfoNum].Checksum() );
922         common->DPrintf( "broadcast for client %d: 0x%x\n", userInfoNum, sessLocal.mapSpawnData.userInfo[userInfoNum].Checksum() );
923         sessLocal.mapSpawnData.userInfo[userInfoNum].Print();
924 #endif
925
926         if ( gameModifiedInfo || sendToAll ) {
927                 msg.WriteDeltaDict( *gameInfo, NULL );
928         } else {
929                 msg.WriteDeltaDict( *gameInfo, &sessLocal.mapSpawnData.userInfo[userInfoNum] );
930         }
931
932         for ( int i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
933                 if ( clients[i].clientState >= SCS_CONNECTED && ( sendToAll || i != userInfoNum || gameModifiedInfo ) ) {
934                         SendReliableMessage( i, msg );
935                 }
936         }
937
938         sessLocal.mapSpawnData.userInfo[userInfoNum] = *gameInfo;
939 }
940
941 /*
942 ==================
943 idAsyncServer::UpdateUI
944 if the game modifies userInfo, it will call this through command system
945 we then need to get the info from the game, and broadcast to clients
946 ( using DeltaDict and our current mapSpawnData as a base )
947 ==================
948 */
949 void idAsyncServer::UpdateUI( int clientNum ) {
950         const idDict    *info = game->GetUserInfo( clientNum );
951
952         if ( !info ) {
953                 common->Warning( "idAsyncServer::UpdateUI: no info from game\n" );
954                 return;
955         }
956
957         SendUserInfoBroadcast( clientNum, *info, true );
958 }
959
960 /*
961 ==================
962 idAsyncServer::SendUserInfoToClient
963 ==================
964 */
965 void idAsyncServer::SendUserInfoToClient( int clientNum, int userInfoNum, const idDict &info ) {
966         idBitMsg        msg;
967         byte            msgBuf[MAX_MESSAGE_SIZE];
968
969         if ( clients[clientNum].clientState < SCS_CONNECTED ) {
970                 return;
971         }
972
973         msg.Init( msgBuf, sizeof( msgBuf ) );
974         msg.WriteByte( SERVER_RELIABLE_MESSAGE_CLIENTINFO );
975         msg.WriteByte( userInfoNum );
976         msg.WriteBits( 0, 1 );
977
978 #if ID_CLIENTINFO_TAGS
979         msg.WriteLong( 0 );
980         common->DPrintf( "user info %d to client %d: NULL base\n", userInfoNum, clientNum );
981 #endif
982
983         msg.WriteDeltaDict( info, NULL );
984
985         SendReliableMessage( clientNum, msg );
986 }
987
988 /*
989 ==================
990 idAsyncServer::SendSyncedCvarsBroadcast
991 ==================
992 */
993 void idAsyncServer::SendSyncedCvarsBroadcast( const idDict &cvars ) {
994         idBitMsg        msg;
995         byte            msgBuf[MAX_MESSAGE_SIZE];
996         int                     i;
997
998         msg.Init( msgBuf, sizeof( msgBuf ) );
999         msg.WriteByte( SERVER_RELIABLE_MESSAGE_SYNCEDCVARS );
1000         msg.WriteDeltaDict( cvars, &sessLocal.mapSpawnData.syncedCVars );
1001
1002         for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
1003                 if ( clients[i].clientState >= SCS_CONNECTED ) {
1004                         SendReliableMessage( i, msg );
1005                 }
1006         }
1007
1008         sessLocal.mapSpawnData.syncedCVars = cvars;
1009 }
1010
1011 /*
1012 ==================
1013 idAsyncServer::SendSyncedCvarsToClient
1014 ==================
1015 */
1016 void idAsyncServer::SendSyncedCvarsToClient( int clientNum, const idDict &cvars ) {
1017         idBitMsg        msg;
1018         byte            msgBuf[MAX_MESSAGE_SIZE];
1019
1020         if ( clients[clientNum].clientState < SCS_CONNECTED ) {
1021                 return;
1022         }
1023
1024         msg.Init( msgBuf, sizeof( msgBuf ) );
1025         msg.WriteByte( SERVER_RELIABLE_MESSAGE_SYNCEDCVARS );
1026         msg.WriteDeltaDict( cvars, NULL );
1027
1028         SendReliableMessage( clientNum, msg );
1029 }
1030
1031 /*
1032 ==================
1033 idAsyncServer::SendApplySnapshotToClient
1034 ==================
1035 */
1036 void idAsyncServer::SendApplySnapshotToClient( int clientNum, int sequence ) {
1037         idBitMsg        msg;
1038         byte            msgBuf[MAX_MESSAGE_SIZE];
1039
1040         msg.Init( msgBuf, sizeof( msgBuf ) );
1041         msg.WriteByte( SERVER_RELIABLE_MESSAGE_APPLYSNAPSHOT );
1042         msg.WriteLong( sequence );
1043
1044         SendReliableMessage( clientNum, msg );
1045 }
1046
1047 /*
1048 ==================
1049 idAsyncServer::SendEmptyToClient
1050 ==================
1051 */
1052 bool idAsyncServer::SendEmptyToClient( int clientNum, bool force ) {
1053         idBitMsg        msg;
1054         byte            msgBuf[MAX_MESSAGE_SIZE];
1055
1056         serverClient_t &client = clients[clientNum];
1057
1058         if ( client.lastEmptyTime > realTime ) {
1059                 client.lastEmptyTime = realTime;
1060         }
1061
1062         if ( !force && ( realTime - client.lastEmptyTime < EMPTY_RESEND_TIME ) ) {
1063                 return false;
1064         }
1065
1066         if ( idAsyncNetwork::verbose.GetInteger() ) {
1067                 common->Printf( "sending empty to client %d: gameInitId = %d, gameFrame = %d, gameTime = %d\n", clientNum, gameInitId, gameFrame, gameTime );
1068         }
1069
1070         msg.Init( msgBuf, sizeof( msgBuf ) );
1071         msg.WriteLong( gameInitId );
1072         msg.WriteByte( SERVER_UNRELIABLE_MESSAGE_EMPTY );
1073
1074         client.channel.SendMessage( serverPort, serverTime, msg );
1075
1076         client.lastEmptyTime = realTime;
1077
1078         return true;
1079 }
1080
1081 /*
1082 ==================
1083 idAsyncServer::SendPingToClient
1084 ==================
1085 */
1086 bool idAsyncServer::SendPingToClient( int clientNum ) {
1087         idBitMsg        msg;
1088         byte            msgBuf[MAX_MESSAGE_SIZE];
1089
1090         serverClient_t &client = clients[clientNum];
1091
1092         if ( client.lastPingTime > realTime ) {
1093                 client.lastPingTime = realTime;
1094         }
1095
1096         if ( realTime - client.lastPingTime < PING_RESEND_TIME ) {
1097                 return false;
1098         }
1099
1100         if ( idAsyncNetwork::verbose.GetInteger() == 2 ) {
1101                 common->Printf( "pinging client %d: gameInitId = %d, gameFrame = %d, gameTime = %d\n", clientNum, gameInitId, gameFrame, gameTime );
1102         }
1103
1104         msg.Init( msgBuf, sizeof( msgBuf ) );
1105         msg.WriteLong( gameInitId );
1106         msg.WriteByte( SERVER_UNRELIABLE_MESSAGE_PING );
1107         msg.WriteLong( realTime );
1108
1109         client.channel.SendMessage( serverPort, serverTime, msg );
1110
1111         client.lastPingTime = realTime;
1112
1113         return true;
1114 }
1115
1116 /*
1117 ==================
1118 idAsyncServer::SendGameInitToClient
1119 ==================
1120 */
1121 void idAsyncServer::SendGameInitToClient( int clientNum ) {
1122         idBitMsg        msg;
1123         byte            msgBuf[MAX_MESSAGE_SIZE];
1124
1125         if ( idAsyncNetwork::verbose.GetInteger() ) {
1126                 common->Printf( "sending gameinit to client %d: gameInitId = %d, gameFrame = %d, gameTime = %d\n", clientNum, gameInitId, gameFrame, gameTime );
1127         }
1128
1129         serverClient_t &client = clients[clientNum];
1130
1131         // clear the unsent fragments. might flood winsock but that's ok
1132         while( client.channel.UnsentFragmentsLeft() ) {
1133                 client.channel.SendNextFragment( serverPort, serverTime );
1134         }                       
1135
1136         msg.Init( msgBuf, sizeof( msgBuf ) );
1137         msg.WriteLong( gameInitId );
1138         msg.WriteByte( SERVER_UNRELIABLE_MESSAGE_GAMEINIT );
1139         msg.WriteLong( gameFrame );
1140         msg.WriteLong( gameTime );
1141         msg.WriteDeltaDict( sessLocal.mapSpawnData.serverInfo, NULL );
1142         client.gameInitSequence = client.channel.SendMessage( serverPort, serverTime, msg );
1143 }
1144
1145 /*
1146 ==================
1147 idAsyncServer::SendSnapshotToClient
1148 ==================
1149 */
1150 bool idAsyncServer::SendSnapshotToClient( int clientNum ) {
1151         int                     i, j, index, numUsercmds;
1152         idBitMsg        msg;
1153         byte            msgBuf[MAX_MESSAGE_SIZE];
1154         usercmd_t *     last;
1155         byte            clientInPVS[MAX_ASYNC_CLIENTS >> 3];
1156
1157         serverClient_t &client = clients[clientNum];
1158
1159         if ( serverTime - client.lastSnapshotTime < idAsyncNetwork::serverSnapshotDelay.GetInteger() ) {
1160                 return false;
1161         }
1162
1163         if ( idAsyncNetwork::verbose.GetInteger() == 2 ) {
1164                 common->Printf( "sending snapshot to client %d: gameInitId = %d, gameFrame = %d, gameTime = %d\n", clientNum, gameInitId, gameFrame, gameTime );
1165         }
1166
1167         // how far is the client ahead of the server minus the packet delay
1168         client.clientAheadTime = client.gameTime - ( gameTime + gameTimeResidual );
1169
1170         // write the snapshot
1171         msg.Init( msgBuf, sizeof( msgBuf ) );
1172         msg.WriteLong( gameInitId );
1173         msg.WriteByte( SERVER_UNRELIABLE_MESSAGE_SNAPSHOT );
1174         msg.WriteLong( client.snapshotSequence );
1175         msg.WriteLong( gameFrame );
1176         msg.WriteLong( gameTime );
1177         msg.WriteByte( idMath::ClampChar( client.numDuplicatedUsercmds ) );
1178         msg.WriteShort( idMath::ClampShort( client.clientAheadTime ) );
1179
1180         // write the game snapshot
1181         game->ServerWriteSnapshot( clientNum, client.snapshotSequence, msg, clientInPVS, MAX_ASYNC_CLIENTS );
1182
1183         // write the latest user commands from the other clients in the PVS to the snapshot
1184         for ( last = NULL, i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
1185                 serverClient_t &client = clients[i];
1186
1187                 if ( client.clientState == SCS_FREE || i == clientNum ) {
1188                         continue;
1189                 }
1190
1191                 // if the client is not in the PVS
1192                 if ( !( clientInPVS[i >> 3] & ( 1 << ( i & 7 ) ) ) ) {
1193                         continue;
1194                 }
1195
1196                 int maxRelay = idMath::ClampInt( 1, MAX_USERCMD_RELAY, idAsyncNetwork::serverMaxUsercmdRelay.GetInteger() );
1197
1198                 // Max( 1, to always send at least one cmd, which we know we have because we call DuplicateUsercmds in RunFrame
1199                 numUsercmds = Max( 1, Min( client.gameFrame, gameFrame + maxRelay ) - gameFrame );
1200                 msg.WriteByte( i );
1201                 msg.WriteByte( numUsercmds );
1202                 for ( j = 0; j < numUsercmds; j++ ) {
1203                         index = ( gameFrame + j ) & ( MAX_USERCMD_BACKUP - 1 );
1204                         idAsyncNetwork::WriteUserCmdDelta( msg, userCmds[index][i], last );
1205                         last = &userCmds[index][i];
1206                 }
1207         }
1208         msg.WriteByte( MAX_ASYNC_CLIENTS );
1209
1210         client.channel.SendMessage( serverPort, serverTime, msg );
1211
1212         client.lastSnapshotTime = serverTime;
1213         client.snapshotSequence++;
1214         client.numDuplicatedUsercmds = 0;
1215
1216         return true;
1217 }
1218
1219 /*
1220 ==================
1221 idAsyncServer::ProcessUnreliableClientMessage
1222 ==================
1223 */
1224 void idAsyncServer::ProcessUnreliableClientMessage( int clientNum, const idBitMsg &msg ) {
1225         int i, id, acknowledgeSequence, clientGameInitId, clientGameFrame, numUsercmds, index;
1226         usercmd_t *last;
1227
1228         serverClient_t &client = clients[clientNum];
1229
1230         if ( client.clientState == SCS_ZOMBIE ) {
1231                 return;
1232         }
1233
1234         acknowledgeSequence = msg.ReadLong();
1235         clientGameInitId = msg.ReadLong();
1236
1237         // while loading a map the client may send empty messages to keep the connection alive
1238         if ( clientGameInitId == GAME_INIT_ID_MAP_LOAD ) {
1239                 if ( idAsyncNetwork::verbose.GetInteger() ) {                   
1240                         common->Printf( "ignore unreliable msg from client %d, gameInitId == ID_MAP_LOAD\n", clientNum );
1241                 }
1242                 return;
1243         }
1244
1245         // check if the client is in the right game
1246         if ( clientGameInitId != gameInitId ) {
1247                 if ( acknowledgeSequence > client.gameInitSequence ) {
1248                         // the client is connected but not in the right game
1249                         client.clientState = SCS_CONNECTED;
1250
1251                         // send game init to client
1252                         SendGameInitToClient( clientNum );
1253
1254                         if ( sessLocal.mapSpawnData.serverInfo.GetBool( "si_pure" ) ) {
1255                                 client.clientState = SCS_PUREWAIT;
1256                                 if ( !SendReliablePureToClient( clientNum ) ) {
1257                                         client.clientState = SCS_CONNECTED;
1258                                 }
1259                         }
1260                 } else if ( idAsyncNetwork::verbose.GetInteger() ) {
1261                         common->Printf( "ignore unreliable msg from client %d, wrong gameInit, old sequence\n", clientNum );
1262                 }
1263                 return;
1264         }
1265
1266         client.acknowledgeSnapshotSequence = msg.ReadLong();
1267
1268         if ( client.clientState == SCS_CONNECTED ) {
1269
1270                 // the client is in the right game
1271                 client.clientState = SCS_INGAME;
1272
1273                 // send the user info of other clients
1274                 for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
1275                         if ( clients[i].clientState >= SCS_CONNECTED && i != clientNum ) {
1276                                 SendUserInfoToClient( clientNum, i, sessLocal.mapSpawnData.userInfo[i] );
1277                         }
1278                 }
1279
1280                 // send synchronized cvars to client
1281                 SendSyncedCvarsToClient( clientNum, sessLocal.mapSpawnData.syncedCVars );
1282
1283                 SendEnterGameToClient( clientNum );
1284
1285                 // get the client running in the game
1286                 game->ServerClientBegin( clientNum );
1287
1288                 // write any reliable messages to initialize the client game state
1289                 game->ServerWriteInitialReliableMessages( clientNum );
1290         } else if ( client.clientState == SCS_INGAME ) {
1291
1292                 // apply the last snapshot the client received
1293                 if ( game->ServerApplySnapshot( clientNum, client.acknowledgeSnapshotSequence ) ) {
1294                         SendApplySnapshotToClient( clientNum, client.acknowledgeSnapshotSequence );
1295                 }
1296         }
1297
1298         // process the unreliable message
1299         id = msg.ReadByte();
1300         switch( id ) {
1301                 case CLIENT_UNRELIABLE_MESSAGE_EMPTY: {
1302                         if ( idAsyncNetwork::verbose.GetInteger() ) {
1303                                 common->Printf( "received empty message for client %d\n", clientNum );
1304                         }
1305                         break;
1306                 }
1307                 case CLIENT_UNRELIABLE_MESSAGE_PINGRESPONSE: {
1308                         client.clientPing = realTime - msg.ReadLong();
1309                         break;
1310                 }
1311                 case CLIENT_UNRELIABLE_MESSAGE_USERCMD: {
1312
1313                         client.clientPrediction = msg.ReadShort();
1314
1315                         // read user commands
1316                         clientGameFrame = msg.ReadLong();
1317                         numUsercmds = msg.ReadByte();
1318                         for ( last = NULL, i = clientGameFrame - numUsercmds + 1; i <= clientGameFrame; i++ ) {
1319                                 index = i & ( MAX_USERCMD_BACKUP - 1 );
1320                                 idAsyncNetwork::ReadUserCmdDelta( msg, userCmds[index][clientNum], last );
1321                                 userCmds[index][clientNum].gameFrame = i;
1322                                 userCmds[index][clientNum].duplicateCount = 0;
1323                                 if ( idAsyncNetwork::UsercmdInputChanged( userCmds[( i - 1 ) & ( MAX_USERCMD_BACKUP - 1 )][clientNum], userCmds[index][clientNum] ) ) {
1324                                         client.lastInputTime = serverTime;
1325                                 }
1326                                 last = &userCmds[index][clientNum];
1327                         }
1328
1329                         if ( last ) {
1330                                 client.gameFrame = last->gameFrame;
1331                                 client.gameTime = last->gameTime;
1332                         }
1333
1334                         if ( idAsyncNetwork::verbose.GetInteger() == 2 ) {
1335                                 common->Printf( "received user command for client %d, gameInitId = %d, gameFrame, %d gameTime %d\n", clientNum, clientGameInitId, client.gameFrame, client.gameTime );
1336                         }
1337                         break;
1338                 }
1339                 default: {
1340                         common->Printf( "unknown unreliable message %d from client %d\n", id, clientNum );
1341                         break;
1342                 }
1343         }
1344 }
1345
1346 /*
1347 ==================
1348 idAsyncServer::ProcessReliableClientMessages
1349 ==================
1350 */
1351 void idAsyncServer::ProcessReliableClientMessages( int clientNum ) {
1352         idBitMsg        msg;
1353         byte            msgBuf[MAX_MESSAGE_SIZE];
1354         byte            id;
1355
1356         serverClient_t &client = clients[clientNum];
1357
1358         msg.Init( msgBuf, sizeof( msgBuf ) );
1359
1360         while ( client.channel.GetReliableMessage( msg ) ) {
1361                 id = msg.ReadByte();
1362                 switch( id ) {
1363                         case CLIENT_RELIABLE_MESSAGE_CLIENTINFO: {
1364                                 idDict info;
1365                                 msg.ReadDeltaDict( info, &sessLocal.mapSpawnData.userInfo[clientNum] );
1366                                 SendUserInfoBroadcast( clientNum, info );
1367                                 break;
1368                         }
1369                         case CLIENT_RELIABLE_MESSAGE_PRINT: {
1370                                 char string[MAX_STRING_CHARS];
1371                                 msg.ReadString( string, sizeof( string ) );
1372                                 common->Printf( "%s\n", string );
1373                                 break;
1374                         }
1375                         case CLIENT_RELIABLE_MESSAGE_DISCONNECT: {
1376                                 DropClient( clientNum, "#str_07138" );
1377                                 break;
1378                         }
1379                         case CLIENT_RELIABLE_MESSAGE_PURE: {
1380                                 // we get this message once the client has successfully updated it's pure list
1381                                 ProcessReliablePure( clientNum, msg );
1382                                 break;
1383                         }
1384                         default: {
1385                                 // pass reliable message on to game code
1386                                 game->ServerProcessReliableMessage( clientNum, msg );
1387                                 break;
1388                         }
1389                 }
1390         }
1391 }
1392
1393 /*
1394 ==================
1395 idAsyncServer::ProcessAuthMessage
1396 ==================
1397 */
1398 void idAsyncServer::ProcessAuthMessage( const idBitMsg &msg ) {
1399         netadr_t                client_from;
1400         char                    client_guid[ 12 ], string[ MAX_STRING_CHARS ];
1401         int                             i, clientId;
1402         authReply_t             reply;
1403         authReplyMsg_t  replyMsg = AUTH_REPLY_WAITING;
1404         idStr                   replyPrintMsg;
1405         
1406         reply = (authReply_t)msg.ReadByte();
1407         if ( reply <= 0 || reply >= AUTH_MAXSTATES ) {
1408                 common->DPrintf( "auth: invalid reply %d\n", reply );
1409                 return;
1410         }
1411         clientId = msg.ReadShort( );
1412         msg.ReadNetadr( &client_from );
1413         msg.ReadString( client_guid, sizeof( client_guid ) );
1414         if ( reply != AUTH_OK ) {               
1415                 replyMsg = (authReplyMsg_t)msg.ReadByte();
1416                 if ( replyMsg <= 0 || replyMsg >= AUTH_REPLY_MAXSTATES ) {
1417                         common->DPrintf( "auth: invalid reply msg %d\n", replyMsg );
1418                         return;
1419                 }
1420                 if ( replyMsg == AUTH_REPLY_PRINT ) {
1421                         msg.ReadString( string, MAX_STRING_CHARS );
1422                         replyPrintMsg = string;
1423                 }
1424         }
1425
1426         lastAuthTime = serverTime;
1427
1428         // no message parsing below
1429         
1430         for ( i = 0; i < MAX_CHALLENGES; i++ ) {
1431                 if ( !challenges[i].connected && challenges[ i ].clientId == clientId ) {
1432                         // return if something is wrong
1433                         // break if we have found a valid auth
1434                         if ( !strlen( challenges[ i ].guid ) ) {
1435                                 common->DPrintf( "auth: client %s has no guid yet\n", Sys_NetAdrToString( challenges[ i ].address ) );
1436                                 return;
1437                         }
1438                         if ( idStr::Cmp( challenges[ i ].guid, client_guid ) ) {
1439                                 common->DPrintf( "auth: client %s %s not matched, auth server says guid %s\n", Sys_NetAdrToString( challenges[ i ].address ), challenges[i].guid, client_guid );
1440                                 return;
1441                         }
1442                         if ( !Sys_CompareNetAdrBase( client_from, challenges[i].address ) ) {
1443                                 // let auth work when server and master don't see the same IP
1444                                 common->DPrintf( "auth: matched guid '%s' for != IPs %s and %s\n", client_guid, Sys_NetAdrToString( client_from ), Sys_NetAdrToString( challenges[i].address ) );
1445                         }
1446                         break;
1447                 }
1448         }
1449         if ( i >= MAX_CHALLENGES ) {
1450                 common->DPrintf( "auth: failed client lookup %s %s\n", Sys_NetAdrToString( client_from ), client_guid );
1451                 return;
1452         }
1453
1454         if ( challenges[ i ].authState != CDK_WAIT ) {
1455                 common->DWarning( "auth: challenge 0x%x %s authState %d != CDK_WAIT", challenges[ i ].challenge, Sys_NetAdrToString( challenges[ i ].address ), challenges[ i ].authState );
1456                 return;
1457         }
1458         
1459         idStr::snPrintf( challenges[ i ].guid, 12, client_guid );
1460         if ( reply == AUTH_OK ) {
1461                 challenges[ i ].authState = CDK_OK;
1462                 common->Printf( "client %s %s is authed\n", Sys_NetAdrToString( client_from ), client_guid );
1463         } else {
1464                 const char *msg;
1465                 if ( replyMsg != AUTH_REPLY_PRINT ) {
1466                         msg = authReplyMsg[ replyMsg ];
1467                 } else {
1468                         msg = replyPrintMsg.c_str();
1469                 }
1470                 // maybe localize it
1471                 const char *l_msg = common->GetLanguageDict()->GetString( msg );
1472                 common->DPrintf( "auth: client %s %s - %s %s\n", Sys_NetAdrToString( client_from ), client_guid, authReplyStr[ reply ], l_msg );
1473                 challenges[ i ].authReply = reply;
1474                 challenges[ i ].authReplyMsg = replyMsg;
1475                 challenges[ i ].authReplyPrint = replyPrintMsg;
1476         }
1477 }
1478
1479 /*
1480 ==================
1481 idAsyncServer::ProcessChallengeMessage
1482 ==================
1483 */
1484 void idAsyncServer::ProcessChallengeMessage( const netadr_t from, const idBitMsg &msg ) {
1485         int                     i, clientId, oldest, oldestTime;
1486         idBitMsg        outMsg;
1487         byte            msgBuf[MAX_MESSAGE_SIZE];
1488
1489         clientId = msg.ReadLong();
1490
1491         oldest = 0;
1492         oldestTime = 0x7fffffff;
1493
1494         // see if we already have a challenge for this ip
1495         for ( i = 0; i < MAX_CHALLENGES; i++ ) {
1496                 if ( !challenges[i].connected && Sys_CompareNetAdrBase( from, challenges[i].address ) && clientId == challenges[i].clientId ) {
1497                         break;
1498                 }
1499                 if ( challenges[i].time < oldestTime ) {
1500                         oldestTime = challenges[i].time;
1501                         oldest = i;
1502                 }
1503         }
1504
1505         if ( i >= MAX_CHALLENGES ) {
1506                 // this is the first time this client has asked for a challenge
1507                 i = oldest;
1508                 challenges[i].address = from;
1509                 challenges[i].clientId = clientId;
1510                 challenges[i].challenge = ( (rand() << 16) ^ rand() ) ^ serverTime;
1511                 challenges[i].time = serverTime;
1512                 challenges[i].connected = false;
1513                 challenges[i].authState = CDK_WAIT;
1514                 challenges[i].authReply = AUTH_NONE;
1515                 challenges[i].authReplyMsg = AUTH_REPLY_WAITING;
1516                 challenges[i].authReplyPrint = "";
1517                 challenges[i].guid[0] = '\0';
1518         }
1519         challenges[i].pingTime = serverTime;
1520
1521         common->Printf( "sending challenge 0x%x to %s\n", challenges[i].challenge, Sys_NetAdrToString( from ) );
1522
1523         outMsg.Init( msgBuf, sizeof( msgBuf ) );
1524         outMsg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
1525         outMsg.WriteString( "challengeResponse" );
1526         outMsg.WriteLong( challenges[i].challenge );
1527         outMsg.WriteShort( serverId );
1528         outMsg.WriteString( cvarSystem->GetCVarString( "fs_game_base" ) );
1529         outMsg.WriteString( cvarSystem->GetCVarString( "fs_game" ) );
1530
1531         serverPort.SendPacket( from, outMsg.GetData(), outMsg.GetSize() );
1532
1533         if ( Sys_IsLANAddress( from ) ) {
1534                 // no CD Key check for LAN clients
1535                 challenges[i].authState = CDK_OK;
1536         } else {
1537                 if ( idAsyncNetwork::LANServer.GetBool() ) {
1538                         common->Printf( "net_LANServer is enabled. Client %s is not a LAN address, will be rejected\n", Sys_NetAdrToString( from ) );
1539                         challenges[ i ].authState = CDK_ONLYLAN;
1540                 } else {
1541                         // emit a cd key confirmation request
1542                         outMsg.BeginWriting();
1543                         outMsg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
1544                         outMsg.WriteString( "srvAuth" );
1545                         outMsg.WriteLong( ASYNC_PROTOCOL_VERSION );
1546                         outMsg.WriteNetadr( from );
1547                         outMsg.WriteLong( -1 ); // this identifies "challenge" auth vs "connect" auth
1548                         // protocol 1.37 addition
1549                         outMsg.WriteByte( fileSystem->RunningD3XP() );
1550                         serverPort.SendPacket( idAsyncNetwork::GetMasterAddress(), outMsg.GetData(), outMsg.GetSize() );
1551                 }
1552         }
1553 }
1554
1555 /*
1556 ==================
1557 idAsyncServer::SendPureServerMessage
1558 ==================
1559 */
1560 bool idAsyncServer::SendPureServerMessage( const netadr_t to, int OS ) {
1561         idBitMsg        outMsg;
1562         byte            msgBuf[ MAX_MESSAGE_SIZE ];
1563         int                     serverChecksums[ MAX_PURE_PAKS ];
1564         int                     gamePakChecksum;
1565         int                     i;
1566
1567         fileSystem->GetPureServerChecksums( serverChecksums, OS, &gamePakChecksum );
1568         if ( !serverChecksums[ 0 ] ) {
1569                 // happens if you run fully expanded assets with si_pure 1
1570                 common->Warning( "pure server has no pak files referenced" );
1571                 return false;
1572         }
1573         common->DPrintf( "client %s: sending pure pak list\n", Sys_NetAdrToString( to ) );
1574
1575         // send our list of required paks
1576         outMsg.Init( msgBuf, sizeof( msgBuf ) );
1577         outMsg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
1578         outMsg.WriteString( "pureServer" );
1579
1580         i = 0;
1581         while ( serverChecksums[ i ] ) {
1582                 outMsg.WriteLong( serverChecksums[ i++ ] );
1583         }
1584         outMsg.WriteLong( 0 );
1585
1586         // write the pak checksum for game code
1587         outMsg.WriteLong( gamePakChecksum );
1588
1589         serverPort.SendPacket( to, outMsg.GetData(), outMsg.GetSize() );
1590         return true;
1591 }
1592
1593 /*
1594 ==================
1595 idAsyncServer::SendReliablePureToClient
1596 ==================
1597 */
1598 bool idAsyncServer::SendReliablePureToClient( int clientNum ) {
1599         idBitMsg        msg;
1600         byte            msgBuf[ MAX_MESSAGE_SIZE ];
1601         int                     serverChecksums[ MAX_PURE_PAKS ];
1602         int                     i;
1603         int                     gamePakChecksum;
1604
1605         fileSystem->GetPureServerChecksums( serverChecksums, clients[ clientNum ].OS, &gamePakChecksum );
1606         if ( !serverChecksums[ 0 ] ) {
1607                 // happens if you run fully expanded assets with si_pure 1
1608                 common->Warning( "pure server has no pak files referenced" );
1609                 return false;
1610         }
1611
1612         common->DPrintf( "client %d: sending pure pak list (reliable channel) @ gameInitId %d\n", clientNum, gameInitId );
1613
1614         msg.Init( msgBuf, sizeof( msgBuf ) );
1615         msg.WriteByte( SERVER_RELIABLE_MESSAGE_PURE );
1616
1617         msg.WriteLong( gameInitId );
1618
1619         i = 0;
1620         while ( serverChecksums[ i ] ) {
1621                 msg.WriteLong( serverChecksums[ i++ ] );
1622         }
1623         msg.WriteLong( 0 );
1624         msg.WriteLong( gamePakChecksum );
1625
1626         SendReliableMessage( clientNum, msg );
1627
1628         return true;
1629 }
1630
1631 /*
1632 ==================
1633 idAsyncServer::ValidateChallenge
1634 ==================
1635 */
1636 int idAsyncServer::ValidateChallenge( const netadr_t from, int challenge, int clientId ) {
1637         int i;
1638         for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
1639                 const serverClient_t &client = clients[i];
1640
1641                 if ( client.clientState == SCS_FREE ) {
1642                         continue;
1643                 }
1644                 if ( Sys_CompareNetAdrBase( from, client.channel.GetRemoteAddress() ) &&
1645                                         ( clientId == client.clientId || from.port == client.channel.GetRemoteAddress().port ) ) {
1646                         if ( serverTime - client.lastConnectTime < MIN_RECONNECT_TIME ) {
1647                                 common->Printf( "%s: reconnect rejected : too soon\n", Sys_NetAdrToString( from ) );
1648                                 return -1;
1649                         }
1650                         break;
1651                 }
1652         }
1653
1654         for ( i = 0; i < MAX_CHALLENGES; i++ ) {
1655                 if ( Sys_CompareNetAdrBase( from, challenges[i].address ) && from.port == challenges[i].address.port ) {
1656                         if ( challenge == challenges[i].challenge ) {
1657                                 break;
1658                         }
1659                 }
1660         }
1661         if ( i == MAX_CHALLENGES ) {
1662                 PrintOOB( from, SERVER_PRINT_BADCHALLENGE, "#str_04840" );
1663                 return -1;
1664         }
1665         return i;
1666 }
1667
1668 /*
1669 ==================
1670 idAsyncServer::ProcessConnectMessage
1671 ==================
1672 */
1673 void idAsyncServer::ProcessConnectMessage( const netadr_t from, const idBitMsg &msg ) {
1674         int                     clientNum, protocol, clientDataChecksum, challenge, clientId, ping, clientRate;
1675         idBitMsg        outMsg;
1676         byte            msgBuf[ MAX_MESSAGE_SIZE ];
1677         char            guid[ 12 ];
1678         char            password[ 17 ];
1679         int                     i, ichallenge, islot, OS, numClients;
1680
1681         protocol = msg.ReadLong();
1682         OS = msg.ReadShort();
1683
1684         // check the protocol version
1685         if ( protocol != ASYNC_PROTOCOL_VERSION ) {
1686                 // that's a msg back to a client, we don't know about it's localization, so send english
1687                 PrintOOB( from, SERVER_PRINT_BADPROTOCOL, va( "server uses protocol %d.%d\n", ASYNC_PROTOCOL_MAJOR, ASYNC_PROTOCOL_MINOR ) );
1688                 return;
1689         }
1690
1691         clientDataChecksum = msg.ReadLong();
1692         challenge = msg.ReadLong();
1693         clientId = msg.ReadShort();
1694         clientRate = msg.ReadLong();
1695
1696         // check the client data - only for non pure servers
1697         if ( !sessLocal.mapSpawnData.serverInfo.GetInt( "si_pure" ) && clientDataChecksum != serverDataChecksum ) {
1698                 PrintOOB( from, SERVER_PRINT_MISC, "#str_04842" );
1699                 return;
1700         }
1701
1702         if ( ( ichallenge = ValidateChallenge( from, challenge, clientId ) ) == -1 ) {
1703                 return;
1704         }
1705         challenges[ ichallenge ].OS = OS;
1706
1707         msg.ReadString( guid, sizeof( guid ) );
1708
1709         switch ( challenges[ ichallenge ].authState ) {
1710                 case CDK_PUREWAIT:
1711                         SendPureServerMessage( from, OS );
1712                         return;
1713                 case CDK_ONLYLAN:
1714                         common->DPrintf( "%s: not a lan client\n", Sys_NetAdrToString( from ) );
1715                         PrintOOB( from, SERVER_PRINT_MISC, "#str_04843" );
1716                         return;
1717                 case CDK_WAIT:
1718                         if ( challenges[ ichallenge ].authReply == AUTH_NONE && Min( serverTime - lastAuthTime, serverTime - challenges[ ichallenge ].time ) > AUTHORIZE_TIMEOUT ) {
1719                                 common->DPrintf( "%s: Authorize server timed out\n", Sys_NetAdrToString( from ) );
1720                                 break; // will continue with the connecting process
1721                         }
1722                         const char *msg, *l_msg;
1723                         if ( challenges[ ichallenge ].authReplyMsg != AUTH_REPLY_PRINT ) {
1724                                 msg = authReplyMsg[ challenges[ ichallenge ].authReplyMsg ];
1725                         } else {
1726                                 msg = challenges[ ichallenge ].authReplyPrint.c_str();
1727                         }
1728                         l_msg = common->GetLanguageDict()->GetString( msg );
1729                         
1730                         common->DPrintf( "%s: %s\n", Sys_NetAdrToString( from ), l_msg );
1731
1732                         if ( challenges[ ichallenge ].authReplyMsg == AUTH_REPLY_UNKNOWN || challenges[ ichallenge ].authReplyMsg == AUTH_REPLY_WAITING ) {
1733                                 // the client may be trying to connect to us in LAN mode, and the server disagrees
1734                                 // let the client know so it would switch to authed connection
1735                                 idBitMsg outMsg;
1736                                 byte msgBuf[ MAX_MESSAGE_SIZE ];
1737                                 outMsg.Init( msgBuf, sizeof( msgBuf ) );
1738                                 outMsg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
1739                                 outMsg.WriteString( "authrequired" );
1740                                 serverPort.SendPacket( from, outMsg.GetData(), outMsg.GetSize() );
1741                         }
1742                 
1743                         PrintOOB( from, SERVER_PRINT_MISC, msg );
1744                 
1745                         // update the guid in the challenges
1746                         idStr::snPrintf( challenges[ ichallenge ].guid, sizeof( challenges[ ichallenge ].guid ), guid );
1747
1748                         // once auth replied denied, stop sending further requests
1749                         if ( challenges[ ichallenge ].authReply != AUTH_DENY ) {
1750                                 // emit a cd key confirmation request
1751                                 outMsg.Init( msgBuf, sizeof( msgBuf ) );
1752                                 outMsg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
1753                                 outMsg.WriteString( "srvAuth" );
1754                                 outMsg.WriteLong( ASYNC_PROTOCOL_VERSION );
1755                                 outMsg.WriteNetadr( from );
1756                                 outMsg.WriteLong( clientId );
1757                                 outMsg.WriteString( guid );     
1758                                 // protocol 1.37 addition
1759                                 outMsg.WriteByte( fileSystem->RunningD3XP() );
1760                                 serverPort.SendPacket( idAsyncNetwork::GetMasterAddress(), outMsg.GetData(), outMsg.GetSize() );
1761                         }
1762                         return;
1763                 default:
1764                         assert( challenges[ ichallenge ].authState == CDK_OK || challenges[ ichallenge ].authState == CDK_PUREOK );
1765         }
1766
1767         numClients = 0;
1768         for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
1769                 serverClient_t &client = clients[ i ];
1770                 if ( client.clientState >= SCS_PUREWAIT ) {
1771                         numClients++;
1772                 }
1773         }
1774
1775         // game may be passworded, client banned by IP or GUID
1776         // if authState == CDK_PUREOK, the check was already performed once before entering pure checks
1777         // but meanwhile, the max players may have been reached
1778         msg.ReadString( password, sizeof( password ) );
1779         char reason[MAX_STRING_CHARS];
1780         allowReply_t reply = game->ServerAllowClient( numClients, Sys_NetAdrToString( from ), guid, password, reason );
1781         if ( reply != ALLOW_YES ) {
1782                 common->DPrintf( "game denied connection for %s\n", Sys_NetAdrToString( from ) );
1783
1784                 // SERVER_PRINT_GAMEDENY passes the game opcode through. Don't use PrintOOB
1785                 outMsg.Init( msgBuf, sizeof( msgBuf ) );
1786                 outMsg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
1787                 outMsg.WriteString( "print" );
1788                 outMsg.WriteLong( SERVER_PRINT_GAMEDENY );
1789                 outMsg.WriteLong( reply );
1790                 outMsg.WriteString( reason );
1791                 serverPort.SendPacket( from, outMsg.GetData(), outMsg.GetSize() );
1792
1793                 return;
1794         }
1795
1796         // enter pure checks if necessary
1797         if ( sessLocal.mapSpawnData.serverInfo.GetInt( "si_pure" ) && challenges[ ichallenge ].authState != CDK_PUREOK ) {
1798                 if ( SendPureServerMessage( from, OS ) ) {
1799                         challenges[ ichallenge ].authState = CDK_PUREWAIT;
1800                         return;
1801                 }
1802         }
1803
1804         // push back decl checksum here when running pure. just an additional safe check
1805         if ( sessLocal.mapSpawnData.serverInfo.GetInt( "si_pure" ) && clientDataChecksum != serverDataChecksum ) {
1806                 PrintOOB( from, SERVER_PRINT_MISC, "#str_04844" );
1807                 return;
1808         }
1809
1810         ping = serverTime - challenges[ ichallenge ].pingTime;
1811         common->Printf( "challenge from %s connecting with %d ping\n", Sys_NetAdrToString( from ), ping );
1812         challenges[ ichallenge ].connected = true;
1813
1814         // find a slot for the client
1815         for ( islot = 0; islot < 3; islot++ ) {
1816                 for ( clientNum = 0; clientNum < MAX_ASYNC_CLIENTS; clientNum++ ) {
1817                         serverClient_t &client = clients[ clientNum ];
1818
1819                         if ( islot == 0 ) {
1820                                 // if this slot uses the same IP and port
1821                                 if ( Sys_CompareNetAdrBase( from, client.channel.GetRemoteAddress() ) &&
1822                                                 ( clientId == client.clientId || from.port == client.channel.GetRemoteAddress().port ) ) {
1823                                         break;
1824                                 }
1825                         } else if ( islot == 1 ) {
1826                                 // if this client is not connected and the slot uses the same IP
1827                                 if ( client.clientState >= SCS_PUREWAIT ) {
1828                                         continue;
1829                                 }
1830                                 if ( Sys_CompareNetAdrBase( from, client.channel.GetRemoteAddress() ) ) {
1831                                         break;
1832                                 }
1833                         } else if ( islot == 2 ) {
1834                                 // if this slot is free
1835                                 if ( client.clientState == SCS_FREE ) {
1836                                         break;
1837                                 }
1838                         }
1839                 }
1840
1841                 if ( clientNum < MAX_ASYNC_CLIENTS ) {
1842                         // initialize
1843                         clients[ clientNum ].channel.Init( from, serverId );
1844                         clients[ clientNum ].OS = OS;
1845                         strncpy( clients[ clientNum ].guid, guid, 12 );
1846                         clients[ clientNum ].guid[11] = 0;
1847                         break;
1848                 }
1849         }
1850
1851         // if no free spots available
1852         if ( clientNum >= MAX_ASYNC_CLIENTS ) {
1853                 PrintOOB( from, SERVER_PRINT_MISC, "#str_04845" );
1854                 return;
1855         }
1856
1857         common->Printf( "sending connect response to %s\n", Sys_NetAdrToString( from ) );
1858
1859         // send connect response message
1860         outMsg.Init( msgBuf, sizeof( msgBuf ) );
1861         outMsg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
1862         outMsg.WriteString( "connectResponse" );
1863         outMsg.WriteLong( clientNum );
1864         outMsg.WriteLong( gameInitId );
1865         outMsg.WriteLong( gameFrame );
1866         outMsg.WriteLong( gameTime );
1867         outMsg.WriteDeltaDict( sessLocal.mapSpawnData.serverInfo, NULL );
1868
1869         serverPort.SendPacket( from, outMsg.GetData(), outMsg.GetSize() );
1870         
1871         InitClient( clientNum, clientId, clientRate );
1872
1873         clients[clientNum].gameInitSequence = 1;
1874         clients[clientNum].snapshotSequence = 1;
1875
1876         // clear the challenge struct so a reconnect from this client IP starts clean
1877         memset( &challenges[ ichallenge ], 0, sizeof( challenge_t ) );
1878 }
1879
1880 /*
1881 ==================
1882 idAsyncServer::VerifyChecksumMessage
1883 ==================
1884 */
1885 bool idAsyncServer::VerifyChecksumMessage( int clientNum, const netadr_t *from, const idBitMsg &msg, idStr &reply, int OS ) {
1886         int             i, numChecksums;
1887         int             checksums[ MAX_PURE_PAKS ];
1888         int             gamePakChecksum;
1889         int             serverChecksums[ MAX_PURE_PAKS ];
1890         int             serverGamePakChecksum;
1891
1892         // pak checksums, in a 0-terminated list
1893         numChecksums = 0;
1894         do {
1895                 i = msg.ReadLong( );
1896                 checksums[ numChecksums++ ] = i;
1897                 // just to make sure a broken client doesn't crash us
1898                 if ( numChecksums >= MAX_PURE_PAKS ) {
1899                         common->Warning( "MAX_PURE_PAKS ( %d ) exceeded in idAsyncServer::ProcessPureMessage\n", MAX_PURE_PAKS );
1900                         sprintf( reply, "#str_07144" );
1901                         return false;
1902                 }
1903         } while ( i );
1904         numChecksums--;
1905
1906         // code pak checksum
1907         gamePakChecksum = msg.ReadLong( );
1908
1909         fileSystem->GetPureServerChecksums( serverChecksums, OS, &serverGamePakChecksum );
1910         assert( serverChecksums[ 0 ] );
1911
1912         // compare the lists
1913         if ( serverGamePakChecksum != gamePakChecksum ) {
1914                 common->Printf( "client %s: invalid game code pak ( 0x%x )\n", from ? Sys_NetAdrToString( *from ) : va( "%d", clientNum ), gamePakChecksum );
1915                 sprintf( reply, "#str_07145" );
1916                 return false;
1917         }
1918         for ( i = 0; serverChecksums[ i ] != 0; i++ ) {
1919                 if ( checksums[ i ] != serverChecksums[ i ] ) {
1920                         common->DPrintf( "client %s: pak missing ( 0x%x )\n", from ? Sys_NetAdrToString( *from ) : va( "%d", clientNum ), serverChecksums[ i ] );
1921                         sprintf( reply, "pak missing ( 0x%x )\n", serverChecksums[ i ] );
1922                         return false;
1923                 }
1924         }
1925         if ( checksums[ i ] != 0 ) {
1926                 common->DPrintf( "client %s: extra pak file referenced ( 0x%x )\n", from ? Sys_NetAdrToString( *from ) : va( "%d", clientNum ), checksums[ i ] );
1927                 sprintf( reply, "extra pak file referenced ( 0x%x )\n", checksums[ i ] );
1928                 return false;
1929         }
1930         return true;
1931 }
1932
1933 /*
1934 ==================
1935 idAsyncServer::ProcessPureMessage
1936 ==================
1937 */
1938 void idAsyncServer::ProcessPureMessage( const netadr_t from, const idBitMsg &msg ) {
1939         int             iclient, challenge, clientId;
1940         idStr   reply;
1941
1942         challenge = msg.ReadLong();
1943         clientId = msg.ReadShort();
1944
1945         if ( ( iclient = ValidateChallenge( from, challenge, clientId ) ) == -1 ) {
1946                 return;
1947         }
1948
1949         if ( challenges[ iclient ].authState != CDK_PUREWAIT ) {
1950                 common->DPrintf( "client %s: got pure message, not in CDK_PUREWAIT\n", Sys_NetAdrToString( from ) );
1951                 return;
1952         }
1953
1954         if ( !VerifyChecksumMessage( iclient, &from, msg, reply, challenges[ iclient ].OS ) ) {
1955                 PrintOOB( from, SERVER_PRINT_MISC, reply );
1956                 return;
1957         }
1958
1959         common->DPrintf( "client %s: passed pure checks\n", Sys_NetAdrToString( from ) );
1960         challenges[ iclient ].authState = CDK_PUREOK; // next connect message will get the client through completely
1961 }
1962
1963 /*
1964 ==================
1965 idAsyncServer::ProcessReliablePure
1966 ==================
1967 */
1968 void idAsyncServer::ProcessReliablePure( int clientNum, const idBitMsg &msg ) {
1969         idStr           reply;
1970         idBitMsg        outMsg;
1971         byte            msgBuf[MAX_MESSAGE_SIZE];
1972         int                     clientGameInitId;
1973         
1974         clientGameInitId = msg.ReadLong();
1975         if ( clientGameInitId != gameInitId ) {
1976                 common->DPrintf( "client %d: ignoring reliable pure from an old gameInit (%d)\n", clientNum, clientGameInitId );
1977                 return;
1978         }
1979
1980         if ( clients[ clientNum ].clientState != SCS_PUREWAIT ) {
1981                 // should not happen unless something is very wrong. still, don't let this crash us, just get rid of the client
1982                 common->DPrintf( "client %d: got reliable pure while != SCS_PUREWAIT, sending a reload\n", clientNum );
1983                 outMsg.Init( msgBuf, sizeof( msgBuf ) );
1984                 outMsg.WriteByte( SERVER_RELIABLE_MESSAGE_RELOAD );
1985                 SendReliableMessage( clientNum, msg );
1986                 // go back to SCS_CONNECTED to sleep on the client until it goes away for a reconnect
1987                 clients[ clientNum ].clientState = SCS_CONNECTED;
1988                 return;
1989         }
1990
1991         if ( !VerifyChecksumMessage( clientNum, NULL, msg, reply, clients[ clientNum ].OS ) ) {
1992                 DropClient( clientNum, reply );
1993                 return;
1994         }
1995         common->DPrintf( "client %d: passed pure checks (reliable channel)\n", clientNum );
1996         clients[ clientNum ].clientState = SCS_CONNECTED;
1997 }
1998
1999 /*
2000 ==================
2001 idAsyncServer::RemoteConsoleOutput
2002 ==================
2003 */
2004 void idAsyncServer::RemoteConsoleOutput( const char *string ) {
2005         noRconOutput = false;
2006         PrintOOB( rconAddress, SERVER_PRINT_RCON, string );
2007 }
2008
2009 /*
2010 ==================
2011 RConRedirect
2012 ==================
2013 */
2014 void RConRedirect( const char *string ) {
2015         idAsyncNetwork::server.RemoteConsoleOutput( string );
2016 }
2017
2018 /*
2019 ==================
2020 idAsyncServer::ProcessRemoteConsoleMessage
2021 ==================
2022 */
2023 void idAsyncServer::ProcessRemoteConsoleMessage( const netadr_t from, const idBitMsg &msg ) {
2024         idBitMsg        outMsg;
2025         byte            msgBuf[952];
2026         char            string[MAX_STRING_CHARS];
2027
2028         if ( idAsyncNetwork::serverRemoteConsolePassword.GetString()[0] == '\0' ) {
2029                 PrintOOB( from, SERVER_PRINT_MISC, "#str_04846" );
2030                 return;
2031         }
2032
2033         msg.ReadString( string, sizeof( string ) );
2034
2035         if ( idStr::Icmp( string, idAsyncNetwork::serverRemoteConsolePassword.GetString() ) != 0 ) {
2036                 PrintOOB( from, SERVER_PRINT_MISC, "#str_04847" );
2037                 return;
2038         }
2039
2040         msg.ReadString( string, sizeof( string ) );
2041
2042         common->Printf( "rcon from %s: %s\n", Sys_NetAdrToString( from ), string );
2043
2044         rconAddress = from;
2045         noRconOutput = true;
2046         common->BeginRedirect( (char *)msgBuf, sizeof( msgBuf ), RConRedirect );
2047
2048         cmdSystem->BufferCommandText( CMD_EXEC_NOW, string );
2049
2050         common->EndRedirect();
2051
2052         if ( noRconOutput ) {
2053                 PrintOOB( rconAddress, SERVER_PRINT_RCON, "#str_04848" );
2054         }
2055 }
2056
2057 /*
2058 ==================
2059 idAsyncServer::ProcessGetInfoMessage
2060 ==================
2061 */
2062 void idAsyncServer::ProcessGetInfoMessage( const netadr_t from, const idBitMsg &msg ) {
2063         int                     i, challenge;
2064         idBitMsg        outMsg;
2065         byte            msgBuf[MAX_MESSAGE_SIZE];
2066
2067         if ( !IsActive() ) {
2068                 return;
2069         }
2070
2071         common->DPrintf( "Sending info response to %s\n", Sys_NetAdrToString( from ) );
2072
2073         challenge = msg.ReadLong();
2074
2075         outMsg.Init( msgBuf, sizeof( msgBuf ) );
2076         outMsg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
2077         outMsg.WriteString( "infoResponse" );
2078         outMsg.WriteLong( challenge );
2079         outMsg.WriteLong( ASYNC_PROTOCOL_VERSION );
2080         outMsg.WriteDeltaDict( sessLocal.mapSpawnData.serverInfo, NULL );
2081
2082         for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
2083                 serverClient_t &client = clients[i];
2084
2085                 if ( client.clientState < SCS_CONNECTED ) {
2086                         continue;
2087                 }
2088
2089                 outMsg.WriteByte( i );
2090                 outMsg.WriteShort( client.clientPing );
2091                 outMsg.WriteLong( client.channel.GetMaxOutgoingRate() );
2092                 outMsg.WriteString( sessLocal.mapSpawnData.userInfo[i].GetString( "ui_name", "Player" ) );
2093         }
2094         outMsg.WriteByte( MAX_ASYNC_CLIENTS );
2095         outMsg.WriteLong( fileSystem->GetOSMask() );
2096
2097         serverPort.SendPacket( from, outMsg.GetData(), outMsg.GetSize() );
2098 }
2099
2100 /*
2101 ===============
2102 idAsyncServer::PrintLocalServerInfo
2103 see (client) "getInfo" -> (server) "infoResponse" -> (client)ProcessGetInfoMessage
2104 ===============
2105 */
2106 void idAsyncServer::PrintLocalServerInfo( void ) {
2107         int i;
2108
2109         common->Printf( "server '%s' IP = %s\nprotocol %d.%d OS mask 0x%x\n", 
2110                                         sessLocal.mapSpawnData.serverInfo.GetString( "si_name" ),
2111                                         Sys_NetAdrToString( serverPort.GetAdr() ),
2112                                         ASYNC_PROTOCOL_MAJOR,
2113                                         ASYNC_PROTOCOL_MINOR,
2114                                         fileSystem->GetOSMask() );
2115         sessLocal.mapSpawnData.serverInfo.Print();
2116         for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
2117                 serverClient_t &client = clients[i];
2118                 if ( client.clientState < SCS_CONNECTED ) {
2119                         continue;
2120                 }
2121                 common->Printf( "client %2d: %s, ping = %d, rate = %d\n", i,
2122                                                 sessLocal.mapSpawnData.userInfo[i].GetString( "ui_name", "Player" ),
2123                                                 client.clientPing, client.channel.GetMaxOutgoingRate() );
2124         }
2125 }
2126
2127 /*
2128 ==================
2129 idAsyncServer::ConnectionlessMessage
2130 ==================
2131 */
2132 bool idAsyncServer::ConnectionlessMessage( const netadr_t from, const idBitMsg &msg ) {
2133         char            string[MAX_STRING_CHARS*2];  // M. Quinn - Even Balance - PB Packets need more than 1024
2134
2135         msg.ReadString( string, sizeof( string ) );
2136
2137         // info request
2138         if ( idStr::Icmp( string, "getInfo" ) == 0 ) {
2139                 ProcessGetInfoMessage( from, msg );
2140                 return false;
2141         }
2142
2143         // remote console
2144         if ( idStr::Icmp( string, "rcon" ) == 0 ) {
2145                 ProcessRemoteConsoleMessage( from, msg );
2146                 return true;
2147         }
2148
2149         if ( !active ) {
2150                 PrintOOB( from, SERVER_PRINT_MISC, "#str_04849" );
2151                 return false;
2152         }
2153
2154         // challenge from a client
2155         if ( idStr::Icmp( string, "challenge" ) == 0 ) {
2156                 ProcessChallengeMessage( from, msg );
2157                 return false;
2158         }
2159
2160         // connect from a client
2161         if ( idStr::Icmp( string, "connect" ) == 0 ) {
2162                 ProcessConnectMessage( from, msg );
2163                 return false;
2164         }
2165
2166         // pure mesasge from a client
2167         if ( idStr::Icmp( string, "pureClient" ) == 0 ) {
2168                 ProcessPureMessage( from, msg );
2169                 return false;
2170         }
2171
2172         // download request
2173         if ( idStr::Icmp( string, "downloadRequest" ) == 0 ) {          
2174                 ProcessDownloadRequestMessage( from, msg );
2175         }
2176         
2177         // auth server
2178         if ( idStr::Icmp( string, "auth" ) == 0 ) {
2179                 if ( !Sys_CompareNetAdrBase( from, idAsyncNetwork::GetMasterAddress() ) ) {
2180                         common->Printf( "auth: bad source %s\n", Sys_NetAdrToString( from ) );
2181                         return false;
2182                 }
2183                 if ( idAsyncNetwork::LANServer.GetBool() ) {
2184                         common->Printf( "auth message from master. net_LANServer is enabled, ignored.\n" );
2185                 }
2186                 ProcessAuthMessage( msg );
2187                 return false;
2188         }
2189
2190         return false;
2191 }
2192
2193 /*
2194 ==================
2195 idAsyncServer::ProcessMessage
2196 ==================
2197 */
2198 bool idAsyncServer::ProcessMessage( const netadr_t from, idBitMsg &msg ) {
2199         int                     i, id, sequence;
2200         idBitMsg        outMsg;
2201         byte            msgBuf[MAX_MESSAGE_SIZE];
2202
2203         id = msg.ReadShort();
2204
2205         // check for a connectionless message
2206         if ( id == CONNECTIONLESS_MESSAGE_ID ) {
2207                 return ConnectionlessMessage( from, msg );
2208         }
2209
2210         if ( msg.GetRemaingData() < 4 ) {
2211                 common->DPrintf( "%s: tiny packet\n", Sys_NetAdrToString( from ) );
2212                 return false;
2213         }
2214
2215         // find out which client the message is from
2216         for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
2217                 serverClient_t &client = clients[i];
2218
2219                 if ( client.clientState == SCS_FREE ) {
2220                         continue;
2221                 }
2222
2223                 // This does not compare the UDP port, because some address translating
2224                 // routers will change that at arbitrary times.
2225                 if ( !Sys_CompareNetAdrBase( from, client.channel.GetRemoteAddress() ) || id != client.clientId ) {
2226                         continue;
2227                 }
2228
2229                 // make sure it is a valid, in sequence packet
2230                 if ( !client.channel.Process( from, serverTime, msg, sequence ) ) {
2231                         return false;           // out of order, duplicated, fragment, etc.
2232                 }
2233
2234                 // zombie clients still need to do the channel processing to make sure they don't
2235                 // need to retransmit the final reliable message, but they don't do any other processing
2236                 if ( client.clientState == SCS_ZOMBIE ) {
2237                         return false;
2238                 }
2239
2240                 client.lastPacketTime = serverTime;
2241
2242                 ProcessReliableClientMessages( i );
2243                 ProcessUnreliableClientMessage( i, msg );
2244
2245                 return false;
2246         }
2247         
2248         // if we received a sequenced packet from an address we don't recognize,
2249         // send an out of band disconnect packet to it
2250         outMsg.Init( msgBuf, sizeof( msgBuf ) );
2251         outMsg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
2252         outMsg.WriteString( "disconnect" );
2253         serverPort.SendPacket( from, outMsg.GetData(), outMsg.GetSize() );
2254
2255         return false;
2256 }
2257
2258 /*
2259 ==================
2260 idAsyncServer::SendReliableGameMessage
2261 ==================
2262 */
2263 void idAsyncServer::SendReliableGameMessage( int clientNum, const idBitMsg &msg ) {
2264         int                     i;
2265         idBitMsg        outMsg;
2266         byte            msgBuf[MAX_MESSAGE_SIZE];
2267
2268         outMsg.Init( msgBuf, sizeof( msgBuf ) );
2269         outMsg.WriteByte( SERVER_RELIABLE_MESSAGE_GAME );
2270         outMsg.WriteData( msg.GetData(), msg.GetSize() );
2271
2272         if ( clientNum >= 0 && clientNum < MAX_ASYNC_CLIENTS ) {
2273                 if ( clients[clientNum].clientState == SCS_INGAME ) {
2274                         SendReliableMessage( clientNum, outMsg );
2275                 }
2276                 return;
2277         }
2278
2279         for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
2280                 if ( clients[i].clientState != SCS_INGAME ) {
2281                         continue;
2282                 }
2283                 SendReliableMessage( i, outMsg );
2284         }
2285 }
2286
2287 /*
2288 ==================
2289 idAsyncServer::LocalClientSendReliableMessageExcluding
2290 ==================
2291 */
2292 void idAsyncServer::SendReliableGameMessageExcluding( int clientNum, const idBitMsg &msg ) {
2293         int                     i;
2294         idBitMsg        outMsg;
2295         byte            msgBuf[MAX_MESSAGE_SIZE];
2296
2297         assert( clientNum >= 0 && clientNum < MAX_ASYNC_CLIENTS );
2298
2299         outMsg.Init( msgBuf, sizeof( msgBuf ) );
2300         outMsg.WriteByte( SERVER_RELIABLE_MESSAGE_GAME );
2301         outMsg.WriteData( msg.GetData(), msg.GetSize() );
2302
2303         for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
2304                 if ( i == clientNum ) {
2305                         continue;
2306                 }
2307                 if ( clients[i].clientState != SCS_INGAME ) {
2308                         continue;
2309                 }
2310                 SendReliableMessage( i, outMsg );
2311         }       
2312 }
2313
2314 /*
2315 ==================
2316 idAsyncServer::LocalClientSendReliableMessage
2317 ==================
2318 */
2319 void idAsyncServer::LocalClientSendReliableMessage( const idBitMsg &msg ) {
2320         if ( localClientNum < 0 ) {
2321                 common->Printf( "LocalClientSendReliableMessage: no local client\n" );
2322                 return;
2323         }
2324         game->ServerProcessReliableMessage( localClientNum, msg );
2325 }
2326
2327 /*
2328 ==================
2329 idAsyncServer::ProcessConnectionLessMessages
2330 ==================
2331 */
2332 void idAsyncServer::ProcessConnectionLessMessages( void ) {
2333         int                     size, id;
2334         idBitMsg        msg;
2335         byte            msgBuf[MAX_MESSAGE_SIZE];
2336         netadr_t        from;
2337
2338         if ( !serverPort.GetPort() ) {
2339                 return;
2340         }
2341
2342         while( serverPort.GetPacket( from, msgBuf, size, sizeof( msgBuf ) ) ) {
2343                 msg.Init( msgBuf, sizeof( msgBuf ) );
2344                 msg.SetSize( size );
2345                 msg.BeginReading();
2346                 id = msg.ReadShort();
2347                 if ( id == CONNECTIONLESS_MESSAGE_ID ) {
2348                         ConnectionlessMessage( from, msg );
2349                 }
2350         }
2351 }
2352
2353 /*
2354 ==================
2355 idAsyncServer::UpdateTime
2356 ==================
2357 */
2358 int idAsyncServer::UpdateTime( int clamp ) {
2359         int time, msec;
2360
2361         time = Sys_Milliseconds();
2362         msec = idMath::ClampInt( 0, clamp, time - realTime );
2363         realTime = time;
2364         serverTime += msec;
2365         return msec;
2366 }
2367
2368 /*
2369 ==================
2370 idAsyncServer::RunFrame
2371 ==================
2372 */
2373 void idAsyncServer::RunFrame( void ) {
2374         int                     i, msec, size;
2375         bool            newPacket;
2376         idBitMsg        msg;
2377         byte            msgBuf[MAX_MESSAGE_SIZE];
2378         netadr_t        from;
2379         int                     outgoingRate, incomingRate;
2380         float           outgoingCompression, incomingCompression;
2381
2382         msec = UpdateTime( 100 );
2383
2384         if ( !serverPort.GetPort() ) {
2385                 return;
2386         }
2387
2388         if ( !active ) {
2389                 ProcessConnectionLessMessages();
2390                 return;
2391         }
2392         
2393         gameTimeResidual += msec;
2394
2395         // spin in place processing incoming packets until enough time lapsed to run a new game frame
2396         do {
2397
2398                 do {
2399
2400                         // blocking read with game time residual timeout
2401                         newPacket = serverPort.GetPacketBlocking( from, msgBuf, size, sizeof( msgBuf ), USERCMD_MSEC - gameTimeResidual - 1 );
2402                         if ( newPacket ) {
2403                                 msg.Init( msgBuf, sizeof( msgBuf ) );
2404                                 msg.SetSize( size );
2405                                 msg.BeginReading();
2406                                 if ( ProcessMessage( from, msg ) ) {
2407                                         return; // return because rcon was used
2408                                 }
2409                         }
2410
2411                         msec = UpdateTime( 100 );
2412                         gameTimeResidual += msec;
2413
2414                 } while( newPacket );
2415
2416         } while( gameTimeResidual < USERCMD_MSEC );
2417
2418         // send heart beat to master servers
2419         MasterHeartbeat();
2420
2421         // check for clients that timed out
2422         CheckClientTimeouts();
2423
2424         if ( idAsyncNetwork::idleServer.GetBool() == ( !GetNumClients() || GetNumIdleClients() != GetNumClients()  ) ) {
2425                 idAsyncNetwork::idleServer.SetBool( !idAsyncNetwork::idleServer.GetBool() );
2426                 // the need to propagate right away, only this
2427                 sessLocal.mapSpawnData.serverInfo.Set( "si_idleServer", idAsyncNetwork::idleServer.GetString() );
2428                 game->SetServerInfo( sessLocal.mapSpawnData.serverInfo );
2429         }
2430
2431         // make sure the time doesn't wrap
2432         if ( serverTime > 0x70000000 ) {
2433                 ExecuteMapChange();
2434                 return;
2435         }
2436
2437         // check for synchronized cvar changes
2438         if ( cvarSystem->GetModifiedFlags() & CVAR_NETWORKSYNC ) {
2439                 idDict newCvars;
2440                 newCvars = *cvarSystem->MoveCVarsToDict( CVAR_NETWORKSYNC );
2441                 SendSyncedCvarsBroadcast( newCvars );
2442                 cvarSystem->ClearModifiedFlags( CVAR_NETWORKSYNC );
2443         }
2444
2445         // check for user info changes of the local client
2446         if ( cvarSystem->GetModifiedFlags() & CVAR_USERINFO ) {
2447                 if ( localClientNum >= 0 ) {
2448                         idDict newInfo;
2449                         game->ThrottleUserInfo( );
2450                         newInfo = *cvarSystem->MoveCVarsToDict( CVAR_USERINFO );
2451                         SendUserInfoBroadcast( localClientNum, newInfo );
2452                 }
2453                 cvarSystem->ClearModifiedFlags( CVAR_USERINFO );
2454         }
2455
2456         // advance the server game
2457         while( gameTimeResidual >= USERCMD_MSEC ) {
2458
2459                 // sample input for the local client
2460                 LocalClientInput();
2461
2462                 // duplicate usercmds for clients if no new ones are available
2463                 DuplicateUsercmds( gameFrame, gameTime );
2464
2465                 // advance game
2466                 gameReturn_t ret = game->RunFrame( userCmds[gameFrame & ( MAX_USERCMD_BACKUP - 1 ) ] );
2467
2468                 idAsyncNetwork::ExecuteSessionCommand( ret.sessionCommand );
2469
2470                 // update time
2471                 gameFrame++;
2472                 gameTime += USERCMD_MSEC;
2473                 gameTimeResidual -= USERCMD_MSEC;
2474         }
2475
2476         // duplicate usercmds so there is always at least one available to send with snapshots
2477         DuplicateUsercmds( gameFrame, gameTime );
2478
2479         // send snapshots to connected clients
2480         for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
2481                 serverClient_t &client = clients[i];
2482
2483                 if ( client.clientState == SCS_FREE || i == localClientNum ) {
2484                         continue;
2485                 }
2486
2487                 // modify maximum rate if necesary
2488                 if ( idAsyncNetwork::serverMaxClientRate.IsModified() ) {
2489                         client.channel.SetMaxOutgoingRate( Min( client.clientRate, idAsyncNetwork::serverMaxClientRate.GetInteger() ) );
2490                 }
2491
2492                 // if the channel is not yet ready to send new data
2493                 if ( !client.channel.ReadyToSend( serverTime ) ) {
2494                         continue;
2495                 }
2496
2497                 // send additional message fragments if the last message was too large to send at once
2498                 if ( client.channel.UnsentFragmentsLeft() ) {
2499                         client.channel.SendNextFragment( serverPort, serverTime );
2500                         continue;
2501                 }
2502
2503                 if ( client.clientState == SCS_INGAME ) {
2504                         if ( !SendSnapshotToClient( i ) ) {
2505                                 SendPingToClient( i );
2506                         }
2507                 } else {
2508                         SendEmptyToClient( i );
2509                 }
2510         }
2511
2512         if ( com_showAsyncStats.GetBool() ) {
2513
2514                 UpdateAsyncStatsAvg();
2515
2516                 // dedicated will verbose to console
2517                 if ( idAsyncNetwork::serverDedicated.GetBool() && serverTime >= nextAsyncStatsTime ) {
2518                         common->Printf( "delay = %d msec, total outgoing rate = %d KB/s, total incoming rate = %d KB/s\n", GetDelay(), 
2519                                                         GetOutgoingRate() >> 10, GetIncomingRate() >> 10 );
2520
2521                         for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
2522
2523                                 outgoingRate = GetClientOutgoingRate( i );
2524                                 incomingRate = GetClientIncomingRate( i );
2525                                 outgoingCompression = GetClientOutgoingCompression( i );
2526                                 incomingCompression = GetClientIncomingCompression( i );
2527
2528                                 if ( outgoingRate != -1 && incomingRate != -1 ) {
2529                                         common->Printf( "client %d: out rate = %d B/s (% -2.1f%%), in rate = %d B/s (% -2.1f%%)\n",
2530                                                                         i, outgoingRate, outgoingCompression, incomingRate, incomingCompression );
2531                                 }
2532                         }
2533
2534                         idStr msg;
2535                         GetAsyncStatsAvgMsg( msg );
2536                         common->Printf( va( "%s\n", msg.c_str() ) );
2537
2538                         nextAsyncStatsTime = serverTime + 1000;
2539                 }
2540         }
2541
2542         idAsyncNetwork::serverMaxClientRate.ClearModified();
2543 }
2544
2545 /*
2546 ==================
2547 idAsyncServer::PacifierUpdate
2548 ==================
2549 */
2550 void idAsyncServer::PacifierUpdate( void ) {
2551         int i;
2552
2553         if ( !IsActive() ) {
2554                 return;
2555         }
2556         realTime = Sys_Milliseconds();
2557         ProcessConnectionLessMessages();
2558         for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
2559                 if ( clients[i].clientState >= SCS_PUREWAIT ) {
2560                         if ( clients[i].channel.UnsentFragmentsLeft() ) {
2561                                 clients[i].channel.SendNextFragment( serverPort, serverTime );
2562                         } else {
2563                                 SendEmptyToClient( i );
2564                         }
2565                 }
2566         }
2567 }
2568
2569 /*
2570 ==================
2571 idAsyncServer::PrintOOB
2572 ==================
2573 */
2574 void idAsyncServer::PrintOOB( const netadr_t to, int opcode, const char *string ) {
2575         idBitMsg        outMsg;
2576         byte            msgBuf[ MAX_MESSAGE_SIZE ];
2577
2578         outMsg.Init( msgBuf, sizeof( msgBuf ) );
2579         outMsg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
2580         outMsg.WriteString( "print" );
2581         outMsg.WriteLong( opcode );
2582         outMsg.WriteString( string );
2583         serverPort.SendPacket( to, outMsg.GetData(), outMsg.GetSize() );
2584 }
2585
2586 /*
2587 ==================
2588 idAsyncServer::MasterHeartbeat
2589 ==================
2590 */
2591 void idAsyncServer::MasterHeartbeat( bool force ) {
2592         if ( idAsyncNetwork::LANServer.GetBool() ) {
2593                 if ( force ) {
2594                         common->Printf( "net_LANServer is enabled. Not sending heartbeats\n" );
2595                 }
2596                 return;
2597         }
2598         if ( force ) {
2599                 nextHeartbeatTime = 0;
2600         }
2601         // not yet
2602         if ( serverTime < nextHeartbeatTime ) {
2603                 return;
2604         }
2605         nextHeartbeatTime = serverTime + HEARTBEAT_MSEC;
2606         for ( int i = 0 ; i < MAX_MASTER_SERVERS ; i++ ) {
2607                 netadr_t adr;
2608                 if ( idAsyncNetwork::GetMasterAddress( i, adr ) ) {
2609                         common->Printf( "Sending heartbeat to %s\n", Sys_NetAdrToString( adr ) );
2610                         idBitMsg outMsg;
2611                         byte msgBuf[ MAX_MESSAGE_SIZE ];
2612                         outMsg.Init( msgBuf, sizeof( msgBuf ) );
2613                         outMsg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
2614                         outMsg.WriteString( "heartbeat" );
2615                         serverPort.SendPacket( adr, outMsg.GetData(), outMsg.GetSize() );
2616                 }
2617         }
2618 }
2619
2620 /*
2621 ===============
2622 idAsyncServer::SendEnterGameToClient
2623 ===============
2624 */
2625 void idAsyncServer::SendEnterGameToClient( int clientNum ) {
2626         idBitMsg        msg;
2627         byte            msgBuf[ MAX_MESSAGE_SIZE ];
2628
2629         msg.Init( msgBuf, sizeof( msgBuf ) );
2630         msg.WriteByte( SERVER_RELIABLE_MESSAGE_ENTERGAME );
2631         SendReliableMessage( clientNum, msg );
2632 }
2633
2634 /*
2635 ===============
2636 idAsyncServer::UpdateAsyncStatsAvg
2637 ===============
2638 */
2639 void idAsyncServer::UpdateAsyncStatsAvg( void ) {
2640         stats_average_sum -= stats_outrate[ stats_current ];
2641         stats_outrate[ stats_current ] = idAsyncNetwork::server.GetOutgoingRate();
2642         if ( stats_outrate[ stats_current ] > stats_max ) {
2643                 stats_max = stats_outrate[ stats_current ];
2644                 stats_max_index = stats_current;
2645         } else if ( stats_current == stats_max_index ) {
2646                 // find the new max
2647                 int i;
2648                 stats_max = 0;
2649                 for ( i = 0; i < stats_numsamples ; i++ ) {
2650                         if ( stats_outrate[ i ] > stats_max ) {
2651                                 stats_max = stats_outrate[ i ];
2652                                 stats_max_index = i;
2653                         }
2654                 }
2655         }
2656         stats_average_sum += stats_outrate[ stats_current ];
2657         stats_current++; stats_current %= stats_numsamples;
2658 }
2659
2660 /*
2661 ===============
2662 idAsyncServer::GetAsyncStatsAvgMsg
2663 ===============
2664 */
2665 void idAsyncServer::GetAsyncStatsAvgMsg( idStr &msg ) {
2666         sprintf( msg, "avrg out: %d B/s - max %d B/s ( over %d ms )", stats_average_sum / stats_numsamples, stats_max, idAsyncNetwork::serverSnapshotDelay.GetInteger() * stats_numsamples );
2667 }
2668
2669 /*
2670 ===============
2671 idAsyncServer::ProcessDownloadRequestMessage
2672 ===============
2673 */
2674 void idAsyncServer::ProcessDownloadRequestMessage( const netadr_t from, const idBitMsg &msg ) {
2675         int                     challenge, clientId, iclient, numPaks, i;
2676         int                     dlGamePak;
2677         int                     dlPakChecksum;
2678         int                     dlSize[ MAX_PURE_PAKS ];        // sizes
2679         idStrList       pakNames;                                       // relative path
2680         idStrList       pakURLs;                                        // game URLs
2681         char            pakbuf[ MAX_STRING_CHARS ];
2682         idStr           paklist;
2683         byte            msgBuf[ MAX_MESSAGE_SIZE ];
2684         byte            tmpBuf[ MAX_MESSAGE_SIZE ];
2685         idBitMsg        outMsg, tmpMsg;
2686         int                     dlRequest;
2687         int                     voidSlots = 0;                          // to count and verbose the right number of paks requested for downloads
2688
2689         challenge = msg.ReadLong();
2690         clientId = msg.ReadShort();
2691         dlRequest = msg.ReadLong();
2692
2693         if ( ( iclient = ValidateChallenge( from, challenge, clientId ) ) == -1 ) {
2694                 return;
2695         }
2696
2697         if ( challenges[ iclient ].authState != CDK_PUREWAIT ) {
2698                 common->DPrintf( "client %s: got download request message, not in CDK_PUREWAIT\n", Sys_NetAdrToString( from ) );
2699                 return;
2700         }
2701         
2702         // the first token of the pak names list passed to the game will be empty if no game pak is requested
2703         dlGamePak = msg.ReadLong();
2704         if ( dlGamePak ) {
2705                 if ( !( dlSize[ 0 ] = fileSystem->ValidateDownloadPakForChecksum( dlGamePak, pakbuf, true ) ) ) {
2706                         common->Warning( "client requested unknown game pak 0x%x", dlGamePak );
2707                         pakbuf[ 0 ] = '\0';
2708                         voidSlots++;
2709                 }
2710         } else {
2711                 pakbuf[ 0 ] = '\0';
2712                 voidSlots++;
2713         }
2714         pakNames.Append( pakbuf );
2715         numPaks = 1;
2716
2717         // read the checksums, build path names and pass that to the game code
2718         dlPakChecksum = msg.ReadLong();
2719         while ( dlPakChecksum ) {
2720                 if ( !( dlSize[ numPaks ] = fileSystem->ValidateDownloadPakForChecksum( dlPakChecksum, pakbuf, false ) ) ) {
2721                         // we pass an empty token to the game so our list doesn't get offset
2722                         common->Warning( "client requested an unknown pak 0x%x", dlPakChecksum );
2723                         pakbuf[ 0 ] = '\0';
2724                         voidSlots++;
2725                 }
2726                 pakNames.Append( pakbuf );
2727                 numPaks++;
2728                 dlPakChecksum = msg.ReadLong();
2729         }
2730
2731         for ( i = 0; i < pakNames.Num(); i++ ) {
2732                 if ( i > 0 ) {
2733                         paklist += ";";
2734                 }
2735                 paklist += pakNames[ i ].c_str();
2736         }
2737
2738         // read the message and pass it to the game code
2739         common->DPrintf( "got download request for %d paks - %s\n", numPaks - voidSlots, paklist.c_str() );
2740
2741         outMsg.Init( msgBuf, sizeof( msgBuf ) );
2742         outMsg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
2743         outMsg.WriteString( "downloadInfo" );
2744         outMsg.WriteLong( dlRequest );
2745         if ( !game->DownloadRequest( Sys_NetAdrToString( from ), challenges[ iclient ].guid, paklist.c_str(), pakbuf ) ) {
2746                 common->DPrintf( "game: no downloads\n" );
2747                 outMsg.WriteByte( SERVER_DL_NONE );
2748                 serverPort.SendPacket( from, outMsg.GetData(), outMsg.GetSize() );
2749                 return;
2750         }
2751
2752         char *token, *next;
2753         int type = 0;
2754
2755         token = pakbuf;
2756         next = strchr( token, ';' );
2757         while ( token ) {
2758                 if ( next ) {
2759                         *next = '\0';
2760                 }
2761                 
2762                 if ( type == 0 ) {
2763                         type = atoi( token );
2764                 } else if ( type == SERVER_DL_REDIRECT ) {
2765                         common->DPrintf( "download request: redirect to URL %s\n", token );
2766                         outMsg.WriteByte( SERVER_DL_REDIRECT );
2767                         outMsg.WriteString( token );
2768                         serverPort.SendPacket( from, outMsg.GetData(), outMsg.GetSize() );
2769                         return;
2770                 } else if ( type == SERVER_DL_LIST ) {
2771                         pakURLs.Append( token );
2772                 } else {
2773                         common->DPrintf( "wrong op type %d\n", type );
2774                         next = token = NULL;
2775                 }
2776                 
2777                 if ( next ) {
2778                         token = next + 1;
2779                         next = strchr( token, ';' );
2780                 } else {
2781                         token = NULL;
2782                 }
2783         }
2784
2785         if ( type == SERVER_DL_LIST ) {
2786                 int totalDlSize = 0;
2787                 int numActualPaks = 0;
2788                 
2789                 // put the answer packet together
2790                 outMsg.WriteByte( SERVER_DL_LIST );                     
2791                 
2792                 tmpMsg.Init( tmpBuf, MAX_MESSAGE_SIZE );
2793
2794                 for ( i = 0; i < pakURLs.Num(); i++ ) {
2795                         tmpMsg.BeginWriting();
2796                         if ( !dlSize[ i ] || !pakURLs[ i ].Length() ) {
2797                                 // still send the relative path so the client knows what it missed
2798                                 tmpMsg.WriteByte( SERVER_PAK_NO );
2799                                 tmpMsg.WriteString( pakNames[ i ] );
2800                         } else {
2801                                 totalDlSize += dlSize[ i ];
2802                                 numActualPaks++;
2803                                 tmpMsg.WriteByte( SERVER_PAK_YES );
2804                                 tmpMsg.WriteString( pakNames[ i ] );
2805                                 tmpMsg.WriteString( pakURLs[ i ] );
2806                                 tmpMsg.WriteLong( dlSize[ i ] );
2807                         }
2808                         
2809                         // keep last 5 bytes for an 'end of message' - SERVER_PAK_END and the totalDlSize long
2810                         if ( outMsg.GetRemainingSpace() - tmpMsg.GetSize() > 5 ) {
2811                                 outMsg.WriteData( tmpMsg.GetData(), tmpMsg.GetSize() );
2812                         } else {
2813                                 outMsg.WriteByte( SERVER_PAK_END );
2814                                 break;
2815                         }
2816                 }
2817                 if ( i == pakURLs.Num() ) {
2818                         // put a closure even if size not exceeded
2819                         outMsg.WriteByte( SERVER_PAK_END );
2820                 }
2821                 common->DPrintf( "download request: download %d paks, %d bytes\n", numActualPaks, totalDlSize );
2822
2823                 serverPort.SendPacket( from, outMsg.GetData(), outMsg.GetSize() );
2824         }
2825 }