sys_lobby.cpp 133 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142
  1. /*
  2. ===========================================================================
  3. Doom 3 BFG Edition GPL Source Code
  4. Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
  6. Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. In addition, the Doom 3 BFG Edition 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 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
  17. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
  18. ===========================================================================
  19. */
  20. #pragma hdrstop
  21. #include "../idlib/precompiled.h"
  22. #include "sys_lobby.h"
  23. extern idCVar net_connectTimeoutInSeconds;
  24. extern idCVar net_headlessServer;
  25. idCVar net_checkVersion( "net_checkVersion", "0", CVAR_INTEGER, "Check for matching version when clients connect. 0: normal rules, 1: force check, otherwise no check (pass always)" );
  26. idCVar net_peerTimeoutInSeconds( "net_peerTimeoutInSeconds", "30", CVAR_INTEGER, "If the host hasn't received a response from a peer in this amount of time (in seconds), the peer will be disconnected." );
  27. idCVar net_peerTimeoutInSeconds_Lobby( "net_peerTimeoutInSeconds_Lobby", "20", CVAR_INTEGER, "If the host hasn't received a response from a peer in this amount of time (in seconds), the peer will be disconnected." );
  28. // NOTE - The snapshot exchange does the bandwidth challenge
  29. idCVar net_bw_challenge_enable( "net_bw_challenge_enable", "0", CVAR_BOOL, "Enable pre game bandwidth challenge for throttling snap rate" );
  30. idCVar net_bw_test_interval( "net_bw_test_interval", "33", CVAR_INTEGER, "MS - how often to send packets in bandwidth test" );
  31. idCVar net_bw_test_numPackets( "net_bw_test_numPackets", "30", CVAR_INTEGER, "Number of bandwidth challenge packets to send" );
  32. idCVar net_bw_test_packetSizeBytes( "net_bw_test_packetSizeBytes", "1024", CVAR_INTEGER, "Size of each packet to send out" );
  33. idCVar net_bw_test_timeout( "net_bw_test_timeout", "500", CVAR_INTEGER, "MS after receiving a bw test packet that client will time out" );
  34. idCVar net_bw_test_host_timeout( "net_bw_test_host_timeout", "3000", CVAR_INTEGER, "How long host will wait in MS to hear bw results from peers" );
  35. idCVar net_bw_test_throttle_rate_pct( "net_bw_test_throttle_rate_pct", "0.80", CVAR_FLOAT, "Min rate % a peer must match in bandwidth challenge before being throttled. 1.0=perfect, 0.0=received nothing" );
  36. idCVar net_bw_test_throttle_byte_pct( "net_bw_test_throttle_byte_pct", "0.80", CVAR_FLOAT, "Min byte % a peer must match in bandwidth challenge before being throttled. 1.0=perfect (received everything) 0.0=Received nothing" );
  37. idCVar net_bw_test_throttle_seq_pct( "net_bw_test_throttle_seq_pct", "0.80", CVAR_FLOAT, "Min sequence % a peer must match in bandwidth test before being throttled. 1.0=perfect. This score will be more adversely affected by packet loss than byte %" );
  38. idCVar net_ignoreConnects( "net_ignoreConnects", "0", CVAR_INTEGER, "Test as if no one can connect to me. 0 = off, 1 = ignore with no reply, 2 = send goodbye" );
  39. idCVar net_skipGoodbye( "net_skipGoodbye", "0", CVAR_BOOL, "" );
  40. extern unsigned long NetGetVersionChecksum();
  41. /*
  42. ========================
  43. idLobby::idLobby
  44. ========================
  45. */
  46. idLobby::idLobby() {
  47. lobbyType = TYPE_INVALID;
  48. sessionCB = NULL;
  49. localReadSS = NULL;
  50. objMemory = NULL;
  51. haveSubmittedSnaps = false;
  52. state = STATE_IDLE;
  53. failedReason = FAILED_UNKNOWN;
  54. host = -1;
  55. peerIndexOnHost = -1;
  56. isHost = false;
  57. needToDisplayMigrateMsg = false;
  58. migrateMsgFlags = 0;
  59. partyToken = 0; // will be initialized later
  60. loaded = false;
  61. respondToArbitrate = false;
  62. waitForPartyOk = false;
  63. startLoadingFromHost = false;
  64. nextSendPingValuesTime = 0;
  65. lastPingValuesRecvTime = 0;
  66. nextSendMigrationGameTime = 0;
  67. nextSendMigrationGamePeer = 0;
  68. bandwidthChallengeStartTime = 0;
  69. bandwidthChallengeEndTime = 0;
  70. bandwidthChallengeFinished = false;
  71. bandwidthChallengeNumGoodSeq = 0;
  72. lastSnapBspHistoryUpdateSequence = -1;
  73. assert( userList.Max() == freeUsers.Max() );
  74. assert( userList.Max() == userPool.Max() );
  75. userPool.SetNum( userPool.Max() );
  76. assert( freeUsers.Num() == 0 );
  77. assert( freeUsers.Num() == 0 );
  78. // Initialize free user list
  79. for ( int i = 0; i < userPool.Num(); i++ ) {
  80. freeUsers.Append( &userPool[i] );
  81. }
  82. showHostLeftTheSession = false;
  83. connectIsFromInvite = false;
  84. }
  85. /*
  86. ========================
  87. idLobby::Initialize
  88. ========================
  89. */
  90. void idLobby::Initialize( lobbyType_t sessionType_, idSessionCallbacks * callbacks ) {
  91. assert( callbacks != NULL );
  92. lobbyType = sessionType_;
  93. sessionCB = callbacks;
  94. if ( lobbyType == GetActingGameStateLobbyType() ) {
  95. // only needed in multiplayer mode
  96. objMemory = (uint8*)Mem_Alloc( SNAP_OBJ_JOB_MEMORY, TAG_NETWORKING );
  97. lzwData = (lzwCompressionData_t*)Mem_Alloc( sizeof( lzwCompressionData_t ), TAG_NETWORKING );
  98. }
  99. }
  100. //===============================================================================
  101. // ** BEGIN PUBLIC INTERFACE ***
  102. //===============================================================================
  103. /*
  104. ========================
  105. idLobby::StartHosting
  106. ========================
  107. */
  108. void idLobby::StartHosting( const idMatchParameters & parms_ ) {
  109. parms = parms_;
  110. // Allow common to modify the parms
  111. common->OnStartHosting( parms );
  112. Shutdown(); // Make sure we're in a shutdown state before proceeding
  113. assert( GetNumLobbyUsers() == 0 );
  114. assert( lobbyBackend == NULL );
  115. // Get the skill level of all the players that will eventually go into the lobby
  116. StartCreating();
  117. }
  118. /*
  119. ========================
  120. idLobby::StartFinding
  121. ========================
  122. */
  123. void idLobby::StartFinding( const idMatchParameters & parms_ ) {
  124. parms = parms_;
  125. Shutdown(); // Make sure we're in a shutdown state before proceeding
  126. assert( GetNumLobbyUsers() == 0 );
  127. assert( lobbyBackend == NULL );
  128. // Clear search results
  129. searchResults.Clear();
  130. lobbyBackend = sessionCB->FindLobbyBackend( parms, sessionCB->GetPartyLobby().GetNumLobbyUsers(), sessionCB->GetPartyLobby().GetAverageSessionLevel(), idLobbyBackend::TYPE_GAME );
  131. SetState( STATE_SEARCHING );
  132. }
  133. /*
  134. ========================
  135. idLobby::Pump
  136. ========================
  137. */
  138. void idLobby::Pump() {
  139. // Check the heartbeat of all our peers, make sure we shouldn't disconnect from peers that haven't sent a heartbeat in awhile
  140. CheckHeartBeats();
  141. UpdateHostMigration();
  142. UpdateLocalSessionUsers();
  143. switch ( state ) {
  144. case STATE_IDLE: State_Idle(); break;
  145. case STATE_CREATE_LOBBY_BACKEND: State_Create_Lobby_Backend(); break;
  146. case STATE_SEARCHING: State_Searching(); break;
  147. case STATE_OBTAINING_ADDRESS: State_Obtaining_Address(); break;
  148. case STATE_CONNECT_HELLO_WAIT: State_Connect_Hello_Wait(); break;
  149. case STATE_FINALIZE_CONNECT: State_Finalize_Connect(); break;
  150. case STATE_FAILED: break;
  151. default:
  152. idLib::Error( "idLobby::Pump: Unknown state." );
  153. }
  154. }
  155. /*
  156. ========================
  157. idLobby::ProcessSnapAckQueue
  158. ========================
  159. */
  160. void idLobby::ProcessSnapAckQueue() {
  161. SCOPED_PROFILE_EVENT( "ProcessSnapAckQueue" );
  162. const int SNAP_ACKS_TO_PROCESS_PER_FRAME = 1;
  163. int numProcessed = 0;
  164. while ( snapDeltaAckQueue.Num() > 0 && numProcessed < SNAP_ACKS_TO_PROCESS_PER_FRAME ) {
  165. if ( ApplySnapshotDeltaInternal( snapDeltaAckQueue[0].p, snapDeltaAckQueue[0].snapshotNumber ) ) {
  166. numProcessed++;
  167. }
  168. snapDeltaAckQueue.RemoveIndex( 0 );
  169. }
  170. }
  171. /*
  172. ========================
  173. idLobby::Shutdown
  174. ========================
  175. */
  176. void idLobby::Shutdown( bool retainMigrationInfo, bool skipGoodbye ) {
  177. // Cancel host migration if we were in the process of it and this is the session type that was migrating
  178. if ( !retainMigrationInfo && migrationInfo.state != MIGRATE_NONE ) {
  179. idLib::Printf( "Cancelling host migration on %s.\n", GetLobbyName() );
  180. EndMigration();
  181. }
  182. failedReason = FAILED_UNKNOWN;
  183. if ( lobbyBackend == NULL ) {
  184. NET_VERBOSE_PRINT( "NET: ShutdownLobby (already shutdown) (%s)\n", GetLobbyName() );
  185. // If we don't have this lobbyBackend type, we better be properly shutdown for this lobby
  186. assert( GetNumLobbyUsers() == 0 );
  187. assert( host == -1 );
  188. assert( peerIndexOnHost == -1 );
  189. assert( !isHost );
  190. assert( lobbyType != GetActingGameStateLobbyType() || !loaded );
  191. assert( lobbyType != GetActingGameStateLobbyType() || !respondToArbitrate );
  192. assert( snapDeltaAckQueue.Num() == 0 );
  193. // Make sure we don't have old peers connected to this lobby
  194. for ( int p = 0; p < peers.Num(); p++ ) {
  195. assert( peers[p].GetConnectionState() == CONNECTION_FREE );
  196. }
  197. state = STATE_IDLE;
  198. return;
  199. }
  200. NET_VERBOSE_PRINT( "NET: ShutdownLobby (%s)\n", GetLobbyName() );
  201. for ( int p = 0; p < peers.Num(); p++ ) {
  202. if ( peers[p].GetConnectionState() != CONNECTION_FREE ) {
  203. SetPeerConnectionState( p, CONNECTION_FREE, skipGoodbye ); // This will send goodbye's
  204. }
  205. }
  206. // Remove any users that weren't handled in ResetPeers
  207. // (this will happen as a client, because we won't get the reliable msg from the server since we are severing the connection)
  208. for ( int i = 0; i < GetNumLobbyUsers(); i++ ) {
  209. lobbyUser_t * user = GetLobbyUser( i );
  210. UnregisterUser( user );
  211. }
  212. FreeAllUsers();
  213. host = -1;
  214. peerIndexOnHost = -1;
  215. isHost = false;
  216. needToDisplayMigrateMsg = false;
  217. migrationDlg = GDM_INVALID;
  218. partyToken = 0; // Reset our party token so we recompute
  219. loaded = false;
  220. respondToArbitrate = false;
  221. waitForPartyOk = false;
  222. startLoadingFromHost = false;
  223. snapDeltaAckQueue.Clear();
  224. // Shutdown the lobbyBackend
  225. if ( !retainMigrationInfo ) {
  226. sessionCB->DestroyLobbyBackend( lobbyBackend );
  227. lobbyBackend = NULL;
  228. }
  229. state = STATE_IDLE;
  230. }
  231. /*
  232. ========================
  233. idLobby::HandlePacket
  234. ========================
  235. */
  236. void idLobby::HandlePacket( lobbyAddress_t & remoteAddress, idBitMsg fragMsg, idPacketProcessor::sessionId_t sessionID ) {
  237. SCOPED_PROFILE_EVENT( "HandlePacket" );
  238. // msg will hold a fully constructed msg using the packet processor
  239. byte msgBuffer[ idPacketProcessor::MAX_MSG_SIZE ];
  240. idBitMsg msg;
  241. msg.InitWrite( msgBuffer, sizeof( msgBuffer ) );
  242. int peerNum = FindPeer( remoteAddress, sessionID );
  243. int type = idPacketProcessor::RETURN_TYPE_NONE;
  244. int userData = 0;
  245. if ( peerNum >= 0 ) {
  246. if ( !peers[peerNum].IsActive() ) {
  247. idLib::Printf( "NET: Received in-band packet from peer %s with no active connection.\n", remoteAddress.ToString() );
  248. return;
  249. }
  250. type = peers[ peerNum ].packetProc->ProcessIncoming( Sys_Milliseconds(), peers[peerNum].sessionID, fragMsg, msg, userData, peerNum );
  251. } else {
  252. if ( !idPacketProcessor::ProcessConnectionlessIncoming( fragMsg, msg, userData ) ) {
  253. idLib::Printf( "ProcessConnectionlessIncoming FAILED from %s.\n", remoteAddress.ToString() );
  254. // Not a valid connectionless packet
  255. return;
  256. }
  257. // Valid connectionless packets are always RETURN_TYPE_OOB
  258. type = idPacketProcessor::RETURN_TYPE_OOB;
  259. // Find the peer this connectionless msg should go to
  260. peerNum = FindPeer( remoteAddress, sessionID, true );
  261. }
  262. if ( type == idPacketProcessor::RETURN_TYPE_NONE ) {
  263. // This packet is not necessarily invalid, it could be a start or middle of a fragmented packet that's not fully constructed.
  264. return;
  265. }
  266. if ( peerNum >= 0 ) {
  267. // Update their heart beat (only if we've received a valid packet (we've checked type == idPacketProcessor::RETURN_TYPE_NONE))
  268. peers[peerNum].lastHeartBeat = Sys_Milliseconds();
  269. }
  270. // Handle server query requests. We do this before the STATE_IDLE check. This is so we respond.
  271. // We may want to change this to just ignore the request if we are idle, and change the timeout time
  272. // on the requesters part to just timeout faster.
  273. if ( type == idPacketProcessor::RETURN_TYPE_OOB ) {
  274. if ( userData == OOB_MATCH_QUERY || userData == OOB_SYSTEMLINK_QUERY ) {
  275. sessionCB->HandleServerQueryRequest( remoteAddress, msg, userData );
  276. return;
  277. }
  278. if ( userData == OOB_MATCH_QUERY_ACK ) {
  279. sessionCB->HandleServerQueryAck( remoteAddress, msg );
  280. return;
  281. }
  282. }
  283. if ( type == idPacketProcessor::RETURN_TYPE_OOB ) {
  284. if ( userData == OOB_VOICE_AUDIO ) {
  285. sessionCB->HandleOobVoiceAudio( remoteAddress, msg );
  286. } else if ( userData == OOB_HELLO ) {
  287. // Handle new peer connect request
  288. peerNum = HandleInitialPeerConnection( msg, remoteAddress, peerNum );
  289. return;
  290. } else if ( userData == OOB_MIGRATE_INVITE ) {
  291. NET_VERBOSE_PRINT( "NET: Migration invite for session %s from %s (state = %s)\n", GetLobbyName(), remoteAddress.ToString(), session->GetStateString() );
  292. // Get connection info
  293. lobbyConnectInfo_t connectInfo;
  294. connectInfo.ReadFromMsg( msg );
  295. if ( lobbyBackend != NULL && lobbyBackend->GetState() != idLobbyBackend::STATE_FAILED && lobbyBackend->IsOwnerOfConnectInfo( connectInfo ) ) { // Ignore duplicate invites
  296. idLib::Printf( "NET: Already migrated to %s.\n", remoteAddress.ToString() );
  297. return;
  298. }
  299. if ( migrationInfo.state == MIGRATE_NONE ) {
  300. if ( IsPeer() && host >= 0 && host < peers.Num() && Sys_Milliseconds() - peers[host].lastHeartBeat > 8 * 1000 ) {
  301. // Force migration early if we get an invite, and it has been some time since we've heard from the host
  302. PickNewHost();
  303. } else {
  304. idLib::Printf( "NET: Ignoring migration invite because we are not migrating %s\n", remoteAddress.ToString() );
  305. SendGoodbye( remoteAddress ); // So they can remove us from their invite list
  306. return;
  307. }
  308. }
  309. if ( !sessionCB->PreMigrateInvite( *this ) ) {
  310. NET_VERBOSE_PRINT( "NET: sessionCB->PreMigrateInvite( *this ) failed from %s\n", remoteAddress.ToString() );
  311. return;
  312. }
  313. // If we are also becoming a new host, see who wins
  314. if ( migrationInfo.state == MIGRATE_BECOMING_HOST ) {
  315. int inviteIndex = FindMigrationInviteIndex( remoteAddress );
  316. if ( inviteIndex != -1 ) {
  317. // We found them in our list, check to make sure our ping is better
  318. int ping1 = migrationInfo.ourPingMs;
  319. lobbyUserID_t userId1 = migrationInfo.ourUserId;
  320. int ping2 = migrationInfo.invites[inviteIndex].pingMs;
  321. lobbyUserID_t userId2 = migrationInfo.invites[inviteIndex].userId;
  322. if ( IsBetterHost( ping1, userId1, ping2, userId2 ) ) {
  323. idLib::Printf( "NET: Ignoring migration invite from %s, since our ping is better (%i / %i).\n", remoteAddress.ToString(), ping1, ping2 );
  324. return;
  325. }
  326. }
  327. }
  328. bool fromGame = msg.ReadBool();
  329. // Kill the current lobbyBackend
  330. Shutdown();
  331. // Connect to the lobby
  332. ConnectTo( connectInfo, true ); // Pass in true for the invite flag, so we can connect to invite only lobby if we need to
  333. if ( verify( sessionCB != NULL ) ) {
  334. if ( sessionCB->BecomingPeer( *this ) ) {
  335. migrationInfo.persistUntilGameEndsData.wasMigratedJoin = true;
  336. migrationInfo.persistUntilGameEndsData.wasMigratedGame = fromGame;
  337. }
  338. }
  339. } else if ( userData == OOB_GOODBYE || userData == OOB_GOODBYE_W_PARTY || userData == OOB_GOODBYE_FULL ) {
  340. HandleGoodbyeFromPeer( peerNum, remoteAddress, userData );
  341. return;
  342. } else if ( userData == OOB_RESOURCE_LIST ) {
  343. if ( !verify( lobbyType == GetActingGameStateLobbyType() ) ) {
  344. return;
  345. }
  346. if ( peerNum != host ) {
  347. NET_VERBOSE_PRINT( "NET: Resource list from non-host %i, %s\n", peerNum, remoteAddress.ToString() );
  348. return;
  349. }
  350. if ( peerNum >= 0 && !peers[peerNum].IsConnected() ) {
  351. NET_VERBOSE_PRINT( "NET: Resource list from host with no game connection: %i, %s\n", peerNum, remoteAddress.ToString() );
  352. return;
  353. }
  354. } else if ( userData == OOB_BANDWIDTH_TEST ) {
  355. int seqNum = msg.ReadLong();
  356. // TODO: We should read the random data and verify the MD5 checksum
  357. int time = Sys_Milliseconds();
  358. bool inOrder = ( seqNum == 0 || peers[peerNum].bandwidthSequenceNum + 1 == seqNum );
  359. int timeSinceLast = 0;
  360. if ( bandwidthChallengeStartTime <= 0 ) {
  361. // Reset the test
  362. NET_VERBOSE_PRINT( "\nNET: Starting bandwidth test @ %d\n", time );
  363. bandwidthChallengeStartTime = time;
  364. peers[peerNum].bandwidthSequenceNum = 0;
  365. peers[peerNum].bandwidthTestBytes = peers[peerNum].packetProc->GetIncomingBytes();
  366. } else {
  367. timeSinceLast = time - (bandwidthChallengeEndTime - session->GetTitleStorageInt( "net_bw_test_timeout", net_bw_test_timeout.GetInteger() ) );
  368. }
  369. if ( inOrder ) {
  370. bandwidthChallengeNumGoodSeq++;
  371. }
  372. bandwidthChallengeEndTime = time + session->GetTitleStorageInt( "net_bw_test_timeout", net_bw_test_timeout.GetInteger() );
  373. NET_VERBOSE_PRINT( " NET: %sRecevied OOB bandwidth test %d delta time: %d incoming rate: %.2f incoming rate 2: %d\n", inOrder ? "^2" : "^1", seqNum, timeSinceLast, peers[peerNum].packetProc->GetIncomingRateBytes(), peers[peerNum].packetProc->GetIncomingRate2() );
  374. peers[peerNum].bandwidthSequenceNum = seqNum;
  375. } else {
  376. NET_VERBOSE_PRINT( "NET: Unknown oob packet %d from %s (%d)\n", userData, remoteAddress.ToString(), peerNum );
  377. }
  378. } else if ( type == idPacketProcessor::RETURN_TYPE_INBAND ) {
  379. // Process in-band message
  380. if ( peerNum < 0 ) {
  381. idLib::Printf( "NET: In-band message from unknown peer: %s\n", remoteAddress.ToString() );
  382. return;
  383. }
  384. if ( !verify( peers[ peerNum ].address.Compare( remoteAddress ) ) ) {
  385. idLib::Printf( "NET: Peer with wrong address: %i, %s\n", peerNum, remoteAddress.ToString() );
  386. return;
  387. }
  388. // Handle reliable
  389. int numReliable = peers[ peerNum ].packetProc->GetNumReliables();
  390. for ( int r = 0; r < numReliable; r++ ) {
  391. // Just in case one of the reliable msg's cause this peer to disconnect
  392. // (this can happen when our party/game host is the same, he quits the game lobby, and sends a reliable msg for us to leave the game)
  393. peerNum = FindPeer( remoteAddress, sessionID );
  394. if ( peerNum == -1 ) {
  395. idLib::Printf( "NET: Dropped peer while processing reliable msg's: %i, %s\n", peerNum, remoteAddress.ToString() );
  396. break;
  397. }
  398. const byte * reliableData = peers[ peerNum ].packetProc->GetReliable( r );
  399. int reliableSize = peers[ peerNum ].packetProc->GetReliableSize( r );
  400. idBitMsg reliableMsg( reliableData, reliableSize );
  401. reliableMsg.SetSize( reliableSize );
  402. HandleReliableMsg( peerNum, reliableMsg );
  403. }
  404. if ( peerNum == -1 || !peers[ peerNum ].IsConnected() ) {
  405. // If the peer still has no connection after HandleReliableMsg, then something is wrong.
  406. // (We could have been in CONNECTION_CONNECTING state for this session type, but the first message
  407. // we should receive from the server is the ack, otherwise, something went wrong somewhere)
  408. idLib::Printf( "NET: In-band message from host with no active connection: %i, %s\n", peerNum, remoteAddress.ToString() );
  409. return;
  410. }
  411. // Handle unreliable part (if any)
  412. if ( msg.GetRemainingData() > 0 && loaded ) {
  413. if ( !verify( lobbyType == GetActingGameStateLobbyType() ) ) {
  414. idLib::Printf( "NET: Snapshot msg for non game session lobby %s\n", remoteAddress.ToString() );
  415. return;
  416. }
  417. if ( peerNum == host ) {
  418. idSnapShot localSnap;
  419. int sequence = -1;
  420. int baseseq = -1;
  421. bool fullSnap = false;
  422. localReadSS = &localSnap;
  423. // If we are the peer, we assume we only receive snapshot data on the in-band channel
  424. const byte * deltaData = msg.GetReadData() + msg.GetReadCount();
  425. int deltaLength = msg.GetRemainingData();
  426. if ( peers[ peerNum ].snapProc->ReceiveSnapshotDelta( deltaData, deltaLength, 0, sequence, baseseq, localSnap, fullSnap ) ) {
  427. NET_VERBOSESNAPSHOT_PRINT_LEVEL( 2, va( "NET: Got %s snapshot %d delta'd against %d. SS Time: %d\n", ( fullSnap ? "partial" : "full" ), sequence, baseseq, localSnap.GetTime() ) );
  428. if ( sessionCB->GetState() != idSession::INGAME && sequence != -1 ) {
  429. int seq = peers[ peerNum ].snapProc->GetLastAppendedSequence();
  430. // When we aren't in the game, we need to send this as reliable msg's, since usercmds won't be taking care of it for us
  431. byte ackbuffer[32];
  432. idBitMsg ackmsg( ackbuffer, sizeof( ackbuffer ) );
  433. ackmsg.WriteLong( seq );
  434. // Add incoming BPS for QoS
  435. float incomingBPS = peers[ peerNum ].receivedBps;
  436. if ( peers[ peerNum ].receivedBpsIndex != seq ) {
  437. incomingBPS = idMath::ClampFloat( 0.0f, static_cast<float>( idLobby::BANDWIDTH_REPORTING_MAX ), peers[host].packetProc->GetIncomingRateBytes() );
  438. peers[ peerNum ].receivedBpsIndex = seq;
  439. peers[ peerNum ].receivedBps = incomingBPS;
  440. }
  441. ackmsg.WriteQuantizedUFloat< idLobby::BANDWIDTH_REPORTING_MAX, idLobby::BANDWIDTH_REPORTING_BITS >( incomingBPS );
  442. QueueReliableMessage( host, RELIABLE_SNAPSHOT_ACK, ackbuffer, sizeof( ackbuffer ) );
  443. }
  444. }
  445. if ( fullSnap ) {
  446. sessionCB->ReceivedFullSnap();
  447. common->NetReceiveSnapshot( localSnap );
  448. }
  449. localReadSS = NULL;
  450. } else {
  451. // If we are the host, we assume we only receive usercmds on the inband channel
  452. int snapNum = 0;
  453. uint16 receivedBps_quantized = 0;
  454. byte usercmdBuffer[idPacketProcessor::MAX_FINAL_PACKET_SIZE];
  455. lzwCompressionData_t lzwData;
  456. idLZWCompressor lzwCompressor( &lzwData );
  457. lzwCompressor.Start( const_cast<byte *>( msg.GetReadData() ) + msg.GetReadCount(), msg.GetRemainingData() );
  458. lzwCompressor.ReadAgnostic( snapNum );
  459. lzwCompressor.ReadAgnostic( receivedBps_quantized );
  460. int usercmdSize = lzwCompressor.Read( usercmdBuffer, sizeof( usercmdBuffer ), true );
  461. lzwCompressor.End();
  462. float receivedBps = ( receivedBps_quantized / (float)( BIT( idLobby::BANDWIDTH_REPORTING_BITS ) - 1 ) ) * (float)idLobby::BANDWIDTH_REPORTING_MAX;
  463. if ( peers[ peerNum ].receivedBpsIndex != snapNum ) {
  464. peers[ peerNum ].receivedBps = receivedBps;
  465. peers[ peerNum ].receivedBpsIndex = snapNum;
  466. }
  467. if ( snapNum < 50 ) {
  468. NET_VERBOSE_PRINT( "NET: peer %d ack'd snapNum %d\n", peerNum, snapNum );
  469. }
  470. ApplySnapshotDelta( peerNum, snapNum );
  471. idBitMsg usercmdMsg( (const byte *)usercmdBuffer, usercmdSize );
  472. common->NetReceiveUsercmds( peerNum, usercmdMsg );
  473. }
  474. }
  475. }
  476. }
  477. /*
  478. ========================
  479. idLobby::HasActivePeers
  480. ========================
  481. */
  482. bool idLobby::HasActivePeers() const {
  483. for ( int p = 0; p < peers.Num(); p++ ) {
  484. if ( peers[p].GetConnectionState() != CONNECTION_FREE ) {
  485. return true;
  486. }
  487. }
  488. return false;
  489. }
  490. /*
  491. ========================
  492. idLobby::NumFreeSlots
  493. ========================
  494. */
  495. int idLobby::NumFreeSlots() const {
  496. if ( parms.matchFlags & MATCH_JOIN_IN_PROGRESS ) {
  497. return parms.numSlots - GetNumConnectedUsers();
  498. } else {
  499. return parms.numSlots - GetNumLobbyUsers();
  500. }
  501. }
  502. //===============================================================================
  503. // ** END PUBLIC INTERFACE ***
  504. //===============================================================================
  505. //===============================================================================
  506. // ** BEGIN STATE CODE ***
  507. //===============================================================================
  508. const char * idLobby::stateToString[ NUM_STATES ] = {
  509. ASSERT_ENUM_STRING( STATE_IDLE, 0 ),
  510. ASSERT_ENUM_STRING( STATE_CREATE_LOBBY_BACKEND, 1 ),
  511. ASSERT_ENUM_STRING( STATE_SEARCHING, 2 ),
  512. ASSERT_ENUM_STRING( STATE_OBTAINING_ADDRESS, 3 ),
  513. ASSERT_ENUM_STRING( STATE_CONNECT_HELLO_WAIT, 4 ),
  514. ASSERT_ENUM_STRING( STATE_FINALIZE_CONNECT, 5 ),
  515. ASSERT_ENUM_STRING( STATE_FAILED, 6 ),
  516. };
  517. /*
  518. ========================
  519. idLobby::State_Idle
  520. ========================
  521. */
  522. void idLobby::State_Idle() {
  523. // If lobbyBackend is in a failed state, shutdown, go to a failed state ourself, and return
  524. if ( lobbyBackend != NULL && lobbyBackend->GetState() == idLobbyBackend::STATE_FAILED ) {
  525. HandleConnectionAttemptFailed();
  526. common->Dialog().ClearDialog( GDM_MIGRATING );
  527. common->Dialog().ClearDialog( GDM_MIGRATING_WAITING );
  528. common->Dialog().ClearDialog( GDM_MIGRATING_RELAUNCHING );
  529. return;
  530. }
  531. if ( migrationInfo.persistUntilGameEndsData.hasGameData && sessionCB->GetState() <= idSession::IDLE ) {
  532. // This can happen with 'leaveGame' or 'disconnect' since those paths don't go through endMatch
  533. // This seems like an ok catch all place but there may be a better way to handle this
  534. ResetAllMigrationState();
  535. common->Dialog().ClearDialog( GDM_MIGRATING );
  536. common->Dialog().ClearDialog( GDM_MIGRATING_WAITING );
  537. common->Dialog().ClearDialog( GDM_MIGRATING_RELAUNCHING );
  538. }
  539. }
  540. /*
  541. ========================
  542. idLobby::State_Create_Lobby_Backend
  543. ========================
  544. */
  545. void idLobby::State_Create_Lobby_Backend() {
  546. if ( !verify( lobbyBackend != NULL ) ) {
  547. SetState( STATE_FAILED );
  548. return;
  549. }
  550. assert( lobbyBackend != NULL );
  551. if ( migrationInfo.state == MIGRATE_BECOMING_HOST ) {
  552. const int DETECT_SERVICE_DISCONNECT_TIMEOUT_IN_SECONDS = session->GetTitleStorageInt( "DETECT_SERVICE_DISCONNECT_TIMEOUT_IN_SECONDS", 30 );
  553. // If we are taking too long, cancel the connection
  554. if ( DETECT_SERVICE_DISCONNECT_TIMEOUT_IN_SECONDS > 0 ) {
  555. if ( Sys_Milliseconds() - migrationInfo.migrationStartTime > 1000 * DETECT_SERVICE_DISCONNECT_TIMEOUT_IN_SECONDS ) {
  556. SetState( STATE_FAILED );
  557. return;
  558. }
  559. }
  560. }
  561. if ( lobbyBackend->GetState() == idLobbyBackend::STATE_CREATING ) {
  562. return; // Busy but valid
  563. }
  564. if ( lobbyBackend->GetState() != idLobbyBackend::STATE_READY ) {
  565. SetState( STATE_FAILED );
  566. return;
  567. }
  568. // Success
  569. InitStateLobbyHost();
  570. // Set state to idle to signify to session we are done creating
  571. SetState( STATE_IDLE );
  572. }
  573. /*
  574. ========================
  575. idLobby::State_Searching
  576. ========================
  577. */
  578. void idLobby::State_Searching() {
  579. if ( !verify( lobbyBackend != NULL ) ) {
  580. SetState( STATE_FAILED );
  581. return;
  582. }
  583. if ( lobbyBackend->GetState() == idLobbyBackend::STATE_SEARCHING ) {
  584. return; // Busy but valid
  585. }
  586. if ( lobbyBackend->GetState() != idLobbyBackend::STATE_READY ) {
  587. SetState( STATE_FAILED ); // Any other lobbyBackend state is invalid
  588. return;
  589. }
  590. // Done searching, get results from lobbyBackend
  591. lobbyBackend->GetSearchResults( searchResults );
  592. if ( searchResults.Num() == 0 ) {
  593. // If we didn't get any results, set state to failed
  594. SetState( STATE_FAILED );
  595. return;
  596. }
  597. extern idCVar net_maxSearchResultsToTry;
  598. const int maxSearchResultsToTry = session->GetTitleStorageInt( "net_maxSearchResultsToTry", net_maxSearchResultsToTry.GetInteger() );
  599. if ( searchResults.Num() > maxSearchResultsToTry ) {
  600. searchResults.SetNum( maxSearchResultsToTry );
  601. }
  602. // Set state to idle to signify we are done searching
  603. SetState( STATE_IDLE );
  604. }
  605. /*
  606. ========================
  607. idLobby::State_Obtaining_Address
  608. ========================
  609. */
  610. void idLobby::State_Obtaining_Address() {
  611. if ( lobbyBackend->GetState() == idLobbyBackend::STATE_OBTAINING_ADDRESS ) {
  612. return; // Valid but not ready
  613. }
  614. if ( lobbyBackend->GetState() != idLobbyBackend::STATE_READY ) {
  615. // There was an error, signify to caller
  616. failedReason = migrationInfo.persistUntilGameEndsData.wasMigratedJoin ? FAILED_MIGRATION_CONNECT_FAILED : FAILED_CONNECT_FAILED;
  617. NET_VERBOSE_PRINT("idLobby::State_Obtaining_Address: the lobby backend failed." );
  618. SetState( STATE_FAILED );
  619. return;
  620. }
  621. //
  622. // We have the address of the lobbyBackend, we can now send a hello packet
  623. //
  624. // This will be the host for this lobby type
  625. host = AddPeer( hostAddress, GenerateSessionID() );
  626. // Record start time of connection attempt to the host
  627. helloStartTime = Sys_Milliseconds();
  628. lastConnectRequest = helloStartTime;
  629. connectionAttempts = 0;
  630. // Change state to connecting
  631. SetState( STATE_CONNECT_HELLO_WAIT );
  632. // Send first connect attempt now (we'll send more periodically if we fail to receive an ack)
  633. // (we do this after changing state, since the function expects we're in the right state)
  634. SendConnectionRequest();
  635. }
  636. /*
  637. ========================
  638. idLobby::State_Finalize_Connect
  639. ========================
  640. */
  641. void idLobby::State_Finalize_Connect() {
  642. if ( lobbyBackend->GetState() == idLobbyBackend::STATE_CREATING ) {
  643. // Valid but busy
  644. return;
  645. }
  646. if ( lobbyBackend->GetState() != idLobbyBackend::STATE_READY ) {
  647. // Any other state not valid, failed
  648. SetState( STATE_FAILED );
  649. return;
  650. }
  651. // Success
  652. SetState( STATE_IDLE );
  653. // Tell session mgr if this was a migration
  654. if ( migrationInfo.persistUntilGameEndsData.wasMigratedJoin ) {
  655. sessionCB->BecamePeer( *this );
  656. }
  657. }
  658. /*
  659. ========================
  660. idLobby::State_Connect_Hello_Wait
  661. ========================
  662. */
  663. void idLobby::State_Connect_Hello_Wait() {
  664. if ( lobbyBackend->GetState() != idLobbyBackend::STATE_READY ) {
  665. // If the lobbyBackend is in an error state, shut everything down
  666. NET_VERBOSE_PRINT( "NET: Lobby is no longer ready while waiting for lobbyType %s hello.\n", GetLobbyName() );
  667. HandleConnectionAttemptFailed();
  668. return;
  669. }
  670. int time = Sys_Milliseconds();
  671. const int timeoutMs = session->GetTitleStorageInt( "net_connectTimeoutInSeconds", net_connectTimeoutInSeconds.GetInteger() ) * 1000;
  672. if ( timeoutMs != 0 && time - helloStartTime > timeoutMs ) {
  673. NET_VERBOSE_PRINT( "NET: Timeout waiting for lobbyType %s for party hello.\n", GetLobbyName() );
  674. HandleConnectionAttemptFailed();
  675. return;
  676. }
  677. if ( connectionAttempts < MAX_CONNECT_ATTEMPTS ) {
  678. assert( connectionAttempts >= 1 ); // Should have at least the initial connection attempt
  679. // See if we need to send another hello request
  680. // (keep getting more frequent to increase chance due to possible packet loss, but clamp to MIN_CONNECT_FREQUENCY seconds)
  681. // TODO: We could eventually make timing out a function of actual number of attempts rather than just plain time.
  682. int resendTime = Max( MIN_CONNECT_FREQUENCY_IN_SECONDS, CONNECT_REQUEST_FREQUENCY_IN_SECONDS / connectionAttempts ) * 1000;
  683. if ( time - lastConnectRequest > resendTime ) {
  684. SendConnectionRequest();
  685. lastConnectRequest = time;
  686. }
  687. }
  688. }
  689. /*
  690. ========================
  691. idLobby::SetState
  692. ========================
  693. */
  694. void idLobby::SetState( lobbyState_t newState ) {
  695. assert( newState < NUM_STATES );
  696. assert( state < NUM_STATES );
  697. verify_array_size( stateToString, NUM_STATES );
  698. if ( state == newState ) {
  699. NET_VERBOSE_PRINT( "NET: idLobby::SetState: State SAME %s for session %s\n", stateToString[ newState ], GetLobbyName() );
  700. return;
  701. }
  702. // Set the current state
  703. NET_VERBOSE_PRINT( "NET: idLobby::SetState: State changing from %s to %s for session %s\n", stateToString[ state ], stateToString[ newState ], GetLobbyName() );
  704. state = newState;
  705. }
  706. //===============================================================================
  707. // ** END STATE CODE ***
  708. //===============================================================================
  709. /*
  710. ========================
  711. idLobby::StartCreating
  712. ========================
  713. */
  714. void idLobby::StartCreating() {
  715. assert( lobbyBackend == NULL );
  716. assert( state == STATE_IDLE );
  717. float skillLevel = GetAverageLocalUserLevel( true );
  718. lobbyBackend = sessionCB->CreateLobbyBackend( parms, skillLevel, (idLobbyBackend::lobbyBackendType_t)lobbyType );
  719. SetState( STATE_CREATE_LOBBY_BACKEND );
  720. }
  721. /*
  722. ========================
  723. idLobby::FindPeer
  724. ========================
  725. */
  726. int idLobby::FindPeer( const lobbyAddress_t & remoteAddress, idPacketProcessor::sessionId_t sessionID, bool ignoreSessionID ) {
  727. bool connectionless = ( sessionID == idPacketProcessor::SESSION_ID_CONNECTIONLESS_PARTY ||
  728. sessionID == idPacketProcessor::SESSION_ID_CONNECTIONLESS_GAME ||
  729. sessionID == idPacketProcessor::SESSION_ID_CONNECTIONLESS_GAME_STATE );
  730. if ( connectionless && !ignoreSessionID ) {
  731. return -1; // This was meant to be connectionless. FindPeer is meant for connected (or connecting) peers
  732. }
  733. for ( int p = 0; p < peers.Num(); p++ ) {
  734. if ( peers[p].GetConnectionState() == CONNECTION_FREE ) {
  735. continue;
  736. }
  737. if ( peers[p].address.Compare( remoteAddress ) ) {
  738. if ( connectionless && ignoreSessionID ) {
  739. return p;
  740. }
  741. // Using a rolling check, so that we account for possible packet loss, and out of order issues
  742. if ( IsPeer() ) {
  743. idPacketProcessor::sessionId_t searchStart = peers[p].sessionID;
  744. // Since we only roll the code between matches, we should only need to look ahead a couple increments.
  745. // Worse case, if the stars line up, the client doesn't see the new sessionId, and times out, and gets booted.
  746. // This should be impossible though, since the timings won't be possible considering how long it takes to end the match,
  747. // and restart, and then restart again.
  748. int numTries = 2;
  749. while ( numTries-- > 0 && searchStart != sessionID ) {
  750. searchStart = IncrementSessionID( searchStart );
  751. if ( searchStart == sessionID ) {
  752. idLib::Printf( "NET: Rolling session ID check found new ID: %i\n", searchStart );
  753. if ( peers[p].packetProc != NULL ) {
  754. peers[p].packetProc->VerifyEmptyReliableQueue( RELIABLE_GAME_DATA, RELIABLE_DUMMY_MSG );
  755. }
  756. peers[p].sessionID = searchStart;
  757. break;
  758. }
  759. }
  760. }
  761. if ( peers[p].sessionID != sessionID ) {
  762. continue;
  763. }
  764. return p;
  765. }
  766. }
  767. return -1;
  768. }
  769. /*
  770. ========================
  771. idLobby::FindAnyPeer
  772. Find a peer when we don't know the session id, and we don't care since it's a connectionless msg
  773. ========================
  774. */
  775. int idLobby::FindAnyPeer( const lobbyAddress_t & remoteAddress ) const {
  776. for ( int p = 0; p < peers.Num(); p++ ) {
  777. if ( peers[p].GetConnectionState() == CONNECTION_FREE ) {
  778. continue;
  779. }
  780. if ( peers[p].address.Compare( remoteAddress ) ) {
  781. return p;
  782. }
  783. }
  784. return -1;
  785. }
  786. /*
  787. ========================
  788. idLobby::FindFreePeer
  789. ========================
  790. */
  791. int idLobby::FindFreePeer() const {
  792. // Return the first non active peer
  793. for ( int p = 0; p < peers.Num(); p++ ) {
  794. if ( !peers[p].IsActive() ) {
  795. return p;
  796. }
  797. }
  798. return -1;
  799. }
  800. /*
  801. ========================
  802. idLobby::AddPeer
  803. ========================
  804. */
  805. int idLobby::AddPeer( const lobbyAddress_t & remoteAddress, idPacketProcessor::sessionId_t sessionID ) {
  806. // First, make sure we don't already have this peer
  807. int p = FindPeer( remoteAddress, sessionID );
  808. assert( p == -1 ); // When using session ID's, we SHOULDN'T find this remoteAddress/sessionID combo
  809. if ( p == -1 ) {
  810. // If we didn't find the peer, we need to add a new one
  811. p = FindFreePeer();
  812. if ( p == -1 ) {
  813. peer_t newPeer;
  814. p = peers.Append( newPeer );
  815. }
  816. peer_t & peer = peers[p];
  817. peer.ResetAllData();
  818. assert( peer.connectionState == CONNECTION_FREE );
  819. peer.address = remoteAddress;
  820. peer.sessionID = sessionID;
  821. NET_VERBOSE_PRINT( "NET: Added peer %s at index %i\n", remoteAddress.ToString(), p );
  822. } else {
  823. NET_VERBOSE_PRINT( "NET: Found peer %s at index %i\n", remoteAddress.ToString(), p );
  824. }
  825. SetPeerConnectionState( p, CONNECTION_CONNECTING );
  826. if ( lobbyType == GetActingGameStateLobbyType() ) {
  827. // Reset various flags used in game mode
  828. peers[p].ResetMatchData();
  829. }
  830. return p;
  831. }
  832. /*
  833. ========================
  834. idLobby::DisconnectPeerFromSession
  835. ========================
  836. */
  837. void idLobby::DisconnectPeerFromSession( int p ) {
  838. if ( !verify( IsHost() ) ) {
  839. return;
  840. }
  841. peer_t & peer = peers[p];
  842. if ( peer.GetConnectionState() != CONNECTION_FREE ) {
  843. SetPeerConnectionState( p, CONNECTION_FREE );
  844. }
  845. }
  846. /*
  847. ========================
  848. idLobby::DisconnectAllPeers
  849. ========================
  850. */
  851. void idLobby::DisconnectAllPeers() {
  852. for ( int p = 0; p < peers.Num(); p++ ) {
  853. DisconnectPeerFromSession( p );
  854. }
  855. }
  856. /*
  857. ========================
  858. idLobby::SendGoodbye
  859. ========================
  860. */
  861. void idLobby::SendGoodbye( const lobbyAddress_t & remoteAddress, bool wasFull ) {
  862. if ( net_skipGoodbye.GetBool() ) {
  863. return;
  864. }
  865. NET_VERBOSE_PRINT( "NET: Sending goodbye to %s for %s (wasFull = %i)\n", remoteAddress.ToString(), GetLobbyName(), wasFull );
  866. static const int NUM_REDUNDANT_GOODBYES = 10;
  867. int msgType = OOB_GOODBYE;
  868. if ( wasFull ) {
  869. msgType = OOB_GOODBYE_FULL;
  870. } else if ( lobbyType == TYPE_GAME && ( sessionCB->GetSessionOptions() & idSession::OPTION_LEAVE_WITH_PARTY ) && !( parms.matchFlags & MATCH_PARTY_INVITE_PLACEHOLDER ) ) {
  871. msgType = OOB_GOODBYE_W_PARTY;
  872. }
  873. for ( int i = 0; i < NUM_REDUNDANT_GOODBYES; i++ ) {
  874. SendConnectionLess( remoteAddress, msgType );
  875. }
  876. }
  877. /*
  878. ========================
  879. idLobby::SetPeerConnectionState
  880. ========================
  881. */
  882. void idLobby::SetPeerConnectionState( int p, connectionState_t newState, bool skipGoodbye ) {
  883. if ( !verify( p >= 0 && p < peers.Num() ) ) {
  884. idLib::Printf( "NET: SetPeerConnectionState invalid peer index %i\n", p );
  885. return;
  886. }
  887. peer_t & peer = peers[p];
  888. const lobbyType_t actingGameStateLobbyType = GetActingGameStateLobbyType();
  889. if ( peer.GetConnectionState() == newState ) {
  890. idLib::Printf( "NET: SetPeerConnectionState: Peer already in state %i\n", newState );
  891. assert( 0 ); // This case means something is most likely bad, and it's the programmers fault
  892. assert( ( peer.packetProc != NULL ) == peer.IsActive() );
  893. assert( ( ( peer.snapProc != NULL ) == peer.IsActive() ) == ( actingGameStateLobbyType == lobbyType ) );
  894. return;
  895. }
  896. if ( newState == CONNECTION_CONNECTING ) {
  897. //mem.PushHeap();
  898. // We better be coming from a free connection state if we are trying to connect
  899. assert( peer.GetConnectionState() == CONNECTION_FREE );
  900. assert( peer.packetProc == NULL );
  901. peer.packetProc = new ( TAG_NETWORKING )idPacketProcessor();
  902. if ( lobbyType == actingGameStateLobbyType ) {
  903. assert( peer.snapProc == NULL );
  904. peer.snapProc = new ( TAG_NETWORKING )idSnapshotProcessor();
  905. }
  906. //mem.PopHeap();
  907. } else if ( newState == CONNECTION_ESTABLISHED ) {
  908. // If we are marking this peer as connected for the first time, make sure this peer was actually trying to connect.
  909. assert( peer.GetConnectionState() == CONNECTION_CONNECTING );
  910. } else if ( newState == CONNECTION_FREE ) {
  911. // If we are freeing this connection and we had an established connection before, make sure to send a goodbye
  912. if ( peer.GetConnectionState() == CONNECTION_ESTABLISHED && !skipGoodbye ) {
  913. idLib::Printf("SetPeerConnectionState: Sending goodbye to peer %s from session %s\n", peer.address.ToString(), GetLobbyName() );
  914. SendGoodbye( peer.address );
  915. }
  916. }
  917. peer.connectionState = newState;
  918. if ( !peer.IsActive() ) {
  919. if ( peer.packetProc != NULL ) {
  920. delete peer.packetProc;
  921. peer.packetProc = NULL;
  922. }
  923. if ( peer.snapProc != NULL ) {
  924. assert( lobbyType == actingGameStateLobbyType );
  925. delete peer.snapProc;
  926. peer.snapProc = NULL;
  927. }
  928. }
  929. // Do this in case we disconnected the peer
  930. if ( IsHost() ) {
  931. RemoveUsersWithDisconnectedPeers();
  932. }
  933. }
  934. /*
  935. ========================
  936. idLobby::QueueReliableMessage
  937. ========================
  938. */
  939. void idLobby::QueueReliableMessage( int p, byte type, const byte * data, int dataLen ) {
  940. if ( !verify( p >= 0 && p < peers.Num() ) ) {
  941. return;
  942. }
  943. peer_t & peer = peers[p];
  944. if ( !peer.IsConnected() ) {
  945. // Don't send to this peer if we don't have an established connection of this session type
  946. NET_VERBOSE_PRINT( "NET: Not sending reliable type %i to peer %i because connectionState is %i\n", type, p, peer.GetConnectionState() );
  947. return;
  948. }
  949. if ( peer.packetProc->NumQueuedReliables() > 2 ) {
  950. idLib::PrintfIf( false, "NET: peer.packetProc->NumQueuedReliables() > 2: %i (%i / %s)\n", peer.packetProc->NumQueuedReliables(), p, peer.address.ToString() );
  951. }
  952. if ( !peer.packetProc->QueueReliableMessage( type, data, dataLen ) ) {
  953. // For now, when this happens, disconnect from all session types
  954. NET_VERBOSE_PRINT( "NET: Dropping peer because we overflowed his reliable message queue\n" );
  955. if ( IsHost() ) {
  956. // Disconnect peer from this session type
  957. DisconnectPeerFromSession( p );
  958. } else {
  959. Shutdown(); // Shutdown session if we can't queue the reliable
  960. }
  961. }
  962. }
  963. /*
  964. ========================
  965. idLobby::GetNumConnectedPeers
  966. ========================
  967. */
  968. int idLobby::GetNumConnectedPeers() const {
  969. int numConnected = 0;
  970. for ( int i = 0; i < peers.Num(); i++ ) {
  971. if ( peers[i].IsConnected() ) {
  972. numConnected++;
  973. }
  974. }
  975. return numConnected;
  976. }
  977. /*
  978. ========================
  979. idLobby::GetNumConnectedPeersInGame
  980. ========================
  981. */
  982. int idLobby::GetNumConnectedPeersInGame() const {
  983. int numActive = 0;
  984. for ( int i = 0; i < peers.Num(); i++ ) {
  985. if ( peers[i].IsConnected() && peers[i].inGame ) {
  986. numActive++;
  987. }
  988. }
  989. return numActive;
  990. }
  991. /*
  992. ========================
  993. idLobby::SendMatchParmsToPeers
  994. ========================
  995. */
  996. void idLobby::SendMatchParmsToPeers() {
  997. if ( !IsHost() ) {
  998. return;
  999. }
  1000. if ( GetNumConnectedPeers() == 0 ) {
  1001. return;
  1002. }
  1003. byte buffer[ idPacketProcessor::MAX_PACKET_SIZE ];
  1004. idBitMsg msg( buffer, sizeof( buffer ) );
  1005. parms.Write( msg );
  1006. for ( int p = 0; p < peers.Num(); p++ ) {
  1007. if ( !peers[p].IsConnected() ) {
  1008. continue;
  1009. }
  1010. QueueReliableMessage( p, RELIABLE_MATCH_PARMS, msg.GetReadData(), msg.GetSize() );
  1011. }
  1012. }
  1013. /*
  1014. ========================
  1015. STATIC idLobby::IsReliablePlayerToPlayerType
  1016. ========================
  1017. */
  1018. bool idLobby::IsReliablePlayerToPlayerType( byte type ) {
  1019. return ( type >= RELIABLE_PLAYER_TO_PLAYER_BEGIN ) && ( type < RELIABLE_PLAYER_TO_PLAYER_END );
  1020. }
  1021. /*
  1022. ========================
  1023. idLobby::HandleReliablePlayerToPlayerMsg
  1024. ========================
  1025. */
  1026. void idLobby::HandleReliablePlayerToPlayerMsg( int peerNum, idBitMsg & msg, int type ) {
  1027. reliablePlayerToPlayerHeader_t info;
  1028. int c, b;
  1029. msg.SaveReadState( c, b ); // in case we need to forward or fail
  1030. if ( !info.Read( this, msg ) ) {
  1031. idLib::Warning( "NET: Ignoring invalid reliable player to player message" );
  1032. msg.RestoreReadState( c, b );
  1033. return;
  1034. }
  1035. const bool isForLocalPlayer = IsSessionUserIndexLocal( info.toSessionUserIndex );
  1036. if ( isForLocalPlayer ) {
  1037. HandleReliablePlayerToPlayerMsg( info, msg, type );
  1038. } else if ( IsHost() ) {
  1039. const int targetPeer = PeerIndexForSessionUserIndex( info.toSessionUserIndex );
  1040. msg.RestoreReadState( c, b );
  1041. // forward the rest of the data
  1042. const byte * data = msg.GetReadData() + msg.GetReadCount();
  1043. int dataLen = msg.GetSize() - msg.GetReadCount();
  1044. QueueReliableMessage( targetPeer, type, data, dataLen );
  1045. } else {
  1046. idLib::Warning( "NET: Can't forward reliable message for remote player: I'm not the host" );
  1047. }
  1048. }
  1049. /*
  1050. ========================
  1051. idLobby::HandleReliablePlayerToPlayerMsg
  1052. ========================
  1053. */
  1054. void idLobby::HandleReliablePlayerToPlayerMsg( const reliablePlayerToPlayerHeader_t & info, idBitMsg & msg, int reliableType ) {
  1055. #if 0
  1056. // Remember that the reliablePlayerToPlayerHeader_t was already removed from the msg
  1057. reliablePlayerToPlayer_t type = (reliablePlayerToPlayer_t)( reliableType - RELIABLE_PLAYER_TO_PLAYER_BEGIN );
  1058. switch( type ) {
  1059. case RELIABLE_PLAYER_TO_PLAYER_VOICE_EVENT: {
  1060. sessionCB->HandleReliableVoiceEvent( *this, info.fromSessionUserIndex, info.toSessionUserIndex, msg );
  1061. break;
  1062. }
  1063. default: {
  1064. idLib::Warning( "NET: Ignored unknown player to player reliable type %i", (int) type );
  1065. }
  1066. };
  1067. #endif
  1068. }
  1069. /*
  1070. ========================
  1071. idLobby::SendConnectionLess
  1072. ========================
  1073. */
  1074. void idLobby::SendConnectionLess( const lobbyAddress_t & remoteAddress, byte type, const byte * data, int dataLen ) {
  1075. idBitMsg msg( data, dataLen );
  1076. msg.SetSize( dataLen );
  1077. byte buffer[ idPacketProcessor::MAX_OOB_MSG_SIZE ];
  1078. idBitMsg processedMsg( buffer, sizeof( buffer ) );
  1079. // Process the send
  1080. idPacketProcessor::ProcessConnectionlessOutgoing( msg, processedMsg, lobbyType, type );
  1081. const bool useDirectPort = ( lobbyType == TYPE_GAME_STATE );
  1082. // Send it
  1083. sessionCB->SendRawPacket( remoteAddress, processedMsg.GetReadData(), processedMsg.GetSize(), useDirectPort );
  1084. }
  1085. /*
  1086. ========================
  1087. idLobby::SendConnectionRequest
  1088. ========================
  1089. */
  1090. void idLobby::SendConnectionRequest() {
  1091. // Some sanity checking
  1092. assert( state == STATE_CONNECT_HELLO_WAIT );
  1093. assert( peers[host].GetConnectionState() == CONNECTION_CONNECTING );
  1094. assert( GetNumLobbyUsers() == 0 );
  1095. // Buffer to hold connect msg
  1096. byte buffer[ idPacketProcessor::MAX_PACKET_SIZE - 2 ];
  1097. idBitMsg msg( buffer, sizeof( buffer ) );
  1098. // Add the current version info to the handshake
  1099. const unsigned long localChecksum = NetGetVersionChecksum();
  1100. NET_VERBOSE_PRINT( "NET: version = %i\n", localChecksum );
  1101. msg.WriteLong( localChecksum );
  1102. msg.WriteUShort( peers[host].sessionID );
  1103. msg.WriteBool( connectIsFromInvite );
  1104. // We use InitSessionUsersFromLocalUsers here to copy the current local users over to session users simply to have a list
  1105. // to send on the initial connection attempt. We immediately clear our session user list once sent.
  1106. InitSessionUsersFromLocalUsers( true );
  1107. if ( GetNumLobbyUsers() > 0 ) {
  1108. // Fill up the msg with the users on this machine
  1109. msg.WriteByte( GetNumLobbyUsers() );
  1110. for ( int u = 0; u < GetNumLobbyUsers(); u++ ) {
  1111. GetLobbyUser( u )->WriteToMsg( msg );
  1112. }
  1113. } else {
  1114. FreeAllUsers();
  1115. SetState( STATE_FAILED );
  1116. return;
  1117. }
  1118. // We just used these users to fill up the msg above, we will get the real list from the server if we connect.
  1119. FreeAllUsers();
  1120. NET_VERBOSE_PRINT( "NET: Sending hello to: %s (lobbyType: %s, session ID %i, attempt: %i)\n", hostAddress.ToString(), GetLobbyName(), peers[host].sessionID, connectionAttempts );
  1121. SendConnectionLess( hostAddress, OOB_HELLO, msg.GetReadData(), msg.GetSize() );
  1122. connectionAttempts++;
  1123. }
  1124. /*
  1125. ========================
  1126. idLobby::ConnectTo
  1127. Fires off a request to get the address of a lobbyBackend owner, and then attempts to connect (eventually handled in HandleObtainingLobbyOwnerAddress)
  1128. ========================
  1129. */
  1130. void idLobby::ConnectTo( const lobbyConnectInfo_t & connectInfo, bool fromInvite ) {
  1131. NET_VERBOSE_PRINT( "NET: idSessionLocal::ConnectTo: fromInvite = %i\n", fromInvite );
  1132. // Make sure current session is shutdown
  1133. Shutdown();
  1134. connectIsFromInvite = fromInvite;
  1135. lobbyBackend = sessionCB->JoinFromConnectInfo( connectInfo, (idLobbyBackend::lobbyBackendType_t)lobbyType );
  1136. // First, we need the address of the lobbyBackend owner
  1137. lobbyBackend->GetOwnerAddress( hostAddress );
  1138. SetState( STATE_OBTAINING_ADDRESS );
  1139. }
  1140. /*
  1141. ========================
  1142. idLobby::HandleGoodbyeFromPeer
  1143. ========================
  1144. */
  1145. void idLobby::HandleGoodbyeFromPeer( int peerNum, lobbyAddress_t & remoteAddress, int msgType ) {
  1146. if ( migrationInfo.state != MIGRATE_NONE ) {
  1147. // If this peer is on our invite list, remove them
  1148. for ( int i = 0; i < migrationInfo.invites.Num(); i++ ) {
  1149. if ( migrationInfo.invites[i].address.Compare( remoteAddress, true ) ) {
  1150. migrationInfo.invites.RemoveIndex( i );
  1151. break;
  1152. }
  1153. }
  1154. }
  1155. if ( peerNum < 0 ) {
  1156. NET_VERBOSE_PRINT( "NET: Goodbye from unknown peer %s on session %s\n", remoteAddress.ToString(), GetLobbyName() );
  1157. return;
  1158. }
  1159. if ( peers[peerNum].GetConnectionState() == CONNECTION_FREE ) {
  1160. NET_VERBOSE_PRINT( "NET: Goodbye from peer %s on session %s that is not connected\n", remoteAddress.ToString(), GetLobbyName() );
  1161. return;
  1162. }
  1163. if ( IsHost() ) {
  1164. // Goodbye from peer, remove him
  1165. NET_VERBOSE_PRINT( "NET: Goodbye from peer %s, on session %s\n", remoteAddress.ToString(), GetLobbyName() );
  1166. DisconnectPeerFromSession( peerNum );
  1167. } else {
  1168. // Let session handler take care of this
  1169. NET_VERBOSE_PRINT( "NET: Goodbye from host %s, on session %s\n", remoteAddress.ToString(), GetLobbyName() );
  1170. sessionCB->GoodbyeFromHost( *this, peerNum, remoteAddress, msgType );
  1171. }
  1172. }
  1173. /*
  1174. ========================
  1175. idLobby::HandleGoodbyeFromPeer
  1176. ========================
  1177. */
  1178. void idLobby::HandleConnectionAttemptFailed() {
  1179. Shutdown();
  1180. failedReason = migrationInfo.persistUntilGameEndsData.wasMigratedJoin ? FAILED_MIGRATION_CONNECT_FAILED : FAILED_CONNECT_FAILED;
  1181. SetState( STATE_FAILED );
  1182. if ( migrationInfo.persistUntilGameEndsData.wasMigratedJoin ) {
  1183. sessionCB->FailedGameMigration( *this );
  1184. }
  1185. ResetAllMigrationState();
  1186. needToDisplayMigrateMsg = false;
  1187. migrateMsgFlags = 0;
  1188. }
  1189. /*
  1190. ========================
  1191. idLobby::ConnectToNextSearchResult
  1192. ========================
  1193. */
  1194. bool idLobby::ConnectToNextSearchResult() {
  1195. if ( lobbyType != TYPE_GAME ) {
  1196. return false; // Only game sessions use matchmaking searches
  1197. }
  1198. // End current session lobby (this WON'T free search results)
  1199. Shutdown();
  1200. if ( searchResults.Num() == 0 ) {
  1201. return false; // No more search results to connect to, give up
  1202. }
  1203. // Get next search result
  1204. lobbyConnectInfo_t connectInfo = searchResults[0];
  1205. // Remove this search result
  1206. searchResults.RemoveIndex( 0 );
  1207. // If we are connecting to a game lobby, tell our party to connect to this lobby as well
  1208. if ( lobbyType == TYPE_GAME && sessionCB->GetPartyLobby().IsLobbyActive() ) {
  1209. sessionCB->GetPartyLobby().SendMembersToLobby( lobbyType, connectInfo, true );
  1210. }
  1211. // Attempt to connect the lobby
  1212. ConnectTo( connectInfo, true ); // Pass in true for invite, since searches are for matchmaking, and we should always be able to connect to those types of matches
  1213. // Clear the "Lobby was Full" dialog in case it's up, since we are going to try to connect to a different lobby now
  1214. common->Dialog().ClearDialog( GDM_LOBBY_FULL );
  1215. return true; // Notify caller we are attempting to connect
  1216. }
  1217. /*
  1218. ========================
  1219. idLobby::CheckVersion
  1220. ========================
  1221. */
  1222. bool idLobby::CheckVersion( idBitMsg & msg, lobbyAddress_t peerAddress ) {
  1223. const unsigned long remoteChecksum = msg.ReadLong();
  1224. if ( net_checkVersion.GetInteger() == 1 ) {
  1225. const unsigned long localChecksum = NetGetVersionChecksum();
  1226. NET_VERBOSE_PRINT( "NET: Comparing handshake version - localChecksum = %i, remoteChecksum = %i\n", localChecksum, remoteChecksum );
  1227. return ( remoteChecksum == localChecksum );
  1228. }
  1229. return true;
  1230. }
  1231. /*
  1232. ========================
  1233. idLobby::VerifyNumConnectingUsers
  1234. Make sure number of users connecting is valid, and make sure we have enough room
  1235. ========================
  1236. */
  1237. bool idLobby::VerifyNumConnectingUsers( idBitMsg & msg ) {
  1238. int c, b;
  1239. msg.SaveReadState( c, b );
  1240. const int numUsers = msg.ReadByte();
  1241. msg.RestoreReadState( c, b );
  1242. const int numFreeSlots = NumFreeSlots();
  1243. NET_VERBOSE_PRINT( "NET: VerifyNumConnectingUsers %i users, %i free slots for %s\n", numUsers, numFreeSlots, GetLobbyName() );
  1244. if ( numUsers <= 0 || numUsers > MAX_PLAYERS - 1 ) {
  1245. NET_VERBOSE_PRINT( "NET: Invalid numUsers %i\n", numUsers );
  1246. return false;
  1247. } else if ( numUsers > numFreeSlots ) {
  1248. NET_VERBOSE_PRINT( "NET: %i slots requested, but only %i are available\n", numUsers, numFreeSlots );
  1249. return false;
  1250. } else if ( lobbyType == TYPE_PARTY && sessionCB->GetState() >= idSession::GAME_LOBBY && sessionCB->GetGameLobby().IsLobbyActive() && !IsMigrating() ) {
  1251. const int numFreeGameSlots = sessionCB->GetGameLobby().NumFreeSlots();
  1252. if ( numUsers > numFreeGameSlots ) {
  1253. NET_VERBOSE_PRINT( "NET: %i slots requested, but only %i are available on the active game session\n", numUsers, numFreeGameSlots );
  1254. return false;
  1255. }
  1256. }
  1257. return true;
  1258. }
  1259. /*
  1260. ========================
  1261. idLobby::VerifyLobbyUserIDs
  1262. ========================
  1263. */
  1264. bool idLobby::VerifyLobbyUserIDs( idBitMsg & msg ) {
  1265. int c, b;
  1266. msg.SaveReadState( c, b );
  1267. const int numUsers = msg.ReadByte();
  1268. // Add the new users to our own list
  1269. for ( int u = 0; u < numUsers; u++ ) {
  1270. lobbyUser_t newUser;
  1271. // Read in the new user
  1272. newUser.ReadFromMsg( msg );
  1273. if ( GetLobbyUserIndexByID( newUser.lobbyUserID, true ) != -1 ) {
  1274. msg.RestoreReadState( c, b );
  1275. return false;
  1276. }
  1277. }
  1278. msg.RestoreReadState( c, b );
  1279. return true;
  1280. }
  1281. /*
  1282. ========================
  1283. idLobby::HandleInitialPeerConnection
  1284. Received on an initial peer connect request (OOB_HELLO)
  1285. ========================
  1286. */
  1287. int idLobby::HandleInitialPeerConnection( idBitMsg & msg, const lobbyAddress_t & peerAddress, int peerNum ) {
  1288. if ( net_ignoreConnects.GetInteger() > 0 ) {
  1289. if ( net_ignoreConnects.GetInteger() == 2 ) {
  1290. SendGoodbye( peerAddress );
  1291. }
  1292. return -1;
  1293. }
  1294. if ( !IsHost() ) {
  1295. NET_VERBOSE_PRINT( "NET: Got connectionless hello from peer %s on session, and we are not a host\n", peerAddress.ToString() );
  1296. SendGoodbye( peerAddress );
  1297. return -1;
  1298. }
  1299. // See if this is a peer migrating to us, if so, remove them from our invite list
  1300. bool migrationInvite = false;
  1301. int migrationGameData = -1;
  1302. for ( int i = migrationInfo.invites.Num() - 1; i >= 0; i-- ) {
  1303. if ( migrationInfo.invites[i].address.Compare( peerAddress, true ) ) {
  1304. migrationGameData = migrationInfo.invites[i].migrationGameData;
  1305. migrationInfo.invites.RemoveIndex( i ); // Remove this peer from the list, since this peer will now be connected (or rejected, either way we don't want to keep sending invites)
  1306. migrationInvite = true;
  1307. NET_VERBOSE_PRINT( "^2NET: Response from migration invite %s. GameData: %d\n", peerAddress.ToString(), migrationGameData );
  1308. }
  1309. }
  1310. if ( !MatchTypeIsJoinInProgress( parms.matchFlags ) && lobbyType == TYPE_GAME && migrationInfo.persistUntilGameEndsData.wasMigratedHost && IsMigratedStatsGame() && !migrationInvite ) {
  1311. // No matter what, don't let people join migrated game sessions that are going to continue on to the same game
  1312. // Not on invite list in a migrated game session - bounce him
  1313. NET_VERBOSE_PRINT( "NET: Denying game connection from %s since not on migration invite list\n", peerAddress.ToString() );
  1314. for ( int i = migrationInfo.invites.Num() - 1; i >= 0; i-- ) {
  1315. NET_VERBOSE_PRINT( " Invite[%d] addr: %s\n", i, migrationInfo.invites[i].address.ToString() );
  1316. }
  1317. SendGoodbye( peerAddress );
  1318. return -1;
  1319. }
  1320. if ( MatchTypeIsJoinInProgress( parms.matchFlags ) ) {
  1321. // If this is for a game connection, make sure we have a game lobby
  1322. if ( ( lobbyType == TYPE_GAME || lobbyType == TYPE_GAME_STATE ) && sessionCB->GetState() < idSession::GAME_LOBBY ) {
  1323. NET_VERBOSE_PRINT( "NET: Denying game connection from %s because we don't have a game lobby\n", peerAddress.ToString() );
  1324. SendGoodbye( peerAddress );
  1325. return -1;
  1326. }
  1327. } else {
  1328. // If this is for a game connection, make sure we are in the game lobby
  1329. if ( lobbyType == TYPE_GAME && sessionCB->GetState() != idSession::GAME_LOBBY ) {
  1330. NET_VERBOSE_PRINT( "NET: Denying game connection from %s while not in game lobby\n", peerAddress.ToString() );
  1331. SendGoodbye( peerAddress );
  1332. return -1;
  1333. }
  1334. // If this is for a party connection, make sure we are not in game, unless this was for host migration invite
  1335. if ( !migrationInvite && lobbyType == TYPE_PARTY && ( sessionCB->GetState() == idSession::INGAME || sessionCB->GetState() == idSession::LOADING ) ) {
  1336. NET_VERBOSE_PRINT( "NET: Denying party connection from %s because we were already in a game\n", peerAddress.ToString() );
  1337. SendGoodbye( peerAddress );
  1338. return -1;
  1339. }
  1340. }
  1341. if ( !CheckVersion( msg, peerAddress ) ) {
  1342. idLib::Printf( "NET: Denying user %s with wrong version number\n", peerAddress.ToString() );
  1343. SendGoodbye( peerAddress );
  1344. return -1;
  1345. }
  1346. idPacketProcessor::sessionId_t sessionID = msg.ReadUShort();
  1347. // Check to see if this is a peer trying to connect with a different sessionID
  1348. // If the peer got abruptly disconnected, the peer could be trying to reconnect from a non clean disconnect
  1349. if ( peerNum >= 0 ) {
  1350. peer_t & existingPeer = peers[peerNum];
  1351. assert( existingPeer.GetConnectionState() != CONNECTION_FREE );
  1352. if ( existingPeer.sessionID == sessionID ) {
  1353. return peerNum; // If this is the same sessionID, then assume redundant connection attempt
  1354. }
  1355. //
  1356. // This peer must be trying to reconnect from a previous abrupt disconnect
  1357. //
  1358. NET_VERBOSE_PRINT( "NET: Reconnecting peer %s for session %s\n", peerAddress.ToString(), GetLobbyName() );
  1359. // Assume a peer is trying to reconnect from a non clean disconnect
  1360. // We want to set the connection back to FREE manually, so we don't send a goodbye
  1361. existingPeer.connectionState = CONNECTION_FREE;
  1362. if ( existingPeer.packetProc != NULL ) {
  1363. delete existingPeer.packetProc;
  1364. existingPeer.packetProc = NULL;
  1365. }
  1366. if ( existingPeer.snapProc != NULL ) {
  1367. assert( lobbyType == TYPE_GAME ); // Only games sessions should be creating snap processors
  1368. delete existingPeer.snapProc;
  1369. existingPeer.snapProc = NULL;
  1370. }
  1371. RemoveUsersWithDisconnectedPeers();
  1372. peerNum = -1;
  1373. }
  1374. // See if this was from an invite we sent out. If it wasn't, make sure we aren't invite only
  1375. const bool fromInvite = msg.ReadBool();
  1376. if ( !fromInvite && MatchTypeInviteOnly( parms.matchFlags ) ) {
  1377. idLib::Printf( "NET: Denying user %s because they were not invited to an invite only match\n", peerAddress.ToString() );
  1378. SendGoodbye( peerAddress );
  1379. return -1;
  1380. }
  1381. // Make sure we have room for the users connecting
  1382. if ( !VerifyNumConnectingUsers( msg ) ) {
  1383. NET_VERBOSE_PRINT( "NET: Denying connection from %s in session %s due to being out of user slots\n", peerAddress.ToString(), GetLobbyName() );
  1384. SendGoodbye( peerAddress, true );
  1385. return -1;
  1386. }
  1387. // Make sure there are no lobby id conflicts
  1388. if ( !verify( VerifyLobbyUserIDs( msg ) ) ) {
  1389. NET_VERBOSE_PRINT( "NET: Denying connection from %s in session %s due to lobby id conflict\n", peerAddress.ToString(), GetLobbyName() );
  1390. SendGoodbye( peerAddress, true );
  1391. return -1;
  1392. }
  1393. // Calling AddPeer will set our connectionState to this peer as CONNECTION_CONNECTING (which will get set to CONNECTION_ESTABLISHED below)
  1394. peerNum = AddPeer( peerAddress, sessionID );
  1395. peer_t & newPeer = peers[peerNum];
  1396. assert( newPeer.GetConnectionState() == CONNECTION_CONNECTING );
  1397. assert( lobbyType != GetActingGameStateLobbyType() || newPeer.snapProc != NULL );
  1398. // First, add users from this new peer to our user list
  1399. // (which will then forward the list to all peers except peerNum)
  1400. AddUsersFromMsg( msg, peerNum );
  1401. // Mark the peer as connected for this session type
  1402. SetPeerConnectionState( peerNum, CONNECTION_ESTABLISHED );
  1403. // Update their heart beat to current
  1404. newPeer.lastHeartBeat = Sys_Milliseconds();
  1405. byte buffer[ idPacketProcessor::MAX_PACKET_SIZE ];
  1406. idBitMsg outmsg( buffer, sizeof( buffer ) );
  1407. // Let them know their peer index on this host
  1408. // peerIndexOnHost (put this here so it shows up in search results when finding out where it's used/referenced)
  1409. outmsg.WriteLong( peerNum );
  1410. // If they are connecting to our party lobby, let them know the party token
  1411. if ( lobbyType == TYPE_PARTY ) {
  1412. outmsg.WriteLong( GetPartyTokenAsHost() );
  1413. }
  1414. if ( lobbyType == TYPE_GAME || lobbyType == TYPE_GAME_STATE ) {
  1415. // If this is a game session, reset the loading and ingame flags
  1416. newPeer.loaded = false;
  1417. newPeer.inGame = false;
  1418. }
  1419. // Write out current match parms
  1420. parms.Write( outmsg );
  1421. // Send list of existing users to this new peer
  1422. // (the users from the new peer will also be in this list, since we already called AddUsersFromMsg)
  1423. outmsg.WriteByte( GetNumLobbyUsers() );
  1424. for ( int u = 0; u < GetNumLobbyUsers(); u++ ) {
  1425. GetLobbyUser( u )->WriteToMsg( outmsg );
  1426. }
  1427. lobbyBackend->FillMsgWithPostConnectInfo( outmsg );
  1428. NET_VERBOSE_PRINT( "NET: Sending response to %s, lobbyType %s, sessionID %i\n", peerAddress.ToString(), GetLobbyName(), sessionID );
  1429. QueueReliableMessage( peerNum, RELIABLE_HELLO, outmsg.GetReadData(), outmsg.GetSize() );
  1430. if ( MatchTypeIsJoinInProgress( parms.matchFlags ) ) {
  1431. // If have an active game lobby, and someone joins our party, tell them to join our game
  1432. if ( lobbyType == TYPE_PARTY && sessionCB->GetState() >= idSession::GAME_LOBBY ) {
  1433. SendPeerMembersToLobby( peerNum, TYPE_GAME, false );
  1434. }
  1435. // We are are ingame, then start the client loading immediately
  1436. if ( ( lobbyType == TYPE_GAME || lobbyType == TYPE_GAME_STATE ) && sessionCB->GetState() >= idSession::LOADING ) {
  1437. idLib::Printf( "******* JOIN IN PROGRESS ********\n" );
  1438. if ( sessionCB->GetState() == idSession::INGAME ) {
  1439. newPeer.pauseSnapshots = true; // Since this player joined in progress, let game dictate when to start sending snaps
  1440. }
  1441. QueueReliableMessage( peerNum, idLobby::RELIABLE_START_LOADING );
  1442. }
  1443. } else {
  1444. // If we are in a game lobby, and someone joins our party, tell them to join our game
  1445. if ( lobbyType == TYPE_PARTY && sessionCB->GetState() == idSession::GAME_LOBBY ) {
  1446. SendPeerMembersToLobby( peerNum, TYPE_GAME, false );
  1447. }
  1448. }
  1449. // Send mic status of the current lobby to applicable peers
  1450. SendPeersMicStatusToNewUsers( peerNum );
  1451. // If we made is this far, update the users migration game data index
  1452. for ( int u = 0; u < GetNumLobbyUsers(); u++ ) {
  1453. if ( GetLobbyUser( u )->peerIndex == peerNum ) {
  1454. GetLobbyUser( u )->migrationGameData = migrationGameData;
  1455. }
  1456. }
  1457. return peerNum;
  1458. }
  1459. /*
  1460. ========================
  1461. idLobby::InitStateLobbyHost
  1462. ========================
  1463. */
  1464. void idLobby::InitStateLobbyHost() {
  1465. assert( lobbyBackend != NULL );
  1466. // We will be the host
  1467. isHost = true;
  1468. if ( net_headlessServer.GetBool() ) {
  1469. return; // Don't add any players to headless server
  1470. }
  1471. if ( migrationInfo.state != MIGRATE_NONE ) {
  1472. migrationInfo.persistUntilGameEndsData.wasMigratedHost = true; // InitSessionUsersFromLocalUsers needs to know this
  1473. migrationInfo.persistUntilGameEndsData.hasRelaunchedMigratedGame = false;
  1474. // migrationDlg = GDM_MIGRATING_WAITING;
  1475. }
  1476. // Initialize the initial user list for this lobby
  1477. InitSessionUsersFromLocalUsers( MatchTypeIsOnline( parms.matchFlags ) );
  1478. // Set the session's hostAddress to the local players' address.
  1479. const int myUserIndex = GetLobbyUserIndexByLocalUserHandle( sessionCB->GetSignInManager().GetMasterLocalUserHandle() );
  1480. if ( myUserIndex != -1 ) {
  1481. hostAddress = GetLobbyUser( myUserIndex )->address;
  1482. }
  1483. // Since we are the host, we have to register our initial session users with the lobby
  1484. // All additional users will join through AddUsersFromMsg, and RegisterUser is handled in there from here on out.
  1485. // Peers will add users exclusively through AddUsersFromMsg.
  1486. for ( int i = 0; i < GetNumLobbyUsers(); i++ ) {
  1487. lobbyUser_t * user = GetLobbyUser( i );
  1488. RegisterUser( user );
  1489. if ( lobbyType == TYPE_PARTY ) {
  1490. user->partyToken = GetPartyTokenAsHost();
  1491. }
  1492. }
  1493. // Set the lobbies skill level
  1494. lobbyBackend->UpdateLobbySkill( GetAverageSessionLevel() );
  1495. // Make sure and register all the addresses of the invites we'll send out as the new host
  1496. if ( migrationInfo.state != MIGRATE_NONE ) {
  1497. // Tell the session that we became the host, so the session mgr can adjust state if needed
  1498. sessionCB->BecameHost( *this );
  1499. // Register this address with this lobbyBackend
  1500. for ( int i = 0; i < migrationInfo.invites.Num(); i++ ) {
  1501. lobbyBackend->RegisterAddress( migrationInfo.invites[i].address );
  1502. }
  1503. }
  1504. }
  1505. /*
  1506. ========================
  1507. idLobby::SendMembersToLobby
  1508. ========================
  1509. */
  1510. void idLobby::SendMembersToLobby( lobbyType_t destLobbyType, const lobbyConnectInfo_t & connectInfo, bool waitForOtherMembers ) {
  1511. // It's not our job to send party members to a game if we aren't the party host
  1512. if ( !IsHost() ) {
  1513. return;
  1514. }
  1515. // Send the message to all connected peers
  1516. for ( int i = 0; i < peers.Num(); i++ ) {
  1517. if ( peers[ i ].IsConnected() ) {
  1518. SendPeerMembersToLobby( i, destLobbyType, connectInfo, waitForOtherMembers );
  1519. }
  1520. }
  1521. }
  1522. /*
  1523. ========================
  1524. idLobby::SendMembersToLobby
  1525. ========================
  1526. */
  1527. void idLobby::SendMembersToLobby( idLobby & destLobby, bool waitForOtherMembers ) {
  1528. if ( destLobby.lobbyBackend == NULL ) {
  1529. return; // We don't have a game lobbyBackend to get an address for
  1530. }
  1531. lobbyConnectInfo_t connectInfo = destLobby.lobbyBackend->GetConnectInfo();
  1532. SendMembersToLobby( destLobby.lobbyType, connectInfo, waitForOtherMembers );
  1533. }
  1534. /*
  1535. ========================
  1536. idLobby::SendPeerMembersToLobby
  1537. Give the address of a game lobby to a particular peer, notifying that peer to send a hello to the same server.
  1538. ========================
  1539. */
  1540. void idLobby::SendPeerMembersToLobby( int peerIndex, lobbyType_t destLobbyType, const lobbyConnectInfo_t & connectInfo, bool waitForOtherMembers ) {
  1541. // It's not our job to send party members to a game if we aren't the party host
  1542. if ( !IsHost() ) {
  1543. return;
  1544. }
  1545. assert( peerIndex >= 0 );
  1546. assert( peerIndex < peers.Num() );
  1547. peer_t & peer = peers[ peerIndex ];
  1548. NET_VERBOSE_PRINT( "NET: Sending peer %i (%s) to game lobby\n", peerIndex, peer.address.ToString() );
  1549. if ( !peer.IsConnected() ) {
  1550. idLib::Warning( "NET: Can't send peer %i to game lobby: peer isn't in party", peerIndex );
  1551. return;
  1552. }
  1553. byte buffer[ idPacketProcessor::MAX_PACKET_SIZE - 2 ];
  1554. idBitMsg outmsg( buffer, sizeof( buffer ) );
  1555. // Have lobby fill out msg with connection info
  1556. connectInfo.WriteToMsg( outmsg );
  1557. outmsg.WriteByte( destLobbyType );
  1558. outmsg.WriteBool( waitForOtherMembers );
  1559. QueueReliableMessage( peerIndex, RELIABLE_CONNECT_AND_MOVE_TO_LOBBY, outmsg.GetReadData(), outmsg.GetSize() );
  1560. }
  1561. /*
  1562. ========================
  1563. idLobby::SendPeerMembersToLobby
  1564. Give the address of a game lobby to a particular peer, notifying that peer to send a hello to the same server.
  1565. ========================
  1566. */
  1567. void idLobby::SendPeerMembersToLobby( int peerIndex, lobbyType_t destLobbyType, bool waitForOtherMembers ) {
  1568. idLobby * lobby = sessionCB->GetLobbyFromType( destLobbyType );
  1569. if ( !verify( lobby != NULL ) ) {
  1570. return;
  1571. }
  1572. if ( !verify( lobby->lobbyBackend != NULL ) ) {
  1573. return;
  1574. }
  1575. lobbyConnectInfo_t connectInfo = lobby->lobbyBackend->GetConnectInfo();
  1576. SendPeerMembersToLobby( peerIndex, destLobbyType, connectInfo, waitForOtherMembers );
  1577. }
  1578. /*
  1579. ========================
  1580. idLobby::NotifyPartyOfLeavingGameLobby
  1581. ========================
  1582. */
  1583. void idLobby::NotifyPartyOfLeavingGameLobby() {
  1584. if ( lobbyType != TYPE_PARTY ) {
  1585. return; // We are not a party lobby
  1586. }
  1587. if ( !IsHost() ) {
  1588. return; // We are not the host of a party lobby, we can't do this
  1589. }
  1590. if ( !( sessionCB->GetSessionOptions() & idSession::OPTION_LEAVE_WITH_PARTY ) ) {
  1591. return; // Options aren't set to notify party of leaving
  1592. }
  1593. // Tell our party to leave the game they are in
  1594. for ( int i = 0; i < peers.Num(); i++ ) {
  1595. if ( peers[ i ].IsConnected() ) {
  1596. QueueReliableMessage( i, RELIABLE_PARTY_LEAVE_GAME_LOBBY );
  1597. }
  1598. }
  1599. }
  1600. /*
  1601. ========================
  1602. idLobby::GetPartyTokenAsHost
  1603. ========================
  1604. */
  1605. uint32 idLobby::GetPartyTokenAsHost() {
  1606. assert( lobbyType == TYPE_PARTY );
  1607. assert( IsHost() );
  1608. if ( partyToken == 0 ) {
  1609. // I don't know if this is mathematically sound, but it seems reasonable.
  1610. // Don't do this at app startup (i.e. in the constructor) or it will be a lot less random.
  1611. unsigned long seed = Sys_Milliseconds(); // time app has been running
  1612. idLocalUser * masterUser = session->GetSignInManager().GetMasterLocalUser();
  1613. if ( masterUser != NULL ) {
  1614. seed += idStr::Hash( masterUser->GetGamerTag() );
  1615. }
  1616. partyToken = idRandom( seed ).RandomInt();
  1617. idLib::Printf( "NET: PartyToken is %u (seed = %u)\n", partyToken, seed );
  1618. }
  1619. return partyToken;
  1620. }
  1621. /*
  1622. ========================
  1623. idLobby::EncodeSessionID
  1624. ========================
  1625. */
  1626. idPacketProcessor::sessionId_t idLobby::EncodeSessionID( uint32 key ) const {
  1627. assert( sizeof( uint32 ) >= sizeof( idPacketProcessor::sessionId_t ) );
  1628. const int numBits = sizeof( idPacketProcessor::sessionId_t ) * 8 - idPacketProcessor::NUM_LOBBY_TYPE_BITS;
  1629. const uint32 mask = ( 1 << numBits ) - 1;
  1630. idPacketProcessor::sessionId_t sessionID = ( key & mask ) << idPacketProcessor::NUM_LOBBY_TYPE_BITS;
  1631. sessionID |= ( lobbyType + 1 );
  1632. return sessionID;
  1633. }
  1634. /*
  1635. ========================
  1636. idLobby::EncodeSessionID
  1637. ========================
  1638. */
  1639. void idLobby::DecodeSessionID( idPacketProcessor::sessionId_t sessionID, uint32 & key ) const {
  1640. assert( sizeof( uint32 ) >= sizeof( idPacketProcessor::sessionId_t ) );
  1641. key = sessionID >> idPacketProcessor::NUM_LOBBY_TYPE_BITS;
  1642. }
  1643. /*
  1644. ========================
  1645. idLobby::GenerateSessionID
  1646. ========================
  1647. */
  1648. idPacketProcessor::sessionId_t idLobby::GenerateSessionID() const {
  1649. idPacketProcessor::sessionId_t sessionID = EncodeSessionID( Sys_Milliseconds() );
  1650. // Make sure we can use it
  1651. while ( !SessionIDCanBeUsedForInBand( sessionID ) ) {
  1652. sessionID = IncrementSessionID( sessionID );
  1653. }
  1654. return sessionID;
  1655. }
  1656. /*
  1657. ========================
  1658. idLobby::SessionIDCanBeUsedForInBand
  1659. ========================
  1660. */
  1661. bool idLobby::SessionIDCanBeUsedForInBand( idPacketProcessor::sessionId_t sessionID ) const {
  1662. if ( sessionID == idPacketProcessor::SESSION_ID_INVALID ) {
  1663. return false;
  1664. }
  1665. if ( sessionID == idPacketProcessor::SESSION_ID_CONNECTIONLESS_PARTY ) {
  1666. return false;
  1667. }
  1668. if ( sessionID == idPacketProcessor::SESSION_ID_CONNECTIONLESS_GAME ) {
  1669. return false;
  1670. }
  1671. if ( sessionID == idPacketProcessor::SESSION_ID_CONNECTIONLESS_GAME_STATE ) {
  1672. return false;
  1673. }
  1674. return true;
  1675. }
  1676. /*
  1677. ========================
  1678. idLobby::IncrementSessionID
  1679. ========================
  1680. */
  1681. idPacketProcessor::sessionId_t idLobby::IncrementSessionID( idPacketProcessor::sessionId_t sessionID ) const {
  1682. // Increment, taking into account valid id's
  1683. while ( 1 ) {
  1684. uint32 key = 0;
  1685. DecodeSessionID( sessionID, key );
  1686. key++;
  1687. sessionID = EncodeSessionID( key );
  1688. if ( SessionIDCanBeUsedForInBand( sessionID ) ) {
  1689. break;
  1690. }
  1691. }
  1692. return sessionID;
  1693. }
  1694. #define VERIFY_CONNECTED_PEER( p, sessionType_, msgType ) \
  1695. if ( !verify( lobbyType == sessionType_ ) ) { \
  1696. idLib::Printf( "NET: " #msgType ", peer:%s invalid session type for " #sessionType_ " %i.\n", peer.address.ToString(), sessionType_ ); \
  1697. return; \
  1698. } \
  1699. if ( peers[p].GetConnectionState() != CONNECTION_ESTABLISHED ) { \
  1700. idLib::Printf( "NET: " #msgType ", peer:%s not connected for " #sessionType_ " %i.\n", peer.address.ToString(), sessionType_ ); \
  1701. return; \
  1702. }
  1703. #define VERIFY_CONNECTING_PEER( p, sessionType_, msgType ) \
  1704. if ( !verify( lobbyType == sessionType_ ) ) { \
  1705. idLib::Printf( "NET: " #msgType ", peer:%s invalid session type for " #sessionType_ " %i.\n", peer.address.ToString(), sessionType_ ); \
  1706. return; \
  1707. } \
  1708. if ( peers[p].GetConnectionState() != CONNECTION_CONNECTING ) { \
  1709. idLib::Printf( "NET: " #msgType ", peer:%s not connecting for " #sessionType_ " %i.\n", peer.address.ToString(), sessionType_ ); \
  1710. return; \
  1711. }
  1712. #define VERIFY_FROM_HOST( p, sessionType_, msgType ) \
  1713. VERIFY_CONNECTED_PEER( p, sessionType_, msgType ); \
  1714. if ( p != host ) { \
  1715. idLib::Printf( "NET: "#msgType", not from "#sessionType_" host: %s\n", peer.address.ToString() ); \
  1716. return; \
  1717. } \
  1718. #define VERIFY_FROM_CONNECTING_HOST( p, sessionType_, msgType ) \
  1719. VERIFY_CONNECTING_PEER( p, sessionType_, msgType ); \
  1720. if ( p != host ) { \
  1721. idLib::Printf( "NET: "#msgType", not from "#sessionType_" host: %s\n", peer.address.ToString() ); \
  1722. return; \
  1723. } \
  1724. /*
  1725. ========================
  1726. idLobby::HandleHelloAck
  1727. ========================
  1728. */
  1729. void idLobby::HandleHelloAck( int p, idBitMsg & msg ) {
  1730. peer_t & peer = peers[p];
  1731. if ( state != STATE_CONNECT_HELLO_WAIT ) {
  1732. idLib::Printf( "NET: Hello ack for session type %s while not waiting for hello.\n", GetLobbyName() );
  1733. SendGoodbye( peer.address ); // We send a customary goodbye to make sure we are not in their list anymore
  1734. return;
  1735. }
  1736. if ( p != host ) {
  1737. // This shouldn't be possible
  1738. idLib::Printf( "NET: Hello ack for session type %s, not from correct host.\n", GetLobbyName() );
  1739. SendGoodbye( peer.address ); // We send a customary goodbye to make sure we are not in their list anymore
  1740. return;
  1741. }
  1742. assert( GetNumLobbyUsers() == 0 );
  1743. NET_VERBOSE_PRINT( "NET: Hello ack for session type %s from %s\n", GetLobbyName(), peer.address.ToString() );
  1744. // We are now connected to this session type
  1745. SetPeerConnectionState( p, CONNECTION_ESTABLISHED );
  1746. // Obtain what our peer index is on the host is
  1747. peerIndexOnHost = msg.ReadLong();
  1748. // If we connected to a party lobby, get the party token from the lobby owner
  1749. if ( lobbyType == TYPE_PARTY ) {
  1750. partyToken = msg.ReadLong();
  1751. }
  1752. // Read match parms
  1753. parms.Read( msg );
  1754. // Update lobbyBackend with parms
  1755. if ( lobbyBackend != NULL ) {
  1756. lobbyBackend->UpdateMatchParms( parms );
  1757. }
  1758. // Populate the user list with the one from the host (which will also include our local users)
  1759. // This ensures the user lists are kept in sync
  1760. FreeAllUsers();
  1761. AddUsersFromMsg( msg, p );
  1762. // Make sure the host has a current heartbeat
  1763. peer.lastHeartBeat = Sys_Milliseconds();
  1764. lobbyBackend->PostConnectFromMsg( msg );
  1765. // Tell the lobby controller to finalize the connection
  1766. SetState( STATE_FINALIZE_CONNECT );
  1767. //
  1768. // Success - We've received an ack from the server, letting us know we've been registered with the lobbies
  1769. //
  1770. }
  1771. /*
  1772. ========================
  1773. idLobby::GetLobbyUserName
  1774. ========================
  1775. */
  1776. const char * idLobby::GetLobbyUserName( lobbyUserID_t lobbyUserID ) const {
  1777. const int index = GetLobbyUserIndexByID( lobbyUserID );
  1778. const lobbyUser_t * user = GetLobbyUser( index );
  1779. if ( user == NULL ) {
  1780. for ( int i = 0; i < disconnectedUsers.Num(); i++ ) {
  1781. if ( disconnectedUsers[i].lobbyUserID.CompareIgnoreLobbyType( lobbyUserID ) ) {
  1782. return disconnectedUsers[i].gamertag;
  1783. }
  1784. }
  1785. return INVALID_LOBBY_USER_NAME;
  1786. }
  1787. return user->gamertag;
  1788. }
  1789. /*
  1790. ========================
  1791. idLobby::GetLobbyUserSkinIndex
  1792. ========================
  1793. */
  1794. int idLobby::GetLobbyUserSkinIndex( lobbyUserID_t lobbyUserID ) const {
  1795. const int userIndex = GetLobbyUserIndexByID( lobbyUserID );
  1796. const lobbyUser_t * user = GetLobbyUser( userIndex );
  1797. return user ? user->selectedSkin : 0;
  1798. }
  1799. /*
  1800. ========================
  1801. idLobby::GetLobbyUserWeaponAutoSwitch
  1802. ========================
  1803. */
  1804. bool idLobby::GetLobbyUserWeaponAutoSwitch( lobbyUserID_t lobbyUserID ) const {
  1805. const int userIndex = GetLobbyUserIndexByID( lobbyUserID );
  1806. const lobbyUser_t * user = GetLobbyUser( userIndex );
  1807. return user ? user->weaponAutoSwitch : true;
  1808. }
  1809. /*
  1810. ========================
  1811. idLobby::GetLobbyUserWeaponAutoReload
  1812. ========================
  1813. */
  1814. bool idLobby::GetLobbyUserWeaponAutoReload( lobbyUserID_t lobbyUserID ) const {
  1815. const int userIndex = GetLobbyUserIndexByID( lobbyUserID );
  1816. const lobbyUser_t * user = GetLobbyUser( userIndex );
  1817. return user ? user->weaponAutoReload: true;
  1818. }
  1819. /*
  1820. ========================
  1821. idLobby::GetLobbyUserLevel
  1822. ========================
  1823. */
  1824. int idLobby::GetLobbyUserLevel( lobbyUserID_t lobbyUserID ) const {
  1825. const int userIndex = GetLobbyUserIndexByID( lobbyUserID );
  1826. const lobbyUser_t * user = GetLobbyUser( userIndex );
  1827. return user ? user->level : 0;
  1828. }
  1829. /*
  1830. ========================
  1831. idLobby::GetLobbyUserQoS
  1832. ========================
  1833. */
  1834. int idLobby::GetLobbyUserQoS( lobbyUserID_t lobbyUserID ) const {
  1835. const int userIndex = GetLobbyUserIndexByID( lobbyUserID );
  1836. if ( IsHost() && IsSessionUserIndexLocal( userIndex ) ) {
  1837. return 0; // Local users on the host of the active session have 0 ping
  1838. }
  1839. const lobbyUser_t * user = GetLobbyUser( userIndex );
  1840. if ( !verify( user != NULL ) ) {
  1841. return 0;
  1842. }
  1843. return user->pingMs;
  1844. }
  1845. /*
  1846. ========================
  1847. idLobby::GetLobbyUserTeam
  1848. ========================
  1849. */
  1850. int idLobby::GetLobbyUserTeam( lobbyUserID_t lobbyUserID ) const {
  1851. const int userIndex = GetLobbyUserIndexByID( lobbyUserID );
  1852. const lobbyUser_t * user = GetLobbyUser( userIndex );
  1853. return user ? user->teamNumber : 0;
  1854. }
  1855. /*
  1856. ========================
  1857. idLobby::SetLobbyUserTeam
  1858. ========================
  1859. */
  1860. bool idLobby::SetLobbyUserTeam( lobbyUserID_t lobbyUserID, int teamNumber ) {
  1861. const int userIndex = GetLobbyUserIndexByID( lobbyUserID );
  1862. lobbyUser_t * user = GetLobbyUser( userIndex );
  1863. if ( user != NULL ) {
  1864. if ( teamNumber != user->teamNumber ) {
  1865. user->teamNumber = teamNumber;
  1866. if ( IsHost() ) {
  1867. byte buffer[ idPacketProcessor::MAX_PACKET_SIZE - 2 ];
  1868. idBitMsg msg( buffer, sizeof( buffer ) );
  1869. CreateUserUpdateMessage( userIndex, msg );
  1870. idBitMsg readMsg;
  1871. readMsg.InitRead( buffer, msg.GetSize() );
  1872. UpdateSessionUserOnPeers( readMsg );
  1873. }
  1874. return true;
  1875. }
  1876. }
  1877. return false;
  1878. }
  1879. /*
  1880. ========================
  1881. idLobby::GetLobbyUserPartyToken
  1882. ========================
  1883. */
  1884. int idLobby::GetLobbyUserPartyToken( lobbyUserID_t lobbyUserID ) const {
  1885. const int userIndex = GetLobbyUserIndexByID( lobbyUserID );
  1886. const lobbyUser_t * user = GetLobbyUser( userIndex );
  1887. return user ? user->partyToken : 0;
  1888. }
  1889. /*
  1890. ========================
  1891. idLobby::GetProfileFromLobbyUser
  1892. ========================
  1893. */
  1894. idPlayerProfile * idLobby::GetProfileFromLobbyUser( lobbyUserID_t lobbyUserID ) {
  1895. const int userIndex = GetLobbyUserIndexByID( lobbyUserID );
  1896. idPlayerProfile * profile = NULL;
  1897. idLocalUser * localUser = GetLocalUserFromLobbyUserIndex( userIndex );
  1898. if ( localUser != NULL ) {
  1899. profile = localUser->GetProfile();
  1900. }
  1901. if ( profile == NULL ) {
  1902. // Whoops
  1903. profile = session->GetSignInManager().GetDefaultProfile();
  1904. //idLib::Warning( "Returning fake profile until the code is fixed to handle NULL profiles." );
  1905. }
  1906. return profile;
  1907. }
  1908. /*
  1909. ========================
  1910. idLobby::GetLocalUserFromLobbyUser
  1911. ========================
  1912. */
  1913. idLocalUser * idLobby::GetLocalUserFromLobbyUser( lobbyUserID_t lobbyUserID ) {
  1914. const int userIndex = GetLobbyUserIndexByID( lobbyUserID );
  1915. return GetLocalUserFromLobbyUserIndex( userIndex );
  1916. }
  1917. /*
  1918. ========================
  1919. idLobby::GetNumLobbyUsersOnTeam
  1920. ========================
  1921. */
  1922. int idLobby::GetNumLobbyUsersOnTeam( int teamNumber ) const {
  1923. int numTeam = 0;
  1924. for ( int i = 0; i < GetNumLobbyUsers(); ++i ) {
  1925. if ( GetLobbyUser( i )->teamNumber == teamNumber ) {
  1926. ++numTeam;
  1927. }
  1928. }
  1929. return numTeam;
  1930. }
  1931. /*
  1932. ========================
  1933. idLobby::GetPeerName
  1934. ========================
  1935. */
  1936. const char * idLobby::GetPeerName( int peerNum ) const {
  1937. for ( int i = 0; i < GetNumLobbyUsers(); ++i ) {
  1938. if ( !verify( GetLobbyUser( i ) != NULL ) ) {
  1939. continue;
  1940. }
  1941. if ( GetLobbyUser( i )->peerIndex == peerNum ) {
  1942. return GetLobbyUserName( GetLobbyUser( i )->lobbyUserID );
  1943. }
  1944. }
  1945. return INVALID_LOBBY_USER_NAME;
  1946. }
  1947. /*
  1948. ========================
  1949. idLobby::HandleReliableMsg
  1950. ========================
  1951. */
  1952. void idLobby::HandleReliableMsg( int p, idBitMsg & msg ) {
  1953. peer_t & peer = peers[p];
  1954. int reliableType = msg.ReadByte();
  1955. //idLib::Printf(" Received reliable msg: %i \n", reliableType );
  1956. const lobbyType_t actingGameStateLobbyType = GetActingGameStateLobbyType();
  1957. if ( reliableType == RELIABLE_HELLO ) {
  1958. VERIFY_FROM_CONNECTING_HOST( p, lobbyType, RELIABLE_HELLO );
  1959. // This is sent from the host acking a request to join the game lobby
  1960. HandleHelloAck( p, msg );
  1961. return;
  1962. } else if ( reliableType == RELIABLE_USER_CONNECT_REQUEST ) {
  1963. VERIFY_CONNECTED_PEER( p, lobbyType, RELIABLE_USER_CONNECT_REQUEST );
  1964. // This message is sent from a peer requesting for a new user to join the game lobby
  1965. // This will be sent while we are in a game lobby as a host. otherwise, denied.
  1966. NET_VERBOSE_PRINT( "NET: RELIABLE_USER_CONNECT_REQUEST (%s) from %s\n", GetLobbyName(), peer.address.ToString() );
  1967. idSession::sessionState_t expectedState = ( lobbyType == TYPE_PARTY ) ? idSession::PARTY_LOBBY : idSession::GAME_LOBBY;
  1968. if ( sessionCB->GetState() == expectedState && IsHost() && NumFreeSlots() > 0 ) { // This assumes only one user in the msg
  1969. // Add user to session, which will also forward the operation to all other peers
  1970. AddUsersFromMsg( msg, p );
  1971. } else {
  1972. // Let peer know user couldn't be added
  1973. HandleUserConnectFailure( p, msg, RELIABLE_USER_CONNECT_DENIED );
  1974. }
  1975. } else if ( reliableType == RELIABLE_USER_CONNECT_DENIED ) {
  1976. // This message is sent back from the host when a RELIABLE_PARTY_USER_CONNECT_REQUEST failed
  1977. VERIFY_FROM_HOST( p, lobbyType, RELIABLE_PARTY_USER_CONNECT_DENIED );
  1978. // Remove this user from the sign-in manager, so we don't keep trying to add them
  1979. if ( !sessionCB->GetSignInManager().RemoveLocalUserByHandle( localUserHandle_t( msg.ReadLong() ) ) ) {
  1980. NET_VERBOSE_PRINT( "NET: RELIABLE_PARTY_USER_CONNECT_DENIED, local user not found\n" );
  1981. return;
  1982. }
  1983. } else if ( reliableType == RELIABLE_KICK_PLAYER ) {
  1984. VERIFY_FROM_HOST( p, lobbyType, RELIABLE_KICK_PLAYER );
  1985. common->Dialog().AddDialog( GDM_KICKED, DIALOG_ACCEPT, NULL, NULL, false );
  1986. if ( sessionCB->GetPartyLobby().IsHost() ) {
  1987. session->SetSessionOption( idSession::OPTION_LEAVE_WITH_PARTY );
  1988. }
  1989. session->Cancel();
  1990. } else if ( reliableType == RELIABLE_HEADSET_STATE ) {
  1991. HandleHeadsetStateChange( p, msg );
  1992. } else if ( reliableType == RELIABLE_USER_CONNECTED ) {
  1993. // This message is sent back from the host when users have connected, and we need to update our lists to reflect that
  1994. VERIFY_FROM_HOST( p, lobbyType, RELIABLE_USER_CONNECTED );
  1995. NET_VERBOSE_PRINT( "NET: RELIABLE_USER_CONNECTED (%s) from %s\n", GetLobbyName(), peer.address.ToString() );
  1996. AddUsersFromMsg( msg, p );
  1997. } else if ( reliableType == RELIABLE_USER_DISCONNECTED ) {
  1998. // This message is sent back from the host when users have diconnected, and we need to update our lists to reflect that
  1999. VERIFY_FROM_HOST( p, lobbyType, RELIABLE_USER_DISCONNECTED );
  2000. ProcessUserDisconnectMsg( msg );
  2001. } else if ( reliableType == RELIABLE_MATCH_PARMS ) {
  2002. parms.Read( msg );
  2003. // Update lobby with parms
  2004. if ( lobbyBackend != NULL ) {
  2005. lobbyBackend->UpdateMatchParms( parms );
  2006. }
  2007. } else if ( reliableType == RELIABLE_START_LOADING ) {
  2008. // This message is sent from the host to start loading a map
  2009. VERIFY_FROM_HOST( p, actingGameStateLobbyType, RELIABLE_START_LOADING );
  2010. NET_VERBOSE_PRINT( "NET: RELIABLE_START_LOADING from %s\n", peer.address.ToString() );
  2011. startLoadingFromHost = true;
  2012. } else if ( reliableType == RELIABLE_LOADING_DONE ) {
  2013. // This message is sent from the peers to state they are done loading the map
  2014. VERIFY_CONNECTED_PEER( p, actingGameStateLobbyType, RELIABLE_LOADING_DONE );
  2015. unsigned long networkChecksum = 0;
  2016. networkChecksum = msg.ReadLong();
  2017. peer.networkChecksum = networkChecksum;
  2018. peer.loaded = true;
  2019. } else if ( reliableType == RELIABLE_IN_GAME ) {
  2020. VERIFY_CONNECTED_PEER( p, actingGameStateLobbyType, RELIABLE_IN_GAME );
  2021. peer.inGame = true;
  2022. } else if ( reliableType == RELIABLE_SNAPSHOT_ACK ) {
  2023. VERIFY_CONNECTED_PEER( p, actingGameStateLobbyType, RELIABLE_SNAPSHOT_ACK );
  2024. // update our base state for his last received snapshot
  2025. int snapNum = msg.ReadLong();
  2026. float receivedBps = msg.ReadQuantizedUFloat< BANDWIDTH_REPORTING_MAX, BANDWIDTH_REPORTING_BITS >();
  2027. // Update reported received bps
  2028. if ( peer.receivedBpsIndex != snapNum ) {
  2029. // Only do this the first time we get reported bps per snapshot. Subsequent ACKs of the same shot will usually have lower reported bps
  2030. // due to more time elapsing but not receiving a new ss
  2031. peer.receivedBps = receivedBps;
  2032. peer.receivedBpsIndex = snapNum;
  2033. }
  2034. ApplySnapshotDelta( p, snapNum );
  2035. //idLib::Printf( "NET: Peer %d Ack'd snapshot %d\n", p, snapNum );
  2036. NET_VERBOSESNAPSHOT_PRINT_LEVEL( 2, va( "NET: Peer %d Ack'd snapshot %d\n", p, snapNum ) );
  2037. } else if ( reliableType == RELIABLE_RESOURCE_ACK ) {
  2038. } else if ( reliableType == RELIABLE_UPDATE_MATCH_PARMS ) {
  2039. VERIFY_CONNECTED_PEER( p, TYPE_GAME, RELIABLE_UPDATE_MATCH_PARMS );
  2040. int msgType = msg.ReadLong();
  2041. sessionCB->HandlePeerMatchParamUpdate( p, msgType );
  2042. } else if ( reliableType == RELIABLE_MATCHFINISHED ) {
  2043. VERIFY_FROM_HOST( p, actingGameStateLobbyType, RELIABLE_MATCHFINISHED );
  2044. sessionCB->ClearMigrationState();
  2045. } else if ( reliableType == RELIABLE_ENDMATCH ) {
  2046. VERIFY_FROM_HOST( p, actingGameStateLobbyType, RELIABLE_ENDMATCH );
  2047. sessionCB->EndMatchInternal();
  2048. } else if ( reliableType == RELIABLE_ENDMATCH_PREMATURE ) {
  2049. VERIFY_FROM_HOST( p, actingGameStateLobbyType, RELIABLE_ENDMATCH_PREMATURE );
  2050. sessionCB->EndMatchInternal( true );
  2051. } else if ( reliableType == RELIABLE_START_MATCH_GAME_LOBBY_HOST ) {
  2052. // This message should be from the host of the game lobby, telling us (as the host of the GameStateLobby) to start loading
  2053. VERIFY_CONNECTED_PEER( p, TYPE_GAME_STATE, RELIABLE_START_MATCH_GAME_LOBBY_HOST );
  2054. if ( session->GetState() >= idSession::LOADING ) {
  2055. NET_VERBOSE_PRINT( "NET: RELIABLE_START_MATCH_GAME_LOBBY_HOST already loading\n" );
  2056. return;
  2057. }
  2058. // Read match parms, and start loading
  2059. parms.Read( msg );
  2060. // Send these new match parms to currently connected peers
  2061. SendMatchParmsToPeers();
  2062. startLoadingFromHost = true; // Hijack this flag
  2063. } else if ( reliableType == RELIABLE_ARBITRATE ) {
  2064. VERIFY_CONNECTED_PEER( p, TYPE_GAME, RELIABLE_ARBITRATE );
  2065. // Host telling us to arbitrate
  2066. // Set a flag to do this later, since the lobby may not be in a state where it can fulfil the request at the moment
  2067. respondToArbitrate = true;
  2068. } else if ( reliableType == RELIABLE_ARBITRATE_OK ) {
  2069. VERIFY_CONNECTED_PEER( p, TYPE_GAME, RELIABLE_ARBITRATE_OK );
  2070. NET_VERBOSE_PRINT( "NET: Got an arbitration ok from %d\n", p );
  2071. everyoneArbitrated = true;
  2072. for ( int i = 0; i < GetNumLobbyUsers(); i++ ) {
  2073. lobbyUser_t * user = GetLobbyUser( i );
  2074. if ( !verify( user != NULL ) ) {
  2075. continue;
  2076. }
  2077. if ( user->peerIndex == p ) {
  2078. user->arbitrationAcked = true;
  2079. } else if ( !user->arbitrationAcked ) {
  2080. everyoneArbitrated = false;
  2081. }
  2082. }
  2083. if ( everyoneArbitrated ) {
  2084. NET_VERBOSE_PRINT( "NET: Everyone says they registered for arbitration, verifying\n" );
  2085. lobbyBackend->Arbitrate();
  2086. //sessionCB->EveryoneArbitrated();
  2087. return;
  2088. }
  2089. } else if ( reliableType == RELIABLE_POST_STATS ) {
  2090. VERIFY_FROM_HOST( p, actingGameStateLobbyType, RELIABLE_POST_STATS );
  2091. sessionCB->RecvLeaderboardStats( msg );
  2092. } else if ( reliableType == RELIABLE_SESSION_USER_MODIFIED ) {
  2093. VERIFY_CONNECTED_PEER( p, lobbyType, RELIABLE_SESSION_USER_MODIFIED );
  2094. UpdateSessionUserOnPeers( msg );
  2095. } else if ( reliableType == RELIABLE_UPDATE_SESSION_USER ) {
  2096. VERIFY_FROM_HOST( p, lobbyType, RELIABLE_UPDATE_SESSION_USER );
  2097. HandleUpdateSessionUser( msg );
  2098. } else if ( reliableType == RELIABLE_CONNECT_AND_MOVE_TO_LOBBY ) {
  2099. VERIFY_FROM_HOST( p, lobbyType, RELIABLE_CONNECT_AND_MOVE_TO_LOBBY );
  2100. NET_VERBOSE_PRINT( "NET: RELIABLE_CONNECT_AND_MOVE_TO_LOBBY\n" );
  2101. if ( IsHost() ) {
  2102. idLib::Printf( "RELIABLE_CONNECT_AND_MOVE_TO_LOBBY: We are the host.\n" );
  2103. return;
  2104. }
  2105. // Get connection info
  2106. lobbyConnectInfo_t connectInfo;
  2107. connectInfo.ReadFromMsg( msg );
  2108. const lobbyType_t destLobbyType = (lobbyType_t)msg.ReadByte();
  2109. const bool waitForMembers = msg.ReadBool();
  2110. assert( destLobbyType > lobbyType ); // Make sure this is a proper transition (i.e. TYPE_PARTY moves to TYPE_GAME, TYPE_GAME moves to TYPE_GAME_STATE)
  2111. sessionCB->ConnectAndMoveToLobby( destLobbyType, connectInfo, waitForMembers );
  2112. } else if ( reliableType == RELIABLE_PARTY_CONNECT_OK ) {
  2113. VERIFY_FROM_HOST( p, TYPE_PARTY, RELIABLE_PARTY_CONNECT_OK );
  2114. if ( !sessionCB->GetGameLobby().waitForPartyOk ) {
  2115. idLib::Printf( "RELIABLE_PARTY_CONNECT_OK: Wasn't waiting for ok.\n" );
  2116. }
  2117. sessionCB->GetGameLobby().waitForPartyOk = false;
  2118. } else if ( reliableType == RELIABLE_PARTY_LEAVE_GAME_LOBBY ) {
  2119. VERIFY_FROM_HOST( p, TYPE_PARTY, RELIABLE_PARTY_LEAVE_GAME_LOBBY );
  2120. NET_VERBOSE_PRINT( "NET: RELIABLE_PARTY_LEAVE_GAME_LOBBY\n" );
  2121. if ( sessionCB->GetState() != idSession::GAME_LOBBY ) {
  2122. idLib::Printf( "RELIABLE_PARTY_LEAVE_GAME_LOBBY: Not in a game lobby, ignoring.\n" );
  2123. return;
  2124. }
  2125. if ( IsHost() ) {
  2126. idLib::Printf( "RELIABLE_PARTY_LEAVE_GAME_LOBBY: Host of party, ignoring.\n" );
  2127. return;
  2128. }
  2129. sessionCB->LeaveGameLobby();
  2130. } else if ( IsReliablePlayerToPlayerType( reliableType ) ) {
  2131. HandleReliablePlayerToPlayerMsg( p, msg, reliableType );
  2132. } else if ( reliableType == RELIABLE_PING ) {
  2133. HandleReliablePing( p, msg );
  2134. } else if ( reliableType == RELIABLE_PING_VALUES ) {
  2135. HandlePingValues( msg );
  2136. } else if ( reliableType == RELIABLE_BANDWIDTH_VALUES ) {
  2137. HandleBandwidhTestValue( p, msg );
  2138. } else if ( reliableType == RELIABLE_MIGRATION_GAME_DATA ) {
  2139. HandleMigrationGameData( msg );
  2140. } else if ( reliableType >= RELIABLE_GAME_DATA ) {
  2141. VERIFY_CONNECTED_PEER( p, lobbyType, RELIABLE_GAME_DATA );
  2142. common->NetReceiveReliable( p, reliableType - RELIABLE_GAME_DATA, msg );
  2143. } else if ( reliableType == RELIABLE_DUMMY_MSG ) {
  2144. // Ignore dummy msg's
  2145. NET_VERBOSE_PRINT( "NET: ignoring dummy msg from %s\n", peer.address.ToString() );
  2146. } else {
  2147. NET_VERBOSE_PRINT( "NET: Unknown reliable packet type %d from %s\n", reliableType, peer.address.ToString() );
  2148. }
  2149. }
  2150. /*
  2151. ========================
  2152. idLobby::GetTotalOutgoingRate
  2153. ========================
  2154. */
  2155. int idLobby::GetTotalOutgoingRate() {
  2156. int totalSendRate = 0;
  2157. for ( int p = 0; p < peers.Num(); p++ ) {
  2158. const peer_t & peer = peers[p];
  2159. if ( !peer.IsConnected() ) {
  2160. continue;
  2161. }
  2162. const idPacketProcessor & proc = *peer.packetProc;
  2163. totalSendRate += proc.GetOutgoingRateBytes();
  2164. }
  2165. return totalSendRate;
  2166. }
  2167. /*
  2168. ========================
  2169. idLobby::DrawDebugNetworkHUD
  2170. ========================
  2171. */
  2172. extern idCVar net_forceUpstream;
  2173. void idLobby::DrawDebugNetworkHUD() const {
  2174. int totalSendRate = 0;
  2175. int totalRecvRate = 0;
  2176. float totalSentMB = 0.0f;
  2177. float totalRecvMB = 0.0f;
  2178. const float Y_OFFSET = 20.0f;
  2179. const float X_OFFSET = 20.0f;
  2180. const float Y_SPACING = 15.0f;
  2181. float curY = Y_OFFSET;
  2182. int numLines = ( net_forceUpstream.GetFloat() != 0.0f ? 6: 5 );
  2183. renderSystem->DrawFilled( idVec4( 0.0f, 0.0f, 0.0f, 0.7f ), X_OFFSET - 10.0f, curY - 10.0f, 1550, ( peers.Num() + numLines ) * Y_SPACING + 20.0f );
  2184. renderSystem->DrawSmallStringExt( idMath::Ftoi( X_OFFSET ), idMath::Ftoi( curY ), "# Peer | Sent kB/s | Recv kB/s | Sent MB | Recv MB | Ping | L | % | R.NM | R.SZ | R.AK | T", colorGreen, false );
  2185. curY += Y_SPACING;
  2186. renderSystem->DrawSmallStringExt( idMath::Ftoi( X_OFFSET ), idMath::Ftoi( curY ), "------------------------------------------------------------------------------------------------------------------------------------", colorGreen, false );
  2187. curY += Y_SPACING;
  2188. for ( int p = 0; p < peers.Num(); p++ ) {
  2189. const peer_t & peer = peers[p];
  2190. if ( !peer.IsConnected() ) {
  2191. continue;
  2192. }
  2193. const idPacketProcessor & proc = *peer.packetProc;
  2194. totalSendRate += proc.GetOutgoingRateBytes();
  2195. totalRecvRate += proc.GetIncomingRateBytes();
  2196. float sentKps = (float)proc.GetOutgoingRateBytes() / 1024.0f;
  2197. float recvKps = (float)proc.GetIncomingRateBytes() / 1024.0f;
  2198. float sentMB = (float)proc.GetOutgoingBytes() / ( 1024.0f * 1024.0f );
  2199. float recvMB = (float)proc.GetIncomingBytes() / ( 1024.0f * 1024.0f );
  2200. totalSentMB += sentMB;
  2201. totalRecvMB += recvMB;
  2202. idVec4 color = sentKps > 20.0f ? colorRed : colorGreen;
  2203. int resourcePercent = 0;
  2204. idStr name = peer.address.ToString();
  2205. name += lobbyType == TYPE_PARTY ? "(P": "(G";
  2206. name += host == p ? ":H)" : ":C)";
  2207. renderSystem->DrawSmallStringExt( X_OFFSET, curY, va( "%i %22s | %2.02f kB/s | %2.02f kB/s | %2.02f MB | %2.02f MB |%4i ms | %i | %i%% | %i | %i | %i | %2.2f / %2.2f / %i", p, name.c_str(), sentKps, recvKps, sentMB, recvMB, peer.lastPingRtt, peer.loaded, resourcePercent, peer.packetProc->NumQueuedReliables(), peer.packetProc->GetReliableDataSize(), peer.packetProc->NeedToSendReliableAck(), peer.snapHz, peer.maxSnapBps, peer.failedPingRecoveries ), color, false );
  2208. curY += Y_SPACING;
  2209. }
  2210. renderSystem->DrawSmallStringExt( idMath::Ftoi( X_OFFSET ), idMath::Ftoi( curY ), "------------------------------------------------------------------------------------------------------------------------------------", colorGreen, false );
  2211. curY += Y_SPACING;
  2212. float totalSentKps = (float)totalSendRate / 1024.0f;
  2213. float totalRecvKps = (float)totalRecvRate / 1024.0f;
  2214. idVec4 color = totalSentKps > 100.0f ? colorRed : colorGreen;
  2215. renderSystem->DrawSmallStringExt( X_OFFSET, curY, va( "# %20s | %2.02f KB/s | %2.02f KB/s | %2.02f MB | %2.02f MB", "", totalSentKps, totalRecvKps, totalSentMB, totalRecvMB ), color, false );
  2216. curY += Y_SPACING;
  2217. if ( net_forceUpstream.GetFloat() != 0.0f ) {
  2218. float upstreamDropRate = session->GetUpstreamDropRate();
  2219. float upstreamQueuedRate = session->GetUpstreamQueueRate();
  2220. int queuedBytes = session->GetQueuedBytes();
  2221. renderSystem->DrawSmallStringExt( X_OFFSET, curY, va( "Queued: %d | Dropping: %2.02f kB/s Queue: %2.02f kB/s -> Effective %2.02f kB/s", queuedBytes, upstreamDropRate / 1024.0f, upstreamQueuedRate / 1024.0f, totalSentKps - ( upstreamDropRate / 1024.0f ) + ( upstreamQueuedRate / 1024.0f ) ), color, false );
  2222. }
  2223. }
  2224. /*
  2225. ========================
  2226. idLobby::DrawDebugNetworkHUD2
  2227. ========================
  2228. */
  2229. void idLobby::DrawDebugNetworkHUD2() const {
  2230. int totalSendRate = 0;
  2231. int totalRecvRate = 0;
  2232. const float Y_OFFSET = 20.0f;
  2233. const float X_OFFSET = 20.0f;
  2234. const float Y_SPACING = 15.0f;
  2235. float curY = Y_OFFSET;
  2236. renderSystem->DrawFilled( idVec4( 0.0f, 0.0f, 0.0f, 0.7f ), X_OFFSET - 10.0f, curY - 10.0f, 550, (peers.Num() + 4) * Y_SPACING + 20.0f );
  2237. const char* stateName = session->GetStateString();
  2238. renderSystem->DrawFilled( idVec4( 1.0f, 1.0f, 1.0f, 0.7f ), X_OFFSET - 10.0f, curY - 10.0f, 550, (peers.Num() + 5) * Y_SPACING + 20.0f );
  2239. renderSystem->DrawSmallStringExt( idMath::Ftoi( X_OFFSET ), idMath::Ftoi( curY ), va("State: %s. Local time: %d", stateName, Sys_Milliseconds() ), colorGreen, false );
  2240. curY += Y_SPACING;
  2241. renderSystem->DrawSmallStringExt( idMath::Ftoi( X_OFFSET ), idMath::Ftoi( curY ), "Peer | Sent kB/s | Recv kB/s | L | R | Resources", colorGreen, false );
  2242. curY += Y_SPACING;
  2243. renderSystem->DrawSmallStringExt( idMath::Ftoi( X_OFFSET ), idMath::Ftoi( curY ), "------------------------------------------------------------------", colorGreen, false );
  2244. curY += Y_SPACING;
  2245. for ( int p = 0; p < peers.Num(); p++ ) {
  2246. if ( !peers[ p ].IsConnected() ) {
  2247. continue;
  2248. }
  2249. idPacketProcessor & proc = *peers[ p ].packetProc;
  2250. totalSendRate += proc.GetOutgoingRate2();
  2251. totalRecvRate += proc.GetIncomingRate2();
  2252. float sentKps = ( float )proc.GetOutgoingRate2() / 1024.0f;
  2253. float recvKps = ( float )proc.GetIncomingRate2() / 1024.0f;
  2254. // should probably complement that with a bandwidth reading
  2255. // right now I am mostly concerned about fragmentation and the latency spikes it will cause
  2256. idVec4 color = proc.TickFragmentAccumulator() ? colorRed : colorGreen;
  2257. int rLoaded = peers[ p ].numResources;
  2258. int rTotal = 0;
  2259. // show the names of the clients connected to the server. Also make sure it looks reasonably good.
  2260. idStr peerName;
  2261. if ( IsHost() ) {
  2262. peerName = GetPeerName( p );
  2263. int MAX_PEERNAME_LENGTH = 10;
  2264. int nameLength = peerName.Length();
  2265. if ( nameLength > MAX_PEERNAME_LENGTH ) {
  2266. peerName = peerName.Left( MAX_PEERNAME_LENGTH );
  2267. } else if ( nameLength < MAX_PEERNAME_LENGTH ) {
  2268. idStr filler;
  2269. filler.Fill( ' ', MAX_PEERNAME_LENGTH );
  2270. peerName += filler.Left( MAX_PEERNAME_LENGTH - nameLength );
  2271. }
  2272. } else {
  2273. peerName = "Local ";
  2274. }
  2275. renderSystem->DrawSmallStringExt( X_OFFSET, curY, va( "%i - %s | %2.02f kB/s | %2.02f kB/s | %i | %i | %d/%d", p, peerName.c_str(), sentKps, recvKps, peers[p].loaded, peers[p].address.UsingRelay(), rLoaded, rTotal ), color, false );
  2276. curY += Y_SPACING;
  2277. }
  2278. renderSystem->DrawSmallStringExt( idMath::Ftoi( X_OFFSET ), idMath::Ftoi( curY ), "------------------------------------------------------------------", colorGreen, false );
  2279. curY += Y_SPACING;
  2280. float totalSentKps = (float)totalSendRate / 1024.0f;
  2281. float totalRecvKps = (float)totalRecvRate / 1024.0f;
  2282. renderSystem->DrawSmallStringExt( X_OFFSET, curY, va( "Total | %2.02f KB/s | %2.02f KB/s", totalSentKps, totalRecvKps ), colorGreen, false );
  2283. }
  2284. /*
  2285. ========================
  2286. idLobby::DrawDebugNetworkHUD_ServerSnapshotMetrics
  2287. ========================
  2288. */
  2289. idCVar net_debughud3_bps_max( "net_debughud3_bps_max", "5120.0f", CVAR_FLOAT, "Highest factor of server base snapRate that a client can be throttled" );
  2290. void idLobby::DrawDebugNetworkHUD_ServerSnapshotMetrics( bool draw ) {
  2291. const float Y_OFFSET = 20.0f;
  2292. const float X_OFFSET = 20.0f;
  2293. const float Y_SPACING = 15.0f;
  2294. idVec4 color = colorWhite;
  2295. float curY = Y_OFFSET;
  2296. if ( !draw ) {
  2297. for ( int p=0; p < peers.Num(); p++ ) {
  2298. for ( int i=0; i < peers[p].debugGraphs.Num(); i++ ) {
  2299. if ( peers[p].debugGraphs[i] != NULL ) {
  2300. peers[p].debugGraphs[i]->Enable( false );
  2301. } else {
  2302. return;
  2303. }
  2304. }
  2305. }
  2306. return;
  2307. }
  2308. static int lastTime = 0;
  2309. int time = Sys_Milliseconds();
  2310. for ( int p = 0; p < peers.Num(); p++ ) {
  2311. peer_t & peer = peers[p];
  2312. if ( !peer.IsConnected() ) {
  2313. continue;
  2314. }
  2315. idPacketProcessor * packetProc = peer.packetProc;
  2316. idSnapshotProcessor * snapProc = peer.snapProc;
  2317. if ( !verify( packetProc != NULL && snapProc != NULL ) ) {
  2318. continue;
  2319. }
  2320. int snapSeq = snapProc->GetSnapSequence();
  2321. int snapBase = snapProc->GetBaseSequence();
  2322. int deltaSeq = snapSeq - snapBase;
  2323. bool throttled = peer.throttledSnapRate > common->GetSnapRate();
  2324. int numLines = net_forceUpstream.GetBool() ? 5 : 4;
  2325. const int width = renderSystem->GetWidth()/2.0f - (X_OFFSET * 2);
  2326. enum netDebugGraphs_t {
  2327. GRAPH_SNAPSENT,
  2328. GRAPH_OUTGOING,
  2329. GRAPH_INCOMINGREPORTED,
  2330. GRAPH_MAX
  2331. };
  2332. peer.debugGraphs.SetNum( GRAPH_MAX, NULL );
  2333. for ( int i=0; i < GRAPH_MAX; i++ ) {
  2334. // Initialize graphs
  2335. if ( peer.debugGraphs[i] == NULL ) {
  2336. peer.debugGraphs[i] = console->CreateGraph( 500 );
  2337. if ( !verify( peer.debugGraphs[i] != NULL ) ) {
  2338. continue;
  2339. }
  2340. peer.debugGraphs[i]->SetPosition( X_OFFSET - 10.0f + width, curY - 10.0f, width , Y_SPACING * numLines );
  2341. }
  2342. peer.debugGraphs[i]->Enable( true );
  2343. }
  2344. renderSystem->DrawFilled( idVec4( 0.0f, 0.0f, 0.0f, 0.7f ), X_OFFSET - 10.0f, curY - 10.0f, width, ( Y_SPACING * numLines ) + 20.0f );
  2345. renderSystem->DrawSmallStringExt( X_OFFSET, curY, va( "Peer %d - %s RTT %d %sPeerSnapRate: %d %s", p, GetPeerName( p ), peer.lastPingRtt, throttled ? "^1" : "^2", peer.throttledSnapRate/1000, throttled ? "^1Throttled" : "" ), color, false );
  2346. curY += Y_SPACING;
  2347. renderSystem->DrawSmallStringExt( X_OFFSET, curY, va( "SnapSeq %d BaseSeq %d Delta %d Queue %d", snapSeq, snapBase, deltaSeq, snapProc->GetSnapQueueSize() ), color, false );
  2348. curY += Y_SPACING;
  2349. renderSystem->DrawSmallStringExt( X_OFFSET, curY, va( "Reliables: %d / %d bytes Reliable Ack: %d", packetProc->NumQueuedReliables(), packetProc->GetReliableDataSize(), packetProc->NeedToSendReliableAck() ), color, false );
  2350. curY += Y_SPACING;
  2351. renderSystem->DrawSmallStringExt( X_OFFSET, curY, va( "Outgoing %.2f kB/s Reported %.2f kB/s Throttle: %.2f", peer.packetProc->GetOutgoingRateBytes() / 1024.0f, peers[p].receivedBps / 1024.0f, peer.receivedThrottle ), color, false );
  2352. curY += Y_SPACING;
  2353. if ( net_forceUpstream.GetFloat() != 0.0f ) {
  2354. float upstreamDropRate = session->GetUpstreamDropRate();
  2355. float upstreamQueuedRate = session->GetUpstreamQueueRate();
  2356. int queuedBytes = session->GetQueuedBytes();
  2357. renderSystem->DrawSmallStringExt( X_OFFSET, curY, va( "Queued: %d | Dropping: %2.02f kB/s Queue: %2.02f kB/s ", queuedBytes, upstreamDropRate / 1024.0f, upstreamQueuedRate / 1024.0f ), color, false );
  2358. }
  2359. curY += Y_SPACING;
  2360. if ( peer.debugGraphs[GRAPH_SNAPSENT] != NULL ) {
  2361. if ( peer.lastSnapTime > lastTime ) {
  2362. peer.debugGraphs[GRAPH_SNAPSENT]->SetValue(-1, 1.0f, colorBlue );
  2363. } else {
  2364. peer.debugGraphs[GRAPH_SNAPSENT]->SetValue(-1, 0.0f, colorBlue );
  2365. }
  2366. }
  2367. if ( peer.debugGraphs[GRAPH_OUTGOING] != NULL ) {
  2368. idVec4 bgColor( vec4_zero );
  2369. peer.debugGraphs[GRAPH_OUTGOING]->SetBackgroundColor( bgColor );
  2370. idVec4 lineColor = colorLtGrey;
  2371. lineColor.w = 0.5f;
  2372. float outgoingRate = peer.sentBpsHistory[ peer.receivedBpsIndex % MAX_BPS_HISTORY ];
  2373. // peer.packetProc->GetOutgoingRateBytes()
  2374. peer.debugGraphs[GRAPH_OUTGOING]->SetValue(-1, idMath::ClampFloat( 0.0f, 1.0f, outgoingRate / net_debughud3_bps_max.GetFloat() ), lineColor );
  2375. }
  2376. if ( peer.debugGraphs[GRAPH_INCOMINGREPORTED] != NULL ) {
  2377. idVec4 lineColor = colorYellow;
  2378. extern idCVar net_peer_throttle_bps_peer_threshold_pct;
  2379. extern idCVar net_peer_throttle_bps_host_threshold;
  2380. if ( peer.packetProc->GetOutgoingRateBytes() > net_peer_throttle_bps_host_threshold.GetFloat() ) {
  2381. float pct = peer.packetProc->GetOutgoingRateBytes() > 0.0f ? peer.receivedBps / peer.packetProc->GetOutgoingRateBytes() : 0.0f;
  2382. if ( pct < net_peer_throttle_bps_peer_threshold_pct.GetFloat() ) {
  2383. lineColor = colorRed;
  2384. } else {
  2385. lineColor = colorGreen;
  2386. }
  2387. }
  2388. idVec4 bgColor( vec4_zero );
  2389. peer.debugGraphs[GRAPH_INCOMINGREPORTED]->SetBackgroundColor( bgColor );
  2390. peer.debugGraphs[GRAPH_INCOMINGREPORTED]->SetFillMode( idDebugGraph::GRAPH_LINE );
  2391. peer.debugGraphs[GRAPH_INCOMINGREPORTED]->SetValue(-1, idMath::ClampFloat( 0.0f, 1.0f, peer.receivedBps / net_debughud3_bps_max.GetFloat() ), lineColor );
  2392. }
  2393. // Skip down
  2394. curY += ( Y_SPACING * 2.0f);
  2395. }
  2396. lastTime = time;
  2397. }
  2398. /*
  2399. ========================
  2400. idLobby::CheckHeartBeats
  2401. ========================
  2402. */
  2403. void idLobby::CheckHeartBeats() {
  2404. // Disconnect peers that haven't responded within net_peerTimeoutInSeconds
  2405. int time = Sys_Milliseconds();
  2406. int timeoutInMs = session->GetTitleStorageInt( "net_peerTimeoutInSeconds", net_peerTimeoutInSeconds.GetInteger() ) * 1000;
  2407. if ( sessionCB->GetState() < idSession::LOADING && migrationInfo.state == MIGRATE_NONE ) {
  2408. // Use shorter timeout in lobby (TCR)
  2409. timeoutInMs = session->GetTitleStorageInt( "net_peerTimeoutInSeconds_Lobby", net_peerTimeoutInSeconds_Lobby.GetInteger() ) * 1000;
  2410. }
  2411. if ( timeoutInMs > 0 ) {
  2412. for ( int p = 0; p < peers.Num(); p++ ) {
  2413. if ( peers[p].IsConnected() ) {
  2414. bool peerTimeout = false;
  2415. if( time - peers[p].lastHeartBeat > timeoutInMs ) {
  2416. peerTimeout = true;
  2417. }
  2418. // if reliable queue is almost full, disconnect the peer.
  2419. // (this seems reasonable since the reliable queue is set to 64 currently. In practice we should never
  2420. // have more than 3 or 4 queued)
  2421. if ( peers[ p ].packetProc->NumQueuedReliables() > idPacketProcessor::MAX_RELIABLE_QUEUE - 1 ) {
  2422. peerTimeout = true;
  2423. }
  2424. if( peerTimeout ) {
  2425. // Disconnect the peer from any sessions we are a host of
  2426. if ( IsHost() ) {
  2427. idLib::Printf("Peer %i timed out for %s session @ %d (lastHeartBeat %d)\n", p, GetLobbyName(), time, peers[p].lastHeartBeat );
  2428. DisconnectPeerFromSession( p );
  2429. }
  2430. // Handle peers not receiving a heartbeat from the host in awhile
  2431. if ( IsPeer() ) {
  2432. if ( migrationInfo.state != MIGRATE_PICKING_HOST ) {
  2433. idLib::Printf("Host timed out for %s session\n", GetLobbyName() );
  2434. // Pick a host for this session
  2435. PickNewHost();
  2436. }
  2437. }
  2438. }
  2439. }
  2440. }
  2441. }
  2442. if ( IsHost() && lobbyType == GetActingGameStateLobbyType() ) {
  2443. for ( int p = 0; p < peers.Num(); p++ ) {
  2444. if ( !peers[p].IsConnected() ) {
  2445. continue;
  2446. }
  2447. CheckPeerThrottle( p );
  2448. }
  2449. }
  2450. }
  2451. /*
  2452. ========================
  2453. idLobby::CheckHeartBeats
  2454. ========================
  2455. */
  2456. bool idLobby::IsLosingConnectionToHost() const {
  2457. if ( !verify( IsPeer() && host >= 0 && host < peers.Num() ) ) {
  2458. return false;
  2459. }
  2460. if ( !peers[ host ].IsConnected() ) {
  2461. return true;
  2462. }
  2463. int time = Sys_Milliseconds();
  2464. int timeoutInMs = session->GetTitleStorageInt( "net_peerTimeoutInSeconds", net_peerTimeoutInSeconds.GetInteger() ) * 1000;
  2465. // return true if heartbeat > half the timeout length
  2466. if ( timeoutInMs > 0 && time - peers[ host ].lastHeartBeat > timeoutInMs / 2 ) {
  2467. return true;
  2468. }
  2469. // return true if reliable queue is more than half full
  2470. // (this seems reasonable since the reliable queue is set to 64 currently. In practice we should never
  2471. // have more than 3 or 4 queued)
  2472. if ( peers[ host ].packetProc->NumQueuedReliables() > idPacketProcessor::MAX_RELIABLE_QUEUE / 2 ) {
  2473. return true;
  2474. }
  2475. return false;
  2476. }
  2477. /*
  2478. ========================
  2479. idLobby::IsMigratedStatsGame
  2480. ========================
  2481. */
  2482. bool idLobby::IsMigratedStatsGame() const {
  2483. if ( !IsLobbyActive() ) {
  2484. return false;
  2485. }
  2486. if ( lobbyType != TYPE_GAME ) {
  2487. return false; // Only game session migrates games stats
  2488. }
  2489. if ( !MatchTypeHasStats( parms.matchFlags ) ) {
  2490. return false; // Only stats games migrate stats
  2491. }
  2492. if ( !MatchTypeIsRanked( parms.matchFlags ) ) {
  2493. return false; // Only ranked games should migrate stats into new game
  2494. }
  2495. return migrationInfo.persistUntilGameEndsData.wasMigratedGame && migrationInfo.persistUntilGameEndsData.hasGameData;
  2496. }
  2497. /*
  2498. ========================
  2499. idLobby::ShouldRelaunchMigrationGame
  2500. returns true if we are hosting a migrated game and we had valid migration data
  2501. ========================
  2502. */
  2503. bool idLobby::ShouldRelaunchMigrationGame() const {
  2504. if ( IsMigrating() ) {
  2505. return false; // Don't relaunch until all clients have reconnected
  2506. }
  2507. if ( !IsMigratedStatsGame() ) {
  2508. return false; // If we are not migrating stats, we don't want to relaunch a new game
  2509. }
  2510. if ( !migrationInfo.persistUntilGameEndsData.wasMigratedHost ) {
  2511. return false; // Only relaunch if we are the host
  2512. }
  2513. if ( migrationInfo.persistUntilGameEndsData.hasRelaunchedMigratedGame ) {
  2514. return false; // We already relaunched this game
  2515. }
  2516. return true;
  2517. }
  2518. /*
  2519. ========================
  2520. idLobby::ShouldShowMigratingDialog
  2521. ========================
  2522. */
  2523. bool idLobby::ShouldShowMigratingDialog() const {
  2524. if ( IsMigrating() ) {
  2525. return true; // If we are in the process of truly migrating, then definitely return true
  2526. }
  2527. if ( sessionCB->GetState() == idSession::INGAME ) {
  2528. return false;
  2529. }
  2530. // We're either waiting on the server (which could be us) to relaunch, so show the dialog
  2531. return IsMigratedStatsGame() && sessionCB->GetState() != idSession::INGAME;
  2532. }
  2533. /*
  2534. ========================
  2535. idLobby::IsMigrating
  2536. ========================
  2537. */
  2538. bool idLobby::IsMigrating() const {
  2539. return migrationInfo.state != idLobby::MIGRATE_NONE;
  2540. }
  2541. /*
  2542. ========================
  2543. idLobby::PingPeers
  2544. Host only.
  2545. ========================
  2546. */
  2547. void idLobby::PingPeers() {
  2548. if ( !verify( IsHost() )) {
  2549. return;
  2550. }
  2551. const int now = Sys_Milliseconds();
  2552. pktPing_t packet;
  2553. memset( &packet, 0, sizeof( packet ) ); // We're gonna memset like it's 1999.
  2554. packet.timestamp = now;
  2555. byte packetCopy[ sizeof( packet ) ];
  2556. idBitMsg msg( packetCopy, sizeof( packetCopy ) );
  2557. msg.WriteLong( packet.timestamp );
  2558. for ( int i = 0; i < peers.Num(); ++i ) {
  2559. peer_t & peer = peers[ i ];
  2560. if ( !peer.IsConnected() ) {
  2561. continue;
  2562. }
  2563. if ( peer.nextPing <= now ) {
  2564. peer.nextPing = now + PING_INTERVAL_MS;
  2565. QueueReliableMessage( i, RELIABLE_PING, msg.GetReadData(), msg.GetSize() );
  2566. }
  2567. }
  2568. }
  2569. /*
  2570. ========================
  2571. idLobby::ThrottlePeerSnapRate
  2572. ========================
  2573. */
  2574. void idLobby::ThrottlePeerSnapRate( int p ) {
  2575. if ( !verify( IsHost() ) || !verify( p >= 0 ) ) {
  2576. return;
  2577. }
  2578. peers[p].throttledSnapRate = common->GetSnapRate() * 2;
  2579. idLib::Printf( "^1Throttling peer %d %s!\n", p, GetPeerName(p) );
  2580. idLib::Printf( " New snaprate: %d\n", peers[p].throttledSnapRate / 1000 );
  2581. }
  2582. /*
  2583. ========================
  2584. idLobby::SaturatePeers
  2585. ========================
  2586. */
  2587. void idLobby::BeginBandwidthTest() {
  2588. if ( !verify( IsHost() ) ) {
  2589. idLib::Warning("Bandwidth test should only be done on host");
  2590. return;
  2591. }
  2592. if ( bandwidthChallengeStartTime > 0 ) {
  2593. idLib::Warning("Already started bandwidth test");
  2594. return;
  2595. }
  2596. int time = Sys_Milliseconds();
  2597. bandwidthChallengeStartTime = time;
  2598. bandwidthChallengeEndTime = 0;
  2599. bandwidthChallengeFinished = false;
  2600. bandwidthChallengeNumGoodSeq = 0;
  2601. for ( int p = 0; p < peers.Num(); ++p ) {
  2602. if ( !peers[ p ].IsConnected() ) {
  2603. continue;
  2604. }
  2605. if ( !verify( peers[ p ].packetProc != NULL ) ) {
  2606. continue;
  2607. }
  2608. peers[ p ].bandwidthSequenceNum = 0;
  2609. peers[ p ].bandwidthChallengeStartSendTime = 0;
  2610. peers[ p ].bandwidthChallengeResults = false;
  2611. peers[ p ].bandwidthChallengeSendComplete = false;
  2612. peers[ p ].bandwidthTestBytes = peers[ p ].packetProc->GetOutgoingBytes(); // cache this off so we can see the difference when we are done
  2613. }
  2614. }
  2615. /*
  2616. ========================
  2617. idLobby::SaturatePeers
  2618. ========================
  2619. */
  2620. bool idLobby::BandwidthTestStarted() {
  2621. return bandwidthChallengeStartTime != 0;
  2622. }
  2623. /*
  2624. ========================
  2625. idLobby::ServerUpdateBandwidthTest
  2626. ========================
  2627. */
  2628. void idLobby::ServerUpdateBandwidthTest() {
  2629. if ( bandwidthChallengeStartTime <= 0 ) {
  2630. // Not doing a test
  2631. return;
  2632. }
  2633. if ( !verify( IsHost() )) {
  2634. return;
  2635. }
  2636. int time = Sys_Milliseconds();
  2637. if ( bandwidthChallengeFinished ) {
  2638. // test is over
  2639. return;
  2640. }
  2641. idRandom random;
  2642. random.SetSeed( time );
  2643. bool sentAll = true;
  2644. bool recAll = true;
  2645. for ( int i = 0; i < peers.Num(); ++i ) {
  2646. peer_t & peer = peers[ i ];
  2647. if ( !peer.IsConnected() ) {
  2648. continue;
  2649. }
  2650. if ( peer.bandwidthChallengeResults ) {
  2651. continue;
  2652. }
  2653. recAll = false;
  2654. if ( peer.bandwidthChallengeSendComplete ) {
  2655. continue;
  2656. }
  2657. sentAll = false;
  2658. if ( time - peer.bandwidthTestLastSendTime < session->GetTitleStorageInt( "net_bw_test_interval", net_bw_test_interval.GetInteger() ) ) {
  2659. continue;
  2660. }
  2661. if ( peer.packetProc->HasMoreFragments() ) {
  2662. continue;
  2663. }
  2664. if ( peer.bandwidthChallengeStartSendTime == 0 ) {
  2665. peer.bandwidthChallengeStartSendTime = time;
  2666. }
  2667. peer.bandwidthTestLastSendTime = time;
  2668. // Ok, send him a big packet
  2669. byte buffer[ idPacketProcessor::MAX_OOB_MSG_SIZE ]; // <---- NOTE - When calling ProcessOutgoingMsg with true for oob, we can't go over this size
  2670. idBitMsg msg( buffer, sizeof(buffer) );
  2671. msg.WriteLong( peer.bandwidthSequenceNum++ );
  2672. unsigned int randomSize = Min( (unsigned int)(sizeof(buffer) - 12), (unsigned int)session->GetTitleStorageInt( "net_bw_test_packetSizeBytes", net_bw_test_packetSizeBytes.GetInteger() ) );
  2673. msg.WriteLong( randomSize );
  2674. for ( unsigned int j=0; j < randomSize; j++ ) {
  2675. msg.WriteByte( random.RandomInt( 255 ) );
  2676. }
  2677. unsigned int checksum = MD5_BlockChecksum( &buffer[8], randomSize );
  2678. msg.WriteLong( checksum );
  2679. NET_VERBOSE_PRINT("Net: Sending bw challenge to peer %d time %d packet size %d\n", i, time, msg.GetSize() );
  2680. ProcessOutgoingMsg( i, buffer, msg.GetSize(), true, OOB_BANDWIDTH_TEST );
  2681. if ( session->GetTitleStorageInt( "net_bw_test_numPackets", net_bw_test_numPackets.GetInteger() ) > 0 && peer.bandwidthSequenceNum >= net_bw_test_numPackets.GetInteger() ) {
  2682. int sentBytes = peers[i].packetProc->GetOutgoingBytes() - peers[i].bandwidthTestBytes; // FIXME: this won't include the last sent msg
  2683. peers[i].bandwidthTestBytes = sentBytes; // this now means total bytes sent (we don't care about starting/ending total bytes sent to peer)
  2684. peers[i].bandwidthChallengeSendComplete = true;
  2685. NET_VERBOSE_PRINT("Sent enough packets to peer %d for bandwidth test in %dms. Total bytes: %d\n", i, time - bandwidthChallengeStartTime, sentBytes );
  2686. }
  2687. }
  2688. if ( sentAll ) {
  2689. if ( bandwidthChallengeEndTime == 0 ) {
  2690. // We finished sending all our packets, set the timeout time
  2691. bandwidthChallengeEndTime = time + session->GetTitleStorageInt( "net_bw_test_host_timeout", net_bw_test_host_timeout.GetInteger() );
  2692. NET_VERBOSE_PRINT("Net: finished sending BWC to peers. Waiting until %d to hear back\n", bandwidthChallengeEndTime );
  2693. }
  2694. }
  2695. if ( recAll ) {
  2696. bandwidthChallengeFinished = true;
  2697. bandwidthChallengeStartTime = 0;
  2698. } else if ( bandwidthChallengeEndTime != 0 && bandwidthChallengeEndTime < time ) {
  2699. // Timed out waiting for someone - throttle them and move on
  2700. NET_VERBOSE_PRINT("^2Net: timed out waiting for bandwidth challenge results \n");
  2701. for ( int i=0; i < peers.Num(); i++ ) {
  2702. NET_VERBOSE_PRINT(" Peer[%d] %s. SentAll: %d RecAll: %d\n", i, GetPeerName(i), peers[i].bandwidthChallengeSendComplete, peers[i].bandwidthChallengeResults );
  2703. if ( peers[i].bandwidthChallengeSendComplete && !peers[i].bandwidthChallengeResults ) {
  2704. ThrottlePeerSnapRate( i );
  2705. }
  2706. }
  2707. bandwidthChallengeFinished = true;
  2708. bandwidthChallengeStartTime = 0;
  2709. }
  2710. }
  2711. /*
  2712. ========================
  2713. idLobby::UpdateBandwidthTest
  2714. This will be called on clients to check current state of bandwidth testing
  2715. ========================
  2716. */
  2717. void idLobby::ClientUpdateBandwidthTest() {
  2718. if ( !verify( !IsHost() ) || !verify( host >= 0 ) ) {
  2719. return;
  2720. }
  2721. if ( !peers[host].IsConnected() ) {
  2722. return;
  2723. }
  2724. if ( bandwidthChallengeStartTime <= 0 ) {
  2725. // Not doing a test
  2726. return;
  2727. }
  2728. int time = Sys_Milliseconds();
  2729. if ( bandwidthChallengeEndTime > time ) {
  2730. // Test is still going on
  2731. return;
  2732. }
  2733. // Its been long enough since we last received bw test msg. So lets send the results to the server
  2734. byte buffer[ idPacketProcessor::MAX_MSG_SIZE ];
  2735. idBitMsg msg( buffer, sizeof( buffer ) );
  2736. // Send total time it took to receive all the msgs
  2737. // (note, subtract net_bw_test_timeout to get 'last recevied bandwidth test packet')
  2738. // (^^ Note if the last packet is fragmented and we never get it, this is technically wrong!)
  2739. int totalTime = ( bandwidthChallengeEndTime - session->GetTitleStorageInt( "net_bw_test_timeout", net_bw_test_timeout.GetInteger() ) ) - bandwidthChallengeStartTime;
  2740. msg.WriteLong( totalTime );
  2741. // Send total number of complete, in order packets we got
  2742. msg.WriteLong( bandwidthChallengeNumGoodSeq );
  2743. // Send the overall average bandwidth in KBS
  2744. // Note that sending the number of good packets is not enough. If the packets going out are fragmented, and we
  2745. // drop fragments, the number of good sequences will be lower than the bandwidth we actually received.
  2746. int totalIncomingBytes = peers[host].packetProc->GetIncomingBytes() - peers[host].bandwidthTestBytes;
  2747. msg.WriteLong( totalIncomingBytes );
  2748. idLib::Printf("^3Finished Bandwidth test: \n");
  2749. idLib::Printf(" Total time: %d\n", totalTime );
  2750. idLib::Printf(" Num good packets: %d\n", bandwidthChallengeNumGoodSeq );
  2751. idLib::Printf(" Total received byes: %d\n\n", totalIncomingBytes );
  2752. bandwidthChallengeStartTime = 0;
  2753. bandwidthChallengeNumGoodSeq = 0;
  2754. QueueReliableMessage( host, RELIABLE_BANDWIDTH_VALUES, msg.GetReadData(), msg.GetSize() );
  2755. }
  2756. /*
  2757. ========================
  2758. idLobby::HandleBandwidhTestValue
  2759. ========================
  2760. */
  2761. void idLobby::HandleBandwidhTestValue( int p, idBitMsg & msg ) {
  2762. if ( !IsHost() ) {
  2763. return;
  2764. }
  2765. idLib::Printf("Received RELIABLE_BANDWIDTH_CHECK %d\n", Sys_Milliseconds() );
  2766. if ( bandwidthChallengeStartTime < 0 || bandwidthChallengeFinished ) {
  2767. idLib::Warning("Received bandwidth test results too early from peer %d", p );
  2768. return;
  2769. }
  2770. int totalTime = msg.ReadLong();
  2771. int totalGoodSeq = msg.ReadLong();
  2772. int totalReceivedBytes = msg.ReadLong();
  2773. // This is the % of complete packets we received. If the packets used in the BWC are big enough to fragment, then pctPackets
  2774. // will be lower than bytesPct (we will have received a larger PCT of overall bandwidth than PCT of full packets received).
  2775. // Im not sure if this is a useful distinction or not, but it may be good to compare against for now.
  2776. float pctPackets = peers[p].bandwidthSequenceNum > 0 ? (float) totalGoodSeq / (float)peers[p].bandwidthSequenceNum : -1.0f;
  2777. // This is the % of total bytes sent/bytes received.
  2778. float bytesPct = peers[p].bandwidthTestBytes > 0 ? (float) totalReceivedBytes / (float)peers[p].bandwidthTestBytes : -1.0f;
  2779. // Calculate overall bandwidth for the test. That is, total amount received over time.
  2780. // We may want to expand this to also factor in an average instantaneous rate.
  2781. // For now we are mostly concerned with culling out poor performing clients
  2782. float peerKBS = -1.0f;
  2783. if ( verify( totalTime > 0 ) ) {
  2784. peerKBS = ( (float)totalReceivedBytes / 1024.0f ) / MS2SEC(totalTime);
  2785. }
  2786. int totalSendTime = peers[p].bandwidthTestLastSendTime - peers[p].bandwidthChallengeStartSendTime;
  2787. float outgoingKBS = -1.0f;
  2788. if ( verify( totalSendTime > 0 ) ) {
  2789. outgoingKBS = ( (float)peers[p].bandwidthTestBytes / 1024.0f ) / MS2SEC(totalSendTime);
  2790. }
  2791. float pctKBS = peerKBS / outgoingKBS;
  2792. bool failedRate = ( pctKBS < session->GetTitleStorageFloat( "net_bw_test_throttle_rate_pct", net_bw_test_throttle_rate_pct.GetFloat() ) );
  2793. bool failedByte = ( bytesPct < session->GetTitleStorageFloat( "net_bw_test_throttle_byte_pct", net_bw_test_throttle_byte_pct.GetFloat() ) );
  2794. bool failedSeq = ( pctPackets < session->GetTitleStorageFloat( "net_bw_test_throttle_seq_pct", net_bw_test_throttle_seq_pct.GetFloat() ) );
  2795. idLib::Printf("^3Finished Bandwidth test %s: \n", GetPeerName(p) );
  2796. idLib::Printf(" Total time: %dms\n", totalTime );
  2797. idLib::Printf(" %sNum good packets: %d (%.2f%)\n", ( failedSeq ? "^1" : "^2" ), totalGoodSeq, pctPackets );
  2798. idLib::Printf(" %sTotal received bytes: %d (%.2f%)\n", ( failedByte ? "^1" : "^2" ), totalReceivedBytes, bytesPct );
  2799. idLib::Printf(" %sEffective downstream: %.2fkbs (host: %.2fkbs) -> %.2f%\n\n", ( failedRate ? "^1" : "^2" ), peerKBS, outgoingKBS, pctKBS );
  2800. // If shittConnection(totalTime, totalGoodSeq/totalSeq, totalReceivedBytes/totalSentBytes)
  2801. // throttle this user:
  2802. // peers[p].throttledSnapRate = baseSnapRate * 2
  2803. if ( failedRate || failedByte || failedSeq ) {
  2804. ThrottlePeerSnapRate( p );
  2805. }
  2806. // See if we are finished
  2807. peers[p].bandwidthChallengeResults = true;
  2808. bandwidthChallengeFinished = true;
  2809. for ( int i=0; i < peers.Num(); i++ ) {
  2810. if ( peers[i].bandwidthChallengeSendComplete && !peers[i].bandwidthChallengeResults ) {
  2811. bandwidthChallengeFinished = false;
  2812. }
  2813. }
  2814. if ( bandwidthChallengeFinished ) {
  2815. bandwidthChallengeStartTime = 0;
  2816. }
  2817. }
  2818. /*
  2819. ========================
  2820. idLobby::SendPingValues
  2821. Host only
  2822. Periodically send all peers' pings to all peers (for the UI).
  2823. ========================
  2824. */
  2825. void idLobby::SendPingValues() {
  2826. if ( !verify( IsHost() ) ) {
  2827. // paranoia
  2828. return;
  2829. }
  2830. const int now = Sys_Milliseconds();
  2831. if ( nextSendPingValuesTime > now ) {
  2832. return;
  2833. }
  2834. nextSendPingValuesTime = now + PING_INTERVAL_MS;
  2835. pktPingValues_t packet;
  2836. memset( &packet, 0, sizeof(packet) );
  2837. for( int i = 0; i < peers.Max(); ++i ) {
  2838. if ( i >= peers.Num() ) {
  2839. packet.pings[ i ] = -1;
  2840. } else if ( peers[ i ].IsConnected() ) {
  2841. packet.pings[ i ] = peers[ i ].lastPingRtt;
  2842. } else {
  2843. packet.pings[ i ] = -1;
  2844. }
  2845. }
  2846. byte packetCopy[ sizeof(packet) ];
  2847. idBitMsg msg( packetCopy, sizeof(packetCopy) );
  2848. for( int i = 0; i < peers.Max(); ++i ) {
  2849. msg.WriteShort( packet.pings[ i ] );
  2850. }
  2851. for ( int i = 0; i < peers.Num(); i++ ) {
  2852. if ( peers[ i ].IsConnected() ) {
  2853. QueueReliableMessage( i, RELIABLE_PING_VALUES, msg.GetReadData(), msg.GetSize() );
  2854. }
  2855. }
  2856. }
  2857. /*
  2858. ========================
  2859. idLobby::PumpPings
  2860. Host: Periodically determine the round-trip time for a packet to all peers, and tell everyone
  2861. what everyone else's ping to the host is so they can display it in the UI.
  2862. Client: Indicate to the player when the server hasn't updated the ping values in too long.
  2863. This is usually going to preceed a connection timeout.
  2864. ========================
  2865. */
  2866. void idLobby::PumpPings() {
  2867. if ( IsHost() ) {
  2868. // Calculate ping to all peers
  2869. PingPeers();
  2870. // Send the hosts calculated ping values to each peer to everyone has updated ping times
  2871. SendPingValues();
  2872. // Do bandwidth testing
  2873. ServerUpdateBandwidthTest();
  2874. // Send Migration Data
  2875. SendMigrationGameData();
  2876. } else if ( IsPeer() ) {
  2877. ClientUpdateBandwidthTest();
  2878. if ( lastPingValuesRecvTime + PING_INTERVAL_MS + 1000 < Sys_Milliseconds() && migrationInfo.state == MIGRATE_NONE ) {
  2879. for ( int userIndex = 0; userIndex < GetNumLobbyUsers(); ++userIndex ) {
  2880. lobbyUser_t * user = GetLobbyUser( userIndex );
  2881. if ( !verify( user != NULL ) ) {
  2882. continue;
  2883. }
  2884. user->pingMs = 999999;
  2885. }
  2886. }
  2887. }
  2888. }
  2889. /*
  2890. ========================
  2891. idLobby::HandleReliablePing
  2892. ========================
  2893. */
  2894. void idLobby::HandleReliablePing( int p, idBitMsg & msg ) {
  2895. int c, b;
  2896. msg.SaveReadState( c, b );
  2897. pktPing_t ping;
  2898. memset( &ping, 0, sizeof( ping ) );
  2899. if ( !verify( sizeof( ping ) <= msg.GetRemainingData() ) ) {
  2900. NET_VERBOSE_PRINT( "NET: Ignoring ping from peer %i because packet was the wrong size\n", p );
  2901. return;
  2902. }
  2903. ping.timestamp = msg.ReadLong();
  2904. if ( IsHost() ) {
  2905. // we should probably verify here whether or not this ping was solicited or not
  2906. HandlePingReply( p, ping );
  2907. } else {
  2908. // this means the server is requesting a ping, so reply
  2909. msg.RestoreReadState( c, b );
  2910. QueueReliableMessage( p, RELIABLE_PING, msg.GetReadData() + msg.GetReadCount(), msg.GetRemainingData() );
  2911. }
  2912. }
  2913. /*
  2914. ========================
  2915. idLobby::HandlePingReply
  2916. ========================
  2917. */
  2918. void idLobby::HandlePingReply( int p, const pktPing_t & ping ) {
  2919. const int now = Sys_Milliseconds();
  2920. const int rtt = now - ping.timestamp;
  2921. peers[p].lastPingRtt = rtt;
  2922. for ( int userIndex = 0; userIndex < GetNumLobbyUsers(); ++userIndex ) {
  2923. lobbyUser_t * u = GetLobbyUser( userIndex );
  2924. if ( u->peerIndex == p ) {
  2925. u->pingMs = rtt;
  2926. }
  2927. }
  2928. }
  2929. /*
  2930. ========================
  2931. idLobby::HandlePingValues
  2932. ========================
  2933. */
  2934. void idLobby::HandlePingValues( idBitMsg & msg ) {
  2935. pktPingValues_t packet;
  2936. memset( &packet, 0, sizeof( packet ) );
  2937. for( int i = 0; i < peers.Max(); ++i ) {
  2938. packet.pings[ i ] = msg.ReadShort();
  2939. }
  2940. assert( IsPeer() );
  2941. lastPingValuesRecvTime = Sys_Milliseconds();
  2942. for ( int userIndex = 0; userIndex < GetNumLobbyUsers(); ++userIndex ) {
  2943. lobbyUser_t * u = GetLobbyUser( userIndex );
  2944. if ( u->peerIndex != -1 && verify( u->peerIndex >= 0 && u->peerIndex < MAX_PEERS ) ) {
  2945. u->pingMs = packet.pings[ u->peerIndex ];
  2946. } else {
  2947. u->pingMs = 0;
  2948. }
  2949. }
  2950. // Stuff our ping in the hosts slot
  2951. if ( peerIndexOnHost != -1 && verify( peerIndexOnHost >= 0 && peerIndexOnHost < MAX_PEERS ) ) {
  2952. peers[host].lastPingRtt = packet.pings[ peerIndexOnHost ];
  2953. } else {
  2954. peers[host].lastPingRtt = 0;
  2955. }
  2956. }
  2957. /*
  2958. ========================
  2959. idLobby::SendAnotherFragment
  2960. Other than connectionless sends, this should be the chokepoint for sending packets to peers.
  2961. ========================
  2962. */
  2963. bool idLobby::SendAnotherFragment( int p ) {
  2964. peer_t & peer = peers[p];
  2965. if ( !peer.IsConnected() ) { // Not connected to any mode (party or game), so no need to send
  2966. return false;
  2967. }
  2968. if ( !peer.packetProc->HasMoreFragments() ) {
  2969. return false; // No fragments to send for this peer
  2970. }
  2971. if ( !CanSendMoreData( p ) ) {
  2972. return false; // We need to throttle the sends so we don't saturate the connection
  2973. }
  2974. int time = Sys_Milliseconds();
  2975. if ( time - peer.lastFragmentSendTime < 2 ) {
  2976. NET_VERBOSE_PRINT("Too soon to send another packet. Delta: %d \n", ( time-peer.lastFragmentSendTime) );
  2977. return false; // Too soon to send another fragment
  2978. }
  2979. peer.lastFragmentSendTime = time;
  2980. bool sentFragment = false;
  2981. while ( true ) {
  2982. idBitMsg msg;
  2983. // We use the final packet size here because it has been processed, and no more headers will be added
  2984. byte buffer[ idPacketProcessor::MAX_FINAL_PACKET_SIZE ];
  2985. msg.InitWrite( buffer, sizeof( buffer ) );
  2986. if ( !peers[p].packetProc->GetSendFragment( time, peers[p].sessionID, msg ) ) {
  2987. break;
  2988. }
  2989. const bool useDirectPort = ( lobbyType == TYPE_GAME_STATE );
  2990. msg.BeginReading();
  2991. sessionCB->SendRawPacket( peers[p].address, msg.GetReadData(), msg.GetSize(), useDirectPort );
  2992. sentFragment = true;
  2993. break; // Comment this out to send all fragments in one burst
  2994. }
  2995. if ( peer.packetProc->HasMoreFragments() ) {
  2996. NET_VERBOSE_PRINT("More packets left after ::SendAnotherFragment\n");
  2997. }
  2998. return sentFragment;
  2999. }
  3000. /*
  3001. ========================
  3002. idLobby::CanSendMoreData
  3003. ========================
  3004. */
  3005. bool idLobby::CanSendMoreData( int p ) {
  3006. if ( !verify( p >= 0 && p < peers.Num() ) ) {
  3007. NET_VERBOSE_PRINT( "NET: CanSendMoreData %i NO: not a peer\n", p );
  3008. return false;
  3009. }
  3010. peer_t & peer = peers[p];
  3011. if ( !peer.IsConnected() ) {
  3012. NET_VERBOSE_PRINT( "NET: CanSendMoreData %i NO: not connected\n", p );
  3013. return false;
  3014. }
  3015. return peer.packetProc->CanSendMoreData();
  3016. }
  3017. /*
  3018. ========================
  3019. idLobby::ProcessOutgoingMsg
  3020. ========================
  3021. */
  3022. void idLobby::ProcessOutgoingMsg( int p, const void * data, int size, bool isOOB, int userData ) {
  3023. peer_t & peer = peers[p];
  3024. if ( peer.GetConnectionState() != CONNECTION_ESTABLISHED ) {
  3025. idLib::Printf( "peer.GetConnectionState() != CONNECTION_ESTABLISHED\n" );
  3026. return; // Peer not fully connected for this session type, return
  3027. }
  3028. if ( peer.packetProc->HasMoreFragments() ) {
  3029. idLib::Error( "FATAL: Attempt to process a packet while fragments still need to be sent.\n" ); // We can't handle this case
  3030. }
  3031. int currentTime = Sys_Milliseconds();
  3032. // if ( currentTime - peer.lastProcTime < 30 ) {
  3033. // idLib::Printf("ProcessOutgoingMsg called within %dms %s\n", (currentTime - peer.lastProcTime), GetLobbyName() );
  3034. // }
  3035. peer.lastProcTime = currentTime;
  3036. if ( !isOOB ) {
  3037. // Keep track of the last time an in-band packet was sent
  3038. // (used for things like knowing when reliables could have been last sent)
  3039. peer.lastInBandProcTime = peer.lastProcTime;
  3040. }
  3041. idBitMsg msg;
  3042. msg.InitRead( (byte*)data, size );
  3043. peer.packetProc->ProcessOutgoing( currentTime, msg, isOOB, userData );
  3044. }
  3045. /*
  3046. ========================
  3047. idLobby::ResendReliables
  3048. ========================
  3049. */
  3050. void idLobby::ResendReliables( int p ) {
  3051. peer_t & peer = peers[p];
  3052. if ( !peer.IsConnected() ) {
  3053. return;
  3054. }
  3055. if ( peer.packetProc->HasMoreFragments() ) {
  3056. return; // We can't send more data while fragments are still being sent out
  3057. }
  3058. if ( !CanSendMoreData( p ) ) {
  3059. return;
  3060. }
  3061. int time = Sys_Milliseconds();
  3062. const int DEFAULT_MIN_RESEND = 20; // Quicker resend while not in game to speed up resource transmission acks
  3063. const int DEFAULT_MIN_RESEND_INGAME = 100;
  3064. int resendWait = DEFAULT_MIN_RESEND_INGAME;
  3065. if ( sessionCB->GetState() == idSession::INGAME ) {
  3066. // setup some minimum waits and account for ping
  3067. resendWait = Max( DEFAULT_MIN_RESEND_INGAME, peer.lastPingRtt / 2 );
  3068. if ( lobbyType == TYPE_PARTY ) {
  3069. resendWait = Max( 500, resendWait ); // party session does not need fast frequency at all once in game
  3070. }
  3071. } else {
  3072. // don't trust the ping when still loading stuff
  3073. // need to resend fast to speed up transmission of network decls
  3074. resendWait = DEFAULT_MIN_RESEND;
  3075. }
  3076. if ( time - peer.lastInBandProcTime < resendWait ) {
  3077. // no need to resend reliables if they went out on an in-band packet recently
  3078. return;
  3079. }
  3080. if ( peer.packetProc->NumQueuedReliables() > 0 || peer.packetProc->NeedToSendReliableAck() ) {
  3081. //NET_VERBOSE_PRINT( "NET: ResendReliables %s\n", GetLobbyName() );
  3082. ProcessOutgoingMsg( p, NULL, 0, false, 0 ); // Force an empty unreliable msg so any reliables will get processed as well
  3083. }
  3084. }
  3085. /*
  3086. ========================
  3087. idLobby::PumpPackets
  3088. ========================
  3089. */
  3090. void idLobby::PumpPackets() {
  3091. int newTime = Sys_Milliseconds();
  3092. for ( int p = 0; p < peers.Num(); p++ ) {
  3093. if ( peers[p].IsConnected() ) {
  3094. peers[p].packetProc->RefreshRates( newTime );
  3095. }
  3096. }
  3097. // Resend reliable msg's (do this before we send out the fragments)
  3098. for ( int p = 0; p < peers.Num(); p++ ) {
  3099. ResendReliables( p );
  3100. }
  3101. // If we haven't sent anything to our peers in a long time, make sure to send an empty packet (so our heartbeat gets updated) so we don't get disconnected
  3102. // NOTE - We used to only send these to the host, but the host needs to also send these to clients
  3103. for ( int p = 0; p < peers.Num(); p++ ) {
  3104. if ( !peers[p].IsConnected() || peers[p].packetProc->HasMoreFragments() ) {
  3105. continue;
  3106. }
  3107. if ( newTime - peers[p].lastProcTime > 1000 * PEER_HEARTBEAT_IN_SECONDS ) {
  3108. //NET_VERBOSE_PRINT( "NET: ProcessOutgoing Heartbeat %s\n", GetLobbyName() );
  3109. ProcessOutgoingMsg( p, NULL, 0, false, 0 );
  3110. }
  3111. }
  3112. // Send any unsent fragments for each peer (do this last)
  3113. for ( int p = 0; p < peers.Num(); p++ ) {
  3114. SendAnotherFragment( p );
  3115. }
  3116. }
  3117. /*
  3118. ========================
  3119. idLobby::UpdateMatchParms
  3120. ========================
  3121. */
  3122. void idLobby::UpdateMatchParms( const idMatchParameters & p ) {
  3123. if ( !IsHost() ) {
  3124. return;
  3125. }
  3126. parms = p;
  3127. // Update lobbyBackend with parms
  3128. if ( lobbyBackend != NULL ) {
  3129. lobbyBackend->UpdateMatchParms( parms );
  3130. }
  3131. SendMatchParmsToPeers();
  3132. }
  3133. /*
  3134. ========================
  3135. idLobby::GetHostUserName
  3136. ========================
  3137. */
  3138. const char * idLobby::GetHostUserName() const {
  3139. if ( !IsLobbyActive() ) {
  3140. return INVALID_LOBBY_USER_NAME;
  3141. }
  3142. return GetPeerName( -1 ); // This will just grab the first user with this peerIndex (which should be the host)
  3143. }
  3144. /*
  3145. ========================
  3146. idLobby::SendReliable
  3147. ========================
  3148. */
  3149. void idLobby::SendReliable( int type, idBitMsg & msg, bool callReceiveReliable /*= true*/, peerMask_t sessionUserMask /*= MAX_UNSIGNED_TYPE( peerMask_t ) */ ) {
  3150. //assert( lobbyType == GetActingGameStateLobbyType() );
  3151. assert( type < 256 ); // QueueReliable only accepts a byte for message type
  3152. // the queuing below sends the whole message
  3153. // I don't know if whole message is a good thing or a bad thing, but if the passed message has been read from already, this is most likely not going to do what the caller expects
  3154. assert( msg.GetReadCount() + msg.GetReadBit() == 0 );
  3155. if ( callReceiveReliable ) {
  3156. // NOTE: this will put the msg's read status to fully read - which is why the assert check is above
  3157. common->NetReceiveReliable( -1, type, msg );
  3158. }
  3159. uint32 sentPeerMask = 0;
  3160. for ( int i = 0; i < GetNumLobbyUsers(); ++i ) {
  3161. lobbyUser_t * user = GetLobbyUser( i );
  3162. if ( user->peerIndex == -1 ) {
  3163. continue;
  3164. }
  3165. // We only care about sending these to peers in our party lobby
  3166. if ( user->IsDisconnected() ) {
  3167. continue;
  3168. }
  3169. // Don't sent to a user if they are in the exlusion session user mask
  3170. if ( sessionUserMask != 0 && ( sessionUserMask & ( BIT( i ) ) ) == 0 ) {
  3171. continue;
  3172. }
  3173. const int peerIndex = user->peerIndex;
  3174. if ( peerIndex >= peers.Num() ) {
  3175. continue;
  3176. }
  3177. peer_t & peer = peers[peerIndex];
  3178. if ( !peer.IsConnected() ) {
  3179. continue;
  3180. }
  3181. if ( ( sentPeerMask & ( 1 << user->peerIndex ) ) == 0 ) {
  3182. QueueReliableMessage( user->peerIndex, idLobby::RELIABLE_GAME_DATA + type, msg.GetReadData(), msg.GetSize() );
  3183. sentPeerMask |= 1 << user->peerIndex;
  3184. }
  3185. }
  3186. }
  3187. /*
  3188. ========================
  3189. idLobby::SendReliableToLobbyUser
  3190. can only be used on the server. will take care of calling locally if addressed to player 0
  3191. ========================
  3192. */
  3193. void idLobby::SendReliableToLobbyUser( lobbyUserID_t lobbyUserID, int type, idBitMsg & msg ) {
  3194. assert( lobbyType == GetActingGameStateLobbyType() );
  3195. assert( type < 256 ); // QueueReliable only accepts a byte for message type
  3196. assert( IsHost() ); // This function should only be called in the server atm
  3197. const int peerIndex = PeerIndexFromLobbyUser( lobbyUserID );
  3198. if ( peerIndex >= 0 ) {
  3199. // will send the remainder of a message that was started reading through, but not handling a partial byte read
  3200. assert( msg.GetReadBit() == 0 );
  3201. QueueReliableMessage( peerIndex, idLobby::RELIABLE_GAME_DATA + type, msg.GetReadData() + msg.GetReadCount(), msg.GetRemainingData() );
  3202. } else {
  3203. common->NetReceiveReliable( -1, type, msg );
  3204. }
  3205. }
  3206. /*
  3207. ========================
  3208. idLobby::SendReliableToHost
  3209. will make sure to invoke locally if used on the server
  3210. ========================
  3211. */
  3212. void idLobby::SendReliableToHost( int type, idBitMsg & msg ) {
  3213. assert( lobbyType == GetActingGameStateLobbyType() );
  3214. if ( IsHost() ) {
  3215. common->NetReceiveReliable( -1, type, msg );
  3216. } else {
  3217. // will send the remainder of a message that was started reading through, but not handling a partial byte read
  3218. assert( msg.GetReadBit() == 0 );
  3219. QueueReliableMessage( host, idLobby::RELIABLE_GAME_DATA + type, msg.GetReadData() + msg.GetReadCount(), msg.GetRemainingData() );
  3220. }
  3221. }
  3222. /*
  3223. ================================================================================================
  3224. idLobby::reliablePlayerToPlayerHeader_t
  3225. ================================================================================================
  3226. */
  3227. /*
  3228. ========================
  3229. idLobby::reliablePlayerToPlayerHeader_t::reliablePlayerToPlayerHeader_t
  3230. ========================
  3231. */
  3232. idLobby::reliablePlayerToPlayerHeader_t::reliablePlayerToPlayerHeader_t() : fromSessionUserIndex( -1 ), toSessionUserIndex( -1 ) {
  3233. }
  3234. /*
  3235. ========================
  3236. idSessionLocal::reliablePlayerToPlayerHeader_t::Read
  3237. ========================
  3238. */
  3239. bool idLobby::reliablePlayerToPlayerHeader_t::Read( idLobby * lobby, idBitMsg & msg ) {
  3240. assert( lobby != NULL );
  3241. lobbyUserID_t lobbyUserIDFrom;
  3242. lobbyUserID_t lobbyUserIDTo;
  3243. lobbyUserIDFrom.ReadFromMsg( msg );
  3244. lobbyUserIDTo.ReadFromMsg( msg );
  3245. fromSessionUserIndex = lobby->GetLobbyUserIndexByID( lobbyUserIDFrom );
  3246. toSessionUserIndex = lobby->GetLobbyUserIndexByID( lobbyUserIDTo );
  3247. if ( !verify( lobby->GetLobbyUser( fromSessionUserIndex ) != NULL ) ) {
  3248. return false;
  3249. }
  3250. if ( !verify( lobby->GetLobbyUser( toSessionUserIndex ) != NULL ) ) {
  3251. return false;
  3252. }
  3253. return true;
  3254. }
  3255. /*
  3256. ========================
  3257. idLobby::reliablePlayerToPlayerHeader_t::Write
  3258. ========================
  3259. */
  3260. bool idLobby::reliablePlayerToPlayerHeader_t::Write( idLobby * lobby, idBitMsg & msg ) {
  3261. if ( !verify( lobby->GetLobbyUser( fromSessionUserIndex ) != NULL ) ) {
  3262. return false;
  3263. }
  3264. if ( !verify( lobby->GetLobbyUser( toSessionUserIndex ) != NULL ) ) {
  3265. return false;
  3266. }
  3267. lobby->GetLobbyUser( fromSessionUserIndex )->lobbyUserID.WriteToMsg( msg );
  3268. lobby->GetLobbyUser( toSessionUserIndex )->lobbyUserID.WriteToMsg( msg );
  3269. return true;
  3270. }
  3271. /*
  3272. ========================
  3273. idLobby::GetNumActiveLobbyUsers
  3274. ========================
  3275. */
  3276. int idLobby::GetNumActiveLobbyUsers() const {
  3277. int numActive = 0;
  3278. for ( int i = 0; i < GetNumLobbyUsers(); ++i ) {
  3279. if ( !GetLobbyUser( i )->IsDisconnected() ) {
  3280. numActive++;
  3281. }
  3282. }
  3283. return numActive;
  3284. }
  3285. /*
  3286. ========================
  3287. idLobby::AllPeersInGame
  3288. ========================
  3289. */
  3290. bool idLobby::AllPeersInGame() const {
  3291. assert( lobbyType == GetActingGameStateLobbyType() ); // This function doesn't make sense on a party lobby currently
  3292. for ( int p = 0; p < peers.Num(); p++ ) {
  3293. if ( peers[p].IsConnected() && !peers[p].inGame ) {
  3294. return false;
  3295. }
  3296. }
  3297. return true;
  3298. }
  3299. /*
  3300. ========================
  3301. idLobby::PeerIndexFromLobbyUser
  3302. ========================
  3303. */
  3304. int idLobby::PeerIndexFromLobbyUser( lobbyUserID_t lobbyUserID ) const {
  3305. const int lobbyUserIndex = GetLobbyUserIndexByID( lobbyUserID );
  3306. const lobbyUser_t * user = GetLobbyUser( lobbyUserIndex );
  3307. if ( user == NULL ) {
  3308. // This needs to be OK for bot support ( or else add bots at the session level )
  3309. return -1;
  3310. }
  3311. return user->peerIndex;
  3312. }
  3313. /*
  3314. ========================
  3315. idLobby::GetPeerTimeSinceLastPacket
  3316. ========================
  3317. */
  3318. int idLobby::GetPeerTimeSinceLastPacket( int peerIndex ) const {
  3319. if ( peerIndex < 0 ) {
  3320. return 0;
  3321. }
  3322. return Sys_Milliseconds() - peers[peerIndex].lastHeartBeat;
  3323. }
  3324. /*
  3325. ========================
  3326. idLobby::GetActingGameStateLobbyType
  3327. ========================
  3328. */
  3329. idLobby::lobbyType_t idLobby::GetActingGameStateLobbyType() const {
  3330. extern idCVar net_useGameStateLobby;
  3331. return ( net_useGameStateLobby.GetBool() ) ? TYPE_GAME_STATE : TYPE_GAME;
  3332. }
  3333. //========================================================================================================================
  3334. // idLobby::peer_t
  3335. //========================================================================================================================
  3336. /*
  3337. ========================
  3338. idLobby::peer_t::GetConnectionState
  3339. ========================
  3340. */
  3341. idLobby::connectionState_t idLobby::peer_t::GetConnectionState() const {
  3342. return connectionState;
  3343. }