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