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