AsyncClient.cpp 74 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363
  1. /*
  2. ===========================================================================
  3. Doom 3 GPL Source Code
  4. Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
  6. Doom 3 Source Code is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. Doom 3 Source Code is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. 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.
  17. 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.
  18. ===========================================================================
  19. */
  20. #include "../../idlib/precompiled.h"
  21. #pragma hdrstop
  22. #include "AsyncNetwork.h"
  23. #include "../Session_local.h"
  24. const int SETUP_CONNECTION_RESEND_TIME = 1000;
  25. const int EMPTY_RESEND_TIME = 500;
  26. const int PREDICTION_FAST_ADJUST = 4;
  27. /*
  28. ==================
  29. idAsyncClient::idAsyncClient
  30. ==================
  31. */
  32. idAsyncClient::idAsyncClient( void ) {
  33. guiNetMenu = NULL;
  34. updateState = UPDATE_NONE;
  35. Clear();
  36. }
  37. /*
  38. ==================
  39. idAsyncClient::Clear
  40. ==================
  41. */
  42. void idAsyncClient::Clear( void ) {
  43. active = false;
  44. realTime = 0;
  45. clientTime = 0;
  46. clientId = 0;
  47. clientDataChecksum = 0;
  48. clientNum = 0;
  49. clientState = CS_DISCONNECTED;
  50. clientPrediction = 0;
  51. clientPredictTime = 0;
  52. serverId = 0;
  53. serverChallenge = 0;
  54. serverMessageSequence = 0;
  55. lastConnectTime = -9999;
  56. lastEmptyTime = -9999;
  57. lastPacketTime = -9999;
  58. lastSnapshotTime = -9999;
  59. snapshotGameFrame = 0;
  60. snapshotGameTime = 0;
  61. snapshotSequence = 0;
  62. gameInitId = GAME_INIT_ID_INVALID;
  63. gameFrame = 0;
  64. gameTimeResidual = 0;
  65. gameTime = 0;
  66. memset( userCmds, 0, sizeof( userCmds ) );
  67. backgroundDownload.completed = true;
  68. lastRconTime = 0;
  69. showUpdateMessage = false;
  70. lastFrameDelta = 0;
  71. dlRequest = -1;
  72. dlCount = -1;
  73. memset( dlChecksums, 0, sizeof( int ) * MAX_PURE_PAKS );
  74. currentDlSize = 0;
  75. totalDlSize = 0;
  76. }
  77. /*
  78. ==================
  79. idAsyncClient::Shutdown
  80. ==================
  81. */
  82. void idAsyncClient::Shutdown( void ) {
  83. guiNetMenu = NULL;
  84. updateMSG.Clear();
  85. updateURL.Clear();
  86. updateFile.Clear();
  87. updateFallback.Clear();
  88. backgroundDownload.url.url.Clear();
  89. dlList.Clear();
  90. }
  91. /*
  92. ==================
  93. idAsyncClient::InitPort
  94. ==================
  95. */
  96. bool idAsyncClient::InitPort( void ) {
  97. // if this is the first time we connect to a server, open the UDP port
  98. if ( !clientPort.GetPort() ) {
  99. if ( !clientPort.InitForPort( PORT_ANY ) ) {
  100. common->Printf( "Couldn't open client network port.\n" );
  101. return false;
  102. }
  103. }
  104. // maintain it valid between connects and ui manager reloads
  105. guiNetMenu = uiManager->FindGui( "guis/netmenu.gui", true, false, true );
  106. return true;
  107. }
  108. /*
  109. ==================
  110. idAsyncClient::ClosePort
  111. ==================
  112. */
  113. void idAsyncClient::ClosePort( void ) {
  114. clientPort.Close();
  115. }
  116. /*
  117. ==================
  118. idAsyncClient::ClearPendingPackets
  119. ==================
  120. */
  121. void idAsyncClient::ClearPendingPackets( void ) {
  122. int size;
  123. byte msgBuf[MAX_MESSAGE_SIZE];
  124. netadr_t from;
  125. while( clientPort.GetPacket( from, msgBuf, size, sizeof( msgBuf ) ) ) {
  126. }
  127. }
  128. /*
  129. ==================
  130. idAsyncClient::HandleGuiCommandInternal
  131. ==================
  132. */
  133. const char* idAsyncClient::HandleGuiCommandInternal( const char *cmd ) {
  134. if ( !idStr::Cmp( cmd, "abort" ) || !idStr::Cmp( cmd, "pure_abort" ) ) {
  135. common->DPrintf( "connection aborted\n" );
  136. cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
  137. return "";
  138. } else {
  139. common->DWarning( "idAsyncClient::HandleGuiCommand: unknown cmd %s", cmd );
  140. }
  141. return NULL;
  142. }
  143. /*
  144. ==================
  145. idAsyncClient::HandleGuiCommand
  146. ==================
  147. */
  148. const char* idAsyncClient::HandleGuiCommand( const char *cmd ) {
  149. return idAsyncNetwork::client.HandleGuiCommandInternal( cmd );
  150. }
  151. /*
  152. ==================
  153. idAsyncClient::ConnectToServer
  154. ==================
  155. */
  156. void idAsyncClient::ConnectToServer( const netadr_t adr ) {
  157. // shutdown any current game. that includes network disconnect
  158. session->Stop();
  159. if ( !InitPort() ) {
  160. return;
  161. }
  162. if ( cvarSystem->GetCVarBool( "net_serverDedicated" ) ) {
  163. common->Printf( "Can't connect to a server as dedicated\n" );
  164. return;
  165. }
  166. // trash any currently pending packets
  167. ClearPendingPackets();
  168. serverAddress = adr;
  169. // clear the client state
  170. Clear();
  171. // get a pseudo random client id, but don't use the id which is reserved for connectionless packets
  172. clientId = Sys_Milliseconds() & CONNECTIONLESS_MESSAGE_ID_MASK;
  173. // calculate a checksum on some of the essential data used
  174. clientDataChecksum = declManager->GetChecksum();
  175. // start challenging the server
  176. clientState = CS_CHALLENGING;
  177. active = true;
  178. guiNetMenu = uiManager->FindGui( "guis/netmenu.gui", true, false, true );
  179. guiNetMenu->SetStateString( "status", va( common->GetLanguageDict()->GetString( "#str_06749" ), Sys_NetAdrToString( adr ) ) );
  180. session->SetGUI( guiNetMenu, HandleGuiCommand );
  181. }
  182. /*
  183. ==================
  184. idAsyncClient::Reconnect
  185. ==================
  186. */
  187. void idAsyncClient::Reconnect( void ) {
  188. ConnectToServer( serverAddress );
  189. }
  190. /*
  191. ==================
  192. idAsyncClient::ConnectToServer
  193. ==================
  194. */
  195. void idAsyncClient::ConnectToServer( const char *address ) {
  196. int serverNum;
  197. netadr_t adr;
  198. if ( idStr::IsNumeric( address ) ) {
  199. serverNum = atoi( address );
  200. if ( serverNum < 0 || serverNum >= serverList.Num() ) {
  201. session->MessageBox( MSG_OK, va( common->GetLanguageDict()->GetString( "#str_06733" ), serverNum ), common->GetLanguageDict()->GetString( "#str_06735" ), true );
  202. return;
  203. }
  204. adr = serverList[ serverNum ].adr;
  205. } else {
  206. if ( !Sys_StringToNetAdr( address, &adr, true ) ) {
  207. session->MessageBox( MSG_OK, va( common->GetLanguageDict()->GetString( "#str_06734" ), address ), common->GetLanguageDict()->GetString( "#str_06735" ), true );
  208. return;
  209. }
  210. }
  211. if ( !adr.port ) {
  212. adr.port = PORT_SERVER;
  213. }
  214. common->Printf( "\"%s\" resolved to %s\n", address, Sys_NetAdrToString( adr ) );
  215. ConnectToServer( adr );
  216. }
  217. /*
  218. ==================
  219. idAsyncClient::DisconnectFromServer
  220. ==================
  221. */
  222. void idAsyncClient::DisconnectFromServer( void ) {
  223. idBitMsg msg;
  224. byte msgBuf[MAX_MESSAGE_SIZE];
  225. if ( clientState >= CS_CONNECTED ) {
  226. // if we were actually connected, clear the pure list
  227. fileSystem->ClearPureChecksums();
  228. // send reliable disconnect to server
  229. msg.Init( msgBuf, sizeof( msgBuf ) );
  230. msg.WriteByte( CLIENT_RELIABLE_MESSAGE_DISCONNECT );
  231. msg.WriteString( "disconnect" );
  232. if ( !channel.SendReliableMessage( msg ) ) {
  233. common->Error( "client->server reliable messages overflow\n" );
  234. }
  235. SendEmptyToServer( true );
  236. SendEmptyToServer( true );
  237. SendEmptyToServer( true );
  238. }
  239. if ( clientState != CS_PURERESTART ) {
  240. channel.Shutdown();
  241. clientState = CS_DISCONNECTED;
  242. }
  243. active = false;
  244. }
  245. /*
  246. ==================
  247. idAsyncClient::GetServerInfo
  248. ==================
  249. */
  250. void idAsyncClient::GetServerInfo( const netadr_t adr ) {
  251. idBitMsg msg;
  252. byte msgBuf[MAX_MESSAGE_SIZE];
  253. if ( !InitPort() ) {
  254. return;
  255. }
  256. msg.Init( msgBuf, sizeof( msgBuf ) );
  257. msg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
  258. msg.WriteString( "getInfo" );
  259. msg.WriteLong( serverList.GetChallenge() ); // challenge
  260. clientPort.SendPacket( adr, msg.GetData(), msg.GetSize() );
  261. }
  262. /*
  263. ==================
  264. idAsyncClient::GetServerInfo
  265. ==================
  266. */
  267. void idAsyncClient::GetServerInfo( const char *address ) {
  268. netadr_t adr;
  269. if ( address && *address != '\0' ) {
  270. if ( !Sys_StringToNetAdr( address, &adr, true ) ) {
  271. common->Printf( "Couldn't get server address for \"%s\"\n", address );
  272. return;
  273. }
  274. } else if ( active ) {
  275. adr = serverAddress;
  276. } else if ( idAsyncNetwork::server.IsActive() ) {
  277. // used to be a Sys_StringToNetAdr( "localhost", &adr, true ); and send a packet over loopback
  278. // but this breaks with net_ip ( typically, for multi-homed servers )
  279. idAsyncNetwork::server.PrintLocalServerInfo();
  280. return;
  281. } else {
  282. common->Printf( "no server found\n" );
  283. return;
  284. }
  285. if ( !adr.port ) {
  286. adr.port = PORT_SERVER;
  287. }
  288. GetServerInfo( adr );
  289. }
  290. /*
  291. ==================
  292. idAsyncClient::GetLANServers
  293. ==================
  294. */
  295. void idAsyncClient::GetLANServers( void ) {
  296. int i;
  297. idBitMsg msg;
  298. byte msgBuf[MAX_MESSAGE_SIZE];
  299. netadr_t broadcastAddress;
  300. if ( !InitPort() ) {
  301. return;
  302. }
  303. idAsyncNetwork::LANServer.SetBool( true );
  304. serverList.SetupLANScan();
  305. msg.Init( msgBuf, sizeof( msgBuf ) );
  306. msg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
  307. msg.WriteString( "getInfo" );
  308. msg.WriteLong( serverList.GetChallenge() );
  309. broadcastAddress.type = NA_BROADCAST;
  310. for ( i = 0; i < MAX_SERVER_PORTS; i++ ) {
  311. broadcastAddress.port = PORT_SERVER + i;
  312. clientPort.SendPacket( broadcastAddress, msg.GetData(), msg.GetSize() );
  313. }
  314. }
  315. /*
  316. ==================
  317. idAsyncClient::GetNETServers
  318. ==================
  319. */
  320. void idAsyncClient::GetNETServers( void ) {
  321. idBitMsg msg;
  322. byte msgBuf[MAX_MESSAGE_SIZE];
  323. idAsyncNetwork::LANServer.SetBool( false );
  324. // NetScan only clears GUI and results, not the stored list
  325. serverList.Clear( );
  326. serverList.NetScan( );
  327. serverList.StartServers( true );
  328. msg.Init( msgBuf, sizeof( msgBuf ) );
  329. msg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
  330. msg.WriteString( "getServers" );
  331. msg.WriteLong( ASYNC_PROTOCOL_VERSION );
  332. msg.WriteString( cvarSystem->GetCVarString( "fs_game" ) );
  333. msg.WriteBits( cvarSystem->GetCVarInteger( "gui_filter_password" ), 2 );
  334. msg.WriteBits( cvarSystem->GetCVarInteger( "gui_filter_players" ), 2 );
  335. msg.WriteBits( cvarSystem->GetCVarInteger( "gui_filter_gameType" ), 2 );
  336. netadr_t adr;
  337. if ( idAsyncNetwork::GetMasterAddress( 0, adr ) ) {
  338. clientPort.SendPacket( adr, msg.GetData(), msg.GetSize() );
  339. }
  340. }
  341. /*
  342. ==================
  343. idAsyncClient::ListServers
  344. ==================
  345. */
  346. void idAsyncClient::ListServers( void ) {
  347. int i;
  348. for ( i = 0; i < serverList.Num(); i++ ) {
  349. common->Printf( "%3d: %s %dms (%s)\n", i, serverList[i].serverInfo.GetString( "si_name" ), serverList[ i ].ping, Sys_NetAdrToString( serverList[i].adr ) );
  350. }
  351. }
  352. /*
  353. ==================
  354. idAsyncClient::ClearServers
  355. ==================
  356. */
  357. void idAsyncClient::ClearServers( void ) {
  358. serverList.Clear();
  359. }
  360. /*
  361. ==================
  362. idAsyncClient::RemoteConsole
  363. ==================
  364. */
  365. void idAsyncClient::RemoteConsole( const char *command ) {
  366. netadr_t adr;
  367. idBitMsg msg;
  368. byte msgBuf[MAX_MESSAGE_SIZE];
  369. if ( !InitPort() ) {
  370. return;
  371. }
  372. if ( active ) {
  373. adr = serverAddress;
  374. } else {
  375. Sys_StringToNetAdr( idAsyncNetwork::clientRemoteConsoleAddress.GetString(), &adr, true );
  376. }
  377. if ( !adr.port ) {
  378. adr.port = PORT_SERVER;
  379. }
  380. lastRconAddress = adr;
  381. lastRconTime = realTime;
  382. msg.Init( msgBuf, sizeof( msgBuf ) );
  383. msg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
  384. msg.WriteString( "rcon" );
  385. msg.WriteString( idAsyncNetwork::clientRemoteConsolePassword.GetString() );
  386. msg.WriteString( command );
  387. clientPort.SendPacket( adr, msg.GetData(), msg.GetSize() );
  388. }
  389. /*
  390. ==================
  391. idAsyncClient::GetPrediction
  392. ==================
  393. */
  394. int idAsyncClient::GetPrediction( void ) const {
  395. if ( clientState < CS_CONNECTED ) {
  396. return -1;
  397. } else {
  398. return clientPrediction;
  399. }
  400. }
  401. /*
  402. ==================
  403. idAsyncClient::GetTimeSinceLastPacket
  404. ==================
  405. */
  406. int idAsyncClient::GetTimeSinceLastPacket( void ) const {
  407. if ( clientState < CS_CONNECTED ) {
  408. return -1;
  409. } else {
  410. return clientTime - lastPacketTime;
  411. }
  412. }
  413. /*
  414. ==================
  415. idAsyncClient::GetOutgoingRate
  416. ==================
  417. */
  418. int idAsyncClient::GetOutgoingRate( void ) const {
  419. if ( clientState < CS_CONNECTED ) {
  420. return -1;
  421. } else {
  422. return channel.GetOutgoingRate();
  423. }
  424. }
  425. /*
  426. ==================
  427. idAsyncClient::GetIncomingRate
  428. ==================
  429. */
  430. int idAsyncClient::GetIncomingRate( void ) const {
  431. if ( clientState < CS_CONNECTED ) {
  432. return -1;
  433. } else {
  434. return channel.GetIncomingRate();
  435. }
  436. }
  437. /*
  438. ==================
  439. idAsyncClient::GetOutgoingCompression
  440. ==================
  441. */
  442. float idAsyncClient::GetOutgoingCompression( void ) const {
  443. if ( clientState < CS_CONNECTED ) {
  444. return 0.0f;
  445. } else {
  446. return channel.GetOutgoingCompression();
  447. }
  448. }
  449. /*
  450. ==================
  451. idAsyncClient::GetIncomingCompression
  452. ==================
  453. */
  454. float idAsyncClient::GetIncomingCompression( void ) const {
  455. if ( clientState < CS_CONNECTED ) {
  456. return 0.0f;
  457. } else {
  458. return channel.GetIncomingCompression();
  459. }
  460. }
  461. /*
  462. ==================
  463. idAsyncClient::GetIncomingPacketLoss
  464. ==================
  465. */
  466. float idAsyncClient::GetIncomingPacketLoss( void ) const {
  467. if ( clientState < CS_CONNECTED ) {
  468. return 0.0f;
  469. } else {
  470. return channel.GetIncomingPacketLoss();
  471. }
  472. }
  473. /*
  474. ==================
  475. idAsyncClient::DuplicateUsercmds
  476. ==================
  477. */
  478. void idAsyncClient::DuplicateUsercmds( int frame, int time ) {
  479. int i, previousIndex, currentIndex;
  480. previousIndex = ( frame - 1 ) & ( MAX_USERCMD_BACKUP - 1 );
  481. currentIndex = frame & ( MAX_USERCMD_BACKUP - 1 );
  482. // duplicate previous user commands if no new commands are available for a client
  483. for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
  484. idAsyncNetwork::DuplicateUsercmd( userCmds[previousIndex][i], userCmds[currentIndex][i], frame, time );
  485. }
  486. }
  487. /*
  488. ==================
  489. idAsyncClient::SendUserInfoToServer
  490. ==================
  491. */
  492. void idAsyncClient::SendUserInfoToServer( void ) {
  493. idBitMsg msg;
  494. byte msgBuf[MAX_MESSAGE_SIZE];
  495. idDict info;
  496. if ( clientState < CS_CONNECTED ) {
  497. return;
  498. }
  499. info = *cvarSystem->MoveCVarsToDict( CVAR_USERINFO );
  500. // send reliable client info to server
  501. msg.Init( msgBuf, sizeof( msgBuf ) );
  502. msg.WriteByte( CLIENT_RELIABLE_MESSAGE_CLIENTINFO );
  503. msg.WriteDeltaDict( info, &sessLocal.mapSpawnData.userInfo[ clientNum ] );
  504. if ( !channel.SendReliableMessage( msg ) ) {
  505. common->Error( "client->server reliable messages overflow\n" );
  506. }
  507. sessLocal.mapSpawnData.userInfo[clientNum] = info;
  508. }
  509. /*
  510. ==================
  511. idAsyncClient::SendEmptyToServer
  512. ==================
  513. */
  514. void idAsyncClient::SendEmptyToServer( bool force, bool mapLoad ) {
  515. idBitMsg msg;
  516. byte msgBuf[MAX_MESSAGE_SIZE];
  517. if ( lastEmptyTime > realTime ) {
  518. lastEmptyTime = realTime;
  519. }
  520. if ( !force && ( realTime - lastEmptyTime < EMPTY_RESEND_TIME ) ) {
  521. return;
  522. }
  523. if ( idAsyncNetwork::verbose.GetInteger() ) {
  524. common->Printf( "sending empty to server, gameInitId = %d\n", mapLoad ? GAME_INIT_ID_MAP_LOAD : gameInitId );
  525. }
  526. msg.Init( msgBuf, sizeof( msgBuf ) );
  527. msg.WriteLong( serverMessageSequence );
  528. msg.WriteLong( mapLoad ? GAME_INIT_ID_MAP_LOAD : gameInitId );
  529. msg.WriteLong( snapshotSequence );
  530. msg.WriteByte( CLIENT_UNRELIABLE_MESSAGE_EMPTY );
  531. channel.SendMessage( clientPort, clientTime, msg );
  532. while( channel.UnsentFragmentsLeft() ) {
  533. channel.SendNextFragment( clientPort, clientTime );
  534. }
  535. lastEmptyTime = realTime;
  536. }
  537. /*
  538. ==================
  539. idAsyncClient::SendPingResponseToServer
  540. ==================
  541. */
  542. void idAsyncClient::SendPingResponseToServer( int time ) {
  543. idBitMsg msg;
  544. byte msgBuf[MAX_MESSAGE_SIZE];
  545. if ( idAsyncNetwork::verbose.GetInteger() == 2 ) {
  546. common->Printf( "sending ping response to server, gameInitId = %d\n", gameInitId );
  547. }
  548. msg.Init( msgBuf, sizeof( msgBuf ) );
  549. msg.WriteLong( serverMessageSequence );
  550. msg.WriteLong( gameInitId );
  551. msg.WriteLong( snapshotSequence );
  552. msg.WriteByte( CLIENT_UNRELIABLE_MESSAGE_PINGRESPONSE );
  553. msg.WriteLong( time );
  554. channel.SendMessage( clientPort, clientTime, msg );
  555. while( channel.UnsentFragmentsLeft() ) {
  556. channel.SendNextFragment( clientPort, clientTime );
  557. }
  558. }
  559. /*
  560. ==================
  561. idAsyncClient::SendUsercmdsToServer
  562. ==================
  563. */
  564. void idAsyncClient::SendUsercmdsToServer( void ) {
  565. int i, numUsercmds, index;
  566. idBitMsg msg;
  567. byte msgBuf[MAX_MESSAGE_SIZE];
  568. usercmd_t * last;
  569. if ( idAsyncNetwork::verbose.GetInteger() == 2 ) {
  570. common->Printf( "sending usercmd to server: gameInitId = %d, gameFrame = %d, gameTime = %d\n", gameInitId, gameFrame, gameTime );
  571. }
  572. // generate user command for this client
  573. index = gameFrame & ( MAX_USERCMD_BACKUP - 1 );
  574. userCmds[index][clientNum] = usercmdGen->GetDirectUsercmd();
  575. userCmds[index][clientNum].gameFrame = gameFrame;
  576. userCmds[index][clientNum].gameTime = gameTime;
  577. // send the user commands to the server
  578. msg.Init( msgBuf, sizeof( msgBuf ) );
  579. msg.WriteLong( serverMessageSequence );
  580. msg.WriteLong( gameInitId );
  581. msg.WriteLong( snapshotSequence );
  582. msg.WriteByte( CLIENT_UNRELIABLE_MESSAGE_USERCMD );
  583. msg.WriteShort( clientPrediction );
  584. numUsercmds = idMath::ClampInt( 0, 10, idAsyncNetwork::clientUsercmdBackup.GetInteger() ) + 1;
  585. // write the user commands
  586. msg.WriteLong( gameFrame );
  587. msg.WriteByte( numUsercmds );
  588. for ( last = NULL, i = gameFrame - numUsercmds + 1; i <= gameFrame; i++ ) {
  589. index = i & ( MAX_USERCMD_BACKUP - 1 );
  590. idAsyncNetwork::WriteUserCmdDelta( msg, userCmds[index][clientNum], last );
  591. last = &userCmds[index][clientNum];
  592. }
  593. channel.SendMessage( clientPort, clientTime, msg );
  594. while( channel.UnsentFragmentsLeft() ) {
  595. channel.SendNextFragment( clientPort, clientTime );
  596. }
  597. }
  598. /*
  599. ==================
  600. idAsyncClient::InitGame
  601. ==================
  602. */
  603. void idAsyncClient::InitGame( int serverGameInitId, int serverGameFrame, int serverGameTime, const idDict &serverSI ) {
  604. gameInitId = serverGameInitId;
  605. gameFrame = snapshotGameFrame = serverGameFrame;
  606. gameTime = snapshotGameTime = serverGameTime;
  607. gameTimeResidual = 0;
  608. memset( userCmds, 0, sizeof( userCmds ) );
  609. for ( int i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
  610. sessLocal.mapSpawnData.userInfo[ i ].Clear();
  611. }
  612. sessLocal.mapSpawnData.serverInfo = serverSI;
  613. }
  614. /*
  615. ==================
  616. idAsyncClient::ProcessUnreliableServerMessage
  617. ==================
  618. */
  619. void idAsyncClient::ProcessUnreliableServerMessage( const idBitMsg &msg ) {
  620. int i, j, index, id, numDuplicatedUsercmds, aheadOfServer, numUsercmds, delta;
  621. int serverGameInitId, serverGameFrame, serverGameTime;
  622. idDict serverSI;
  623. usercmd_t *last;
  624. bool pureWait;
  625. serverGameInitId = msg.ReadLong();
  626. id = msg.ReadByte();
  627. switch( id ) {
  628. case SERVER_UNRELIABLE_MESSAGE_EMPTY: {
  629. if ( idAsyncNetwork::verbose.GetInteger() ) {
  630. common->Printf( "received empty message from server\n" );
  631. }
  632. break;
  633. }
  634. case SERVER_UNRELIABLE_MESSAGE_PING: {
  635. if ( idAsyncNetwork::verbose.GetInteger() == 2 ) {
  636. common->Printf( "received ping message from server\n" );
  637. }
  638. SendPingResponseToServer( msg.ReadLong() );
  639. break;
  640. }
  641. case SERVER_UNRELIABLE_MESSAGE_GAMEINIT: {
  642. serverGameFrame = msg.ReadLong();
  643. serverGameTime = msg.ReadLong();
  644. msg.ReadDeltaDict( serverSI, NULL );
  645. pureWait = serverSI.GetBool( "si_pure" );
  646. InitGame( serverGameInitId, serverGameFrame, serverGameTime, serverSI );
  647. channel.ResetRate();
  648. if ( idAsyncNetwork::verbose.GetInteger() ) {
  649. common->Printf( "received gameinit, gameInitId = %d, gameFrame = %d, gameTime = %d\n", gameInitId, gameFrame, gameTime );
  650. }
  651. // mute sound
  652. soundSystem->SetMute( true );
  653. // ensure chat icon goes away when the GUI is changed...
  654. //cvarSystem->SetCVarBool( "ui_chat", false );
  655. if ( pureWait ) {
  656. guiNetMenu = uiManager->FindGui( "guis/netmenu.gui", true, false, true );
  657. session->SetGUI( guiNetMenu, HandleGuiCommand );
  658. session->MessageBox( MSG_ABORT, common->GetLanguageDict()->GetString ( "#str_04317" ), common->GetLanguageDict()->GetString ( "#str_04318" ), false, "pure_abort" );
  659. } else {
  660. // load map
  661. session->SetGUI( NULL, NULL );
  662. sessLocal.ExecuteMapChange();
  663. }
  664. break;
  665. }
  666. case SERVER_UNRELIABLE_MESSAGE_SNAPSHOT: {
  667. // if the snapshot is from a different game
  668. if ( serverGameInitId != gameInitId ) {
  669. if ( idAsyncNetwork::verbose.GetInteger() ) {
  670. common->Printf( "ignoring snapshot with != gameInitId\n" );
  671. }
  672. break;
  673. }
  674. snapshotSequence = msg.ReadLong();
  675. snapshotGameFrame = msg.ReadLong();
  676. snapshotGameTime = msg.ReadLong();
  677. numDuplicatedUsercmds = msg.ReadByte();
  678. aheadOfServer = msg.ReadShort();
  679. // read the game snapshot
  680. game->ClientReadSnapshot( clientNum, snapshotSequence, snapshotGameFrame, snapshotGameTime, numDuplicatedUsercmds, aheadOfServer, msg );
  681. // read user commands of other clients from the snapshot
  682. for ( last = NULL, i = msg.ReadByte(); i < MAX_ASYNC_CLIENTS; i = msg.ReadByte() ) {
  683. numUsercmds = msg.ReadByte();
  684. if ( numUsercmds > MAX_USERCMD_RELAY ) {
  685. common->Error( "snapshot %d contains too many user commands for client %d", snapshotSequence, i );
  686. break;
  687. }
  688. for ( j = 0; j < numUsercmds; j++ ) {
  689. index = ( snapshotGameFrame + j ) & ( MAX_USERCMD_BACKUP - 1 );
  690. idAsyncNetwork::ReadUserCmdDelta( msg, userCmds[index][i], last );
  691. userCmds[index][i].gameFrame = snapshotGameFrame + j;
  692. userCmds[index][i].duplicateCount = 0;
  693. last = &userCmds[index][i];
  694. }
  695. // clear all user commands after the ones just read from the snapshot
  696. for ( j = numUsercmds; j < MAX_USERCMD_BACKUP; j++ ) {
  697. index = ( snapshotGameFrame + j ) & ( MAX_USERCMD_BACKUP - 1 );
  698. userCmds[index][i].gameFrame = 0;
  699. userCmds[index][i].gameTime = 0;
  700. }
  701. }
  702. // if this is the first snapshot after a game init was received
  703. if ( clientState == CS_CONNECTED ) {
  704. gameTimeResidual = 0;
  705. clientState = CS_INGAME;
  706. assert( !sessLocal.GetActiveMenu( ) );
  707. if ( idAsyncNetwork::verbose.GetInteger() ) {
  708. common->Printf( "received first snapshot, gameInitId = %d, gameFrame %d gameTime %d\n", gameInitId, snapshotGameFrame, snapshotGameTime );
  709. }
  710. }
  711. // if the snapshot is newer than the clients current game time
  712. if ( gameTime < snapshotGameTime || gameTime > snapshotGameTime + idAsyncNetwork::clientMaxPrediction.GetInteger() ) {
  713. gameFrame = snapshotGameFrame;
  714. gameTime = snapshotGameTime;
  715. gameTimeResidual = idMath::ClampInt( -idAsyncNetwork::clientMaxPrediction.GetInteger(), idAsyncNetwork::clientMaxPrediction.GetInteger(), gameTimeResidual );
  716. clientPredictTime = idMath::ClampInt( -idAsyncNetwork::clientMaxPrediction.GetInteger(), idAsyncNetwork::clientMaxPrediction.GetInteger(), clientPredictTime );
  717. }
  718. // adjust the client prediction time based on the snapshot time
  719. clientPrediction -= ( 1 - ( INTSIGNBITSET( aheadOfServer - idAsyncNetwork::clientPrediction.GetInteger() ) << 1 ) );
  720. clientPrediction = idMath::ClampInt( idAsyncNetwork::clientPrediction.GetInteger(), idAsyncNetwork::clientMaxPrediction.GetInteger(), clientPrediction );
  721. delta = gameTime - ( snapshotGameTime + clientPrediction );
  722. clientPredictTime -= ( delta / PREDICTION_FAST_ADJUST ) + ( 1 - ( INTSIGNBITSET( delta ) << 1 ) );
  723. lastSnapshotTime = clientTime;
  724. if ( idAsyncNetwork::verbose.GetInteger() == 2 ) {
  725. common->Printf( "received snapshot, gameInitId = %d, gameFrame = %d, gameTime = %d\n", gameInitId, gameFrame, gameTime );
  726. }
  727. if ( numDuplicatedUsercmds && ( idAsyncNetwork::verbose.GetInteger() == 2 ) ) {
  728. common->Printf( "server duplicated %d user commands before snapshot %d\n", numDuplicatedUsercmds, snapshotGameFrame );
  729. }
  730. break;
  731. }
  732. default: {
  733. common->Printf( "unknown unreliable server message %d\n", id );
  734. break;
  735. }
  736. }
  737. }
  738. /*
  739. ==================
  740. idAsyncClient::ProcessReliableMessagePure
  741. ==================
  742. */
  743. void idAsyncClient::ProcessReliableMessagePure( const idBitMsg &msg ) {
  744. idBitMsg outMsg;
  745. byte msgBuf[ MAX_MESSAGE_SIZE ];
  746. int inChecksums[ MAX_PURE_PAKS ];
  747. int i;
  748. int gamePakChecksum;
  749. int serverGameInitId;
  750. session->SetGUI( NULL, NULL );
  751. serverGameInitId = msg.ReadLong();
  752. if ( serverGameInitId != gameInitId ) {
  753. common->DPrintf( "ignoring pure server checksum from an outdated gameInitId (%d)\n", serverGameInitId );
  754. return;
  755. }
  756. if ( !ValidatePureServerChecksums( serverAddress, msg ) ) {
  757. return;
  758. }
  759. if ( idAsyncNetwork::verbose.GetInteger() ) {
  760. common->Printf( "received new pure server info. ExecuteMapChange and report back\n" );
  761. }
  762. // it is now ok to load the next map with updated pure checksums
  763. sessLocal.ExecuteMapChange( true );
  764. // upon receiving our pure list, the server will send us SCS_INGAME and we'll start getting snapshots
  765. fileSystem->GetPureServerChecksums( inChecksums, -1, &gamePakChecksum );
  766. outMsg.Init( msgBuf, sizeof( msgBuf ) );
  767. outMsg.WriteByte( CLIENT_RELIABLE_MESSAGE_PURE );
  768. outMsg.WriteLong( gameInitId );
  769. i = 0;
  770. while ( inChecksums[ i ] ) {
  771. outMsg.WriteLong( inChecksums[ i++ ] );
  772. }
  773. outMsg.WriteLong( 0 );
  774. outMsg.WriteLong( gamePakChecksum );
  775. if ( !channel.SendReliableMessage( outMsg ) ) {
  776. common->Error( "client->server reliable messages overflow\n" );
  777. }
  778. }
  779. /*
  780. ===============
  781. idAsyncClient::ReadLocalizedServerString
  782. ===============
  783. */
  784. void idAsyncClient::ReadLocalizedServerString( const idBitMsg &msg, char *out, int maxLen ) {
  785. msg.ReadString( out, maxLen );
  786. // look up localized string. if the message is not an #str_ format, we'll just get it back unchanged
  787. idStr::snPrintf( out, maxLen - 1, "%s", common->GetLanguageDict()->GetString( out ) );
  788. }
  789. /*
  790. ==================
  791. idAsyncClient::ProcessReliableServerMessages
  792. ==================
  793. */
  794. void idAsyncClient::ProcessReliableServerMessages( void ) {
  795. idBitMsg msg;
  796. byte msgBuf[MAX_MESSAGE_SIZE];
  797. byte id;
  798. msg.Init( msgBuf, sizeof( msgBuf ) );
  799. while ( channel.GetReliableMessage( msg ) ) {
  800. id = msg.ReadByte();
  801. switch( id ) {
  802. case SERVER_RELIABLE_MESSAGE_CLIENTINFO: {
  803. int clientNum;
  804. clientNum = msg.ReadByte();
  805. idDict &info = sessLocal.mapSpawnData.userInfo[ clientNum ];
  806. bool haveBase = ( msg.ReadBits( 1 ) != 0 );
  807. #if ID_CLIENTINFO_TAGS
  808. int checksum = info.Checksum();
  809. int srv_checksum = msg.ReadLong();
  810. if ( checksum != srv_checksum ) {
  811. common->DPrintf( "SERVER_RELIABLE_MESSAGE_CLIENTINFO %d (haveBase: %s): != checksums srv: 0x%x local: 0x%x\n", clientNum, haveBase ? "true" : "false", checksum, srv_checksum );
  812. info.Print();
  813. } else {
  814. common->DPrintf( "SERVER_RELIABLE_MESSAGE_CLIENTINFO %d (haveBase: %s): checksums ok 0x%x\n", clientNum, haveBase ? "true" : "false", checksum );
  815. }
  816. #endif
  817. if ( haveBase ) {
  818. msg.ReadDeltaDict( info, &info );
  819. } else {
  820. msg.ReadDeltaDict( info, NULL );
  821. }
  822. // server forces us to a different userinfo
  823. if ( clientNum == idAsyncClient::clientNum ) {
  824. common->DPrintf( "local user info modified by server\n" );
  825. cvarSystem->SetCVarsFromDict( info );
  826. cvarSystem->ClearModifiedFlags( CVAR_USERINFO ); // don't emit back
  827. }
  828. game->SetUserInfo( clientNum, info, true, false );
  829. break;
  830. }
  831. case SERVER_RELIABLE_MESSAGE_SYNCEDCVARS: {
  832. idDict &info = sessLocal.mapSpawnData.syncedCVars;
  833. msg.ReadDeltaDict( info, &info );
  834. cvarSystem->SetCVarsFromDict( info );
  835. if ( !idAsyncNetwork::allowCheats.GetBool() ) {
  836. cvarSystem->ResetFlaggedVariables( CVAR_CHEAT );
  837. }
  838. break;
  839. }
  840. case SERVER_RELIABLE_MESSAGE_PRINT: {
  841. char string[MAX_STRING_CHARS];
  842. msg.ReadString( string, MAX_STRING_CHARS );
  843. common->Printf( "%s\n", string );
  844. break;
  845. }
  846. case SERVER_RELIABLE_MESSAGE_DISCONNECT: {
  847. int clientNum;
  848. char string[MAX_STRING_CHARS];
  849. clientNum = msg.ReadLong( );
  850. ReadLocalizedServerString( msg, string, MAX_STRING_CHARS );
  851. if ( clientNum == idAsyncClient::clientNum ) {
  852. session->Stop();
  853. session->MessageBox( MSG_OK, string, common->GetLanguageDict()->GetString ( "#str_04319" ), true );
  854. session->StartMenu();
  855. } else {
  856. common->Printf( "client %d %s\n", clientNum, string );
  857. cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "addChatLine \"%s^0 %s\"", sessLocal.mapSpawnData.userInfo[ clientNum ].GetString( "ui_name" ), string ) );
  858. sessLocal.mapSpawnData.userInfo[ clientNum ].Clear();
  859. }
  860. break;
  861. }
  862. case SERVER_RELIABLE_MESSAGE_APPLYSNAPSHOT: {
  863. int sequence;
  864. sequence = msg.ReadLong();
  865. if ( !game->ClientApplySnapshot( clientNum, sequence ) ) {
  866. session->Stop();
  867. common->Error( "couldn't apply snapshot %d", sequence );
  868. }
  869. break;
  870. }
  871. case SERVER_RELIABLE_MESSAGE_PURE: {
  872. ProcessReliableMessagePure( msg );
  873. break;
  874. }
  875. case SERVER_RELIABLE_MESSAGE_RELOAD: {
  876. if ( idAsyncNetwork::verbose.GetBool() ) {
  877. common->Printf( "got MESSAGE_RELOAD from server\n" );
  878. }
  879. // simply reconnect, so that if the server restarts in pure mode we can get the right list and avoid spurious reloads
  880. cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "reconnect\n" );
  881. break;
  882. }
  883. case SERVER_RELIABLE_MESSAGE_ENTERGAME: {
  884. SendUserInfoToServer();
  885. game->SetUserInfo( clientNum, sessLocal.mapSpawnData.userInfo[ clientNum ], true, false );
  886. cvarSystem->ClearModifiedFlags( CVAR_USERINFO );
  887. break;
  888. }
  889. default: {
  890. // pass reliable message on to game code
  891. game->ClientProcessReliableMessage( clientNum, msg );
  892. break;
  893. }
  894. }
  895. }
  896. }
  897. /*
  898. ==================
  899. idAsyncClient::ProcessChallengeResponseMessage
  900. ==================
  901. */
  902. void idAsyncClient::ProcessChallengeResponseMessage( const netadr_t from, const idBitMsg &msg ) {
  903. char serverGame[ MAX_STRING_CHARS ], serverGameBase[ MAX_STRING_CHARS ];
  904. if ( clientState != CS_CHALLENGING ) {
  905. common->Printf( "Unwanted challenge response received.\n" );
  906. return;
  907. }
  908. serverChallenge = msg.ReadLong();
  909. serverId = msg.ReadShort();
  910. msg.ReadString( serverGameBase, MAX_STRING_CHARS );
  911. msg.ReadString( serverGame, MAX_STRING_CHARS );
  912. // the server is running a different game... we need to reload in the correct fs_game
  913. // even pure pak checks would fail if we didn't, as there are files we may not even see atm
  914. // NOTE: we could read the pure list from the server at the same time and set it up for the restart
  915. // ( if the client can restart directly with the right pak order, then we avoid an extra reloadEngine later.. )
  916. if ( idStr::Icmp( cvarSystem->GetCVarString( "fs_game_base" ), serverGameBase ) ||
  917. idStr::Icmp( cvarSystem->GetCVarString( "fs_game" ), serverGame ) ) {
  918. // bug #189 - if the server is running ROE and ROE is not locally installed, refuse to connect or we might crash
  919. if ( !fileSystem->HasD3XP() && ( !idStr::Icmp( serverGameBase, "d3xp" ) || !idStr::Icmp( serverGame, "d3xp" ) ) ) {
  920. common->Printf( "The server is running Doom3: Resurrection of Evil expansion pack. RoE is not installed on this client. Aborting the connection..\n" );
  921. cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "disconnect\n" );
  922. return;
  923. }
  924. common->Printf( "The server is running a different mod (%s-%s). Restarting..\n", serverGameBase, serverGame );
  925. cvarSystem->SetCVarString( "fs_game_base", serverGameBase );
  926. cvarSystem->SetCVarString( "fs_game", serverGame );
  927. cmdSystem->BufferCommandText( CMD_EXEC_NOW, "reloadEngine" );
  928. cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "reconnect\n" );
  929. return;
  930. }
  931. common->Printf( "received challenge response 0x%x from %s\n", serverChallenge, Sys_NetAdrToString( from ) );
  932. // start sending connect packets instead of challenge request packets
  933. clientState = CS_CONNECTING;
  934. lastConnectTime = -9999;
  935. // take this address as the new server address. This allows
  936. // a server proxy to hand off connections to multiple servers
  937. serverAddress = from;
  938. }
  939. /*
  940. ==================
  941. idAsyncClient::ProcessConnectResponseMessage
  942. ==================
  943. */
  944. void idAsyncClient::ProcessConnectResponseMessage( const netadr_t from, const idBitMsg &msg ) {
  945. int serverGameInitId, serverGameFrame, serverGameTime;
  946. idDict serverSI;
  947. if ( clientState >= CS_CONNECTED ) {
  948. common->Printf( "Duplicate connect received.\n" );
  949. return;
  950. }
  951. if ( clientState != CS_CONNECTING ) {
  952. common->Printf( "Connect response packet while not connecting.\n" );
  953. return;
  954. }
  955. if ( !Sys_CompareNetAdrBase( from, serverAddress ) ) {
  956. common->Printf( "Connect response from a different server.\n" );
  957. common->Printf( "%s should have been %s\n", Sys_NetAdrToString( from ), Sys_NetAdrToString( serverAddress ) );
  958. return;
  959. }
  960. common->Printf( "received connect response from %s\n", Sys_NetAdrToString( from ) );
  961. channel.Init( from, clientId );
  962. clientNum = msg.ReadLong();
  963. clientState = CS_CONNECTED;
  964. lastPacketTime = -9999;
  965. serverGameInitId = msg.ReadLong();
  966. serverGameFrame = msg.ReadLong();
  967. serverGameTime = msg.ReadLong();
  968. msg.ReadDeltaDict( serverSI, NULL );
  969. InitGame( serverGameInitId, serverGameFrame, serverGameTime, serverSI );
  970. // load map
  971. session->SetGUI( NULL, NULL );
  972. sessLocal.ExecuteMapChange();
  973. clientPredictTime = clientPrediction = idMath::ClampInt( 0, idAsyncNetwork::clientMaxPrediction.GetInteger(), clientTime - lastConnectTime );
  974. }
  975. /*
  976. ==================
  977. idAsyncClient::ProcessDisconnectMessage
  978. ==================
  979. */
  980. void idAsyncClient::ProcessDisconnectMessage( const netadr_t from, const idBitMsg &msg ) {
  981. if ( clientState == CS_DISCONNECTED ) {
  982. common->Printf( "Disconnect packet while not connected.\n" );
  983. return;
  984. }
  985. if ( !Sys_CompareNetAdrBase( from, serverAddress ) ) {
  986. common->Printf( "Disconnect packet from unknown server.\n" );
  987. return;
  988. }
  989. session->Stop();
  990. session->MessageBox( MSG_OK, common->GetLanguageDict()->GetString ( "#str_04320" ), NULL, true );
  991. session->StartMenu();
  992. }
  993. /*
  994. ==================
  995. idAsyncClient::ProcessInfoResponseMessage
  996. ==================
  997. */
  998. void idAsyncClient::ProcessInfoResponseMessage( const netadr_t from, const idBitMsg &msg ) {
  999. int i, protocol, index;
  1000. networkServer_t serverInfo;
  1001. bool verbose = false;
  1002. if ( from.type == NA_LOOPBACK || cvarSystem->GetCVarBool( "developer" ) ) {
  1003. verbose = true;
  1004. }
  1005. serverInfo.clients = 0;
  1006. serverInfo.adr = from;
  1007. serverInfo.challenge = msg.ReadLong(); // challenge
  1008. protocol = msg.ReadLong();
  1009. if ( protocol != ASYNC_PROTOCOL_VERSION ) {
  1010. 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 );
  1011. return;
  1012. }
  1013. msg.ReadDeltaDict( serverInfo.serverInfo, NULL );
  1014. if ( verbose ) {
  1015. common->Printf( "server IP = %s\n", Sys_NetAdrToString( serverInfo.adr ) );
  1016. serverInfo.serverInfo.Print();
  1017. }
  1018. for ( i = msg.ReadByte(); i < MAX_ASYNC_CLIENTS; i = msg.ReadByte() ) {
  1019. serverInfo.pings[ serverInfo.clients ] = msg.ReadShort();
  1020. serverInfo.rate[ serverInfo.clients ] = msg.ReadLong();
  1021. msg.ReadString( serverInfo.nickname[ serverInfo.clients ], MAX_NICKLEN );
  1022. if ( verbose ) {
  1023. common->Printf( "client %2d: %s, ping = %d, rate = %d\n", i, serverInfo.nickname[ serverInfo.clients ], serverInfo.pings[ serverInfo.clients ], serverInfo.rate[ serverInfo.clients ] );
  1024. }
  1025. serverInfo.clients++;
  1026. }
  1027. serverInfo.OSMask = msg.ReadLong();
  1028. index = serverList.InfoResponse( serverInfo );
  1029. common->Printf( "%d: server %s - protocol %d.%d - %s\n", index, Sys_NetAdrToString( serverInfo.adr ), protocol >> 16, protocol & 0xffff, serverInfo.serverInfo.GetString( "si_name" ) );
  1030. }
  1031. /*
  1032. ==================
  1033. idAsyncClient::ProcessPrintMessage
  1034. ==================
  1035. */
  1036. void idAsyncClient::ProcessPrintMessage( const netadr_t from, const idBitMsg &msg ) {
  1037. char string[ MAX_STRING_CHARS ];
  1038. int opcode;
  1039. int game_opcode = ALLOW_YES;
  1040. const char *retpass;
  1041. opcode = msg.ReadLong();
  1042. if ( opcode == SERVER_PRINT_GAMEDENY ) {
  1043. game_opcode = msg.ReadLong();
  1044. }
  1045. ReadLocalizedServerString( msg, string, MAX_STRING_CHARS );
  1046. common->Printf( "%s\n", string );
  1047. guiNetMenu->SetStateString( "status", string );
  1048. if ( opcode == SERVER_PRINT_GAMEDENY ) {
  1049. if ( game_opcode == ALLOW_BADPASS ) {
  1050. retpass = session->MessageBox( MSG_PROMPT, common->GetLanguageDict()->GetString ( "#str_04321" ), string, true, "passprompt_ok" );
  1051. ClearPendingPackets();
  1052. guiNetMenu->SetStateString( "status", common->GetLanguageDict()->GetString ( "#str_04322" ));
  1053. if ( retpass ) {
  1054. // #790
  1055. cvarSystem->SetCVarString( "password", "" );
  1056. cvarSystem->SetCVarString( "password", retpass );
  1057. } else {
  1058. cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
  1059. }
  1060. } else if ( game_opcode == ALLOW_NO ) {
  1061. session->MessageBox( MSG_OK, string, common->GetLanguageDict()->GetString ( "#str_04323" ), true );
  1062. ClearPendingPackets();
  1063. cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
  1064. }
  1065. // ALLOW_NOTYET just keeps running as usual. The GUI has an abort button
  1066. } else if ( opcode == SERVER_PRINT_BADCHALLENGE && clientState >= CS_CONNECTING ) {
  1067. cmdSystem->BufferCommandText( CMD_EXEC_NOW, "reconnect" );
  1068. }
  1069. }
  1070. /*
  1071. ==================
  1072. idAsyncClient::ProcessServersListMessage
  1073. ==================
  1074. */
  1075. void idAsyncClient::ProcessServersListMessage( const netadr_t from, const idBitMsg &msg ) {
  1076. if ( !Sys_CompareNetAdrBase( idAsyncNetwork::GetMasterAddress(), from ) ) {
  1077. common->DPrintf( "received a server list from %s - not a valid master\n", Sys_NetAdrToString( from ) );
  1078. return;
  1079. }
  1080. while ( msg.GetRemaingData() ) {
  1081. int a,b,c,d;
  1082. a = msg.ReadByte(); b = msg.ReadByte(); c = msg.ReadByte(); d = msg.ReadByte();
  1083. serverList.AddServer( serverList.Num(), va( "%i.%i.%i.%i:%i", a, b, c, d, msg.ReadShort() ) );
  1084. }
  1085. }
  1086. /*
  1087. ==================
  1088. idAsyncClient::ProcessAuthKeyMessage
  1089. ==================
  1090. */
  1091. void idAsyncClient::ProcessAuthKeyMessage( const netadr_t from, const idBitMsg &msg ) {
  1092. authKeyMsg_t authMsg;
  1093. char read_string[ MAX_STRING_CHARS ];
  1094. const char *retkey;
  1095. authBadKeyStatus_t authBadStatus;
  1096. int key_index;
  1097. bool valid[ 2 ];
  1098. idStr auth_msg;
  1099. if ( clientState != CS_CONNECTING && !session->WaitingForGameAuth() ) {
  1100. common->Printf( "clientState != CS_CONNECTING, not waiting for game auth, authKey ignored\n" );
  1101. return;
  1102. }
  1103. authMsg = (authKeyMsg_t)msg.ReadByte();
  1104. if ( authMsg == AUTHKEY_BADKEY ) {
  1105. valid[ 0 ] = valid[ 1 ] = true;
  1106. key_index = 0;
  1107. authBadStatus = (authBadKeyStatus_t)msg.ReadByte();
  1108. switch ( authBadStatus ) {
  1109. case AUTHKEY_BAD_INVALID:
  1110. valid[ 0 ] = ( msg.ReadByte() == 1 );
  1111. valid[ 1 ] = ( msg.ReadByte() == 1 );
  1112. idAsyncNetwork::BuildInvalidKeyMsg( auth_msg, valid );
  1113. break;
  1114. case AUTHKEY_BAD_BANNED:
  1115. key_index = msg.ReadByte();
  1116. auth_msg = common->GetLanguageDict()->GetString( va( "#str_0719%1d", 6 + key_index ) );
  1117. auth_msg += "\n";
  1118. auth_msg += common->GetLanguageDict()->GetString( "#str_04304" );
  1119. valid[ key_index ] = false;
  1120. break;
  1121. case AUTHKEY_BAD_INUSE:
  1122. key_index = msg.ReadByte();
  1123. auth_msg = common->GetLanguageDict()->GetString( va( "#str_0719%1d", 8 + key_index ) );
  1124. auth_msg += "\n";
  1125. auth_msg += common->GetLanguageDict()->GetString( "#str_04304" );
  1126. valid[ key_index ] = false;
  1127. break;
  1128. case AUTHKEY_BAD_MSG:
  1129. // a general message explaining why this key is denied
  1130. // no specific use for this atm. let's not clear the keys either
  1131. msg.ReadString( read_string, MAX_STRING_CHARS );
  1132. auth_msg = read_string;
  1133. break;
  1134. }
  1135. common->DPrintf( "auth deny: %s\n", auth_msg.c_str() );
  1136. // keys to be cleared. applies to both net connect and game auth
  1137. session->ClearCDKey( valid );
  1138. // get rid of the bad key - at least that's gonna annoy people who stole a fake key
  1139. if ( clientState == CS_CONNECTING ) {
  1140. while ( 1 ) {
  1141. // here we use the auth status message
  1142. retkey = session->MessageBox( MSG_CDKEY, auth_msg, common->GetLanguageDict()->GetString( "#str_04325" ), true );
  1143. if ( retkey ) {
  1144. if ( session->CheckKey( retkey, true, valid ) ) {
  1145. cmdSystem->BufferCommandText( CMD_EXEC_NOW, "reconnect" );
  1146. } else {
  1147. // build a more precise message about the offline check failure
  1148. idAsyncNetwork::BuildInvalidKeyMsg( auth_msg, valid );
  1149. session->MessageBox( MSG_OK, auth_msg.c_str(), common->GetLanguageDict()->GetString( "#str_04327" ), true );
  1150. continue;
  1151. }
  1152. } else {
  1153. cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
  1154. }
  1155. break;
  1156. }
  1157. } else {
  1158. // forward the auth status information to the session code
  1159. session->CDKeysAuthReply( false, auth_msg );
  1160. }
  1161. } else {
  1162. msg.ReadString( read_string, MAX_STRING_CHARS );
  1163. cvarSystem->SetCVarString( "com_guid", read_string );
  1164. common->Printf( "guid set to %s\n", read_string );
  1165. session->CDKeysAuthReply( true, NULL );
  1166. }
  1167. }
  1168. /*
  1169. ==================
  1170. idAsyncClient::ProcessVersionMessage
  1171. ==================
  1172. */
  1173. void idAsyncClient::ProcessVersionMessage( const netadr_t from, const idBitMsg &msg ) {
  1174. char string[ MAX_STRING_CHARS ];
  1175. if ( updateState != UPDATE_SENT ) {
  1176. common->Printf( "ProcessVersionMessage: version reply, != UPDATE_SENT\n" );
  1177. return;
  1178. }
  1179. common->Printf( "A new version is available\n" );
  1180. msg.ReadString( string, MAX_STRING_CHARS );
  1181. updateMSG = string;
  1182. updateDirectDownload = ( msg.ReadByte() != 0 );
  1183. msg.ReadString( string, MAX_STRING_CHARS );
  1184. updateURL = string;
  1185. updateMime = (dlMime_t)msg.ReadByte();
  1186. msg.ReadString( string, MAX_STRING_CHARS );
  1187. updateFallback = string;
  1188. updateState = UPDATE_READY;
  1189. }
  1190. /*
  1191. ==================
  1192. idAsyncClient::ValidatePureServerChecksums
  1193. ==================
  1194. */
  1195. bool idAsyncClient::ValidatePureServerChecksums( const netadr_t from, const idBitMsg &msg ) {
  1196. int i, numChecksums, numMissingChecksums;
  1197. int inChecksums[ MAX_PURE_PAKS ];
  1198. int inGamePakChecksum;
  1199. int missingChecksums[ MAX_PURE_PAKS ];
  1200. int missingGamePakChecksum;
  1201. idBitMsg dlmsg;
  1202. byte msgBuf[MAX_MESSAGE_SIZE];
  1203. // read checksums
  1204. // pak checksums, in a 0-terminated list
  1205. numChecksums = 0;
  1206. do {
  1207. i = msg.ReadLong( );
  1208. inChecksums[ numChecksums++ ] = i;
  1209. // just to make sure a broken message doesn't crash us
  1210. if ( numChecksums >= MAX_PURE_PAKS ) {
  1211. common->Warning( "MAX_PURE_PAKS ( %d ) exceeded in idAsyncClient::ProcessPureMessage\n", MAX_PURE_PAKS );
  1212. return false;
  1213. }
  1214. } while ( i );
  1215. inChecksums[ numChecksums ] = 0;
  1216. inGamePakChecksum = msg.ReadLong();
  1217. fsPureReply_t reply = fileSystem->SetPureServerChecksums( inChecksums, inGamePakChecksum, missingChecksums, &missingGamePakChecksum );
  1218. switch ( reply ) {
  1219. case PURE_RESTART:
  1220. // need to restart the filesystem with a different pure configuration
  1221. cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
  1222. // restart with the right FS configuration and get back to the server
  1223. clientState = CS_PURERESTART;
  1224. fileSystem->SetRestartChecksums( inChecksums, inGamePakChecksum );
  1225. cmdSystem->BufferCommandText( CMD_EXEC_NOW, "reloadEngine" );
  1226. return false;
  1227. case PURE_MISSING: {
  1228. idStr checksums;
  1229. i = 0;
  1230. while ( missingChecksums[ i ] ) {
  1231. checksums += va( "0x%x ", missingChecksums[ i++ ] );
  1232. }
  1233. numMissingChecksums = i;
  1234. if ( idAsyncNetwork::clientDownload.GetInteger() == 0 ) {
  1235. // never any downloads
  1236. idStr message = va( common->GetLanguageDict()->GetString( "#str_07210" ), Sys_NetAdrToString( from ) );
  1237. if ( numMissingChecksums > 0 ) {
  1238. message += va( common->GetLanguageDict()->GetString( "#str_06751" ), numMissingChecksums, checksums.c_str() );
  1239. }
  1240. if ( missingGamePakChecksum ) {
  1241. message += va( common->GetLanguageDict()->GetString( "#str_06750" ), missingGamePakChecksum );
  1242. }
  1243. common->Printf( message );
  1244. cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
  1245. session->MessageBox( MSG_OK, message, common->GetLanguageDict()->GetString( "#str_06735" ), true );
  1246. } else {
  1247. if ( clientState >= CS_CONNECTED ) {
  1248. // we are already connected, reconnect to negociate the paks in connectionless mode
  1249. cmdSystem->BufferCommandText( CMD_EXEC_NOW, "reconnect" );
  1250. return false;
  1251. }
  1252. // ask the server to send back download info
  1253. common->DPrintf( "missing %d paks: %s\n", numMissingChecksums + ( missingGamePakChecksum ? 1 : 0 ), checksums.c_str() );
  1254. if ( missingGamePakChecksum ) {
  1255. common->DPrintf( "game code pak: 0x%x\n", missingGamePakChecksum );
  1256. }
  1257. // store the requested downloads
  1258. GetDownloadRequest( missingChecksums, numMissingChecksums, missingGamePakChecksum );
  1259. // build the download request message
  1260. // NOTE: in a specific function?
  1261. dlmsg.Init( msgBuf, sizeof( msgBuf ) );
  1262. dlmsg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
  1263. dlmsg.WriteString( "downloadRequest" );
  1264. dlmsg.WriteLong( serverChallenge );
  1265. dlmsg.WriteShort( clientId );
  1266. // used to make sure the server replies to the same download request
  1267. dlmsg.WriteLong( dlRequest );
  1268. // special case the code pak - if we have a 0 checksum then we don't need to download it
  1269. dlmsg.WriteLong( missingGamePakChecksum );
  1270. // 0-terminated list of missing paks
  1271. i = 0;
  1272. while ( missingChecksums[ i ] ) {
  1273. dlmsg.WriteLong( missingChecksums[ i++ ] );
  1274. }
  1275. dlmsg.WriteLong( 0 );
  1276. clientPort.SendPacket( from, dlmsg.GetData(), dlmsg.GetSize() );
  1277. }
  1278. return false;
  1279. }
  1280. case PURE_NODLL:
  1281. common->Printf( common->GetLanguageDict()->GetString( "#str_07211" ), Sys_NetAdrToString( from ) );
  1282. cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
  1283. return false;
  1284. default:
  1285. return true;
  1286. }
  1287. return true;
  1288. }
  1289. /*
  1290. ==================
  1291. idAsyncClient::ProcessPureMessage
  1292. ==================
  1293. */
  1294. void idAsyncClient::ProcessPureMessage( const netadr_t from, const idBitMsg &msg ) {
  1295. idBitMsg outMsg;
  1296. byte msgBuf[ MAX_MESSAGE_SIZE ];
  1297. int i;
  1298. int inChecksums[ MAX_PURE_PAKS ];
  1299. int gamePakChecksum;
  1300. if ( clientState != CS_CONNECTING ) {
  1301. common->Printf( "clientState != CS_CONNECTING, pure msg ignored\n" );
  1302. return;
  1303. }
  1304. if ( !ValidatePureServerChecksums( from, msg ) ) {
  1305. return;
  1306. }
  1307. fileSystem->GetPureServerChecksums( inChecksums, -1, &gamePakChecksum );
  1308. outMsg.Init( msgBuf, sizeof( msgBuf ) );
  1309. outMsg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
  1310. outMsg.WriteString( "pureClient" );
  1311. outMsg.WriteLong( serverChallenge );
  1312. outMsg.WriteShort( clientId );
  1313. i = 0;
  1314. while ( inChecksums[ i ] ) {
  1315. outMsg.WriteLong( inChecksums[ i++ ] );
  1316. }
  1317. outMsg.WriteLong( 0 );
  1318. outMsg.WriteLong( gamePakChecksum );
  1319. clientPort.SendPacket( from, outMsg.GetData(), outMsg.GetSize() );
  1320. }
  1321. /*
  1322. ==================
  1323. idAsyncClient::ConnectionlessMessage
  1324. ==================
  1325. */
  1326. void idAsyncClient::ConnectionlessMessage( const netadr_t from, const idBitMsg &msg ) {
  1327. char string[MAX_STRING_CHARS*2]; // M. Quinn - Even Balance - PB packets can go beyond 1024
  1328. msg.ReadString( string, sizeof( string ) );
  1329. // info response from a server, are accepted from any source
  1330. if ( idStr::Icmp( string, "infoResponse" ) == 0 ) {
  1331. ProcessInfoResponseMessage( from, msg );
  1332. return;
  1333. }
  1334. // from master server:
  1335. if ( Sys_CompareNetAdrBase( from, idAsyncNetwork::GetMasterAddress( ) ) ) {
  1336. // server list
  1337. if ( idStr::Icmp( string, "servers" ) == 0 ) {
  1338. ProcessServersListMessage( from, msg );
  1339. return;
  1340. }
  1341. if ( idStr::Icmp( string, "authKey" ) == 0 ) {
  1342. ProcessAuthKeyMessage( from, msg );
  1343. return;
  1344. }
  1345. if ( idStr::Icmp( string, "newVersion" ) == 0 ) {
  1346. ProcessVersionMessage( from, msg );
  1347. return;
  1348. }
  1349. }
  1350. // ignore if not from the current/last server
  1351. if ( !Sys_CompareNetAdrBase( from, serverAddress ) && ( lastRconTime + 10000 < realTime || !Sys_CompareNetAdrBase( from, lastRconAddress ) ) ) {
  1352. common->DPrintf( "got message '%s' from bad source: %s\n", string, Sys_NetAdrToString( from ) );
  1353. return;
  1354. }
  1355. // challenge response from the server we are connecting to
  1356. if ( idStr::Icmp( string, "challengeResponse" ) == 0 ) {
  1357. ProcessChallengeResponseMessage( from, msg );
  1358. return;
  1359. }
  1360. // connect response from the server we are connecting to
  1361. if ( idStr::Icmp( string, "connectResponse" ) == 0 ) {
  1362. ProcessConnectResponseMessage( from, msg );
  1363. return;
  1364. }
  1365. // a disconnect message from the server, which will happen if the server
  1366. // dropped the connection but is still getting packets from this client
  1367. if ( idStr::Icmp( string, "disconnect" ) == 0 ) {
  1368. ProcessDisconnectMessage( from, msg );
  1369. return;
  1370. }
  1371. // print request from server
  1372. if ( idStr::Icmp( string, "print" ) == 0 ) {
  1373. ProcessPrintMessage( from, msg );
  1374. return;
  1375. }
  1376. // server pure list
  1377. if ( idStr::Icmp( string, "pureServer" ) == 0 ) {
  1378. ProcessPureMessage( from, msg );
  1379. return;
  1380. }
  1381. if ( idStr::Icmp( string, "downloadInfo" ) == 0 ) {
  1382. ProcessDownloadInfoMessage( from, msg );
  1383. }
  1384. if ( idStr::Icmp( string, "authrequired" ) == 0 ) {
  1385. // server telling us that he's expecting an auth mode connect, just in case we're trying to connect in LAN mode
  1386. if ( idAsyncNetwork::LANServer.GetBool() ) {
  1387. common->Warning( "server %s requests master authorization for this client. Turning off LAN mode\n", Sys_NetAdrToString( from ) );
  1388. idAsyncNetwork::LANServer.SetBool( false );
  1389. }
  1390. }
  1391. common->DPrintf( "ignored message from %s: %s\n", Sys_NetAdrToString( from ), string );
  1392. }
  1393. /*
  1394. =================
  1395. idAsyncClient::ProcessMessage
  1396. =================
  1397. */
  1398. void idAsyncClient::ProcessMessage( const netadr_t from, idBitMsg &msg ) {
  1399. int id;
  1400. id = msg.ReadShort();
  1401. // check for a connectionless packet
  1402. if ( id == CONNECTIONLESS_MESSAGE_ID ) {
  1403. ConnectionlessMessage( from, msg );
  1404. return;
  1405. }
  1406. if ( clientState < CS_CONNECTED ) {
  1407. return; // can't be a valid sequenced packet
  1408. }
  1409. if ( msg.GetRemaingData() < 4 ) {
  1410. common->DPrintf( "%s: tiny packet\n", Sys_NetAdrToString( from ) );
  1411. return;
  1412. }
  1413. // is this a packet from the server
  1414. if ( !Sys_CompareNetAdrBase( from, channel.GetRemoteAddress() ) || id != serverId ) {
  1415. common->DPrintf( "%s: sequenced server packet without connection\n", Sys_NetAdrToString( from ) );
  1416. return;
  1417. }
  1418. if ( !channel.Process( from, clientTime, msg, serverMessageSequence ) ) {
  1419. return; // out of order, duplicated, fragment, etc.
  1420. }
  1421. lastPacketTime = clientTime;
  1422. ProcessReliableServerMessages();
  1423. ProcessUnreliableServerMessage( msg );
  1424. }
  1425. /*
  1426. ==================
  1427. idAsyncClient::SetupConnection
  1428. ==================
  1429. */
  1430. void idAsyncClient::SetupConnection( void ) {
  1431. idBitMsg msg;
  1432. byte msgBuf[MAX_MESSAGE_SIZE];
  1433. if ( clientTime - lastConnectTime < SETUP_CONNECTION_RESEND_TIME ) {
  1434. return;
  1435. }
  1436. if ( clientState == CS_CHALLENGING ) {
  1437. common->Printf( "sending challenge to %s\n", Sys_NetAdrToString( serverAddress ) );
  1438. msg.Init( msgBuf, sizeof( msgBuf ) );
  1439. msg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
  1440. msg.WriteString( "challenge" );
  1441. msg.WriteLong( clientId );
  1442. clientPort.SendPacket( serverAddress, msg.GetData(), msg.GetSize() );
  1443. } else if ( clientState == CS_CONNECTING ) {
  1444. common->Printf( "sending connect to %s with challenge 0x%x\n", Sys_NetAdrToString( serverAddress ), serverChallenge );
  1445. msg.Init( msgBuf, sizeof( msgBuf ) );
  1446. msg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
  1447. msg.WriteString( "connect" );
  1448. msg.WriteLong( ASYNC_PROTOCOL_VERSION );
  1449. #if ID_FAKE_PURE
  1450. // fake win32 OS - might need to adapt depending on the case
  1451. msg.WriteShort( 0 );
  1452. #else
  1453. msg.WriteShort( BUILD_OS_ID );
  1454. #endif
  1455. msg.WriteLong( clientDataChecksum );
  1456. msg.WriteLong( serverChallenge );
  1457. msg.WriteShort( clientId );
  1458. msg.WriteLong( cvarSystem->GetCVarInteger( "net_clientMaxRate" ) );
  1459. msg.WriteString( cvarSystem->GetCVarString( "com_guid" ) );
  1460. msg.WriteString( cvarSystem->GetCVarString( "password" ), -1, false );
  1461. // do not make the protocol depend on PB
  1462. msg.WriteShort( 0 );
  1463. clientPort.SendPacket( serverAddress, msg.GetData(), msg.GetSize() );
  1464. if ( idAsyncNetwork::LANServer.GetBool() ) {
  1465. common->Printf( "net_LANServer is set, connecting in LAN mode\n" );
  1466. } else {
  1467. // emit a cd key authorization request
  1468. // modified at protocol 1.37 for XP key addition
  1469. msg.BeginWriting();
  1470. msg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
  1471. msg.WriteString( "clAuth" );
  1472. msg.WriteLong( ASYNC_PROTOCOL_VERSION );
  1473. msg.WriteNetadr( serverAddress );
  1474. // if we don't have a com_guid, this will request a direct reply from auth with it
  1475. msg.WriteByte( cvarSystem->GetCVarString( "com_guid" )[0] ? 1 : 0 );
  1476. // send the main key, and flag an extra byte to add XP key
  1477. msg.WriteString( session->GetCDKey( false ) );
  1478. const char *xpkey = session->GetCDKey( true );
  1479. msg.WriteByte( xpkey ? 1 : 0 );
  1480. if ( xpkey ) {
  1481. msg.WriteString( xpkey );
  1482. }
  1483. clientPort.SendPacket( idAsyncNetwork::GetMasterAddress(), msg.GetData(), msg.GetSize() );
  1484. }
  1485. } else {
  1486. return;
  1487. }
  1488. lastConnectTime = clientTime;
  1489. }
  1490. /*
  1491. ==================
  1492. idAsyncClient::SendReliableGameMessage
  1493. ==================
  1494. */
  1495. void idAsyncClient::SendReliableGameMessage( const idBitMsg &msg ) {
  1496. idBitMsg outMsg;
  1497. byte msgBuf[MAX_MESSAGE_SIZE];
  1498. if ( clientState < CS_INGAME ) {
  1499. return;
  1500. }
  1501. outMsg.Init( msgBuf, sizeof( msgBuf ) );
  1502. outMsg.WriteByte( CLIENT_RELIABLE_MESSAGE_GAME );
  1503. outMsg.WriteData( msg.GetData(), msg.GetSize() );
  1504. if ( !channel.SendReliableMessage( outMsg ) ) {
  1505. common->Error( "client->server reliable messages overflow\n" );
  1506. }
  1507. }
  1508. /*
  1509. ==================
  1510. idAsyncClient::Idle
  1511. ==================
  1512. */
  1513. void idAsyncClient::Idle( void ) {
  1514. // also need to read mouse for the connecting guis
  1515. usercmdGen->GetDirectUsercmd();
  1516. SendEmptyToServer();
  1517. }
  1518. /*
  1519. ==================
  1520. idAsyncClient::UpdateTime
  1521. ==================
  1522. */
  1523. int idAsyncClient::UpdateTime( int clamp ) {
  1524. int time, msec;
  1525. time = Sys_Milliseconds();
  1526. msec = idMath::ClampInt( 0, clamp, time - realTime );
  1527. realTime = time;
  1528. clientTime += msec;
  1529. return msec;
  1530. }
  1531. /*
  1532. ==================
  1533. idAsyncClient::RunFrame
  1534. ==================
  1535. */
  1536. void idAsyncClient::RunFrame( void ) {
  1537. int msec, size;
  1538. bool newPacket;
  1539. idBitMsg msg;
  1540. byte msgBuf[MAX_MESSAGE_SIZE];
  1541. netadr_t from;
  1542. msec = UpdateTime( 100 );
  1543. //bc makes typing work, but screws up a lot of things. this is a horrifying hack.
  1544. if (cvarSystem->GetCVarBool("initdeck") == true)
  1545. {
  1546. cvarSystem->SetCVarBool("initdeck", false, 0);
  1547. bool lastPredictFrame = ( snapshotGameFrame + 1 >= gameFrame && gameTimeResidual + clientPredictTime < USERCMD_MSEC );
  1548. gameReturn_t ret = game->ClientPrediction( clientNum, userCmds[ snapshotGameFrame & ( MAX_USERCMD_BACKUP - 1 ) ], lastPredictFrame );
  1549. idAsyncNetwork::ExecuteSessionCommand( ret.sessionCommand );
  1550. }
  1551. if ( !clientPort.GetPort() )
  1552. {
  1553. return; //BC single player exits here.
  1554. }
  1555. // handle ongoing pk4 downloads and patch downloads
  1556. HandleDownloads();
  1557. gameTimeResidual += msec;
  1558. // spin in place processing incoming packets until enough time lapsed to run a new game frame
  1559. do {
  1560. do {
  1561. // blocking read with game time residual timeout
  1562. newPacket = clientPort.GetPacketBlocking( from, msgBuf, size, sizeof( msgBuf ), USERCMD_MSEC - ( gameTimeResidual + clientPredictTime ) - 1 );
  1563. if ( newPacket ) {
  1564. msg.Init( msgBuf, sizeof( msgBuf ) );
  1565. msg.SetSize( size );
  1566. msg.BeginReading();
  1567. ProcessMessage( from, msg );
  1568. }
  1569. msec = UpdateTime( 100 );
  1570. gameTimeResidual += msec;
  1571. } while( newPacket );
  1572. } while( gameTimeResidual + clientPredictTime < USERCMD_MSEC );
  1573. // update server list
  1574. serverList.RunFrame();
  1575. if ( clientState == CS_DISCONNECTED ) {
  1576. usercmdGen->GetDirectUsercmd();
  1577. gameTimeResidual = USERCMD_MSEC - 1;
  1578. clientPredictTime = 0;
  1579. return;
  1580. }
  1581. if ( clientState == CS_PURERESTART ) {
  1582. clientState = CS_DISCONNECTED;
  1583. Reconnect();
  1584. gameTimeResidual = USERCMD_MSEC - 1;
  1585. clientPredictTime = 0;
  1586. return;
  1587. }
  1588. // if not connected setup a connection
  1589. if ( clientState < CS_CONNECTED ) {
  1590. // also need to read mouse for the connecting guis
  1591. usercmdGen->GetDirectUsercmd();
  1592. SetupConnection();
  1593. gameTimeResidual = USERCMD_MSEC - 1;
  1594. clientPredictTime = 0;
  1595. return;
  1596. }
  1597. if ( CheckTimeout() ) {
  1598. return;
  1599. }
  1600. // if not yet in the game send empty messages to keep data flowing through the channel
  1601. if ( clientState < CS_INGAME ) {
  1602. Idle();
  1603. gameTimeResidual = 0;
  1604. return;
  1605. }
  1606. // check for user info changes
  1607. if ( cvarSystem->GetModifiedFlags() & CVAR_USERINFO ) {
  1608. game->ThrottleUserInfo( );
  1609. SendUserInfoToServer( );
  1610. game->SetUserInfo( clientNum, sessLocal.mapSpawnData.userInfo[ clientNum ], true, false );
  1611. cvarSystem->ClearModifiedFlags( CVAR_USERINFO );
  1612. }
  1613. if ( gameTimeResidual + clientPredictTime >= USERCMD_MSEC ) {
  1614. lastFrameDelta = 0;
  1615. }
  1616. // generate user commands for the predicted time
  1617. while ( gameTimeResidual + clientPredictTime >= USERCMD_MSEC ) {
  1618. // send the user commands of this client to the server
  1619. SendUsercmdsToServer();
  1620. // update time
  1621. gameFrame++;
  1622. gameTime += USERCMD_MSEC;
  1623. gameTimeResidual -= USERCMD_MSEC;
  1624. // run from the snapshot up to the local game frame
  1625. while ( snapshotGameFrame < gameFrame ) {
  1626. lastFrameDelta++;
  1627. // duplicate usercmds for clients if no new ones are available
  1628. DuplicateUsercmds( snapshotGameFrame, snapshotGameTime );
  1629. // indicate the last prediction frame before a render
  1630. bool lastPredictFrame = ( snapshotGameFrame + 1 >= gameFrame && gameTimeResidual + clientPredictTime < USERCMD_MSEC );
  1631. // run client prediction
  1632. gameReturn_t ret = game->ClientPrediction( clientNum, userCmds[ snapshotGameFrame & ( MAX_USERCMD_BACKUP - 1 ) ], lastPredictFrame );
  1633. idAsyncNetwork::ExecuteSessionCommand( ret.sessionCommand );
  1634. snapshotGameFrame++;
  1635. snapshotGameTime += USERCMD_MSEC;
  1636. }
  1637. }
  1638. }
  1639. /*
  1640. ==================
  1641. idAsyncClient::PacifierUpdate
  1642. ==================
  1643. */
  1644. void idAsyncClient::PacifierUpdate( void ) {
  1645. if ( !IsActive() ) {
  1646. return;
  1647. }
  1648. realTime = Sys_Milliseconds();
  1649. SendEmptyToServer( false, true );
  1650. }
  1651. void idAsyncClient::SendBugreport(const char *text)
  1652. {
  1653. //common->Printf("hi hi\n");
  1654. //sys->OpenURL( "http://blendogames.com/qc/reportabug.php", false );
  1655. //bc use doom3's file download system to send qc bug reports.
  1656. backgroundDownload.completed = false;
  1657. backgroundDownload.opcode = DLTYPE_URL;
  1658. backgroundDownload.f = NULL;
  1659. backgroundDownload.url.status = DL_WAIT;
  1660. backgroundDownload.url.dlnow = 0;
  1661. backgroundDownload.url.dltotal = 0;
  1662. backgroundDownload.url.url = va("http://blendogames.com/qc/reportabug.php?text=%s", text);
  1663. fileSystem->BackgroundDownload( &backgroundDownload );
  1664. }
  1665. /*
  1666. ==================
  1667. idAsyncClient::SendVersionCheck
  1668. ==================
  1669. */
  1670. void idAsyncClient::SendVersionCheck( bool fromMenu ) {
  1671. idBitMsg msg;
  1672. byte msgBuf[MAX_MESSAGE_SIZE];
  1673. if ( updateState != UPDATE_NONE && !fromMenu ) {
  1674. common->DPrintf( "up-to-date check was already performed\n" );
  1675. return;
  1676. }
  1677. InitPort();
  1678. msg.Init( msgBuf, sizeof( msgBuf ) );
  1679. msg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
  1680. msg.WriteString( "versionCheck" );
  1681. msg.WriteLong( ASYNC_PROTOCOL_VERSION );
  1682. msg.WriteShort( BUILD_OS_ID );
  1683. msg.WriteString( cvarSystem->GetCVarString( "si_version" ) );
  1684. msg.WriteString( cvarSystem->GetCVarString( "com_guid" ) );
  1685. clientPort.SendPacket( idAsyncNetwork::GetMasterAddress(), msg.GetData(), msg.GetSize() );
  1686. common->DPrintf( "sent a version check request\n" );
  1687. updateState = UPDATE_SENT;
  1688. updateSentTime = clientTime;
  1689. showUpdateMessage = fromMenu;
  1690. }
  1691. /*
  1692. ==================
  1693. idAsyncClient::SendVersionDLUpdate
  1694. sending those packets is not strictly necessary. just a way to tell the update server
  1695. about what is going on. allows the update server to have a more precise view of the overall
  1696. network load for the updates
  1697. ==================
  1698. */
  1699. void idAsyncClient::SendVersionDLUpdate( int state ) {
  1700. idBitMsg msg;
  1701. byte msgBuf[MAX_MESSAGE_SIZE];
  1702. msg.Init( msgBuf, sizeof( msgBuf ) );
  1703. msg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
  1704. msg.WriteString( "versionDL" );
  1705. msg.WriteLong( ASYNC_PROTOCOL_VERSION );
  1706. msg.WriteShort( state );
  1707. clientPort.SendPacket( idAsyncNetwork::GetMasterAddress(), msg.GetData(), msg.GetSize() );
  1708. }
  1709. /*
  1710. ==================
  1711. idAsyncClient::HandleDownloads
  1712. ==================
  1713. */
  1714. void idAsyncClient::HandleDownloads( void ) {
  1715. if ( updateState == UPDATE_SENT && clientTime > updateSentTime + 2000 ) {
  1716. // timing out on no reply
  1717. updateState = UPDATE_DONE;
  1718. if ( showUpdateMessage ) {
  1719. session->MessageBox( MSG_OK, common->GetLanguageDict()->GetString ( "#str_04839" ), common->GetLanguageDict()->GetString ( "#str_04837" ), true );
  1720. showUpdateMessage = false;
  1721. }
  1722. common->DPrintf( "No update available\n" );
  1723. } else if ( backgroundDownload.completed ) {
  1724. // only enter these if the download slot is free
  1725. if ( updateState == UPDATE_READY ) {
  1726. //
  1727. if ( session->MessageBox( MSG_YESNO, updateMSG, common->GetLanguageDict()->GetString ( "#str_04330" ), true, "yes" )[0] ) {
  1728. if ( !updateDirectDownload ) {
  1729. sys->OpenURL( updateURL, true );
  1730. updateState = UPDATE_DONE;
  1731. } else {
  1732. // we're just creating the file at toplevel inside fs_savepath
  1733. updateURL.ExtractFileName( updateFile );
  1734. idFile_Permanent *f = static_cast< idFile_Permanent *>( fileSystem->OpenFileWrite( updateFile ) );
  1735. dltotal = 0;
  1736. dlnow = 0;
  1737. backgroundDownload.completed = false;
  1738. backgroundDownload.opcode = DLTYPE_URL;
  1739. backgroundDownload.f = f;
  1740. backgroundDownload.url.status = DL_WAIT;
  1741. backgroundDownload.url.dlnow = 0;
  1742. backgroundDownload.url.dltotal = 0;
  1743. backgroundDownload.url.url = updateURL;
  1744. fileSystem->BackgroundDownload( &backgroundDownload );
  1745. updateState = UPDATE_DLING;
  1746. SendVersionDLUpdate( 0 );
  1747. session->DownloadProgressBox( &backgroundDownload, va( "Downloading %s\n", updateFile.c_str() ) );
  1748. updateState = UPDATE_DONE;
  1749. if ( backgroundDownload.url.status == DL_DONE ) {
  1750. SendVersionDLUpdate( 1 );
  1751. idStr fullPath = f->GetFullPath();
  1752. fileSystem->CloseFile( f );
  1753. if ( session->MessageBox( MSG_YESNO, common->GetLanguageDict()->GetString ( "#str_04331" ), common->GetLanguageDict()->GetString ( "#str_04332" ), true, "yes" )[0] ) {
  1754. if ( updateMime == FILE_EXEC ) {
  1755. sys->StartProcess( fullPath, true );
  1756. } else {
  1757. sys->OpenURL( va( "file://%s", fullPath.c_str() ), true );
  1758. }
  1759. } else {
  1760. session->MessageBox( MSG_OK, va( common->GetLanguageDict()->GetString ( "#str_04333" ), fullPath.c_str() ), common->GetLanguageDict()->GetString ( "#str_04334" ), true );
  1761. }
  1762. } else {
  1763. if ( backgroundDownload.url.dlerror[ 0 ] ) {
  1764. common->Warning( "update download failed. curl error: %s", backgroundDownload.url.dlerror );
  1765. }
  1766. SendVersionDLUpdate( 2 );
  1767. idStr name = f->GetName();
  1768. fileSystem->CloseFile( f );
  1769. fileSystem->RemoveFile( name );
  1770. session->MessageBox( MSG_OK, common->GetLanguageDict()->GetString ( "#str_04335" ), common->GetLanguageDict()->GetString ( "#str_04336" ), true );
  1771. if ( updateFallback.Length() ) {
  1772. sys->OpenURL( updateFallback.c_str(), true );
  1773. } else {
  1774. common->Printf( "no fallback URL\n" );
  1775. }
  1776. }
  1777. }
  1778. } else {
  1779. updateState = UPDATE_DONE;
  1780. }
  1781. } else if ( dlList.Num() ) {
  1782. int numPaks = dlList.Num();
  1783. int pakCount = 1;
  1784. int progress_start, progress_end;
  1785. currentDlSize = 0;
  1786. do {
  1787. if ( dlList[ 0 ].url[ 0 ] == '\0' ) {
  1788. // ignore empty files
  1789. dlList.RemoveIndex( 0 );
  1790. continue;
  1791. }
  1792. common->Printf( "start download for %s\n", dlList[ 0 ].url.c_str() );
  1793. idFile_Permanent *f = static_cast< idFile_Permanent *>( fileSystem->MakeTemporaryFile( ) );
  1794. if ( !f ) {
  1795. common->Warning( "could not create temporary file" );
  1796. dlList.Clear();
  1797. return;
  1798. }
  1799. backgroundDownload.completed = false;
  1800. backgroundDownload.opcode = DLTYPE_URL;
  1801. backgroundDownload.f = f;
  1802. backgroundDownload.url.status = DL_WAIT;
  1803. backgroundDownload.url.dlnow = 0;
  1804. backgroundDownload.url.dltotal = dlList[ 0 ].size;
  1805. backgroundDownload.url.url = dlList[ 0 ].url;
  1806. fileSystem->BackgroundDownload( &backgroundDownload );
  1807. idStr dltitle;
  1808. // "Downloading %s"
  1809. sprintf( dltitle, common->GetLanguageDict()->GetString( "#str_07213" ), dlList[ 0 ].filename.c_str() );
  1810. if ( numPaks > 1 ) {
  1811. dltitle += va( " (%d/%d)", pakCount, numPaks );
  1812. }
  1813. if ( totalDlSize ) {
  1814. progress_start = (int)( (float)currentDlSize * 100.0f / (float)totalDlSize );
  1815. progress_end = (int)( (float)( currentDlSize + dlList[ 0 ].size ) * 100.0f / (float)totalDlSize );
  1816. } else {
  1817. progress_start = 0;
  1818. progress_end = 100;
  1819. }
  1820. session->DownloadProgressBox( &backgroundDownload, dltitle, progress_start, progress_end );
  1821. if ( backgroundDownload.url.status == DL_DONE ) {
  1822. idFile *saveas;
  1823. const int CHUNK_SIZE = 1024 * 1024;
  1824. byte *buf;
  1825. int remainlen;
  1826. int readlen;
  1827. int retlen;
  1828. int checksum;
  1829. common->Printf( "file downloaded\n" );
  1830. idStr finalPath = cvarSystem->GetCVarString( "fs_savepath" );
  1831. finalPath.AppendPath( dlList[ 0 ].filename );
  1832. fileSystem->CreateOSPath( finalPath );
  1833. // do the final copy ourselves so we do by small chunks in case the file is big
  1834. saveas = fileSystem->OpenExplicitFileWrite( finalPath );
  1835. buf = (byte*)Mem_Alloc( CHUNK_SIZE );
  1836. f->Seek( 0, FS_SEEK_END );
  1837. remainlen = f->Tell();
  1838. f->Seek( 0, FS_SEEK_SET );
  1839. while ( remainlen ) {
  1840. readlen = Min( remainlen, CHUNK_SIZE );
  1841. retlen = f->Read( buf, readlen );
  1842. if ( retlen != readlen ) {
  1843. common->FatalError( "short read %d of %d in idFileSystem::HandleDownload", retlen, readlen );
  1844. }
  1845. retlen = saveas->Write( buf, readlen );
  1846. if ( retlen != readlen ) {
  1847. common->FatalError( "short write %d of %d in idFileSystem::HandleDownload", retlen, readlen );
  1848. }
  1849. remainlen -= readlen;
  1850. }
  1851. fileSystem->CloseFile( f );
  1852. fileSystem->CloseFile( saveas );
  1853. common->Printf( "saved as %s\n", finalPath.c_str() );
  1854. Mem_Free( buf );
  1855. // add that file to our paks list
  1856. checksum = fileSystem->AddZipFile( dlList[ 0 ].filename );
  1857. // verify the checksum to be what the server says
  1858. if ( !checksum || checksum != dlList[ 0 ].checksum ) {
  1859. // "pak is corrupted ( checksum 0x%x, expected 0x%x )"
  1860. session->MessageBox( MSG_OK, va( common->GetLanguageDict()->GetString( "#str_07214" ) , checksum, dlList[0].checksum ), "Download failed", true );
  1861. fileSystem->RemoveFile( dlList[ 0 ].filename );
  1862. dlList.Clear();
  1863. return;
  1864. }
  1865. currentDlSize += dlList[ 0 ].size;
  1866. } else {
  1867. common->Warning( "download failed: %s", dlList[ 0 ].url.c_str() );
  1868. if ( backgroundDownload.url.dlerror[ 0 ] ) {
  1869. common->Warning( "curl error: %s", backgroundDownload.url.dlerror );
  1870. }
  1871. // "The download failed or was cancelled"
  1872. // "Download failed"
  1873. session->MessageBox( MSG_OK, common->GetLanguageDict()->GetString( "#str_07215" ), common->GetLanguageDict()->GetString( "#str_07216" ), true );
  1874. dlList.Clear();
  1875. return;
  1876. }
  1877. pakCount++;
  1878. dlList.RemoveIndex( 0 );
  1879. } while ( dlList.Num() );
  1880. // all downloads successful - do the dew
  1881. cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "reconnect\n" );
  1882. }
  1883. }
  1884. }
  1885. /*
  1886. ===============
  1887. idAsyncClient::SendAuthCheck
  1888. ===============
  1889. */
  1890. bool idAsyncClient::SendAuthCheck( const char *cdkey, const char *xpkey ) {
  1891. idBitMsg msg;
  1892. byte msgBuf[MAX_MESSAGE_SIZE];
  1893. msg.Init( msgBuf, sizeof( msgBuf ) );
  1894. msg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
  1895. msg.WriteString( "gameAuth" );
  1896. msg.WriteLong( ASYNC_PROTOCOL_VERSION );
  1897. msg.WriteByte( cdkey ? 1 : 0 );
  1898. msg.WriteString( cdkey ? cdkey : "" );
  1899. msg.WriteByte( xpkey ? 1 : 0 );
  1900. msg.WriteString( xpkey ? xpkey : "" );
  1901. InitPort();
  1902. clientPort.SendPacket( idAsyncNetwork::GetMasterAddress(), msg.GetData(), msg.GetSize() );
  1903. return true;
  1904. }
  1905. /*
  1906. ===============
  1907. idAsyncClient::CheckTimeout
  1908. ===============
  1909. */
  1910. bool idAsyncClient::CheckTimeout( void ) {
  1911. if ( lastPacketTime > 0 && ( lastPacketTime + idAsyncNetwork::clientServerTimeout.GetInteger()*1000 < clientTime ) ) {
  1912. session->StopBox();
  1913. session->MessageBox( MSG_OK, common->GetLanguageDict()->GetString ( "#str_04328" ), common->GetLanguageDict()->GetString ( "#str_04329" ), true );
  1914. cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
  1915. return true;
  1916. }
  1917. return false;
  1918. }
  1919. /*
  1920. ===============
  1921. idAsyncClient::ProcessDownloadInfoMessage
  1922. ===============
  1923. */
  1924. void idAsyncClient::ProcessDownloadInfoMessage( const netadr_t from, const idBitMsg &msg ) {
  1925. char buf[ MAX_STRING_CHARS ];
  1926. int srvDlRequest = msg.ReadLong();
  1927. int infoType = msg.ReadByte();
  1928. int pakDl;
  1929. int pakIndex;
  1930. pakDlEntry_t entry;
  1931. bool gotAllFiles = true;
  1932. idStr sizeStr;
  1933. bool gotGame = false;
  1934. if ( dlRequest == -1 || srvDlRequest != dlRequest ) {
  1935. common->Warning( "bad download id from server, ignored" );
  1936. return;
  1937. }
  1938. // mark the dlRequest as dead now whatever how we process it
  1939. dlRequest = -1;
  1940. if ( infoType == SERVER_DL_REDIRECT ) {
  1941. msg.ReadString( buf, MAX_STRING_CHARS );
  1942. cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
  1943. // "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?"
  1944. // "Missing required files"
  1945. if ( session->MessageBox( MSG_YESNO, va( common->GetLanguageDict()->GetString( "#str_07217" ), buf ),
  1946. common->GetLanguageDict()->GetString( "#str_07218" ), true, "yes" )[ 0 ] ) {
  1947. sys->OpenURL( buf, true );
  1948. }
  1949. } else if ( infoType == SERVER_DL_LIST ) {
  1950. cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
  1951. if ( dlList.Num() ) {
  1952. common->Warning( "tried to process a download list while already busy downloading things" );
  1953. return;
  1954. }
  1955. // read the URLs, check against what we requested, prompt for download
  1956. pakIndex = -1;
  1957. totalDlSize = 0;
  1958. do {
  1959. pakIndex++;
  1960. pakDl = msg.ReadByte();
  1961. if ( pakDl == SERVER_PAK_YES ) {
  1962. if ( pakIndex == 0 ) {
  1963. gotGame = true;
  1964. }
  1965. msg.ReadString( buf, MAX_STRING_CHARS );
  1966. entry.filename = buf;
  1967. msg.ReadString( buf, MAX_STRING_CHARS );
  1968. entry.url = buf;
  1969. entry.size = msg.ReadLong();
  1970. // checksums are not transmitted, we read them from the dl request we sent
  1971. entry.checksum = dlChecksums[ pakIndex ];
  1972. totalDlSize += entry.size;
  1973. dlList.Append( entry );
  1974. common->Printf( "download %s from %s ( 0x%x )\n", entry.filename.c_str(), entry.url.c_str(), entry.checksum );
  1975. } else if ( pakDl == SERVER_PAK_NO ) {
  1976. msg.ReadString( buf, MAX_STRING_CHARS );
  1977. entry.filename = buf;
  1978. entry.url = "";
  1979. entry.size = 0;
  1980. entry.checksum = 0;
  1981. dlList.Append( entry );
  1982. // first pak is game pak, only fail it if we actually requested it
  1983. if ( pakIndex != 0 || dlChecksums[ 0 ] != 0 ) {
  1984. common->Printf( "no download offered for %s ( 0x%x )\n", entry.filename.c_str(), dlChecksums[ pakIndex ] );
  1985. gotAllFiles = false;
  1986. }
  1987. } else {
  1988. assert( pakDl == SERVER_PAK_END );
  1989. }
  1990. } while ( pakDl != SERVER_PAK_END );
  1991. if ( dlList.Num() < dlCount ) {
  1992. common->Printf( "%d files were ignored by the server\n", dlCount - dlList.Num() );
  1993. gotAllFiles = false;
  1994. }
  1995. sizeStr.BestUnit( "%.2f", totalDlSize, MEASURE_SIZE );
  1996. cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
  1997. if ( totalDlSize == 0 ) {
  1998. // was no downloadable stuff for us
  1999. // "Can't connect to the pure server: no downloads offered"
  2000. // "Missing required files"
  2001. dlList.Clear();
  2002. session->MessageBox( MSG_OK, common->GetLanguageDict()->GetString( "#str_07219" ), common->GetLanguageDict()->GetString( "#str_07218" ), true );
  2003. return;
  2004. }
  2005. bool asked = false;
  2006. if ( gotGame ) {
  2007. asked = true;
  2008. // "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."
  2009. // "Missing game binaries"
  2010. if ( !session->MessageBox( MSG_YESNO, common->GetLanguageDict()->GetString( "#str_07220" ), common->GetLanguageDict()->GetString( "#str_07221" ), true, "yes" )[ 0 ] ) {
  2011. dlList.Clear();
  2012. return;
  2013. }
  2014. }
  2015. if ( !gotAllFiles ) {
  2016. asked = true;
  2017. // "The server only offers to download some of the files required to connect ( %s ). Download anyway?"
  2018. // "Missing required files"
  2019. if ( !session->MessageBox( MSG_YESNO, va( common->GetLanguageDict()->GetString( "#str_07222" ), sizeStr.c_str() ),
  2020. common->GetLanguageDict()->GetString( "#str_07218" ), true, "yes" )[ 0 ] ) {
  2021. dlList.Clear();
  2022. return;
  2023. }
  2024. }
  2025. if ( !asked && idAsyncNetwork::clientDownload.GetInteger() == 1 ) {
  2026. // "You need to download some files to connect to this server ( %s ), proceed?"
  2027. // "Missing required files"
  2028. if ( !session->MessageBox( MSG_YESNO, va( common->GetLanguageDict()->GetString( "#str_07224" ), sizeStr.c_str() ),
  2029. common->GetLanguageDict()->GetString( "#str_07218" ), true, "yes" )[ 0 ] ) {
  2030. dlList.Clear();
  2031. return;
  2032. }
  2033. }
  2034. } else {
  2035. cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
  2036. // "You are missing some files to connect to this server, and the server doesn't provide downloads."
  2037. // "Missing required files"
  2038. session->MessageBox( MSG_OK, common->GetLanguageDict()->GetString( "#str_07223" ), common->GetLanguageDict()->GetString( "#str_07218" ), true );
  2039. }
  2040. }
  2041. /*
  2042. ===============
  2043. idAsyncClient::GetDownloadRequest
  2044. ===============
  2045. */
  2046. int idAsyncClient::GetDownloadRequest( const int checksums[ MAX_PURE_PAKS ], int count, int gamePakChecksum ) {
  2047. assert( !checksums[ count ] ); // 0-terminated
  2048. if ( memcmp( dlChecksums + 1, checksums, sizeof( int ) * count ) || gamePakChecksum != dlChecksums[ 0 ] ) {
  2049. idRandom newreq;
  2050. dlChecksums[ 0 ] = gamePakChecksum;
  2051. memcpy( dlChecksums + 1, checksums, sizeof( int ) * MAX_PURE_PAKS );
  2052. newreq.SetSeed( Sys_Milliseconds() );
  2053. dlRequest = newreq.RandomInt();
  2054. dlCount = count + ( gamePakChecksum ? 1 : 0 );
  2055. return dlRequest;
  2056. }
  2057. // this is the same dlRequest, we haven't heard from the server. keep the same id
  2058. return dlRequest;
  2059. }