123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826 |
- /*
- ===========================================================================
- 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 MIN_RECONNECT_TIME = 2000;
- const int EMPTY_RESEND_TIME = 500;
- const int PING_RESEND_TIME = 500;
- const int NOINPUT_IDLE_TIME = 30000;
- const int HEARTBEAT_MSEC = 5*60*1000;
- // must be kept in sync with authReplyMsg_t
- const char* authReplyMsg[] = {
- // "Waiting for authorization",
- "#str_07204",
- // "Client unknown to auth",
- "#str_07205",
- // "Access denied - CD Key in use",
- "#str_07206",
- // "Auth custom message", // placeholder - we propagate a message from the master
- "#str_07207",
- // "Authorize Server - Waiting for client"
- "#str_07208"
- };
- const char* authReplyStr[] = {
- "AUTH_NONE",
- "AUTH_OK",
- "AUTH_WAIT",
- "AUTH_DENY"
- };
- /*
- ==================
- idAsyncServer::idAsyncServer
- ==================
- */
- idAsyncServer::idAsyncServer( void ) {
- int i;
- active = false;
- realTime = 0;
- serverTime = 0;
- serverId = 0;
- serverDataChecksum = 0;
- localClientNum = -1;
- gameInitId = 0;
- gameFrame = 0;
- gameTime = 0;
- gameTimeResidual = 0;
- memset( challenges, 0, sizeof( challenges ) );
- memset( userCmds, 0, sizeof( userCmds ) );
- for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
- ClearClient( i );
- }
- serverReloadingEngine = false;
- nextHeartbeatTime = 0;
- nextAsyncStatsTime = 0;
- noRconOutput = true;
- lastAuthTime = 0;
- memset( stats_outrate, 0, sizeof( stats_outrate ) );
- stats_current = 0;
- stats_average_sum = 0;
- stats_max = 0;
- stats_max_index = 0;
- }
- /*
- ==================
- idAsyncServer::InitPort
- ==================
- */
- bool idAsyncServer::InitPort( void ) {
- int lastPort;
- // if this is the first time we have spawned a server, open the UDP port
- if ( !serverPort.GetPort() ) {
- if ( cvarSystem->GetCVarInteger( "net_port" ) != 0 ) {
- if ( !serverPort.InitForPort( cvarSystem->GetCVarInteger( "net_port" ) ) ) {
- common->Printf( "Unable to open server on port %d (net_port)\n", cvarSystem->GetCVarInteger( "net_port" ) );
- return false;
- }
- } else {
- // scan for multiple ports, in case other servers are running on this IP already
- for ( lastPort = 0; lastPort < NUM_SERVER_PORTS; lastPort++ ) {
- if ( serverPort.InitForPort( PORT_SERVER + lastPort ) ) {
- break;
- }
- }
- if ( lastPort >= NUM_SERVER_PORTS ) {
- common->Printf( "Unable to open server network port.\n" );
- return false;
- }
- }
- }
- return true;
- }
- /*
- ==================
- idAsyncServer::ClosePort
- ==================
- */
- void idAsyncServer::ClosePort( void ) {
- int i;
- serverPort.Close();
- for ( i = 0; i < MAX_CHALLENGES; i++ ) {
- challenges[ i ].authReplyPrint.Clear();
- }
- }
- /*
- ==================
- idAsyncServer::Spawn
- ==================
- */
- void idAsyncServer::Spawn( void ) {
- int i, size;
- byte msgBuf[MAX_MESSAGE_SIZE];
- netadr_t from;
- // shutdown any current game
- session->Stop();
- if ( active ) {
- return;
- }
- if ( !InitPort() ) {
- return;
- }
- // trash any currently pending packets
- while( serverPort.GetPacket( from, msgBuf, size, sizeof( msgBuf ) ) ) {
- }
- // reset cheats cvars
- if ( !idAsyncNetwork::allowCheats.GetBool() ) {
- cvarSystem->ResetFlaggedVariables( CVAR_CHEAT );
- }
- memset( challenges, 0, sizeof( challenges ) );
- memset( userCmds, 0, sizeof( userCmds ) );
- for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
- ClearClient( i );
- }
- common->Printf( "Server spawned on port %i.\n", serverPort.GetPort() );
- // calculate a checksum on some of the essential data used
- serverDataChecksum = declManager->GetChecksum();
- // get a pseudo random server id, but don't use the id which is reserved for connectionless packets
- serverId = Sys_Milliseconds() & CONNECTIONLESS_MESSAGE_ID_MASK;
- active = true;
- nextHeartbeatTime = 0;
- nextAsyncStatsTime = 0;
- ExecuteMapChange();
- }
- /*
- ==================
- idAsyncServer::Kill
- ==================
- */
- void idAsyncServer::Kill( void ) {
- int i, j;
- if ( !active ) {
- return;
- }
- // drop all clients
- for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
- DropClient( i, "#str_07135" );
- }
- // send some empty messages to the zombie clients to make sure they disconnect
- for ( j = 0; j < 4; j++ ) {
- for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
- if ( clients[i].clientState == SCS_ZOMBIE ) {
- if ( clients[i].channel.UnsentFragmentsLeft() ) {
- clients[i].channel.SendNextFragment( serverPort, serverTime );
- } else {
- SendEmptyToClient( i, true );
- }
- }
- }
- Sys_Sleep( 10 );
- }
- // reset any pureness
- fileSystem->ClearPureChecksums();
- active = false;
- // shutdown any current game
- session->Stop();
- }
- /*
- ==================
- idAsyncServer::ExecuteMapChange
- ==================
- */
- void idAsyncServer::ExecuteMapChange( void ) {
- int i;
- idBitMsg msg;
- byte msgBuf[MAX_MESSAGE_SIZE];
- idStr mapName;
- findFile_t ff;
- bool addonReload = false;
- char bestGameType[ MAX_STRING_CHARS ];
- assert( active );
- // reset any pureness
- fileSystem->ClearPureChecksums();
- // make sure the map/gametype combo is good
- game->GetBestGameType( cvarSystem->GetCVarString("si_map"), cvarSystem->GetCVarString("si_gametype"), bestGameType );
- cvarSystem->SetCVarString("si_gametype", bestGameType );
- // initialize map settings
- cmdSystem->BufferCommandText( CMD_EXEC_NOW, "rescanSI" );
- sprintf( mapName, "maps/%s", sessLocal.mapSpawnData.serverInfo.GetString( "si_map" ) );
- mapName.SetFileExtension( ".map" );
- ff = fileSystem->FindFile( mapName, !serverReloadingEngine );
- switch( ff ) {
- case FIND_NO:
- common->Printf( "Can't find map %s\n", mapName.c_str() );
- cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "disconnect\n" );
- return;
- case FIND_ADDON:
- // NOTE: we have no problem with addon dependencies here because if the map is in
- // an addon pack that's already on search list, then all it's deps are assumed to be on search as well
- common->Printf( "map %s is in an addon pak - reloading\n", mapName.c_str() );
- addonReload = true;
- break;
- default:
- break;
- }
- // if we are asked to do a full reload, the strategy is completely different
- if ( !serverReloadingEngine && ( addonReload || idAsyncNetwork::serverReloadEngine.GetInteger() != 0 ) ) {
- if ( idAsyncNetwork::serverReloadEngine.GetInteger() != 0 ) {
- common->Printf( "net_serverReloadEngine enabled - doing a full reload\n" );
- }
- // tell the clients to reconnect
- // FIXME: shouldn't they wait for the new pure list, then reload?
- // in a lot of cases this is going to trigger two reloadEngines for the clients
- // one to restart, the other one to set paks right ( with addon for instance )
- // can fix by reconnecting without reloading and waiting for the server to tell..
- for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
- if ( clients[ i ].clientState >= SCS_PUREWAIT && i != localClientNum ) {
- msg.Init( msgBuf, sizeof( msgBuf ) );
- msg.WriteByte( SERVER_RELIABLE_MESSAGE_RELOAD );
- SendReliableMessage( i, msg );
- clients[ i ].clientState = SCS_ZOMBIE; // so we don't bother sending a disconnect
- }
- }
- cmdSystem->BufferCommandText( CMD_EXEC_NOW, "reloadEngine" );
- serverReloadingEngine = true; // don't get caught in endless loop
- cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "spawnServer\n" );
- // decrease feature
- if ( idAsyncNetwork::serverReloadEngine.GetInteger() > 0 ) {
- idAsyncNetwork::serverReloadEngine.SetInteger( idAsyncNetwork::serverReloadEngine.GetInteger() - 1 );
- }
- return;
- }
- serverReloadingEngine = false;
- serverTime = 0;
- // initialize game id and time
- gameInitId ^= Sys_Milliseconds(); // NOTE: make sure the gameInitId is always a positive number because negative numbers have special meaning
- gameFrame = 0;
- gameTime = 0;
- gameTimeResidual = 0;
- memset( userCmds, 0, sizeof( userCmds ) );
- if ( idAsyncNetwork::serverDedicated.GetInteger() == 0 ) {
- InitLocalClient( 0 );
- } else {
- localClientNum = -1;
- }
- // re-initialize all connected clients for the new map
- for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
- if ( clients[i].clientState >= SCS_PUREWAIT && i != localClientNum ) {
- InitClient( i, clients[i].clientId, clients[i].clientRate );
- SendGameInitToClient( i );
- if ( sessLocal.mapSpawnData.serverInfo.GetBool( "si_pure" ) ) {
- clients[ i ].clientState = SCS_PUREWAIT;
- }
- }
- }
- // setup the game pak checksums
- // since this is not dependant on si_pure we catch anything bad before loading map
- if ( sessLocal.mapSpawnData.serverInfo.GetInt( "si_pure" ) ) {
- if ( !fileSystem->UpdateGamePakChecksums( ) ) {
- session->MessageBox( MSG_OK, common->GetLanguageDict()->GetString ( "#str_04337" ), common->GetLanguageDict()->GetString ( "#str_04338" ), true );
- cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "disconnect\n" );
- return;
- }
- }
- // load map
- sessLocal.ExecuteMapChange();
- if ( localClientNum >= 0 ) {
- BeginLocalClient();
- } else {
- game->SetLocalClient( -1 );
- }
- if ( sessLocal.mapSpawnData.serverInfo.GetInt( "si_pure" ) ) {
- // lock down the pak list
- fileSystem->UpdatePureServerChecksums( );
- // tell the clients so they can work out their pure lists
- for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
- if ( clients[ i ].clientState == SCS_PUREWAIT ) {
- if ( !SendReliablePureToClient( i ) ) {
- clients[ i ].clientState = SCS_CONNECTED;
- }
- }
- }
- }
- // serverTime gets reset, force a heartbeat so timings restart
- MasterHeartbeat( true );
- }
- /*
- ==================
- idAsyncServer::GetPort
- ==================
- */
- int idAsyncServer::GetPort( void ) const {
- return serverPort.GetPort();
- }
- /*
- ===============
- idAsyncServer::GetBoundAdr
- ===============
- */
- netadr_t idAsyncServer::GetBoundAdr( void ) const {
- return serverPort.GetAdr();
- }
- /*
- ==================
- idAsyncServer::GetOutgoingRate
- ==================
- */
- int idAsyncServer::GetOutgoingRate( void ) const {
- int i, rate;
- rate = 0;
- for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
- const serverClient_t &client = clients[i];
- if ( client.clientState >= SCS_CONNECTED ) {
- rate += client.channel.GetOutgoingRate();
- }
- }
- return rate;
- }
- /*
- ==================
- idAsyncServer::GetIncomingRate
- ==================
- */
- int idAsyncServer::GetIncomingRate( void ) const {
- int i, rate;
- rate = 0;
- for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
- const serverClient_t &client = clients[i];
- if ( client.clientState >= SCS_CONNECTED ) {
- rate += client.channel.GetIncomingRate();
- }
- }
- return rate;
- }
- /*
- ==================
- idAsyncServer::IsClientInGame
- ==================
- */
- bool idAsyncServer::IsClientInGame( int clientNum ) const {
- return ( clients[clientNum].clientState >= SCS_INGAME );
- }
- /*
- ==================
- idAsyncServer::GetClientPing
- ==================
- */
- int idAsyncServer::GetClientPing( int clientNum ) const {
- const serverClient_t &client = clients[clientNum];
- if ( client.clientState < SCS_CONNECTED ) {
- return 99999;
- } else {
- return client.clientPing;
- }
- }
- /*
- ==================
- idAsyncServer::GetClientPrediction
- ==================
- */
- int idAsyncServer::GetClientPrediction( int clientNum ) const {
- const serverClient_t &client = clients[clientNum];
- if ( client.clientState < SCS_CONNECTED ) {
- return 99999;
- } else {
- return client.clientPrediction;
- }
- }
- /*
- ==================
- idAsyncServer::GetClientTimeSinceLastPacket
- ==================
- */
- int idAsyncServer::GetClientTimeSinceLastPacket( int clientNum ) const {
- const serverClient_t &client = clients[clientNum];
- if ( client.clientState < SCS_CONNECTED ) {
- return 99999;
- } else {
- return serverTime - client.lastPacketTime;
- }
- }
- /*
- ==================
- idAsyncServer::GetClientTimeSinceLastInput
- ==================
- */
- int idAsyncServer::GetClientTimeSinceLastInput( int clientNum ) const {
- const serverClient_t &client = clients[clientNum];
- if ( client.clientState < SCS_CONNECTED ) {
- return 99999;
- } else {
- return serverTime - client.lastInputTime;
- }
- }
- /*
- ==================
- idAsyncServer::GetClientOutgoingRate
- ==================
- */
- int idAsyncServer::GetClientOutgoingRate( int clientNum ) const {
- const serverClient_t &client = clients[clientNum];
- if ( client.clientState < SCS_CONNECTED ) {
- return -1;
- } else {
- return client.channel.GetOutgoingRate();
- }
- }
- /*
- ==================
- idAsyncServer::GetClientIncomingRate
- ==================
- */
- int idAsyncServer::GetClientIncomingRate( int clientNum ) const {
- const serverClient_t &client = clients[clientNum];
- if ( client.clientState < SCS_CONNECTED ) {
- return -1;
- } else {
- return client.channel.GetIncomingRate();
- }
- }
- /*
- ==================
- idAsyncServer::GetClientOutgoingCompression
- ==================
- */
- float idAsyncServer::GetClientOutgoingCompression( int clientNum ) const {
- const serverClient_t &client = clients[clientNum];
- if ( client.clientState < SCS_CONNECTED ) {
- return 0.0f;
- } else {
- return client.channel.GetOutgoingCompression();
- }
- }
- /*
- ==================
- idAsyncServer::GetClientIncomingCompression
- ==================
- */
- float idAsyncServer::GetClientIncomingCompression( int clientNum ) const {
- const serverClient_t &client = clients[clientNum];
- if ( client.clientState < SCS_CONNECTED ) {
- return 0.0f;
- } else {
- return client.channel.GetIncomingCompression();
- }
- }
- /*
- ==================
- idAsyncServer::GetClientIncomingPacketLoss
- ==================
- */
- float idAsyncServer::GetClientIncomingPacketLoss( int clientNum ) const {
- const serverClient_t &client = clients[clientNum];
- if ( client.clientState < SCS_CONNECTED ) {
- return 0.0f;
- } else {
- return client.channel.GetIncomingPacketLoss();
- }
- }
- /*
- ==================
- idAsyncServer::GetNumClients
- ==================
- */
- int idAsyncServer::GetNumClients( void ) const {
- int ret = 0;
- for ( int i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
- if ( clients[ i ].clientState >= SCS_CONNECTED ) {
- ret++;
- }
- }
- return ret;
- }
- /*
- ==================
- idAsyncServer::GetNumIdleClients
- ==================
- */
- int idAsyncServer::GetNumIdleClients( void ) const {
- int ret = 0;
- for ( int i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
- if ( clients[ i ].clientState >= SCS_CONNECTED ) {
- if ( serverTime - clients[ i ].lastInputTime > NOINPUT_IDLE_TIME ) {
- ret++;
- }
- }
- }
- return ret;
- }
- /*
- ==================
- idAsyncServer::DuplicateUsercmds
- ==================
- */
- void idAsyncServer::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++ ) {
- if ( clients[i].clientState == SCS_FREE ) {
- continue;
- }
- if ( idAsyncNetwork::DuplicateUsercmd( userCmds[previousIndex][i], userCmds[currentIndex][i], frame, time ) ) {
- clients[i].numDuplicatedUsercmds++;
- }
- }
- }
- /*
- ==================
- idAsyncServer::ClearClient
- ==================
- */
- void idAsyncServer::ClearClient( int clientNum ) {
- serverClient_t &client = clients[clientNum];
- client.clientId = 0;
- client.clientState = SCS_FREE;
- client.clientPrediction = 0;
- client.clientAheadTime = 0;
- client.clientRate = 0;
- client.clientPing = 0;
- client.gameInitSequence = 0;
- client.gameFrame = 0;
- client.gameTime = 0;
- client.channel.Shutdown();
- client.lastConnectTime = 0;
- client.lastEmptyTime = 0;
- client.lastPingTime = 0;
- client.lastSnapshotTime = 0;
- client.lastPacketTime = 0;
- client.lastInputTime = 0;
- client.snapshotSequence = 0;
- client.acknowledgeSnapshotSequence = 0;
- client.numDuplicatedUsercmds = 0;
- }
- /*
- ==================
- idAsyncServer::InitClient
- ==================
- */
- void idAsyncServer::InitClient( int clientNum, int clientId, int clientRate ) {
- int i;
- // clear the user info
- sessLocal.mapSpawnData.userInfo[ clientNum ].Clear(); // always start with a clean base
- // clear the server client
- serverClient_t &client = clients[clientNum];
- client.clientId = clientId;
- client.clientState = SCS_CONNECTED;
- client.clientPrediction = 0;
- client.clientAheadTime = 0;
- client.gameInitSequence = -1;
- client.gameFrame = 0;
- client.gameTime = 0;
- client.channel.ResetRate();
- client.clientRate = clientRate ? clientRate : idAsyncNetwork::serverMaxClientRate.GetInteger();
- client.channel.SetMaxOutgoingRate( Min( idAsyncNetwork::serverMaxClientRate.GetInteger(), client.clientRate ) );
- client.clientPing = 0;
- client.lastConnectTime = serverTime;
- client.lastEmptyTime = serverTime;
- client.lastPingTime = serverTime;
- client.lastSnapshotTime = serverTime;
- client.lastPacketTime = serverTime;
- client.lastInputTime = serverTime;
- client.acknowledgeSnapshotSequence = 0;
- client.numDuplicatedUsercmds = 0;
- // clear the user commands
- for ( i = 0; i < MAX_USERCMD_BACKUP; i++ ) {
- memset( &userCmds[i][clientNum], 0, sizeof( userCmds[i][clientNum] ) );
- }
- // let the game know a player connected
- game->ServerClientConnect( clientNum, client.guid );
- }
- /*
- ==================
- idAsyncServer::InitLocalClient
- ==================
- */
- void idAsyncServer::InitLocalClient( int clientNum ) {
- netadr_t badAddress;
- localClientNum = clientNum;
- InitClient( clientNum, 0, 0 );
- memset( &badAddress, 0, sizeof( badAddress ) );
- badAddress.type = NA_BAD;
- clients[clientNum].channel.Init( badAddress, serverId );
- clients[clientNum].clientState = SCS_INGAME;
- sessLocal.mapSpawnData.userInfo[clientNum] = *cvarSystem->MoveCVarsToDict( CVAR_USERINFO );
- }
- /*
- ==================
- idAsyncServer::BeginLocalClient
- ==================
- */
- void idAsyncServer::BeginLocalClient( void ) {
- game->SetLocalClient( localClientNum );
- game->SetUserInfo( localClientNum, sessLocal.mapSpawnData.userInfo[localClientNum], false, false );
- game->ServerClientBegin( localClientNum );
- }
- /*
- ==================
- idAsyncServer::LocalClientInput
- ==================
- */
- void idAsyncServer::LocalClientInput( void ) {
- int index;
- if ( localClientNum < 0 ) {
- return;
- }
- index = gameFrame & ( MAX_USERCMD_BACKUP - 1 );
- userCmds[index][localClientNum] = usercmdGen->GetDirectUsercmd();
- userCmds[index][localClientNum].gameFrame = gameFrame;
- userCmds[index][localClientNum].gameTime = gameTime;
- if ( idAsyncNetwork::UsercmdInputChanged( userCmds[( gameFrame - 1 ) & ( MAX_USERCMD_BACKUP - 1 )][localClientNum], userCmds[index][localClientNum] ) ) {
- clients[localClientNum].lastInputTime = serverTime;
- }
- clients[localClientNum].gameFrame = gameFrame;
- clients[localClientNum].gameTime = gameTime;
- clients[localClientNum].lastPacketTime = serverTime;
- }
- /*
- ==================
- idAsyncServer::DropClient
- ==================
- */
- void idAsyncServer::DropClient( int clientNum, const char *reason ) {
- int i;
- idBitMsg msg;
- byte msgBuf[MAX_MESSAGE_SIZE];
- serverClient_t &client = clients[clientNum];
- if ( client.clientState <= SCS_ZOMBIE ) {
- return;
- }
- if ( client.clientState >= SCS_PUREWAIT && clientNum != localClientNum ) {
- msg.Init( msgBuf, sizeof( msgBuf ) );
- msg.WriteByte( SERVER_RELIABLE_MESSAGE_DISCONNECT );
- msg.WriteLong( clientNum );
- msg.WriteString( reason );
- for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
- // clientNum so SCS_PUREWAIT client gets it's own disconnect msg
- if ( i == clientNum || clients[i].clientState >= SCS_CONNECTED ) {
- SendReliableMessage( i, msg );
- }
- }
- }
- reason = common->GetLanguageDict()->GetString( reason );
- common->Printf( "client %d %s\n", clientNum, reason );
- cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "addChatLine \"%s^0 %s\"", sessLocal.mapSpawnData.userInfo[ clientNum ].GetString( "ui_name" ), reason ) );
- // remove the player from the game
- game->ServerClientDisconnect( clientNum );
- client.clientState = SCS_ZOMBIE;
- }
- /*
- ==================
- idAsyncServer::SendReliableMessage
- ==================
- */
- void idAsyncServer::SendReliableMessage( int clientNum, const idBitMsg &msg ) {
- if ( clientNum == localClientNum ) {
- return;
- }
- if ( !clients[ clientNum ].channel.SendReliableMessage( msg ) ) {
- clients[ clientNum ].channel.ClearReliableMessages();
- DropClient( clientNum, "#str_07136" );
- }
- }
- /*
- ==================
- idAsyncServer::CheckClientTimeouts
- ==================
- */
- void idAsyncServer::CheckClientTimeouts( void ) {
- int i, zombieTimeout, clientTimeout;
- zombieTimeout = serverTime - idAsyncNetwork::serverZombieTimeout.GetInteger() * 1000;
- clientTimeout = serverTime - idAsyncNetwork::serverClientTimeout.GetInteger() * 1000;
- for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
- serverClient_t &client = clients[i];
- if ( i == localClientNum ) {
- continue;
- }
- if ( client.lastPacketTime > serverTime ) {
- client.lastPacketTime = serverTime;
- continue;
- }
- if ( client.clientState == SCS_ZOMBIE && client.lastPacketTime < zombieTimeout ) {
- client.channel.Shutdown();
- client.clientState = SCS_FREE;
- continue;
- }
- if ( client.clientState >= SCS_PUREWAIT && client.lastPacketTime < clientTimeout ) {
- DropClient( i, "#str_07137" );
- continue;
- }
- }
- }
- /*
- ==================
- idAsyncServer::SendPrintBroadcast
- ==================
- */
- void idAsyncServer::SendPrintBroadcast( const char *string ) {
- int i;
- idBitMsg msg;
- byte msgBuf[MAX_MESSAGE_SIZE];
- msg.Init( msgBuf, sizeof( msgBuf ) );
- msg.WriteByte( SERVER_RELIABLE_MESSAGE_PRINT );
- msg.WriteString( string );
- for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
- if ( clients[i].clientState >= SCS_CONNECTED ) {
- SendReliableMessage( i, msg );
- }
- }
- }
- /*
- ==================
- idAsyncServer::SendPrintToClient
- ==================
- */
- void idAsyncServer::SendPrintToClient( int clientNum, const char *string ) {
- idBitMsg msg;
- byte msgBuf[MAX_MESSAGE_SIZE];
- serverClient_t &client = clients[clientNum];
- if ( client.clientState < SCS_CONNECTED ) {
- return;
- }
- msg.Init( msgBuf, sizeof( msgBuf ) );
- msg.WriteByte( SERVER_RELIABLE_MESSAGE_PRINT );
- msg.WriteString( string );
- SendReliableMessage( clientNum, msg );
- }
- /*
- ==================
- idAsyncServer::SendUserInfoBroadcast
- ==================
- */
- void idAsyncServer::SendUserInfoBroadcast( int userInfoNum, const idDict &info, bool sendToAll ) {
- idBitMsg msg;
- byte msgBuf[MAX_MESSAGE_SIZE];
- const idDict *gameInfo;
- bool gameModifiedInfo;
- gameInfo = game->SetUserInfo( userInfoNum, info, false, true );
- if ( gameInfo ) {
- gameModifiedInfo = true;
- } else {
- gameModifiedInfo = false;
- gameInfo = &info;
- }
- if ( userInfoNum == localClientNum ) {
- common->DPrintf( "local user info modified by server\n" );
- cvarSystem->SetCVarsFromDict( *gameInfo );
- cvarSystem->ClearModifiedFlags( CVAR_USERINFO ); // don't emit back
- }
- msg.Init( msgBuf, sizeof( msgBuf ) );
- msg.WriteByte( SERVER_RELIABLE_MESSAGE_CLIENTINFO );
- msg.WriteByte( userInfoNum );
- if ( gameModifiedInfo || sendToAll ) {
- msg.WriteBits( 0, 1 );
- } else {
- msg.WriteBits( 1, 1 );
- }
- #if ID_CLIENTINFO_TAGS
- msg.WriteLong( sessLocal.mapSpawnData.userInfo[userInfoNum].Checksum() );
- common->DPrintf( "broadcast for client %d: 0x%x\n", userInfoNum, sessLocal.mapSpawnData.userInfo[userInfoNum].Checksum() );
- sessLocal.mapSpawnData.userInfo[userInfoNum].Print();
- #endif
- if ( gameModifiedInfo || sendToAll ) {
- msg.WriteDeltaDict( *gameInfo, NULL );
- } else {
- msg.WriteDeltaDict( *gameInfo, &sessLocal.mapSpawnData.userInfo[userInfoNum] );
- }
- for ( int i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
- if ( clients[i].clientState >= SCS_CONNECTED && ( sendToAll || i != userInfoNum || gameModifiedInfo ) ) {
- SendReliableMessage( i, msg );
- }
- }
- sessLocal.mapSpawnData.userInfo[userInfoNum] = *gameInfo;
- }
- /*
- ==================
- idAsyncServer::UpdateUI
- if the game modifies userInfo, it will call this through command system
- we then need to get the info from the game, and broadcast to clients
- ( using DeltaDict and our current mapSpawnData as a base )
- ==================
- */
- void idAsyncServer::UpdateUI( int clientNum ) {
- const idDict *info = game->GetUserInfo( clientNum );
- if ( !info ) {
- common->Warning( "idAsyncServer::UpdateUI: no info from game\n" );
- return;
- }
- SendUserInfoBroadcast( clientNum, *info, true );
- }
- /*
- ==================
- idAsyncServer::SendUserInfoToClient
- ==================
- */
- void idAsyncServer::SendUserInfoToClient( int clientNum, int userInfoNum, const idDict &info ) {
- idBitMsg msg;
- byte msgBuf[MAX_MESSAGE_SIZE];
- if ( clients[clientNum].clientState < SCS_CONNECTED ) {
- return;
- }
- msg.Init( msgBuf, sizeof( msgBuf ) );
- msg.WriteByte( SERVER_RELIABLE_MESSAGE_CLIENTINFO );
- msg.WriteByte( userInfoNum );
- msg.WriteBits( 0, 1 );
- #if ID_CLIENTINFO_TAGS
- msg.WriteLong( 0 );
- common->DPrintf( "user info %d to client %d: NULL base\n", userInfoNum, clientNum );
- #endif
- msg.WriteDeltaDict( info, NULL );
- SendReliableMessage( clientNum, msg );
- }
- /*
- ==================
- idAsyncServer::SendSyncedCvarsBroadcast
- ==================
- */
- void idAsyncServer::SendSyncedCvarsBroadcast( const idDict &cvars ) {
- idBitMsg msg;
- byte msgBuf[MAX_MESSAGE_SIZE];
- int i;
- msg.Init( msgBuf, sizeof( msgBuf ) );
- msg.WriteByte( SERVER_RELIABLE_MESSAGE_SYNCEDCVARS );
- msg.WriteDeltaDict( cvars, &sessLocal.mapSpawnData.syncedCVars );
- for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
- if ( clients[i].clientState >= SCS_CONNECTED ) {
- SendReliableMessage( i, msg );
- }
- }
- sessLocal.mapSpawnData.syncedCVars = cvars;
- }
- /*
- ==================
- idAsyncServer::SendSyncedCvarsToClient
- ==================
- */
- void idAsyncServer::SendSyncedCvarsToClient( int clientNum, const idDict &cvars ) {
- idBitMsg msg;
- byte msgBuf[MAX_MESSAGE_SIZE];
- if ( clients[clientNum].clientState < SCS_CONNECTED ) {
- return;
- }
- msg.Init( msgBuf, sizeof( msgBuf ) );
- msg.WriteByte( SERVER_RELIABLE_MESSAGE_SYNCEDCVARS );
- msg.WriteDeltaDict( cvars, NULL );
- SendReliableMessage( clientNum, msg );
- }
- /*
- ==================
- idAsyncServer::SendApplySnapshotToClient
- ==================
- */
- void idAsyncServer::SendApplySnapshotToClient( int clientNum, int sequence ) {
- idBitMsg msg;
- byte msgBuf[MAX_MESSAGE_SIZE];
- msg.Init( msgBuf, sizeof( msgBuf ) );
- msg.WriteByte( SERVER_RELIABLE_MESSAGE_APPLYSNAPSHOT );
- msg.WriteLong( sequence );
- SendReliableMessage( clientNum, msg );
- }
- /*
- ==================
- idAsyncServer::SendEmptyToClient
- ==================
- */
- bool idAsyncServer::SendEmptyToClient( int clientNum, bool force ) {
- idBitMsg msg;
- byte msgBuf[MAX_MESSAGE_SIZE];
- serverClient_t &client = clients[clientNum];
- if ( client.lastEmptyTime > realTime ) {
- client.lastEmptyTime = realTime;
- }
- if ( !force && ( realTime - client.lastEmptyTime < EMPTY_RESEND_TIME ) ) {
- return false;
- }
- if ( idAsyncNetwork::verbose.GetInteger() ) {
- common->Printf( "sending empty to client %d: gameInitId = %d, gameFrame = %d, gameTime = %d\n", clientNum, gameInitId, gameFrame, gameTime );
- }
- msg.Init( msgBuf, sizeof( msgBuf ) );
- msg.WriteLong( gameInitId );
- msg.WriteByte( SERVER_UNRELIABLE_MESSAGE_EMPTY );
- client.channel.SendMessage( serverPort, serverTime, msg );
- client.lastEmptyTime = realTime;
- return true;
- }
- /*
- ==================
- idAsyncServer::SendPingToClient
- ==================
- */
- bool idAsyncServer::SendPingToClient( int clientNum ) {
- idBitMsg msg;
- byte msgBuf[MAX_MESSAGE_SIZE];
- serverClient_t &client = clients[clientNum];
- if ( client.lastPingTime > realTime ) {
- client.lastPingTime = realTime;
- }
- if ( realTime - client.lastPingTime < PING_RESEND_TIME ) {
- return false;
- }
- if ( idAsyncNetwork::verbose.GetInteger() == 2 ) {
- common->Printf( "pinging client %d: gameInitId = %d, gameFrame = %d, gameTime = %d\n", clientNum, gameInitId, gameFrame, gameTime );
- }
- msg.Init( msgBuf, sizeof( msgBuf ) );
- msg.WriteLong( gameInitId );
- msg.WriteByte( SERVER_UNRELIABLE_MESSAGE_PING );
- msg.WriteLong( realTime );
- client.channel.SendMessage( serverPort, serverTime, msg );
- client.lastPingTime = realTime;
- return true;
- }
- /*
- ==================
- idAsyncServer::SendGameInitToClient
- ==================
- */
- void idAsyncServer::SendGameInitToClient( int clientNum ) {
- idBitMsg msg;
- byte msgBuf[MAX_MESSAGE_SIZE];
- if ( idAsyncNetwork::verbose.GetInteger() ) {
- common->Printf( "sending gameinit to client %d: gameInitId = %d, gameFrame = %d, gameTime = %d\n", clientNum, gameInitId, gameFrame, gameTime );
- }
- serverClient_t &client = clients[clientNum];
- // clear the unsent fragments. might flood winsock but that's ok
- while( client.channel.UnsentFragmentsLeft() ) {
- client.channel.SendNextFragment( serverPort, serverTime );
- }
- msg.Init( msgBuf, sizeof( msgBuf ) );
- msg.WriteLong( gameInitId );
- msg.WriteByte( SERVER_UNRELIABLE_MESSAGE_GAMEINIT );
- msg.WriteLong( gameFrame );
- msg.WriteLong( gameTime );
- msg.WriteDeltaDict( sessLocal.mapSpawnData.serverInfo, NULL );
- client.gameInitSequence = client.channel.SendMessage( serverPort, serverTime, msg );
- }
- /*
- ==================
- idAsyncServer::SendSnapshotToClient
- ==================
- */
- bool idAsyncServer::SendSnapshotToClient( int clientNum ) {
- int i, j, index, numUsercmds;
- idBitMsg msg;
- byte msgBuf[MAX_MESSAGE_SIZE];
- usercmd_t * last;
- byte clientInPVS[MAX_ASYNC_CLIENTS >> 3];
- serverClient_t &client = clients[clientNum];
- if ( serverTime - client.lastSnapshotTime < idAsyncNetwork::serverSnapshotDelay.GetInteger() ) {
- return false;
- }
- if ( idAsyncNetwork::verbose.GetInteger() == 2 ) {
- common->Printf( "sending snapshot to client %d: gameInitId = %d, gameFrame = %d, gameTime = %d\n", clientNum, gameInitId, gameFrame, gameTime );
- }
- // how far is the client ahead of the server minus the packet delay
- client.clientAheadTime = client.gameTime - ( gameTime + gameTimeResidual );
- // write the snapshot
- msg.Init( msgBuf, sizeof( msgBuf ) );
- msg.WriteLong( gameInitId );
- msg.WriteByte( SERVER_UNRELIABLE_MESSAGE_SNAPSHOT );
- msg.WriteLong( client.snapshotSequence );
- msg.WriteLong( gameFrame );
- msg.WriteLong( gameTime );
- msg.WriteByte( idMath::ClampChar( client.numDuplicatedUsercmds ) );
- msg.WriteShort( idMath::ClampShort( client.clientAheadTime ) );
- // write the game snapshot
- game->ServerWriteSnapshot( clientNum, client.snapshotSequence, msg, clientInPVS, MAX_ASYNC_CLIENTS );
- // write the latest user commands from the other clients in the PVS to the snapshot
- for ( last = NULL, i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
- serverClient_t &client = clients[i];
- if ( client.clientState == SCS_FREE || i == clientNum ) {
- continue;
- }
- // if the client is not in the PVS
- if ( !( clientInPVS[i >> 3] & ( 1 << ( i & 7 ) ) ) ) {
- continue;
- }
- int maxRelay = idMath::ClampInt( 1, MAX_USERCMD_RELAY, idAsyncNetwork::serverMaxUsercmdRelay.GetInteger() );
- // Max( 1, to always send at least one cmd, which we know we have because we call DuplicateUsercmds in RunFrame
- numUsercmds = Max( 1, Min( client.gameFrame, gameFrame + maxRelay ) - gameFrame );
- msg.WriteByte( i );
- msg.WriteByte( numUsercmds );
- for ( j = 0; j < numUsercmds; j++ ) {
- index = ( gameFrame + j ) & ( MAX_USERCMD_BACKUP - 1 );
- idAsyncNetwork::WriteUserCmdDelta( msg, userCmds[index][i], last );
- last = &userCmds[index][i];
- }
- }
- msg.WriteByte( MAX_ASYNC_CLIENTS );
- client.channel.SendMessage( serverPort, serverTime, msg );
- client.lastSnapshotTime = serverTime;
- client.snapshotSequence++;
- client.numDuplicatedUsercmds = 0;
- return true;
- }
- /*
- ==================
- idAsyncServer::ProcessUnreliableClientMessage
- ==================
- */
- void idAsyncServer::ProcessUnreliableClientMessage( int clientNum, const idBitMsg &msg ) {
- int i, id, acknowledgeSequence, clientGameInitId, clientGameFrame, numUsercmds, index;
- usercmd_t *last;
- serverClient_t &client = clients[clientNum];
- if ( client.clientState == SCS_ZOMBIE ) {
- return;
- }
- acknowledgeSequence = msg.ReadLong();
- clientGameInitId = msg.ReadLong();
- // while loading a map the client may send empty messages to keep the connection alive
- if ( clientGameInitId == GAME_INIT_ID_MAP_LOAD ) {
- if ( idAsyncNetwork::verbose.GetInteger() ) {
- common->Printf( "ignore unreliable msg from client %d, gameInitId == ID_MAP_LOAD\n", clientNum );
- }
- return;
- }
- // check if the client is in the right game
- if ( clientGameInitId != gameInitId ) {
- if ( acknowledgeSequence > client.gameInitSequence ) {
- // the client is connected but not in the right game
- client.clientState = SCS_CONNECTED;
- // send game init to client
- SendGameInitToClient( clientNum );
- if ( sessLocal.mapSpawnData.serverInfo.GetBool( "si_pure" ) ) {
- client.clientState = SCS_PUREWAIT;
- if ( !SendReliablePureToClient( clientNum ) ) {
- client.clientState = SCS_CONNECTED;
- }
- }
- } else if ( idAsyncNetwork::verbose.GetInteger() ) {
- common->Printf( "ignore unreliable msg from client %d, wrong gameInit, old sequence\n", clientNum );
- }
- return;
- }
- client.acknowledgeSnapshotSequence = msg.ReadLong();
- if ( client.clientState == SCS_CONNECTED ) {
- // the client is in the right game
- client.clientState = SCS_INGAME;
- // send the user info of other clients
- for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
- if ( clients[i].clientState >= SCS_CONNECTED && i != clientNum ) {
- SendUserInfoToClient( clientNum, i, sessLocal.mapSpawnData.userInfo[i] );
- }
- }
- // send synchronized cvars to client
- SendSyncedCvarsToClient( clientNum, sessLocal.mapSpawnData.syncedCVars );
- SendEnterGameToClient( clientNum );
- // get the client running in the game
- game->ServerClientBegin( clientNum );
- // write any reliable messages to initialize the client game state
- game->ServerWriteInitialReliableMessages( clientNum );
- } else if ( client.clientState == SCS_INGAME ) {
- // apply the last snapshot the client received
- if ( game->ServerApplySnapshot( clientNum, client.acknowledgeSnapshotSequence ) ) {
- SendApplySnapshotToClient( clientNum, client.acknowledgeSnapshotSequence );
- }
- }
- // process the unreliable message
- id = msg.ReadByte();
- switch( id ) {
- case CLIENT_UNRELIABLE_MESSAGE_EMPTY: {
- if ( idAsyncNetwork::verbose.GetInteger() ) {
- common->Printf( "received empty message for client %d\n", clientNum );
- }
- break;
- }
- case CLIENT_UNRELIABLE_MESSAGE_PINGRESPONSE: {
- client.clientPing = realTime - msg.ReadLong();
- break;
- }
- case CLIENT_UNRELIABLE_MESSAGE_USERCMD: {
- client.clientPrediction = msg.ReadShort();
- // read user commands
- clientGameFrame = msg.ReadLong();
- numUsercmds = msg.ReadByte();
- for ( last = NULL, i = clientGameFrame - numUsercmds + 1; i <= clientGameFrame; i++ ) {
- index = i & ( MAX_USERCMD_BACKUP - 1 );
- idAsyncNetwork::ReadUserCmdDelta( msg, userCmds[index][clientNum], last );
- userCmds[index][clientNum].gameFrame = i;
- userCmds[index][clientNum].duplicateCount = 0;
- if ( idAsyncNetwork::UsercmdInputChanged( userCmds[( i - 1 ) & ( MAX_USERCMD_BACKUP - 1 )][clientNum], userCmds[index][clientNum] ) ) {
- client.lastInputTime = serverTime;
- }
- last = &userCmds[index][clientNum];
- }
- if ( last ) {
- client.gameFrame = last->gameFrame;
- client.gameTime = last->gameTime;
- }
- if ( idAsyncNetwork::verbose.GetInteger() == 2 ) {
- common->Printf( "received user command for client %d, gameInitId = %d, gameFrame, %d gameTime %d\n", clientNum, clientGameInitId, client.gameFrame, client.gameTime );
- }
- break;
- }
- default: {
- common->Printf( "unknown unreliable message %d from client %d\n", id, clientNum );
- break;
- }
- }
- }
- /*
- ==================
- idAsyncServer::ProcessReliableClientMessages
- ==================
- */
- void idAsyncServer::ProcessReliableClientMessages( int clientNum ) {
- idBitMsg msg;
- byte msgBuf[MAX_MESSAGE_SIZE];
- byte id;
- serverClient_t &client = clients[clientNum];
- msg.Init( msgBuf, sizeof( msgBuf ) );
- while ( client.channel.GetReliableMessage( msg ) ) {
- id = msg.ReadByte();
- switch( id ) {
- case CLIENT_RELIABLE_MESSAGE_CLIENTINFO: {
- idDict info;
- msg.ReadDeltaDict( info, &sessLocal.mapSpawnData.userInfo[clientNum] );
- SendUserInfoBroadcast( clientNum, info );
- break;
- }
- case CLIENT_RELIABLE_MESSAGE_PRINT: {
- char string[MAX_STRING_CHARS];
- msg.ReadString( string, sizeof( string ) );
- common->Printf( "%s\n", string );
- break;
- }
- case CLIENT_RELIABLE_MESSAGE_DISCONNECT: {
- DropClient( clientNum, "#str_07138" );
- break;
- }
- case CLIENT_RELIABLE_MESSAGE_PURE: {
- // we get this message once the client has successfully updated it's pure list
- ProcessReliablePure( clientNum, msg );
- break;
- }
- default: {
- // pass reliable message on to game code
- game->ServerProcessReliableMessage( clientNum, msg );
- break;
- }
- }
- }
- }
- /*
- ==================
- idAsyncServer::ProcessAuthMessage
- ==================
- */
- void idAsyncServer::ProcessAuthMessage( const idBitMsg &msg ) {
- netadr_t client_from;
- char client_guid[ 12 ], string[ MAX_STRING_CHARS ];
- int i, clientId;
- authReply_t reply;
- authReplyMsg_t replyMsg = AUTH_REPLY_WAITING;
- idStr replyPrintMsg;
-
- reply = (authReply_t)msg.ReadByte();
- if ( reply <= 0 || reply >= AUTH_MAXSTATES ) {
- common->DPrintf( "auth: invalid reply %d\n", reply );
- return;
- }
- clientId = msg.ReadShort( );
- msg.ReadNetadr( &client_from );
- msg.ReadString( client_guid, sizeof( client_guid ) );
- if ( reply != AUTH_OK ) {
- replyMsg = (authReplyMsg_t)msg.ReadByte();
- if ( replyMsg <= 0 || replyMsg >= AUTH_REPLY_MAXSTATES ) {
- common->DPrintf( "auth: invalid reply msg %d\n", replyMsg );
- return;
- }
- if ( replyMsg == AUTH_REPLY_PRINT ) {
- msg.ReadString( string, MAX_STRING_CHARS );
- replyPrintMsg = string;
- }
- }
- lastAuthTime = serverTime;
- // no message parsing below
-
- for ( i = 0; i < MAX_CHALLENGES; i++ ) {
- if ( !challenges[i].connected && challenges[ i ].clientId == clientId ) {
- // return if something is wrong
- // break if we have found a valid auth
- if ( !strlen( challenges[ i ].guid ) ) {
- common->DPrintf( "auth: client %s has no guid yet\n", Sys_NetAdrToString( challenges[ i ].address ) );
- return;
- }
- if ( idStr::Cmp( challenges[ i ].guid, client_guid ) ) {
- common->DPrintf( "auth: client %s %s not matched, auth server says guid %s\n", Sys_NetAdrToString( challenges[ i ].address ), challenges[i].guid, client_guid );
- return;
- }
- if ( !Sys_CompareNetAdrBase( client_from, challenges[i].address ) ) {
- // let auth work when server and master don't see the same IP
- common->DPrintf( "auth: matched guid '%s' for != IPs %s and %s\n", client_guid, Sys_NetAdrToString( client_from ), Sys_NetAdrToString( challenges[i].address ) );
- }
- break;
- }
- }
- if ( i >= MAX_CHALLENGES ) {
- common->DPrintf( "auth: failed client lookup %s %s\n", Sys_NetAdrToString( client_from ), client_guid );
- return;
- }
- if ( challenges[ i ].authState != CDK_WAIT ) {
- common->DWarning( "auth: challenge 0x%x %s authState %d != CDK_WAIT", challenges[ i ].challenge, Sys_NetAdrToString( challenges[ i ].address ), challenges[ i ].authState );
- return;
- }
-
- idStr::snPrintf( challenges[ i ].guid, 12, client_guid );
- if ( reply == AUTH_OK ) {
- challenges[ i ].authState = CDK_OK;
- common->Printf( "client %s %s is authed\n", Sys_NetAdrToString( client_from ), client_guid );
- } else {
- const char *msg;
- if ( replyMsg != AUTH_REPLY_PRINT ) {
- msg = authReplyMsg[ replyMsg ];
- } else {
- msg = replyPrintMsg.c_str();
- }
- // maybe localize it
- const char *l_msg = common->GetLanguageDict()->GetString( msg );
- common->DPrintf( "auth: client %s %s - %s %s\n", Sys_NetAdrToString( client_from ), client_guid, authReplyStr[ reply ], l_msg );
- challenges[ i ].authReply = reply;
- challenges[ i ].authReplyMsg = replyMsg;
- challenges[ i ].authReplyPrint = replyPrintMsg;
- }
- }
- /*
- ==================
- idAsyncServer::ProcessChallengeMessage
- ==================
- */
- void idAsyncServer::ProcessChallengeMessage( const netadr_t from, const idBitMsg &msg ) {
- int i, clientId, oldest, oldestTime;
- idBitMsg outMsg;
- byte msgBuf[MAX_MESSAGE_SIZE];
- clientId = msg.ReadLong();
- oldest = 0;
- oldestTime = 0x7fffffff;
- // see if we already have a challenge for this ip
- for ( i = 0; i < MAX_CHALLENGES; i++ ) {
- if ( !challenges[i].connected && Sys_CompareNetAdrBase( from, challenges[i].address ) && clientId == challenges[i].clientId ) {
- break;
- }
- if ( challenges[i].time < oldestTime ) {
- oldestTime = challenges[i].time;
- oldest = i;
- }
- }
- if ( i >= MAX_CHALLENGES ) {
- // this is the first time this client has asked for a challenge
- i = oldest;
- challenges[i].address = from;
- challenges[i].clientId = clientId;
- challenges[i].challenge = ( (rand() << 16) ^ rand() ) ^ serverTime;
- challenges[i].time = serverTime;
- challenges[i].connected = false;
- challenges[i].authState = CDK_WAIT;
- challenges[i].authReply = AUTH_NONE;
- challenges[i].authReplyMsg = AUTH_REPLY_WAITING;
- challenges[i].authReplyPrint = "";
- challenges[i].guid[0] = '\0';
- }
- challenges[i].pingTime = serverTime;
- common->Printf( "sending challenge 0x%x to %s\n", challenges[i].challenge, Sys_NetAdrToString( from ) );
- outMsg.Init( msgBuf, sizeof( msgBuf ) );
- outMsg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
- outMsg.WriteString( "challengeResponse" );
- outMsg.WriteLong( challenges[i].challenge );
- outMsg.WriteShort( serverId );
- outMsg.WriteString( cvarSystem->GetCVarString( "fs_game_base" ) );
- outMsg.WriteString( cvarSystem->GetCVarString( "fs_game" ) );
- serverPort.SendPacket( from, outMsg.GetData(), outMsg.GetSize() );
- if ( Sys_IsLANAddress( from ) ) {
- // no CD Key check for LAN clients
- challenges[i].authState = CDK_OK;
- } else {
- if ( idAsyncNetwork::LANServer.GetBool() ) {
- common->Printf( "net_LANServer is enabled. Client %s is not a LAN address, will be rejected\n", Sys_NetAdrToString( from ) );
- challenges[ i ].authState = CDK_ONLYLAN;
- } else {
- // emit a cd key confirmation request
- outMsg.BeginWriting();
- outMsg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
- outMsg.WriteString( "srvAuth" );
- outMsg.WriteLong( ASYNC_PROTOCOL_VERSION );
- outMsg.WriteNetadr( from );
- outMsg.WriteLong( -1 ); // this identifies "challenge" auth vs "connect" auth
- // protocol 1.37 addition
- outMsg.WriteByte( fileSystem->RunningD3XP() );
- serverPort.SendPacket( idAsyncNetwork::GetMasterAddress(), outMsg.GetData(), outMsg.GetSize() );
- }
- }
- }
- /*
- ==================
- idAsyncServer::SendPureServerMessage
- ==================
- */
- bool idAsyncServer::SendPureServerMessage( const netadr_t to, int OS ) {
- idBitMsg outMsg;
- byte msgBuf[ MAX_MESSAGE_SIZE ];
- int serverChecksums[ MAX_PURE_PAKS ];
- int gamePakChecksum;
- int i;
- fileSystem->GetPureServerChecksums( serverChecksums, OS, &gamePakChecksum );
- if ( !serverChecksums[ 0 ] ) {
- // happens if you run fully expanded assets with si_pure 1
- common->Warning( "pure server has no pak files referenced" );
- return false;
- }
- common->DPrintf( "client %s: sending pure pak list\n", Sys_NetAdrToString( to ) );
- // send our list of required paks
- outMsg.Init( msgBuf, sizeof( msgBuf ) );
- outMsg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
- outMsg.WriteString( "pureServer" );
- i = 0;
- while ( serverChecksums[ i ] ) {
- outMsg.WriteLong( serverChecksums[ i++ ] );
- }
- outMsg.WriteLong( 0 );
- // write the pak checksum for game code
- outMsg.WriteLong( gamePakChecksum );
- serverPort.SendPacket( to, outMsg.GetData(), outMsg.GetSize() );
- return true;
- }
- /*
- ==================
- idAsyncServer::SendReliablePureToClient
- ==================
- */
- bool idAsyncServer::SendReliablePureToClient( int clientNum ) {
- idBitMsg msg;
- byte msgBuf[ MAX_MESSAGE_SIZE ];
- int serverChecksums[ MAX_PURE_PAKS ];
- int i;
- int gamePakChecksum;
- fileSystem->GetPureServerChecksums( serverChecksums, clients[ clientNum ].OS, &gamePakChecksum );
- if ( !serverChecksums[ 0 ] ) {
- // happens if you run fully expanded assets with si_pure 1
- common->Warning( "pure server has no pak files referenced" );
- return false;
- }
- common->DPrintf( "client %d: sending pure pak list (reliable channel) @ gameInitId %d\n", clientNum, gameInitId );
- msg.Init( msgBuf, sizeof( msgBuf ) );
- msg.WriteByte( SERVER_RELIABLE_MESSAGE_PURE );
- msg.WriteLong( gameInitId );
- i = 0;
- while ( serverChecksums[ i ] ) {
- msg.WriteLong( serverChecksums[ i++ ] );
- }
- msg.WriteLong( 0 );
- msg.WriteLong( gamePakChecksum );
- SendReliableMessage( clientNum, msg );
- return true;
- }
- /*
- ==================
- idAsyncServer::ValidateChallenge
- ==================
- */
- int idAsyncServer::ValidateChallenge( const netadr_t from, int challenge, int clientId ) {
- int i;
- for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
- const serverClient_t &client = clients[i];
- if ( client.clientState == SCS_FREE ) {
- continue;
- }
- if ( Sys_CompareNetAdrBase( from, client.channel.GetRemoteAddress() ) &&
- ( clientId == client.clientId || from.port == client.channel.GetRemoteAddress().port ) ) {
- if ( serverTime - client.lastConnectTime < MIN_RECONNECT_TIME ) {
- common->Printf( "%s: reconnect rejected : too soon\n", Sys_NetAdrToString( from ) );
- return -1;
- }
- break;
- }
- }
- for ( i = 0; i < MAX_CHALLENGES; i++ ) {
- if ( Sys_CompareNetAdrBase( from, challenges[i].address ) && from.port == challenges[i].address.port ) {
- if ( challenge == challenges[i].challenge ) {
- break;
- }
- }
- }
- if ( i == MAX_CHALLENGES ) {
- PrintOOB( from, SERVER_PRINT_BADCHALLENGE, "#str_04840" );
- return -1;
- }
- return i;
- }
- /*
- ==================
- idAsyncServer::ProcessConnectMessage
- ==================
- */
- void idAsyncServer::ProcessConnectMessage( const netadr_t from, const idBitMsg &msg ) {
- int clientNum, protocol, clientDataChecksum, challenge, clientId, ping, clientRate;
- idBitMsg outMsg;
- byte msgBuf[ MAX_MESSAGE_SIZE ];
- char guid[ 12 ];
- char password[ 17 ];
- int i, ichallenge, islot, OS, numClients;
- protocol = msg.ReadLong();
- OS = msg.ReadShort();
- // check the protocol version
- if ( protocol != ASYNC_PROTOCOL_VERSION ) {
- // that's a msg back to a client, we don't know about it's localization, so send english
- PrintOOB( from, SERVER_PRINT_BADPROTOCOL, va( "server uses protocol %d.%d\n", ASYNC_PROTOCOL_MAJOR, ASYNC_PROTOCOL_MINOR ) );
- return;
- }
- clientDataChecksum = msg.ReadLong();
- challenge = msg.ReadLong();
- clientId = msg.ReadShort();
- clientRate = msg.ReadLong();
- // check the client data - only for non pure servers
- if ( !sessLocal.mapSpawnData.serverInfo.GetInt( "si_pure" ) && clientDataChecksum != serverDataChecksum ) {
- PrintOOB( from, SERVER_PRINT_MISC, "#str_04842" );
- return;
- }
- if ( ( ichallenge = ValidateChallenge( from, challenge, clientId ) ) == -1 ) {
- return;
- }
- challenges[ ichallenge ].OS = OS;
- msg.ReadString( guid, sizeof( guid ) );
- switch ( challenges[ ichallenge ].authState ) {
- case CDK_PUREWAIT:
- SendPureServerMessage( from, OS );
- return;
- case CDK_ONLYLAN:
- common->DPrintf( "%s: not a lan client\n", Sys_NetAdrToString( from ) );
- PrintOOB( from, SERVER_PRINT_MISC, "#str_04843" );
- return;
- case CDK_WAIT:
- if ( challenges[ ichallenge ].authReply == AUTH_NONE && Min( serverTime - lastAuthTime, serverTime - challenges[ ichallenge ].time ) > AUTHORIZE_TIMEOUT ) {
- common->DPrintf( "%s: Authorize server timed out\n", Sys_NetAdrToString( from ) );
- break; // will continue with the connecting process
- }
- const char *msg, *l_msg;
- if ( challenges[ ichallenge ].authReplyMsg != AUTH_REPLY_PRINT ) {
- msg = authReplyMsg[ challenges[ ichallenge ].authReplyMsg ];
- } else {
- msg = challenges[ ichallenge ].authReplyPrint.c_str();
- }
- l_msg = common->GetLanguageDict()->GetString( msg );
-
- common->DPrintf( "%s: %s\n", Sys_NetAdrToString( from ), l_msg );
- if ( challenges[ ichallenge ].authReplyMsg == AUTH_REPLY_UNKNOWN || challenges[ ichallenge ].authReplyMsg == AUTH_REPLY_WAITING ) {
- // the client may be trying to connect to us in LAN mode, and the server disagrees
- // let the client know so it would switch to authed connection
- idBitMsg outMsg;
- byte msgBuf[ MAX_MESSAGE_SIZE ];
- outMsg.Init( msgBuf, sizeof( msgBuf ) );
- outMsg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
- outMsg.WriteString( "authrequired" );
- serverPort.SendPacket( from, outMsg.GetData(), outMsg.GetSize() );
- }
-
- PrintOOB( from, SERVER_PRINT_MISC, msg );
-
- // update the guid in the challenges
- idStr::snPrintf( challenges[ ichallenge ].guid, sizeof( challenges[ ichallenge ].guid ), guid );
- // once auth replied denied, stop sending further requests
- if ( challenges[ ichallenge ].authReply != AUTH_DENY ) {
- // emit a cd key confirmation request
- outMsg.Init( msgBuf, sizeof( msgBuf ) );
- outMsg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
- outMsg.WriteString( "srvAuth" );
- outMsg.WriteLong( ASYNC_PROTOCOL_VERSION );
- outMsg.WriteNetadr( from );
- outMsg.WriteLong( clientId );
- outMsg.WriteString( guid );
- // protocol 1.37 addition
- outMsg.WriteByte( fileSystem->RunningD3XP() );
- serverPort.SendPacket( idAsyncNetwork::GetMasterAddress(), outMsg.GetData(), outMsg.GetSize() );
- }
- return;
- default:
- assert( challenges[ ichallenge ].authState == CDK_OK || challenges[ ichallenge ].authState == CDK_PUREOK );
- }
- numClients = 0;
- for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
- serverClient_t &client = clients[ i ];
- if ( client.clientState >= SCS_PUREWAIT ) {
- numClients++;
- }
- }
- // game may be passworded, client banned by IP or GUID
- // if authState == CDK_PUREOK, the check was already performed once before entering pure checks
- // but meanwhile, the max players may have been reached
- msg.ReadString( password, sizeof( password ) );
- char reason[MAX_STRING_CHARS];
- allowReply_t reply = game->ServerAllowClient( numClients, Sys_NetAdrToString( from ), guid, password, reason );
- if ( reply != ALLOW_YES ) {
- common->DPrintf( "game denied connection for %s\n", Sys_NetAdrToString( from ) );
- // SERVER_PRINT_GAMEDENY passes the game opcode through. Don't use PrintOOB
- outMsg.Init( msgBuf, sizeof( msgBuf ) );
- outMsg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
- outMsg.WriteString( "print" );
- outMsg.WriteLong( SERVER_PRINT_GAMEDENY );
- outMsg.WriteLong( reply );
- outMsg.WriteString( reason );
- serverPort.SendPacket( from, outMsg.GetData(), outMsg.GetSize() );
- return;
- }
- // enter pure checks if necessary
- if ( sessLocal.mapSpawnData.serverInfo.GetInt( "si_pure" ) && challenges[ ichallenge ].authState != CDK_PUREOK ) {
- if ( SendPureServerMessage( from, OS ) ) {
- challenges[ ichallenge ].authState = CDK_PUREWAIT;
- return;
- }
- }
- // push back decl checksum here when running pure. just an additional safe check
- if ( sessLocal.mapSpawnData.serverInfo.GetInt( "si_pure" ) && clientDataChecksum != serverDataChecksum ) {
- PrintOOB( from, SERVER_PRINT_MISC, "#str_04844" );
- return;
- }
- ping = serverTime - challenges[ ichallenge ].pingTime;
- common->Printf( "challenge from %s connecting with %d ping\n", Sys_NetAdrToString( from ), ping );
- challenges[ ichallenge ].connected = true;
- // find a slot for the client
- for ( islot = 0; islot < 3; islot++ ) {
- for ( clientNum = 0; clientNum < MAX_ASYNC_CLIENTS; clientNum++ ) {
- serverClient_t &client = clients[ clientNum ];
- if ( islot == 0 ) {
- // if this slot uses the same IP and port
- if ( Sys_CompareNetAdrBase( from, client.channel.GetRemoteAddress() ) &&
- ( clientId == client.clientId || from.port == client.channel.GetRemoteAddress().port ) ) {
- break;
- }
- } else if ( islot == 1 ) {
- // if this client is not connected and the slot uses the same IP
- if ( client.clientState >= SCS_PUREWAIT ) {
- continue;
- }
- if ( Sys_CompareNetAdrBase( from, client.channel.GetRemoteAddress() ) ) {
- break;
- }
- } else if ( islot == 2 ) {
- // if this slot is free
- if ( client.clientState == SCS_FREE ) {
- break;
- }
- }
- }
- if ( clientNum < MAX_ASYNC_CLIENTS ) {
- // initialize
- clients[ clientNum ].channel.Init( from, serverId );
- clients[ clientNum ].OS = OS;
- strncpy( clients[ clientNum ].guid, guid, 12 );
- clients[ clientNum ].guid[11] = 0;
- break;
- }
- }
- // if no free spots available
- if ( clientNum >= MAX_ASYNC_CLIENTS ) {
- PrintOOB( from, SERVER_PRINT_MISC, "#str_04845" );
- return;
- }
- common->Printf( "sending connect response to %s\n", Sys_NetAdrToString( from ) );
- // send connect response message
- outMsg.Init( msgBuf, sizeof( msgBuf ) );
- outMsg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
- outMsg.WriteString( "connectResponse" );
- outMsg.WriteLong( clientNum );
- outMsg.WriteLong( gameInitId );
- outMsg.WriteLong( gameFrame );
- outMsg.WriteLong( gameTime );
- outMsg.WriteDeltaDict( sessLocal.mapSpawnData.serverInfo, NULL );
- serverPort.SendPacket( from, outMsg.GetData(), outMsg.GetSize() );
-
- InitClient( clientNum, clientId, clientRate );
- clients[clientNum].gameInitSequence = 1;
- clients[clientNum].snapshotSequence = 1;
- // clear the challenge struct so a reconnect from this client IP starts clean
- memset( &challenges[ ichallenge ], 0, sizeof( challenge_t ) );
- }
- /*
- ==================
- idAsyncServer::VerifyChecksumMessage
- ==================
- */
- bool idAsyncServer::VerifyChecksumMessage( int clientNum, const netadr_t *from, const idBitMsg &msg, idStr &reply, int OS ) {
- int i, numChecksums;
- int checksums[ MAX_PURE_PAKS ];
- int gamePakChecksum;
- int serverChecksums[ MAX_PURE_PAKS ];
- int serverGamePakChecksum;
- // pak checksums, in a 0-terminated list
- numChecksums = 0;
- do {
- i = msg.ReadLong( );
- checksums[ numChecksums++ ] = i;
- // just to make sure a broken client doesn't crash us
- if ( numChecksums >= MAX_PURE_PAKS ) {
- common->Warning( "MAX_PURE_PAKS ( %d ) exceeded in idAsyncServer::ProcessPureMessage\n", MAX_PURE_PAKS );
- sprintf( reply, "#str_07144" );
- return false;
- }
- } while ( i );
- numChecksums--;
- // code pak checksum
- gamePakChecksum = msg.ReadLong( );
- fileSystem->GetPureServerChecksums( serverChecksums, OS, &serverGamePakChecksum );
- assert( serverChecksums[ 0 ] );
- // compare the lists
- if ( serverGamePakChecksum != gamePakChecksum ) {
- common->Printf( "client %s: invalid game code pak ( 0x%x )\n", from ? Sys_NetAdrToString( *from ) : va( "%d", clientNum ), gamePakChecksum );
- sprintf( reply, "#str_07145" );
- return false;
- }
- for ( i = 0; serverChecksums[ i ] != 0; i++ ) {
- if ( checksums[ i ] != serverChecksums[ i ] ) {
- common->DPrintf( "client %s: pak missing ( 0x%x )\n", from ? Sys_NetAdrToString( *from ) : va( "%d", clientNum ), serverChecksums[ i ] );
- sprintf( reply, "pak missing ( 0x%x )\n", serverChecksums[ i ] );
- return false;
- }
- }
- if ( checksums[ i ] != 0 ) {
- common->DPrintf( "client %s: extra pak file referenced ( 0x%x )\n", from ? Sys_NetAdrToString( *from ) : va( "%d", clientNum ), checksums[ i ] );
- sprintf( reply, "extra pak file referenced ( 0x%x )\n", checksums[ i ] );
- return false;
- }
- return true;
- }
- /*
- ==================
- idAsyncServer::ProcessPureMessage
- ==================
- */
- void idAsyncServer::ProcessPureMessage( const netadr_t from, const idBitMsg &msg ) {
- int iclient, challenge, clientId;
- idStr reply;
- challenge = msg.ReadLong();
- clientId = msg.ReadShort();
- if ( ( iclient = ValidateChallenge( from, challenge, clientId ) ) == -1 ) {
- return;
- }
- if ( challenges[ iclient ].authState != CDK_PUREWAIT ) {
- common->DPrintf( "client %s: got pure message, not in CDK_PUREWAIT\n", Sys_NetAdrToString( from ) );
- return;
- }
- if ( !VerifyChecksumMessage( iclient, &from, msg, reply, challenges[ iclient ].OS ) ) {
- PrintOOB( from, SERVER_PRINT_MISC, reply );
- return;
- }
- common->DPrintf( "client %s: passed pure checks\n", Sys_NetAdrToString( from ) );
- challenges[ iclient ].authState = CDK_PUREOK; // next connect message will get the client through completely
- }
- /*
- ==================
- idAsyncServer::ProcessReliablePure
- ==================
- */
- void idAsyncServer::ProcessReliablePure( int clientNum, const idBitMsg &msg ) {
- idStr reply;
- idBitMsg outMsg;
- byte msgBuf[MAX_MESSAGE_SIZE];
- int clientGameInitId;
-
- clientGameInitId = msg.ReadLong();
- if ( clientGameInitId != gameInitId ) {
- common->DPrintf( "client %d: ignoring reliable pure from an old gameInit (%d)\n", clientNum, clientGameInitId );
- return;
- }
- if ( clients[ clientNum ].clientState != SCS_PUREWAIT ) {
- // should not happen unless something is very wrong. still, don't let this crash us, just get rid of the client
- common->DPrintf( "client %d: got reliable pure while != SCS_PUREWAIT, sending a reload\n", clientNum );
- outMsg.Init( msgBuf, sizeof( msgBuf ) );
- outMsg.WriteByte( SERVER_RELIABLE_MESSAGE_RELOAD );
- SendReliableMessage( clientNum, msg );
- // go back to SCS_CONNECTED to sleep on the client until it goes away for a reconnect
- clients[ clientNum ].clientState = SCS_CONNECTED;
- return;
- }
- if ( !VerifyChecksumMessage( clientNum, NULL, msg, reply, clients[ clientNum ].OS ) ) {
- DropClient( clientNum, reply );
- return;
- }
- common->DPrintf( "client %d: passed pure checks (reliable channel)\n", clientNum );
- clients[ clientNum ].clientState = SCS_CONNECTED;
- }
- /*
- ==================
- idAsyncServer::RemoteConsoleOutput
- ==================
- */
- void idAsyncServer::RemoteConsoleOutput( const char *string ) {
- noRconOutput = false;
- PrintOOB( rconAddress, SERVER_PRINT_RCON, string );
- }
- /*
- ==================
- RConRedirect
- ==================
- */
- void RConRedirect( const char *string ) {
- idAsyncNetwork::server.RemoteConsoleOutput( string );
- }
- /*
- ==================
- idAsyncServer::ProcessRemoteConsoleMessage
- ==================
- */
- void idAsyncServer::ProcessRemoteConsoleMessage( const netadr_t from, const idBitMsg &msg ) {
- idBitMsg outMsg;
- byte msgBuf[952];
- char string[MAX_STRING_CHARS];
- if ( idAsyncNetwork::serverRemoteConsolePassword.GetString()[0] == '\0' ) {
- PrintOOB( from, SERVER_PRINT_MISC, "#str_04846" );
- return;
- }
- msg.ReadString( string, sizeof( string ) );
- if ( idStr::Icmp( string, idAsyncNetwork::serverRemoteConsolePassword.GetString() ) != 0 ) {
- PrintOOB( from, SERVER_PRINT_MISC, "#str_04847" );
- return;
- }
- msg.ReadString( string, sizeof( string ) );
- common->Printf( "rcon from %s: %s\n", Sys_NetAdrToString( from ), string );
- rconAddress = from;
- noRconOutput = true;
- common->BeginRedirect( (char *)msgBuf, sizeof( msgBuf ), RConRedirect );
- cmdSystem->BufferCommandText( CMD_EXEC_NOW, string );
- common->EndRedirect();
- if ( noRconOutput ) {
- PrintOOB( rconAddress, SERVER_PRINT_RCON, "#str_04848" );
- }
- }
- /*
- ==================
- idAsyncServer::ProcessGetInfoMessage
- ==================
- */
- void idAsyncServer::ProcessGetInfoMessage( const netadr_t from, const idBitMsg &msg ) {
- int i, challenge;
- idBitMsg outMsg;
- byte msgBuf[MAX_MESSAGE_SIZE];
- if ( !IsActive() ) {
- return;
- }
- common->DPrintf( "Sending info response to %s\n", Sys_NetAdrToString( from ) );
- challenge = msg.ReadLong();
- outMsg.Init( msgBuf, sizeof( msgBuf ) );
- outMsg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
- outMsg.WriteString( "infoResponse" );
- outMsg.WriteLong( challenge );
- outMsg.WriteLong( ASYNC_PROTOCOL_VERSION );
- outMsg.WriteDeltaDict( sessLocal.mapSpawnData.serverInfo, NULL );
- for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
- serverClient_t &client = clients[i];
- if ( client.clientState < SCS_CONNECTED ) {
- continue;
- }
- outMsg.WriteByte( i );
- outMsg.WriteShort( client.clientPing );
- outMsg.WriteLong( client.channel.GetMaxOutgoingRate() );
- outMsg.WriteString( sessLocal.mapSpawnData.userInfo[i].GetString( "ui_name", "Player" ) );
- }
- outMsg.WriteByte( MAX_ASYNC_CLIENTS );
- outMsg.WriteLong( fileSystem->GetOSMask() );
- serverPort.SendPacket( from, outMsg.GetData(), outMsg.GetSize() );
- }
- /*
- ===============
- idAsyncServer::PrintLocalServerInfo
- see (client) "getInfo" -> (server) "infoResponse" -> (client)ProcessGetInfoMessage
- ===============
- */
- void idAsyncServer::PrintLocalServerInfo( void ) {
- int i;
- common->Printf( "server '%s' IP = %s\nprotocol %d.%d OS mask 0x%x\n",
- sessLocal.mapSpawnData.serverInfo.GetString( "si_name" ),
- Sys_NetAdrToString( serverPort.GetAdr() ),
- ASYNC_PROTOCOL_MAJOR,
- ASYNC_PROTOCOL_MINOR,
- fileSystem->GetOSMask() );
- sessLocal.mapSpawnData.serverInfo.Print();
- for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
- serverClient_t &client = clients[i];
- if ( client.clientState < SCS_CONNECTED ) {
- continue;
- }
- common->Printf( "client %2d: %s, ping = %d, rate = %d\n", i,
- sessLocal.mapSpawnData.userInfo[i].GetString( "ui_name", "Player" ),
- client.clientPing, client.channel.GetMaxOutgoingRate() );
- }
- }
- /*
- ==================
- idAsyncServer::ConnectionlessMessage
- ==================
- */
- bool idAsyncServer::ConnectionlessMessage( const netadr_t from, const idBitMsg &msg ) {
- char string[MAX_STRING_CHARS*2]; // M. Quinn - Even Balance - PB Packets need more than 1024
- msg.ReadString( string, sizeof( string ) );
- // info request
- if ( idStr::Icmp( string, "getInfo" ) == 0 ) {
- ProcessGetInfoMessage( from, msg );
- return false;
- }
- // remote console
- if ( idStr::Icmp( string, "rcon" ) == 0 ) {
- ProcessRemoteConsoleMessage( from, msg );
- return true;
- }
- if ( !active ) {
- PrintOOB( from, SERVER_PRINT_MISC, "#str_04849" );
- return false;
- }
- // challenge from a client
- if ( idStr::Icmp( string, "challenge" ) == 0 ) {
- ProcessChallengeMessage( from, msg );
- return false;
- }
- // connect from a client
- if ( idStr::Icmp( string, "connect" ) == 0 ) {
- ProcessConnectMessage( from, msg );
- return false;
- }
- // pure mesasge from a client
- if ( idStr::Icmp( string, "pureClient" ) == 0 ) {
- ProcessPureMessage( from, msg );
- return false;
- }
- // download request
- if ( idStr::Icmp( string, "downloadRequest" ) == 0 ) {
- ProcessDownloadRequestMessage( from, msg );
- }
-
- // auth server
- if ( idStr::Icmp( string, "auth" ) == 0 ) {
- if ( !Sys_CompareNetAdrBase( from, idAsyncNetwork::GetMasterAddress() ) ) {
- common->Printf( "auth: bad source %s\n", Sys_NetAdrToString( from ) );
- return false;
- }
- if ( idAsyncNetwork::LANServer.GetBool() ) {
- common->Printf( "auth message from master. net_LANServer is enabled, ignored.\n" );
- }
- ProcessAuthMessage( msg );
- return false;
- }
- return false;
- }
- /*
- ==================
- idAsyncServer::ProcessMessage
- ==================
- */
- bool idAsyncServer::ProcessMessage( const netadr_t from, idBitMsg &msg ) {
- int i, id, sequence;
- idBitMsg outMsg;
- byte msgBuf[MAX_MESSAGE_SIZE];
- id = msg.ReadShort();
- // check for a connectionless message
- if ( id == CONNECTIONLESS_MESSAGE_ID ) {
- return ConnectionlessMessage( from, msg );
- }
- if ( msg.GetRemaingData() < 4 ) {
- common->DPrintf( "%s: tiny packet\n", Sys_NetAdrToString( from ) );
- return false;
- }
- // find out which client the message is from
- for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
- serverClient_t &client = clients[i];
- if ( client.clientState == SCS_FREE ) {
- continue;
- }
- // This does not compare the UDP port, because some address translating
- // routers will change that at arbitrary times.
- if ( !Sys_CompareNetAdrBase( from, client.channel.GetRemoteAddress() ) || id != client.clientId ) {
- continue;
- }
- // make sure it is a valid, in sequence packet
- if ( !client.channel.Process( from, serverTime, msg, sequence ) ) {
- return false; // out of order, duplicated, fragment, etc.
- }
- // zombie clients still need to do the channel processing to make sure they don't
- // need to retransmit the final reliable message, but they don't do any other processing
- if ( client.clientState == SCS_ZOMBIE ) {
- return false;
- }
- client.lastPacketTime = serverTime;
- ProcessReliableClientMessages( i );
- ProcessUnreliableClientMessage( i, msg );
- return false;
- }
-
- // if we received a sequenced packet from an address we don't recognize,
- // send an out of band disconnect packet to it
- outMsg.Init( msgBuf, sizeof( msgBuf ) );
- outMsg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
- outMsg.WriteString( "disconnect" );
- serverPort.SendPacket( from, outMsg.GetData(), outMsg.GetSize() );
- return false;
- }
- /*
- ==================
- idAsyncServer::SendReliableGameMessage
- ==================
- */
- void idAsyncServer::SendReliableGameMessage( int clientNum, const idBitMsg &msg ) {
- int i;
- idBitMsg outMsg;
- byte msgBuf[MAX_MESSAGE_SIZE];
- outMsg.Init( msgBuf, sizeof( msgBuf ) );
- outMsg.WriteByte( SERVER_RELIABLE_MESSAGE_GAME );
- outMsg.WriteData( msg.GetData(), msg.GetSize() );
- if ( clientNum >= 0 && clientNum < MAX_ASYNC_CLIENTS ) {
- if ( clients[clientNum].clientState == SCS_INGAME ) {
- SendReliableMessage( clientNum, outMsg );
- }
- return;
- }
- for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
- if ( clients[i].clientState != SCS_INGAME ) {
- continue;
- }
- SendReliableMessage( i, outMsg );
- }
- }
- /*
- ==================
- idAsyncServer::LocalClientSendReliableMessageExcluding
- ==================
- */
- void idAsyncServer::SendReliableGameMessageExcluding( int clientNum, const idBitMsg &msg ) {
- int i;
- idBitMsg outMsg;
- byte msgBuf[MAX_MESSAGE_SIZE];
- assert( clientNum >= 0 && clientNum < MAX_ASYNC_CLIENTS );
- outMsg.Init( msgBuf, sizeof( msgBuf ) );
- outMsg.WriteByte( SERVER_RELIABLE_MESSAGE_GAME );
- outMsg.WriteData( msg.GetData(), msg.GetSize() );
- for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
- if ( i == clientNum ) {
- continue;
- }
- if ( clients[i].clientState != SCS_INGAME ) {
- continue;
- }
- SendReliableMessage( i, outMsg );
- }
- }
- /*
- ==================
- idAsyncServer::LocalClientSendReliableMessage
- ==================
- */
- void idAsyncServer::LocalClientSendReliableMessage( const idBitMsg &msg ) {
- if ( localClientNum < 0 ) {
- common->Printf( "LocalClientSendReliableMessage: no local client\n" );
- return;
- }
- game->ServerProcessReliableMessage( localClientNum, msg );
- }
- /*
- ==================
- idAsyncServer::ProcessConnectionLessMessages
- ==================
- */
- void idAsyncServer::ProcessConnectionLessMessages( void ) {
- int size, id;
- idBitMsg msg;
- byte msgBuf[MAX_MESSAGE_SIZE];
- netadr_t from;
- if ( !serverPort.GetPort() ) {
- return;
- }
- while( serverPort.GetPacket( from, msgBuf, size, sizeof( msgBuf ) ) ) {
- msg.Init( msgBuf, sizeof( msgBuf ) );
- msg.SetSize( size );
- msg.BeginReading();
- id = msg.ReadShort();
- if ( id == CONNECTIONLESS_MESSAGE_ID ) {
- ConnectionlessMessage( from, msg );
- }
- }
- }
- /*
- ==================
- idAsyncServer::UpdateTime
- ==================
- */
- int idAsyncServer::UpdateTime( int clamp ) {
- int time, msec;
- time = Sys_Milliseconds();
- msec = idMath::ClampInt( 0, clamp, time - realTime );
- realTime = time;
- serverTime += msec;
- return msec;
- }
- /*
- ==================
- idAsyncServer::RunFrame
- ==================
- */
- void idAsyncServer::RunFrame( void ) {
- int i, msec, size;
- bool newPacket;
- idBitMsg msg;
- byte msgBuf[MAX_MESSAGE_SIZE];
- netadr_t from;
- int outgoingRate, incomingRate;
- float outgoingCompression, incomingCompression;
- msec = UpdateTime( 100 );
- if ( !serverPort.GetPort() ) {
- return;
- }
- if ( !active ) {
- ProcessConnectionLessMessages();
- return;
- }
-
- 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 = serverPort.GetPacketBlocking( from, msgBuf, size, sizeof( msgBuf ), USERCMD_MSEC - gameTimeResidual - 1 );
- if ( newPacket ) {
- msg.Init( msgBuf, sizeof( msgBuf ) );
- msg.SetSize( size );
- msg.BeginReading();
- if ( ProcessMessage( from, msg ) ) {
- return; // return because rcon was used
- }
- }
- msec = UpdateTime( 100 );
- gameTimeResidual += msec;
- } while( newPacket );
- } while( gameTimeResidual < USERCMD_MSEC );
- // send heart beat to master servers
- MasterHeartbeat();
- // check for clients that timed out
- CheckClientTimeouts();
- if ( idAsyncNetwork::idleServer.GetBool() == ( !GetNumClients() || GetNumIdleClients() != GetNumClients() ) ) {
- idAsyncNetwork::idleServer.SetBool( !idAsyncNetwork::idleServer.GetBool() );
- // the need to propagate right away, only this
- sessLocal.mapSpawnData.serverInfo.Set( "si_idleServer", idAsyncNetwork::idleServer.GetString() );
- game->SetServerInfo( sessLocal.mapSpawnData.serverInfo );
- }
- // make sure the time doesn't wrap
- if ( serverTime > 0x70000000 ) {
- ExecuteMapChange();
- return;
- }
- // check for synchronized cvar changes
- if ( cvarSystem->GetModifiedFlags() & CVAR_NETWORKSYNC ) {
- idDict newCvars;
- newCvars = *cvarSystem->MoveCVarsToDict( CVAR_NETWORKSYNC );
- SendSyncedCvarsBroadcast( newCvars );
- cvarSystem->ClearModifiedFlags( CVAR_NETWORKSYNC );
- }
- // check for user info changes of the local client
- if ( cvarSystem->GetModifiedFlags() & CVAR_USERINFO ) {
- if ( localClientNum >= 0 ) {
- idDict newInfo;
- game->ThrottleUserInfo( );
- newInfo = *cvarSystem->MoveCVarsToDict( CVAR_USERINFO );
- SendUserInfoBroadcast( localClientNum, newInfo );
- }
- cvarSystem->ClearModifiedFlags( CVAR_USERINFO );
- }
- // advance the server game
- while( gameTimeResidual >= USERCMD_MSEC ) {
- // sample input for the local client
- LocalClientInput();
- // duplicate usercmds for clients if no new ones are available
- DuplicateUsercmds( gameFrame, gameTime );
- // advance game
- gameReturn_t ret = game->RunFrame( userCmds[gameFrame & ( MAX_USERCMD_BACKUP - 1 ) ] );
- idAsyncNetwork::ExecuteSessionCommand( ret.sessionCommand );
- // update time
- gameFrame++;
- gameTime += USERCMD_MSEC;
- gameTimeResidual -= USERCMD_MSEC;
- }
- // duplicate usercmds so there is always at least one available to send with snapshots
- DuplicateUsercmds( gameFrame, gameTime );
- // send snapshots to connected clients
- for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
- serverClient_t &client = clients[i];
- if ( client.clientState == SCS_FREE || i == localClientNum ) {
- continue;
- }
- // modify maximum rate if necesary
- if ( idAsyncNetwork::serverMaxClientRate.IsModified() ) {
- client.channel.SetMaxOutgoingRate( Min( client.clientRate, idAsyncNetwork::serverMaxClientRate.GetInteger() ) );
- }
- // if the channel is not yet ready to send new data
- if ( !client.channel.ReadyToSend( serverTime ) ) {
- continue;
- }
- // send additional message fragments if the last message was too large to send at once
- if ( client.channel.UnsentFragmentsLeft() ) {
- client.channel.SendNextFragment( serverPort, serverTime );
- continue;
- }
- if ( client.clientState == SCS_INGAME ) {
- if ( !SendSnapshotToClient( i ) ) {
- SendPingToClient( i );
- }
- } else {
- SendEmptyToClient( i );
- }
- }
- if ( com_showAsyncStats.GetBool() ) {
- UpdateAsyncStatsAvg();
- // dedicated will verbose to console
- if ( idAsyncNetwork::serverDedicated.GetBool() && serverTime >= nextAsyncStatsTime ) {
- common->Printf( "delay = %d msec, total outgoing rate = %d KB/s, total incoming rate = %d KB/s\n", GetDelay(),
- GetOutgoingRate() >> 10, GetIncomingRate() >> 10 );
- for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
- outgoingRate = GetClientOutgoingRate( i );
- incomingRate = GetClientIncomingRate( i );
- outgoingCompression = GetClientOutgoingCompression( i );
- incomingCompression = GetClientIncomingCompression( i );
- if ( outgoingRate != -1 && incomingRate != -1 ) {
- common->Printf( "client %d: out rate = %d B/s (% -2.1f%%), in rate = %d B/s (% -2.1f%%)\n",
- i, outgoingRate, outgoingCompression, incomingRate, incomingCompression );
- }
- }
- idStr msg;
- GetAsyncStatsAvgMsg( msg );
- common->Printf( va( "%s\n", msg.c_str() ) );
- nextAsyncStatsTime = serverTime + 1000;
- }
- }
- idAsyncNetwork::serverMaxClientRate.ClearModified();
- }
- /*
- ==================
- idAsyncServer::PacifierUpdate
- ==================
- */
- void idAsyncServer::PacifierUpdate( void ) {
- int i;
- if ( !IsActive() ) {
- return;
- }
- realTime = Sys_Milliseconds();
- ProcessConnectionLessMessages();
- for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
- if ( clients[i].clientState >= SCS_PUREWAIT ) {
- if ( clients[i].channel.UnsentFragmentsLeft() ) {
- clients[i].channel.SendNextFragment( serverPort, serverTime );
- } else {
- SendEmptyToClient( i );
- }
- }
- }
- }
- /*
- ==================
- idAsyncServer::PrintOOB
- ==================
- */
- void idAsyncServer::PrintOOB( const netadr_t to, int opcode, const char *string ) {
- idBitMsg outMsg;
- byte msgBuf[ MAX_MESSAGE_SIZE ];
- outMsg.Init( msgBuf, sizeof( msgBuf ) );
- outMsg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
- outMsg.WriteString( "print" );
- outMsg.WriteLong( opcode );
- outMsg.WriteString( string );
- serverPort.SendPacket( to, outMsg.GetData(), outMsg.GetSize() );
- }
- /*
- ==================
- idAsyncServer::MasterHeartbeat
- ==================
- */
- void idAsyncServer::MasterHeartbeat( bool force ) {
- if ( idAsyncNetwork::LANServer.GetBool() ) {
- if ( force ) {
- common->Printf( "net_LANServer is enabled. Not sending heartbeats\n" );
- }
- return;
- }
- if ( force ) {
- nextHeartbeatTime = 0;
- }
- // not yet
- if ( serverTime < nextHeartbeatTime ) {
- return;
- }
- nextHeartbeatTime = serverTime + HEARTBEAT_MSEC;
- for ( int i = 0 ; i < MAX_MASTER_SERVERS ; i++ ) {
- netadr_t adr;
- if ( idAsyncNetwork::GetMasterAddress( i, adr ) ) {
- common->Printf( "Sending heartbeat to %s\n", Sys_NetAdrToString( adr ) );
- idBitMsg outMsg;
- byte msgBuf[ MAX_MESSAGE_SIZE ];
- outMsg.Init( msgBuf, sizeof( msgBuf ) );
- outMsg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
- outMsg.WriteString( "heartbeat" );
- serverPort.SendPacket( adr, outMsg.GetData(), outMsg.GetSize() );
- }
- }
- }
- /*
- ===============
- idAsyncServer::SendEnterGameToClient
- ===============
- */
- void idAsyncServer::SendEnterGameToClient( int clientNum ) {
- idBitMsg msg;
- byte msgBuf[ MAX_MESSAGE_SIZE ];
- msg.Init( msgBuf, sizeof( msgBuf ) );
- msg.WriteByte( SERVER_RELIABLE_MESSAGE_ENTERGAME );
- SendReliableMessage( clientNum, msg );
- }
- /*
- ===============
- idAsyncServer::UpdateAsyncStatsAvg
- ===============
- */
- void idAsyncServer::UpdateAsyncStatsAvg( void ) {
- stats_average_sum -= stats_outrate[ stats_current ];
- stats_outrate[ stats_current ] = idAsyncNetwork::server.GetOutgoingRate();
- if ( stats_outrate[ stats_current ] > stats_max ) {
- stats_max = stats_outrate[ stats_current ];
- stats_max_index = stats_current;
- } else if ( stats_current == stats_max_index ) {
- // find the new max
- int i;
- stats_max = 0;
- for ( i = 0; i < stats_numsamples ; i++ ) {
- if ( stats_outrate[ i ] > stats_max ) {
- stats_max = stats_outrate[ i ];
- stats_max_index = i;
- }
- }
- }
- stats_average_sum += stats_outrate[ stats_current ];
- stats_current++; stats_current %= stats_numsamples;
- }
- /*
- ===============
- idAsyncServer::GetAsyncStatsAvgMsg
- ===============
- */
- void idAsyncServer::GetAsyncStatsAvgMsg( idStr &msg ) {
- sprintf( msg, "avrg out: %d B/s - max %d B/s ( over %d ms )", stats_average_sum / stats_numsamples, stats_max, idAsyncNetwork::serverSnapshotDelay.GetInteger() * stats_numsamples );
- }
- /*
- ===============
- idAsyncServer::ProcessDownloadRequestMessage
- ===============
- */
- void idAsyncServer::ProcessDownloadRequestMessage( const netadr_t from, const idBitMsg &msg ) {
- int challenge, clientId, iclient, numPaks, i;
- int dlGamePak;
- int dlPakChecksum;
- int dlSize[ MAX_PURE_PAKS ]; // sizes
- idStrList pakNames; // relative path
- idStrList pakURLs; // game URLs
- char pakbuf[ MAX_STRING_CHARS ];
- idStr paklist;
- byte msgBuf[ MAX_MESSAGE_SIZE ];
- byte tmpBuf[ MAX_MESSAGE_SIZE ];
- idBitMsg outMsg, tmpMsg;
- int dlRequest;
- int voidSlots = 0; // to count and verbose the right number of paks requested for downloads
- challenge = msg.ReadLong();
- clientId = msg.ReadShort();
- dlRequest = msg.ReadLong();
- if ( ( iclient = ValidateChallenge( from, challenge, clientId ) ) == -1 ) {
- return;
- }
- if ( challenges[ iclient ].authState != CDK_PUREWAIT ) {
- common->DPrintf( "client %s: got download request message, not in CDK_PUREWAIT\n", Sys_NetAdrToString( from ) );
- return;
- }
-
- // the first token of the pak names list passed to the game will be empty if no game pak is requested
- dlGamePak = msg.ReadLong();
- if ( dlGamePak ) {
- if ( !( dlSize[ 0 ] = fileSystem->ValidateDownloadPakForChecksum( dlGamePak, pakbuf, true ) ) ) {
- common->Warning( "client requested unknown game pak 0x%x", dlGamePak );
- pakbuf[ 0 ] = '\0';
- voidSlots++;
- }
- } else {
- pakbuf[ 0 ] = '\0';
- voidSlots++;
- }
- pakNames.Append( pakbuf );
- numPaks = 1;
- // read the checksums, build path names and pass that to the game code
- dlPakChecksum = msg.ReadLong();
- while ( dlPakChecksum ) {
- if ( !( dlSize[ numPaks ] = fileSystem->ValidateDownloadPakForChecksum( dlPakChecksum, pakbuf, false ) ) ) {
- // we pass an empty token to the game so our list doesn't get offset
- common->Warning( "client requested an unknown pak 0x%x", dlPakChecksum );
- pakbuf[ 0 ] = '\0';
- voidSlots++;
- }
- pakNames.Append( pakbuf );
- numPaks++;
- dlPakChecksum = msg.ReadLong();
- }
- for ( i = 0; i < pakNames.Num(); i++ ) {
- if ( i > 0 ) {
- paklist += ";";
- }
- paklist += pakNames[ i ].c_str();
- }
- // read the message and pass it to the game code
- common->DPrintf( "got download request for %d paks - %s\n", numPaks - voidSlots, paklist.c_str() );
- outMsg.Init( msgBuf, sizeof( msgBuf ) );
- outMsg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
- outMsg.WriteString( "downloadInfo" );
- outMsg.WriteLong( dlRequest );
- if ( !game->DownloadRequest( Sys_NetAdrToString( from ), challenges[ iclient ].guid, paklist.c_str(), pakbuf ) ) {
- common->DPrintf( "game: no downloads\n" );
- outMsg.WriteByte( SERVER_DL_NONE );
- serverPort.SendPacket( from, outMsg.GetData(), outMsg.GetSize() );
- return;
- }
- char *token, *next;
- int type = 0;
- token = pakbuf;
- next = strchr( token, ';' );
- while ( token ) {
- if ( next ) {
- *next = '\0';
- }
-
- if ( type == 0 ) {
- type = atoi( token );
- } else if ( type == SERVER_DL_REDIRECT ) {
- common->DPrintf( "download request: redirect to URL %s\n", token );
- outMsg.WriteByte( SERVER_DL_REDIRECT );
- outMsg.WriteString( token );
- serverPort.SendPacket( from, outMsg.GetData(), outMsg.GetSize() );
- return;
- } else if ( type == SERVER_DL_LIST ) {
- pakURLs.Append( token );
- } else {
- common->DPrintf( "wrong op type %d\n", type );
- next = token = NULL;
- }
-
- if ( next ) {
- token = next + 1;
- next = strchr( token, ';' );
- } else {
- token = NULL;
- }
- }
- if ( type == SERVER_DL_LIST ) {
- int totalDlSize = 0;
- int numActualPaks = 0;
-
- // put the answer packet together
- outMsg.WriteByte( SERVER_DL_LIST );
-
- tmpMsg.Init( tmpBuf, MAX_MESSAGE_SIZE );
- for ( i = 0; i < pakURLs.Num(); i++ ) {
- tmpMsg.BeginWriting();
- if ( !dlSize[ i ] || !pakURLs[ i ].Length() ) {
- // still send the relative path so the client knows what it missed
- tmpMsg.WriteByte( SERVER_PAK_NO );
- tmpMsg.WriteString( pakNames[ i ] );
- } else {
- totalDlSize += dlSize[ i ];
- numActualPaks++;
- tmpMsg.WriteByte( SERVER_PAK_YES );
- tmpMsg.WriteString( pakNames[ i ] );
- tmpMsg.WriteString( pakURLs[ i ] );
- tmpMsg.WriteLong( dlSize[ i ] );
- }
-
- // keep last 5 bytes for an 'end of message' - SERVER_PAK_END and the totalDlSize long
- if ( outMsg.GetRemainingSpace() - tmpMsg.GetSize() > 5 ) {
- outMsg.WriteData( tmpMsg.GetData(), tmpMsg.GetSize() );
- } else {
- outMsg.WriteByte( SERVER_PAK_END );
- break;
- }
- }
- if ( i == pakURLs.Num() ) {
- // put a closure even if size not exceeded
- outMsg.WriteByte( SERVER_PAK_END );
- }
- common->DPrintf( "download request: download %d paks, %d bytes\n", numActualPaks, totalDlSize );
- serverPort.SendPacket( from, outMsg.GetData(), outMsg.GetSize() );
- }
- }
|