doom3-gpl
Doom 3 GPL source release
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
AsyncClient.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 
37 const int EMPTY_RESEND_TIME = 500;
38 const int PREDICTION_FAST_ADJUST = 4;
39 
40 
41 /*
42 ==================
43 idAsyncClient::idAsyncClient
44 ==================
45 */
47  guiNetMenu = NULL;
49  Clear();
50 }
51 
52 /*
53 ==================
54 idAsyncClient::Clear
55 ==================
56 */
57 void idAsyncClient::Clear( void ) {
58  active = false;
59  realTime = 0;
60  clientTime = 0;
61  clientId = 0;
63  clientNum = 0;
65  clientPrediction = 0;
67  serverId = 0;
68  serverChallenge = 0;
70  lastConnectTime = -9999;
71  lastEmptyTime = -9999;
72  lastPacketTime = -9999;
73  lastSnapshotTime = -9999;
75  snapshotGameTime = 0;
76  snapshotSequence = 0;
78  gameFrame = 0;
79  gameTimeResidual = 0;
80  gameTime = 0;
81  memset( userCmds, 0, sizeof( userCmds ) );
83  lastRconTime = 0;
84  showUpdateMessage = false;
85  lastFrameDelta = 0;
86 
87  dlRequest = -1;
88  dlCount = -1;
89  memset( dlChecksums, 0, sizeof( int ) * MAX_PURE_PAKS );
90  currentDlSize = 0;
91  totalDlSize = 0;
92 }
93 
94 /*
95 ==================
96 idAsyncClient::Shutdown
97 ==================
98 */
100  guiNetMenu = NULL;
101  updateMSG.Clear();
102  updateURL.Clear();
103  updateFile.Clear();
106  dlList.Clear();
107 }
108 
109 /*
110 ==================
111 idAsyncClient::InitPort
112 ==================
113 */
115  // if this is the first time we connect to a server, open the UDP port
116  if ( !clientPort.GetPort() ) {
117  if ( !clientPort.InitForPort( PORT_ANY ) ) {
118  common->Printf( "Couldn't open client network port.\n" );
119  return false;
120  }
121  }
122  // maintain it valid between connects and ui manager reloads
123  guiNetMenu = uiManager->FindGui( "guis/netmenu.gui", true, false, true );
124 
125  return true;
126 }
127 
128 /*
129 ==================
130 idAsyncClient::ClosePort
131 ==================
132 */
134  clientPort.Close();
135 }
136 
137 /*
138 ==================
139 idAsyncClient::ClearPendingPackets
140 ==================
141 */
143  int size;
144  byte msgBuf[MAX_MESSAGE_SIZE];
145  netadr_t from;
146 
147  while( clientPort.GetPacket( from, msgBuf, size, sizeof( msgBuf ) ) ) {
148  }
149 }
150 
151 /*
152 ==================
153 idAsyncClient::HandleGuiCommandInternal
154 ==================
155 */
156 const char* idAsyncClient::HandleGuiCommandInternal( const char *cmd ) {
157  if ( !idStr::Cmp( cmd, "abort" ) || !idStr::Cmp( cmd, "pure_abort" ) ) {
158  common->DPrintf( "connection aborted\n" );
159  cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
160  return "";
161  } else {
162  common->DWarning( "idAsyncClient::HandleGuiCommand: unknown cmd %s", cmd );
163  }
164  return NULL;
165 }
166 
167 /*
168 ==================
169 idAsyncClient::HandleGuiCommand
170 ==================
171 */
172 const char* idAsyncClient::HandleGuiCommand( const char *cmd ) {
174 }
175 
176 /*
177 ==================
178 idAsyncClient::ConnectToServer
179 ==================
180 */
182  // shutdown any current game. that includes network disconnect
183  session->Stop();
184 
185  if ( !InitPort() ) {
186  return;
187  }
188 
189  if ( cvarSystem->GetCVarBool( "net_serverDedicated" ) ) {
190  common->Printf( "Can't connect to a server as dedicated\n" );
191  return;
192  }
193 
194  // trash any currently pending packets
196 
197  serverAddress = adr;
198 
199  // clear the client state
200  Clear();
201 
202  // get a pseudo random client id, but don't use the id which is reserved for connectionless packets
204 
205  // calculate a checksum on some of the essential data used
207 
208  // start challenging the server
210 
211  active = true;
212 
213  guiNetMenu = uiManager->FindGui( "guis/netmenu.gui", true, false, true );
214  guiNetMenu->SetStateString( "status", va( common->GetLanguageDict()->GetString( "#str_06749" ), Sys_NetAdrToString( adr ) ) );
216 }
217 
218 /*
219 ==================
220 idAsyncClient::Reconnect
221 ==================
222 */
225 }
226 
227 /*
228 ==================
229 idAsyncClient::ConnectToServer
230 ==================
231 */
233  int serverNum;
234  netadr_t adr;
235 
236  if ( idStr::IsNumeric( address ) ) {
237  serverNum = atoi( address );
238  if ( serverNum < 0 || serverNum >= serverList.Num() ) {
239  session->MessageBox( MSG_OK, va( common->GetLanguageDict()->GetString( "#str_06733" ), serverNum ), common->GetLanguageDict()->GetString( "#str_06735" ), true );
240  return;
241  }
242  adr = serverList[ serverNum ].adr;
243  } else {
244  if ( !Sys_StringToNetAdr( address, &adr, true ) ) {
245  session->MessageBox( MSG_OK, va( common->GetLanguageDict()->GetString( "#str_06734" ), address ), common->GetLanguageDict()->GetString( "#str_06735" ), true );
246  return;
247  }
248  }
249  if ( !adr.port ) {
250  adr.port = PORT_SERVER;
251  }
252 
253  common->Printf( "\"%s\" resolved to %s\n", address, Sys_NetAdrToString( adr ) );
254 
255  ConnectToServer( adr );
256 }
257 
258 /*
259 ==================
260 idAsyncClient::DisconnectFromServer
261 ==================
262 */
264  idBitMsg msg;
265  byte msgBuf[MAX_MESSAGE_SIZE];
266 
267  if ( clientState >= CS_CONNECTED ) {
268  // if we were actually connected, clear the pure list
270 
271  // send reliable disconnect to server
272  msg.Init( msgBuf, sizeof( msgBuf ) );
274  msg.WriteString( "disconnect" );
275 
276  if ( !channel.SendReliableMessage( msg ) ) {
277  common->Error( "client->server reliable messages overflow\n" );
278  }
279 
280  SendEmptyToServer( true );
281  SendEmptyToServer( true );
282  SendEmptyToServer( true );
283  }
284 
285  if ( clientState != CS_PURERESTART ) {
286  channel.Shutdown();
288  }
289 
290  active = false;
291 }
292 
293 /*
294 ==================
295 idAsyncClient::GetServerInfo
296 ==================
297 */
299  idBitMsg msg;
300  byte msgBuf[MAX_MESSAGE_SIZE];
301 
302  if ( !InitPort() ) {
303  return;
304  }
305 
306  msg.Init( msgBuf, sizeof( msgBuf ) );
308  msg.WriteString( "getInfo" );
309  msg.WriteLong( serverList.GetChallenge() ); // challenge
310 
311  clientPort.SendPacket( adr, msg.GetData(), msg.GetSize() );
312 }
313 
314 /*
315 ==================
316 idAsyncClient::GetServerInfo
317 ==================
318 */
320  netadr_t adr;
321 
322  if ( address && *address != '\0' ) {
323  if ( !Sys_StringToNetAdr( address, &adr, true ) ) {
324  common->Printf( "Couldn't get server address for \"%s\"\n", address );
325  return;
326  }
327  } else if ( active ) {
328  adr = serverAddress;
329  } else if ( idAsyncNetwork::server.IsActive() ) {
330  // used to be a Sys_StringToNetAdr( "localhost", &adr, true ); and send a packet over loopback
331  // but this breaks with net_ip ( typically, for multi-homed servers )
333  return;
334  } else {
335  common->Printf( "no server found\n" );
336  return;
337  }
338 
339  if ( !adr.port ) {
340  adr.port = PORT_SERVER;
341  }
342 
343  GetServerInfo( adr );
344 }
345 
346 /*
347 ==================
348 idAsyncClient::GetLANServers
349 ==================
350 */
352  int i;
353  idBitMsg msg;
354  byte msgBuf[MAX_MESSAGE_SIZE];
355  netadr_t broadcastAddress;
356 
357  if ( !InitPort() ) {
358  return;
359  }
360 
362 
364 
365  msg.Init( msgBuf, sizeof( msgBuf ) );
367  msg.WriteString( "getInfo" );
369 
370  broadcastAddress.type = NA_BROADCAST;
371  for ( i = 0; i < MAX_SERVER_PORTS; i++ ) {
372  broadcastAddress.port = PORT_SERVER + i;
373  clientPort.SendPacket( broadcastAddress, msg.GetData(), msg.GetSize() );
374  }
375 }
376 
377 /*
378 ==================
379 idAsyncClient::GetNETServers
380 ==================
381 */
383  idBitMsg msg;
384  byte msgBuf[MAX_MESSAGE_SIZE];
385 
387 
388  // NetScan only clears GUI and results, not the stored list
389  serverList.Clear( );
390  serverList.NetScan( );
391  serverList.StartServers( true );
392 
393  msg.Init( msgBuf, sizeof( msgBuf ) );
395  msg.WriteString( "getServers" );
397  msg.WriteString( cvarSystem->GetCVarString( "fs_game" ) );
398  msg.WriteBits( cvarSystem->GetCVarInteger( "gui_filter_password" ), 2 );
399  msg.WriteBits( cvarSystem->GetCVarInteger( "gui_filter_players" ), 2 );
400  msg.WriteBits( cvarSystem->GetCVarInteger( "gui_filter_gameType" ), 2 );
401 
402  netadr_t adr;
403  if ( idAsyncNetwork::GetMasterAddress( 0, adr ) ) {
404  clientPort.SendPacket( adr, msg.GetData(), msg.GetSize() );
405  }
406 }
407 
408 /*
409 ==================
410 idAsyncClient::ListServers
411 ==================
412 */
414  int i;
415 
416  for ( i = 0; i < serverList.Num(); i++ ) {
417  common->Printf( "%3d: %s %dms (%s)\n", i, serverList[i].serverInfo.GetString( "si_name" ), serverList[ i ].ping, Sys_NetAdrToString( serverList[i].adr ) );
418  }
419 }
420 
421 /*
422 ==================
423 idAsyncClient::ClearServers
424 ==================
425 */
427  serverList.Clear();
428 }
429 
430 /*
431 ==================
432 idAsyncClient::RemoteConsole
433 ==================
434 */
435 void idAsyncClient::RemoteConsole( const char *command ) {
436  netadr_t adr;
437  idBitMsg msg;
438  byte msgBuf[MAX_MESSAGE_SIZE];
439 
440  if ( !InitPort() ) {
441  return;
442  }
443 
444  if ( active ) {
445  adr = serverAddress;
446  } else {
448  }
449 
450  if ( !adr.port ) {
451  adr.port = PORT_SERVER;
452  }
453 
454  lastRconAddress = adr;
456 
457  msg.Init( msgBuf, sizeof( msgBuf ) );
459  msg.WriteString( "rcon" );
461  msg.WriteString( command );
462 
463  clientPort.SendPacket( adr, msg.GetData(), msg.GetSize() );
464 }
465 
466 /*
467 ==================
468 idAsyncClient::GetPrediction
469 ==================
470 */
471 int idAsyncClient::GetPrediction( void ) const {
472  if ( clientState < CS_CONNECTED ) {
473  return -1;
474  } else {
475  return clientPrediction;
476  }
477 }
478 
479 /*
480 ==================
481 idAsyncClient::GetTimeSinceLastPacket
482 ==================
483 */
485  if ( clientState < CS_CONNECTED ) {
486  return -1;
487  } else {
488  return clientTime - lastPacketTime;
489  }
490 }
491 
492 /*
493 ==================
494 idAsyncClient::GetOutgoingRate
495 ==================
496 */
498  if ( clientState < CS_CONNECTED ) {
499  return -1;
500  } else {
501  return channel.GetOutgoingRate();
502  }
503 }
504 
505 /*
506 ==================
507 idAsyncClient::GetIncomingRate
508 ==================
509 */
511  if ( clientState < CS_CONNECTED ) {
512  return -1;
513  } else {
514  return channel.GetIncomingRate();
515  }
516 }
517 
518 /*
519 ==================
520 idAsyncClient::GetOutgoingCompression
521 ==================
522 */
524  if ( clientState < CS_CONNECTED ) {
525  return 0.0f;
526  } else {
528  }
529 }
530 
531 /*
532 ==================
533 idAsyncClient::GetIncomingCompression
534 ==================
535 */
537  if ( clientState < CS_CONNECTED ) {
538  return 0.0f;
539  } else {
541  }
542 }
543 
544 /*
545 ==================
546 idAsyncClient::GetIncomingPacketLoss
547 ==================
548 */
550  if ( clientState < CS_CONNECTED ) {
551  return 0.0f;
552  } else {
554  }
555 }
556 
557 /*
558 ==================
559 idAsyncClient::DuplicateUsercmds
560 ==================
561 */
562 void idAsyncClient::DuplicateUsercmds( int frame, int time ) {
563  int i, previousIndex, currentIndex;
564 
565  previousIndex = ( frame - 1 ) & ( MAX_USERCMD_BACKUP - 1 );
566  currentIndex = frame & ( MAX_USERCMD_BACKUP - 1 );
567 
568  // duplicate previous user commands if no new commands are available for a client
569  for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
570  idAsyncNetwork::DuplicateUsercmd( userCmds[previousIndex][i], userCmds[currentIndex][i], frame, time );
571  }
572 }
573 
574 /*
575 ==================
576 idAsyncClient::SendUserInfoToServer
577 ==================
578 */
580  idBitMsg msg;
581  byte msgBuf[MAX_MESSAGE_SIZE];
582  idDict info;
583 
584  if ( clientState < CS_CONNECTED ) {
585  return;
586  }
587 
589 
590  // send reliable client info to server
591  msg.Init( msgBuf, sizeof( msgBuf ) );
594 
595  if ( !channel.SendReliableMessage( msg ) ) {
596  common->Error( "client->server reliable messages overflow\n" );
597  }
598 
600 }
601 
602 /*
603 ==================
604 idAsyncClient::SendEmptyToServer
605 ==================
606 */
607 void idAsyncClient::SendEmptyToServer( bool force, bool mapLoad ) {
608  idBitMsg msg;
609  byte msgBuf[MAX_MESSAGE_SIZE];
610 
611  if ( lastEmptyTime > realTime ) {
613  }
614 
615  if ( !force && ( realTime - lastEmptyTime < EMPTY_RESEND_TIME ) ) {
616  return;
617  }
618 
619  if ( idAsyncNetwork::verbose.GetInteger() ) {
620  common->Printf( "sending empty to server, gameInitId = %d\n", mapLoad ? GAME_INIT_ID_MAP_LOAD : gameInitId );
621  }
622 
623  msg.Init( msgBuf, sizeof( msgBuf ) );
625  msg.WriteLong( mapLoad ? GAME_INIT_ID_MAP_LOAD : gameInitId );
628 
630 
631  while( channel.UnsentFragmentsLeft() ) {
633  }
634 
636 }
637 
638 /*
639 ==================
640 idAsyncClient::SendPingResponseToServer
641 ==================
642 */
644  idBitMsg msg;
645  byte msgBuf[MAX_MESSAGE_SIZE];
646 
647  if ( idAsyncNetwork::verbose.GetInteger() == 2 ) {
648  common->Printf( "sending ping response to server, gameInitId = %d\n", gameInitId );
649  }
650 
651  msg.Init( msgBuf, sizeof( msgBuf ) );
653  msg.WriteLong( gameInitId );
656  msg.WriteLong( time );
657 
659  while( channel.UnsentFragmentsLeft() ) {
661  }
662 }
663 
664 /*
665 ==================
666 idAsyncClient::SendUsercmdsToServer
667 ==================
668 */
670  int i, numUsercmds, index;
671  idBitMsg msg;
672  byte msgBuf[MAX_MESSAGE_SIZE];
673  usercmd_t * last;
674 
675  if ( idAsyncNetwork::verbose.GetInteger() == 2 ) {
676  common->Printf( "sending usercmd to server: gameInitId = %d, gameFrame = %d, gameTime = %d\n", gameInitId, gameFrame, gameTime );
677  }
678 
679  // generate user command for this client
680  index = gameFrame & ( MAX_USERCMD_BACKUP - 1 );
682  userCmds[index][clientNum].gameFrame = gameFrame;
683  userCmds[index][clientNum].gameTime = gameTime;
684 
685  // send the user commands to the server
686  msg.Init( msgBuf, sizeof( msgBuf ) );
688  msg.WriteLong( gameInitId );
692 
693  numUsercmds = idMath::ClampInt( 0, 10, idAsyncNetwork::clientUsercmdBackup.GetInteger() ) + 1;
694 
695  // write the user commands
696  msg.WriteLong( gameFrame );
697  msg.WriteByte( numUsercmds );
698  for ( last = NULL, i = gameFrame - numUsercmds + 1; i <= gameFrame; i++ ) {
699  index = i & ( MAX_USERCMD_BACKUP - 1 );
700  idAsyncNetwork::WriteUserCmdDelta( msg, userCmds[index][clientNum], last );
701  last = &userCmds[index][clientNum];
702  }
703 
705  while( channel.UnsentFragmentsLeft() ) {
707  }
708 }
709 
710 /*
711 ==================
712 idAsyncClient::InitGame
713 ==================
714 */
715 void idAsyncClient::InitGame( int serverGameInitId, int serverGameFrame, int serverGameTime, const idDict &serverSI ) {
716  gameInitId = serverGameInitId;
717  gameFrame = snapshotGameFrame = serverGameFrame;
718  gameTime = snapshotGameTime = serverGameTime;
719  gameTimeResidual = 0;
720  memset( userCmds, 0, sizeof( userCmds ) );
721 
722  for ( int i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
724  }
725 
726  sessLocal.mapSpawnData.serverInfo = serverSI;
727 }
728 
729 /*
730 ==================
731 idAsyncClient::ProcessUnreliableServerMessage
732 ==================
733 */
735  int i, j, index, id, numDuplicatedUsercmds, aheadOfServer, numUsercmds, delta;
736  int serverGameInitId, serverGameFrame, serverGameTime;
737  idDict serverSI;
738  usercmd_t *last;
739  bool pureWait;
740 
741  serverGameInitId = msg.ReadLong();
742 
743  id = msg.ReadByte();
744  switch( id ) {
746  if ( idAsyncNetwork::verbose.GetInteger() ) {
747  common->Printf( "received empty message from server\n" );
748  }
749  break;
750  }
752  if ( idAsyncNetwork::verbose.GetInteger() == 2 ) {
753  common->Printf( "received ping message from server\n" );
754  }
756  break;
757  }
759  serverGameFrame = msg.ReadLong();
760  serverGameTime = msg.ReadLong();
761  msg.ReadDeltaDict( serverSI, NULL );
762  pureWait = serverSI.GetBool( "si_pure" );
763 
764  InitGame( serverGameInitId, serverGameFrame, serverGameTime, serverSI );
765 
766  channel.ResetRate();
767 
768  if ( idAsyncNetwork::verbose.GetInteger() ) {
769  common->Printf( "received gameinit, gameInitId = %d, gameFrame = %d, gameTime = %d\n", gameInitId, gameFrame, gameTime );
770  }
771 
772  // mute sound
773  soundSystem->SetMute( true );
774 
775  // ensure chat icon goes away when the GUI is changed...
776  //cvarSystem->SetCVarBool( "ui_chat", false );
777 
778  if ( pureWait ) {
779  guiNetMenu = uiManager->FindGui( "guis/netmenu.gui", true, false, true );
781  session->MessageBox( MSG_ABORT, common->GetLanguageDict()->GetString ( "#str_04317" ), common->GetLanguageDict()->GetString ( "#str_04318" ), false, "pure_abort" );
782  } else {
783  // load map
784  session->SetGUI( NULL, NULL );
786  }
787 
788  break;
789  }
791  // if the snapshot is from a different game
792  if ( serverGameInitId != gameInitId ) {
793  if ( idAsyncNetwork::verbose.GetInteger() ) {
794  common->Printf( "ignoring snapshot with != gameInitId\n" );
795  }
796  break;
797  }
798 
799  snapshotSequence = msg.ReadLong();
800  snapshotGameFrame = msg.ReadLong();
801  snapshotGameTime = msg.ReadLong();
802  numDuplicatedUsercmds = msg.ReadByte();
803  aheadOfServer = msg.ReadShort();
804 
805  // read the game snapshot
806  game->ClientReadSnapshot( clientNum, snapshotSequence, snapshotGameFrame, snapshotGameTime, numDuplicatedUsercmds, aheadOfServer, msg );
807 
808  // read user commands of other clients from the snapshot
809  for ( last = NULL, i = msg.ReadByte(); i < MAX_ASYNC_CLIENTS; i = msg.ReadByte() ) {
810  numUsercmds = msg.ReadByte();
811  if ( numUsercmds > MAX_USERCMD_RELAY ) {
812  common->Error( "snapshot %d contains too many user commands for client %d", snapshotSequence, i );
813  break;
814  }
815  for ( j = 0; j < numUsercmds; j++ ) {
816  index = ( snapshotGameFrame + j ) & ( MAX_USERCMD_BACKUP - 1 );
817  idAsyncNetwork::ReadUserCmdDelta( msg, userCmds[index][i], last );
820  last = &userCmds[index][i];
821  }
822  // clear all user commands after the ones just read from the snapshot
823  for ( j = numUsercmds; j < MAX_USERCMD_BACKUP; j++ ) {
824  index = ( snapshotGameFrame + j ) & ( MAX_USERCMD_BACKUP - 1 );
825  userCmds[index][i].gameFrame = 0;
826  userCmds[index][i].gameTime = 0;
827  }
828  }
829 
830  // if this is the first snapshot after a game init was received
831  if ( clientState == CS_CONNECTED ) {
832  gameTimeResidual = 0;
835  if ( idAsyncNetwork::verbose.GetInteger() ) {
836  common->Printf( "received first snapshot, gameInitId = %d, gameFrame %d gameTime %d\n", gameInitId, snapshotGameFrame, snapshotGameTime );
837  }
838  }
839 
840  // if the snapshot is newer than the clients current game time
841  if ( gameTime < snapshotGameTime || gameTime > snapshotGameTime + idAsyncNetwork::clientMaxPrediction.GetInteger() ) {
846  }
847 
848  // adjust the client prediction time based on the snapshot time
849  clientPrediction -= ( 1 - ( INTSIGNBITSET( aheadOfServer - idAsyncNetwork::clientPrediction.GetInteger() ) << 1 ) );
852  clientPredictTime -= ( delta / PREDICTION_FAST_ADJUST ) + ( 1 - ( INTSIGNBITSET( delta ) << 1 ) );
853 
855 
856  if ( idAsyncNetwork::verbose.GetInteger() == 2 ) {
857  common->Printf( "received snapshot, gameInitId = %d, gameFrame = %d, gameTime = %d\n", gameInitId, gameFrame, gameTime );
858  }
859 
860  if ( numDuplicatedUsercmds && ( idAsyncNetwork::verbose.GetInteger() == 2 ) ) {
861  common->Printf( "server duplicated %d user commands before snapshot %d\n", numDuplicatedUsercmds, snapshotGameFrame );
862  }
863  break;
864  }
865  default: {
866  common->Printf( "unknown unreliable server message %d\n", id );
867  break;
868  }
869  }
870 }
871 
872 /*
873 ==================
874 idAsyncClient::ProcessReliableMessagePure
875 ==================
876 */
878  idBitMsg outMsg;
879  byte msgBuf[ MAX_MESSAGE_SIZE ];
880  int inChecksums[ MAX_PURE_PAKS ];
881  int i;
882  int gamePakChecksum;
883  int serverGameInitId;
884 
885  session->SetGUI( NULL, NULL );
886 
887  serverGameInitId = msg.ReadLong();
888 
889  if ( serverGameInitId != gameInitId ) {
890  common->DPrintf( "ignoring pure server checksum from an outdated gameInitId (%d)\n", serverGameInitId );
891  return;
892  }
893 
895 
896  return;
897  }
898 
899  if ( idAsyncNetwork::verbose.GetInteger() ) {
900  common->Printf( "received new pure server info. ExecuteMapChange and report back\n" );
901  }
902 
903  // it is now ok to load the next map with updated pure checksums
904  sessLocal.ExecuteMapChange( true );
905 
906  // upon receiving our pure list, the server will send us SCS_INGAME and we'll start getting snapshots
907  fileSystem->GetPureServerChecksums( inChecksums, -1, &gamePakChecksum );
908  outMsg.Init( msgBuf, sizeof( msgBuf ) );
910 
911  outMsg.WriteLong( gameInitId );
912 
913  i = 0;
914  while ( inChecksums[ i ] ) {
915  outMsg.WriteLong( inChecksums[ i++ ] );
916  }
917  outMsg.WriteLong( 0 );
918  outMsg.WriteLong( gamePakChecksum );
919 
920  if ( !channel.SendReliableMessage( outMsg ) ) {
921  common->Error( "client->server reliable messages overflow\n" );
922  }
923 }
924 
925 /*
926 ===============
927 idAsyncClient::ReadLocalizedServerString
928 ===============
929 */
930 void idAsyncClient::ReadLocalizedServerString( const idBitMsg &msg, char *out, int maxLen ) {
931  msg.ReadString( out, maxLen );
932  // look up localized string. if the message is not an #str_ format, we'll just get it back unchanged
933  idStr::snPrintf( out, maxLen - 1, "%s", common->GetLanguageDict()->GetString( out ) );
934 }
935 
936 /*
937 ==================
938 idAsyncClient::ProcessReliableServerMessages
939 ==================
940 */
942  idBitMsg msg;
943  byte msgBuf[MAX_MESSAGE_SIZE];
944  byte id;
945 
946  msg.Init( msgBuf, sizeof( msgBuf ) );
947 
948  while ( channel.GetReliableMessage( msg ) ) {
949  id = msg.ReadByte();
950  switch( id ) {
952  int clientNum;
953  clientNum = msg.ReadByte();
954 
956  bool haveBase = ( msg.ReadBits( 1 ) != 0 );
957 
958 #if ID_CLIENTINFO_TAGS
959  int checksum = info.Checksum();
960  int srv_checksum = msg.ReadLong();
961  if ( checksum != srv_checksum ) {
962  common->DPrintf( "SERVER_RELIABLE_MESSAGE_CLIENTINFO %d (haveBase: %s): != checksums srv: 0x%x local: 0x%x\n", clientNum, haveBase ? "true" : "false", checksum, srv_checksum );
963  info.Print();
964  } else {
965  common->DPrintf( "SERVER_RELIABLE_MESSAGE_CLIENTINFO %d (haveBase: %s): checksums ok 0x%x\n", clientNum, haveBase ? "true" : "false", checksum );
966  }
967 #endif
968 
969  if ( haveBase ) {
970  msg.ReadDeltaDict( info, &info );
971  } else {
972  msg.ReadDeltaDict( info, NULL );
973  }
974 
975  // server forces us to a different userinfo
976  if ( clientNum == idAsyncClient::clientNum ) {
977  common->DPrintf( "local user info modified by server\n" );
978  cvarSystem->SetCVarsFromDict( info );
979  cvarSystem->ClearModifiedFlags( CVAR_USERINFO ); // don't emit back
980  }
981  game->SetUserInfo( clientNum, info, true, false );
982  break;
983  }
986  msg.ReadDeltaDict( info, &info );
987  cvarSystem->SetCVarsFromDict( info );
988  if ( !idAsyncNetwork::allowCheats.GetBool() ) {
990  }
991  break;
992  }
994  char string[MAX_STRING_CHARS];
995  msg.ReadString( string, MAX_STRING_CHARS );
996  common->Printf( "%s\n", string );
997  break;
998  }
1000  int clientNum;
1001  char string[MAX_STRING_CHARS];
1002  clientNum = msg.ReadLong( );
1004  if ( clientNum == idAsyncClient::clientNum ) {
1005  session->Stop();
1006  session->MessageBox( MSG_OK, string, common->GetLanguageDict()->GetString ( "#str_04319" ), true );
1007  session->StartMenu();
1008  } else {
1009  common->Printf( "client %d %s\n", clientNum, string );
1010  cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "addChatLine \"%s^0 %s\"", sessLocal.mapSpawnData.userInfo[ clientNum ].GetString( "ui_name" ), string ) );
1012  }
1013  break;
1014  }
1016  int sequence;
1017  sequence = msg.ReadLong();
1018  if ( !game->ClientApplySnapshot( clientNum, sequence ) ) {
1019  session->Stop();
1020  common->Error( "couldn't apply snapshot %d", sequence );
1021  }
1022  break;
1023  }
1026  break;
1027  }
1029  if ( idAsyncNetwork::verbose.GetBool() ) {
1030  common->Printf( "got MESSAGE_RELOAD from server\n" );
1031  }
1032  // simply reconnect, so that if the server restarts in pure mode we can get the right list and avoid spurious reloads
1033  cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "reconnect\n" );
1034  break;
1035  }
1040  break;
1041  }
1042  default: {
1043  // pass reliable message on to game code
1045  break;
1046  }
1047  }
1048  }
1049 }
1050 
1051 /*
1052 ==================
1053 idAsyncClient::ProcessChallengeResponseMessage
1054 ==================
1055 */
1057  char serverGame[ MAX_STRING_CHARS ], serverGameBase[ MAX_STRING_CHARS ];
1058 
1059  if ( clientState != CS_CHALLENGING ) {
1060  common->Printf( "Unwanted challenge response received.\n" );
1061  return;
1062  }
1063 
1064  serverChallenge = msg.ReadLong();
1065  serverId = msg.ReadShort();
1066  msg.ReadString( serverGameBase, MAX_STRING_CHARS );
1067  msg.ReadString( serverGame, MAX_STRING_CHARS );
1068 
1069  // the server is running a different game... we need to reload in the correct fs_game
1070  // even pure pak checks would fail if we didn't, as there are files we may not even see atm
1071  // NOTE: we could read the pure list from the server at the same time and set it up for the restart
1072  // ( if the client can restart directly with the right pak order, then we avoid an extra reloadEngine later.. )
1073  if ( idStr::Icmp( cvarSystem->GetCVarString( "fs_game_base" ), serverGameBase ) ||
1074  idStr::Icmp( cvarSystem->GetCVarString( "fs_game" ), serverGame ) ) {
1075  // bug #189 - if the server is running ROE and ROE is not locally installed, refuse to connect or we might crash
1076  if ( !fileSystem->HasD3XP() && ( !idStr::Icmp( serverGameBase, "d3xp" ) || !idStr::Icmp( serverGame, "d3xp" ) ) ) {
1077  common->Printf( "The server is running Doom3: Resurrection of Evil expansion pack. RoE is not installed on this client. Aborting the connection..\n" );
1078  cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "disconnect\n" );
1079  return;
1080  }
1081  common->Printf( "The server is running a different mod (%s-%s). Restarting..\n", serverGameBase, serverGame );
1082  cvarSystem->SetCVarString( "fs_game_base", serverGameBase );
1083  cvarSystem->SetCVarString( "fs_game", serverGame );
1084  cmdSystem->BufferCommandText( CMD_EXEC_NOW, "reloadEngine" );
1085  cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "reconnect\n" );
1086  return;
1087  }
1088 
1089  common->Printf( "received challenge response 0x%x from %s\n", serverChallenge, Sys_NetAdrToString( from ) );
1090 
1091  // start sending connect packets instead of challenge request packets
1093  lastConnectTime = -9999;
1094 
1095  // take this address as the new server address. This allows
1096  // a server proxy to hand off connections to multiple servers
1097  serverAddress = from;
1098 }
1099 
1100 /*
1101 ==================
1102 idAsyncClient::ProcessConnectResponseMessage
1103 ==================
1104 */
1106  int serverGameInitId, serverGameFrame, serverGameTime;
1107  idDict serverSI;
1108 
1109  if ( clientState >= CS_CONNECTED ) {
1110  common->Printf( "Duplicate connect received.\n" );
1111  return;
1112  }
1113  if ( clientState != CS_CONNECTING ) {
1114  common->Printf( "Connect response packet while not connecting.\n" );
1115  return;
1116  }
1117  if ( !Sys_CompareNetAdrBase( from, serverAddress ) ) {
1118  common->Printf( "Connect response from a different server.\n" );
1119  common->Printf( "%s should have been %s\n", Sys_NetAdrToString( from ), Sys_NetAdrToString( serverAddress ) );
1120  return;
1121  }
1122 
1123  common->Printf( "received connect response from %s\n", Sys_NetAdrToString( from ) );
1124 
1125  channel.Init( from, clientId );
1126  clientNum = msg.ReadLong();
1128  lastPacketTime = -9999;
1129 
1130  serverGameInitId = msg.ReadLong();
1131  serverGameFrame = msg.ReadLong();
1132  serverGameTime = msg.ReadLong();
1133  msg.ReadDeltaDict( serverSI, NULL );
1134 
1135  InitGame( serverGameInitId, serverGameFrame, serverGameTime, serverSI );
1136 
1137  // load map
1138  session->SetGUI( NULL, NULL );
1140 
1142 }
1143 
1144 /*
1145 ==================
1146 idAsyncClient::ProcessDisconnectMessage
1147 ==================
1148 */
1150  if ( clientState == CS_DISCONNECTED ) {
1151  common->Printf( "Disconnect packet while not connected.\n" );
1152  return;
1153  }
1154  if ( !Sys_CompareNetAdrBase( from, serverAddress ) ) {
1155  common->Printf( "Disconnect packet from unknown server.\n" );
1156  return;
1157  }
1158  session->Stop();
1159  session->MessageBox( MSG_OK, common->GetLanguageDict()->GetString ( "#str_04320" ), NULL, true );
1160  session->StartMenu();
1161 }
1162 
1163 /*
1164 ==================
1165 idAsyncClient::ProcessInfoResponseMessage
1166 ==================
1167 */
1169  int i, protocol, index;
1170  networkServer_t serverInfo;
1171  bool verbose = false;
1172 
1173  if ( from.type == NA_LOOPBACK || cvarSystem->GetCVarBool( "developer" ) ) {
1174  verbose = true;
1175  }
1176 
1177  serverInfo.clients = 0;
1178  serverInfo.adr = from;
1179  serverInfo.challenge = msg.ReadLong(); // challenge
1180  protocol = msg.ReadLong();
1181  if ( protocol != ASYNC_PROTOCOL_VERSION ) {
1182  common->Printf( "server %s ignored - protocol %d.%d, expected %d.%d\n", Sys_NetAdrToString( serverInfo.adr ), protocol >> 16, protocol & 0xffff, ASYNC_PROTOCOL_MAJOR, ASYNC_PROTOCOL_MINOR );
1183  return;
1184  }
1185  msg.ReadDeltaDict( serverInfo.serverInfo, NULL );
1186 
1187  if ( verbose ) {
1188  common->Printf( "server IP = %s\n", Sys_NetAdrToString( serverInfo.adr ) );
1189  serverInfo.serverInfo.Print();
1190  }
1191  for ( i = msg.ReadByte(); i < MAX_ASYNC_CLIENTS; i = msg.ReadByte() ) {
1192  serverInfo.pings[ serverInfo.clients ] = msg.ReadShort();
1193  serverInfo.rate[ serverInfo.clients ] = msg.ReadLong();
1194  msg.ReadString( serverInfo.nickname[ serverInfo.clients ], MAX_NICKLEN );
1195  if ( verbose ) {
1196  common->Printf( "client %2d: %s, ping = %d, rate = %d\n", i, serverInfo.nickname[ serverInfo.clients ], serverInfo.pings[ serverInfo.clients ], serverInfo.rate[ serverInfo.clients ] );
1197  }
1198  serverInfo.clients++;
1199  }
1200  serverInfo.OSMask = msg.ReadLong();
1201  index = serverList.InfoResponse( serverInfo );
1202 
1203  common->Printf( "%d: server %s - protocol %d.%d - %s\n", index, Sys_NetAdrToString( serverInfo.adr ), protocol >> 16, protocol & 0xffff, serverInfo.serverInfo.GetString( "si_name" ) );
1204 }
1205 
1206 /*
1207 ==================
1208 idAsyncClient::ProcessPrintMessage
1209 ==================
1210 */
1211 void idAsyncClient::ProcessPrintMessage( const netadr_t from, const idBitMsg &msg ) {
1212  char string[ MAX_STRING_CHARS ];
1213  int opcode;
1214  int game_opcode = ALLOW_YES;
1215  const char *retpass;
1216 
1217  opcode = msg.ReadLong();
1218  if ( opcode == SERVER_PRINT_GAMEDENY ) {
1219  game_opcode = msg.ReadLong();
1220  }
1222  common->Printf( "%s\n", string );
1223  guiNetMenu->SetStateString( "status", string );
1224  if ( opcode == SERVER_PRINT_GAMEDENY ) {
1225  if ( game_opcode == ALLOW_BADPASS ) {
1226  retpass = session->MessageBox( MSG_PROMPT, common->GetLanguageDict()->GetString ( "#str_04321" ), string, true, "passprompt_ok" );
1228  guiNetMenu->SetStateString( "status", common->GetLanguageDict()->GetString ( "#str_04322" ));
1229  if ( retpass ) {
1230  // #790
1231  cvarSystem->SetCVarString( "password", "" );
1232  cvarSystem->SetCVarString( "password", retpass );
1233  } else {
1234  cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
1235  }
1236  } else if ( game_opcode == ALLOW_NO ) {
1237  session->MessageBox( MSG_OK, string, common->GetLanguageDict()->GetString ( "#str_04323" ), true );
1239  cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
1240  }
1241  // ALLOW_NOTYET just keeps running as usual. The GUI has an abort button
1242  } else if ( opcode == SERVER_PRINT_BADCHALLENGE && clientState >= CS_CONNECTING ) {
1243  cmdSystem->BufferCommandText( CMD_EXEC_NOW, "reconnect" );
1244  }
1245 }
1246 
1247 /*
1248 ==================
1249 idAsyncClient::ProcessServersListMessage
1250 ==================
1251 */
1254  common->DPrintf( "received a server list from %s - not a valid master\n", Sys_NetAdrToString( from ) );
1255  return;
1256  }
1257  while ( msg.GetRemaingData() ) {
1258  int a,b,c,d;
1259  a = msg.ReadByte(); b = msg.ReadByte(); c = msg.ReadByte(); d = msg.ReadByte();
1260  serverList.AddServer( serverList.Num(), va( "%i.%i.%i.%i:%i", a, b, c, d, msg.ReadShort() ) );
1261  }
1262 }
1263 
1264 /*
1265 ==================
1266 idAsyncClient::ProcessAuthKeyMessage
1267 ==================
1268 */
1270  authKeyMsg_t authMsg;
1271  char read_string[ MAX_STRING_CHARS ];
1272  const char *retkey;
1273  authBadKeyStatus_t authBadStatus;
1274  int key_index;
1275  bool valid[ 2 ];
1276  idStr auth_msg;
1277 
1279  common->Printf( "clientState != CS_CONNECTING, not waiting for game auth, authKey ignored\n" );
1280  return;
1281  }
1282 
1283  authMsg = (authKeyMsg_t)msg.ReadByte();
1284  if ( authMsg == AUTHKEY_BADKEY ) {
1285  valid[ 0 ] = valid[ 1 ] = true;
1286  key_index = 0;
1287  authBadStatus = (authBadKeyStatus_t)msg.ReadByte();
1288  switch ( authBadStatus ) {
1289  case AUTHKEY_BAD_INVALID:
1290  valid[ 0 ] = ( msg.ReadByte() == 1 );
1291  valid[ 1 ] = ( msg.ReadByte() == 1 );
1292  idAsyncNetwork::BuildInvalidKeyMsg( auth_msg, valid );
1293  break;
1294  case AUTHKEY_BAD_BANNED:
1295  key_index = msg.ReadByte();
1296  auth_msg = common->GetLanguageDict()->GetString( va( "#str_0719%1d", 6 + key_index ) );
1297  auth_msg += "\n";
1298  auth_msg += common->GetLanguageDict()->GetString( "#str_04304" );
1299  valid[ key_index ] = false;
1300  break;
1301  case AUTHKEY_BAD_INUSE:
1302  key_index = msg.ReadByte();
1303  auth_msg = common->GetLanguageDict()->GetString( va( "#str_0719%1d", 8 + key_index ) );
1304  auth_msg += "\n";
1305  auth_msg += common->GetLanguageDict()->GetString( "#str_04304" );
1306  valid[ key_index ] = false;
1307  break;
1308  case AUTHKEY_BAD_MSG:
1309  // a general message explaining why this key is denied
1310  // no specific use for this atm. let's not clear the keys either
1311  msg.ReadString( read_string, MAX_STRING_CHARS );
1312  auth_msg = read_string;
1313  break;
1314  }
1315  common->DPrintf( "auth deny: %s\n", auth_msg.c_str() );
1316 
1317  // keys to be cleared. applies to both net connect and game auth
1318  session->ClearCDKey( valid );
1319 
1320  // get rid of the bad key - at least that's gonna annoy people who stole a fake key
1321  if ( clientState == CS_CONNECTING ) {
1322  while ( 1 ) {
1323  // here we use the auth status message
1324  retkey = session->MessageBox( MSG_CDKEY, auth_msg, common->GetLanguageDict()->GetString( "#str_04325" ), true );
1325  if ( retkey ) {
1326  if ( session->CheckKey( retkey, true, valid ) ) {
1327  cmdSystem->BufferCommandText( CMD_EXEC_NOW, "reconnect" );
1328  } else {
1329  // build a more precise message about the offline check failure
1330  idAsyncNetwork::BuildInvalidKeyMsg( auth_msg, valid );
1331  session->MessageBox( MSG_OK, auth_msg.c_str(), common->GetLanguageDict()->GetString( "#str_04327" ), true );
1332  continue;
1333  }
1334  } else {
1335  cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
1336  }
1337  break;
1338  }
1339  } else {
1340  // forward the auth status information to the session code
1341  session->CDKeysAuthReply( false, auth_msg );
1342  }
1343  } else {
1344  msg.ReadString( read_string, MAX_STRING_CHARS );
1345  cvarSystem->SetCVarString( "com_guid", read_string );
1346  common->Printf( "guid set to %s\n", read_string );
1347  session->CDKeysAuthReply( true, NULL );
1348  }
1349 }
1350 
1351 /*
1352 ==================
1353 idAsyncClient::ProcessVersionMessage
1354 ==================
1355 */
1357  char string[ MAX_STRING_CHARS ];
1358 
1359  if ( updateState != UPDATE_SENT ) {
1360  common->Printf( "ProcessVersionMessage: version reply, != UPDATE_SENT\n" );
1361  return;
1362  }
1363 
1364  common->Printf( "A new version is available\n" );
1365  msg.ReadString( string, MAX_STRING_CHARS );
1366  updateMSG = string;
1367  updateDirectDownload = ( msg.ReadByte() != 0 );
1368  msg.ReadString( string, MAX_STRING_CHARS );
1369  updateURL = string;
1370  updateMime = (dlMime_t)msg.ReadByte();
1371  msg.ReadString( string, MAX_STRING_CHARS );
1374 }
1375 
1376 /*
1377 ==================
1378 idAsyncClient::ValidatePureServerChecksums
1379 ==================
1380 */
1382  int i, numChecksums, numMissingChecksums;
1383  int inChecksums[ MAX_PURE_PAKS ];
1384  int inGamePakChecksum;
1385  int missingChecksums[ MAX_PURE_PAKS ];
1386  int missingGamePakChecksum;
1387  idBitMsg dlmsg;
1388  byte msgBuf[MAX_MESSAGE_SIZE];
1389 
1390  // read checksums
1391  // pak checksums, in a 0-terminated list
1392  numChecksums = 0;
1393  do {
1394  i = msg.ReadLong( );
1395  inChecksums[ numChecksums++ ] = i;
1396  // just to make sure a broken message doesn't crash us
1397  if ( numChecksums >= MAX_PURE_PAKS ) {
1398  common->Warning( "MAX_PURE_PAKS ( %d ) exceeded in idAsyncClient::ProcessPureMessage\n", MAX_PURE_PAKS );
1399  return false;
1400  }
1401  } while ( i );
1402  inChecksums[ numChecksums ] = 0;
1403  inGamePakChecksum = msg.ReadLong();
1404 
1405  fsPureReply_t reply = fileSystem->SetPureServerChecksums( inChecksums, inGamePakChecksum, missingChecksums, &missingGamePakChecksum );
1406  switch ( reply ) {
1407  case PURE_RESTART:
1408  // need to restart the filesystem with a different pure configuration
1409  cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
1410  // restart with the right FS configuration and get back to the server
1412  fileSystem->SetRestartChecksums( inChecksums, inGamePakChecksum );
1413  cmdSystem->BufferCommandText( CMD_EXEC_NOW, "reloadEngine" );
1414  return false;
1415  case PURE_MISSING: {
1416 
1417  idStr checksums;
1418 
1419  i = 0;
1420  while ( missingChecksums[ i ] ) {
1421  checksums += va( "0x%x ", missingChecksums[ i++ ] );
1422  }
1423  numMissingChecksums = i;
1424 
1425  if ( idAsyncNetwork::clientDownload.GetInteger() == 0 ) {
1426  // never any downloads
1427  idStr message = va( common->GetLanguageDict()->GetString( "#str_07210" ), Sys_NetAdrToString( from ) );
1428 
1429  if ( numMissingChecksums > 0 ) {
1430  message += va( common->GetLanguageDict()->GetString( "#str_06751" ), numMissingChecksums, checksums.c_str() );
1431  }
1432  if ( missingGamePakChecksum ) {
1433  message += va( common->GetLanguageDict()->GetString( "#str_06750" ), missingGamePakChecksum );
1434  }
1435 
1436  common->Printf( message );
1437  cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
1438  session->MessageBox( MSG_OK, message, common->GetLanguageDict()->GetString( "#str_06735" ), true );
1439  } else {
1440  if ( clientState >= CS_CONNECTED ) {
1441  // we are already connected, reconnect to negociate the paks in connectionless mode
1442  cmdSystem->BufferCommandText( CMD_EXEC_NOW, "reconnect" );
1443  return false;
1444  }
1445  // ask the server to send back download info
1446  common->DPrintf( "missing %d paks: %s\n", numMissingChecksums + ( missingGamePakChecksum ? 1 : 0 ), checksums.c_str() );
1447  if ( missingGamePakChecksum ) {
1448  common->DPrintf( "game code pak: 0x%x\n", missingGamePakChecksum );
1449  }
1450  // store the requested downloads
1451  GetDownloadRequest( missingChecksums, numMissingChecksums, missingGamePakChecksum );
1452  // build the download request message
1453  // NOTE: in a specific function?
1454  dlmsg.Init( msgBuf, sizeof( msgBuf ) );
1456  dlmsg.WriteString( "downloadRequest" );
1457  dlmsg.WriteLong( serverChallenge );
1458  dlmsg.WriteShort( clientId );
1459  // used to make sure the server replies to the same download request
1460  dlmsg.WriteLong( dlRequest );
1461  // special case the code pak - if we have a 0 checksum then we don't need to download it
1462  dlmsg.WriteLong( missingGamePakChecksum );
1463  // 0-terminated list of missing paks
1464  i = 0;
1465  while ( missingChecksums[ i ] ) {
1466  dlmsg.WriteLong( missingChecksums[ i++ ] );
1467  }
1468  dlmsg.WriteLong( 0 );
1469  clientPort.SendPacket( from, dlmsg.GetData(), dlmsg.GetSize() );
1470  }
1471 
1472  return false;
1473  }
1474  case PURE_NODLL:
1475  common->Printf( common->GetLanguageDict()->GetString( "#str_07211" ), Sys_NetAdrToString( from ) );
1476  cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
1477  return false;
1478  default:
1479  return true;
1480  }
1481  return true;
1482 }
1483 
1484 /*
1485 ==================
1486 idAsyncClient::ProcessPureMessage
1487 ==================
1488 */
1489 void idAsyncClient::ProcessPureMessage( const netadr_t from, const idBitMsg &msg ) {
1490  idBitMsg outMsg;
1491  byte msgBuf[ MAX_MESSAGE_SIZE ];
1492  int i;
1493  int inChecksums[ MAX_PURE_PAKS ];
1494  int gamePakChecksum;
1495 
1496  if ( clientState != CS_CONNECTING ) {
1497  common->Printf( "clientState != CS_CONNECTING, pure msg ignored\n" );
1498  return;
1499  }
1500 
1501  if ( !ValidatePureServerChecksums( from, msg ) ) {
1502  return;
1503  }
1504 
1505  fileSystem->GetPureServerChecksums( inChecksums, -1, &gamePakChecksum );
1506  outMsg.Init( msgBuf, sizeof( msgBuf ) );
1508  outMsg.WriteString( "pureClient" );
1509  outMsg.WriteLong( serverChallenge );
1510  outMsg.WriteShort( clientId );
1511  i = 0;
1512  while ( inChecksums[ i ] ) {
1513  outMsg.WriteLong( inChecksums[ i++ ] );
1514  }
1515  outMsg.WriteLong( 0 );
1516  outMsg.WriteLong( gamePakChecksum );
1517  clientPort.SendPacket( from, outMsg.GetData(), outMsg.GetSize() );
1518 }
1519 
1520 /*
1521 ==================
1522 idAsyncClient::ConnectionlessMessage
1523 ==================
1524 */
1526  char string[MAX_STRING_CHARS*2]; // M. Quinn - Even Balance - PB packets can go beyond 1024
1527 
1528  msg.ReadString( string, sizeof( string ) );
1529 
1530  // info response from a server, are accepted from any source
1531  if ( idStr::Icmp( string, "infoResponse" ) == 0 ) {
1532  ProcessInfoResponseMessage( from, msg );
1533  return;
1534  }
1535 
1536  // from master server:
1538  // server list
1539  if ( idStr::Icmp( string, "servers" ) == 0 ) {
1540  ProcessServersListMessage( from, msg );
1541  return;
1542  }
1543 
1544  if ( idStr::Icmp( string, "authKey" ) == 0 ) {
1545  ProcessAuthKeyMessage( from, msg );
1546  return;
1547  }
1548 
1549  if ( idStr::Icmp( string, "newVersion" ) == 0 ) {
1550  ProcessVersionMessage( from, msg );
1551  return;
1552  }
1553  }
1554 
1555  // ignore if not from the current/last server
1556  if ( !Sys_CompareNetAdrBase( from, serverAddress ) && ( lastRconTime + 10000 < realTime || !Sys_CompareNetAdrBase( from, lastRconAddress ) ) ) {
1557  common->DPrintf( "got message '%s' from bad source: %s\n", string, Sys_NetAdrToString( from ) );
1558  return;
1559  }
1560 
1561  // challenge response from the server we are connecting to
1562  if ( idStr::Icmp( string, "challengeResponse" ) == 0 ) {
1563  ProcessChallengeResponseMessage( from, msg );
1564  return;
1565  }
1566 
1567  // connect response from the server we are connecting to
1568  if ( idStr::Icmp( string, "connectResponse" ) == 0 ) {
1569  ProcessConnectResponseMessage( from, msg );
1570  return;
1571  }
1572 
1573  // a disconnect message from the server, which will happen if the server
1574  // dropped the connection but is still getting packets from this client
1575  if ( idStr::Icmp( string, "disconnect" ) == 0 ) {
1576  ProcessDisconnectMessage( from, msg );
1577  return;
1578  }
1579 
1580  // print request from server
1581  if ( idStr::Icmp( string, "print" ) == 0 ) {
1582  ProcessPrintMessage( from, msg );
1583  return;
1584  }
1585 
1586  // server pure list
1587  if ( idStr::Icmp( string, "pureServer" ) == 0 ) {
1588  ProcessPureMessage( from, msg );
1589  return;
1590  }
1591 
1592  if ( idStr::Icmp( string, "downloadInfo" ) == 0 ) {
1593  ProcessDownloadInfoMessage( from, msg );
1594  }
1595 
1596  if ( idStr::Icmp( string, "authrequired" ) == 0 ) {
1597  // server telling us that he's expecting an auth mode connect, just in case we're trying to connect in LAN mode
1598  if ( idAsyncNetwork::LANServer.GetBool() ) {
1599  common->Warning( "server %s requests master authorization for this client. Turning off LAN mode\n", Sys_NetAdrToString( from ) );
1601  }
1602  }
1603 
1604  common->DPrintf( "ignored message from %s: %s\n", Sys_NetAdrToString( from ), string );
1605 }
1606 
1607 /*
1608 =================
1609 idAsyncClient::ProcessMessage
1610 =================
1611 */
1613  int id;
1614 
1615  id = msg.ReadShort();
1616 
1617  // check for a connectionless packet
1618  if ( id == CONNECTIONLESS_MESSAGE_ID ) {
1619  ConnectionlessMessage( from, msg );
1620  return;
1621  }
1622 
1623  if ( clientState < CS_CONNECTED ) {
1624  return; // can't be a valid sequenced packet
1625  }
1626 
1627  if ( msg.GetRemaingData() < 4 ) {
1628  common->DPrintf( "%s: tiny packet\n", Sys_NetAdrToString( from ) );
1629  return;
1630  }
1631 
1632  // is this a packet from the server
1633  if ( !Sys_CompareNetAdrBase( from, channel.GetRemoteAddress() ) || id != serverId ) {
1634  common->DPrintf( "%s: sequenced server packet without connection\n", Sys_NetAdrToString( from ) );
1635  return;
1636  }
1637 
1638  if ( !channel.Process( from, clientTime, msg, serverMessageSequence ) ) {
1639  return; // out of order, duplicated, fragment, etc.
1640  }
1641 
1645 }
1646 
1647 /*
1648 ==================
1649 idAsyncClient::SetupConnection
1650 ==================
1651 */
1653  idBitMsg msg;
1654  byte msgBuf[MAX_MESSAGE_SIZE];
1655 
1657  return;
1658  }
1659 
1660  if ( clientState == CS_CHALLENGING ) {
1661  common->Printf( "sending challenge to %s\n", Sys_NetAdrToString( serverAddress ) );
1662  msg.Init( msgBuf, sizeof( msgBuf ) );
1664  msg.WriteString( "challenge" );
1665  msg.WriteLong( clientId );
1667  } else if ( clientState == CS_CONNECTING ) {
1668  common->Printf( "sending connect to %s with challenge 0x%x\n", Sys_NetAdrToString( serverAddress ), serverChallenge );
1669  msg.Init( msgBuf, sizeof( msgBuf ) );
1671  msg.WriteString( "connect" );
1673 #if ID_FAKE_PURE
1674  // fake win32 OS - might need to adapt depending on the case
1675  msg.WriteShort( 0 );
1676 #else
1677  msg.WriteShort( BUILD_OS_ID );
1678 #endif
1680  msg.WriteLong( serverChallenge );
1681  msg.WriteShort( clientId );
1682  msg.WriteLong( cvarSystem->GetCVarInteger( "net_clientMaxRate" ) );
1683  msg.WriteString( cvarSystem->GetCVarString( "com_guid" ) );
1684  msg.WriteString( cvarSystem->GetCVarString( "password" ), -1, false );
1685  // do not make the protocol depend on PB
1686  msg.WriteShort( 0 );
1688 
1689  if ( idAsyncNetwork::LANServer.GetBool() ) {
1690  common->Printf( "net_LANServer is set, connecting in LAN mode\n" );
1691  } else {
1692  // emit a cd key authorization request
1693  // modified at protocol 1.37 for XP key addition
1694  msg.BeginWriting();
1696  msg.WriteString( "clAuth" );
1698  msg.WriteNetadr( serverAddress );
1699  // if we don't have a com_guid, this will request a direct reply from auth with it
1700  msg.WriteByte( cvarSystem->GetCVarString( "com_guid" )[0] ? 1 : 0 );
1701  // send the main key, and flag an extra byte to add XP key
1702  msg.WriteString( session->GetCDKey( false ) );
1703  const char *xpkey = session->GetCDKey( true );
1704  msg.WriteByte( xpkey ? 1 : 0 );
1705  if ( xpkey ) {
1706  msg.WriteString( xpkey );
1707  }
1709  }
1710  } else {
1711  return;
1712  }
1713 
1715 }
1716 
1717 /*
1718 ==================
1719 idAsyncClient::SendReliableGameMessage
1720 ==================
1721 */
1723  idBitMsg outMsg;
1724  byte msgBuf[MAX_MESSAGE_SIZE];
1725 
1726  if ( clientState < CS_INGAME ) {
1727  return;
1728  }
1729 
1730  outMsg.Init( msgBuf, sizeof( msgBuf ) );
1732  outMsg.WriteData( msg.GetData(), msg.GetSize() );
1733  if ( !channel.SendReliableMessage( outMsg ) ) {
1734  common->Error( "client->server reliable messages overflow\n" );
1735  }
1736 }
1737 
1738 /*
1739 ==================
1740 idAsyncClient::Idle
1741 ==================
1742 */
1743 void idAsyncClient::Idle( void ) {
1744  // also need to read mouse for the connecting guis
1746 
1748 }
1749 
1750 /*
1751 ==================
1752 idAsyncClient::UpdateTime
1753 ==================
1754 */
1755 int idAsyncClient::UpdateTime( int clamp ) {
1756  int time, msec;
1757 
1758  time = Sys_Milliseconds();
1759  msec = idMath::ClampInt( 0, clamp, time - realTime );
1760  realTime = time;
1761  clientTime += msec;
1762  return msec;
1763 }
1764 
1765 /*
1766 ==================
1767 idAsyncClient::RunFrame
1768 ==================
1769 */
1771  int msec, size;
1772  bool newPacket;
1773  idBitMsg msg;
1774  byte msgBuf[MAX_MESSAGE_SIZE];
1775  netadr_t from;
1776 
1777  msec = UpdateTime( 100 );
1778 
1779  if ( !clientPort.GetPort() ) {
1780  return;
1781  }
1782 
1783  // handle ongoing pk4 downloads and patch downloads
1784  HandleDownloads();
1785 
1786  gameTimeResidual += msec;
1787 
1788  // spin in place processing incoming packets until enough time lapsed to run a new game frame
1789  do {
1790 
1791  do {
1792 
1793  // blocking read with game time residual timeout
1794  newPacket = clientPort.GetPacketBlocking( from, msgBuf, size, sizeof( msgBuf ), USERCMD_MSEC - ( gameTimeResidual + clientPredictTime ) - 1 );
1795  if ( newPacket ) {
1796  msg.Init( msgBuf, sizeof( msgBuf ) );
1797  msg.SetSize( size );
1798  msg.BeginReading();
1799  ProcessMessage( from, msg );
1800  }
1801 
1802  msec = UpdateTime( 100 );
1803  gameTimeResidual += msec;
1804 
1805  } while( newPacket );
1806 
1808 
1809  // update server list
1810  serverList.RunFrame();
1811 
1812  if ( clientState == CS_DISCONNECTED ) {
1815  clientPredictTime = 0;
1816  return;
1817  }
1818 
1819  if ( clientState == CS_PURERESTART ) {
1821  Reconnect();
1823  clientPredictTime = 0;
1824  return;
1825  }
1826 
1827  // if not connected setup a connection
1828  if ( clientState < CS_CONNECTED ) {
1829  // also need to read mouse for the connecting guis
1831  SetupConnection();
1833  clientPredictTime = 0;
1834  return;
1835  }
1836 
1837  if ( CheckTimeout() ) {
1838  return;
1839  }
1840 
1841  // if not yet in the game send empty messages to keep data flowing through the channel
1842  if ( clientState < CS_INGAME ) {
1843  Idle();
1844  gameTimeResidual = 0;
1845  return;
1846  }
1847 
1848  // check for user info changes
1850  game->ThrottleUserInfo( );
1854  }
1855 
1857  lastFrameDelta = 0;
1858  }
1859 
1860  // generate user commands for the predicted time
1862 
1863  // send the user commands of this client to the server
1865 
1866  // update time
1867  gameFrame++;
1870 
1871  // run from the snapshot up to the local game frame
1872  while ( snapshotGameFrame < gameFrame ) {
1873 
1874  lastFrameDelta++;
1875 
1876  // duplicate usercmds for clients if no new ones are available
1878 
1879  // indicate the last prediction frame before a render
1880  bool lastPredictFrame = ( snapshotGameFrame + 1 >= gameFrame && gameTimeResidual + clientPredictTime < USERCMD_MSEC );
1881 
1882  // run client prediction
1883  gameReturn_t ret = game->ClientPrediction( clientNum, userCmds[ snapshotGameFrame & ( MAX_USERCMD_BACKUP - 1 ) ], lastPredictFrame );
1884 
1886 
1889  }
1890  }
1891 }
1892 
1893 /*
1894 ==================
1895 idAsyncClient::PacifierUpdate
1896 ==================
1897 */
1899  if ( !IsActive() ) {
1900  return;
1901  }
1903  SendEmptyToServer( false, true );
1904 }
1905 
1906 /*
1907 ==================
1908 idAsyncClient::SendVersionCheck
1909 ==================
1910 */
1911 void idAsyncClient::SendVersionCheck( bool fromMenu ) {
1912  idBitMsg msg;
1913  byte msgBuf[MAX_MESSAGE_SIZE];
1914 
1915  if ( updateState != UPDATE_NONE && !fromMenu ) {
1916  common->DPrintf( "up-to-date check was already performed\n" );
1917  return;
1918  }
1919 
1920  InitPort();
1921  msg.Init( msgBuf, sizeof( msgBuf ) );
1923  msg.WriteString( "versionCheck" );
1925  msg.WriteShort( BUILD_OS_ID );
1926  msg.WriteString( cvarSystem->GetCVarString( "si_version" ) );
1927  msg.WriteString( cvarSystem->GetCVarString( "com_guid" ) );
1929 
1930  common->DPrintf( "sent a version check request\n" );
1931 
1934  showUpdateMessage = fromMenu;
1935 }
1936 
1937 /*
1938 ==================
1939 idAsyncClient::SendVersionDLUpdate
1940 
1941 sending those packets is not strictly necessary. just a way to tell the update server
1942 about what is going on. allows the update server to have a more precise view of the overall
1943 network load for the updates
1944 ==================
1945 */
1947  idBitMsg msg;
1948  byte msgBuf[MAX_MESSAGE_SIZE];
1949 
1950  msg.Init( msgBuf, sizeof( msgBuf ) );
1952  msg.WriteString( "versionDL" );
1954  msg.WriteShort( state );
1956 }
1957 
1958 /*
1959 ==================
1960 idAsyncClient::HandleDownloads
1961 ==================
1962 */
1964 
1965  if ( updateState == UPDATE_SENT && clientTime > updateSentTime + 2000 ) {
1966  // timing out on no reply
1968  if ( showUpdateMessage ) {
1969  session->MessageBox( MSG_OK, common->GetLanguageDict()->GetString ( "#str_04839" ), common->GetLanguageDict()->GetString ( "#str_04837" ), true );
1970  showUpdateMessage = false;
1971  }
1972  common->DPrintf( "No update available\n" );
1973  } else if ( backgroundDownload.completed ) {
1974  // only enter these if the download slot is free
1975  if ( updateState == UPDATE_READY ) {
1976  //
1977  if ( session->MessageBox( MSG_YESNO, updateMSG, common->GetLanguageDict()->GetString ( "#str_04330" ), true, "yes" )[0] ) {
1978  if ( !updateDirectDownload ) {
1979  sys->OpenURL( updateURL, true );
1981  } else {
1982 
1983  // we're just creating the file at toplevel inside fs_savepath
1986  dltotal = 0;
1987  dlnow = 0;
1988 
1989  backgroundDownload.completed = false;
1997 
1999  SendVersionDLUpdate( 0 );
2000  session->DownloadProgressBox( &backgroundDownload, va( "Downloading %s\n", updateFile.c_str() ) );
2002  if ( backgroundDownload.url.status == DL_DONE ) {
2003  SendVersionDLUpdate( 1 );
2004  idStr fullPath = f->GetFullPath();
2005  fileSystem->CloseFile( f );
2006  if ( session->MessageBox( MSG_YESNO, common->GetLanguageDict()->GetString ( "#str_04331" ), common->GetLanguageDict()->GetString ( "#str_04332" ), true, "yes" )[0] ) {
2007  if ( updateMime == FILE_EXEC ) {
2008  sys->StartProcess( fullPath, true );
2009  } else {
2010  sys->OpenURL( va( "file://%s", fullPath.c_str() ), true );
2011  }
2012  } else {
2013  session->MessageBox( MSG_OK, va( common->GetLanguageDict()->GetString ( "#str_04333" ), fullPath.c_str() ), common->GetLanguageDict()->GetString ( "#str_04334" ), true );
2014  }
2015  } else {
2016  if ( backgroundDownload.url.dlerror[ 0 ] ) {
2017  common->Warning( "update download failed. curl error: %s", backgroundDownload.url.dlerror );
2018  }
2019  SendVersionDLUpdate( 2 );
2020  idStr name = f->GetName();
2021  fileSystem->CloseFile( f );
2022  fileSystem->RemoveFile( name );
2023  session->MessageBox( MSG_OK, common->GetLanguageDict()->GetString ( "#str_04335" ), common->GetLanguageDict()->GetString ( "#str_04336" ), true );
2024  if ( updateFallback.Length() ) {
2025  sys->OpenURL( updateFallback.c_str(), true );
2026  } else {
2027  common->Printf( "no fallback URL\n" );
2028  }
2029  }
2030  }
2031  } else {
2033  }
2034  } else if ( dlList.Num() ) {
2035 
2036  int numPaks = dlList.Num();
2037  int pakCount = 1;
2038  int progress_start, progress_end;
2039  currentDlSize = 0;
2040 
2041  do {
2042 
2043  if ( dlList[ 0 ].url[ 0 ] == '\0' ) {
2044  // ignore empty files
2045  dlList.RemoveIndex( 0 );
2046  continue;
2047  }
2048  common->Printf( "start download for %s\n", dlList[ 0 ].url.c_str() );
2049 
2051  if ( !f ) {
2052  common->Warning( "could not create temporary file" );
2053  dlList.Clear();
2054  return;
2055  }
2056 
2057  backgroundDownload.completed = false;
2063  backgroundDownload.url.url = dlList[ 0 ].url;
2065  idStr dltitle;
2066  // "Downloading %s"
2067  sprintf( dltitle, common->GetLanguageDict()->GetString( "#str_07213" ), dlList[ 0 ].filename.c_str() );
2068  if ( numPaks > 1 ) {
2069  dltitle += va( " (%d/%d)", pakCount, numPaks );
2070  }
2071  if ( totalDlSize ) {
2072  progress_start = (int)( (float)currentDlSize * 100.0f / (float)totalDlSize );
2073  progress_end = (int)( (float)( currentDlSize + dlList[ 0 ].size ) * 100.0f / (float)totalDlSize );
2074  } else {
2075  progress_start = 0;
2076  progress_end = 100;
2077  }
2078  session->DownloadProgressBox( &backgroundDownload, dltitle, progress_start, progress_end );
2079  if ( backgroundDownload.url.status == DL_DONE ) {
2080  idFile *saveas;
2081  const int CHUNK_SIZE = 1024 * 1024;
2082  byte *buf;
2083  int remainlen;
2084  int readlen;
2085  int retlen;
2086  int checksum;
2087 
2088  common->Printf( "file downloaded\n" );
2089  idStr finalPath = cvarSystem->GetCVarString( "fs_savepath" );
2090  finalPath.AppendPath( dlList[ 0 ].filename );
2091  fileSystem->CreateOSPath( finalPath );
2092  // do the final copy ourselves so we do by small chunks in case the file is big
2093  saveas = fileSystem->OpenExplicitFileWrite( finalPath );
2094  buf = (byte*)Mem_Alloc( CHUNK_SIZE );
2095  f->Seek( 0, FS_SEEK_END );
2096  remainlen = f->Tell();
2097  f->Seek( 0, FS_SEEK_SET );
2098  while ( remainlen ) {
2099  readlen = Min( remainlen, CHUNK_SIZE );
2100  retlen = f->Read( buf, readlen );
2101  if ( retlen != readlen ) {
2102  common->FatalError( "short read %d of %d in idFileSystem::HandleDownload", retlen, readlen );
2103  }
2104  retlen = saveas->Write( buf, readlen );
2105  if ( retlen != readlen ) {
2106  common->FatalError( "short write %d of %d in idFileSystem::HandleDownload", retlen, readlen );
2107  }
2108  remainlen -= readlen;
2109  }
2110  fileSystem->CloseFile( f );
2111  fileSystem->CloseFile( saveas );
2112  common->Printf( "saved as %s\n", finalPath.c_str() );
2113  Mem_Free( buf );
2114 
2115  // add that file to our paks list
2116  checksum = fileSystem->AddZipFile( dlList[ 0 ].filename );
2117 
2118  // verify the checksum to be what the server says
2119  if ( !checksum || checksum != dlList[ 0 ].checksum ) {
2120  // "pak is corrupted ( checksum 0x%x, expected 0x%x )"
2121  session->MessageBox( MSG_OK, va( common->GetLanguageDict()->GetString( "#str_07214" ) , checksum, dlList[0].checksum ), "Download failed", true );
2122  fileSystem->RemoveFile( dlList[ 0 ].filename );
2123  dlList.Clear();
2124  return;
2125  }
2126 
2127  currentDlSize += dlList[ 0 ].size;
2128 
2129  } else {
2130  common->Warning( "download failed: %s", dlList[ 0 ].url.c_str() );
2131  if ( backgroundDownload.url.dlerror[ 0 ] ) {
2132  common->Warning( "curl error: %s", backgroundDownload.url.dlerror );
2133  }
2134  // "The download failed or was cancelled"
2135  // "Download failed"
2136  session->MessageBox( MSG_OK, common->GetLanguageDict()->GetString( "#str_07215" ), common->GetLanguageDict()->GetString( "#str_07216" ), true );
2137  dlList.Clear();
2138  return;
2139  }
2140 
2141  pakCount++;
2142  dlList.RemoveIndex( 0 );
2143  } while ( dlList.Num() );
2144 
2145  // all downloads successful - do the dew
2146  cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "reconnect\n" );
2147  }
2148  }
2149 }
2150 
2151 /*
2152 ===============
2153 idAsyncClient::SendAuthCheck
2154 ===============
2155 */
2156 bool idAsyncClient::SendAuthCheck( const char *cdkey, const char *xpkey ) {
2157  idBitMsg msg;
2158  byte msgBuf[MAX_MESSAGE_SIZE];
2159 
2160  msg.Init( msgBuf, sizeof( msgBuf ) );
2162  msg.WriteString( "gameAuth" );
2164  msg.WriteByte( cdkey ? 1 : 0 );
2165  msg.WriteString( cdkey ? cdkey : "" );
2166  msg.WriteByte( xpkey ? 1 : 0 );
2167  msg.WriteString( xpkey ? xpkey : "" );
2168  InitPort();
2170  return true;
2171 }
2172 
2173 /*
2174 ===============
2175 idAsyncClient::CheckTimeout
2176 ===============
2177 */
2179  if ( lastPacketTime > 0 && ( lastPacketTime + idAsyncNetwork::clientServerTimeout.GetInteger()*1000 < clientTime ) ) {
2180  session->StopBox();
2181  session->MessageBox( MSG_OK, common->GetLanguageDict()->GetString ( "#str_04328" ), common->GetLanguageDict()->GetString ( "#str_04329" ), true );
2182  cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
2183  return true;
2184  }
2185  return false;
2186 }
2187 
2188 /*
2189 ===============
2190 idAsyncClient::ProcessDownloadInfoMessage
2191 ===============
2192 */
2194  char buf[ MAX_STRING_CHARS ];
2195  int srvDlRequest = msg.ReadLong();
2196  int infoType = msg.ReadByte();
2197  int pakDl;
2198  int pakIndex;
2199 
2200  pakDlEntry_t entry;
2201  bool gotAllFiles = true;
2202  idStr sizeStr;
2203  bool gotGame = false;
2204 
2205  if ( dlRequest == -1 || srvDlRequest != dlRequest ) {
2206  common->Warning( "bad download id from server, ignored" );
2207  return;
2208  }
2209  // mark the dlRequest as dead now whatever how we process it
2210  dlRequest = -1;
2211 
2212  if ( infoType == SERVER_DL_REDIRECT ) {
2213  msg.ReadString( buf, MAX_STRING_CHARS );
2214  cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
2215  // "You are missing required pak files to connect to this server.\nThe server gave a web page though:\n%s\nDo you want to go there now?"
2216  // "Missing required files"
2217  if ( session->MessageBox( MSG_YESNO, va( common->GetLanguageDict()->GetString( "#str_07217" ), buf ),
2218  common->GetLanguageDict()->GetString( "#str_07218" ), true, "yes" )[ 0 ] ) {
2219  sys->OpenURL( buf, true );
2220  }
2221  } else if ( infoType == SERVER_DL_LIST ) {
2222  cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
2223  if ( dlList.Num() ) {
2224  common->Warning( "tried to process a download list while already busy downloading things" );
2225  return;
2226  }
2227  // read the URLs, check against what we requested, prompt for download
2228  pakIndex = -1;
2229  totalDlSize = 0;
2230  do {
2231  pakIndex++;
2232  pakDl = msg.ReadByte();
2233  if ( pakDl == SERVER_PAK_YES ) {
2234  if ( pakIndex == 0 ) {
2235  gotGame = true;
2236  }
2237  msg.ReadString( buf, MAX_STRING_CHARS );
2238  entry.filename = buf;
2239  msg.ReadString( buf, MAX_STRING_CHARS );
2240  entry.url = buf;
2241  entry.size = msg.ReadLong();
2242  // checksums are not transmitted, we read them from the dl request we sent
2243  entry.checksum = dlChecksums[ pakIndex ];
2244  totalDlSize += entry.size;
2245  dlList.Append( entry );
2246  common->Printf( "download %s from %s ( 0x%x )\n", entry.filename.c_str(), entry.url.c_str(), entry.checksum );
2247  } else if ( pakDl == SERVER_PAK_NO ) {
2248  msg.ReadString( buf, MAX_STRING_CHARS );
2249  entry.filename = buf;
2250  entry.url = "";
2251  entry.size = 0;
2252  entry.checksum = 0;
2253  dlList.Append( entry );
2254  // first pak is game pak, only fail it if we actually requested it
2255  if ( pakIndex != 0 || dlChecksums[ 0 ] != 0 ) {
2256  common->Printf( "no download offered for %s ( 0x%x )\n", entry.filename.c_str(), dlChecksums[ pakIndex ] );
2257  gotAllFiles = false;
2258  }
2259  } else {
2260  assert( pakDl == SERVER_PAK_END );
2261  }
2262  } while ( pakDl != SERVER_PAK_END );
2263  if ( dlList.Num() < dlCount ) {
2264  common->Printf( "%d files were ignored by the server\n", dlCount - dlList.Num() );
2265  gotAllFiles = false;
2266  }
2267  sizeStr.BestUnit( "%.2f", totalDlSize, MEASURE_SIZE );
2268  cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
2269  if ( totalDlSize == 0 ) {
2270  // was no downloadable stuff for us
2271  // "Can't connect to the pure server: no downloads offered"
2272  // "Missing required files"
2273  dlList.Clear();
2274  session->MessageBox( MSG_OK, common->GetLanguageDict()->GetString( "#str_07219" ), common->GetLanguageDict()->GetString( "#str_07218" ), true );
2275  return;
2276  }
2277  bool asked = false;
2278  if ( gotGame ) {
2279  asked = true;
2280  // "You need to download game code to connect to this server. Are you sure? You should only answer yes if you trust the server administrators."
2281  // "Missing game binaries"
2282  if ( !session->MessageBox( MSG_YESNO, common->GetLanguageDict()->GetString( "#str_07220" ), common->GetLanguageDict()->GetString( "#str_07221" ), true, "yes" )[ 0 ] ) {
2283  dlList.Clear();
2284  return;
2285  }
2286  }
2287  if ( !gotAllFiles ) {
2288  asked = true;
2289  // "The server only offers to download some of the files required to connect ( %s ). Download anyway?"
2290  // "Missing required files"
2291  if ( !session->MessageBox( MSG_YESNO, va( common->GetLanguageDict()->GetString( "#str_07222" ), sizeStr.c_str() ),
2292  common->GetLanguageDict()->GetString( "#str_07218" ), true, "yes" )[ 0 ] ) {
2293  dlList.Clear();
2294  return;
2295  }
2296  }
2297  if ( !asked && idAsyncNetwork::clientDownload.GetInteger() == 1 ) {
2298  // "You need to download some files to connect to this server ( %s ), proceed?"
2299  // "Missing required files"
2300  if ( !session->MessageBox( MSG_YESNO, va( common->GetLanguageDict()->GetString( "#str_07224" ), sizeStr.c_str() ),
2301  common->GetLanguageDict()->GetString( "#str_07218" ), true, "yes" )[ 0 ] ) {
2302  dlList.Clear();
2303  return;
2304  }
2305  }
2306  } else {
2307  cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
2308  // "You are missing some files to connect to this server, and the server doesn't provide downloads."
2309  // "Missing required files"
2310  session->MessageBox( MSG_OK, common->GetLanguageDict()->GetString( "#str_07223" ), common->GetLanguageDict()->GetString( "#str_07218" ), true );
2311  }
2312 }
2313 
2314 /*
2315 ===============
2316 idAsyncClient::GetDownloadRequest
2317 ===============
2318 */
2319 int idAsyncClient::GetDownloadRequest( const int checksums[ MAX_PURE_PAKS ], int count, int gamePakChecksum ) {
2320  assert( !checksums[ count ] ); // 0-terminated
2321  if ( memcmp( dlChecksums + 1, checksums, sizeof( int ) * count ) || gamePakChecksum != dlChecksums[ 0 ] ) {
2322  idRandom newreq;
2323 
2324  dlChecksums[ 0 ] = gamePakChecksum;
2325  memcpy( dlChecksums + 1, checksums, sizeof( int ) * MAX_PURE_PAKS );
2326 
2327  newreq.SetSeed( Sys_Milliseconds() );
2328  dlRequest = newreq.RandomInt();
2329  dlCount = count + ( gamePakChecksum ? 1 : 0 );
2330  return dlRequest;
2331  }
2332  // this is the same dlRequest, we haven't heard from the server. keep the same id
2333  return dlRequest;
2334 }
void ProcessAuthKeyMessage(const netadr_t from, const idBitMsg &msg)
int InfoResponse(networkServer_t &server)
Definition: ServerScan.cpp:125
void BeginWriting(void)
Definition: BitMsg.h:265
virtual const idDict * SetUserInfo(int clientNum, const idDict &userInfo, bool isClient, bool canModify)=0
const int MAX_ASYNC_CLIENTS
Definition: AsyncNetwork.h:44
netadr_t adr
Definition: ServerScan.h:51
static int snPrintf(char *dest, int size, const char *fmt,...) id_attribute((format(printf
Definition: Str.cpp:1465
void Shutdown(void)
Definition: AsyncClient.cpp:99
void Close()
Definition: posix_net.cpp:474
usercmd_t userCmds[MAX_USERCMD_BACKUP][MAX_ASYNC_CLIENTS]
Definition: AsyncClient.h:155
void ClearServers(void)
void WriteByte(int c)
Definition: BitMsg.h:283
virtual const char * GetFullPath(void)
Definition: File.h:194
int SendMessage(idPort &port, const int time, const idBitMsg &msg)
Definition: MsgChannel.cpp:471
idStr updateFallback
Definition: AsyncClient.h:166
void ProcessInfoResponseMessage(const netadr_t from, const idBitMsg &msg)
virtual int Read(void *buffer, int len)
Definition: File.cpp:1047
backgroundDownload_t backgroundDownload
Definition: AsyncClient.h:169
assert(prefInfo.fullscreenBtn)
int dlChecksums[MAX_PURE_PAKS]
Definition: AsyncClient.h:176
int Cmp(const char *text) const
Definition: Str.h:652
bool IsActive(void) const
Definition: AsyncClient.h:96
idCVarSystem * cvarSystem
Definition: CVarSystem.cpp:487
void SendPingResponseToServer(int time)
void ProcessChallengeResponseMessage(const netadr_t from, const idBitMsg &msg)
virtual void CDKeysAuthReply(bool valid, const char *auth_msg)=0
bool Process(const netadr_t from, int time, idBitMsg &msg, int &sequence)
Definition: MsgChannel.cpp:539
netadrtype_t type
Definition: sys_public.h:405
virtual void SetStateString(const char *varName, const char *value)=0
fsPureReply_t
Definition: FileSystem.h:66
bool CheckTimeout(void)
virtual void virtual void virtual const idLangDict * GetLanguageDict(void)=0
void HandleDownloads(void)
int GetDownloadRequest(const int checksums[MAX_PURE_PAKS], int count, int gamePakChecksum)
static const char * HandleGuiCommand(const char *cmd)
#define CONNECTIONLESS_MESSAGE_ID_MASK
Definition: MsgChannel.h:51
void ProcessReliableServerMessages(void)
static idCVar clientPrediction
Definition: AsyncNetwork.h:182
void Clear(void)
Definition: AsyncClient.cpp:57
int size
Definition: AsyncClient.h:72
urlDownload_t url
Definition: FileSystem.h:117
int snapshotSequence
Definition: AsyncClient.h:146
int Length(void) const
Definition: Str.h:702
const int ASYNC_PROTOCOL_VERSION
Definition: AsyncNetwork.h:41
char dlerror[MAX_STRING_CHARS]
Definition: FileSystem.h:99
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
virtual void ClearPureChecksums(void)=0
void Reconnect(void)
virtual void ClearCDKey(bool valid[2])=0
static idCVar clientDownload
Definition: AsyncNetwork.h:196
int clientDataChecksum
Definition: AsyncClient.h:126
#define ASYNC_PROTOCOL_MAJOR
Definition: Licensee.h:77
virtual int Tell(void)
Definition: File.cpp:1169
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
static idAsyncClient client
Definition: AsyncNetwork.h:168
int ReadLong(void) const
Definition: BitMsg.h:375
int GetRemaingData(void) const
Definition: BitMsg.h:351
int Sys_Milliseconds(void)
case const int
Definition: Callbacks.cpp:52
dlStatus_t status
Definition: FileSystem.h:103
static idCVar clientMaxPrediction
Definition: AsyncNetwork.h:183
unsigned short port
Definition: sys_public.h:407
void GetServerInfo(const netadr_t adr)
idFileSystem * fileSystem
Definition: FileSystem.cpp:500
void RunFrame()
Definition: ServerScan.cpp:293
int gameTimeResidual
Definition: AsyncClient.h:153
bool SendReliableMessage(const idBitMsg &msg)
Definition: MsgChannel.cpp:670
void ClosePort(void)
netadr_t lastRconAddress
Definition: AsyncClient.h:137
case const float
Definition: Callbacks.cpp:62
Definition: Game.h:57
Definition: AsyncClient.h:69
void SendReliableGameMessage(const idBitMsg &msg)
void Init(const netadr_t adr, const int id)
Definition: MsgChannel.cpp:259
bool Sys_StringToNetAdr(const char *s, netadr_t *a, bool doDNSResolve)
Definition: posix_net.cpp:171
void ConnectToServer(const netadr_t adr)
static idCVar LANServer
Definition: AsyncNetwork.h:192
int GetChallenge()
Definition: ServerScan.cpp:245
GLenum GLsizei const GLvoid * string
Definition: glext.h:3472
const int MAX_NICKLEN
Definition: AsyncNetwork.h:54
virtual int Seek(long offset, fsOrigin_t origin)
Definition: File.cpp:1198
float GetIncomingCompression(void) const
virtual void SetCVarString(const char *name, const char *value, int flags=0)=0
void SendEmptyToServer(bool force=false, bool mapLoad=false)
idUserInterface * guiNetMenu
Definition: AsyncClient.h:157
idCmdSystem * cmdSystem
Definition: CmdSystem.cpp:116
Definition: Game.h:60
idPort clientPort
Definition: AsyncClient.h:124
void SendVersionDLUpdate(int state)
void SendNextFragment(idPort &port, const int time)
Definition: MsgChannel.cpp:414
int ReadString(char *buffer, int bufferSize) const
Definition: BitMsg.cpp:424
void WriteString(const char *s, int maxLength=-1, bool make7Bit=true)
Definition: BitMsg.cpp:174
int Checksum(void) const
Definition: Dict.cpp:237
virtual void StartMenu(bool playIntro=false)=0
idList< pakDlEntry_t > dlList
Definition: AsyncClient.h:178
void ClearPendingPackets(void)
int clientPredictTime
Definition: AsyncClient.h:130
void GetNETServers(void)
static idCVar clientRemoteConsoleAddress
Definition: AsyncNetwork.h:185
static int ClampInt(int min, int max, int value)
Definition: Math.h:883
virtual void BackgroundDownload(backgroundDownload_t *bgl)=0
idUserInterfaceManager * uiManager
void SendVersionCheck(bool fromMenu=false)
int i
Definition: process.py:33
int ReadByte(void) const
Definition: BitMsg.h:363
virtual void ClientProcessReliableMessage(int clientNum, const idBitMsg &msg)=0
netadr_t GetRemoteAddress(void) const
Definition: MsgChannel.h:103
virtual gameReturn_t ClientPrediction(int clientNum, const usercmd_t *clientCmds, bool lastPredictFrame)=0
char nickname[MAX_NICKLEN][MAX_ASYNC_CLIENTS]
Definition: ServerScan.h:56
int Icmp(const char *text) const
Definition: Str.h:667
int BestUnit(const char *format, float value, Measure_t measure)
Definition: Str.cpp:1591
void Init(byte *data, int length)
Definition: BitMsg.h:155
volatile bool completed
Definition: FileSystem.h:118
bool updateDirectDownload
Definition: AsyncClient.h:163
void WriteData(const void *data, int length)
Definition: BitMsg.cpp:210
virtual idUserInterface * FindGui(const char *qpath, bool autoLoad=false, bool needUnique=false, bool forceUnique=false)=0
static idCVar clientUsercmdBackup
Definition: AsyncNetwork.h:184
dlMime_t
Definition: FileSystem.h:86
bool GetPacket(netadr_t &from, void *data, int &size, int maxSize)
Definition: posix_net.cpp:487
bool showUpdateMessage
Definition: AsyncClient.h:167
virtual void RemoveFile(const char *relativePath)=0
Definition: File.h:50
virtual int GetChecksum(void) const =0
virtual void BufferCommandText(cmdExecution_t exec, const char *text)=0
#define PORT_SERVER
Definition: Licensee.h:69
idStr updateFile
Definition: AsyncClient.h:164
const int USERCMD_MSEC
Definition: UsercmdGen.h:41
const int GAME_INIT_ID_INVALID
Definition: AsyncNetwork.h:60
void WriteNetadr(const netadr_t adr)
Definition: BitMsg.cpp:219
int RandomInt(void)
Definition: Random.h:70
bool ReadDeltaDict(idDict &dict, const idDict *base) const
Definition: BitMsg.cpp:558
void ExtractFileName(idStr &dest) const
Definition: Str.cpp:921
int snapshotGameTime
Definition: AsyncClient.h:148
GLuint GLuint GLsizei count
Definition: glext.h:2845
int UpdateTime(int clamp)
void SendPacket(const netadr_t to, const void *data, int size)
Definition: posix_net.cpp:572
void SetSeed(int seed)
Definition: Random.h:62
int gameTime
Definition: UsercmdGen.h:92
virtual void OpenURL(const char *url, bool quit)=0
void ProcessPrintMessage(const netadr_t from, const idBitMsg &msg)
GLuint index
Definition: glext.h:3476
const int GAME_INIT_ID_MAP_LOAD
Definition: AsyncNetwork.h:61
const GLubyte * c
Definition: glext.h:4677
const char * GetString(const char *key, const char *defaultString="") const
Definition: Dict.h:240
const int SETUP_CONNECTION_RESEND_TIME
Definition: AsyncClient.cpp:36
int rate[MAX_ASYNC_CLIENTS]
Definition: ServerScan.h:58
const int ASYNC_PROTOCOL_MINOR
Definition: AsyncNetwork.h:40
static idAsyncServer server
Definition: AsyncNetwork.h:167
#define MAX_STRING_CHARS
Definition: Lib.h:95
idSys * sys
Definition: maya_main.cpp:48
static void WriteUserCmdDelta(idBitMsg &msg, const usercmd_t &cmd, const usercmd_t *base)
int checksum
Definition: AsyncClient.h:73
static void BuildInvalidKeyMsg(idStr &msg, bool valid[2])
int GetPrediction(void) const
virtual idFile * MakeTemporaryFile(void)=0
bool GetReliableMessage(idBitMsg &msg)
Definition: MsgChannel.cpp:690
virtual void SetRestartChecksums(const int pureChecksums[MAX_PURE_PAKS], int gamePakChecksum)=0
void WriteLong(int c)
Definition: BitMsg.h:295
void PacifierUpdate(void)
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
const char * HandleGuiCommandInternal(const char *cmd)
float GetIncomingPacketLoss(void) const
int GetSize(void) const
Definition: BitMsg.h:187
virtual fsPureReply_t SetPureServerChecksums(const int pureChecksums[MAX_PURE_PAKS], int gamePakChecksum, int missingChecksums[MAX_PURE_PAKS], int *missingGamePakChecksum)=0
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 void DownloadProgressBox(backgroundDownload_t *bgl, const char *title, int progress_start=0, int progress_end=100)=0
virtual const char * GetCVarString(const char *name) const =0
void BeginReading(void) const
Definition: BitMsg.h:346
void Clear(void)
Definition: Dict.cpp:201
void WriteShort(int c)
Definition: BitMsg.h:287
idServerScan serverList
Definition: AsyncClient.h:117
virtual void virtual void FatalError(const char *fmt,...) id_attribute((format(printf
#define PORT_ANY
Definition: sys_public.h:410
virtual idFile * OpenFileWrite(const char *relativePath, const char *basePath="fs_savepath")=0
void ProcessUnreliableServerMessage(const idBitMsg &msg)
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
bool InitForPort(int portNumber)
Definition: posix_net.cpp:598
void Shutdown(void)
Definition: MsgChannel.cpp:291
virtual void StartProcess(const char *exePath, bool quit)=0
authBadKeyStatus_t
Definition: AsyncClient.h:54
void AddServer(int id, const char *srv)
Definition: ServerScan.cpp:189
virtual void StopBox(void)=0
void SetSize(int size)
Definition: BitMsg.h:191
float GetOutgoingCompression(void) const
void Mem_Free(void *ptr)
Definition: Heap.cpp:1087
void SetupConnection(void)
Definition: Session.h:51
void StartServers(bool timeout)
Definition: ServerScan.cpp:225
void Clear(void)
Definition: Str.h:724
int gameFrame
Definition: UsercmdGen.h:91
virtual const char * GetName(void)
Definition: File.h:193
void RunFrame(void)
int clientPrediction
Definition: AsyncClient.h:129
idUsercmdGen * usercmdGen
Definition: UsercmdGen.cpp:419
void PrintLocalServerInfo(void)
clientUpdateState_t updateState
Definition: AsyncClient.h:159
void ProcessMessage(const netadr_t from, idBitMsg &msg)
static idCVar allowCheats
Definition: AsyncNetwork.h:171
bool InitPort(void)
GLubyte GLubyte GLubyte a
Definition: glext.h:4662
virtual void Printf(const char *fmt,...) id_attribute((format(printf
void ProcessServersListMessage(const netadr_t from, const idBitMsg &msg)
static netadr_t GetMasterAddress(void)
int GetOutgoingRate(void) const
netadr_t serverAddress
Definition: AsyncClient.h:132
void ExecuteMapChange(bool noFadeWipe=false)
Definition: Session.cpp:1515
void AppendPath(const char *text)
Definition: Str.cpp:830
const char * Sys_NetAdrToString(const netadr_t a)
Definition: posix_net.cpp:187
int size
Definition: List.h:136
virtual const char * GetCDKey(bool xp)=0
bool ValidatePureServerChecksums(const netadr_t from, const idBitMsg &msg)
idDeclManager * declManager
mapSpawnData_t mapSpawnData
dlMime_t updateMime
Definition: AsyncClient.h:165
GLubyte GLubyte b
Definition: glext.h:4662
virtual void ThrottleUserInfo(void)=0
void GetLANServers(void)
virtual bool CheckKey(const char *key, bool netConnect, bool offline_valid[2])=0
const int MAX_SERVER_PORTS
Definition: AsyncNetwork.h:57
bool IsNumeric(void) const
Definition: Str.h:833
virtual void GetPureServerChecksums(int checksums[MAX_PURE_PAKS], int OS, int *gamePakChecksum)=0
void InitGame(int serverGameInitId, int serverGameFrame, int serverGameTime, const idDict &serverSI)
idUserInterface * GetActiveMenu()
virtual void SetMute(bool mute)=0
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
static void ExecuteSessionCommand(const char *sessCmd)
int duplicateCount
Definition: UsercmdGen.h:93
const int PREDICTION_FAST_ADJUST
Definition: AsyncClient.cpp:38
virtual idFile * OpenExplicitFileWrite(const char *OSPath)=0
bool UnsentFragmentsLeft(void) const
Definition: MsgChannel.h:130
GLuint id
Definition: glext.h:3103
#define MAX_MESSAGE_SIZE
Definition: MsgChannel.h:48
#define INTSIGNBITSET(i)
Definition: Math.h:71
idDict syncedCVars
Definition: Session_local.h:67
static idCVar clientRemoteConsolePassword
Definition: AsyncNetwork.h:186
tuple f
Definition: idal.py:89
#define CONNECTIONLESS_MESSAGE_ID
Definition: MsgChannel.h:50
void ProcessPureMessage(const netadr_t from, const idBitMsg &msg)
int serverMessageSequence
Definition: AsyncClient.h:135
int Num(void) const
Definition: List.h:265
bool RemoveIndex(int index)
Definition: List.h:849
unsigned char byte
Definition: Lib.h:75
void NetScan()
Definition: ServerScan.cpp:254
bool WriteDeltaDict(const idDict &dict, const idDict *base)
Definition: BitMsg.cpp:308
int GetIncomingRate(void) const
Definition: MsgChannel.h:109
virtual void ResetFlaggedVariables(int flags)=0
const GLcharARB * name
Definition: glext.h:3629
float GetIncomingPacketLoss(void) const
Definition: MsgChannel.cpp:785
GLsizeiptr size
Definition: glext.h:3112
idSoundSystem * soundSystem
Definition: snd_system.cpp:92
static bool DuplicateUsercmd(const usercmd_t &previousUserCmd, usercmd_t &currentUserCmd, int frame, int time)
virtual int Write(const void *buffer, int len)
Definition: File.cpp:189
byte * GetData(void)
Definition: BitMsg.h:167
Definition: Str.h:116
void ProcessReliableMessagePure(const idBitMsg &msg)
bool SendAuthCheck(const char *cdkey, const char *xpkey)
short pings[MAX_ASYNC_CLIENTS]
Definition: ServerScan.h:57
GLuint address
Definition: glext.h:5165
idMsgChannel channel
Definition: AsyncClient.h:140
const char * c_str(void) const
Definition: Str.h:487
void DuplicateUsercmds(int frame, int time)
void RemoteConsole(const char *command)
void ProcessDisconnectMessage(const netadr_t from, const idBitMsg &msg)
virtual bool GetCVarBool(const char *name) const =0
void SetBool(const bool value)
Definition: CVarSystem.h:147
void ProcessDownloadInfoMessage(const netadr_t from, const idBitMsg &msg)
int GetTimeSinceLastPacket(void) const
char sessionCommand[MAX_STRING_CHARS]
Definition: Game.h:46
int snapshotGameFrame
Definition: AsyncClient.h:147
void SetupLANScan()
Definition: ServerScan.cpp:111
void Clear()
Definition: ServerScan.cpp:86
virtual const idDict * MoveCVarsToDict(int flags) const =0
idGame * game
Definition: Game_local.cpp:65
void * Mem_Alloc(const int size)
Definition: Heap.cpp:1067
bool GetPacketBlocking(netadr_t &from, void *data, int &size, int maxSize, int timeout)
Definition: posix_net.cpp:520
GLint j
Definition: qgl.h:264
virtual void CreateOSPath(const char *OSPath)=0
int lastSnapshotTime
Definition: AsyncClient.h:144
int GetPort(void) const
Definition: sys_public.h:419
idDict serverInfo
Definition: ServerScan.h:52
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 SendUserInfoToServer(void)
void WriteBits(int value, int numBits)
Definition: BitMsg.cpp:110
virtual void CloseFile(idFile *f)=0
virtual void ClientReadSnapshot(int clientNum, int sequence, const int gameFrame, const int gameTime, const int dupeUsercmds, const int aheadOfServer, const idBitMsg &msg)=0
void ResetRate(void)
Definition: MsgChannel.cpp:301
virtual void DPrintf(const char *fmt,...) id_attribute((format(printf
authKeyMsg_t
Definition: AsyncClient.h:49
void SendUsercmdsToServer(void)
virtual void Error(const char *fmt,...) id_attribute((format(printf
virtual bool ClientApplySnapshot(int clientNum, int sequence)=0
static idCVar verbose
Definition: AsyncNetwork.h:170
static idCVar clientServerTimeout
Definition: AsyncNetwork.h:179
static void ReadUserCmdDelta(const idBitMsg &msg, usercmd_t &cmd, const usercmd_t *base)
int GetIncomingRate(void) const
virtual usercmd_t GetDirectUsercmd(void)=0
virtual void virtual void Warning(const char *fmt,...) id_attribute((format(printf
void Idle(void)
void Print() const
Definition: Dict.cpp:218
void ProcessVersionMessage(const netadr_t from, const idBitMsg &msg)
ID_INLINE T Min(T x, T y)
Definition: Lib.h:159
void ReadLocalizedServerString(const idBitMsg &msg, char *out, int maxLen)
int ReadBits(int numBits) const
Definition: BitMsg.cpp:362
int sprintf(idStr &string, const char *fmt,...)
Definition: Str.cpp:1528
void DisconnectFromServer(void)
virtual void ClearModifiedFlags(int flags)=0
void ProcessConnectResponseMessage(const netadr_t from, const idBitMsg &msg)
virtual bool HasD3XP(void)=0
const int EMPTY_RESEND_TIME
Definition: AsyncClient.cpp:37
virtual void SetGUI(idUserInterface *gui, HandleGuiCommand_t handle)=0
idStr filename
Definition: AsyncClient.h:71
float GetOutgoingCompression(void) const
Definition: MsgChannel.h:112
virtual bool WaitingForGameAuth(void)=0
void ListServers(void)
clientState_t clientState
Definition: AsyncClient.h:128
virtual int AddZipFile(const char *path)=0
virtual void SetCVarsFromDict(const idDict &dict)=0
int ReadShort(void) const
Definition: BitMsg.h:367
idSessionLocal sessLocal
Definition: Session.cpp:47
idStr url
Definition: AsyncClient.h:70
void Clear(void)
Definition: List.h:184
void ConnectionlessMessage(const netadr_t from, const idBitMsg &msg)