sys_session_local.cpp 128 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094
  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_session_local.h"
  23. #include "sys_voicechat.h"
  24. #include "sys_dedicated_server_search.h"
  25. idCVar ui_skinIndex( "ui_skinIndex", "0", CVAR_ARCHIVE, "Selected skin index" );
  26. idCVar ui_autoSwitch( "ui_autoSwitch", "1", CVAR_ARCHIVE | CVAR_BOOL, "auto switch weapon" );
  27. idCVar ui_autoReload( "ui_autoReload", "1", CVAR_ARCHIVE | CVAR_BOOL, "auto reload weapon" );
  28. idCVar net_maxSearchResults( "net_maxSearchResults", "25", CVAR_INTEGER, "Max results that are allowed to be returned in a search request" );
  29. idCVar net_maxSearchResultsToTry( "net_maxSearchResultsToTry", "5", CVAR_INTEGER, "Max results to try before giving up." ); // At 15 second timeouts per, 1 min 15 worth of connecting attempt time
  30. idCVar net_LobbyCoalesceTimeInSeconds( "net_LobbyCoalesceTimeInSeconds", "30", CVAR_INTEGER, "Time in seconds when a lobby will try to coalesce with another lobby when there is only one user." );
  31. idCVar net_LobbyRandomCoalesceTimeInSeconds( "net_LobbyRandomCoalesceTimeInSeconds", "3", CVAR_INTEGER, "Random time to add to net_LobbyCoalesceTimeInSeconds" );
  32. idCVar net_useGameStateLobby( "net_useGameStateLobby", "0", CVAR_BOOL, "" );
  33. //idCVar net_useGameStateLobby( "net_useGameStateLobby", "1", CVAR_BOOL, "" );
  34. #if !defined( ID_RETAIL ) || defined( ID_RETAIL_INTERNAL )
  35. idCVar net_ignoreTitleStorage( "net_ignoreTitleStorage", "0", CVAR_BOOL, "Ignore title storage" );
  36. #endif
  37. idCVar net_maxLoadResourcesTimeInSeconds( "net_maxLoadResourcesTimeInSeconds", "0", CVAR_INTEGER, "How long, in seconds, clients have to load resources. Used for loose asset builds." );
  38. idCVar net_migrateHost( "net_migrateHost", "-1", CVAR_INTEGER, "Become host of session (0 = party, 1 = game) for testing purposes" );
  39. extern idCVar net_debugBaseStates;
  40. idCVar net_testPartyMemberConnectFail( "net_testPartyMemberConnectFail", "-1", CVAR_INTEGER, "Force this party member index to fail to connect to games." );
  41. //FIXME: this could use a better name.
  42. idCVar net_offlineTransitionThreshold( "net_offlineTransitionThreshold", "1000", CVAR_INTEGER, "Time, in milliseconds, to wait before kicking back to the main menu when a profile losses backend connection during an online game");
  43. idCVar net_port( "net_port", "27015", CVAR_INTEGER, "host port number" ); // Port to host when using dedicated servers, port to broadcast on when looking for a dedicated server to connect to
  44. idCVar net_headlessServer( "net_headlessServer", "0", CVAR_BOOL, "toggle to automatically host a game and allow peer[0] to control menus" );
  45. const char * idSessionLocal::stateToString[ NUM_STATES ] = {
  46. ASSERT_ENUM_STRING( STATE_PRESS_START, 0 ),
  47. ASSERT_ENUM_STRING( STATE_IDLE, 1 ),
  48. ASSERT_ENUM_STRING( STATE_PARTY_LOBBY_HOST, 2 ),
  49. ASSERT_ENUM_STRING( STATE_PARTY_LOBBY_PEER, 3 ),
  50. ASSERT_ENUM_STRING( STATE_GAME_LOBBY_HOST, 4 ),
  51. ASSERT_ENUM_STRING( STATE_GAME_LOBBY_PEER, 5 ),
  52. ASSERT_ENUM_STRING( STATE_GAME_STATE_LOBBY_HOST, 6 ),
  53. ASSERT_ENUM_STRING( STATE_GAME_STATE_LOBBY_PEER, 7 ),
  54. ASSERT_ENUM_STRING( STATE_CREATE_AND_MOVE_TO_PARTY_LOBBY, 8 ),
  55. ASSERT_ENUM_STRING( STATE_CREATE_AND_MOVE_TO_GAME_LOBBY, 9 ),
  56. ASSERT_ENUM_STRING( STATE_CREATE_AND_MOVE_TO_GAME_STATE_LOBBY, 10 ),
  57. ASSERT_ENUM_STRING( STATE_FIND_OR_CREATE_MATCH, 11 ),
  58. ASSERT_ENUM_STRING( STATE_CONNECT_AND_MOVE_TO_PARTY, 12 ),
  59. ASSERT_ENUM_STRING( STATE_CONNECT_AND_MOVE_TO_GAME, 13 ),
  60. ASSERT_ENUM_STRING( STATE_CONNECT_AND_MOVE_TO_GAME_STATE, 14 ),
  61. ASSERT_ENUM_STRING( STATE_BUSY, 15 ),
  62. ASSERT_ENUM_STRING( STATE_LOADING, 16 ),
  63. ASSERT_ENUM_STRING( STATE_INGAME, 17 ),
  64. };
  65. struct netVersion_s {
  66. netVersion_s() { sprintf( string, "%s.%d", ENGINE_VERSION, BUILD_NUMBER ); }
  67. char string[256];
  68. } netVersion;
  69. /*
  70. ========================
  71. NetGetVersionChecksum
  72. ========================
  73. */
  74. unsigned long NetGetVersionChecksum() {
  75. #if 0
  76. return idStr( com_version.GetString() ).Hash();
  77. #else
  78. unsigned long ret = 0;
  79. CRC32_InitChecksum( ret );
  80. CRC32_UpdateChecksum( ret, netVersion.string, idStr::Length( netVersion.string ) );
  81. CRC32_FinishChecksum( ret );
  82. NET_VERBOSE_PRINT( "NetGetVersionChecksum - string : %s\n", netVersion.string );
  83. NET_VERBOSE_PRINT( "NetGetVersionChecksum - checksum : %i\n", ret );
  84. return ret;
  85. #endif
  86. }
  87. /*
  88. ========================
  89. idSessionLocal::idSessionLocal
  90. ========================
  91. */
  92. idSessionLocal::idSessionLocal() :
  93. processorSaveFiles( new (TAG_SAVEGAMES) idSaveGameProcessorSaveFiles ),
  94. processorLoadFiles( new (TAG_SAVEGAMES) idSaveGameProcessorLoadFiles ),
  95. processorDelete( new (TAG_SAVEGAMES) idSaveGameProcessorDelete ),
  96. processorEnumerate( new (TAG_SAVEGAMES) idSaveGameProcessorEnumerateGames ) {
  97. InitBaseState();
  98. }
  99. /*
  100. ========================
  101. idSessionLocal::idSessionLocal
  102. ========================
  103. */
  104. idSessionLocal::~idSessionLocal() {
  105. delete processorSaveFiles;
  106. delete processorLoadFiles;
  107. delete processorDelete;
  108. delete processorEnumerate;
  109. delete sessionCallbacks;
  110. }
  111. /*
  112. ========================
  113. idSessionLocal::InitBaseState
  114. ========================
  115. */
  116. void idSessionLocal::InitBaseState() {
  117. //assert( mem.IsGlobalHeap() );
  118. localState = STATE_PRESS_START;
  119. sessionOptions = 0;
  120. currentID = 0;
  121. sessionCallbacks = new (TAG_NETWORKING) idSessionLocalCallbacks( this );
  122. connectType = CONNECT_NONE;
  123. connectTime = 0;
  124. upstreamDropRate = 0.0f;
  125. upstreamDropRateTime = 0;
  126. upstreamQueueRate = 0.0f;
  127. upstreamQueueRateTime = 0;
  128. queuedBytes = 0;
  129. lastVoiceSendtime = 0;
  130. hasShownVoiceRestrictionDialog = false;
  131. isSysUIShowing = false;
  132. pendingInviteDevice = 0;
  133. pendingInviteMode = PENDING_INVITE_NONE;
  134. downloadedContent.Clear();
  135. marketplaceHasNewContent = false;
  136. offlineTransitionTimerStart = 0;
  137. showMigratingInfoStartTime = 0;
  138. nextGameCoalesceTime = 0;
  139. gameLobbyWasCoalesced = false;
  140. numFullSnapsReceived = 0;
  141. flushedStats = false;
  142. titleStorageLoaded = false;
  143. droppedByHost = false;
  144. loadingID = 0;
  145. storedPeer = -1;
  146. storedMsgType = -1;
  147. inviteInfoRequested = false;
  148. enumerationHandle = 0;
  149. waitingOnGameStateMembersToLeaveTime = 0;
  150. waitingOnGameStateMembersToJoinTime = 0;
  151. }
  152. /*
  153. ========================
  154. idSessionLocal::FinishDisconnect
  155. ========================
  156. */
  157. void idSessionLocal::FinishDisconnect() {
  158. GetPort().Close();
  159. while ( sendQueue.Peek() != NULL ) {
  160. sendQueue.RemoveFirst();
  161. }
  162. while ( recvQueue.Peek() != NULL ) {
  163. recvQueue.RemoveFirst();
  164. }
  165. }
  166. //====================================================================================
  167. idCVar net_connectTimeoutInSeconds( "net_connectTimeoutInSeconds", "15", CVAR_INTEGER, "timeout (in seconds) while connecting" );
  168. /*
  169. ========================
  170. idSessionLocal::CreatePartyLobby
  171. ========================
  172. */
  173. void idSessionLocal::CreatePartyLobby( const idMatchParameters & parms_ ) {
  174. NET_VERBOSE_PRINT( "NET: CreatePartyLobby\n" );
  175. // Shutdown any possible party lobby
  176. GetPartyLobby().Shutdown();
  177. GetPartyLobby().ResetAllMigrationState();
  178. // Shutdown any possible game lobby
  179. GetGameLobby().Shutdown();
  180. GetGameStateLobby().Shutdown();
  181. // Start hosting a new party lobby
  182. GetPartyLobby().StartHosting( parms_ );
  183. connectType = CONNECT_NONE;
  184. connectTime = Sys_Milliseconds();
  185. // Wait for it to complete
  186. SetState( STATE_CREATE_AND_MOVE_TO_PARTY_LOBBY );
  187. }
  188. /*
  189. ========================
  190. idSessionLocal::CreateMatch
  191. ========================
  192. */
  193. void idSessionLocal::CreateMatch( const idMatchParameters & p ) {
  194. NET_VERBOSE_PRINT( "NET: CreateMatch\n" );
  195. if ( ( p.matchFlags & MATCH_PARTY_INVITE_PLACEHOLDER ) && !GetPartyLobby().IsLobbyActive() ) {
  196. NET_VERBOSE_PRINT( "NET: CreateMatch MATCH_PARTY_INVITE_PLACEHOLDER\n" );
  197. CreatePartyLobby( p );
  198. connectType = CONNECT_NONE;
  199. return;
  200. }
  201. // Shutdown any possible game lobby
  202. GetGameLobby().Shutdown();
  203. GetGameStateLobby().Shutdown();
  204. GetGameLobby().ResetAllMigrationState();
  205. // Start hosting a new game lobby
  206. GetGameLobby().StartHosting( p );
  207. connectType = CONNECT_NONE;
  208. connectTime = Sys_Milliseconds();
  209. // Wait for it to complete
  210. SetState( STATE_CREATE_AND_MOVE_TO_GAME_LOBBY );
  211. }
  212. /*
  213. ========================
  214. idSessionLocal::CreateGameStateLobby
  215. ========================
  216. */
  217. void idSessionLocal::CreateGameStateLobby( const idMatchParameters & p ) {
  218. NET_VERBOSE_PRINT( "NET: CreateGameStateLobby\n" );
  219. // Shutdown any possible game state lobby
  220. GetGameStateLobby().Shutdown();
  221. GetGameStateLobby().ResetAllMigrationState();
  222. // Start hosting a new game lobby
  223. GetGameStateLobby().StartHosting( p );
  224. connectType = CONNECT_NONE;
  225. connectTime = Sys_Milliseconds();
  226. waitingOnGameStateMembersToLeaveTime = 0; // Make sure to reset
  227. waitingOnGameStateMembersToJoinTime = 0;
  228. // Wait for it to complete
  229. SetState( STATE_CREATE_AND_MOVE_TO_GAME_STATE_LOBBY );
  230. }
  231. /*
  232. ========================
  233. idSessionLocal::FindOrCreateMatch
  234. ========================
  235. */
  236. void idSessionLocal::FindOrCreateMatch( const idMatchParameters & p ) {
  237. NET_VERBOSE_PRINT( "NET: FindOrCreateMatch\n" );
  238. if ( ( p.matchFlags & MATCH_PARTY_INVITE_PLACEHOLDER ) && !GetPartyLobby().IsLobbyActive() ) {
  239. NET_VERBOSE_PRINT( "NET: FindOrCreateMatch MATCH_PARTY_INVITE_PLACEHOLDER\n" );
  240. CreatePartyLobby( p );
  241. connectType = CONNECT_FIND_OR_CREATE;
  242. return;
  243. }
  244. // Shutdown any possible game lobby
  245. GetGameLobby().Shutdown();
  246. GetGameStateLobby().Shutdown();
  247. GetGameLobby().ResetAllMigrationState();
  248. // Start searching for a game
  249. GetGameLobby().StartFinding( p );
  250. connectType = CONNECT_FIND_OR_CREATE;
  251. connectTime = Sys_Milliseconds();
  252. gameLobbyWasCoalesced = false;
  253. numFullSnapsReceived = 0;
  254. // Wait for searching to complete
  255. SetState( STATE_FIND_OR_CREATE_MATCH );
  256. }
  257. /*
  258. ========================
  259. idSessionLocal::StartLoading
  260. ========================
  261. */
  262. void idSessionLocal::StartLoading() {
  263. NET_VERBOSE_PRINT( "NET: StartLoading\n" );
  264. if ( MatchTypeIsOnline( GetActingGameStateLobby().parms.matchFlags ) ) {
  265. if ( !GetActingGameStateLobby().IsHost() ) {
  266. idLib::Warning( "Ignoring call to StartLoading because we are not the host. state is %s", stateToString[ localState ] );
  267. return;
  268. }
  269. for ( int p = 0; p < GetActingGameStateLobby().peers.Num(); p++ ) {
  270. if ( GetActingGameStateLobby().peers[p].IsConnected() ) {
  271. GetActingGameStateLobby().QueueReliableMessage( p, idLobby::RELIABLE_START_LOADING );
  272. GetActingGameStateLobby().peers[p].startResourceLoadTime = Sys_Milliseconds();
  273. }
  274. }
  275. }
  276. VerifySnapshotInitialState();
  277. SetState( STATE_LOADING );
  278. }
  279. /*
  280. ========================
  281. idSessionLocal::StartMatch
  282. ========================
  283. */
  284. void idSessionLocal::StartMatch() {
  285. NET_VERBOSE_PRINT( "NET: StartMatch\n" );
  286. if ( net_headlessServer.GetBool() ) {
  287. StartLoading(); // This is so we can force start matches on headless servers to test performance using bots
  288. return;
  289. }
  290. if ( localState != STATE_GAME_LOBBY_HOST ) {
  291. idLib::Warning( "idSessionLocal::StartMatch called when not hosting game lobby" );
  292. return;
  293. }
  294. assert( !GetGameStateLobby().IsLobbyActive() );
  295. // make absolutely sure we only call StartMatch once per migrate
  296. GetGameLobby().migrationInfo.persistUntilGameEndsData.hasRelaunchedMigratedGame = true;
  297. // Clear snap ack queue between games
  298. GetGameLobby().snapDeltaAckQueue.Clear();
  299. extern idCVar net_bw_challenge_enable;
  300. if ( session->GetTitleStorageBool( "net_bw_challenge_enable", net_bw_challenge_enable.GetBool() ) && GetGameLobby().HasActivePeers() ) {
  301. GetGameLobby().bandwidthChallengeFinished = false;
  302. StartOrContinueBandwidthChallenge( false );
  303. }
  304. if ( GetGameLobby().BandwidthTestStarted() ) {
  305. // Put session in busy state
  306. NET_VERBOSE_PRINT( "NET: StartMatch -> Start Bandwidth Challenge\n" );
  307. SetState( STATE_BUSY );
  308. } else {
  309. // Start loading
  310. StartLoading();
  311. }
  312. }
  313. /*
  314. ========================
  315. idSessionLocal::GetBackState
  316. ========================
  317. */
  318. idSessionLocal::sessionState_t idSessionLocal::GetBackState() {
  319. sessionState_t currentState = GetState();
  320. const bool isInGameLobby = currentState == GAME_LOBBY;
  321. const bool isInPartyLobby = currentState == PARTY_LOBBY;
  322. const bool isInGame = currentState == INGAME || currentState == LOADING; // Counting loading as ingame as far as what back state to go to
  323. if ( isInGame ) {
  324. return GAME_LOBBY; // If in the game, go back to game lobby
  325. }
  326. if ( !isInPartyLobby && isInGameLobby && ShouldHavePartyLobby() ) {
  327. return PARTY_LOBBY; // If in the game lobby, and we should have a party lobby, and we are the host, go back to party lobby
  328. }
  329. if ( currentState != IDLE ) {
  330. return IDLE; // From here, go to idle if we aren't there yet
  331. }
  332. return PRESS_START; // Otherwise, go back to press start
  333. }
  334. /*
  335. ========================
  336. idSessionLocal::Cancel
  337. ========================
  338. */
  339. void idSessionLocal::Cancel() {
  340. NET_VERBOSE_PRINT( "NET: Cancel\n" );
  341. if ( localState == STATE_PRESS_START ) {
  342. return; // We're as far back as we can go
  343. }
  344. ClearVoiceGroups(); // this is here as a catch-all
  345. // See what state we need to go to
  346. switch ( GetBackState() ) {
  347. case GAME_LOBBY:
  348. EndMatch(); // End current match to go to game lobby
  349. break;
  350. case PARTY_LOBBY:
  351. if ( GetPartyLobby().IsHost() ) {
  352. if ( sessionOptions & OPTION_LEAVE_WITH_PARTY ) {
  353. // NOTE - This will send a message on the team lobby channel,
  354. // so it won't be affected by the fact that we're shutting down the game lobby
  355. GetPartyLobby().NotifyPartyOfLeavingGameLobby();
  356. } else {
  357. // Host wants to be alone, disconnect all peers from the party
  358. GetPartyLobby().DisconnectAllPeers();
  359. }
  360. // End the game lobby, and go back to party lobby as host
  361. GetGameLobby().Shutdown();
  362. GetGameStateLobby().Shutdown();
  363. SetState( STATE_PARTY_LOBBY_HOST );
  364. // Always remove this flag. SendGoodbye uses this to determine if we should send a "leave with party"
  365. // and we don't want this flag hanging around, and causing false positives when it's called in the future.
  366. // Make them set this each time.
  367. sessionOptions &= ~OPTION_LEAVE_WITH_PARTY;
  368. } else {
  369. // If we aren't the host of a party and we want to go back to one, we need to create a party now
  370. CreatePartyLobby( GetPartyLobby().parms );
  371. }
  372. break;
  373. case IDLE:
  374. // Go back to main menu
  375. GetGameLobby().Shutdown();
  376. GetGameStateLobby().Shutdown();
  377. GetPartyLobby().Shutdown();
  378. SetState( STATE_IDLE );
  379. break;
  380. case PRESS_START:
  381. // Go back to press start/main
  382. GetGameLobby().Shutdown();
  383. GetGameStateLobby().Shutdown();
  384. GetPartyLobby().Shutdown();
  385. SetState( STATE_PRESS_START );
  386. break;
  387. }
  388. // Validate the current lobby immediately
  389. ValidateLobbies();
  390. }
  391. /*
  392. ========================
  393. idSessionLocal::MoveToPressStart
  394. ========================
  395. */
  396. void idSessionLocal::MoveToPressStart() {
  397. if ( localState != STATE_PRESS_START ) {
  398. assert( signInManager != NULL );
  399. signInManager->RemoveAllLocalUsers();
  400. hasShownVoiceRestrictionDialog = false;
  401. MoveToMainMenu();
  402. session->FinishDisconnect();
  403. SetState( STATE_PRESS_START );
  404. }
  405. }
  406. /*
  407. ========================
  408. idSessionLocal::ShouldShowMigratingDialog
  409. ========================
  410. */
  411. bool idSessionLocal::ShouldShowMigratingDialog() const {
  412. const idLobby * activeLobby = GetActivePlatformLobby();
  413. if ( activeLobby == NULL ) {
  414. return false;
  415. }
  416. return activeLobby->ShouldShowMigratingDialog();
  417. }
  418. /*
  419. ========================
  420. idSessionLocal::IsCurrentLobbyMigrating
  421. ========================
  422. */
  423. bool idSessionLocal::IsCurrentLobbyMigrating() const {
  424. const idLobby * activeLobby = GetActivePlatformLobby();
  425. if ( activeLobby == NULL ) {
  426. return false;
  427. }
  428. return activeLobby->IsMigrating();
  429. }
  430. /*
  431. ========================
  432. idSessionLocal::IsLosingConnectionToHost
  433. ========================
  434. */
  435. bool idSessionLocal::IsLosingConnectionToHost() const {
  436. return GetActingGameStateLobby().IsLosingConnectionToHost();
  437. }
  438. /*
  439. ========================
  440. idSessionLocal::WasMigrationGame
  441. returns true if we are hosting a migrated game and we had valid migration data
  442. ========================
  443. */
  444. bool idSessionLocal::WasMigrationGame() const {
  445. return GetGameLobby().IsMigratedStatsGame();
  446. }
  447. /*
  448. ========================
  449. idSessionLocal::ShouldRelaunchMigrationGame
  450. returns true if we are hosting a migrated game and we had valid migration data
  451. ========================
  452. */
  453. bool idSessionLocal::ShouldRelaunchMigrationGame() const {
  454. return GetGameLobby().ShouldRelaunchMigrationGame() && !IsCurrentLobbyMigrating();
  455. }
  456. /*
  457. ========================
  458. idSessionLocal::GetMigrationGameData
  459. ========================
  460. */
  461. bool idSessionLocal::GetMigrationGameData( idBitMsg & msg, bool reading ) {
  462. return GetGameLobby().GetMigrationGameData( msg, reading );
  463. }
  464. /*
  465. ========================
  466. idSessionLocal::GetMigrationGameDataUser
  467. ========================
  468. */
  469. bool idSessionLocal::GetMigrationGameDataUser( lobbyUserID_t lobbyUserID, idBitMsg & msg, bool reading ) {
  470. if ( GetGameStateLobby().IsHost() ) {
  471. return false;
  472. }
  473. return GetGameLobby().GetMigrationGameDataUser( lobbyUserID, msg, reading );
  474. }
  475. /*
  476. ========================
  477. idSessionLocal::GetMatchParamUpdate
  478. ========================
  479. */
  480. bool idSessionLocal::GetMatchParamUpdate( int &peer, int &msg ){
  481. if ( storedPeer != -1 && storedMsgType != -1 ) {
  482. peer = storedPeer;
  483. msg = storedMsgType;
  484. storedPeer = -1;
  485. storedMsgType = -1;
  486. return true;
  487. }
  488. return false;
  489. }
  490. /*
  491. ========================
  492. idSessionLocal::UpdatePartyParms
  493. Updates the party parameters when in a party lobby OR a game lobby in order to keep them always in sync.
  494. ========================
  495. */
  496. void idSessionLocal::UpdatePartyParms( const idMatchParameters & p ) {
  497. if ( ( GetState() != PARTY_LOBBY && GetState() != GAME_LOBBY ) || !GetPartyLobby().IsHost() ) {
  498. return;
  499. }
  500. // NET_VERBOSE_PRINT( "NET: UpdatePartyParms\n" );
  501. GetPartyLobby().UpdateMatchParms( p );
  502. }
  503. /*
  504. ========================
  505. idSessionLocal::UpdateMatchParms
  506. ========================
  507. */
  508. void idSessionLocal::UpdateMatchParms( const idMatchParameters & p ) {
  509. if ( GetState() != GAME_LOBBY || !GetGameLobby().IsHost() ) {
  510. return;
  511. }
  512. NET_VERBOSE_PRINT( "NET: UpdateMatchParms\n" );
  513. GetGameLobby().UpdateMatchParms( p );
  514. }
  515. /*
  516. ========================
  517. idSessionLocal::StartSessions
  518. ========================
  519. */
  520. void idSessionLocal::StartSessions() {
  521. if ( GetPartyLobby().lobbyBackend != NULL ) {
  522. GetPartyLobby().lobbyBackend->StartSession();
  523. }
  524. if ( GetGameLobby().lobbyBackend != NULL ) {
  525. GetGameLobby().lobbyBackend->StartSession();
  526. }
  527. SetLobbiesAreJoinable( false );
  528. }
  529. /*
  530. ========================
  531. idSessionLocal::EndSessions
  532. ========================
  533. */
  534. void idSessionLocal::EndSessions() {
  535. if ( GetPartyLobby().lobbyBackend != NULL ) {
  536. GetPartyLobby().lobbyBackend->EndSession();
  537. }
  538. if ( GetGameLobby().lobbyBackend != NULL ) {
  539. GetGameLobby().lobbyBackend->EndSession();
  540. }
  541. SetLobbiesAreJoinable( true );
  542. }
  543. /*
  544. ========================
  545. idSessionLocal::SetLobbiesAreJoinable
  546. ========================
  547. */
  548. void idSessionLocal::SetLobbiesAreJoinable( bool joinable ) {
  549. // NOTE - We don't manipulate the joinable state when we are supporting join in progress
  550. // Lobbies will naturally be non searchable when there are no free slots
  551. if ( GetPartyLobby().lobbyBackend != NULL && !MatchTypeIsJoinInProgress( GetPartyLobby().parms.matchFlags ) ) {
  552. NET_VERBOSE_PRINT( "Party lobbyBackend SetIsJoinable: %d\n", joinable );
  553. GetPartyLobby().lobbyBackend->SetIsJoinable( joinable );
  554. }
  555. if ( GetGameLobby().lobbyBackend != NULL && !MatchTypeIsJoinInProgress( GetGameLobby().parms.matchFlags ) ) {
  556. GetGameLobby().lobbyBackend->SetIsJoinable( joinable );
  557. NET_VERBOSE_PRINT( "Game lobbyBackend SetIsJoinable: %d\n", joinable );
  558. }
  559. }
  560. /*
  561. ========================
  562. idSessionLocal::MoveToMainMenu
  563. ========================
  564. */
  565. void idSessionLocal::MoveToMainMenu() {
  566. GetPartyLobby().Shutdown();
  567. GetGameLobby().Shutdown();
  568. GetGameStateLobby().Shutdown();
  569. SetState( STATE_IDLE );
  570. }
  571. /*
  572. ========================
  573. idSessionLocal::HandleVoiceRestrictionDialog
  574. ========================
  575. */
  576. void idSessionLocal::HandleVoiceRestrictionDialog() {
  577. // don't bother complaining about voice restrictions when in a splitscreen lobby
  578. if ( MatchTypeIsLocal( GetActivePlatformLobby()->parms.matchFlags ) ) {
  579. return;
  580. }
  581. // Pop a dialog up the first time we are in a lobby and have voice chat restrictions due to account privileges
  582. if ( voiceChat != NULL && voiceChat->IsRestrictedByPrivleges() && !hasShownVoiceRestrictionDialog ) {
  583. common->Dialog().AddDialog( GDM_VOICE_RESTRICTED, DIALOG_ACCEPT, NULL, NULL, false );
  584. hasShownVoiceRestrictionDialog = true;
  585. }
  586. }
  587. /*
  588. ========================
  589. idSessionLocal::WaitOnLobbyCreate
  590. Called from State_Create_And_Move_To_Party_Lobby and State_Create_And_Move_To_Game_Lobby and State_Create_And_Move_To_Game_State_Lobby.
  591. This function will create the lobby, then wait for it to either succeed or fail.
  592. ========================
  593. */
  594. bool idSessionLocal::WaitOnLobbyCreate( idLobby & lobby ) {
  595. assert( localState == STATE_CREATE_AND_MOVE_TO_PARTY_LOBBY || localState == STATE_CREATE_AND_MOVE_TO_GAME_LOBBY || localState == STATE_CREATE_AND_MOVE_TO_GAME_STATE_LOBBY );
  596. assert( connectType == CONNECT_FIND_OR_CREATE || connectType == CONNECT_NONE );
  597. if ( lobby.GetState() == idLobby::STATE_FAILED ) {
  598. NET_VERBOSE_PRINT( "NET: idSessionLocal::WaitOnLobbyCreate lobby.GetState() == idLobby::STATE_FAILED (%s)\n", lobby.GetLobbyName() );
  599. // If we failed to create a lobby, assume connection to backend service was lost
  600. MoveToMainMenu();
  601. common->Dialog().ClearDialogs( true );
  602. common->Dialog().AddDialog( GDM_CONNECTION_LOST, DIALOG_ACCEPT, NULL, NULL, true, "", 0, true );
  603. return false;
  604. }
  605. if ( DetectDisconnectFromService( true ) ) {
  606. return false;
  607. }
  608. if ( lobby.GetState() != idLobby::STATE_IDLE ) {
  609. return false; // Valid but busy
  610. }
  611. NET_VERBOSE_PRINT( "NET: idSessionLocal::WaitOnLobbyCreate SUCCESS (%s)\n", lobby.GetLobbyName() );
  612. return true;
  613. }
  614. /*
  615. ========================
  616. idSessionLocal::DetectDisconnectFromService
  617. Called from CreateMatch/CreatePartyLobby/FindOrCreateMatch state machines
  618. ========================
  619. */
  620. bool idSessionLocal::DetectDisconnectFromService( bool cancelAndShowMsg ) {
  621. const int DETECT_SERVICE_DISCONNECT_TIMEOUT_IN_SECONDS = session->GetTitleStorageInt( "DETECT_SERVICE_DISCONNECT_TIMEOUT_IN_SECONDS", 30 );
  622. // If we are taking too long, cancel the connection
  623. if ( DETECT_SERVICE_DISCONNECT_TIMEOUT_IN_SECONDS > 0 ) {
  624. if ( Sys_Milliseconds() - connectTime > 1000 * DETECT_SERVICE_DISCONNECT_TIMEOUT_IN_SECONDS ) {
  625. NET_VERBOSE_PRINT( "NET: idSessionLocal::DetectDisconnectFromService timed out\n" );
  626. if ( cancelAndShowMsg ) {
  627. MoveToMainMenu();
  628. common->Dialog().ClearDialogs( true );
  629. common->Dialog().AddDialog( GDM_CONNECTION_LOST, DIALOG_ACCEPT, NULL, NULL, false, "", 0, true );
  630. }
  631. return true;
  632. }
  633. }
  634. return false;
  635. }
  636. /*
  637. ========================
  638. idSessionLocal::HandleConnectionFailed
  639. Called anytime a connection fails, and does the right thing.
  640. ========================
  641. */
  642. void idSessionLocal::HandleConnectionFailed( idLobby & lobby, bool wasFull ) {
  643. assert( localState == STATE_CONNECT_AND_MOVE_TO_PARTY || localState == STATE_CONNECT_AND_MOVE_TO_GAME || localState == STATE_CONNECT_AND_MOVE_TO_GAME_STATE );
  644. assert( connectType == CONNECT_FIND_OR_CREATE || connectType == CONNECT_DIRECT );
  645. bool canPlayOnline = true;
  646. // Check for online status (this is only a problem on the PS3 at the moment. The 360 LIVE system handles this for us
  647. if ( GetSignInManager().GetMasterLocalUser() != NULL ) {
  648. canPlayOnline = GetSignInManager().GetMasterLocalUser()->CanPlayOnline();
  649. }
  650. if ( connectType == CONNECT_FIND_OR_CREATE ) {
  651. // Clear the "Lobby was Full" dialog in case it's up
  652. // We only want to see this msg when doing a direct connect (CONNECT_DIRECT)
  653. common->Dialog().ClearDialog( GDM_LOBBY_FULL );
  654. assert( localState == STATE_CONNECT_AND_MOVE_TO_GAME || localState == STATE_CONNECT_AND_MOVE_TO_GAME_STATE );
  655. assert( lobby.lobbyType == idLobby::TYPE_GAME );
  656. if ( !lobby.ConnectToNextSearchResult() ) {
  657. CreateMatch( GetGameLobby().parms ); // Assume any time we are connecting to a game lobby, it is from a FindOrCreateMatch call, so create a match
  658. }
  659. } else if ( connectType == CONNECT_DIRECT ) {
  660. if ( localState == STATE_CONNECT_AND_MOVE_TO_GAME && GetPartyLobby().IsPeer() ) {
  661. int flags = GetPartyLobby().parms.matchFlags;
  662. if ( MatchTypeIsOnline( flags ) && ( flags & MATCH_REQUIRE_PARTY_LOBBY ) && ( ( flags & MATCH_PARTY_INVITE_PLACEHOLDER ) == 0 ) ) {
  663. // We get here when our party host told us to connect to a game, but the game didn't exist.
  664. // Just drop back to the party lobby and wait for further orders.
  665. SetState( STATE_PARTY_LOBBY_PEER );
  666. return;
  667. }
  668. }
  669. if ( wasFull ) {
  670. common->Dialog().AddDialog( GDM_LOBBY_FULL, DIALOG_ACCEPT, NULL, NULL, false );
  671. } else if ( !canPlayOnline ) {
  672. common->Dialog().AddDialog( GDM_PLAY_ONLINE_NO_PROFILE, DIALOG_ACCEPT, NULL, NULL, false );
  673. } else {
  674. // TEMP HACK: We detect the steam lobby is full in idLobbyBackendWin, and then STATE_FAILED, which brings us here. Need to find a way to notify
  675. // session local that the game was full so we don't do this check here
  676. // eeubanks: Pollard, how do you think we should handle this?
  677. if ( !common->Dialog().HasDialogMsg( GDM_LOBBY_FULL, NULL ) ) {
  678. common->Dialog().AddDialog( GDM_INVALID_INVITE, DIALOG_ACCEPT, NULL, NULL, false );
  679. }
  680. }
  681. MoveToMainMenu();
  682. } else {
  683. // Shouldn't be possible, but just in case
  684. MoveToMainMenu();
  685. }
  686. }
  687. /*
  688. ========================
  689. idSessionLocal::HandleConnectAndMoveToLobby
  690. Called from State_Connect_And_Move_To_Party/State_Connect_And_Move_To_Game
  691. ========================
  692. */
  693. bool idSessionLocal::HandleConnectAndMoveToLobby( idLobby & lobby ) {
  694. assert( localState == STATE_CONNECT_AND_MOVE_TO_PARTY || localState == STATE_CONNECT_AND_MOVE_TO_GAME || localState == STATE_CONNECT_AND_MOVE_TO_GAME_STATE );
  695. assert( connectType == CONNECT_FIND_OR_CREATE || connectType == CONNECT_DIRECT );
  696. if ( lobby.GetState() == idLobby::STATE_FAILED ) {
  697. // If we get here, we were trying to connect to a lobby (from state State_Connect_And_Move_To_Party/State_Connect_And_Move_To_Game)
  698. HandleConnectionFailed( lobby, false );
  699. return true;
  700. }
  701. if ( lobby.GetState() != idLobby::STATE_IDLE ) {
  702. return HandlePackets(); // Valid but busy
  703. }
  704. assert( !GetPartyLobby().waitForPartyOk );
  705. //
  706. // Past this point, we've connected to the lobby
  707. //
  708. // If we are connecting to a game lobby, see if we need to keep waiting as either a host or peer while we're confirming all party members made it
  709. if ( lobby.lobbyType == idLobby::TYPE_GAME ) {
  710. if ( GetPartyLobby().IsHost() ) {
  711. // As a host, wait until all party members make it
  712. assert( !GetGameLobby().waitForPartyOk );
  713. const int timeoutMs = session->GetTitleStorageInt( "net_connectTimeoutInSeconds", net_connectTimeoutInSeconds.GetInteger() ) * 1000;
  714. if ( timeoutMs != 0 && Sys_Milliseconds() - lobby.helloStartTime > timeoutMs ) {
  715. // Took too long, move to next result, or create a game instead
  716. HandleConnectionFailed( lobby, false );
  717. return true;
  718. }
  719. int numUsersIn = 0;
  720. for ( int i = 0; i < GetPartyLobby().GetNumLobbyUsers(); i++ ) {
  721. if ( net_testPartyMemberConnectFail.GetInteger() == i ) {
  722. continue;
  723. }
  724. bool foundUser = false;
  725. lobbyUser_t * partyUser = GetPartyLobby().GetLobbyUser( i );
  726. for ( int j = 0; j < GetGameLobby().GetNumLobbyUsers(); j++ ) {
  727. lobbyUser_t * gameUser = GetGameLobby().GetLobbyUser( j );
  728. if ( GetGameLobby().IsSessionUserLocal( gameUser ) || gameUser->address.Compare( partyUser->address, true ) ) {
  729. numUsersIn++;
  730. foundUser = true;
  731. break;
  732. }
  733. }
  734. assert( !GetPartyLobby().IsSessionUserIndexLocal( i ) || foundUser );
  735. }
  736. if ( numUsersIn != GetPartyLobby().GetNumLobbyUsers() ) {
  737. return HandlePackets(); // All users not in, keep waiting until all user make it, or we time out
  738. }
  739. NET_VERBOSE_PRINT( "NET: All party members made it into the game lobby.\n" );
  740. // Let all the party members know everyone made it, and it's ok to stay at this server
  741. for ( int i = 0; i < GetPartyLobby().peers.Num(); i++ ) {
  742. if ( GetPartyLobby().peers[ i ].IsConnected() ) {
  743. GetPartyLobby().QueueReliableMessage( i, idLobby::RELIABLE_PARTY_CONNECT_OK );
  744. }
  745. }
  746. } else {
  747. if ( !verify ( lobby.host != -1 ) ) {
  748. MoveToMainMenu();
  749. connectType = CONNECT_NONE;
  750. return false;
  751. }
  752. // As a peer, wait for server to tell us everyone made it
  753. if ( GetGameLobby().waitForPartyOk ) {
  754. const int timeoutMs = session->GetTitleStorageInt( "net_connectTimeoutInSeconds", net_connectTimeoutInSeconds.GetInteger() ) * 1000;
  755. if ( timeoutMs != 0 && Sys_Milliseconds() - lobby.helloStartTime > timeoutMs ) {
  756. GetGameLobby().waitForPartyOk = false; // Just connect to this game lobby if we haven't heard from the party host for the entire timeout duration
  757. }
  758. }
  759. if ( GetGameLobby().waitForPartyOk ) {
  760. return HandlePackets(); // Waiting on party host to tell us everyone made it
  761. }
  762. }
  763. }
  764. // Success
  765. switch ( lobby.lobbyType ) {
  766. case idLobby::TYPE_PARTY:
  767. SetState( STATE_PARTY_LOBBY_PEER );
  768. break;
  769. case idLobby::TYPE_GAME:
  770. SetState( STATE_GAME_LOBBY_PEER );
  771. break;
  772. case idLobby::TYPE_GAME_STATE:
  773. waitingOnGameStateMembersToJoinTime = Sys_Milliseconds();
  774. // As a host of the game lobby, it's our duty to notify our members to also join this game state lobby
  775. GetGameLobby().SendMembersToLobby( GetGameStateLobby(), false );
  776. SetState( STATE_GAME_STATE_LOBBY_PEER );
  777. break;
  778. }
  779. connectType = CONNECT_NONE;
  780. return false;
  781. }
  782. /*
  783. ========================
  784. idSessionLocal::State_Create_And_Move_To_Party_Lobby
  785. ========================
  786. */
  787. bool idSessionLocal::State_Create_And_Move_To_Party_Lobby() {
  788. if ( WaitOnLobbyCreate( GetPartyLobby() ) ) {
  789. if ( GetPartyLobby().parms.matchFlags & MATCH_PARTY_INVITE_PLACEHOLDER ) {
  790. // If this party lobby was for a placeholder, continue on with either finding or creating a game lobby
  791. if ( connectType == CONNECT_FIND_OR_CREATE ) {
  792. FindOrCreateMatch( GetPartyLobby().parms );
  793. return true;
  794. } else if ( connectType == CONNECT_NONE ) {
  795. CreateMatch( GetPartyLobby().parms );
  796. return true;
  797. }
  798. }
  799. // Success
  800. SetState( STATE_PARTY_LOBBY_HOST );
  801. return true;
  802. }
  803. return HandlePackets(); // Valid but busy
  804. }
  805. /*
  806. ========================
  807. idSessionLocal::State_Create_And_Move_To_Game_Lobby
  808. ========================
  809. */
  810. bool idSessionLocal::State_Create_And_Move_To_Game_Lobby() {
  811. if ( WaitOnLobbyCreate( GetGameLobby() ) ) {
  812. // Success
  813. SetState( STATE_GAME_LOBBY_HOST );
  814. // Now that we've created our game lobby, send our own party users to it
  815. // NOTE - We pass in false to wait on party members since we are the host, and we know they can connect to us
  816. GetPartyLobby().SendMembersToLobby( GetGameLobby(), false );
  817. return true;
  818. }
  819. return false;
  820. }
  821. /*
  822. ========================
  823. idSessionLocal::State_Create_And_Move_To_Game_State_Lobby
  824. ========================
  825. */
  826. bool idSessionLocal::State_Create_And_Move_To_Game_State_Lobby() {
  827. if ( WaitOnLobbyCreate( GetGameStateLobby() ) ) {
  828. // Success
  829. SetState( STATE_GAME_STATE_LOBBY_HOST );
  830. // Now that we've created our game state lobby, send our own game users to it
  831. // NOTE - We pass in false to wait on party members since we are the host, and we know they can connect to us
  832. GetGameLobby().SendMembersToLobby( GetGameStateLobby(), false );
  833. // If we are the host of a game lobby, we know we are not using dedicated servers, so we want to start the match immediately
  834. // as soon as we detect all users have connected.
  835. if ( GetGameLobby().IsHost() ) {
  836. waitingOnGameStateMembersToJoinTime = Sys_Milliseconds();
  837. }
  838. return true;
  839. }
  840. return false;
  841. }
  842. /*
  843. ========================
  844. idSessionLocal::State_Find_Or_Create_Match
  845. ========================
  846. */
  847. bool idSessionLocal::State_Find_Or_Create_Match() {
  848. assert( connectType == CONNECT_FIND_OR_CREATE );
  849. if ( GetGameLobby().GetState() == idLobby::STATE_FAILED ) {
  850. // Failed to find any games. Create one instead (we're assuming this always gets called from FindOrCreateMatch
  851. CreateMatch( GetGameLobby().parms );
  852. return true;
  853. }
  854. if ( DetectDisconnectFromService( true ) ) {
  855. return false;
  856. }
  857. if ( GetGameLobby().GetState() != idLobby::STATE_IDLE ) {
  858. return HandlePackets(); // Valid but busy
  859. }
  860. // Done searching, connect to the first search result
  861. if ( !GetGameLobby().ConnectToNextSearchResult() ) {
  862. // Failed to find any games. Create one instead (we're assuming this always gets called from FindOrCreateMatch
  863. CreateMatch( GetGameLobby().parms );
  864. return true;
  865. }
  866. SetState( STATE_CONNECT_AND_MOVE_TO_GAME );
  867. return true;
  868. }
  869. /*
  870. ========================
  871. idSessionLocal::State_Connect_And_Move_To_Party
  872. ========================
  873. */
  874. bool idSessionLocal::State_Connect_And_Move_To_Party() {
  875. return HandleConnectAndMoveToLobby( GetPartyLobby() );
  876. }
  877. /*
  878. ========================
  879. idSessionLocal::State_Connect_And_Move_To_Game
  880. ========================
  881. */
  882. bool idSessionLocal::State_Connect_And_Move_To_Game() {
  883. return HandleConnectAndMoveToLobby( GetGameLobby() );
  884. }
  885. /*
  886. ========================
  887. idSessionLocal::State_Connect_And_Move_To_Game_State
  888. ========================
  889. */
  890. bool idSessionLocal::State_Connect_And_Move_To_Game_State() {
  891. return HandleConnectAndMoveToLobby( GetGameStateLobby() );
  892. }
  893. /*
  894. ========================
  895. idSessionLocal::State_InGame
  896. ========================
  897. */
  898. bool idSessionLocal::State_InGame() {
  899. return HandlePackets();
  900. }
  901. /*
  902. ========================
  903. idSessionLocal::State_Loading
  904. ========================
  905. */
  906. bool idSessionLocal::State_Loading() {
  907. HandlePackets();
  908. if ( !GetActingGameStateLobby().loaded ) {
  909. return false;
  910. }
  911. SetVoiceGroupsToTeams();
  912. if ( GetActingGameStateLobby().IsHost() ) {
  913. bool everyoneLoaded = true;
  914. for ( int p = 0; p < GetActingGameStateLobby().peers.Num(); p++ ) {
  915. idLobby::peer_t & peer = GetActingGameStateLobby().peers[p];
  916. if ( !peer.IsConnected() ) {
  917. continue; // We don't care about peers that aren't connected as a game session
  918. }
  919. if ( !peer.loaded ) {
  920. everyoneLoaded = false;
  921. continue; // Don't waste time sending resources to a peer who hasn't loaded the map yet
  922. }
  923. if ( GetActingGameStateLobby().SendResources( p ) ) {
  924. everyoneLoaded = false;
  925. // if client is taking a LONG time to load up - give them the boot: they're just holding up the lunch line. Useful for loose assets playtesting.
  926. int time = Sys_Milliseconds();
  927. int maxLoadTime = net_maxLoadResourcesTimeInSeconds.GetInteger();
  928. if ( maxLoadTime > 0 && peer.startResourceLoadTime + SEC2MS( maxLoadTime ) < time ) {
  929. NET_VERBOSERESOURCE_PRINT( "NET: dropping client %i - %s because they took too long to load resources.\n Check 'net_maxLoadResourcesTimeInSeconds' to adjust the time allowed.\n", p, GetPeerName( p ) );
  930. GetActingGameStateLobby().DisconnectPeerFromSession( p );
  931. continue;
  932. }
  933. }
  934. }
  935. if ( !everyoneLoaded ) {
  936. return false;
  937. }
  938. } else {
  939. // not sure how we got there, but we won't be receiving anything that could get us out of this state anymore
  940. // possible step towards fixing the join stalling/disconnect problems
  941. if ( GetActingGameStateLobby().peers.Num() == 0 ) {
  942. NET_VERBOSE_PRINT( "NET: no peers in idSessionLocal::State_Loading - giving up\n" );
  943. MoveToMainMenu();
  944. }
  945. // need at least a peer with a real connection
  946. bool haveOneGoodPeer = false;
  947. for ( int p = 0; p < GetActingGameStateLobby().peers.Num(); p++ ) {
  948. if ( GetActingGameStateLobby().peers[p].IsConnected() ) {
  949. haveOneGoodPeer = true;
  950. break;
  951. }
  952. }
  953. if ( !haveOneGoodPeer ) {
  954. NET_VERBOSE_PRINT( "NET: no good peers in idSessionLocal::State_Loading - giving up\n" );
  955. MoveToMainMenu();
  956. }
  957. return false;
  958. }
  959. GetActingGameStateLobby().ResetBandwidthStats();
  960. // if we got here then we're the host and everyone indicated map load finished
  961. NET_VERBOSE_PRINT( "NET: (loading) Starting Game\n" );
  962. SetState( STATE_INGAME ); // NOTE - Only the host is in-game at this point, all peers will start becoming in-game when they receive their first full snap
  963. return true;
  964. }
  965. /*
  966. ========================
  967. idSessionLocal::State_Busy
  968. ========================
  969. */
  970. bool idSessionLocal::State_Busy() {
  971. idLobby * activeLobby = GetActivePlatformLobby();
  972. if ( activeLobby == NULL ) {
  973. idLib::Warning("No active session lobby when idSessionLocal::State_Busy called");
  974. return false;
  975. }
  976. if ( activeLobby->bandwidthChallengeFinished ) {
  977. // Start loading
  978. NET_VERBOSE_PRINT( "NET: Bandwidth test finished - Start loading\n" );
  979. StartLoading();
  980. }
  981. return HandlePackets();
  982. }
  983. /*
  984. ========================
  985. idSessionLocal::VerifySnapshotInitialState
  986. ========================
  987. */
  988. void idSessionLocal::VerifySnapshotInitialState() {
  989. // Verify that snapshot state is reset
  990. for ( int p = 0; p < GetActingGameStateLobby().peers.Num(); p++ ) {
  991. if ( !GetActingGameStateLobby().peers[p].IsConnected() ) {
  992. assert( GetActingGameStateLobby().peers[p].snapProc == NULL );
  993. continue;
  994. }
  995. assert( GetActingGameStateLobby().peers[p].snapProc != NULL );
  996. if ( !verify( GetActingGameStateLobby().peers[p].needToSubmitPendingSnap == false ) ) {
  997. idLib::Error( "Invalid needToSubmitPendingSnap state\n" );
  998. }
  999. if ( !verify( GetActingGameStateLobby().peers[p].snapProc->HasPendingSnap() == false ) ) {
  1000. idLib::Error( "Invalid HasPendingSnap state\n" );
  1001. }
  1002. if ( !verify( GetActingGameStateLobby().peers[p].snapProc->GetSnapSequence() == idSnapshotProcessor::INITIAL_SNAP_SEQUENCE ) ) {
  1003. idLib::Error( "Invalid INITIAL_SNAP_SEQUENCE state %d for peer %d \n", GetActingGameStateLobby().peers[p].snapProc->GetSnapSequence(), p );
  1004. }
  1005. if ( !verify( GetActingGameStateLobby().peers[p].snapProc->GetBaseSequence() == -1 ) ) {
  1006. idLib::Error( "Invalid GetBaseSequence state\n" );
  1007. }
  1008. }
  1009. }
  1010. /*
  1011. ========================
  1012. idSessionLocal::State_Party_Lobby_Host
  1013. ========================
  1014. */
  1015. bool idSessionLocal::State_Party_Lobby_Host() {
  1016. HandleVoiceRestrictionDialog();
  1017. return HandlePackets();
  1018. }
  1019. /*
  1020. ========================
  1021. idSessionLocal::State_Game_Lobby_Host
  1022. ========================
  1023. */
  1024. bool idSessionLocal::State_Game_Lobby_Host() {
  1025. HandleVoiceRestrictionDialog();
  1026. return HandlePackets();
  1027. }
  1028. /*
  1029. ========================
  1030. idSessionLocal::State_Game_State_Lobby_Host
  1031. ========================
  1032. */
  1033. bool idSessionLocal::State_Game_State_Lobby_Host() {
  1034. HandleVoiceRestrictionDialog();
  1035. if ( waitingOnGameStateMembersToLeaveTime != 0 ) {
  1036. const int MAX_LEAVE_WAIT_TIME_IN_SECONDS = 5;
  1037. const bool forceDisconnectMembers = ( Sys_Milliseconds() - waitingOnGameStateMembersToLeaveTime ) > MAX_LEAVE_WAIT_TIME_IN_SECONDS * 1000;
  1038. // Check to see if all peers have finally left
  1039. if ( GetGameStateLobby().GetNumConnectedPeers() == 0 || forceDisconnectMembers ) {
  1040. //
  1041. // All peers left, we can stop waiting
  1042. //
  1043. waitingOnGameStateMembersToLeaveTime = 0;
  1044. assert( !GetGameLobby().IsPeer() );
  1045. if ( GetGameLobby().IsHost() ) {
  1046. // If we aren't a dedicated game state host, then drop back to the game lobby as host
  1047. GetGameStateLobby().Shutdown();
  1048. SetState( STATE_GAME_LOBBY_HOST );
  1049. } else {
  1050. // A dedicated game state host will remain in State_Game_State_Lobby_Host mode while waiting for another set of users to join
  1051. // DEDICATED_SERVER_FIXME: Notify master server we can server another game now
  1052. GetGameStateLobby().DisconnectAllPeers();
  1053. }
  1054. }
  1055. } else {
  1056. // When all the players from the game lobby are in the game state lobby, StartLoading
  1057. if ( GetGameLobby().IsHost() ) {
  1058. if ( GetGameStateLobby().GetNumLobbyUsers() == GetGameLobby().GetNumLobbyUsers() ) {
  1059. waitingOnGameStateMembersToJoinTime = 0;
  1060. StartLoading();
  1061. }
  1062. } else {
  1063. // The dedicated server host relies on the game host to say when all users are in
  1064. if ( GetGameStateLobby().startLoadingFromHost ) {
  1065. GetGameStateLobby().startLoadingFromHost = false;
  1066. StartLoading();
  1067. }
  1068. }
  1069. }
  1070. return HandlePackets();
  1071. }
  1072. /*
  1073. ========================
  1074. idSessionLocal::State_Party_Lobby_Peer
  1075. ========================
  1076. */
  1077. bool idSessionLocal::State_Party_Lobby_Peer() {
  1078. HandleVoiceRestrictionDialog();
  1079. return HandlePackets();
  1080. }
  1081. /*
  1082. ========================
  1083. idSessionLocal::State_Game_Lobby_Peer
  1084. ========================
  1085. */
  1086. bool idSessionLocal::State_Game_Lobby_Peer() {
  1087. HandleVoiceRestrictionDialog();
  1088. bool saving = false;
  1089. idPlayerProfile * profile = GetProfileFromMasterLocalUser();
  1090. if ( profile != NULL && ( profile->GetState() == idPlayerProfile::SAVING || profile->GetRequestedState() == idPlayerProfile::SAVE_REQUESTED ) ) {
  1091. saving = true;
  1092. }
  1093. if ( GetActingGameStateLobby().startLoadingFromHost && !saving ) {
  1094. common->Dialog().ClearDialog( GDM_HOST_RETURNED_TO_LOBBY );
  1095. common->Dialog().ClearDialog( GDM_HOST_RETURNED_TO_LOBBY_STATS_DROPPED );
  1096. VerifySnapshotInitialState();
  1097. // Set loading flag back to false
  1098. GetActingGameStateLobby().startLoadingFromHost = false;
  1099. // Set state to loading
  1100. SetState( STATE_LOADING );
  1101. loadingID++;
  1102. return true;
  1103. }
  1104. return HandlePackets();
  1105. }
  1106. /*
  1107. ========================
  1108. idSessionLocal::State_Game_State_Lobby_Peer
  1109. ========================
  1110. */
  1111. bool idSessionLocal::State_Game_State_Lobby_Peer() {
  1112. // We are in charge of telling the dedicated host that all our members are in
  1113. if ( GetGameLobby().IsHost() && waitingOnGameStateMembersToJoinTime != 0 ) {
  1114. int foundMembers = 0;
  1115. for ( int i = 0; i < GetGameLobby().GetNumLobbyUsers(); i++ ) {
  1116. if ( GetGameStateLobby().GetLobbyUserByID( GetGameLobby().GetLobbyUser( i )->lobbyUserID, true ) != NULL ) {
  1117. foundMembers++;
  1118. }
  1119. }
  1120. // Give all of our game members 10 seconds to join, otherwise start without them
  1121. const int MAX_JOIN_WAIT_TIME_IN_SECONDS = 10;
  1122. const bool forceStart = ( Sys_Milliseconds() - waitingOnGameStateMembersToJoinTime ) > MAX_JOIN_WAIT_TIME_IN_SECONDS * 1000;
  1123. if ( foundMembers == GetGameLobby().GetNumLobbyUsers() || forceStart ) {
  1124. byte buffer[ idPacketProcessor::MAX_PACKET_SIZE ];
  1125. idBitMsg msg( buffer, sizeof( buffer ) );
  1126. // Write match paramaters to the game state host, and tell him to start
  1127. GetGameLobby().parms.Write( msg );
  1128. // Tell the game state lobby host we are ready
  1129. GetGameStateLobby().QueueReliableMessage( GetGameStateLobby().host, idLobby::RELIABLE_START_MATCH_GAME_LOBBY_HOST, msg.GetReadData(), msg.GetSize() );
  1130. waitingOnGameStateMembersToJoinTime = 0;
  1131. }
  1132. }
  1133. return State_Game_Lobby_Peer();
  1134. }
  1135. /*
  1136. ========================
  1137. idSessionLocal::~idSession
  1138. ========================
  1139. */
  1140. idSession::~idSession() {
  1141. delete signInManager;
  1142. signInManager = NULL;
  1143. delete saveGameManager;
  1144. saveGameManager = NULL;
  1145. delete dedicatedServerSearch;
  1146. dedicatedServerSearch = NULL;
  1147. }
  1148. idCVar net_verbose( "net_verbose", "0", CVAR_BOOL, "Print a bunch of message about the network session" );
  1149. idCVar net_verboseResource( "net_verboseResource", "0", CVAR_BOOL, "Prints a bunch of message about network resources" );
  1150. idCVar net_verboseReliable( "net_verboseReliable", "0", CVAR_BOOL, "Prints the more spammy messages about reliable network msgs" );
  1151. idCVar si_splitscreen( "si_splitscreen", "0", CVAR_INTEGER, "force splitscreen" );
  1152. idCVar net_forceLatency( "net_forceLatency", "0", CVAR_INTEGER, "Simulate network latency (milliseconds round trip time - applied equally on the receive and on the send)" );
  1153. idCVar net_forceDrop( "net_forceDrop", "0", CVAR_INTEGER, "Percentage chance of simulated network packet loss" );
  1154. idCVar net_forceUpstream( "net_forceUpstream", "0", CVAR_FLOAT, "Force a maximum upstream in kB/s (256kbps <-> 32kB/s)" ); // I would much rather deal in kbps but most of the code is written in bytes ..
  1155. idCVar net_forceUpstreamQueue( "net_forceUpstreamQueue", "64", CVAR_INTEGER, "How much data is queued when enforcing upstream (in kB)" );
  1156. idCVar net_verboseSimulatedTraffic( "net_verboseSimulatedTraffic", "0", CVAR_BOOL, "Print some stats about simulated traffic (net_force* cvars)" );
  1157. /*
  1158. ========================
  1159. idSessionLocal::Initialize
  1160. ========================
  1161. */
  1162. void idSessionLocal::Initialize() {
  1163. }
  1164. /*
  1165. ========================
  1166. idSessionLocal::Shutdown
  1167. ========================
  1168. */
  1169. void idSessionLocal::Shutdown() {
  1170. }
  1171. /*
  1172. ========================
  1173. idSession interface semi-common between platforms (#ifdef's in sys_session_local.cpp)
  1174. ========================
  1175. */
  1176. idCVar com_deviceZeroOverride( "com_deviceZeroOverride", "-1", CVAR_INTEGER, "change input routing for device 0 to poll a different device" );
  1177. idCVar mp_bot_input_override( "mp_bot_input_override", "-1", CVAR_INTEGER, "Override local input routing for bot control" );
  1178. /*
  1179. ========================
  1180. idSessionLocal::GetInputRouting
  1181. This function sets up inputRouting to be a mapping from inputDevice index to session user index.
  1182. ========================
  1183. */
  1184. int idSessionLocal::GetInputRouting( int inputRouting[ MAX_INPUT_DEVICES ] ) {
  1185. int numLocalUsers = 0;
  1186. for ( int i = 0; i < MAX_INPUT_DEVICES; i++ ) {
  1187. inputRouting[i] = -1;
  1188. }
  1189. for ( int i = 0; i < GetActingGameStateLobby().GetNumLobbyUsers(); i++ ) {
  1190. if ( GetActingGameStateLobby().IsSessionUserIndexLocal( i ) ) {
  1191. // Find the local user that this session user maps to
  1192. const idLocalUser * localUser = GetActingGameStateLobby().GetLocalUserFromLobbyUserIndex( i );
  1193. if ( localUser != NULL ) {
  1194. int localDevice = localUser->GetInputDevice();
  1195. if ( localDevice == 0 && com_deviceZeroOverride.GetInteger() > 0 ) {
  1196. localDevice = com_deviceZeroOverride.GetInteger();
  1197. }
  1198. assert( localDevice < MAX_INPUT_DEVICES );
  1199. // Route the input device that this local user is mapped to
  1200. assert( inputRouting[localDevice] == -1 ); // Make sure to only initialize each entry once
  1201. inputRouting[localDevice] = i;
  1202. if ( mp_bot_input_override.GetInteger() >= 0 ) {
  1203. inputRouting[localDevice] = mp_bot_input_override.GetInteger();
  1204. }
  1205. numLocalUsers++;
  1206. }
  1207. }
  1208. }
  1209. // For testing swapping controllers
  1210. if ( si_splitscreen.GetInteger() == 2 && numLocalUsers == 2 ) {
  1211. SwapValues( inputRouting[0], inputRouting[1] );
  1212. }
  1213. return numLocalUsers;
  1214. }
  1215. /*
  1216. ========================
  1217. idSessionLocal::EndMatch
  1218. EndMatch is meant for the host to cleanly end a match and return to the lobby page
  1219. ========================
  1220. */
  1221. void idSessionLocal::EndMatch( bool premature /*=false*/ ) {
  1222. if ( verify( GetActingGameStateLobby().IsHost() ) ) {
  1223. // Host quits back to game lobby, and will notify peers internally to do the same
  1224. EndMatchInternal( premature );
  1225. }
  1226. }
  1227. /*
  1228. ========================
  1229. idSessionLocal::EndMatch
  1230. this is for when the game is over before we go back to lobby. Need this incase the host leaves during this time
  1231. ========================
  1232. */
  1233. void idSessionLocal::MatchFinished( ) {
  1234. if ( verify( GetActingGameStateLobby().IsHost() ) ) {
  1235. // host is putting up end game stats make sure other peers know and clear migration data
  1236. MatchFinishedInternal();
  1237. }
  1238. }
  1239. /*
  1240. ========================
  1241. idSessionLocal::QuitMatch
  1242. QuitMatch is considered a premature ending of a match, and does the right thing depending on whether the host or peer is quitting
  1243. ========================
  1244. */
  1245. void idSessionLocal::QuitMatch() {
  1246. if ( GetActingGameStateLobby().IsHost() && !MatchTypeIsRanked( GetActingGameStateLobby().parms.matchFlags ) ) {
  1247. EndMatch( true ); // When host quits private match, takes members back to game lobby
  1248. } else {
  1249. // Quitting a public match (or not being a host) before it ends takes you to an empty party lobby
  1250. CreatePartyLobby( GetActingGameStateLobby().parms );
  1251. }
  1252. }
  1253. /*
  1254. ========================
  1255. idSessionLocal::QuitMatchToTitle
  1256. QuitMatchToTitle will forcefully quit the match and return to the title screen.
  1257. ========================
  1258. */
  1259. void idSessionLocal::QuitMatchToTitle() {
  1260. MoveToMainMenu();
  1261. }
  1262. /*
  1263. ========================
  1264. idSessionLocal::ClearMigrationState
  1265. ========================
  1266. */
  1267. void idSessionLocal::ClearMigrationState() {
  1268. // We are ending the match without migration, so clear that state
  1269. GetPartyLobby().ResetAllMigrationState();
  1270. GetGameLobby().ResetAllMigrationState();
  1271. }
  1272. /*
  1273. ========================
  1274. idSessionLocal::EndMatchInternal
  1275. ========================
  1276. */
  1277. void idSessionLocal::EndMatchInternal( bool premature/*=false*/ ) {
  1278. assert( GetGameStateLobby().IsLobbyActive() == net_useGameStateLobby.GetBool() );
  1279. ClearVoiceGroups();
  1280. for ( int p = 0; p < GetActingGameStateLobby().peers.Num(); p++ ) {
  1281. // If we are the host, increment the session ID. The client will use a rolling check to catch it
  1282. if ( GetActingGameStateLobby().IsHost() ) {
  1283. if ( GetActingGameStateLobby().peers[p].IsConnected() ) {
  1284. if ( GetActingGameStateLobby().peers[p].packetProc != NULL ) {
  1285. GetActingGameStateLobby().peers[p].packetProc->VerifyEmptyReliableQueue( idLobby::RELIABLE_GAME_DATA, idLobby::RELIABLE_DUMMY_MSG );
  1286. }
  1287. GetActingGameStateLobby().peers[p].sessionID = GetActingGameStateLobby().IncrementSessionID( GetActingGameStateLobby().peers[p].sessionID );
  1288. }
  1289. }
  1290. GetActingGameStateLobby().peers[p].ResetMatchData();
  1291. }
  1292. GetActingGameStateLobby().snapDeltaAckQueue.Clear();
  1293. GetActingGameStateLobby().loaded = false;
  1294. gameLobbyWasCoalesced = false; // Reset this back to false. We use this so the lobby code doesn't randomly choose a map when we coalesce
  1295. numFullSnapsReceived = 0;
  1296. ClearMigrationState();
  1297. if ( GetActingGameStateLobby().IsLobbyActive() && ( GetActingGameStateLobby().GetMatchParms().matchFlags & MATCH_REQUIRE_PARTY_LOBBY ) ) {
  1298. // All peers need to remove disconnected users to stay in sync
  1299. GetActingGameStateLobby().CompactDisconnectedUsers();
  1300. // Go back to the game lobby
  1301. if ( GetActingGameStateLobby().IsHost() ) {
  1302. // We want the game state host to go back to STATE_GAME_STATE_LOBBY_HOST, so he can wait on all his game state peers to leave
  1303. SetState( GetGameStateLobby().IsHost() ? STATE_GAME_STATE_LOBBY_HOST : STATE_GAME_LOBBY_HOST ); // We want the dedicated host to go back to STATE_GAME_STATE_LOBBY_HOST
  1304. } else {
  1305. SetState( STATE_GAME_LOBBY_PEER );
  1306. }
  1307. } else {
  1308. SetState( STATE_IDLE );
  1309. }
  1310. if ( GetActingGameStateLobby().IsHost() ) {
  1311. // Send a reliable msg to all peers to also "EndMatch"
  1312. for ( int p = 0; p < GetActingGameStateLobby().peers.Num(); p++ ) {
  1313. GetActingGameStateLobby().QueueReliableMessage( p, premature ? idLobby::RELIABLE_ENDMATCH_PREMATURE : idLobby::RELIABLE_ENDMATCH );
  1314. }
  1315. } else if ( premature ) {
  1316. // Notify client that host left early and thats why we are back in the lobby
  1317. const bool stats = MatchTypeHasStats( GetActingGameStateLobby().GetMatchParms().matchFlags ) && ( GetFlushedStats() == false );
  1318. common->Dialog().AddDialog( stats ? GDM_HOST_RETURNED_TO_LOBBY_STATS_DROPPED : GDM_HOST_RETURNED_TO_LOBBY, DIALOG_ACCEPT, NULL, NULL, false, __FUNCTION__, __LINE__, true );
  1319. }
  1320. if ( GetGameStateLobby().IsLobbyActive() ) {
  1321. if ( GetGameStateLobby().IsHost() ) {
  1322. // As a game state host, keep the lobby around, so we can make sure we know when everyone leaves (which means they got the reliable msg to EndMatch)
  1323. waitingOnGameStateMembersToLeaveTime = Sys_Milliseconds();
  1324. } else if ( GetGameStateLobby().IsPeer() ) {
  1325. // Game state lobby peers should disconnect now
  1326. GetGameStateLobby().Shutdown();
  1327. }
  1328. }
  1329. }
  1330. /*
  1331. ========================
  1332. idSessionLocal::MatchFinishedInternal
  1333. ========================
  1334. */
  1335. void idSessionLocal::MatchFinishedInternal() {
  1336. ClearMigrationState();
  1337. if ( GetActingGameStateLobby().IsHost() ) {
  1338. // Send a reliable msg to all peers to also "EndMatch"
  1339. for ( int p = 0; p < GetActingGameStateLobby().peers.Num(); p++ ) {
  1340. GetActingGameStateLobby().QueueReliableMessage( p, idLobby::RELIABLE_MATCHFINISHED );
  1341. }
  1342. }
  1343. }
  1344. /*
  1345. ========================
  1346. idSessionLocal::EndMatchForMigration
  1347. ========================
  1348. */
  1349. void idSessionLocal::EndMatchForMigration() {
  1350. ClearVoiceGroups();
  1351. }
  1352. /*
  1353. ========================
  1354. idSessionLocal::ShouldHavePartyLobby
  1355. ========================
  1356. */
  1357. bool idSessionLocal::ShouldHavePartyLobby() {
  1358. if ( GetActivePlatformLobby() == NULL ) {
  1359. return false;
  1360. }
  1361. idMatchParameters & parms = GetActivePlatformLobby()->parms;
  1362. int flags = parms.matchFlags;
  1363. // Don't we always have a party lobby if we're online? At least in Doom 3?
  1364. return MatchTypeIsOnline( flags ) && ( ( flags & MATCH_PARTY_INVITE_PLACEHOLDER ) == 0 );
  1365. }
  1366. /*
  1367. ========================
  1368. idSessionLocal::ValidateLobbies
  1369. Determines if any of the session instances need to become the host
  1370. ========================
  1371. */
  1372. void idSessionLocal::ValidateLobbies() {
  1373. if ( localState == STATE_PRESS_START || localState == STATE_IDLE ) {
  1374. // At press start or main menu, don't do anything
  1375. return;
  1376. }
  1377. if ( GetActivePlatformLobby() == NULL ) {
  1378. // If we're in between lobbies, don't do anything yet (the state transitioning code will handle error cases)
  1379. return;
  1380. }
  1381. // Validate lobbies that should be alive and active
  1382. if ( ShouldHavePartyLobby() && GetState() >= idSession::PARTY_LOBBY ) {
  1383. ValidateLobby( GetPartyLobby() );
  1384. }
  1385. if ( GetState() >= idSession::GAME_LOBBY && !net_headlessServer.GetBool() ) {
  1386. ValidateLobby( GetGameLobby() );
  1387. }
  1388. }
  1389. /*
  1390. ========================
  1391. idSessionLocal::ValidateLobby
  1392. ========================
  1393. */
  1394. void idSessionLocal::ValidateLobby( idLobby & lobby ) {
  1395. if ( lobby.lobbyBackend == NULL || lobby.lobbyBackend->GetState() == idLobbyBackend::STATE_FAILED || lobby.GetState() == idLobby::STATE_FAILED ) {
  1396. NET_VERBOSE_PRINT( "NET: ValidateLobby: FAILED (lobbyType = %i, state = %s)\n", lobby.lobbyType, stateToString[ localState ] );
  1397. if ( lobby.failedReason == idLobby::FAILED_MIGRATION_CONNECT_FAILED || lobby.failedReason == idLobby::FAILED_CONNECT_FAILED ) {
  1398. MoveToMainMenu();
  1399. common->Dialog().AddDialog( GDM_INVALID_INVITE, DIALOG_ACCEPT, NULL, NULL, false ); // The game session no longer exists
  1400. } else {
  1401. // If the lobbyBackend goes bad under our feet for no known reason, assume we lost connection to the back end service
  1402. MoveToMainMenu();
  1403. common->Dialog().ClearDialogs( true );
  1404. common->Dialog().AddDialog( GDM_CONNECTION_LOST, DIALOG_ACCEPT, NULL, NULL, false ); // Lost connection to XBox LIVE
  1405. }
  1406. }
  1407. }
  1408. /*
  1409. ========================
  1410. idSessionLocal::Pump
  1411. ========================
  1412. */
  1413. void idSessionLocal::Pump() {
  1414. SCOPED_PROFILE_EVENT( "Session::Pump" );
  1415. static int lastPumpTime = -1;
  1416. const int time = Sys_Milliseconds();
  1417. const int elapsedPumpSeconds = ( time - lastPumpTime ) / 1000;
  1418. if ( lastPumpTime != -1 && elapsedPumpSeconds > 2 ) {
  1419. idLib::Warning( "idSessionLocal::Pump was not called for %i seconds", elapsedPumpSeconds );
  1420. }
  1421. lastPumpTime = time;
  1422. if ( net_migrateHost.GetInteger() >= 0 ) {
  1423. if ( net_migrateHost.GetInteger() <= 2 ) {
  1424. if ( net_migrateHost.GetInteger() == 0 ) {
  1425. GetPartyLobby().PickNewHost( true, true );
  1426. } else {
  1427. GetGameLobby().PickNewHost( true, true );
  1428. }
  1429. } else {
  1430. GetPartyLobby().PickNewHost( true, true );
  1431. GetGameLobby().PickNewHost( true, true );
  1432. }
  1433. net_migrateHost.SetInteger( -1 );
  1434. }
  1435. PlatformPump();
  1436. if ( HasAchievementSystem() ) {
  1437. GetAchievementSystem().Pump();
  1438. }
  1439. // Send any voice packets if it's time
  1440. SendVoiceAudio();
  1441. bool shouldContinue = true;
  1442. while ( shouldContinue ) {
  1443. // Each iteration, validate the session instances
  1444. ValidateLobbies();
  1445. // Pump state
  1446. shouldContinue = HandleState();
  1447. // Pump lobbies
  1448. PumpLobbies();
  1449. }
  1450. if ( GetPartyLobby().lobbyBackend != NULL ) {
  1451. // Make sure game properties aren't set on the lobbyBackend if we aren't in a game lobby.
  1452. // This is so we show up properly in search results in Play with Friends option
  1453. GetPartyLobby().lobbyBackend->SetInGame( GetGameLobby().IsLobbyActive() );
  1454. // Temp location
  1455. UpdateMasterUserHeadsetState();
  1456. }
  1457. // Do some last minute checks, make sure everything about the current state and lobbyBackend state is valid, otherwise, take action
  1458. ValidateLobbies();
  1459. GetActingGameStateLobby().UpdateSnaps();
  1460. idLobby * activeLobby = GetActivePlatformLobby();
  1461. // Pump pings for the active lobby
  1462. if ( activeLobby != NULL ) {
  1463. activeLobby->PumpPings();
  1464. }
  1465. // Pump packet processing for all lobbies
  1466. GetPartyLobby().PumpPackets();
  1467. GetGameLobby().PumpPackets();
  1468. GetGameStateLobby().PumpPackets();
  1469. int currentTime = Sys_Milliseconds();
  1470. const int SHOW_MIGRATING_INFO_IN_SECONDS = 3; // Show for at least this long once we start showing it
  1471. if ( ShouldShowMigratingDialog() ) {
  1472. showMigratingInfoStartTime = currentTime;
  1473. } else if ( showMigratingInfoStartTime > 0 && ( ( currentTime - showMigratingInfoStartTime ) > SHOW_MIGRATING_INFO_IN_SECONDS * 1000 ) ) {
  1474. showMigratingInfoStartTime = 0;
  1475. }
  1476. bool isShowingMigrate = common->Dialog().HasDialogMsg( GDM_MIGRATING, NULL );
  1477. if ( showMigratingInfoStartTime != 0 ) {
  1478. if ( !isShowingMigrate ) {
  1479. common->Dialog().AddDialog( GDM_MIGRATING, DIALOG_WAIT, NULL, NULL, false, "", 0, false, false, true );
  1480. }
  1481. } else if ( isShowingMigrate ) {
  1482. common->Dialog().ClearDialog( GDM_MIGRATING );
  1483. }
  1484. // Update possible pending invite
  1485. UpdatePendingInvite();
  1486. // Check to see if we should coalesce the lobby
  1487. if ( nextGameCoalesceTime != 0 ) {
  1488. if ( GetGameLobby().IsLobbyActive() &&
  1489. GetGameLobby().IsHost() &&
  1490. GetState() == idSession::GAME_LOBBY &&
  1491. GetPartyLobby().GetNumLobbyUsers() <= 1 &&
  1492. GetGameLobby().GetNumLobbyUsers() == 1 &&
  1493. MatchTypeIsRanked( GetGameLobby().parms.matchFlags ) &&
  1494. Sys_Milliseconds() > nextGameCoalesceTime ) {
  1495. // If the player doesn't care about the mode or map,
  1496. // make sure the search is broadened.
  1497. idMatchParameters newGameParms = GetGameLobby().parms;
  1498. newGameParms.gameMap = GAME_MAP_RANDOM;
  1499. // Assume that if the party lobby's mode is random,
  1500. // the player chose "Quick Match" and doesn't care about the mode.
  1501. // If the player chose "Find Match" and a specific mode,
  1502. // the party lobby mode will be set to non-random.
  1503. if ( GetPartyLobby().parms.gameMode == GAME_MODE_RANDOM ) {
  1504. newGameParms.gameMode = GAME_MODE_RANDOM;
  1505. }
  1506. FindOrCreateMatch( newGameParms );
  1507. gameLobbyWasCoalesced = true; // Remember that this round was coalesced. We so this so main menu doesn't randomize the map, which looks odd
  1508. nextGameCoalesceTime = 0;
  1509. }
  1510. }
  1511. }
  1512. /*
  1513. ========================
  1514. idSessionLocal::ProcessSnapAckQueue
  1515. ========================
  1516. */
  1517. void idSessionLocal::ProcessSnapAckQueue() {
  1518. if ( GetActingGameStateLobby().IsLobbyActive() ) {
  1519. GetActingGameStateLobby().ProcessSnapAckQueue();
  1520. }
  1521. }
  1522. /*
  1523. ========================
  1524. idSessionLocal::UpdatePendingInvite
  1525. ========================
  1526. */
  1527. void idSessionLocal::UpdatePendingInvite() {
  1528. if ( pendingInviteMode == PENDING_INVITE_NONE ) {
  1529. return; // No pending invite
  1530. }
  1531. idLocalUser * masterLocalUser = signInManager->GetMasterLocalUser();
  1532. if ( masterLocalUser == NULL && signInManager->IsDeviceBeingRegistered( pendingInviteDevice ) ) {
  1533. idLib::Printf( "masterLocalUser == NULL\n" );
  1534. return; // Waiting on master to sign in to continue with invite
  1535. }
  1536. const bool wasFromInvite = pendingInviteMode == PENDING_INVITE_WAITING; // Remember if this was a real invite, or a self invitation (matters when lobby is invite only)
  1537. // At this point, the invitee should be ready
  1538. pendingInviteMode = PENDING_INVITE_NONE;
  1539. if ( masterLocalUser == NULL || masterLocalUser->GetInputDevice() != pendingInviteDevice || !masterLocalUser->IsOnline() ) {
  1540. idLib::Printf( "ignoring invite - master local user is not setup properly\n" );
  1541. return; // If there is no master, if the invitee is not online, or different than the current master, then ignore invite
  1542. }
  1543. // Clear any current dialogs, as we're going into a state which will be unstable for any current dialogs.
  1544. // Do we want to throw an assert if a dialog is currently up?
  1545. common->Dialog().ClearDialogs( true );
  1546. // Everything looks good, let's join the party
  1547. ConnectAndMoveToLobby( GetPartyLobby(), pendingInviteConnectInfo, wasFromInvite );
  1548. }
  1549. /*
  1550. ========================
  1551. idSessionLocal::HandleState
  1552. ========================
  1553. */
  1554. bool idSessionLocal::HandleState() {
  1555. // Handle individual lobby states
  1556. GetPartyLobby().Pump();
  1557. GetGameLobby().Pump();
  1558. GetGameStateLobby().Pump();
  1559. // Let IsHost be authoritative on the qualification of peer/host state types
  1560. if ( GetPartyLobby().IsHost() && localState == STATE_PARTY_LOBBY_PEER ) {
  1561. SetState( STATE_PARTY_LOBBY_HOST );
  1562. } else if ( GetPartyLobby().IsPeer() && localState == STATE_PARTY_LOBBY_HOST ) {
  1563. SetState( STATE_PARTY_LOBBY_PEER );
  1564. }
  1565. // Let IsHost be authoritative on the qualification of peer/host state types
  1566. if ( GetGameLobby().IsHost() && localState == STATE_GAME_LOBBY_PEER ) {
  1567. SetState( STATE_GAME_LOBBY_HOST );
  1568. } else if ( GetGameLobby().IsPeer() && localState == STATE_GAME_LOBBY_HOST ) {
  1569. SetState( STATE_GAME_LOBBY_PEER );
  1570. }
  1571. switch ( localState ) {
  1572. case STATE_PRESS_START: return false;
  1573. case STATE_IDLE: HandlePackets(); return false; // Call handle packets, since packets from old sessions could still be in flight, which need to be emptied
  1574. case STATE_PARTY_LOBBY_HOST: return State_Party_Lobby_Host();
  1575. case STATE_PARTY_LOBBY_PEER: return State_Party_Lobby_Peer();
  1576. case STATE_GAME_LOBBY_HOST: return State_Game_Lobby_Host();
  1577. case STATE_GAME_LOBBY_PEER: return State_Game_Lobby_Peer();
  1578. case STATE_GAME_STATE_LOBBY_HOST: return State_Game_State_Lobby_Host();
  1579. case STATE_GAME_STATE_LOBBY_PEER: return State_Game_State_Lobby_Peer();
  1580. case STATE_LOADING: return State_Loading();
  1581. case STATE_INGAME: return State_InGame();
  1582. case STATE_CREATE_AND_MOVE_TO_PARTY_LOBBY: return State_Create_And_Move_To_Party_Lobby();
  1583. case STATE_CREATE_AND_MOVE_TO_GAME_LOBBY: return State_Create_And_Move_To_Game_Lobby();
  1584. case STATE_CREATE_AND_MOVE_TO_GAME_STATE_LOBBY: return State_Create_And_Move_To_Game_State_Lobby();
  1585. case STATE_FIND_OR_CREATE_MATCH: return State_Find_Or_Create_Match();
  1586. case STATE_CONNECT_AND_MOVE_TO_PARTY: return State_Connect_And_Move_To_Party();
  1587. case STATE_CONNECT_AND_MOVE_TO_GAME: return State_Connect_And_Move_To_Game();
  1588. case STATE_CONNECT_AND_MOVE_TO_GAME_STATE: return State_Connect_And_Move_To_Game_State();
  1589. case STATE_BUSY: return State_Busy();
  1590. default:
  1591. idLib::Error( "HandleState: Unknown state in idSessionLocal" );
  1592. }
  1593. }
  1594. /*
  1595. ========================
  1596. idSessionLocal::GetState
  1597. ========================
  1598. */
  1599. idSessionLocal::sessionState_t idSessionLocal::GetState() const {
  1600. // Convert our internal state to one of the external states
  1601. switch ( localState ) {
  1602. case STATE_PRESS_START: return PRESS_START;
  1603. case STATE_IDLE: return IDLE;
  1604. case STATE_PARTY_LOBBY_HOST: return PARTY_LOBBY;
  1605. case STATE_PARTY_LOBBY_PEER: return PARTY_LOBBY;
  1606. case STATE_GAME_LOBBY_HOST: return GAME_LOBBY;
  1607. case STATE_GAME_LOBBY_PEER: return GAME_LOBBY;
  1608. case STATE_GAME_STATE_LOBBY_HOST: return GAME_LOBBY;
  1609. case STATE_GAME_STATE_LOBBY_PEER: return GAME_LOBBY;
  1610. case STATE_LOADING: return LOADING;
  1611. case STATE_INGAME: return INGAME;
  1612. case STATE_CREATE_AND_MOVE_TO_PARTY_LOBBY: return CONNECTING;
  1613. case STATE_CREATE_AND_MOVE_TO_GAME_LOBBY: return CONNECTING;
  1614. case STATE_CREATE_AND_MOVE_TO_GAME_STATE_LOBBY: return CONNECTING;
  1615. case STATE_FIND_OR_CREATE_MATCH: return SEARCHING;
  1616. case STATE_CONNECT_AND_MOVE_TO_PARTY: return CONNECTING;
  1617. case STATE_CONNECT_AND_MOVE_TO_GAME: return CONNECTING;
  1618. case STATE_CONNECT_AND_MOVE_TO_GAME_STATE: return CONNECTING;
  1619. case STATE_BUSY: return BUSY;
  1620. default: {
  1621. idLib::Error( "GetState: Unknown state in idSessionLocal" );
  1622. }
  1623. };
  1624. }
  1625. const char * idSessionLocal::GetStateString() const {
  1626. static const char * stateToString[] = {
  1627. ASSERT_ENUM_STRING( STATE_PRESS_START, 0 ),
  1628. ASSERT_ENUM_STRING( STATE_IDLE, 1 ),
  1629. ASSERT_ENUM_STRING( STATE_PARTY_LOBBY_HOST, 2 ),
  1630. ASSERT_ENUM_STRING( STATE_PARTY_LOBBY_PEER, 3 ),
  1631. ASSERT_ENUM_STRING( STATE_GAME_LOBBY_HOST, 4 ),
  1632. ASSERT_ENUM_STRING( STATE_GAME_LOBBY_PEER, 5 ),
  1633. ASSERT_ENUM_STRING( STATE_GAME_STATE_LOBBY_HOST, 6 ),
  1634. ASSERT_ENUM_STRING( STATE_GAME_STATE_LOBBY_PEER, 7 ),
  1635. ASSERT_ENUM_STRING( STATE_CREATE_AND_MOVE_TO_PARTY_LOBBY, 8 ),
  1636. ASSERT_ENUM_STRING( STATE_CREATE_AND_MOVE_TO_GAME_LOBBY, 9 ),
  1637. ASSERT_ENUM_STRING( STATE_CREATE_AND_MOVE_TO_GAME_STATE_LOBBY, 10 ),
  1638. ASSERT_ENUM_STRING( STATE_FIND_OR_CREATE_MATCH, 11 ),
  1639. ASSERT_ENUM_STRING( STATE_CONNECT_AND_MOVE_TO_PARTY, 12 ),
  1640. ASSERT_ENUM_STRING( STATE_CONNECT_AND_MOVE_TO_GAME, 13 ),
  1641. ASSERT_ENUM_STRING( STATE_CONNECT_AND_MOVE_TO_GAME_STATE, 14 ),
  1642. ASSERT_ENUM_STRING( STATE_BUSY, 15 ),
  1643. ASSERT_ENUM_STRING( STATE_LOADING, 16 ),
  1644. ASSERT_ENUM_STRING( STATE_INGAME, 17 )
  1645. };
  1646. return stateToString[ localState ];
  1647. }
  1648. // idSession interface
  1649. /*
  1650. ========================
  1651. idSessionLocal::LoadingFinished
  1652. Only called by idCommonLocal::FinalizeMapChange
  1653. ========================
  1654. */
  1655. void idSessionLocal::LoadingFinished() {
  1656. NET_VERBOSE_PRINT( "NET: Loading Finished\n" );
  1657. assert( GetState() == idSession::LOADING );
  1658. common->Dialog().ClearDialog( GDM_VOICE_RESTRICTED );
  1659. GetActingGameStateLobby().loaded = true;
  1660. if ( MatchTypeIsLocal( GetActingGameStateLobby().parms.matchFlags ) ) {
  1661. SetState( STATE_INGAME );
  1662. } else if ( !GetActingGameStateLobby().IsHost() ) { // Tell game host we're done loading
  1663. byte buffer[ idPacketProcessor::MAX_PACKET_SIZE ];
  1664. idBitMsg msg( buffer, sizeof( buffer ) );
  1665. GetActingGameStateLobby().QueueReliableMessage( GetActingGameStateLobby().host, idLobby::RELIABLE_LOADING_DONE, msg.GetReadData(), msg.GetSize() );
  1666. } else {
  1667. SetState( STATE_INGAME );
  1668. }
  1669. SetFlushedStats( false );
  1670. }
  1671. /*
  1672. ========================
  1673. idSessionLocal::SendUsercmds
  1674. ========================
  1675. */
  1676. void idSessionLocal::SendUsercmds( idBitMsg & msg ) {
  1677. if ( localState != STATE_INGAME ) {
  1678. return;
  1679. }
  1680. if ( GetActingGameStateLobby().IsPeer() ) {
  1681. idLobby::peer_t & hostPeer = GetActingGameStateLobby().peers[GetActingGameStateLobby().host];
  1682. // Don't send user cmds if we have unsent packet fragments
  1683. // (This can happen if we have packets to send, but SendAnotherFragment got throttled)
  1684. if ( hostPeer.packetProc->HasMoreFragments() ) {
  1685. idLib::Warning( "NET: Client called SendUsercmds while HasMoreFragments(). Skipping userCmds for this frame." );
  1686. return;
  1687. }
  1688. int sequence = hostPeer.snapProc->GetLastAppendedSequence();
  1689. // Add incoming BPS for QoS
  1690. float incomingBPS = hostPeer.receivedBps;
  1691. if ( hostPeer.receivedBpsIndex != sequence ) {
  1692. incomingBPS = idMath::ClampFloat( 0.0f, static_cast<float>( idLobby::BANDWIDTH_REPORTING_MAX ), hostPeer.packetProc->GetIncomingRateBytes() );
  1693. hostPeer.receivedBpsIndex = sequence;
  1694. hostPeer.receivedBps = incomingBPS;
  1695. }
  1696. uint16 incomingBPS_quantized = idMath::Ftoi( incomingBPS * ( ( BIT( idLobby::BANDWIDTH_REPORTING_BITS ) - 1 ) / idLobby::BANDWIDTH_REPORTING_MAX ) );
  1697. byte buffer[idPacketProcessor::MAX_FINAL_PACKET_SIZE];
  1698. lzwCompressionData_t lzwData;
  1699. idLZWCompressor lzwCompressor( &lzwData );
  1700. lzwCompressor.Start( buffer, sizeof( buffer ) );
  1701. lzwCompressor.WriteAgnostic( sequence );
  1702. lzwCompressor.WriteAgnostic( incomingBPS_quantized );
  1703. lzwCompressor.Write( msg.GetReadData(), msg.GetSize() );
  1704. lzwCompressor.End();
  1705. GetActingGameStateLobby().ProcessOutgoingMsg( GetActingGameStateLobby().host, buffer, lzwCompressor.Length(), false, 0 );
  1706. if ( net_debugBaseStates.GetBool() && sequence < 50 ) {
  1707. idLib::Printf( "NET: Acking snap %d \n", sequence );
  1708. }
  1709. }
  1710. }
  1711. /*
  1712. ========================
  1713. idSessionLocal::SendSnapshot
  1714. ========================
  1715. */
  1716. void idSessionLocal::SendSnapshot( idSnapShot & ss ) {
  1717. for ( int p = 0; p < GetActingGameStateLobby().peers.Num(); p++ ) {
  1718. idLobby::peer_t & peer = GetActingGameStateLobby().peers[p];
  1719. if ( !peer.IsConnected() ) {
  1720. continue;
  1721. }
  1722. if ( !peer.loaded ) {
  1723. continue;
  1724. }
  1725. if ( peer.pauseSnapshots ) {
  1726. continue;
  1727. }
  1728. GetActingGameStateLobby().SendSnapshotToPeer( ss, p );
  1729. }
  1730. }
  1731. /*
  1732. ========================
  1733. idSessionLocal::UpdateSignInManager
  1734. ========================
  1735. */
  1736. void idSessionLocal::UpdateSignInManager() {
  1737. if ( !HasSignInManager() ) {
  1738. return;
  1739. }
  1740. if ( net_headlessServer.GetBool() ) {
  1741. return;
  1742. }
  1743. // FIXME: We need to ask the menu system for this info. Just making a best guess for now
  1744. // (assume we are allowed to join the party as a splitscreen user if we are in the party lobby)
  1745. bool allowJoinParty = ( localState == STATE_PARTY_LOBBY_HOST || localState == STATE_PARTY_LOBBY_PEER ) && GetPartyLobby().state == idLobby::STATE_IDLE;
  1746. bool allowJoinGame = ( localState == STATE_GAME_LOBBY_HOST || localState == STATE_GAME_LOBBY_PEER ) && GetGameLobby().state == idLobby::STATE_IDLE;
  1747. bool eitherLobbyRunning = GetActivePlatformLobby() != NULL && ( GetPartyLobby().IsLobbyActive() || GetGameLobby().IsLobbyActive() );
  1748. bool onlineMatch = eitherLobbyRunning && MatchTypeIsOnline( GetActivePlatformLobby()->parms.matchFlags );
  1749. //=================================================================================
  1750. // Get the number of desired signed in local users depending on what mode we're in.
  1751. //=================================================================================
  1752. int minDesiredUsers = 0;
  1753. int maxDesiredUsers = Max( 1, signInManager->GetNumLocalUsers() );
  1754. if ( si_splitscreen.GetInteger() != 0 ) {
  1755. // For debugging, force 2 splitscreen players
  1756. minDesiredUsers = 2;
  1757. maxDesiredUsers = 2;
  1758. allowJoinGame = true;
  1759. } else if ( onlineMatch || ( eitherLobbyRunning == false ) ) {
  1760. // If this an online game, then only 1 user can join locally.
  1761. // Also, if no sessions are active, remove any extra players.
  1762. maxDesiredUsers = 1;
  1763. } else if ( allowJoinParty || allowJoinGame ) {
  1764. // If we are in the party lobby, allow 2 splitscreen users to join
  1765. maxDesiredUsers = 2;
  1766. }
  1767. // Set the number of desired users
  1768. signInManager->SetDesiredLocalUsers( minDesiredUsers, maxDesiredUsers );
  1769. //=================================================================================
  1770. // Update signin manager
  1771. //=================================================================================
  1772. // Update signin mgr. This manager tracks signed in local users, which the session then uses
  1773. // to determine who should be in the lobby.
  1774. signInManager->Pump();
  1775. // Get the master local user
  1776. idLocalUser * masterUser = signInManager->GetMasterLocalUser();
  1777. if ( onlineMatch && masterUser != NULL && !masterUser->CanPlayOnline() && !masterUser->HasOwnerChanged() ) {
  1778. if ( localState > STATE_IDLE ) {
  1779. // User is still valid, just no longer online
  1780. if ( offlineTransitionTimerStart == 0 ) {
  1781. offlineTransitionTimerStart = Sys_Milliseconds();
  1782. }
  1783. if ( ( Sys_Milliseconds() - offlineTransitionTimerStart ) > net_offlineTransitionThreshold.GetInteger() ) {
  1784. MoveToMainMenu();
  1785. common->Dialog().ClearDialogs();
  1786. common->Dialog().AddDialog( GDM_CONNECTION_LOST, DIALOG_ACCEPT, NULL, NULL, false, "", 0, true );
  1787. }
  1788. }
  1789. return; // Bail out so signInManager->ValidateLocalUsers below doesn't prematurely remove the master user before we can detect loss of connection
  1790. } else {
  1791. offlineTransitionTimerStart = 0;
  1792. }
  1793. // Remove local users (from the signin manager) who aren't allowed to be online if this is an online match.
  1794. // Remove local user (from the signin manager) who are not properly signed into a profile.
  1795. signInManager->ValidateLocalUsers( onlineMatch );
  1796. //=================================================================================
  1797. // Check to see if we need to go to "Press Start"
  1798. //=================================================================================
  1799. // Get the master local user (again, after ValidateOnlineLocalUsers, to make sure he is still valid)
  1800. masterUser = signInManager->GetMasterLocalUser();
  1801. if ( masterUser == NULL ) {
  1802. // If we don't have a master user at all, then we need to be at "Press Start"
  1803. MoveToPressStart( GDM_SP_SIGNIN_CHANGE_POST );
  1804. return;
  1805. } else if ( localState == STATE_PRESS_START ) {
  1806. // If we have a master user, and we are at press start, move to the menu area
  1807. SetState( STATE_IDLE );
  1808. }
  1809. // See if the master user either isn't persistent (but needs to be), OR, if the owner changed
  1810. // RequirePersistentMaster is poorly named, this really means RequireSignedInMaster
  1811. if ( masterUser->HasOwnerChanged() || ( RequirePersistentMaster() && !masterUser->IsProfileReady() ) ) {
  1812. MoveToPressStart( GDM_SP_SIGNIN_CHANGE_POST );
  1813. return;
  1814. }
  1815. //=================================================================================
  1816. // Sync lobby users with the signed in users
  1817. // The initial list of session users are normally determined at connect or create time.
  1818. // These functions allow splitscreen users to join in, or check to see if existing
  1819. // users (including the master) need to be removed.
  1820. //=================================================================================
  1821. GetPartyLobby().SyncLobbyUsersWithLocalUsers( allowJoinParty, onlineMatch );
  1822. GetGameLobby().SyncLobbyUsersWithLocalUsers( allowJoinGame, onlineMatch );
  1823. GetGameStateLobby().SyncLobbyUsersWithLocalUsers( allowJoinGame, onlineMatch );
  1824. }
  1825. /*
  1826. ========================
  1827. idSessionLocal::GetProfileFromMasterLocalUser
  1828. ========================
  1829. */
  1830. idPlayerProfile * idSessionLocal::GetProfileFromMasterLocalUser() {
  1831. idPlayerProfile * profile = NULL;
  1832. idLocalUser * masterUser = signInManager->GetMasterLocalUser();
  1833. if ( masterUser != NULL ) {
  1834. profile = masterUser->GetProfile();
  1835. }
  1836. if ( profile == NULL ) {
  1837. // Whoops
  1838. profile = signInManager->GetDefaultProfile();
  1839. //idLib::Warning( "Returning fake profile until the code is fixed to handle NULL profiles." );
  1840. }
  1841. return profile;
  1842. }
  1843. /*
  1844. ========================
  1845. /*
  1846. ========================
  1847. idSessionLocal::MoveToPressStart
  1848. ========================
  1849. */
  1850. void idSessionLocal::MoveToPressStart( gameDialogMessages_t msg ) {
  1851. if ( localState != STATE_PRESS_START ) {
  1852. MoveToPressStart();
  1853. common->Dialog().ClearDialogs();
  1854. common->Dialog().AddDialog( msg, DIALOG_ACCEPT, NULL, NULL, false, "", 0, true );
  1855. }
  1856. }
  1857. /*
  1858. ========================
  1859. idSessionLocal::GetPeerName
  1860. ========================
  1861. */
  1862. const char * idSessionLocal::GetPeerName( int peerNum ) {
  1863. return GetActingGameStateLobby().GetPeerName( peerNum );
  1864. }
  1865. /*
  1866. ========================
  1867. idSessionLocal::SetState
  1868. ========================
  1869. */
  1870. void idSessionLocal::SetState( state_t newState ) {
  1871. assert( newState < NUM_STATES );
  1872. assert( localState < NUM_STATES );
  1873. verify_array_size( stateToString, NUM_STATES );
  1874. if ( newState == localState ) {
  1875. NET_VERBOSE_PRINT( "NET: SetState: State SAME %s\n", stateToString[ newState ] );
  1876. return;
  1877. }
  1878. // Set the current state
  1879. NET_VERBOSE_PRINT( "NET: SetState: State changing from %s to %s\n", stateToString[ localState ], stateToString[ newState ] );
  1880. if ( localState < STATE_LOADING && newState >= STATE_LOADING ) {
  1881. // Tell lobby instances that the match has started
  1882. StartSessions();
  1883. // Clear certain dialog boxes we don't want to see in-game
  1884. common->Dialog().ClearDialog( GDM_LOBBY_DISBANDED ); // The lobby you were previously in has disbanded
  1885. } else if ( localState >= STATE_LOADING && newState < STATE_LOADING ) {
  1886. // Tell lobby instances that the match has ended
  1887. if ( !WasMigrationGame() ) { // Don't end the session if we are going right back into the game
  1888. EndSessions();
  1889. }
  1890. }
  1891. if ( newState == STATE_GAME_LOBBY_HOST || newState == STATE_GAME_LOBBY_PEER ) {
  1892. ComputeNextGameCoalesceTime();
  1893. }
  1894. localState = newState;
  1895. }
  1896. /*
  1897. ========================
  1898. idSessionLocal::HandlePackets
  1899. ========================
  1900. */
  1901. bool idSessionLocal::HandlePackets() {
  1902. SCOPED_PROFILE_EVENT( "Session::HandlePackets" );
  1903. byte packetBuffer[ idPacketProcessor::MAX_FINAL_PACKET_SIZE ];
  1904. lobbyAddress_t remoteAddress;
  1905. int recvSize = 0;
  1906. bool fromDedicated = false;
  1907. while ( ReadRawPacket( remoteAddress, packetBuffer, recvSize, fromDedicated, sizeof( packetBuffer ) ) && recvSize > 0 ) {
  1908. // fragMsg will hold the raw packet
  1909. idBitMsg fragMsg;
  1910. fragMsg.InitRead( packetBuffer, recvSize );
  1911. // Peek at the session ID
  1912. idPacketProcessor::sessionId_t sessionID = idPacketProcessor::GetSessionID( fragMsg );
  1913. // idLib::Printf( "NET: HandlePackets - session %d, size %d \n", sessionID, recvSize );
  1914. // Make sure it's valid
  1915. if ( sessionID == idPacketProcessor::SESSION_ID_INVALID ) {
  1916. idLib::Printf( "NET: Invalid sessionID %s.\n", remoteAddress.ToString() );
  1917. continue;
  1918. }
  1919. //
  1920. // Distribute the packet to the proper lobby
  1921. //
  1922. const int maskedType = sessionID & idPacketProcessor::LOBBY_TYPE_MASK;
  1923. if ( !verify( maskedType > 0 ) ) {
  1924. continue;
  1925. }
  1926. idLobby::lobbyType_t lobbyType = (idLobby::lobbyType_t)( maskedType - 1 );
  1927. switch ( lobbyType ) {
  1928. case idLobby::TYPE_PARTY: GetPartyLobby().HandlePacket( remoteAddress, fragMsg, sessionID ); break;
  1929. case idLobby::TYPE_GAME: GetGameLobby().HandlePacket( remoteAddress, fragMsg, sessionID ); break;
  1930. case idLobby::TYPE_GAME_STATE: GetGameStateLobby().HandlePacket( remoteAddress, fragMsg, sessionID ); break;
  1931. default: assert( 0 );
  1932. }
  1933. }
  1934. return false;
  1935. }
  1936. /*
  1937. ========================
  1938. idSessionLocal::GetActivePlatformLobby
  1939. ========================
  1940. */
  1941. idLobby * idSessionLocal::GetActivePlatformLobby() {
  1942. sessionState_t state = GetState();
  1943. if ( ( state == GAME_LOBBY ) || ( state == BUSY ) || ( state == INGAME ) || ( state == LOADING ) ) {
  1944. return &GetGameLobby();
  1945. } else if ( state == PARTY_LOBBY ) {
  1946. return &GetPartyLobby();
  1947. }
  1948. return NULL;
  1949. }
  1950. /*
  1951. ========================
  1952. idSessionLocal::GetActivePlatformLobby
  1953. ========================
  1954. */
  1955. const idLobby * idSessionLocal::GetActivePlatformLobby() const {
  1956. sessionState_t state = GetState();
  1957. if ( ( state == GAME_LOBBY ) || ( state == BUSY ) || ( state == INGAME ) || ( state == LOADING ) ) {
  1958. return &GetGameLobby();
  1959. } else if ( state == PARTY_LOBBY ) {
  1960. return &GetPartyLobby();
  1961. }
  1962. return NULL;
  1963. }
  1964. /*
  1965. ========================
  1966. idSessionLocal::GetActingGameStateLobby
  1967. ========================
  1968. */
  1969. idLobby & idSessionLocal::GetActingGameStateLobby() {
  1970. if ( net_useGameStateLobby.GetBool() ) {
  1971. return GetGameStateLobby();
  1972. }
  1973. return GetGameLobby();
  1974. }
  1975. /*
  1976. ========================
  1977. idSessionLocal::GetActingGameStateLobby
  1978. ========================
  1979. */
  1980. const idLobby & idSessionLocal::GetActingGameStateLobby() const {
  1981. if ( net_useGameStateLobby.GetBool() ) {
  1982. return GetGameStateLobby();
  1983. }
  1984. return GetGameLobby();
  1985. }
  1986. /*
  1987. ========================
  1988. idSessionLocal::GetLobbyFromType
  1989. ========================
  1990. */
  1991. idLobby * idSessionLocal::GetLobbyFromType( idLobby::lobbyType_t lobbyType ) {
  1992. switch ( lobbyType ) {
  1993. case idLobby::TYPE_PARTY: return &GetPartyLobby();
  1994. case idLobby::TYPE_GAME: return &GetGameLobby();
  1995. case idLobby::TYPE_GAME_STATE: return &GetGameStateLobby();
  1996. }
  1997. return NULL;
  1998. }
  1999. /*
  2000. ========================
  2001. idSessionLocal::GetActivePlatformLobbyBase
  2002. This returns the base version for the idSession version
  2003. ========================
  2004. */
  2005. idLobbyBase & idSessionLocal::GetActivePlatformLobbyBase() {
  2006. idLobby * activeLobby = GetActivePlatformLobby();
  2007. if ( activeLobby != NULL ) {
  2008. return *activeLobby;
  2009. }
  2010. return stubLobby; // So we can return at least something
  2011. }
  2012. /*
  2013. ========================
  2014. idSessionLocal::GetLobbyFromLobbyUserID
  2015. ========================
  2016. */
  2017. idLobbyBase & idSessionLocal::GetLobbyFromLobbyUserID( lobbyUserID_t lobbyUserID ) {
  2018. if ( !lobbyUserID.IsValid() ) {
  2019. return stubLobby; // So we can return at least something
  2020. }
  2021. idLobby * lobby = GetLobbyFromType( (idLobby::lobbyType_t)lobbyUserID.GetLobbyType() );
  2022. if ( lobby != NULL ) {
  2023. return *lobby;
  2024. }
  2025. return stubLobby; // So we can return at least something
  2026. }
  2027. /*
  2028. ========================
  2029. idSessionLocal::TickSendQueue
  2030. ========================
  2031. */
  2032. void idSessionLocal::TickSendQueue() {
  2033. assert( !sendQueue.IsEmpty() );
  2034. int now = Sys_Milliseconds();
  2035. idQueuePacket * packet = sendQueue.Peek();
  2036. while ( packet != NULL ) {
  2037. if ( now < packet->time ) {
  2038. break;
  2039. }
  2040. GetPort( packet->dedicated ).SendRawPacket( packet->address, packet->data, packet->size );
  2041. if ( net_forceUpstream.GetFloat() != 0.0f && net_forceUpstreamQueue.GetFloat() != 0.0f ) {
  2042. // FIXME: no can do both
  2043. assert( net_forceLatency.GetInteger() == 0 );
  2044. // compute / update an added traffic due to the queuing
  2045. // we can't piggyback on upstreamDropRate because of the way it's computed and clamped to zero
  2046. int time = Sys_Milliseconds();
  2047. if ( time > upstreamQueueRateTime ) {
  2048. upstreamQueueRate -= upstreamQueueRate * ( float )( time - upstreamQueueRateTime ) / 1000.0f;
  2049. if ( upstreamQueueRate < 0.0f ) {
  2050. upstreamQueueRate = 0.0f;
  2051. }
  2052. upstreamQueueRateTime = time;
  2053. }
  2054. // update queued bytes
  2055. queuedBytes -= packet->size;
  2056. if ( net_verboseSimulatedTraffic.GetBool() ) {
  2057. idLib::Printf( "send queued packet size %d to %s\n", packet->size, packet->address.ToString() );
  2058. }
  2059. }
  2060. sendQueue.RemoveFirst(); // we have it already, just push it off the queue before freeing
  2061. packetAllocator.Free( packet );
  2062. packet = sendQueue.Peek();
  2063. }
  2064. }
  2065. /*
  2066. ========================
  2067. idSessionLocal::QueuePacket
  2068. ========================
  2069. */
  2070. void idSessionLocal::QueuePacket( idQueue< idQueuePacket,&idQueuePacket::queueNode > & queue, int time, const lobbyAddress_t & to, const void * data, int size, bool dedicated ) {
  2071. //mem.PushHeap();
  2072. idQueuePacket * packet = packetAllocator.Alloc();
  2073. packet->address = to;
  2074. packet->size = size;
  2075. packet->dedicated = dedicated;
  2076. packet->time = time;
  2077. memcpy( packet->data, data, size );
  2078. queue.Add( packet );
  2079. //mem.PopHeap();
  2080. }
  2081. /*
  2082. ========================
  2083. idSessionLocal::ReadRawPacketFromQueue
  2084. ========================
  2085. */
  2086. bool idSessionLocal::ReadRawPacketFromQueue( int time, lobbyAddress_t & from, void * data, int & size, bool & outDedicated, int maxSize ) {
  2087. idQueuePacket * packet = recvQueue.Peek();
  2088. if ( packet == NULL || time < packet->time ) {
  2089. return false; // Either there are no packets, or no packet is ready
  2090. }
  2091. //idLib::Printf( "NET: Packet recvd: %d ms\n", now );
  2092. from = packet->address;
  2093. size = packet->size;
  2094. assert( size <= maxSize );
  2095. outDedicated = packet->dedicated;
  2096. memcpy( data, packet->data, packet->size );
  2097. recvQueue.RemoveFirst(); // we have it already, just push it off the queue before freeing
  2098. packetAllocator.Free( packet );
  2099. return true;
  2100. }
  2101. /*
  2102. ========================
  2103. idSessionLocal::SendRawPacket
  2104. ========================
  2105. */
  2106. void idSessionLocal::SendRawPacket( const lobbyAddress_t & to, const void * data, int size, bool dedicated ) {
  2107. const int now = Sys_Milliseconds();
  2108. if ( net_forceUpstream.GetFloat() != 0 ) {
  2109. // the total bandwidth rate at which the networking systems are trying to push data through
  2110. float totalOutgoingRate = (float)GetActingGameStateLobby().GetTotalOutgoingRate(); // B/s
  2111. // update the rate at which we have been taking data out by dropping it
  2112. int time = Sys_Milliseconds();
  2113. if ( time > upstreamDropRateTime ) {
  2114. upstreamDropRate -= upstreamDropRate * ( float )( time - upstreamDropRateTime ) / 1000.0f;
  2115. if ( upstreamDropRate < 0.0f ) {
  2116. upstreamDropRate = 0.0f;
  2117. }
  2118. upstreamDropRateTime = time;
  2119. }
  2120. if ( (float)( totalOutgoingRate - upstreamDropRate + upstreamQueueRate ) > net_forceUpstream.GetFloat() * 1024.0f ) { // net_forceUpstream is in kB/s, everything else in B/s
  2121. if ( net_forceUpstreamQueue.GetFloat() == 0.0f ) {
  2122. // just drop the packet - not representative, but simple
  2123. if ( net_verboseSimulatedTraffic.GetBool() ) {
  2124. idLib::Printf( "drop %d bytes to %s\n", size, to.ToString() );
  2125. }
  2126. // increase the instant drop rate with the data we just dropped
  2127. upstreamDropRate += size;
  2128. return;
  2129. }
  2130. // simulate a network device with a send queue
  2131. // do we have room in the queue?
  2132. assert( net_forceUpstreamQueue.GetFloat() > 0.0f );
  2133. if ( (float)( queuedBytes + size ) > net_forceUpstreamQueue.GetFloat() * 1024.0f ) { // net_forceUpstreamQueue is in kB/s
  2134. // too much queued, this is still a drop
  2135. // FIXME: factorize
  2136. // just drop the packet - not representative, but simple
  2137. if ( net_verboseSimulatedTraffic.GetBool() ) {
  2138. idLib::Printf( "full queue: drop %d bytes to %s\n", size, to.ToString() );
  2139. }
  2140. // increase the instant drop rate with the data we just dropped
  2141. upstreamDropRate += size;
  2142. return;
  2143. }
  2144. // there is room to buffer up in the queue
  2145. queuedBytes += size;
  2146. // with queuedBytes and the current upstream, when should this packet be sent?
  2147. int queuedPacketSendDelay = 1000.0f * ( (float)queuedBytes / ( net_forceUpstream.GetFloat() * 1024.0f ) ); // in ms
  2148. // queue for sending
  2149. if ( net_verboseSimulatedTraffic.GetBool() ) {
  2150. idLib::Printf( "queuing packet: %d bytes delayed %d ms\n", size, queuedPacketSendDelay );
  2151. }
  2152. QueuePacket( sendQueue, now + queuedPacketSendDelay, to, data, size, dedicated );
  2153. // will abuse the forced latency code below to take care of the sending
  2154. // FIXME: right now, can't have both on
  2155. assert( net_forceLatency.GetInteger() == 0 );
  2156. }
  2157. }
  2158. // short path
  2159. // NOTE: network queuing: will go to tick the queue whenever sendQueue isn't empty, regardless of latency
  2160. if ( net_forceLatency.GetInteger() == 0 && sendQueue.IsEmpty() ) {
  2161. GetPort( dedicated ).SendRawPacket( to, data, size );
  2162. return;
  2163. }
  2164. if ( net_forceUpstream.GetFloat() != 0.0f && net_forceUpstreamQueue.GetFloat() != 0.0f ) {
  2165. // FIXME: not doing both just yet
  2166. assert( net_forceLatency.GetInteger() == 0 );
  2167. TickSendQueue();
  2168. return; // we done (at least for queue only path)
  2169. }
  2170. // queue up
  2171. assert( size != 0 && size <= idPacketProcessor::MAX_FINAL_PACKET_SIZE );
  2172. QueuePacket( sendQueue, now + net_forceLatency.GetInteger() / 2, to, data, size, dedicated );
  2173. TickSendQueue();
  2174. }
  2175. /*
  2176. ========================
  2177. idSessionLocal::ReadRawPacket
  2178. ========================
  2179. */
  2180. bool idSessionLocal::ReadRawPacket( lobbyAddress_t & from, void * data, int & size, bool & outDedicated, int maxSize ) {
  2181. SCOPED_PROFILE_EVENT( "Session::ReadRawPacket" );
  2182. assert( maxSize <= idPacketProcessor::MAX_FINAL_PACKET_SIZE );
  2183. if ( !sendQueue.IsEmpty() ) {
  2184. TickSendQueue();
  2185. }
  2186. const int now = Sys_Milliseconds();
  2187. // Make sure we give both ports equal time
  2188. static bool currentDedicated = false;
  2189. currentDedicated = !currentDedicated;
  2190. for ( int i = 0; i < 2; i++ ) {
  2191. // BRIAN_FIXME: Dedicated servers fuck up running 2 instances on the same machine
  2192. // outDedicated = ( i == 0 ) ? currentDedicated : !currentDedicated;
  2193. outDedicated = false;
  2194. if ( GetPort( outDedicated ).ReadRawPacket( from, data, size, maxSize ) ) {
  2195. if ( net_forceLatency.GetInteger() == 0 && recvQueue.IsEmpty() ) {
  2196. // If we aren't forcing latency, and queue is empty, return result immediately
  2197. return true;
  2198. }
  2199. // the cvar is meant to be a round trip latency so we're applying half on the send and half on the recv
  2200. const int time = ( net_forceLatency.GetInteger() == 0 ) ? 0 : now + net_forceLatency.GetInteger() / 2;
  2201. // Otherwise, queue result
  2202. QueuePacket( recvQueue, time, from, data, size, outDedicated );
  2203. }
  2204. }
  2205. // Return any queued results
  2206. return ReadRawPacketFromQueue( now, from, data, size, outDedicated, maxSize );
  2207. }
  2208. /*
  2209. ========================
  2210. idSessionLocal::ConnectAndMoveToLobby
  2211. ========================
  2212. */
  2213. void idSessionLocal::ConnectAndMoveToLobby( idLobby & lobby, const lobbyConnectInfo_t & connectInfo, bool fromInvite ) {
  2214. // Since we are connecting directly to a lobby, make sure no search results are left over from previous FindOrCreateMatch results
  2215. // If we don't do this, we might think we should attempt to connect to an old search result, and we don't want to in this case
  2216. lobby.searchResults.Clear();
  2217. // Attempt to connect to the lobby
  2218. lobby.ConnectTo( connectInfo, fromInvite );
  2219. connectType = CONNECT_DIRECT;
  2220. // Wait for connection
  2221. switch ( lobby.lobbyType ) {
  2222. case idLobby::TYPE_PARTY: SetState( STATE_CONNECT_AND_MOVE_TO_PARTY ); break;
  2223. case idLobby::TYPE_GAME: SetState( STATE_CONNECT_AND_MOVE_TO_GAME ); break;
  2224. case idLobby::TYPE_GAME_STATE: SetState( STATE_CONNECT_AND_MOVE_TO_GAME_STATE ); break;
  2225. }
  2226. }
  2227. /*
  2228. ========================
  2229. idSessionLocal::GoodbyeFromHost
  2230. ========================
  2231. */
  2232. void idSessionLocal::GoodbyeFromHost( idLobby & lobby, int peerNum, const lobbyAddress_t & remoteAddress, int msgType ) {
  2233. if ( !verify( localState > STATE_IDLE ) ) {
  2234. idLib::Printf( "NET: Got disconnected from host %s on session %s when we were not in a lobby or game.\n", remoteAddress.ToString(), lobby.GetLobbyName() );
  2235. MoveToMainMenu();
  2236. return; // Ignore if we are not past the main menu
  2237. }
  2238. // Goodbye from host. See if we were connecting vs connected
  2239. if ( ( localState == STATE_CONNECT_AND_MOVE_TO_PARTY || localState == STATE_CONNECT_AND_MOVE_TO_GAME ) && lobby.peers[peerNum].GetConnectionState() == idLobby::CONNECTION_CONNECTING ) {
  2240. // We were denied a connection attempt
  2241. idLib::Printf( "NET: Denied connection attempt from host %s on session %s. MsgType %i.\n", remoteAddress.ToString(), lobby.GetLobbyName(), msgType );
  2242. // This will try to move to the next connection if one exists, otherwise will create a match
  2243. HandleConnectionFailed( lobby, msgType == idLobby::OOB_GOODBYE_FULL );
  2244. } else {
  2245. // We were disconnected from a server we were previously connected to
  2246. idLib::Printf( "NET: Disconnected from host %s on session %s. MsgType %i.\n", remoteAddress.ToString(), lobby.GetLobbyName(), msgType );
  2247. const bool leaveGameWithParty = ( msgType == idLobby::OOB_GOODBYE_W_PARTY );
  2248. if ( leaveGameWithParty && lobby.lobbyType == idLobby::TYPE_GAME && lobby.IsPeer() && GetState() == idSession::GAME_LOBBY && GetPartyLobby().host >= 0 &&
  2249. lobby.peers[peerNum].address.Compare( GetPartyLobby().peers[GetPartyLobby().host].address, true ) ) {
  2250. // If a host is telling us goodbye from a game lobby, and the game host is the same as our party host,
  2251. // and we aren't in a game, and the host wants us to leave with him, then do so now
  2252. GetGameLobby().Shutdown();
  2253. GetGameStateLobby().Shutdown();
  2254. SetState( STATE_PARTY_LOBBY_PEER );
  2255. } else {
  2256. // Host left, so pick a new host (possibly even us) for this lobby
  2257. lobby.PickNewHost();
  2258. }
  2259. }
  2260. }
  2261. /*
  2262. ========================
  2263. idSessionLocal::WriteLeaderboardToMsg
  2264. ========================
  2265. */
  2266. void idSessionLocal::WriteLeaderboardToMsg( idBitMsg & msg, const leaderboardDefinition_t * leaderboard, const column_t * stats ) {
  2267. assert( Sys_FindLeaderboardDef( leaderboard->id ) == leaderboard );
  2268. msg.WriteLong( leaderboard->id );
  2269. for ( int i = 0; i < leaderboard->numColumns; ++i ) {
  2270. uint64 value = stats[i].value;
  2271. //idLib::Printf( "value = %i\n", (int32)value );
  2272. for ( int j = 0; j < leaderboard->columnDefs[i].bits; j++ ) {
  2273. msg.WriteBits( value & 1, 1 );
  2274. value >>= 1;
  2275. }
  2276. //msg.WriteData( &stats[i].value, sizeof( stats[i].value ) );
  2277. }
  2278. }
  2279. /*
  2280. ========================
  2281. idSessionLocal::ReadLeaderboardFromMsg
  2282. ========================
  2283. */
  2284. const leaderboardDefinition_t * idSessionLocal::ReadLeaderboardFromMsg( idBitMsg & msg, column_t * stats ) {
  2285. int id = msg.ReadLong();
  2286. const leaderboardDefinition_t * leaderboard = Sys_FindLeaderboardDef( id );
  2287. if ( leaderboard == NULL ) {
  2288. idLib::Printf( "NET: Invalid leaderboard id: %i\n", id );
  2289. return NULL;
  2290. }
  2291. for ( int i = 0; i < leaderboard->numColumns; ++i ) {
  2292. uint64 value = 0;
  2293. for ( int j = 0; j < leaderboard->columnDefs[i].bits; j++ ) {
  2294. value |= (uint64)( msg.ReadBits( 1 ) & 1 ) << j;
  2295. }
  2296. stats[i].value = value;
  2297. //idLib::Printf( "value = %i\n", (int32)value );
  2298. //msg.ReadData( &stats[i].value, sizeof( stats[i].value ) );
  2299. }
  2300. return leaderboard;
  2301. }
  2302. /*
  2303. ========================
  2304. idSessionLocal::SendLeaderboardStatsToPlayer
  2305. ========================
  2306. */
  2307. void idSessionLocal::SendLeaderboardStatsToPlayer( lobbyUserID_t lobbyUserID, const leaderboardDefinition_t * leaderboard, const column_t * stats ) {
  2308. const int sessionUserIndex = GetActingGameStateLobby().GetLobbyUserIndexByID( lobbyUserID );
  2309. if ( GetActingGameStateLobby().IsLobbyUserDisconnected( sessionUserIndex ) ) {
  2310. idLib::Warning( "Tried to tell disconnected user to report stats" );
  2311. return;
  2312. }
  2313. const int peerIndex = GetActingGameStateLobby().PeerIndexFromLobbyUser( lobbyUserID );
  2314. if ( peerIndex == -1 ) {
  2315. idLib::Warning( "Tried to tell invalid peer index to report stats" );
  2316. return;
  2317. }
  2318. if ( !verify( GetActingGameStateLobby().IsHost() ) ||
  2319. !verify( peerIndex < GetActingGameStateLobby().peers.Num() ) ||
  2320. !verify( GetActingGameStateLobby().peers[ peerIndex ].IsConnected() ) ) {
  2321. idLib::Warning( "Tried to tell invalid peer to report stats" );
  2322. return;
  2323. }
  2324. NET_VERBOSE_PRINT( "Telling sessionUserIndex %i (peer %i) to report stats\n", sessionUserIndex, peerIndex );
  2325. lobbyUser_t * gameUser = GetActingGameStateLobby().GetLobbyUser( sessionUserIndex );
  2326. if ( !verify( gameUser != NULL ) ) {
  2327. return;
  2328. }
  2329. byte buffer[ idPacketProcessor::MAX_PACKET_SIZE ];
  2330. idBitMsg msg( buffer, sizeof( buffer ) );
  2331. // Use the user ID
  2332. gameUser->lobbyUserID.WriteToMsg( msg );
  2333. WriteLeaderboardToMsg( msg, leaderboard, stats );
  2334. GetActingGameStateLobby().QueueReliableMessage( peerIndex, idLobby::RELIABLE_POST_STATS, msg.GetReadData(), msg.GetSize() );
  2335. }
  2336. /*
  2337. ========================
  2338. idSessionLocal::RecvLeaderboardStatsForPlayer
  2339. ========================
  2340. */
  2341. void idSessionLocal::RecvLeaderboardStatsForPlayer( idBitMsg & msg ) {
  2342. column_t stats[ MAX_LEADERBOARD_COLUMNS ];
  2343. lobbyUserID_t lobbyUserID;
  2344. lobbyUserID.ReadFromMsg( msg );
  2345. const leaderboardDefinition_t * leaderboard = ReadLeaderboardFromMsg( msg, stats );
  2346. if ( leaderboard == NULL ) {
  2347. idLib::Printf( "RecvLeaderboardStatsForPlayer: Invalid lb.\n" );
  2348. return;
  2349. }
  2350. LeaderboardUpload( lobbyUserID, leaderboard, stats );
  2351. }
  2352. /*
  2353. ========================
  2354. idSessionLocal::RequirePersistentMaster
  2355. ========================
  2356. */
  2357. bool idSessionLocal::RequirePersistentMaster() {
  2358. return signInManager->RequirePersistentMaster();
  2359. }
  2360. /*
  2361. ========================
  2362. CheckAndUpdateValue
  2363. ========================
  2364. */
  2365. template<typename T>
  2366. bool CheckAndUpdateValue( T & value, const T & newValue ) {
  2367. if ( value == newValue ) {
  2368. return false;
  2369. }
  2370. value = newValue;
  2371. return true;
  2372. }
  2373. /*
  2374. ========================
  2375. lobbyUser_t::UpdateClientMutableData
  2376. ========================
  2377. */
  2378. bool lobbyUser_t::UpdateClientMutableData( const idLocalUser * localUser ) {
  2379. bool updated = false;
  2380. const idPlayerProfile * profile = localUser->GetProfile();
  2381. if ( profile != NULL ) {
  2382. updated |= CheckAndUpdateValue( level, profile->GetLevel() );
  2383. }
  2384. updated |= CheckAndUpdateValue( selectedSkin, ui_skinIndex.GetInteger() );
  2385. updated |= CheckAndUpdateValue( weaponAutoSwitch, ui_autoSwitch.GetBool() );
  2386. updated |= CheckAndUpdateValue( weaponAutoReload, ui_autoReload.GetBool() );
  2387. return updated;
  2388. }
  2389. /*
  2390. ========================
  2391. idSessionLocal::ComputeNextGameCoalesceTime
  2392. ========================
  2393. */
  2394. void idSessionLocal::ComputeNextGameCoalesceTime() {
  2395. const int coalesceTimeInSeconds = session->GetTitleStorageInt( "net_LobbyCoalesceTimeInSeconds", net_LobbyCoalesceTimeInSeconds.GetInteger() );
  2396. const int randomCoalesceTimeInSeconds = session->GetTitleStorageInt( "net_LobbyRandomCoalesceTimeInSeconds", net_LobbyRandomCoalesceTimeInSeconds.GetInteger() );
  2397. if ( coalesceTimeInSeconds != 0 ) {
  2398. static idRandom2 random( Sys_Milliseconds() );
  2399. nextGameCoalesceTime = Sys_Milliseconds() + ( coalesceTimeInSeconds + random.RandomInt( randomCoalesceTimeInSeconds ) ) * 1000;
  2400. } else {
  2401. nextGameCoalesceTime = 0;
  2402. }
  2403. }
  2404. /*
  2405. ========================
  2406. lobbyUser_t::Net_BandwidthChallenge
  2407. ========================
  2408. */
  2409. CONSOLE_COMMAND( Net_BandwidthChallenge, "Test network bandwidth", 0 ) {
  2410. session->StartOrContinueBandwidthChallenge( true );
  2411. }
  2412. /*
  2413. ========================
  2414. lobbyUser_t::Net_ThrottlePeer
  2415. ========================
  2416. */
  2417. CONSOLE_COMMAND( Net_ThrottlePeer, "Test network bandwidth", 0 ) {
  2418. int peerNum = -1;
  2419. int snapRate = 0;
  2420. if ( args.Argc() >= 3 ) {
  2421. peerNum = atoi( args.Argv(1) );
  2422. snapRate = atoi( args.Argv(2) );
  2423. }
  2424. // Note DebugSetPeerSnaprate will handle peerNum=-1 by printing out list of peers
  2425. session->DebugSetPeerSnaprate( peerNum, snapRate );
  2426. }
  2427. // FIXME: Move to sys_stats.cpp
  2428. idStaticList< leaderboardDefinition_t *, MAX_LEADERBOARDS > registeredLeaderboards;
  2429. /*
  2430. ========================
  2431. Sys_FindLeaderboardDef
  2432. ========================
  2433. */
  2434. const leaderboardDefinition_t * Sys_FindLeaderboardDef( int id ) {
  2435. for ( int i = 0; i < registeredLeaderboards.Num() ; i++ ) {
  2436. if ( registeredLeaderboards[i] && registeredLeaderboards[i]->id == id ) {
  2437. return registeredLeaderboards[i];
  2438. }
  2439. }
  2440. return NULL;
  2441. }
  2442. /*
  2443. ========================
  2444. Sys_CreateLeaderboardDef
  2445. ========================
  2446. */
  2447. leaderboardDefinition_t * Sys_CreateLeaderboardDef( int id_, int numColumns_, const columnDef_t * columnDefs_,
  2448. rankOrder_t rankOrder_, bool supportsAttachments_, bool checkAgainstCurrent_ ) {
  2449. leaderboardDefinition_t * newDef = new (TAG_NETWORKING) leaderboardDefinition_t( id_, numColumns_, columnDefs_, rankOrder_, supportsAttachments_, checkAgainstCurrent_ );
  2450. // try and reuse a free spot
  2451. int leaderboardHandle = registeredLeaderboards.FindNull();
  2452. if ( leaderboardHandle == -1 ) {
  2453. leaderboardHandle = registeredLeaderboards.Append( NULL );
  2454. }
  2455. registeredLeaderboards[ leaderboardHandle ] = newDef;
  2456. return newDef;
  2457. }
  2458. /*
  2459. ========================
  2460. Sys_CreateLeaderboardDef
  2461. ========================
  2462. */
  2463. void Sys_DestroyLeaderboardDefs() {
  2464. // delete and clear all the contents of the registeredLeaderboards static list.
  2465. registeredLeaderboards.DeleteContents( true );
  2466. }
  2467. /*
  2468. ========================
  2469. idSessionLocal::StartOrContinueBandwidthChallenge
  2470. This will start a bandwidth test if one is not active
  2471. returns true if a test has completed
  2472. ========================
  2473. */
  2474. bool idSessionLocal::StartOrContinueBandwidthChallenge( bool forceStart ) {
  2475. idLobby * activeLobby = GetActivePlatformLobby();
  2476. if ( activeLobby == NULL ) {
  2477. idLib::Warning("No active session lobby when idSessionLocal::StartBandwidthChallenge called");
  2478. return true;
  2479. }
  2480. if ( !forceStart && activeLobby->bandwidthChallengeFinished ) {
  2481. activeLobby->bandwidthChallengeFinished = false;
  2482. return true;
  2483. }
  2484. if ( !activeLobby->BandwidthTestStarted() ) {
  2485. activeLobby->BeginBandwidthTest();
  2486. }
  2487. return false;
  2488. }
  2489. /*
  2490. ========================
  2491. idSessionLocal::DebugSetPeerSnaprate
  2492. This is debug function for manually setting peer's snaprate in game
  2493. ========================
  2494. */
  2495. void idSessionLocal::DebugSetPeerSnaprate( int peerIndex, int snapRateMS ) {
  2496. idLobby * activeLobby = GetActivePlatformLobby();
  2497. if ( activeLobby == NULL ) {
  2498. idLib::Warning("No active session lobby when idSessionLocal::StartBandwidthChallenge called");
  2499. return;
  2500. }
  2501. if ( peerIndex < 0 || peerIndex > activeLobby->peers.Num() ) {
  2502. idLib::Printf("Invalid peer %d\n", peerIndex );
  2503. for ( int i=0; i < activeLobby->peers.Num(); i++ ) {
  2504. idLib::Printf( "Peer[%d] %s\n", i, activeLobby->GetPeerName(i) );
  2505. }
  2506. return;
  2507. }
  2508. activeLobby->peers[peerIndex].throttledSnapRate = snapRateMS * 1000;
  2509. activeLobby->peers[peerIndex].receivedThrottle = 0;
  2510. idLib::Printf( "Set peer %s new snapRate: %d\n", activeLobby->GetPeerName(peerIndex), activeLobby->peers[peerIndex].throttledSnapRate );
  2511. }
  2512. /*
  2513. ========================
  2514. idSessionLocal::DebugSetPeerSnaprate
  2515. This is debug function for manually setting peer's snaprate in game
  2516. ========================
  2517. */
  2518. float idSessionLocal::GetIncomingByteRate() {
  2519. idLobby * activeLobby = GetActivePlatformLobby();
  2520. if ( activeLobby == NULL ) {
  2521. idLib::Warning("No active session lobby when idSessionLocal::GetIncomingByteRate called");
  2522. return 0;
  2523. }
  2524. float total = 0;
  2525. for ( int p=0; p < activeLobby->peers.Num(); p++ ) {
  2526. if ( activeLobby->peers[p].IsConnected() ) {
  2527. total += activeLobby->peers[p].packetProc->GetIncomingRateBytes();
  2528. }
  2529. }
  2530. return total;
  2531. }
  2532. /*
  2533. ========================
  2534. idSessionLocal::OnLocalUserSignin
  2535. ========================
  2536. */
  2537. void idSessionLocal::OnLocalUserSignin( idLocalUser * user ) {
  2538. // Do stuff before calling OnMasterLocalUserSignin()
  2539. session->GetAchievementSystem().RegisterLocalUser( user );
  2540. // We may not have a profile yet, need to call user's version...
  2541. user->LoadProfileSettings();
  2542. // for all consoles except the PS3 we enumerate right away because they don't
  2543. // take such a long time as the PS3. PS3 enumeration is done in the
  2544. // background and kicked off when the profile callback is triggered
  2545. if ( user == GetSignInManager().GetMasterLocalUser() ) {
  2546. OnMasterLocalUserSignin();
  2547. }
  2548. }
  2549. /*
  2550. ========================
  2551. idSessionLocal::OnLocalUserSignout
  2552. ========================
  2553. */
  2554. void idSessionLocal::OnLocalUserSignout( idLocalUser * user ) {
  2555. // Do stuff before calling OnMasterLocalUserSignout()
  2556. session->GetAchievementSystem().RemoveLocalUser( user );
  2557. if ( GetSignInManager().GetMasterLocalUser() == NULL ) {
  2558. OnMasterLocalUserSignout();
  2559. }
  2560. }
  2561. /*
  2562. ========================
  2563. idSessionLocal::OnMasterLocalUserSignout
  2564. ========================
  2565. */
  2566. void idSessionLocal::OnMasterLocalUserSignout() {
  2567. CancelSaveGameWithHandle( enumerationHandle );
  2568. enumerationHandle = 0;
  2569. GetSaveGameManager().GetEnumeratedSavegamesNonConst().Clear();
  2570. }
  2571. /*
  2572. ========================
  2573. idSessionLocal::OnMasterLocalUserSignin
  2574. ========================
  2575. */
  2576. void idSessionLocal::OnMasterLocalUserSignin() {
  2577. enumerationHandle = EnumerateSaveGamesAsync();
  2578. }
  2579. /*
  2580. ========================
  2581. idSessionLocal::OnLocalUserProfileLoaded
  2582. ========================
  2583. */
  2584. void idSessionLocal::OnLocalUserProfileLoaded( idLocalUser * user ) {
  2585. user->RequestSyncAchievements();
  2586. }
  2587. /*
  2588. ========================
  2589. idSessionLocal::SetVoiceGroupsToTeams
  2590. ========================
  2591. */
  2592. void idSessionLocal::SetVoiceGroupsToTeams() {
  2593. // move voice chat to team
  2594. int myTeam = 0;
  2595. for ( int i = 0; i < GetGameLobby().GetNumLobbyUsers(); ++i ) {
  2596. const lobbyUser_t * gameUser = GetGameLobby().GetLobbyUser( i );
  2597. if ( !verify( gameUser != NULL ) ) {
  2598. continue;
  2599. }
  2600. if ( gameUser->IsDisconnected() ) {
  2601. continue;
  2602. }
  2603. int userTeam = gameUser->teamNumber;
  2604. voiceChat->SetTalkerGroup( gameUser, GetGameLobby().lobbyType, userTeam );
  2605. if ( GetGameLobby().IsSessionUserIndexLocal( i ) ) {
  2606. myTeam = userTeam;
  2607. }
  2608. }
  2609. SetActiveChatGroup( myTeam );
  2610. }
  2611. /*
  2612. ========================
  2613. idSessionLocal::ClearVoiceGroups
  2614. ========================
  2615. */
  2616. void idSessionLocal::ClearVoiceGroups() {
  2617. for ( int i = 0; i < GetGameLobby().GetNumLobbyUsers(); ++i ) {
  2618. const lobbyUser_t * gameUser = GetGameLobby().GetLobbyUser( i );
  2619. if ( !verify( gameUser != NULL ) ) {
  2620. continue;
  2621. }
  2622. if ( gameUser->IsDisconnected() ) {
  2623. continue;
  2624. }
  2625. voiceChat->SetTalkerGroup( gameUser, GetGameLobby().lobbyType, 0 );
  2626. }
  2627. SetActiveChatGroup( 0 );
  2628. }
  2629. /*
  2630. ========================
  2631. idSessionLocal::SendVoiceAudio
  2632. ========================
  2633. */
  2634. void idSessionLocal::SendVoiceAudio() {
  2635. if ( voiceChat == NULL ) {
  2636. return;
  2637. }
  2638. idLobby * activeLobby = GetActivePlatformLobby();
  2639. int activeSessionIndex = ( activeLobby != NULL ) ? activeLobby->lobbyType : -1;
  2640. voiceChat->SetActiveLobby( activeSessionIndex );
  2641. voiceChat->Pump();
  2642. if ( activeLobby == NULL ) {
  2643. return;
  2644. }
  2645. int time = Sys_Milliseconds();
  2646. const int VOICE_THROTTLE_TIME_IN_MS = session->GetTitleStorageInt( "VOICE_THROTTLE_TIME_IN_MS", 33) ; // Don't allow faster than 30hz send rate
  2647. if ( time - lastVoiceSendtime < VOICE_THROTTLE_TIME_IN_MS ) {
  2648. return;
  2649. }
  2650. lastVoiceSendtime = time;
  2651. idStaticList< int, MAX_PLAYERS > localTalkers;
  2652. voiceChat->GetActiveLocalTalkers( localTalkers );
  2653. for ( int i = 0; i < localTalkers.Num(); i++ ) {
  2654. // NOTE - For 360, we don't need more than XHV_MAX_VOICECHAT_PACKETS * XHV_VOICECHAT_MODE_PACKET_SIZE bytes
  2655. const int MAX_VDP_DATA_SIZE = 1000;
  2656. byte buffer[MAX_VDP_DATA_SIZE];
  2657. const int titleStorageDataSize = session->GetTitleStorageInt( "MAX_VDP_DATA_SIZE", 1000 );
  2658. const int dataSizeAvailable = Min< int >( titleStorageDataSize, sizeof( buffer ) );
  2659. // in-out parameter
  2660. int dataSize = dataSizeAvailable;
  2661. if ( !voiceChat->GetLocalChatData( localTalkers[i], buffer, dataSize ) ) {
  2662. continue;
  2663. }
  2664. assert( dataSize <= sizeof( buffer ) );
  2665. idStaticList< const lobbyAddress_t *, MAX_PLAYERS > recipients;
  2666. voiceChat->GetRecipientsForTalker( localTalkers[i], recipients );
  2667. for ( int j = 0; j < recipients.Num(); j++ ) {
  2668. activeLobby->SendConnectionLess( *recipients[j], idLobby::OOB_VOICE_AUDIO, buffer, dataSize );
  2669. }
  2670. }
  2671. }
  2672. /*
  2673. ========================
  2674. idSessionLocal::HandleOobVoiceAudio
  2675. ========================
  2676. */
  2677. void idSessionLocal::HandleOobVoiceAudio( const lobbyAddress_t & from, const idBitMsg & msg ) {
  2678. idLobby * activeLobby = GetActivePlatformLobby();
  2679. if ( activeLobby == NULL ) {
  2680. return;
  2681. }
  2682. voiceChat->SetActiveLobby( activeLobby->lobbyType );
  2683. voiceChat->SubmitIncomingChatData( msg.GetReadData() + msg.GetReadCount(), msg.GetRemainingData() );
  2684. }
  2685. /*
  2686. ========================
  2687. idSessionLocal::SetActiveChatGroup
  2688. ========================
  2689. */
  2690. void idSessionLocal::SetActiveChatGroup( int groupIndex ) {
  2691. voiceChat->SetActiveChatGroup( groupIndex );
  2692. }
  2693. /*
  2694. ========================
  2695. idSessionLocal::GetLobbyUserVoiceState
  2696. ========================
  2697. */
  2698. voiceState_t idSessionLocal::GetLobbyUserVoiceState( lobbyUserID_t lobbyUserID ) {
  2699. idLobby * activeLobby = GetActivePlatformLobby();
  2700. if ( activeLobby == NULL ) {
  2701. return VOICECHAT_STATE_NOT_TALKING;
  2702. }
  2703. const lobbyUser_t * user = activeLobby->GetLobbyUserByID( lobbyUserID );
  2704. if ( !verify( user != NULL ) ) {
  2705. return VOICECHAT_STATE_NOT_TALKING;
  2706. }
  2707. return voiceChat->GetVoiceState( user );
  2708. }
  2709. /*
  2710. ========================
  2711. idSessionLocal::GetDisplayStateFromVoiceState
  2712. ========================
  2713. */
  2714. voiceStateDisplay_t idSessionLocal::GetDisplayStateFromVoiceState( voiceState_t voiceState ) const {
  2715. if ( ( GetState() == GAME_LOBBY && MatchTypeIsLocal( GetGameLobby().GetMatchParms().matchFlags ) )
  2716. || ( GetState() == PARTY_LOBBY && MatchTypeIsLocal( GetPartyLobby().GetMatchParms().matchFlags ) ) ) {
  2717. return VOICECHAT_DISPLAY_NONE; // never show voice stuff in splitscreen
  2718. }
  2719. switch ( voiceState ) {
  2720. case VOICECHAT_STATE_MUTED_REMOTE:
  2721. case VOICECHAT_STATE_MUTED_LOCAL:
  2722. case VOICECHAT_STATE_MUTED_ALL:
  2723. return VOICECHAT_DISPLAY_MUTED;
  2724. case VOICECHAT_STATE_NOT_TALKING:
  2725. return VOICECHAT_DISPLAY_NOTTALKING;
  2726. case VOICECHAT_STATE_TALKING:
  2727. return VOICECHAT_DISPLAY_TALKING;
  2728. case VOICECHAT_STATE_TALKING_GLOBAL:
  2729. return VOICECHAT_DISPLAY_TALKING_GLOBAL;
  2730. case VOICECHAT_STATE_NO_MIC:
  2731. default:
  2732. return VOICECHAT_DISPLAY_NOTTALKING;
  2733. }
  2734. }
  2735. /*
  2736. ========================
  2737. idSessionLocal::ToggleLobbyUserVoiceMute
  2738. ========================
  2739. */
  2740. void idSessionLocal::ToggleLobbyUserVoiceMute( lobbyUserID_t lobbyUserID ) {
  2741. idLobby * activeLobby = GetActivePlatformLobby();
  2742. if ( activeLobby == NULL ) {
  2743. return;
  2744. }
  2745. // Get the master local user
  2746. idLocalUser * masterUser = signInManager->GetMasterLocalUser();
  2747. if ( masterUser == NULL ) {
  2748. return;
  2749. }
  2750. const lobbyUser_t * srcUser = activeLobby->GetLobbyUser( activeLobby->GetLobbyUserIndexByLocalUserHandle( masterUser->GetLocalUserHandle() ) );
  2751. if ( srcUser == NULL ) {
  2752. return;
  2753. }
  2754. const lobbyUser_t * targetUser = activeLobby->GetLobbyUserByID( lobbyUserID );
  2755. if ( !verify( targetUser != NULL ) ) {
  2756. return;
  2757. }
  2758. if ( srcUser == targetUser ) {
  2759. return; // Can't toggle yourself
  2760. }
  2761. voiceChat->ToggleMuteLocal( srcUser, targetUser );
  2762. }
  2763. /*
  2764. ========================
  2765. idSessionLocal::UpdateMasterUserHeadsetState
  2766. ========================
  2767. */
  2768. void idSessionLocal::UpdateMasterUserHeadsetState()
  2769. {
  2770. if ( GetState() != PARTY_LOBBY && GetState() != GAME_LOBBY && GetState() != INGAME ) {
  2771. return;
  2772. }
  2773. lobbyUser_t * user = GetActivePlatformLobby()->GetSessionUserFromLocalUser( signInManager->GetMasterLocalUser() );
  2774. // TODO: Is this possible?
  2775. if ( user == NULL ) {
  2776. return;
  2777. }
  2778. int talkerIndex = voiceChat->FindTalkerByUserId( user->lobbyUserID, GetActivePlatformLobby()->lobbyType );
  2779. bool voiceChanged = voiceChat->HasHeadsetStateChanged( talkerIndex );
  2780. if ( voiceChanged ) {
  2781. byte buffer[ idPacketProcessor::MAX_MSG_SIZE ];
  2782. idBitMsg msg( buffer, sizeof( buffer ) );
  2783. msg.WriteLong( 1 );
  2784. user->lobbyUserID.WriteToMsg( msg );
  2785. msg.WriteBool( voiceChat->GetHeadsetState( talkerIndex ) );
  2786. idLib::Printf( "Sending voicestate %d for user %d %s\n", voiceChat->GetHeadsetState( talkerIndex ), talkerIndex, user->gamertag );
  2787. if ( GetActivePlatformLobby()->IsHost() ) {
  2788. for ( int p = 0; p < GetActivePlatformLobby()->peers.Num(); p++ ) {
  2789. if ( GetActivePlatformLobby()->peers[p].IsConnected() ) {
  2790. GetActivePlatformLobby()->QueueReliableMessage( p, idLobby::RELIABLE_HEADSET_STATE, msg.GetReadData(), msg.GetSize() );
  2791. }
  2792. }
  2793. } else {
  2794. GetActivePlatformLobby()->QueueReliableMessage( GetActivePlatformLobby()->host, idLobby::RELIABLE_HEADSET_STATE, msg.GetReadData(), msg.GetSize() );
  2795. }
  2796. }
  2797. }
  2798. /*
  2799. ========================
  2800. idSessionLocal::GetNumContentPackages
  2801. ========================
  2802. */
  2803. int idSessionLocal::GetNumContentPackages() const {
  2804. return downloadedContent.Num();
  2805. }
  2806. /*
  2807. ========================
  2808. idSessionLocal::GetContentPackageID
  2809. ========================
  2810. */
  2811. int idSessionLocal::GetContentPackageID( int contentIndex ) const {
  2812. assert( contentIndex < MAX_CONTENT_PACKAGES );
  2813. if ( downloadedContent[ contentIndex ].isMounted ) {
  2814. return downloadedContent[ contentIndex ].dlcID;
  2815. }
  2816. return 0;
  2817. }
  2818. /*
  2819. ========================
  2820. idSessionLocal::GetContentPackagePath
  2821. ========================
  2822. */
  2823. const char * idSessionLocal::GetContentPackagePath( int contentIndex ) const {
  2824. assert( contentIndex < MAX_CONTENT_PACKAGES );
  2825. if ( downloadedContent[ contentIndex ].isMounted ) {
  2826. return downloadedContent[ contentIndex ].rootPath.c_str();
  2827. }
  2828. return NULL;
  2829. }
  2830. /*
  2831. ========================
  2832. idSessionLocal::GetContentPackageIndexForID
  2833. ========================
  2834. */
  2835. int idSessionLocal::GetContentPackageIndexForID( int contentID ) const {
  2836. int contentIndex = -1;
  2837. for ( int i = 0; i < downloadedContent.Num(); i++ ) {
  2838. if ( downloadedContent[i].dlcID == contentID ) {
  2839. contentIndex = i;
  2840. break;
  2841. }
  2842. }
  2843. return contentIndex;
  2844. }
  2845. /*
  2846. ========================
  2847. idSessionLocal::SetLobbyUserRelativeScore
  2848. ========================
  2849. */
  2850. void idSessionLocal::SetLobbyUserRelativeScore( lobbyUserID_t lobbyUserID, int relativeScore, int team ) {
  2851. // All platforms but 360 stub this out
  2852. }
  2853. /*
  2854. ========================
  2855. idSessionLocal::ReadTitleStorage
  2856. ========================
  2857. */
  2858. void idSessionLocal::ReadTitleStorage( void * buffer, int bufferLen ) {
  2859. // https://ps3.scedev.net/projects/ps3_sdk_docs/docs/ps3-en,NP_Lookup-Reference,sceNpLookupTitleSmallStorageAsync/1
  2860. // If the file is not on the server, this will be handled as though a file of 0 bytes were on the server.
  2861. // This means that 0 will be set to contentLength and 0 (for normal termination) will return for the return value.
  2862. // This situation can occur with problems in actual operation, so the application must be designed not to hang up even in such situations
  2863. //bufferLen = 0;
  2864. idLib::Printf( "ReadTitleStorage: %i bytes\n", bufferLen );
  2865. #if !defined( ID_RETAIL ) || defined( ID_RETAIL_INTERNAL )
  2866. if ( net_ignoreTitleStorage.GetBool() ) {//&& idLib::GetProduction() < PROD_PRODUCTION ) {
  2867. idLib::Printf( "ReadTitleStorage: *********************** IGNORING ********************\n" );
  2868. return;
  2869. }
  2870. #endif
  2871. //idScopedGlobalHeap everythingHereGoesInTheGlobalHeap;
  2872. idParser parser( LEXFL_NOERRORS | LEXFL_NOFATALERRORS | LEXFL_NOSTRINGCONCAT );
  2873. parser.LoadMemory( ( const char* )buffer, bufferLen, "default.tss" );
  2874. bool valid = true;
  2875. while ( true ) {
  2876. idToken token;
  2877. if ( !parser.ReadToken( &token ) ) {
  2878. break;
  2879. }
  2880. if ( token.Icmp( "netvars" ) == 0 ) {
  2881. if ( !titleStorageVars.Parse( parser ) ) {
  2882. valid = false;
  2883. break;
  2884. }
  2885. } else {
  2886. valid = false;
  2887. break;
  2888. }
  2889. }
  2890. if ( valid ) {
  2891. titleStorageLoaded = true;
  2892. idLib::Printf( "ReadTitleStorage: SUCCESS\n" );
  2893. titleStorageVars.Print();
  2894. } else {
  2895. titleStorageLoaded = false;
  2896. idLib::Printf( "ReadTitleStorage: FAILED\n" );
  2897. titleStorageVars.Clear();
  2898. }
  2899. }
  2900. /*
  2901. ========================
  2902. idSessionLocal::ReadDLCInfo
  2903. ========================
  2904. */
  2905. bool idSessionLocal::ReadDLCInfo( idDict & dlcInfo, void * buffer, int bufferLen ) {
  2906. idParser parser( LEXFL_NOERRORS | LEXFL_NOFATALERRORS | LEXFL_NOSTRINGCONCAT );
  2907. parser.LoadMemory( ( const char* )buffer, bufferLen, "info.txt" );
  2908. bool valid = true;
  2909. while ( true ) {
  2910. idToken token;
  2911. if ( !parser.ReadToken( &token ) ) {
  2912. break;
  2913. }
  2914. if ( token.Icmp( "dlcInfo" ) == 0 ) {
  2915. if ( !dlcInfo.Parse( parser ) ) {
  2916. valid = false;
  2917. break;
  2918. }
  2919. } else {
  2920. valid = false;
  2921. break;
  2922. }
  2923. }
  2924. return valid;
  2925. }
  2926. /*
  2927. ========================
  2928. idSessionLocal::IsPlatformPartyInLobby
  2929. ========================
  2930. */
  2931. bool idSessionLocal::IsPlatformPartyInLobby() {
  2932. idLocalUser * user = session->GetSignInManager().GetMasterLocalUser();
  2933. idLobby * lobby = GetActivePlatformLobby();
  2934. if ( user == NULL || lobby == NULL ) {
  2935. return false;
  2936. }
  2937. if ( user->GetPartyCount() > MAX_PLAYERS || user->GetPartyCount() < 2 ) {
  2938. return false;
  2939. }
  2940. // TODO: Implement PC
  2941. return false;
  2942. }
  2943. /*
  2944. ========================
  2945. idSessionLocal::PrePickNewHost
  2946. This is called when we have determined that we need to pick a new host.
  2947. Call PickNewHostInternal to continue on with the host picking process.
  2948. ========================
  2949. */
  2950. void idSessionLocal::PrePickNewHost( idLobby & lobby, bool forceMe, bool inviteOldHost ) {
  2951. NET_VERBOSE_PRINT("idSessionLocal::PrePickNewHost: (%s)\n", lobby.GetLobbyName() );
  2952. if ( GetActivePlatformLobby() == NULL ) {
  2953. NET_VERBOSE_PRINT("idSessionLocal::PrePickNewHost: GetActivePlatformLobby() == NULL (%s)\n", lobby.GetLobbyName() );
  2954. return;
  2955. }
  2956. // Check to see if we can migrate AT ALL
  2957. // This is checking for coop, we should make this a specific option (MATCH_ALLOW_MIGRATION)
  2958. if ( GetPartyLobby().parms.matchFlags & MATCH_PARTY_INVITE_PLACEHOLDER ) {
  2959. NET_VERBOSE_PRINT("idSessionLocal::PrePickNewHost: MATCH_PARTY_INVITE_PLACEHOLDER (%s)\n", lobby.GetLobbyName() );
  2960. // Can't migrate, shut both lobbies down, and create a new match using the original parms
  2961. GetGameStateLobby().Shutdown();
  2962. GetGameLobby().Shutdown();
  2963. GetPartyLobby().Shutdown();
  2964. // Throw up the appropriate dialog message so the player knows what happeend
  2965. if ( localState >= idSessionLocal::STATE_LOADING ) {
  2966. NET_VERBOSE_PRINT("idSessionLocal::PrePickNewHost: localState >= idSessionLocal::STATE_LOADING (%s)\n", lobby.GetLobbyName() );
  2967. common->Dialog().AddDialog( GDM_BECAME_HOST_GAME_STATS_DROPPED, DIALOG_ACCEPT, NULL, NULL, false, __FUNCTION__, __LINE__, true );
  2968. } else {
  2969. NET_VERBOSE_PRINT("idSessionLocal::PrePickNewHost: localState < idSessionLocal::STATE_LOADING (%s)\n", lobby.GetLobbyName() );
  2970. common->Dialog().AddDialog( GDM_LOBBY_BECAME_HOST_GAME, DIALOG_ACCEPT, NULL, NULL, false, __FUNCTION__, __LINE__, true );
  2971. }
  2972. CreateMatch( GetActivePlatformLobby()->parms );
  2973. return;
  2974. }
  2975. // Check to see if the match is searchable
  2976. if ( GetState() >= idSession::GAME_LOBBY && MatchTypeIsSearchable( GetGameLobby().parms.matchFlags ) ) {
  2977. NET_VERBOSE_PRINT("idSessionLocal::PrePickNewHost: MatchTypeIsSearchable (%s)\n", lobby.GetLobbyName() );
  2978. // Searchable games migrate lobbies independently, and don't need to stay in sync
  2979. lobby.PickNewHostInternal( forceMe, inviteOldHost );
  2980. return;
  2981. }
  2982. //
  2983. // Beyond this point, game lobbies must be sync'd with party lobbies as far as host status
  2984. // So to enforce that, we pull you out of the game lobby if you are in one when migration occurs
  2985. //
  2986. // Check to see if we should go back to a party lobby
  2987. if ( GetBackState() >= idSessionLocal::PARTY_LOBBY || GetState() == idSession::PARTY_LOBBY ) {
  2988. NET_VERBOSE_PRINT("idSessionLocal::PrePickNewHost: GetBackState() >= idSessionLocal::PARTY_LOBBY || GetState() == idSession::PARTY_LOBBY (%s)\n", lobby.GetLobbyName() );
  2989. // Force the party lobby to start picking a new host if we lost the game lobby host
  2990. GetPartyLobby().PickNewHostInternal( forceMe, inviteOldHost );
  2991. // End the game lobby, and go back to party lobby
  2992. GetGameStateLobby().Shutdown();
  2993. GetGameLobby().Shutdown();
  2994. SetState( GetPartyLobby().IsHost() ? idSessionLocal::STATE_PARTY_LOBBY_HOST : idSessionLocal::STATE_PARTY_LOBBY_PEER );
  2995. } else {
  2996. NET_VERBOSE_PRINT("idSessionLocal::PrePickNewHost: GetBackState() < idSessionLocal::PARTY_LOBBY && GetState() != idSession::PARTY_LOBBY (%s)\n", lobby.GetLobbyName() );
  2997. if ( localState >= idSessionLocal::STATE_LOADING ) {
  2998. common->Dialog().AddDialog( GDM_HOST_QUIT, DIALOG_ACCEPT, NULL, NULL, false, __FUNCTION__, __LINE__, true ); // The host has quit the session. Returning to the main menu.
  2999. }
  3000. // Go back to main menu
  3001. GetGameLobby().Shutdown();
  3002. GetGameStateLobby().Shutdown();
  3003. GetPartyLobby().Shutdown();
  3004. SetState( idSessionLocal::STATE_IDLE );
  3005. }
  3006. }
  3007. /*
  3008. ========================
  3009. idSessionLocal::PreMigrateInvite
  3010. This is called just before we get invited to a migrated session
  3011. If we return false, the invite will be ignored
  3012. ========================
  3013. */
  3014. bool idSessionLocal::PreMigrateInvite( idLobby & lobby )
  3015. {
  3016. if ( GetActivePlatformLobby() == NULL ) {
  3017. return false;
  3018. }
  3019. // Check to see if we can migrate AT ALL
  3020. // This is checking for coop, we should make this a specific option (MATCH_ALLOW_MIGRATION)
  3021. if ( !verify( ( GetPartyLobby().parms.matchFlags & MATCH_PARTY_INVITE_PLACEHOLDER ) == 0 ) ) {
  3022. return false; // Shouldn't get invites for coop (we should make this a specific option (MATCH_ALLOW_MIGRATION))
  3023. }
  3024. // Check to see if the match is searchable
  3025. if ( MatchTypeIsSearchable( GetGameLobby().parms.matchFlags ) ) {
  3026. // Searchable games migrate lobbies independently, and don't need to stay in sync
  3027. return true;
  3028. }
  3029. //
  3030. // Beyond this point, game lobbies must be sync'd with party lobbies as far as host status
  3031. // So to enforce that, we pull you out of the game lobby if you are in one when migration occurs
  3032. //
  3033. if ( lobby.lobbyType != idLobby::TYPE_PARTY ) {
  3034. return false; // We shouldn't be getting invites from non party lobbies when in a non searchable game
  3035. }
  3036. // Non placeholder Party lobbies can always migrate
  3037. if ( GetBackState() >= idSessionLocal::PARTY_LOBBY ) {
  3038. // Non searchable games go back to the party lobby
  3039. GetGameLobby().Shutdown();
  3040. SetState( GetPartyLobby().IsHost() ? idSessionLocal::STATE_PARTY_LOBBY_HOST : idSessionLocal::STATE_PARTY_LOBBY_PEER );
  3041. }
  3042. return true; // Non placeholder Party lobby invites joinable
  3043. }
  3044. /*
  3045. ================================================================================================
  3046. lobbyAddress_t
  3047. ================================================================================================
  3048. */
  3049. /*
  3050. ========================
  3051. lobbyAddress_t::lobbyAddress_t
  3052. ========================
  3053. */
  3054. lobbyAddress_t::lobbyAddress_t() {
  3055. memset( &netAddr, 0, sizeof( netAddr ) );
  3056. netAddr.type = NA_BAD;
  3057. }
  3058. /*
  3059. ========================
  3060. lobbyAddress_t::InitFromIPandPort
  3061. ========================
  3062. */
  3063. void lobbyAddress_t::InitFromIPandPort( const char * ip, int port ) {
  3064. Sys_StringToNetAdr( ip, &netAddr, true );
  3065. if ( !netAddr.port ) {
  3066. netAddr.port = port;
  3067. }
  3068. }
  3069. /*
  3070. ========================
  3071. lobbyAddress_t::InitFromNetadr
  3072. ========================
  3073. */
  3074. void lobbyAddress_t::InitFromNetadr( const netadr_t & netadr ) {
  3075. assert( netadr.type != NA_BAD );
  3076. netAddr = netadr;
  3077. }
  3078. /*
  3079. ========================
  3080. lobbyAddress_t::ToString
  3081. ========================
  3082. */
  3083. const char * lobbyAddress_t::ToString() const {
  3084. return Sys_NetAdrToString( netAddr );
  3085. }
  3086. /*
  3087. ========================
  3088. lobbyAddress_t::UsingRelay
  3089. ========================
  3090. */
  3091. bool lobbyAddress_t::UsingRelay() const {
  3092. return false;
  3093. }
  3094. /*
  3095. ========================
  3096. lobbyAddress_t::Compare
  3097. ========================
  3098. */
  3099. bool lobbyAddress_t::Compare( const lobbyAddress_t & addr, bool ignoreSessionCheck ) const {
  3100. return Sys_CompareNetAdrBase( netAddr, addr.netAddr );
  3101. }
  3102. /*
  3103. ========================
  3104. lobbyAddress_t::WriteToMsg
  3105. ========================
  3106. */
  3107. void lobbyAddress_t::WriteToMsg( idBitMsg & msg ) const {
  3108. msg.WriteData( &netAddr, sizeof( netAddr ) );
  3109. }
  3110. /*
  3111. ========================
  3112. lobbyAddress_t::ReadFromMsg
  3113. ========================
  3114. */
  3115. void lobbyAddress_t::ReadFromMsg( idBitMsg & msg ) {
  3116. msg.ReadData( &netAddr, sizeof( netAddr ) );
  3117. }
  3118. /*
  3119. ================================================================================================
  3120. idNetSessionPort
  3121. ================================================================================================
  3122. */
  3123. /*
  3124. ========================
  3125. idNetSessionPort::idNetSessionPort
  3126. ========================
  3127. */
  3128. idNetSessionPort::idNetSessionPort() :
  3129. forcePacketDropPrev( 0.0f ),
  3130. forcePacketDropCurr( 0.0f )
  3131. {
  3132. }
  3133. /*
  3134. ========================
  3135. idNetSessionPort::InitPort
  3136. ========================
  3137. */
  3138. bool idNetSessionPort::InitPort( int portNumber, bool useBackend ) {
  3139. return UDP.InitForPort( portNumber );
  3140. }
  3141. /*
  3142. ========================
  3143. idNetSessionPort::ReadRawPacket
  3144. ========================
  3145. */
  3146. bool idNetSessionPort::ReadRawPacket( lobbyAddress_t & from, void * data, int & size, int maxSize ) {
  3147. bool result = UDP.GetPacket( from.netAddr, data, size, maxSize );
  3148. static idRandom2 random( Sys_Milliseconds() );
  3149. if ( net_forceDrop.GetInteger() != 0 ) {
  3150. forcePacketDropCurr = random.RandomInt( 100 );
  3151. if ( net_forceDrop.GetInteger() >= forcePacketDropCurr ) {
  3152. return false;
  3153. }
  3154. }
  3155. return result;
  3156. }
  3157. /*
  3158. ========================
  3159. idNetSessionPort::SendRawPacket
  3160. ========================
  3161. */
  3162. void idNetSessionPort::SendRawPacket( const lobbyAddress_t & to, const void * data, int size ) {
  3163. static idRandom2 random( Sys_Milliseconds() );
  3164. if ( net_forceDrop.GetInteger() != 0 && net_forceDrop.GetInteger() >= random.RandomInt( 100 ) ) {
  3165. return;
  3166. }
  3167. assert( size <= idPacketProcessor::MAX_FINAL_PACKET_SIZE );
  3168. UDP.SendPacket( to.netAddr, data, size );
  3169. }
  3170. /*
  3171. ========================
  3172. idNetSessionPort::IsOpen
  3173. ========================
  3174. */
  3175. bool idNetSessionPort::IsOpen() {
  3176. return UDP.IsOpen();
  3177. }
  3178. /*
  3179. ========================
  3180. idNetSessionPort::Close
  3181. ========================
  3182. */
  3183. void idNetSessionPort::Close() {
  3184. UDP.Close();
  3185. }
  3186. /*
  3187. ================================================================================================
  3188. Commands
  3189. ================================================================================================
  3190. */
  3191. //====================================================================================
  3192. CONSOLE_COMMAND( voicechat_mute, "TEMP", 0 ) {
  3193. if ( args.Argc() != 2 ) {
  3194. idLib::Printf( "Usage: voicechat_mute <user index>\n" );
  3195. return;
  3196. }
  3197. int i = atoi( args.Argv( 1 ) );
  3198. session->ToggleLobbyUserVoiceMute( session->GetActivePlatformLobbyBase().GetLobbyUserIdByOrdinal( i ) );
  3199. }
  3200. /*
  3201. ========================
  3202. force_disconnect_all
  3203. ========================
  3204. */
  3205. CONSOLE_COMMAND( force_disconnect_all, "force disconnect on all users", 0 ) {
  3206. session->GetSignInManager().RemoveAllLocalUsers();
  3207. }
  3208. /*
  3209. ========================
  3210. void Net_DebugOutputSignedInUsers_f
  3211. ========================
  3212. */
  3213. void Net_DebugOutputSignedInUsers_f( const idCmdArgs &args ) {
  3214. session->GetSignInManager().DebugOutputLocalUserInfo();
  3215. }
  3216. idCommandLink Net_DebugOutputSignedInUsers( "net_debugOutputSignedInUsers", Net_DebugOutputSignedInUsers_f, "Outputs all the local users and other debugging information from the sign in manager" );
  3217. /*
  3218. ========================
  3219. void Net_RemoveUserFromLobby_f
  3220. ========================
  3221. */
  3222. void Net_RemoveUserFromLobby_f( const idCmdArgs &args ) {
  3223. if ( args.Argc() > 1 ) {
  3224. int localUserNum = atoi( args.Argv( 1 ) );
  3225. if ( localUserNum < session->GetSignInManager().GetNumLocalUsers() ) {
  3226. session->GetSignInManager().RemoveLocalUserByIndex( localUserNum );
  3227. } else {
  3228. idLib::Printf( "This user is not in the lobby\n" );
  3229. }
  3230. } else {
  3231. idLib::Printf( "Usage: net_RemoveUserFromLobby <localUserNum>\n" );
  3232. }
  3233. }
  3234. idCommandLink Net_RemoveUserFromLobby( "net_removeUserFromLobby", Net_RemoveUserFromLobby_f, "Removes the given user from the lobby" );
  3235. /*
  3236. ========================
  3237. Net_dropClient
  3238. ========================
  3239. */
  3240. CONSOLE_COMMAND( Net_DropClient, "Drop a client", 0 ) {
  3241. if ( args.Argc() < 3 ) {
  3242. idLib::Printf( "usage: Net_DropClient <clientnum> [<session>] 0/default: drop from game, 1: drop from party, otherwise drop from both\n" );
  3243. return;
  3244. }
  3245. int lobbyType = 0;
  3246. if ( args.Argc() > 2 ) {
  3247. lobbyType = atoi( args.Argv( 2 ) );
  3248. }
  3249. session->DropClient( atoi( args.Argv(1) ), lobbyType );
  3250. }
  3251. /*
  3252. ========================
  3253. idSessionLocal::DropClient
  3254. ========================
  3255. */
  3256. void idSessionLocal::DropClient( int peerNum, int session ) {
  3257. if ( session == 1 || session >= 2 ) {
  3258. GetPartyLobby().DisconnectPeerFromSession( peerNum );
  3259. }
  3260. if ( session == 0 || session >= 2 ) {
  3261. GetGameLobby().DisconnectPeerFromSession( peerNum );
  3262. }
  3263. }
  3264. /*
  3265. ========================
  3266. idSessionLocal::ListServersCommon
  3267. ========================
  3268. */
  3269. void idSessionLocal::ListServersCommon() {
  3270. netadr_t broadcast;
  3271. memset( &broadcast, 0, sizeof( broadcast ) );
  3272. broadcast.type = NA_BROADCAST;
  3273. broadcast.port = net_port.GetInteger();
  3274. lobbyAddress_t address;
  3275. address.InitFromNetadr( broadcast );
  3276. byte buffer[ idPacketProcessor::MAX_PACKET_SIZE - 2 ];
  3277. idBitMsg msg( buffer, sizeof( buffer ) );
  3278. // Add the current version info to the query
  3279. const unsigned long localChecksum = NetGetVersionChecksum();
  3280. NET_VERBOSE_PRINT( "ListServers: Hash checksum: %i, broadcasting to: %s\n", localChecksum, address.ToString() );
  3281. msg.WriteLong( localChecksum );
  3282. GetPort();
  3283. // Send the query as a broadcast
  3284. GetPartyLobby().SendConnectionLess( address, idLobby::OOB_MATCH_QUERY, msg.GetReadData(), msg.GetSize() );
  3285. }
  3286. /*
  3287. ========================
  3288. idSessionLocal::HandleDedicatedServerQueryRequest
  3289. ========================
  3290. */
  3291. void idSessionLocal::HandleDedicatedServerQueryRequest( lobbyAddress_t & remoteAddr, idBitMsg & msg, int msgType ) {
  3292. NET_VERBOSE_PRINT( "HandleDedicatedServerQueryRequest from %s\n", remoteAddr.ToString() );
  3293. bool canJoin = true;
  3294. const unsigned long localChecksum = NetGetVersionChecksum();
  3295. const unsigned long remoteChecksum = msg.ReadLong();
  3296. if ( remoteChecksum != localChecksum ) {
  3297. NET_VERBOSE_PRINT( "HandleServerQueryRequest: Invalid version from %s\n", remoteAddr.ToString() );
  3298. canJoin = false;
  3299. }
  3300. // Make sure we are the host of this party session
  3301. if ( !GetPartyLobby().IsHost() ) {
  3302. NET_VERBOSE_PRINT( "HandleServerQueryRequest: Not host of party\n" );
  3303. canJoin = false;
  3304. }
  3305. // Make sure there is a session active
  3306. if ( GetActivePlatformLobby() == NULL ) {
  3307. canJoin = false;
  3308. }
  3309. // Make sure we have enough free slots
  3310. if ( GetPartyLobby().NumFreeSlots() == 0 || GetGameLobby().NumFreeSlots() == 0 ) {
  3311. NET_VERBOSE_PRINT( "No free slots\n" );
  3312. canJoin = false;
  3313. }
  3314. if ( MatchTypeInviteOnly( GetPartyLobby().parms.matchFlags ) ) {
  3315. canJoin = false;
  3316. }
  3317. // Buffer to hold reply msg
  3318. byte buffer[ idPacketProcessor::MAX_PACKET_SIZE - 2 ];
  3319. idBitMsg retmsg( buffer, sizeof( buffer ) );
  3320. idLocalUser * masterUser = GetSignInManager().GetMasterLocalUser();
  3321. if ( masterUser == NULL && !net_headlessServer.GetBool() ) {
  3322. canJoin = false;
  3323. }
  3324. // Send the info about this game session to the caller
  3325. retmsg.WriteBool( canJoin );
  3326. if ( canJoin ) {
  3327. serverInfo_t serverInfo;
  3328. serverInfo.joinable = ( session->GetState() >= idSession::LOADING );
  3329. if ( !net_headlessServer.GetBool() ) {
  3330. serverInfo.serverName = masterUser->GetGamerTag();
  3331. }
  3332. if ( GetGameLobby().IsLobbyActive() ) {
  3333. serverInfo.gameMap = GetGameLobby().parms.gameMap;
  3334. serverInfo.gameMode = GetGameLobby().parms.gameMode;
  3335. } else {
  3336. serverInfo.gameMode = -1;
  3337. }
  3338. serverInfo.numPlayers = GetActivePlatformLobby()->GetNumLobbyUsers();
  3339. serverInfo.maxPlayers = GetActivePlatformLobby()->parms.numSlots;
  3340. serverInfo.Write( retmsg );
  3341. for ( int i = 0; i < GetActivePlatformLobby()->GetNumLobbyUsers(); i++ ) {
  3342. retmsg.WriteString( GetActivePlatformLobby()->GetLobbyUserName( GetActivePlatformLobby()->GetLobbyUserIdByOrdinal( i ) ) );
  3343. }
  3344. }
  3345. // Send it
  3346. GetPartyLobby().SendConnectionLess( remoteAddr, idLobby::OOB_MATCH_QUERY_ACK, retmsg.GetReadData(), retmsg.GetSize() );
  3347. }
  3348. /*
  3349. ========================
  3350. idSessionLocal::HandleDedicatedServerQueryAck
  3351. ========================
  3352. */
  3353. void idSessionLocal::HandleDedicatedServerQueryAck( lobbyAddress_t & remoteAddr, idBitMsg & msg ) {
  3354. NET_VERBOSE_PRINT( "HandleDedicatedServerQueryAck from %s\n", remoteAddr.ToString() );
  3355. dedicatedServerSearch->HandleQueryAck( remoteAddr, msg );
  3356. }
  3357. /*
  3358. ========================
  3359. idSessionLocal::ServerPlayerList
  3360. ========================
  3361. */
  3362. const idList< idStr > * idSessionLocal::ServerPlayerList( int i ) {
  3363. return NULL;
  3364. }
  3365. /*
  3366. ========================
  3367. lobbyUserID_t::Serialize
  3368. ========================
  3369. */
  3370. void lobbyUserID_t::Serialize( idSerializer & ser ) {
  3371. localUserHandle.Serialize( ser );
  3372. ser.Serialize( lobbyType );
  3373. }