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