12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363 |
- /*
- ===========================================================================
- Doom 3 GPL Source Code
- Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
- This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
- Doom 3 Source Code is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- Doom 3 Source Code is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
- 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.
- 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.
- ===========================================================================
- */
- #include "../../idlib/precompiled.h"
- #pragma hdrstop
- #include "AsyncNetwork.h"
- #include "../Session_local.h"
- const int SETUP_CONNECTION_RESEND_TIME = 1000;
- const int EMPTY_RESEND_TIME = 500;
- const int PREDICTION_FAST_ADJUST = 4;
- /*
- ==================
- idAsyncClient::idAsyncClient
- ==================
- */
- idAsyncClient::idAsyncClient( void ) {
- guiNetMenu = NULL;
- updateState = UPDATE_NONE;
- Clear();
- }
- /*
- ==================
- idAsyncClient::Clear
- ==================
- */
- void idAsyncClient::Clear( void ) {
- active = false;
- realTime = 0;
- clientTime = 0;
- clientId = 0;
- clientDataChecksum = 0;
- clientNum = 0;
- clientState = CS_DISCONNECTED;
- clientPrediction = 0;
- clientPredictTime = 0;
- serverId = 0;
- serverChallenge = 0;
- serverMessageSequence = 0;
- lastConnectTime = -9999;
- lastEmptyTime = -9999;
- lastPacketTime = -9999;
- lastSnapshotTime = -9999;
- snapshotGameFrame = 0;
- snapshotGameTime = 0;
- snapshotSequence = 0;
- gameInitId = GAME_INIT_ID_INVALID;
- gameFrame = 0;
- gameTimeResidual = 0;
- gameTime = 0;
- memset( userCmds, 0, sizeof( userCmds ) );
- backgroundDownload.completed = true;
- lastRconTime = 0;
- showUpdateMessage = false;
- lastFrameDelta = 0;
- dlRequest = -1;
- dlCount = -1;
- memset( dlChecksums, 0, sizeof( int ) * MAX_PURE_PAKS );
- currentDlSize = 0;
- totalDlSize = 0;
- }
- /*
- ==================
- idAsyncClient::Shutdown
- ==================
- */
- void idAsyncClient::Shutdown( void ) {
- guiNetMenu = NULL;
- updateMSG.Clear();
- updateURL.Clear();
- updateFile.Clear();
- updateFallback.Clear();
- backgroundDownload.url.url.Clear();
- dlList.Clear();
- }
- /*
- ==================
- idAsyncClient::InitPort
- ==================
- */
- bool idAsyncClient::InitPort( void ) {
- // if this is the first time we connect to a server, open the UDP port
- if ( !clientPort.GetPort() ) {
- if ( !clientPort.InitForPort( PORT_ANY ) ) {
- common->Printf( "Couldn't open client network port.\n" );
- return false;
- }
- }
- // maintain it valid between connects and ui manager reloads
- guiNetMenu = uiManager->FindGui( "guis/netmenu.gui", true, false, true );
- return true;
- }
- /*
- ==================
- idAsyncClient::ClosePort
- ==================
- */
- void idAsyncClient::ClosePort( void ) {
- clientPort.Close();
- }
- /*
- ==================
- idAsyncClient::ClearPendingPackets
- ==================
- */
- void idAsyncClient::ClearPendingPackets( void ) {
- int size;
- byte msgBuf[MAX_MESSAGE_SIZE];
- netadr_t from;
- while( clientPort.GetPacket( from, msgBuf, size, sizeof( msgBuf ) ) ) {
- }
- }
- /*
- ==================
- idAsyncClient::HandleGuiCommandInternal
- ==================
- */
- const char* idAsyncClient::HandleGuiCommandInternal( const char *cmd ) {
- if ( !idStr::Cmp( cmd, "abort" ) || !idStr::Cmp( cmd, "pure_abort" ) ) {
- common->DPrintf( "connection aborted\n" );
- cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
- return "";
- } else {
- common->DWarning( "idAsyncClient::HandleGuiCommand: unknown cmd %s", cmd );
- }
- return NULL;
- }
- /*
- ==================
- idAsyncClient::HandleGuiCommand
- ==================
- */
- const char* idAsyncClient::HandleGuiCommand( const char *cmd ) {
- return idAsyncNetwork::client.HandleGuiCommandInternal( cmd );
- }
- /*
- ==================
- idAsyncClient::ConnectToServer
- ==================
- */
- void idAsyncClient::ConnectToServer( const netadr_t adr ) {
- // shutdown any current game. that includes network disconnect
- session->Stop();
- if ( !InitPort() ) {
- return;
- }
- if ( cvarSystem->GetCVarBool( "net_serverDedicated" ) ) {
- common->Printf( "Can't connect to a server as dedicated\n" );
- return;
- }
- // trash any currently pending packets
- ClearPendingPackets();
-
- serverAddress = adr;
- // clear the client state
- Clear();
- // get a pseudo random client id, but don't use the id which is reserved for connectionless packets
- clientId = Sys_Milliseconds() & CONNECTIONLESS_MESSAGE_ID_MASK;
- // calculate a checksum on some of the essential data used
- clientDataChecksum = declManager->GetChecksum();
- // start challenging the server
- clientState = CS_CHALLENGING;
- active = true;
- guiNetMenu = uiManager->FindGui( "guis/netmenu.gui", true, false, true );
- guiNetMenu->SetStateString( "status", va( common->GetLanguageDict()->GetString( "#str_06749" ), Sys_NetAdrToString( adr ) ) );
- session->SetGUI( guiNetMenu, HandleGuiCommand );
- }
- /*
- ==================
- idAsyncClient::Reconnect
- ==================
- */
- void idAsyncClient::Reconnect( void ) {
- ConnectToServer( serverAddress );
- }
- /*
- ==================
- idAsyncClient::ConnectToServer
- ==================
- */
- void idAsyncClient::ConnectToServer( const char *address ) {
- int serverNum;
- netadr_t adr;
- if ( idStr::IsNumeric( address ) ) {
- serverNum = atoi( address );
- if ( serverNum < 0 || serverNum >= serverList.Num() ) {
- session->MessageBox( MSG_OK, va( common->GetLanguageDict()->GetString( "#str_06733" ), serverNum ), common->GetLanguageDict()->GetString( "#str_06735" ), true );
- return;
- }
- adr = serverList[ serverNum ].adr;
- } else {
- if ( !Sys_StringToNetAdr( address, &adr, true ) ) {
- session->MessageBox( MSG_OK, va( common->GetLanguageDict()->GetString( "#str_06734" ), address ), common->GetLanguageDict()->GetString( "#str_06735" ), true );
- return;
- }
- }
- if ( !adr.port ) {
- adr.port = PORT_SERVER;
- }
- common->Printf( "\"%s\" resolved to %s\n", address, Sys_NetAdrToString( adr ) );
- ConnectToServer( adr );
- }
- /*
- ==================
- idAsyncClient::DisconnectFromServer
- ==================
- */
- void idAsyncClient::DisconnectFromServer( void ) {
- idBitMsg msg;
- byte msgBuf[MAX_MESSAGE_SIZE];
- if ( clientState >= CS_CONNECTED ) {
- // if we were actually connected, clear the pure list
- fileSystem->ClearPureChecksums();
- // send reliable disconnect to server
- msg.Init( msgBuf, sizeof( msgBuf ) );
- msg.WriteByte( CLIENT_RELIABLE_MESSAGE_DISCONNECT );
- msg.WriteString( "disconnect" );
- if ( !channel.SendReliableMessage( msg ) ) {
- common->Error( "client->server reliable messages overflow\n" );
- }
- SendEmptyToServer( true );
- SendEmptyToServer( true );
- SendEmptyToServer( true );
- }
- if ( clientState != CS_PURERESTART ) {
- channel.Shutdown();
- clientState = CS_DISCONNECTED;
- }
- active = false;
- }
- /*
- ==================
- idAsyncClient::GetServerInfo
- ==================
- */
- void idAsyncClient::GetServerInfo( const netadr_t adr ) {
- idBitMsg msg;
- byte msgBuf[MAX_MESSAGE_SIZE];
-
- if ( !InitPort() ) {
- return;
- }
- msg.Init( msgBuf, sizeof( msgBuf ) );
- msg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
- msg.WriteString( "getInfo" );
- msg.WriteLong( serverList.GetChallenge() ); // challenge
- clientPort.SendPacket( adr, msg.GetData(), msg.GetSize() );
- }
- /*
- ==================
- idAsyncClient::GetServerInfo
- ==================
- */
- void idAsyncClient::GetServerInfo( const char *address ) {
- netadr_t adr;
- if ( address && *address != '\0' ) {
- if ( !Sys_StringToNetAdr( address, &adr, true ) ) {
- common->Printf( "Couldn't get server address for \"%s\"\n", address );
- return;
- }
- } else if ( active ) {
- adr = serverAddress;
- } else if ( idAsyncNetwork::server.IsActive() ) {
- // used to be a Sys_StringToNetAdr( "localhost", &adr, true ); and send a packet over loopback
- // but this breaks with net_ip ( typically, for multi-homed servers )
- idAsyncNetwork::server.PrintLocalServerInfo();
- return;
- } else {
- common->Printf( "no server found\n" );
- return;
- }
- if ( !adr.port ) {
- adr.port = PORT_SERVER;
- }
- GetServerInfo( adr );
- }
- /*
- ==================
- idAsyncClient::GetLANServers
- ==================
- */
- void idAsyncClient::GetLANServers( void ) {
- int i;
- idBitMsg msg;
- byte msgBuf[MAX_MESSAGE_SIZE];
- netadr_t broadcastAddress;
- if ( !InitPort() ) {
- return;
- }
- idAsyncNetwork::LANServer.SetBool( true );
- serverList.SetupLANScan();
- msg.Init( msgBuf, sizeof( msgBuf ) );
- msg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
- msg.WriteString( "getInfo" );
- msg.WriteLong( serverList.GetChallenge() );
- broadcastAddress.type = NA_BROADCAST;
- for ( i = 0; i < MAX_SERVER_PORTS; i++ ) {
- broadcastAddress.port = PORT_SERVER + i;
- clientPort.SendPacket( broadcastAddress, msg.GetData(), msg.GetSize() );
- }
- }
- /*
- ==================
- idAsyncClient::GetNETServers
- ==================
- */
- void idAsyncClient::GetNETServers( void ) {
- idBitMsg msg;
- byte msgBuf[MAX_MESSAGE_SIZE];
- idAsyncNetwork::LANServer.SetBool( false );
- // NetScan only clears GUI and results, not the stored list
- serverList.Clear( );
- serverList.NetScan( );
- serverList.StartServers( true );
- msg.Init( msgBuf, sizeof( msgBuf ) );
- msg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
- msg.WriteString( "getServers" );
- msg.WriteLong( ASYNC_PROTOCOL_VERSION );
- msg.WriteString( cvarSystem->GetCVarString( "fs_game" ) );
- msg.WriteBits( cvarSystem->GetCVarInteger( "gui_filter_password" ), 2 );
- msg.WriteBits( cvarSystem->GetCVarInteger( "gui_filter_players" ), 2 );
- msg.WriteBits( cvarSystem->GetCVarInteger( "gui_filter_gameType" ), 2 );
- netadr_t adr;
- if ( idAsyncNetwork::GetMasterAddress( 0, adr ) ) {
- clientPort.SendPacket( adr, msg.GetData(), msg.GetSize() );
- }
- }
- /*
- ==================
- idAsyncClient::ListServers
- ==================
- */
- void idAsyncClient::ListServers( void ) {
- int i;
- for ( i = 0; i < serverList.Num(); i++ ) {
- common->Printf( "%3d: %s %dms (%s)\n", i, serverList[i].serverInfo.GetString( "si_name" ), serverList[ i ].ping, Sys_NetAdrToString( serverList[i].adr ) );
- }
- }
- /*
- ==================
- idAsyncClient::ClearServers
- ==================
- */
- void idAsyncClient::ClearServers( void ) {
- serverList.Clear();
- }
- /*
- ==================
- idAsyncClient::RemoteConsole
- ==================
- */
- void idAsyncClient::RemoteConsole( const char *command ) {
- netadr_t adr;
- idBitMsg msg;
- byte msgBuf[MAX_MESSAGE_SIZE];
- if ( !InitPort() ) {
- return;
- }
- if ( active ) {
- adr = serverAddress;
- } else {
- Sys_StringToNetAdr( idAsyncNetwork::clientRemoteConsoleAddress.GetString(), &adr, true );
- }
-
- if ( !adr.port ) {
- adr.port = PORT_SERVER;
- }
- lastRconAddress = adr;
- lastRconTime = realTime;
- msg.Init( msgBuf, sizeof( msgBuf ) );
- msg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
- msg.WriteString( "rcon" );
- msg.WriteString( idAsyncNetwork::clientRemoteConsolePassword.GetString() );
- msg.WriteString( command );
- clientPort.SendPacket( adr, msg.GetData(), msg.GetSize() );
- }
- /*
- ==================
- idAsyncClient::GetPrediction
- ==================
- */
- int idAsyncClient::GetPrediction( void ) const {
- if ( clientState < CS_CONNECTED ) {
- return -1;
- } else {
- return clientPrediction;
- }
- }
- /*
- ==================
- idAsyncClient::GetTimeSinceLastPacket
- ==================
- */
- int idAsyncClient::GetTimeSinceLastPacket( void ) const {
- if ( clientState < CS_CONNECTED ) {
- return -1;
- } else {
- return clientTime - lastPacketTime;
- }
- }
- /*
- ==================
- idAsyncClient::GetOutgoingRate
- ==================
- */
- int idAsyncClient::GetOutgoingRate( void ) const {
- if ( clientState < CS_CONNECTED ) {
- return -1;
- } else {
- return channel.GetOutgoingRate();
- }
- }
- /*
- ==================
- idAsyncClient::GetIncomingRate
- ==================
- */
- int idAsyncClient::GetIncomingRate( void ) const {
- if ( clientState < CS_CONNECTED ) {
- return -1;
- } else {
- return channel.GetIncomingRate();
- }
- }
- /*
- ==================
- idAsyncClient::GetOutgoingCompression
- ==================
- */
- float idAsyncClient::GetOutgoingCompression( void ) const {
- if ( clientState < CS_CONNECTED ) {
- return 0.0f;
- } else {
- return channel.GetOutgoingCompression();
- }
- }
- /*
- ==================
- idAsyncClient::GetIncomingCompression
- ==================
- */
- float idAsyncClient::GetIncomingCompression( void ) const {
- if ( clientState < CS_CONNECTED ) {
- return 0.0f;
- } else {
- return channel.GetIncomingCompression();
- }
- }
- /*
- ==================
- idAsyncClient::GetIncomingPacketLoss
- ==================
- */
- float idAsyncClient::GetIncomingPacketLoss( void ) const {
- if ( clientState < CS_CONNECTED ) {
- return 0.0f;
- } else {
- return channel.GetIncomingPacketLoss();
- }
- }
- /*
- ==================
- idAsyncClient::DuplicateUsercmds
- ==================
- */
- void idAsyncClient::DuplicateUsercmds( int frame, int time ) {
- int i, previousIndex, currentIndex;
- previousIndex = ( frame - 1 ) & ( MAX_USERCMD_BACKUP - 1 );
- currentIndex = frame & ( MAX_USERCMD_BACKUP - 1 );
- // duplicate previous user commands if no new commands are available for a client
- for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
- idAsyncNetwork::DuplicateUsercmd( userCmds[previousIndex][i], userCmds[currentIndex][i], frame, time );
- }
- }
- /*
- ==================
- idAsyncClient::SendUserInfoToServer
- ==================
- */
- void idAsyncClient::SendUserInfoToServer( void ) {
- idBitMsg msg;
- byte msgBuf[MAX_MESSAGE_SIZE];
- idDict info;
- if ( clientState < CS_CONNECTED ) {
- return;
- }
- info = *cvarSystem->MoveCVarsToDict( CVAR_USERINFO );
-
- // send reliable client info to server
- msg.Init( msgBuf, sizeof( msgBuf ) );
- msg.WriteByte( CLIENT_RELIABLE_MESSAGE_CLIENTINFO );
- msg.WriteDeltaDict( info, &sessLocal.mapSpawnData.userInfo[ clientNum ] );
- if ( !channel.SendReliableMessage( msg ) ) {
- common->Error( "client->server reliable messages overflow\n" );
- }
- sessLocal.mapSpawnData.userInfo[clientNum] = info;
- }
- /*
- ==================
- idAsyncClient::SendEmptyToServer
- ==================
- */
- void idAsyncClient::SendEmptyToServer( bool force, bool mapLoad ) {
- idBitMsg msg;
- byte msgBuf[MAX_MESSAGE_SIZE];
- if ( lastEmptyTime > realTime ) {
- lastEmptyTime = realTime;
- }
- if ( !force && ( realTime - lastEmptyTime < EMPTY_RESEND_TIME ) ) {
- return;
- }
- if ( idAsyncNetwork::verbose.GetInteger() ) {
- common->Printf( "sending empty to server, gameInitId = %d\n", mapLoad ? GAME_INIT_ID_MAP_LOAD : gameInitId );
- }
- msg.Init( msgBuf, sizeof( msgBuf ) );
- msg.WriteLong( serverMessageSequence );
- msg.WriteLong( mapLoad ? GAME_INIT_ID_MAP_LOAD : gameInitId );
- msg.WriteLong( snapshotSequence );
- msg.WriteByte( CLIENT_UNRELIABLE_MESSAGE_EMPTY );
- channel.SendMessage( clientPort, clientTime, msg );
- while( channel.UnsentFragmentsLeft() ) {
- channel.SendNextFragment( clientPort, clientTime );
- }
- lastEmptyTime = realTime;
- }
- /*
- ==================
- idAsyncClient::SendPingResponseToServer
- ==================
- */
- void idAsyncClient::SendPingResponseToServer( int time ) {
- idBitMsg msg;
- byte msgBuf[MAX_MESSAGE_SIZE];
- if ( idAsyncNetwork::verbose.GetInteger() == 2 ) {
- common->Printf( "sending ping response to server, gameInitId = %d\n", gameInitId );
- }
- msg.Init( msgBuf, sizeof( msgBuf ) );
- msg.WriteLong( serverMessageSequence );
- msg.WriteLong( gameInitId );
- msg.WriteLong( snapshotSequence );
- msg.WriteByte( CLIENT_UNRELIABLE_MESSAGE_PINGRESPONSE );
- msg.WriteLong( time );
- channel.SendMessage( clientPort, clientTime, msg );
- while( channel.UnsentFragmentsLeft() ) {
- channel.SendNextFragment( clientPort, clientTime );
- }
- }
- /*
- ==================
- idAsyncClient::SendUsercmdsToServer
- ==================
- */
- void idAsyncClient::SendUsercmdsToServer( void ) {
- int i, numUsercmds, index;
- idBitMsg msg;
- byte msgBuf[MAX_MESSAGE_SIZE];
- usercmd_t * last;
- if ( idAsyncNetwork::verbose.GetInteger() == 2 ) {
- common->Printf( "sending usercmd to server: gameInitId = %d, gameFrame = %d, gameTime = %d\n", gameInitId, gameFrame, gameTime );
- }
- // generate user command for this client
- index = gameFrame & ( MAX_USERCMD_BACKUP - 1 );
- userCmds[index][clientNum] = usercmdGen->GetDirectUsercmd();
- userCmds[index][clientNum].gameFrame = gameFrame;
- userCmds[index][clientNum].gameTime = gameTime;
- // send the user commands to the server
- msg.Init( msgBuf, sizeof( msgBuf ) );
- msg.WriteLong( serverMessageSequence );
- msg.WriteLong( gameInitId );
- msg.WriteLong( snapshotSequence );
- msg.WriteByte( CLIENT_UNRELIABLE_MESSAGE_USERCMD );
- msg.WriteShort( clientPrediction );
- numUsercmds = idMath::ClampInt( 0, 10, idAsyncNetwork::clientUsercmdBackup.GetInteger() ) + 1;
- // write the user commands
- msg.WriteLong( gameFrame );
- msg.WriteByte( numUsercmds );
- for ( last = NULL, i = gameFrame - numUsercmds + 1; i <= gameFrame; i++ ) {
- index = i & ( MAX_USERCMD_BACKUP - 1 );
- idAsyncNetwork::WriteUserCmdDelta( msg, userCmds[index][clientNum], last );
- last = &userCmds[index][clientNum];
- }
- channel.SendMessage( clientPort, clientTime, msg );
- while( channel.UnsentFragmentsLeft() ) {
- channel.SendNextFragment( clientPort, clientTime );
- }
- }
- /*
- ==================
- idAsyncClient::InitGame
- ==================
- */
- void idAsyncClient::InitGame( int serverGameInitId, int serverGameFrame, int serverGameTime, const idDict &serverSI ) {
- gameInitId = serverGameInitId;
- gameFrame = snapshotGameFrame = serverGameFrame;
- gameTime = snapshotGameTime = serverGameTime;
- gameTimeResidual = 0;
- memset( userCmds, 0, sizeof( userCmds ) );
- for ( int i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
- sessLocal.mapSpawnData.userInfo[ i ].Clear();
- }
- sessLocal.mapSpawnData.serverInfo = serverSI;
- }
- /*
- ==================
- idAsyncClient::ProcessUnreliableServerMessage
- ==================
- */
- void idAsyncClient::ProcessUnreliableServerMessage( const idBitMsg &msg ) {
- int i, j, index, id, numDuplicatedUsercmds, aheadOfServer, numUsercmds, delta;
- int serverGameInitId, serverGameFrame, serverGameTime;
- idDict serverSI;
- usercmd_t *last;
- bool pureWait;
- serverGameInitId = msg.ReadLong();
- id = msg.ReadByte();
- switch( id ) {
- case SERVER_UNRELIABLE_MESSAGE_EMPTY: {
- if ( idAsyncNetwork::verbose.GetInteger() ) {
- common->Printf( "received empty message from server\n" );
- }
- break;
- }
- case SERVER_UNRELIABLE_MESSAGE_PING: {
- if ( idAsyncNetwork::verbose.GetInteger() == 2 ) {
- common->Printf( "received ping message from server\n" );
- }
- SendPingResponseToServer( msg.ReadLong() );
- break;
- }
- case SERVER_UNRELIABLE_MESSAGE_GAMEINIT: {
- serverGameFrame = msg.ReadLong();
- serverGameTime = msg.ReadLong();
- msg.ReadDeltaDict( serverSI, NULL );
- pureWait = serverSI.GetBool( "si_pure" );
- InitGame( serverGameInitId, serverGameFrame, serverGameTime, serverSI );
- channel.ResetRate();
- if ( idAsyncNetwork::verbose.GetInteger() ) {
- common->Printf( "received gameinit, gameInitId = %d, gameFrame = %d, gameTime = %d\n", gameInitId, gameFrame, gameTime );
- }
- // mute sound
- soundSystem->SetMute( true );
- // ensure chat icon goes away when the GUI is changed...
- //cvarSystem->SetCVarBool( "ui_chat", false );
- if ( pureWait ) {
- guiNetMenu = uiManager->FindGui( "guis/netmenu.gui", true, false, true );
- session->SetGUI( guiNetMenu, HandleGuiCommand );
- session->MessageBox( MSG_ABORT, common->GetLanguageDict()->GetString ( "#str_04317" ), common->GetLanguageDict()->GetString ( "#str_04318" ), false, "pure_abort" );
- } else {
- // load map
- session->SetGUI( NULL, NULL );
- sessLocal.ExecuteMapChange();
- }
- break;
- }
- case SERVER_UNRELIABLE_MESSAGE_SNAPSHOT: {
- // if the snapshot is from a different game
- if ( serverGameInitId != gameInitId ) {
- if ( idAsyncNetwork::verbose.GetInteger() ) {
- common->Printf( "ignoring snapshot with != gameInitId\n" );
- }
- break;
- }
- snapshotSequence = msg.ReadLong();
- snapshotGameFrame = msg.ReadLong();
- snapshotGameTime = msg.ReadLong();
- numDuplicatedUsercmds = msg.ReadByte();
- aheadOfServer = msg.ReadShort();
- // read the game snapshot
- game->ClientReadSnapshot( clientNum, snapshotSequence, snapshotGameFrame, snapshotGameTime, numDuplicatedUsercmds, aheadOfServer, msg );
- // read user commands of other clients from the snapshot
- for ( last = NULL, i = msg.ReadByte(); i < MAX_ASYNC_CLIENTS; i = msg.ReadByte() ) {
- numUsercmds = msg.ReadByte();
- if ( numUsercmds > MAX_USERCMD_RELAY ) {
- common->Error( "snapshot %d contains too many user commands for client %d", snapshotSequence, i );
- break;
- }
- for ( j = 0; j < numUsercmds; j++ ) {
- index = ( snapshotGameFrame + j ) & ( MAX_USERCMD_BACKUP - 1 );
- idAsyncNetwork::ReadUserCmdDelta( msg, userCmds[index][i], last );
- userCmds[index][i].gameFrame = snapshotGameFrame + j;
- userCmds[index][i].duplicateCount = 0;
- last = &userCmds[index][i];
- }
- // clear all user commands after the ones just read from the snapshot
- for ( j = numUsercmds; j < MAX_USERCMD_BACKUP; j++ ) {
- index = ( snapshotGameFrame + j ) & ( MAX_USERCMD_BACKUP - 1 );
- userCmds[index][i].gameFrame = 0;
- userCmds[index][i].gameTime = 0;
- }
- }
- // if this is the first snapshot after a game init was received
- if ( clientState == CS_CONNECTED ) {
- gameTimeResidual = 0;
- clientState = CS_INGAME;
- assert( !sessLocal.GetActiveMenu( ) );
- if ( idAsyncNetwork::verbose.GetInteger() ) {
- common->Printf( "received first snapshot, gameInitId = %d, gameFrame %d gameTime %d\n", gameInitId, snapshotGameFrame, snapshotGameTime );
- }
- }
- // if the snapshot is newer than the clients current game time
- if ( gameTime < snapshotGameTime || gameTime > snapshotGameTime + idAsyncNetwork::clientMaxPrediction.GetInteger() ) {
- gameFrame = snapshotGameFrame;
- gameTime = snapshotGameTime;
- gameTimeResidual = idMath::ClampInt( -idAsyncNetwork::clientMaxPrediction.GetInteger(), idAsyncNetwork::clientMaxPrediction.GetInteger(), gameTimeResidual );
- clientPredictTime = idMath::ClampInt( -idAsyncNetwork::clientMaxPrediction.GetInteger(), idAsyncNetwork::clientMaxPrediction.GetInteger(), clientPredictTime );
- }
- // adjust the client prediction time based on the snapshot time
- clientPrediction -= ( 1 - ( INTSIGNBITSET( aheadOfServer - idAsyncNetwork::clientPrediction.GetInteger() ) << 1 ) );
- clientPrediction = idMath::ClampInt( idAsyncNetwork::clientPrediction.GetInteger(), idAsyncNetwork::clientMaxPrediction.GetInteger(), clientPrediction );
- delta = gameTime - ( snapshotGameTime + clientPrediction );
- clientPredictTime -= ( delta / PREDICTION_FAST_ADJUST ) + ( 1 - ( INTSIGNBITSET( delta ) << 1 ) );
- lastSnapshotTime = clientTime;
- if ( idAsyncNetwork::verbose.GetInteger() == 2 ) {
- common->Printf( "received snapshot, gameInitId = %d, gameFrame = %d, gameTime = %d\n", gameInitId, gameFrame, gameTime );
- }
- if ( numDuplicatedUsercmds && ( idAsyncNetwork::verbose.GetInteger() == 2 ) ) {
- common->Printf( "server duplicated %d user commands before snapshot %d\n", numDuplicatedUsercmds, snapshotGameFrame );
- }
- break;
- }
- default: {
- common->Printf( "unknown unreliable server message %d\n", id );
- break;
- }
- }
- }
- /*
- ==================
- idAsyncClient::ProcessReliableMessagePure
- ==================
- */
- void idAsyncClient::ProcessReliableMessagePure( const idBitMsg &msg ) {
- idBitMsg outMsg;
- byte msgBuf[ MAX_MESSAGE_SIZE ];
- int inChecksums[ MAX_PURE_PAKS ];
- int i;
- int gamePakChecksum;
- int serverGameInitId;
- session->SetGUI( NULL, NULL );
- serverGameInitId = msg.ReadLong();
- if ( serverGameInitId != gameInitId ) {
- common->DPrintf( "ignoring pure server checksum from an outdated gameInitId (%d)\n", serverGameInitId );
- return;
- }
- if ( !ValidatePureServerChecksums( serverAddress, msg ) ) {
-
- return;
- }
- if ( idAsyncNetwork::verbose.GetInteger() ) {
- common->Printf( "received new pure server info. ExecuteMapChange and report back\n" );
- }
- // it is now ok to load the next map with updated pure checksums
- sessLocal.ExecuteMapChange( true );
- // upon receiving our pure list, the server will send us SCS_INGAME and we'll start getting snapshots
- fileSystem->GetPureServerChecksums( inChecksums, -1, &gamePakChecksum );
- outMsg.Init( msgBuf, sizeof( msgBuf ) );
- outMsg.WriteByte( CLIENT_RELIABLE_MESSAGE_PURE );
- outMsg.WriteLong( gameInitId );
- i = 0;
- while ( inChecksums[ i ] ) {
- outMsg.WriteLong( inChecksums[ i++ ] );
- }
- outMsg.WriteLong( 0 );
- outMsg.WriteLong( gamePakChecksum );
- if ( !channel.SendReliableMessage( outMsg ) ) {
- common->Error( "client->server reliable messages overflow\n" );
- }
- }
- /*
- ===============
- idAsyncClient::ReadLocalizedServerString
- ===============
- */
- void idAsyncClient::ReadLocalizedServerString( const idBitMsg &msg, char *out, int maxLen ) {
- msg.ReadString( out, maxLen );
- // look up localized string. if the message is not an #str_ format, we'll just get it back unchanged
- idStr::snPrintf( out, maxLen - 1, "%s", common->GetLanguageDict()->GetString( out ) );
- }
- /*
- ==================
- idAsyncClient::ProcessReliableServerMessages
- ==================
- */
- void idAsyncClient::ProcessReliableServerMessages( void ) {
- idBitMsg msg;
- byte msgBuf[MAX_MESSAGE_SIZE];
- byte id;
- msg.Init( msgBuf, sizeof( msgBuf ) );
- while ( channel.GetReliableMessage( msg ) ) {
- id = msg.ReadByte();
- switch( id ) {
- case SERVER_RELIABLE_MESSAGE_CLIENTINFO: {
- int clientNum;
- clientNum = msg.ReadByte();
- idDict &info = sessLocal.mapSpawnData.userInfo[ clientNum ];
- bool haveBase = ( msg.ReadBits( 1 ) != 0 );
- #if ID_CLIENTINFO_TAGS
- int checksum = info.Checksum();
- int srv_checksum = msg.ReadLong();
- if ( checksum != srv_checksum ) {
- common->DPrintf( "SERVER_RELIABLE_MESSAGE_CLIENTINFO %d (haveBase: %s): != checksums srv: 0x%x local: 0x%x\n", clientNum, haveBase ? "true" : "false", checksum, srv_checksum );
- info.Print();
- } else {
- common->DPrintf( "SERVER_RELIABLE_MESSAGE_CLIENTINFO %d (haveBase: %s): checksums ok 0x%x\n", clientNum, haveBase ? "true" : "false", checksum );
- }
- #endif
- if ( haveBase ) {
- msg.ReadDeltaDict( info, &info );
- } else {
- msg.ReadDeltaDict( info, NULL );
- }
- // server forces us to a different userinfo
- if ( clientNum == idAsyncClient::clientNum ) {
- common->DPrintf( "local user info modified by server\n" );
- cvarSystem->SetCVarsFromDict( info );
- cvarSystem->ClearModifiedFlags( CVAR_USERINFO ); // don't emit back
- }
- game->SetUserInfo( clientNum, info, true, false );
- break;
- }
- case SERVER_RELIABLE_MESSAGE_SYNCEDCVARS: {
- idDict &info = sessLocal.mapSpawnData.syncedCVars;
- msg.ReadDeltaDict( info, &info );
- cvarSystem->SetCVarsFromDict( info );
- if ( !idAsyncNetwork::allowCheats.GetBool() ) {
- cvarSystem->ResetFlaggedVariables( CVAR_CHEAT );
- }
- break;
- }
- case SERVER_RELIABLE_MESSAGE_PRINT: {
- char string[MAX_STRING_CHARS];
- msg.ReadString( string, MAX_STRING_CHARS );
- common->Printf( "%s\n", string );
- break;
- }
- case SERVER_RELIABLE_MESSAGE_DISCONNECT: {
- int clientNum;
- char string[MAX_STRING_CHARS];
- clientNum = msg.ReadLong( );
- ReadLocalizedServerString( msg, string, MAX_STRING_CHARS );
- if ( clientNum == idAsyncClient::clientNum ) {
- session->Stop();
- session->MessageBox( MSG_OK, string, common->GetLanguageDict()->GetString ( "#str_04319" ), true );
- session->StartMenu();
- } else {
- common->Printf( "client %d %s\n", clientNum, string );
- cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "addChatLine \"%s^0 %s\"", sessLocal.mapSpawnData.userInfo[ clientNum ].GetString( "ui_name" ), string ) );
- sessLocal.mapSpawnData.userInfo[ clientNum ].Clear();
- }
- break;
- }
- case SERVER_RELIABLE_MESSAGE_APPLYSNAPSHOT: {
- int sequence;
- sequence = msg.ReadLong();
- if ( !game->ClientApplySnapshot( clientNum, sequence ) ) {
- session->Stop();
- common->Error( "couldn't apply snapshot %d", sequence );
- }
- break;
- }
- case SERVER_RELIABLE_MESSAGE_PURE: {
- ProcessReliableMessagePure( msg );
- break;
- }
- case SERVER_RELIABLE_MESSAGE_RELOAD: {
- if ( idAsyncNetwork::verbose.GetBool() ) {
- common->Printf( "got MESSAGE_RELOAD from server\n" );
- }
- // simply reconnect, so that if the server restarts in pure mode we can get the right list and avoid spurious reloads
- cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "reconnect\n" );
- break;
- }
- case SERVER_RELIABLE_MESSAGE_ENTERGAME: {
- SendUserInfoToServer();
- game->SetUserInfo( clientNum, sessLocal.mapSpawnData.userInfo[ clientNum ], true, false );
- cvarSystem->ClearModifiedFlags( CVAR_USERINFO );
- break;
- }
- default: {
- // pass reliable message on to game code
- game->ClientProcessReliableMessage( clientNum, msg );
- break;
- }
- }
- }
- }
- /*
- ==================
- idAsyncClient::ProcessChallengeResponseMessage
- ==================
- */
- void idAsyncClient::ProcessChallengeResponseMessage( const netadr_t from, const idBitMsg &msg ) {
- char serverGame[ MAX_STRING_CHARS ], serverGameBase[ MAX_STRING_CHARS ];
- if ( clientState != CS_CHALLENGING ) {
- common->Printf( "Unwanted challenge response received.\n" );
- return;
- }
- serverChallenge = msg.ReadLong();
- serverId = msg.ReadShort();
- msg.ReadString( serverGameBase, MAX_STRING_CHARS );
- msg.ReadString( serverGame, MAX_STRING_CHARS );
- // the server is running a different game... we need to reload in the correct fs_game
- // even pure pak checks would fail if we didn't, as there are files we may not even see atm
- // NOTE: we could read the pure list from the server at the same time and set it up for the restart
- // ( if the client can restart directly with the right pak order, then we avoid an extra reloadEngine later.. )
- if ( idStr::Icmp( cvarSystem->GetCVarString( "fs_game_base" ), serverGameBase ) ||
- idStr::Icmp( cvarSystem->GetCVarString( "fs_game" ), serverGame ) ) {
- // bug #189 - if the server is running ROE and ROE is not locally installed, refuse to connect or we might crash
- if ( !fileSystem->HasD3XP() && ( !idStr::Icmp( serverGameBase, "d3xp" ) || !idStr::Icmp( serverGame, "d3xp" ) ) ) {
- common->Printf( "The server is running Doom3: Resurrection of Evil expansion pack. RoE is not installed on this client. Aborting the connection..\n" );
- cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "disconnect\n" );
- return;
- }
- common->Printf( "The server is running a different mod (%s-%s). Restarting..\n", serverGameBase, serverGame );
- cvarSystem->SetCVarString( "fs_game_base", serverGameBase );
- cvarSystem->SetCVarString( "fs_game", serverGame );
- cmdSystem->BufferCommandText( CMD_EXEC_NOW, "reloadEngine" );
- cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "reconnect\n" );
- return;
- }
- common->Printf( "received challenge response 0x%x from %s\n", serverChallenge, Sys_NetAdrToString( from ) );
- // start sending connect packets instead of challenge request packets
- clientState = CS_CONNECTING;
- lastConnectTime = -9999;
- // take this address as the new server address. This allows
- // a server proxy to hand off connections to multiple servers
- serverAddress = from;
- }
- /*
- ==================
- idAsyncClient::ProcessConnectResponseMessage
- ==================
- */
- void idAsyncClient::ProcessConnectResponseMessage( const netadr_t from, const idBitMsg &msg ) {
- int serverGameInitId, serverGameFrame, serverGameTime;
- idDict serverSI;
- if ( clientState >= CS_CONNECTED ) {
- common->Printf( "Duplicate connect received.\n" );
- return;
- }
- if ( clientState != CS_CONNECTING ) {
- common->Printf( "Connect response packet while not connecting.\n" );
- return;
- }
- if ( !Sys_CompareNetAdrBase( from, serverAddress ) ) {
- common->Printf( "Connect response from a different server.\n" );
- common->Printf( "%s should have been %s\n", Sys_NetAdrToString( from ), Sys_NetAdrToString( serverAddress ) );
- return;
- }
- common->Printf( "received connect response from %s\n", Sys_NetAdrToString( from ) );
- channel.Init( from, clientId );
- clientNum = msg.ReadLong();
- clientState = CS_CONNECTED;
- lastPacketTime = -9999;
- serverGameInitId = msg.ReadLong();
- serverGameFrame = msg.ReadLong();
- serverGameTime = msg.ReadLong();
- msg.ReadDeltaDict( serverSI, NULL );
- InitGame( serverGameInitId, serverGameFrame, serverGameTime, serverSI );
- // load map
- session->SetGUI( NULL, NULL );
- sessLocal.ExecuteMapChange();
- clientPredictTime = clientPrediction = idMath::ClampInt( 0, idAsyncNetwork::clientMaxPrediction.GetInteger(), clientTime - lastConnectTime );
- }
- /*
- ==================
- idAsyncClient::ProcessDisconnectMessage
- ==================
- */
- void idAsyncClient::ProcessDisconnectMessage( const netadr_t from, const idBitMsg &msg ) {
- if ( clientState == CS_DISCONNECTED ) {
- common->Printf( "Disconnect packet while not connected.\n" );
- return;
- }
- if ( !Sys_CompareNetAdrBase( from, serverAddress ) ) {
- common->Printf( "Disconnect packet from unknown server.\n" );
- return;
- }
- session->Stop();
- session->MessageBox( MSG_OK, common->GetLanguageDict()->GetString ( "#str_04320" ), NULL, true );
- session->StartMenu();
- }
- /*
- ==================
- idAsyncClient::ProcessInfoResponseMessage
- ==================
- */
- void idAsyncClient::ProcessInfoResponseMessage( const netadr_t from, const idBitMsg &msg ) {
- int i, protocol, index;
- networkServer_t serverInfo;
- bool verbose = false;
- if ( from.type == NA_LOOPBACK || cvarSystem->GetCVarBool( "developer" ) ) {
- verbose = true;
- }
- serverInfo.clients = 0;
- serverInfo.adr = from;
- serverInfo.challenge = msg.ReadLong(); // challenge
- protocol = msg.ReadLong();
- if ( protocol != ASYNC_PROTOCOL_VERSION ) {
- 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 );
- return;
- }
- msg.ReadDeltaDict( serverInfo.serverInfo, NULL );
- if ( verbose ) {
- common->Printf( "server IP = %s\n", Sys_NetAdrToString( serverInfo.adr ) );
- serverInfo.serverInfo.Print();
- }
- for ( i = msg.ReadByte(); i < MAX_ASYNC_CLIENTS; i = msg.ReadByte() ) {
- serverInfo.pings[ serverInfo.clients ] = msg.ReadShort();
- serverInfo.rate[ serverInfo.clients ] = msg.ReadLong();
- msg.ReadString( serverInfo.nickname[ serverInfo.clients ], MAX_NICKLEN );
- if ( verbose ) {
- common->Printf( "client %2d: %s, ping = %d, rate = %d\n", i, serverInfo.nickname[ serverInfo.clients ], serverInfo.pings[ serverInfo.clients ], serverInfo.rate[ serverInfo.clients ] );
- }
- serverInfo.clients++;
- }
- serverInfo.OSMask = msg.ReadLong();
- index = serverList.InfoResponse( serverInfo );
- common->Printf( "%d: server %s - protocol %d.%d - %s\n", index, Sys_NetAdrToString( serverInfo.adr ), protocol >> 16, protocol & 0xffff, serverInfo.serverInfo.GetString( "si_name" ) );
- }
- /*
- ==================
- idAsyncClient::ProcessPrintMessage
- ==================
- */
- void idAsyncClient::ProcessPrintMessage( const netadr_t from, const idBitMsg &msg ) {
- char string[ MAX_STRING_CHARS ];
- int opcode;
- int game_opcode = ALLOW_YES;
- const char *retpass;
- opcode = msg.ReadLong();
- if ( opcode == SERVER_PRINT_GAMEDENY ) {
- game_opcode = msg.ReadLong();
- }
- ReadLocalizedServerString( msg, string, MAX_STRING_CHARS );
- common->Printf( "%s\n", string );
- guiNetMenu->SetStateString( "status", string );
- if ( opcode == SERVER_PRINT_GAMEDENY ) {
- if ( game_opcode == ALLOW_BADPASS ) {
- retpass = session->MessageBox( MSG_PROMPT, common->GetLanguageDict()->GetString ( "#str_04321" ), string, true, "passprompt_ok" );
- ClearPendingPackets();
- guiNetMenu->SetStateString( "status", common->GetLanguageDict()->GetString ( "#str_04322" ));
- if ( retpass ) {
- // #790
- cvarSystem->SetCVarString( "password", "" );
- cvarSystem->SetCVarString( "password", retpass );
- } else {
- cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
- }
- } else if ( game_opcode == ALLOW_NO ) {
- session->MessageBox( MSG_OK, string, common->GetLanguageDict()->GetString ( "#str_04323" ), true );
- ClearPendingPackets();
- cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
- }
- // ALLOW_NOTYET just keeps running as usual. The GUI has an abort button
- } else if ( opcode == SERVER_PRINT_BADCHALLENGE && clientState >= CS_CONNECTING ) {
- cmdSystem->BufferCommandText( CMD_EXEC_NOW, "reconnect" );
- }
- }
- /*
- ==================
- idAsyncClient::ProcessServersListMessage
- ==================
- */
- void idAsyncClient::ProcessServersListMessage( const netadr_t from, const idBitMsg &msg ) {
- if ( !Sys_CompareNetAdrBase( idAsyncNetwork::GetMasterAddress(), from ) ) {
- common->DPrintf( "received a server list from %s - not a valid master\n", Sys_NetAdrToString( from ) );
- return;
- }
- while ( msg.GetRemaingData() ) {
- int a,b,c,d;
- a = msg.ReadByte(); b = msg.ReadByte(); c = msg.ReadByte(); d = msg.ReadByte();
- serverList.AddServer( serverList.Num(), va( "%i.%i.%i.%i:%i", a, b, c, d, msg.ReadShort() ) );
- }
- }
- /*
- ==================
- idAsyncClient::ProcessAuthKeyMessage
- ==================
- */
- void idAsyncClient::ProcessAuthKeyMessage( const netadr_t from, const idBitMsg &msg ) {
- authKeyMsg_t authMsg;
- char read_string[ MAX_STRING_CHARS ];
- const char *retkey;
- authBadKeyStatus_t authBadStatus;
- int key_index;
- bool valid[ 2 ];
- idStr auth_msg;
- if ( clientState != CS_CONNECTING && !session->WaitingForGameAuth() ) {
- common->Printf( "clientState != CS_CONNECTING, not waiting for game auth, authKey ignored\n" );
- return;
- }
- authMsg = (authKeyMsg_t)msg.ReadByte();
- if ( authMsg == AUTHKEY_BADKEY ) {
- valid[ 0 ] = valid[ 1 ] = true;
- key_index = 0;
- authBadStatus = (authBadKeyStatus_t)msg.ReadByte();
- switch ( authBadStatus ) {
- case AUTHKEY_BAD_INVALID:
- valid[ 0 ] = ( msg.ReadByte() == 1 );
- valid[ 1 ] = ( msg.ReadByte() == 1 );
- idAsyncNetwork::BuildInvalidKeyMsg( auth_msg, valid );
- break;
- case AUTHKEY_BAD_BANNED:
- key_index = msg.ReadByte();
- auth_msg = common->GetLanguageDict()->GetString( va( "#str_0719%1d", 6 + key_index ) );
- auth_msg += "\n";
- auth_msg += common->GetLanguageDict()->GetString( "#str_04304" );
- valid[ key_index ] = false;
- break;
- case AUTHKEY_BAD_INUSE:
- key_index = msg.ReadByte();
- auth_msg = common->GetLanguageDict()->GetString( va( "#str_0719%1d", 8 + key_index ) );
- auth_msg += "\n";
- auth_msg += common->GetLanguageDict()->GetString( "#str_04304" );
- valid[ key_index ] = false;
- break;
- case AUTHKEY_BAD_MSG:
- // a general message explaining why this key is denied
- // no specific use for this atm. let's not clear the keys either
- msg.ReadString( read_string, MAX_STRING_CHARS );
- auth_msg = read_string;
- break;
- }
- common->DPrintf( "auth deny: %s\n", auth_msg.c_str() );
-
- // keys to be cleared. applies to both net connect and game auth
- session->ClearCDKey( valid );
- // get rid of the bad key - at least that's gonna annoy people who stole a fake key
- if ( clientState == CS_CONNECTING ) {
- while ( 1 ) {
- // here we use the auth status message
- retkey = session->MessageBox( MSG_CDKEY, auth_msg, common->GetLanguageDict()->GetString( "#str_04325" ), true );
- if ( retkey ) {
- if ( session->CheckKey( retkey, true, valid ) ) {
- cmdSystem->BufferCommandText( CMD_EXEC_NOW, "reconnect" );
- } else {
- // build a more precise message about the offline check failure
- idAsyncNetwork::BuildInvalidKeyMsg( auth_msg, valid );
- session->MessageBox( MSG_OK, auth_msg.c_str(), common->GetLanguageDict()->GetString( "#str_04327" ), true );
- continue;
- }
- } else {
- cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
- }
- break;
- }
- } else {
- // forward the auth status information to the session code
- session->CDKeysAuthReply( false, auth_msg );
- }
- } else {
- msg.ReadString( read_string, MAX_STRING_CHARS );
- cvarSystem->SetCVarString( "com_guid", read_string );
- common->Printf( "guid set to %s\n", read_string );
- session->CDKeysAuthReply( true, NULL );
- }
- }
- /*
- ==================
- idAsyncClient::ProcessVersionMessage
- ==================
- */
- void idAsyncClient::ProcessVersionMessage( const netadr_t from, const idBitMsg &msg ) {
- char string[ MAX_STRING_CHARS ];
- if ( updateState != UPDATE_SENT ) {
- common->Printf( "ProcessVersionMessage: version reply, != UPDATE_SENT\n" );
- return;
- }
- common->Printf( "A new version is available\n" );
- msg.ReadString( string, MAX_STRING_CHARS );
- updateMSG = string;
- updateDirectDownload = ( msg.ReadByte() != 0 );
- msg.ReadString( string, MAX_STRING_CHARS );
- updateURL = string;
- updateMime = (dlMime_t)msg.ReadByte();
- msg.ReadString( string, MAX_STRING_CHARS );
- updateFallback = string;
- updateState = UPDATE_READY;
- }
- /*
- ==================
- idAsyncClient::ValidatePureServerChecksums
- ==================
- */
- bool idAsyncClient::ValidatePureServerChecksums( const netadr_t from, const idBitMsg &msg ) {
- int i, numChecksums, numMissingChecksums;
- int inChecksums[ MAX_PURE_PAKS ];
- int inGamePakChecksum;
- int missingChecksums[ MAX_PURE_PAKS ];
- int missingGamePakChecksum;
- idBitMsg dlmsg;
- byte msgBuf[MAX_MESSAGE_SIZE];
- // read checksums
- // pak checksums, in a 0-terminated list
- numChecksums = 0;
- do {
- i = msg.ReadLong( );
- inChecksums[ numChecksums++ ] = i;
- // just to make sure a broken message doesn't crash us
- if ( numChecksums >= MAX_PURE_PAKS ) {
- common->Warning( "MAX_PURE_PAKS ( %d ) exceeded in idAsyncClient::ProcessPureMessage\n", MAX_PURE_PAKS );
- return false;
- }
- } while ( i );
- inChecksums[ numChecksums ] = 0;
- inGamePakChecksum = msg.ReadLong();
- fsPureReply_t reply = fileSystem->SetPureServerChecksums( inChecksums, inGamePakChecksum, missingChecksums, &missingGamePakChecksum );
- switch ( reply ) {
- case PURE_RESTART:
- // need to restart the filesystem with a different pure configuration
- cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
- // restart with the right FS configuration and get back to the server
- clientState = CS_PURERESTART;
- fileSystem->SetRestartChecksums( inChecksums, inGamePakChecksum );
- cmdSystem->BufferCommandText( CMD_EXEC_NOW, "reloadEngine" );
- return false;
- case PURE_MISSING: {
- idStr checksums;
- i = 0;
- while ( missingChecksums[ i ] ) {
- checksums += va( "0x%x ", missingChecksums[ i++ ] );
- }
- numMissingChecksums = i;
- if ( idAsyncNetwork::clientDownload.GetInteger() == 0 ) {
- // never any downloads
- idStr message = va( common->GetLanguageDict()->GetString( "#str_07210" ), Sys_NetAdrToString( from ) );
- if ( numMissingChecksums > 0 ) {
- message += va( common->GetLanguageDict()->GetString( "#str_06751" ), numMissingChecksums, checksums.c_str() );
- }
- if ( missingGamePakChecksum ) {
- message += va( common->GetLanguageDict()->GetString( "#str_06750" ), missingGamePakChecksum );
- }
- common->Printf( message );
- cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
- session->MessageBox( MSG_OK, message, common->GetLanguageDict()->GetString( "#str_06735" ), true );
- } else {
- if ( clientState >= CS_CONNECTED ) {
- // we are already connected, reconnect to negociate the paks in connectionless mode
- cmdSystem->BufferCommandText( CMD_EXEC_NOW, "reconnect" );
- return false;
- }
- // ask the server to send back download info
- common->DPrintf( "missing %d paks: %s\n", numMissingChecksums + ( missingGamePakChecksum ? 1 : 0 ), checksums.c_str() );
- if ( missingGamePakChecksum ) {
- common->DPrintf( "game code pak: 0x%x\n", missingGamePakChecksum );
- }
- // store the requested downloads
- GetDownloadRequest( missingChecksums, numMissingChecksums, missingGamePakChecksum );
- // build the download request message
- // NOTE: in a specific function?
- dlmsg.Init( msgBuf, sizeof( msgBuf ) );
- dlmsg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
- dlmsg.WriteString( "downloadRequest" );
- dlmsg.WriteLong( serverChallenge );
- dlmsg.WriteShort( clientId );
- // used to make sure the server replies to the same download request
- dlmsg.WriteLong( dlRequest );
- // special case the code pak - if we have a 0 checksum then we don't need to download it
- dlmsg.WriteLong( missingGamePakChecksum );
- // 0-terminated list of missing paks
- i = 0;
- while ( missingChecksums[ i ] ) {
- dlmsg.WriteLong( missingChecksums[ i++ ] );
- }
- dlmsg.WriteLong( 0 );
- clientPort.SendPacket( from, dlmsg.GetData(), dlmsg.GetSize() );
- }
- return false;
- }
- case PURE_NODLL:
- common->Printf( common->GetLanguageDict()->GetString( "#str_07211" ), Sys_NetAdrToString( from ) );
- cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
- return false;
- default:
- return true;
- }
- return true;
- }
- /*
- ==================
- idAsyncClient::ProcessPureMessage
- ==================
- */
- void idAsyncClient::ProcessPureMessage( const netadr_t from, const idBitMsg &msg ) {
- idBitMsg outMsg;
- byte msgBuf[ MAX_MESSAGE_SIZE ];
- int i;
- int inChecksums[ MAX_PURE_PAKS ];
- int gamePakChecksum;
- if ( clientState != CS_CONNECTING ) {
- common->Printf( "clientState != CS_CONNECTING, pure msg ignored\n" );
- return;
- }
- if ( !ValidatePureServerChecksums( from, msg ) ) {
- return;
- }
- fileSystem->GetPureServerChecksums( inChecksums, -1, &gamePakChecksum );
- outMsg.Init( msgBuf, sizeof( msgBuf ) );
- outMsg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
- outMsg.WriteString( "pureClient" );
- outMsg.WriteLong( serverChallenge );
- outMsg.WriteShort( clientId );
- i = 0;
- while ( inChecksums[ i ] ) {
- outMsg.WriteLong( inChecksums[ i++ ] );
- }
- outMsg.WriteLong( 0 );
- outMsg.WriteLong( gamePakChecksum );
- clientPort.SendPacket( from, outMsg.GetData(), outMsg.GetSize() );
- }
- /*
- ==================
- idAsyncClient::ConnectionlessMessage
- ==================
- */
- void idAsyncClient::ConnectionlessMessage( const netadr_t from, const idBitMsg &msg ) {
- char string[MAX_STRING_CHARS*2]; // M. Quinn - Even Balance - PB packets can go beyond 1024
- msg.ReadString( string, sizeof( string ) );
- // info response from a server, are accepted from any source
- if ( idStr::Icmp( string, "infoResponse" ) == 0 ) {
- ProcessInfoResponseMessage( from, msg );
- return;
- }
- // from master server:
- if ( Sys_CompareNetAdrBase( from, idAsyncNetwork::GetMasterAddress( ) ) ) {
- // server list
- if ( idStr::Icmp( string, "servers" ) == 0 ) {
- ProcessServersListMessage( from, msg );
- return;
- }
-
- if ( idStr::Icmp( string, "authKey" ) == 0 ) {
- ProcessAuthKeyMessage( from, msg );
- return;
- }
- if ( idStr::Icmp( string, "newVersion" ) == 0 ) {
- ProcessVersionMessage( from, msg );
- return;
- }
- }
- // ignore if not from the current/last server
- if ( !Sys_CompareNetAdrBase( from, serverAddress ) && ( lastRconTime + 10000 < realTime || !Sys_CompareNetAdrBase( from, lastRconAddress ) ) ) {
- common->DPrintf( "got message '%s' from bad source: %s\n", string, Sys_NetAdrToString( from ) );
- return;
- }
- // challenge response from the server we are connecting to
- if ( idStr::Icmp( string, "challengeResponse" ) == 0 ) {
- ProcessChallengeResponseMessage( from, msg );
- return;
- }
- // connect response from the server we are connecting to
- if ( idStr::Icmp( string, "connectResponse" ) == 0 ) {
- ProcessConnectResponseMessage( from, msg );
- return;
- }
- // a disconnect message from the server, which will happen if the server
- // dropped the connection but is still getting packets from this client
- if ( idStr::Icmp( string, "disconnect" ) == 0 ) {
- ProcessDisconnectMessage( from, msg );
- return;
- }
- // print request from server
- if ( idStr::Icmp( string, "print" ) == 0 ) {
- ProcessPrintMessage( from, msg );
- return;
- }
- // server pure list
- if ( idStr::Icmp( string, "pureServer" ) == 0 ) {
- ProcessPureMessage( from, msg );
- return;
- }
- if ( idStr::Icmp( string, "downloadInfo" ) == 0 ) {
- ProcessDownloadInfoMessage( from, msg );
- }
- if ( idStr::Icmp( string, "authrequired" ) == 0 ) {
- // server telling us that he's expecting an auth mode connect, just in case we're trying to connect in LAN mode
- if ( idAsyncNetwork::LANServer.GetBool() ) {
- common->Warning( "server %s requests master authorization for this client. Turning off LAN mode\n", Sys_NetAdrToString( from ) );
- idAsyncNetwork::LANServer.SetBool( false );
- }
- }
- common->DPrintf( "ignored message from %s: %s\n", Sys_NetAdrToString( from ), string );
- }
- /*
- =================
- idAsyncClient::ProcessMessage
- =================
- */
- void idAsyncClient::ProcessMessage( const netadr_t from, idBitMsg &msg ) {
- int id;
- id = msg.ReadShort();
- // check for a connectionless packet
- if ( id == CONNECTIONLESS_MESSAGE_ID ) {
- ConnectionlessMessage( from, msg );
- return;
- }
- if ( clientState < CS_CONNECTED ) {
- return; // can't be a valid sequenced packet
- }
- if ( msg.GetRemaingData() < 4 ) {
- common->DPrintf( "%s: tiny packet\n", Sys_NetAdrToString( from ) );
- return;
- }
- // is this a packet from the server
- if ( !Sys_CompareNetAdrBase( from, channel.GetRemoteAddress() ) || id != serverId ) {
- common->DPrintf( "%s: sequenced server packet without connection\n", Sys_NetAdrToString( from ) );
- return;
- }
- if ( !channel.Process( from, clientTime, msg, serverMessageSequence ) ) {
- return; // out of order, duplicated, fragment, etc.
- }
- lastPacketTime = clientTime;
- ProcessReliableServerMessages();
- ProcessUnreliableServerMessage( msg );
- }
- /*
- ==================
- idAsyncClient::SetupConnection
- ==================
- */
- void idAsyncClient::SetupConnection( void ) {
- idBitMsg msg;
- byte msgBuf[MAX_MESSAGE_SIZE];
- if ( clientTime - lastConnectTime < SETUP_CONNECTION_RESEND_TIME ) {
- return;
- }
- if ( clientState == CS_CHALLENGING ) {
- common->Printf( "sending challenge to %s\n", Sys_NetAdrToString( serverAddress ) );
- msg.Init( msgBuf, sizeof( msgBuf ) );
- msg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
- msg.WriteString( "challenge" );
- msg.WriteLong( clientId );
- clientPort.SendPacket( serverAddress, msg.GetData(), msg.GetSize() );
- } else if ( clientState == CS_CONNECTING ) {
- common->Printf( "sending connect to %s with challenge 0x%x\n", Sys_NetAdrToString( serverAddress ), serverChallenge );
- msg.Init( msgBuf, sizeof( msgBuf ) );
- msg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
- msg.WriteString( "connect" );
- msg.WriteLong( ASYNC_PROTOCOL_VERSION );
- #if ID_FAKE_PURE
- // fake win32 OS - might need to adapt depending on the case
- msg.WriteShort( 0 );
- #else
- msg.WriteShort( BUILD_OS_ID );
- #endif
- msg.WriteLong( clientDataChecksum );
- msg.WriteLong( serverChallenge );
- msg.WriteShort( clientId );
- msg.WriteLong( cvarSystem->GetCVarInteger( "net_clientMaxRate" ) );
- msg.WriteString( cvarSystem->GetCVarString( "com_guid" ) );
- msg.WriteString( cvarSystem->GetCVarString( "password" ), -1, false );
- // do not make the protocol depend on PB
- msg.WriteShort( 0 );
- clientPort.SendPacket( serverAddress, msg.GetData(), msg.GetSize() );
-
- if ( idAsyncNetwork::LANServer.GetBool() ) {
- common->Printf( "net_LANServer is set, connecting in LAN mode\n" );
- } else {
- // emit a cd key authorization request
- // modified at protocol 1.37 for XP key addition
- msg.BeginWriting();
- msg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
- msg.WriteString( "clAuth" );
- msg.WriteLong( ASYNC_PROTOCOL_VERSION );
- msg.WriteNetadr( serverAddress );
- // if we don't have a com_guid, this will request a direct reply from auth with it
- msg.WriteByte( cvarSystem->GetCVarString( "com_guid" )[0] ? 1 : 0 );
- // send the main key, and flag an extra byte to add XP key
- msg.WriteString( session->GetCDKey( false ) );
- const char *xpkey = session->GetCDKey( true );
- msg.WriteByte( xpkey ? 1 : 0 );
- if ( xpkey ) {
- msg.WriteString( xpkey );
- }
- clientPort.SendPacket( idAsyncNetwork::GetMasterAddress(), msg.GetData(), msg.GetSize() );
- }
- } else {
- return;
- }
- lastConnectTime = clientTime;
- }
- /*
- ==================
- idAsyncClient::SendReliableGameMessage
- ==================
- */
- void idAsyncClient::SendReliableGameMessage( const idBitMsg &msg ) {
- idBitMsg outMsg;
- byte msgBuf[MAX_MESSAGE_SIZE];
- if ( clientState < CS_INGAME ) {
- return;
- }
- outMsg.Init( msgBuf, sizeof( msgBuf ) );
- outMsg.WriteByte( CLIENT_RELIABLE_MESSAGE_GAME );
- outMsg.WriteData( msg.GetData(), msg.GetSize() );
- if ( !channel.SendReliableMessage( outMsg ) ) {
- common->Error( "client->server reliable messages overflow\n" );
- }
- }
- /*
- ==================
- idAsyncClient::Idle
- ==================
- */
- void idAsyncClient::Idle( void ) {
- // also need to read mouse for the connecting guis
- usercmdGen->GetDirectUsercmd();
- SendEmptyToServer();
- }
- /*
- ==================
- idAsyncClient::UpdateTime
- ==================
- */
- int idAsyncClient::UpdateTime( int clamp ) {
- int time, msec;
- time = Sys_Milliseconds();
- msec = idMath::ClampInt( 0, clamp, time - realTime );
- realTime = time;
- clientTime += msec;
- return msec;
- }
- /*
- ==================
- idAsyncClient::RunFrame
- ==================
- */
- void idAsyncClient::RunFrame( void ) {
- int msec, size;
- bool newPacket;
- idBitMsg msg;
- byte msgBuf[MAX_MESSAGE_SIZE];
- netadr_t from;
- msec = UpdateTime( 100 );
- //bc makes typing work, but screws up a lot of things. this is a horrifying hack.
- if (cvarSystem->GetCVarBool("initdeck") == true)
- {
- cvarSystem->SetCVarBool("initdeck", false, 0);
- bool lastPredictFrame = ( snapshotGameFrame + 1 >= gameFrame && gameTimeResidual + clientPredictTime < USERCMD_MSEC );
- gameReturn_t ret = game->ClientPrediction( clientNum, userCmds[ snapshotGameFrame & ( MAX_USERCMD_BACKUP - 1 ) ], lastPredictFrame );
- idAsyncNetwork::ExecuteSessionCommand( ret.sessionCommand );
- }
- if ( !clientPort.GetPort() )
- {
- return; //BC single player exits here.
- }
- // handle ongoing pk4 downloads and patch downloads
- HandleDownloads();
- gameTimeResidual += msec;
- // spin in place processing incoming packets until enough time lapsed to run a new game frame
- do {
- do {
- // blocking read with game time residual timeout
- newPacket = clientPort.GetPacketBlocking( from, msgBuf, size, sizeof( msgBuf ), USERCMD_MSEC - ( gameTimeResidual + clientPredictTime ) - 1 );
- if ( newPacket ) {
- msg.Init( msgBuf, sizeof( msgBuf ) );
- msg.SetSize( size );
- msg.BeginReading();
- ProcessMessage( from, msg );
- }
- msec = UpdateTime( 100 );
- gameTimeResidual += msec;
- } while( newPacket );
- } while( gameTimeResidual + clientPredictTime < USERCMD_MSEC );
- // update server list
- serverList.RunFrame();
- if ( clientState == CS_DISCONNECTED ) {
- usercmdGen->GetDirectUsercmd();
- gameTimeResidual = USERCMD_MSEC - 1;
- clientPredictTime = 0;
- return;
- }
- if ( clientState == CS_PURERESTART ) {
- clientState = CS_DISCONNECTED;
- Reconnect();
- gameTimeResidual = USERCMD_MSEC - 1;
- clientPredictTime = 0;
- return;
- }
- // if not connected setup a connection
- if ( clientState < CS_CONNECTED ) {
- // also need to read mouse for the connecting guis
- usercmdGen->GetDirectUsercmd();
- SetupConnection();
- gameTimeResidual = USERCMD_MSEC - 1;
- clientPredictTime = 0;
- return;
- }
- if ( CheckTimeout() ) {
- return;
- }
- // if not yet in the game send empty messages to keep data flowing through the channel
- if ( clientState < CS_INGAME ) {
- Idle();
- gameTimeResidual = 0;
- return;
- }
- // check for user info changes
- if ( cvarSystem->GetModifiedFlags() & CVAR_USERINFO ) {
- game->ThrottleUserInfo( );
- SendUserInfoToServer( );
- game->SetUserInfo( clientNum, sessLocal.mapSpawnData.userInfo[ clientNum ], true, false );
- cvarSystem->ClearModifiedFlags( CVAR_USERINFO );
- }
- if ( gameTimeResidual + clientPredictTime >= USERCMD_MSEC ) {
- lastFrameDelta = 0;
- }
- // generate user commands for the predicted time
- while ( gameTimeResidual + clientPredictTime >= USERCMD_MSEC ) {
- // send the user commands of this client to the server
- SendUsercmdsToServer();
- // update time
- gameFrame++;
- gameTime += USERCMD_MSEC;
- gameTimeResidual -= USERCMD_MSEC;
- // run from the snapshot up to the local game frame
- while ( snapshotGameFrame < gameFrame ) {
- lastFrameDelta++;
- // duplicate usercmds for clients if no new ones are available
- DuplicateUsercmds( snapshotGameFrame, snapshotGameTime );
- // indicate the last prediction frame before a render
- bool lastPredictFrame = ( snapshotGameFrame + 1 >= gameFrame && gameTimeResidual + clientPredictTime < USERCMD_MSEC );
- // run client prediction
- gameReturn_t ret = game->ClientPrediction( clientNum, userCmds[ snapshotGameFrame & ( MAX_USERCMD_BACKUP - 1 ) ], lastPredictFrame );
- idAsyncNetwork::ExecuteSessionCommand( ret.sessionCommand );
- snapshotGameFrame++;
- snapshotGameTime += USERCMD_MSEC;
- }
- }
- }
- /*
- ==================
- idAsyncClient::PacifierUpdate
- ==================
- */
- void idAsyncClient::PacifierUpdate( void ) {
- if ( !IsActive() ) {
- return;
- }
- realTime = Sys_Milliseconds();
- SendEmptyToServer( false, true );
- }
- void idAsyncClient::SendBugreport(const char *text)
- {
- //common->Printf("hi hi\n");
- //sys->OpenURL( "http://blendogames.com/qc/reportabug.php", false );
- //bc use doom3's file download system to send qc bug reports.
- backgroundDownload.completed = false;
- backgroundDownload.opcode = DLTYPE_URL;
- backgroundDownload.f = NULL;
- backgroundDownload.url.status = DL_WAIT;
- backgroundDownload.url.dlnow = 0;
- backgroundDownload.url.dltotal = 0;
- backgroundDownload.url.url = va("http://blendogames.com/qc/reportabug.php?text=%s", text);
- fileSystem->BackgroundDownload( &backgroundDownload );
- }
- /*
- ==================
- idAsyncClient::SendVersionCheck
- ==================
- */
- void idAsyncClient::SendVersionCheck( bool fromMenu ) {
- idBitMsg msg;
- byte msgBuf[MAX_MESSAGE_SIZE];
- if ( updateState != UPDATE_NONE && !fromMenu ) {
- common->DPrintf( "up-to-date check was already performed\n" );
- return;
- }
- InitPort();
- msg.Init( msgBuf, sizeof( msgBuf ) );
- msg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
- msg.WriteString( "versionCheck" );
- msg.WriteLong( ASYNC_PROTOCOL_VERSION );
- msg.WriteShort( BUILD_OS_ID );
- msg.WriteString( cvarSystem->GetCVarString( "si_version" ) );
- msg.WriteString( cvarSystem->GetCVarString( "com_guid" ) );
- clientPort.SendPacket( idAsyncNetwork::GetMasterAddress(), msg.GetData(), msg.GetSize() );
- common->DPrintf( "sent a version check request\n" );
- updateState = UPDATE_SENT;
- updateSentTime = clientTime;
- showUpdateMessage = fromMenu;
- }
- /*
- ==================
- idAsyncClient::SendVersionDLUpdate
- sending those packets is not strictly necessary. just a way to tell the update server
- about what is going on. allows the update server to have a more precise view of the overall
- network load for the updates
- ==================
- */
- void idAsyncClient::SendVersionDLUpdate( int state ) {
- idBitMsg msg;
- byte msgBuf[MAX_MESSAGE_SIZE];
- msg.Init( msgBuf, sizeof( msgBuf ) );
- msg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
- msg.WriteString( "versionDL" );
- msg.WriteLong( ASYNC_PROTOCOL_VERSION );
- msg.WriteShort( state );
- clientPort.SendPacket( idAsyncNetwork::GetMasterAddress(), msg.GetData(), msg.GetSize() );
- }
- /*
- ==================
- idAsyncClient::HandleDownloads
- ==================
- */
- void idAsyncClient::HandleDownloads( void ) {
- if ( updateState == UPDATE_SENT && clientTime > updateSentTime + 2000 ) {
- // timing out on no reply
- updateState = UPDATE_DONE;
- if ( showUpdateMessage ) {
- session->MessageBox( MSG_OK, common->GetLanguageDict()->GetString ( "#str_04839" ), common->GetLanguageDict()->GetString ( "#str_04837" ), true );
- showUpdateMessage = false;
- }
- common->DPrintf( "No update available\n" );
- } else if ( backgroundDownload.completed ) {
- // only enter these if the download slot is free
- if ( updateState == UPDATE_READY ) {
- //
- if ( session->MessageBox( MSG_YESNO, updateMSG, common->GetLanguageDict()->GetString ( "#str_04330" ), true, "yes" )[0] ) {
- if ( !updateDirectDownload ) {
- sys->OpenURL( updateURL, true );
- updateState = UPDATE_DONE;
- } else {
- // we're just creating the file at toplevel inside fs_savepath
- updateURL.ExtractFileName( updateFile );
- idFile_Permanent *f = static_cast< idFile_Permanent *>( fileSystem->OpenFileWrite( updateFile ) );
- dltotal = 0;
- dlnow = 0;
- backgroundDownload.completed = false;
- backgroundDownload.opcode = DLTYPE_URL;
- backgroundDownload.f = f;
- backgroundDownload.url.status = DL_WAIT;
- backgroundDownload.url.dlnow = 0;
- backgroundDownload.url.dltotal = 0;
- backgroundDownload.url.url = updateURL;
- fileSystem->BackgroundDownload( &backgroundDownload );
- updateState = UPDATE_DLING;
- SendVersionDLUpdate( 0 );
- session->DownloadProgressBox( &backgroundDownload, va( "Downloading %s\n", updateFile.c_str() ) );
- updateState = UPDATE_DONE;
- if ( backgroundDownload.url.status == DL_DONE ) {
- SendVersionDLUpdate( 1 );
- idStr fullPath = f->GetFullPath();
- fileSystem->CloseFile( f );
- if ( session->MessageBox( MSG_YESNO, common->GetLanguageDict()->GetString ( "#str_04331" ), common->GetLanguageDict()->GetString ( "#str_04332" ), true, "yes" )[0] ) {
- if ( updateMime == FILE_EXEC ) {
- sys->StartProcess( fullPath, true );
- } else {
- sys->OpenURL( va( "file://%s", fullPath.c_str() ), true );
- }
- } else {
- session->MessageBox( MSG_OK, va( common->GetLanguageDict()->GetString ( "#str_04333" ), fullPath.c_str() ), common->GetLanguageDict()->GetString ( "#str_04334" ), true );
- }
- } else {
- if ( backgroundDownload.url.dlerror[ 0 ] ) {
- common->Warning( "update download failed. curl error: %s", backgroundDownload.url.dlerror );
- }
- SendVersionDLUpdate( 2 );
- idStr name = f->GetName();
- fileSystem->CloseFile( f );
- fileSystem->RemoveFile( name );
- session->MessageBox( MSG_OK, common->GetLanguageDict()->GetString ( "#str_04335" ), common->GetLanguageDict()->GetString ( "#str_04336" ), true );
- if ( updateFallback.Length() ) {
- sys->OpenURL( updateFallback.c_str(), true );
- } else {
- common->Printf( "no fallback URL\n" );
- }
- }
- }
- } else {
- updateState = UPDATE_DONE;
- }
- } else if ( dlList.Num() ) {
- int numPaks = dlList.Num();
- int pakCount = 1;
- int progress_start, progress_end;
- currentDlSize = 0;
- do {
- if ( dlList[ 0 ].url[ 0 ] == '\0' ) {
- // ignore empty files
- dlList.RemoveIndex( 0 );
- continue;
- }
- common->Printf( "start download for %s\n", dlList[ 0 ].url.c_str() );
- idFile_Permanent *f = static_cast< idFile_Permanent *>( fileSystem->MakeTemporaryFile( ) );
- if ( !f ) {
- common->Warning( "could not create temporary file" );
- dlList.Clear();
- return;
- }
- backgroundDownload.completed = false;
- backgroundDownload.opcode = DLTYPE_URL;
- backgroundDownload.f = f;
- backgroundDownload.url.status = DL_WAIT;
- backgroundDownload.url.dlnow = 0;
- backgroundDownload.url.dltotal = dlList[ 0 ].size;
- backgroundDownload.url.url = dlList[ 0 ].url;
- fileSystem->BackgroundDownload( &backgroundDownload );
- idStr dltitle;
- // "Downloading %s"
- sprintf( dltitle, common->GetLanguageDict()->GetString( "#str_07213" ), dlList[ 0 ].filename.c_str() );
- if ( numPaks > 1 ) {
- dltitle += va( " (%d/%d)", pakCount, numPaks );
- }
- if ( totalDlSize ) {
- progress_start = (int)( (float)currentDlSize * 100.0f / (float)totalDlSize );
- progress_end = (int)( (float)( currentDlSize + dlList[ 0 ].size ) * 100.0f / (float)totalDlSize );
- } else {
- progress_start = 0;
- progress_end = 100;
- }
- session->DownloadProgressBox( &backgroundDownload, dltitle, progress_start, progress_end );
- if ( backgroundDownload.url.status == DL_DONE ) {
- idFile *saveas;
- const int CHUNK_SIZE = 1024 * 1024;
- byte *buf;
- int remainlen;
- int readlen;
- int retlen;
- int checksum;
- common->Printf( "file downloaded\n" );
- idStr finalPath = cvarSystem->GetCVarString( "fs_savepath" );
- finalPath.AppendPath( dlList[ 0 ].filename );
- fileSystem->CreateOSPath( finalPath );
- // do the final copy ourselves so we do by small chunks in case the file is big
- saveas = fileSystem->OpenExplicitFileWrite( finalPath );
- buf = (byte*)Mem_Alloc( CHUNK_SIZE );
- f->Seek( 0, FS_SEEK_END );
- remainlen = f->Tell();
- f->Seek( 0, FS_SEEK_SET );
- while ( remainlen ) {
- readlen = Min( remainlen, CHUNK_SIZE );
- retlen = f->Read( buf, readlen );
- if ( retlen != readlen ) {
- common->FatalError( "short read %d of %d in idFileSystem::HandleDownload", retlen, readlen );
- }
- retlen = saveas->Write( buf, readlen );
- if ( retlen != readlen ) {
- common->FatalError( "short write %d of %d in idFileSystem::HandleDownload", retlen, readlen );
- }
- remainlen -= readlen;
- }
- fileSystem->CloseFile( f );
- fileSystem->CloseFile( saveas );
- common->Printf( "saved as %s\n", finalPath.c_str() );
- Mem_Free( buf );
-
- // add that file to our paks list
- checksum = fileSystem->AddZipFile( dlList[ 0 ].filename );
- // verify the checksum to be what the server says
- if ( !checksum || checksum != dlList[ 0 ].checksum ) {
- // "pak is corrupted ( checksum 0x%x, expected 0x%x )"
- session->MessageBox( MSG_OK, va( common->GetLanguageDict()->GetString( "#str_07214" ) , checksum, dlList[0].checksum ), "Download failed", true );
- fileSystem->RemoveFile( dlList[ 0 ].filename );
- dlList.Clear();
- return;
- }
- currentDlSize += dlList[ 0 ].size;
-
- } else {
- common->Warning( "download failed: %s", dlList[ 0 ].url.c_str() );
- if ( backgroundDownload.url.dlerror[ 0 ] ) {
- common->Warning( "curl error: %s", backgroundDownload.url.dlerror );
- }
- // "The download failed or was cancelled"
- // "Download failed"
- session->MessageBox( MSG_OK, common->GetLanguageDict()->GetString( "#str_07215" ), common->GetLanguageDict()->GetString( "#str_07216" ), true );
- dlList.Clear();
- return;
- }
- pakCount++;
- dlList.RemoveIndex( 0 );
- } while ( dlList.Num() );
-
- // all downloads successful - do the dew
- cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "reconnect\n" );
- }
- }
- }
- /*
- ===============
- idAsyncClient::SendAuthCheck
- ===============
- */
- bool idAsyncClient::SendAuthCheck( const char *cdkey, const char *xpkey ) {
- idBitMsg msg;
- byte msgBuf[MAX_MESSAGE_SIZE];
- msg.Init( msgBuf, sizeof( msgBuf ) );
- msg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
- msg.WriteString( "gameAuth" );
- msg.WriteLong( ASYNC_PROTOCOL_VERSION );
- msg.WriteByte( cdkey ? 1 : 0 );
- msg.WriteString( cdkey ? cdkey : "" );
- msg.WriteByte( xpkey ? 1 : 0 );
- msg.WriteString( xpkey ? xpkey : "" );
- InitPort();
- clientPort.SendPacket( idAsyncNetwork::GetMasterAddress(), msg.GetData(), msg.GetSize() );
- return true;
- }
- /*
- ===============
- idAsyncClient::CheckTimeout
- ===============
- */
- bool idAsyncClient::CheckTimeout( void ) {
- if ( lastPacketTime > 0 && ( lastPacketTime + idAsyncNetwork::clientServerTimeout.GetInteger()*1000 < clientTime ) ) {
- session->StopBox();
- session->MessageBox( MSG_OK, common->GetLanguageDict()->GetString ( "#str_04328" ), common->GetLanguageDict()->GetString ( "#str_04329" ), true );
- cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
- return true;
- }
- return false;
- }
- /*
- ===============
- idAsyncClient::ProcessDownloadInfoMessage
- ===============
- */
- void idAsyncClient::ProcessDownloadInfoMessage( const netadr_t from, const idBitMsg &msg ) {
- char buf[ MAX_STRING_CHARS ];
- int srvDlRequest = msg.ReadLong();
- int infoType = msg.ReadByte();
- int pakDl;
- int pakIndex;
-
- pakDlEntry_t entry;
- bool gotAllFiles = true;
- idStr sizeStr;
- bool gotGame = false;
- if ( dlRequest == -1 || srvDlRequest != dlRequest ) {
- common->Warning( "bad download id from server, ignored" );
- return;
- }
- // mark the dlRequest as dead now whatever how we process it
- dlRequest = -1;
- if ( infoType == SERVER_DL_REDIRECT ) {
- msg.ReadString( buf, MAX_STRING_CHARS );
- cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
- // "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?"
- // "Missing required files"
- if ( session->MessageBox( MSG_YESNO, va( common->GetLanguageDict()->GetString( "#str_07217" ), buf ),
- common->GetLanguageDict()->GetString( "#str_07218" ), true, "yes" )[ 0 ] ) {
- sys->OpenURL( buf, true );
- }
- } else if ( infoType == SERVER_DL_LIST ) {
- cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
- if ( dlList.Num() ) {
- common->Warning( "tried to process a download list while already busy downloading things" );
- return;
- }
- // read the URLs, check against what we requested, prompt for download
- pakIndex = -1;
- totalDlSize = 0;
- do {
- pakIndex++;
- pakDl = msg.ReadByte();
- if ( pakDl == SERVER_PAK_YES ) {
- if ( pakIndex == 0 ) {
- gotGame = true;
- }
- msg.ReadString( buf, MAX_STRING_CHARS );
- entry.filename = buf;
- msg.ReadString( buf, MAX_STRING_CHARS );
- entry.url = buf;
- entry.size = msg.ReadLong();
- // checksums are not transmitted, we read them from the dl request we sent
- entry.checksum = dlChecksums[ pakIndex ];
- totalDlSize += entry.size;
- dlList.Append( entry );
- common->Printf( "download %s from %s ( 0x%x )\n", entry.filename.c_str(), entry.url.c_str(), entry.checksum );
- } else if ( pakDl == SERVER_PAK_NO ) {
- msg.ReadString( buf, MAX_STRING_CHARS );
- entry.filename = buf;
- entry.url = "";
- entry.size = 0;
- entry.checksum = 0;
- dlList.Append( entry );
- // first pak is game pak, only fail it if we actually requested it
- if ( pakIndex != 0 || dlChecksums[ 0 ] != 0 ) {
- common->Printf( "no download offered for %s ( 0x%x )\n", entry.filename.c_str(), dlChecksums[ pakIndex ] );
- gotAllFiles = false;
- }
- } else {
- assert( pakDl == SERVER_PAK_END );
- }
- } while ( pakDl != SERVER_PAK_END );
- if ( dlList.Num() < dlCount ) {
- common->Printf( "%d files were ignored by the server\n", dlCount - dlList.Num() );
- gotAllFiles = false;
- }
- sizeStr.BestUnit( "%.2f", totalDlSize, MEASURE_SIZE );
- cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
- if ( totalDlSize == 0 ) {
- // was no downloadable stuff for us
- // "Can't connect to the pure server: no downloads offered"
- // "Missing required files"
- dlList.Clear();
- session->MessageBox( MSG_OK, common->GetLanguageDict()->GetString( "#str_07219" ), common->GetLanguageDict()->GetString( "#str_07218" ), true );
- return;
- }
- bool asked = false;
- if ( gotGame ) {
- asked = true;
- // "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."
- // "Missing game binaries"
- if ( !session->MessageBox( MSG_YESNO, common->GetLanguageDict()->GetString( "#str_07220" ), common->GetLanguageDict()->GetString( "#str_07221" ), true, "yes" )[ 0 ] ) {
- dlList.Clear();
- return;
- }
- }
- if ( !gotAllFiles ) {
- asked = true;
- // "The server only offers to download some of the files required to connect ( %s ). Download anyway?"
- // "Missing required files"
- if ( !session->MessageBox( MSG_YESNO, va( common->GetLanguageDict()->GetString( "#str_07222" ), sizeStr.c_str() ),
- common->GetLanguageDict()->GetString( "#str_07218" ), true, "yes" )[ 0 ] ) {
- dlList.Clear();
- return;
- }
- }
- if ( !asked && idAsyncNetwork::clientDownload.GetInteger() == 1 ) {
- // "You need to download some files to connect to this server ( %s ), proceed?"
- // "Missing required files"
- if ( !session->MessageBox( MSG_YESNO, va( common->GetLanguageDict()->GetString( "#str_07224" ), sizeStr.c_str() ),
- common->GetLanguageDict()->GetString( "#str_07218" ), true, "yes" )[ 0 ] ) {
- dlList.Clear();
- return;
- }
- }
- } else {
- cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
- // "You are missing some files to connect to this server, and the server doesn't provide downloads."
- // "Missing required files"
- session->MessageBox( MSG_OK, common->GetLanguageDict()->GetString( "#str_07223" ), common->GetLanguageDict()->GetString( "#str_07218" ), true );
- }
- }
- /*
- ===============
- idAsyncClient::GetDownloadRequest
- ===============
- */
- int idAsyncClient::GetDownloadRequest( const int checksums[ MAX_PURE_PAKS ], int count, int gamePakChecksum ) {
- assert( !checksums[ count ] ); // 0-terminated
- if ( memcmp( dlChecksums + 1, checksums, sizeof( int ) * count ) || gamePakChecksum != dlChecksums[ 0 ] ) {
- idRandom newreq;
- dlChecksums[ 0 ] = gamePakChecksum;
- memcpy( dlChecksums + 1, checksums, sizeof( int ) * MAX_PURE_PAKS );
- newreq.SetSeed( Sys_Milliseconds() );
- dlRequest = newreq.RandomInt();
- dlCount = count + ( gamePakChecksum ? 1 : 0 );
- return dlRequest;
- }
- // this is the same dlRequest, we haven't heard from the server. keep the same id
- return dlRequest;
- }
|