sys_session_callbacks.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  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 "../framework/Common_local.h"
  23. #include "sys_session_local.h"
  24. // The more the idLobby class needs to call back into this class, the more likely we're doing something wrong, and there is a better way.
  25. /*
  26. ========================
  27. idSessionLocalCallbacks::BecomingHost
  28. This is called when
  29. ========================
  30. */
  31. bool idSessionLocalCallbacks::BecomingHost( idLobby & lobby ) {
  32. if ( lobby.lobbyType == idLobby::TYPE_GAME ) {
  33. if ( sessionLocal->GetActivePlatformLobby() != &lobby ) {
  34. idLib::Printf( "BecomingHost: Must be past the party lobby to become host of a game lobby.\n" );
  35. return false;
  36. }
  37. if ( sessionLocal->localState == idSessionLocal::STATE_INGAME || sessionLocal->localState == idSessionLocal::STATE_LOADING ) {
  38. // If we are in a game, go back to the lobby before becoming the new host of a game lobby
  39. sessionLocal->SetState( idSessionLocal::STATE_GAME_LOBBY_PEER );
  40. // session mgr housekeeping that would usually be done through the standard EndMatch path
  41. sessionLocal->EndMatchForMigration();
  42. }
  43. }
  44. return true;
  45. }
  46. /*
  47. ========================
  48. idSessionLocalCallbacks::BecameHost
  49. ========================
  50. */
  51. void idSessionLocalCallbacks::BecameHost( idLobby & lobby ) {
  52. // If we were in the lobby when we switched to host, then set the right state
  53. if ( lobby.lobbyType == idLobby::TYPE_PARTY && sessionLocal->localState == idSessionLocal::STATE_PARTY_LOBBY_PEER ) {
  54. sessionLocal->SetState( idSessionLocal::STATE_PARTY_LOBBY_HOST );
  55. } else if ( lobby.lobbyType == idLobby::TYPE_GAME && sessionLocal->localState == idSessionLocal::STATE_GAME_LOBBY_PEER ) {
  56. sessionLocal->SetState( idSessionLocal::STATE_GAME_LOBBY_HOST );
  57. }
  58. }
  59. /*
  60. ========================
  61. idSessionLocalCallbacks::BecomingPeer
  62. ========================
  63. */
  64. bool idSessionLocalCallbacks::BecomingPeer( idLobby & lobby ) {
  65. if ( lobby.lobbyType == idLobby::TYPE_GAME ) {
  66. if ( sessionLocal->localState == idSessionLocal::STATE_INGAME || sessionLocal->localState == idSessionLocal::STATE_LOADING ) {
  67. // Go to the party lobby while we try to connect to the new host
  68. // This isn't totally necessary but we want to end the current game now and go to some screen.
  69. // When the connection goes through or fails will send the session mgr to the appropriate state (game lobby or main menu)
  70. // What happens if we got the game migration before the party migration?
  71. sessionLocal->SetState( sessionLocal->GetPartyLobby().IsHost() ? idSessionLocal::STATE_PARTY_LOBBY_HOST : idSessionLocal::STATE_PARTY_LOBBY_PEER );
  72. // session mgr housekeeping that would usually be done through the standard EndMatch path
  73. sessionLocal->EndMatchForMigration();
  74. return true; // return true tells the session that we want him to tell us when the connects/fails
  75. }
  76. }
  77. return false;
  78. }
  79. /*
  80. ========================
  81. idSessionLocalCallbacks::BecamePeer
  82. ========================
  83. */
  84. void idSessionLocalCallbacks::BecamePeer( idLobby & lobby ) {
  85. if ( lobby.lobbyType == idLobby::TYPE_GAME ) {
  86. sessionLocal->SetState( idSessionLocal::STATE_GAME_LOBBY_PEER );
  87. }
  88. }
  89. /*
  90. ========================
  91. idSessionLocalCallbacks::FailedGameMigration
  92. ========================
  93. */
  94. void idSessionLocalCallbacks::FailedGameMigration( idLobby & lobby ) {
  95. // We failed to complete a game migration this could happen for a couple reasons:
  96. // -The network invites failed / failed to join migrated session
  97. // -There was nobody to invite
  98. lobby.ResetAllMigrationState();
  99. if ( lobby.lobbyType == idLobby::TYPE_GAME ) { // this check is a redundant since we should only get this CB from the game session
  100. sessionLocal->SetState( idSessionLocal::STATE_GAME_LOBBY_HOST );
  101. // Make sure the sessions are joinable again
  102. sessionLocal->EndSessions();
  103. }
  104. }
  105. /*
  106. ========================
  107. idSessionLocalCallbacks::MigrationEnded
  108. ========================
  109. */
  110. void idSessionLocalCallbacks::MigrationEnded( idLobby & lobby ) {
  111. if ( lobby.migrationInfo.persistUntilGameEndsData.wasMigratedGame ) {
  112. #if 1
  113. if ( lobby.lobbyType == idLobby::TYPE_GAME || ( lobby.lobbyType == idLobby::TYPE_PARTY && session->GetState() <= idSession::PARTY_LOBBY ) ) {
  114. common->Dialog().ClearDialog( GDM_MIGRATING );
  115. common->Dialog().ClearDialog( GDM_MIGRATING_WAITING );
  116. common->Dialog().ClearDialog( GDM_MIGRATING_RELAUNCHING );
  117. if ( lobby.GetNumLobbyUsers() <= 1 ) {
  118. if ( MatchTypeHasStats( lobby.parms.matchFlags ) ) {
  119. common->Dialog().AddDialog( GDM_MIGRATING_FAILED_DISBANDED_STATS, DIALOG_ACCEPT, NULL, NULL, false, "", 0, true ); // Game has disbanded
  120. } else {
  121. common->Dialog().AddDialog( GDM_MIGRATING_FAILED_DISBANDED, DIALOG_ACCEPT, NULL, NULL, false, "", 0, true ); // Game has disbanded
  122. }
  123. } else {
  124. //common->Dialog().AddDialog( GDM_MIGRATING_FAILED_CONNECTION, DIALOG_ACCEPT, NULL, NULL, false, "", 0, true ); // Lost connection to game
  125. if ( lobby.lobbyType == idLobby::TYPE_GAME && MatchTypeHasStats( lobby.parms.matchFlags ) ) {
  126. // This means we came from a public match, so tell them they didn't lose stats
  127. common->Dialog().AddDialog( GDM_HOST_CONNECTION_LOST_STATS, DIALOG_ACCEPT, NULL, NULL, false, "", 0, true ); // The connection to the host has been lost. This game will not count towards your ranking.
  128. } else {
  129. // This means we came from a private match, just say host quit
  130. common->Dialog().AddDialog( GDM_HOST_CONNECTION_LOST, DIALOG_ACCEPT, NULL, NULL, false, "", 0, true ); // The connection to the host has been lost.
  131. }
  132. }
  133. lobby.ResetAllMigrationState();
  134. // Make sure the sessions are joinable again
  135. sessionLocal->EndSessions();
  136. }
  137. #else
  138. // If we get here, we migrated from a game
  139. if ( lobby.GetNumLobbyUsers() <= 1 && lobby.lobbyType == idLobby::TYPE_GAME ) {
  140. if ( !MatchTypeIsJoinInProgress( lobby.parms.matchFlags ) ) {
  141. // Handles 'soft' failed game migration where we migrated from a game and are now alone
  142. gameDialogMessages_t errorDlg = GDM_INVALID;
  143. lobby.migrationInfo.persistUntilGameEndsData.hasGameData = false; // never restart the game if we are by ourselves
  144. if ( lobby.migrationInfo.invites.Num() > 0 ) {
  145. // outstanding invites: migration failed
  146. errorDlg = ( MatchTypeHasStats( lobby.migrateMsgFlags ) && ( sessionLocal->GetFlushedStats() == false ) ) ? GDM_MIGRATING_FAILED_CONNECTION_STATS : GDM_MIGRATING_FAILED_CONNECTION;
  147. } else {
  148. // there was no one to invite
  149. errorDlg = ( MatchTypeHasStats( lobby.migrateMsgFlags ) && ( sessionLocal->GetFlushedStats() == false ) ) ? GDM_MIGRATING_FAILED_DISBANDED_STATS : GDM_MIGRATING_FAILED_DISBANDED;
  150. }
  151. if ( errorDlg != GDM_INVALID ) {
  152. common->Dialog().AddDialog( errorDlg, DIALOG_ACCEPT, NULL, NULL, false );
  153. }
  154. common->Dialog().ClearDialog( GDM_MIGRATING );
  155. common->Dialog().ClearDialog( GDM_MIGRATING_WAITING );
  156. common->Dialog().ClearDialog( GDM_MIGRATING_RELAUNCHING );
  157. FailedGameMigration( lobby );
  158. }
  159. } else if ( lobby.lobbyType == idLobby::TYPE_PARTY ) {
  160. if ( session->GetState() <= idSession::PARTY_LOBBY ) {
  161. // We got dropped the party lobby, let them know what happened
  162. common->Dialog().ClearDialog( GDM_MIGRATING );
  163. common->Dialog().ClearDialog( GDM_MIGRATING_WAITING );
  164. common->Dialog().ClearDialog( GDM_MIGRATING_RELAUNCHING );
  165. if ( lobby.GetNumLobbyUsers() <= 1 ) {
  166. common->Dialog().AddDialog( GDM_MIGRATING_FAILED_DISBANDED, DIALOG_ACCEPT, NULL, NULL, false, "", 0, true ); // Game has disbanded
  167. } else {
  168. //common->Dialog().AddDialog( GDM_MIGRATING_FAILED_CONNECTION, DIALOG_ACCEPT, NULL, NULL, false, "", 0, true ); // Lost connection to game
  169. common->Dialog().AddDialog( GDM_HOST_CONNECTION_LOST_STATS, DIALOG_ACCEPT, NULL, NULL, false, "", 0, true );
  170. }
  171. lobby.ResetAllMigrationState();
  172. // Make sure the sessions are joinable again
  173. sessionLocal->EndSessions();
  174. }
  175. }
  176. #endif
  177. } else if ( lobby.GetNumLobbyUsers() <= 1 && session->GetState() == idSession::PARTY_LOBBY ) {
  178. // If they didn't come from a game, and are by themselves, just show the lobby disband msg
  179. common->Dialog().AddDialog( GDM_LOBBY_DISBANDED, DIALOG_ACCEPT, NULL, NULL, false, "", 0, true ); // The lobby you were previously in has disbanded
  180. // Make sure the sessions are joinable again
  181. sessionLocal->EndSessions();
  182. }
  183. }
  184. /*
  185. ========================
  186. idSessionLocalCallbacks::GoodbyeFromHost
  187. ========================
  188. */
  189. void idSessionLocalCallbacks::GoodbyeFromHost( idLobby & lobby, int peerNum, const lobbyAddress_t & remoteAddress, int msgType ) {
  190. sessionLocal->GoodbyeFromHost( lobby, peerNum, remoteAddress, msgType );
  191. }
  192. /*
  193. ========================
  194. idSessionLocalCallbacks::AnyPeerHasAddress
  195. ========================
  196. */
  197. bool idSessionLocalCallbacks::AnyPeerHasAddress( const lobbyAddress_t & remoteAddress ) const {
  198. return sessionLocal->GetPartyLobby().FindAnyPeer( remoteAddress ) || sessionLocal->GetGameLobby().FindAnyPeer( remoteAddress );
  199. }
  200. /*
  201. ========================
  202. idSessionLocalCallbacks::RecvLeaderboardStats
  203. ========================
  204. */
  205. void idSessionLocalCallbacks::RecvLeaderboardStats( idBitMsg & msg ) {
  206. // Steam and PS3 just write them as they come per player, they don't need to flush
  207. sessionLocal->RecvLeaderboardStatsForPlayer( msg );
  208. }
  209. /*
  210. ========================
  211. idSessionLocalCallbacks::ReceivedFullSnap
  212. ========================
  213. */
  214. void idSessionLocalCallbacks::ReceivedFullSnap() {
  215. // If we received a full snap, then we can transition into the INGAME state
  216. sessionLocal->numFullSnapsReceived++;
  217. if ( sessionLocal->numFullSnapsReceived < 2 ) {
  218. return;
  219. }
  220. if ( sessionLocal->localState != idSessionLocal::STATE_INGAME ) {
  221. sessionLocal->GetActingGameStateLobby().QueueReliableMessage( sessionLocal->GetActingGameStateLobby().host, idLobby::RELIABLE_IN_GAME ); // Let host know we are in game now
  222. sessionLocal->SetState( idSessionLocal::STATE_INGAME );
  223. }
  224. }
  225. /*
  226. ========================
  227. idSessionLocalCallbacks::LeaveGameLobby
  228. ========================
  229. */
  230. void idSessionLocalCallbacks::LeaveGameLobby() {
  231. // Make sure we're in the game lobby
  232. if ( session->GetState() != idSession::GAME_LOBBY ) {
  233. return;
  234. }
  235. // If we're the host of the party, only we are allowed to make this call
  236. if ( sessionLocal->GetPartyLobby().IsHost() ) {
  237. return;
  238. }
  239. sessionLocal->GetGameLobby().Shutdown();
  240. sessionLocal->SetState( idSessionLocal::STATE_PARTY_LOBBY_PEER );
  241. }
  242. /*
  243. ========================
  244. idSessionLocalCallbacks::PrePickNewHost
  245. This is called when we have determined that we need to pick a new host.
  246. Call PickNewHostInternal to continue on with the host picking process.
  247. ========================
  248. */
  249. void idSessionLocalCallbacks::PrePickNewHost( idLobby & lobby, bool forceMe, bool inviteOldHost ) {
  250. sessionLocal->PrePickNewHost( lobby, forceMe, inviteOldHost );
  251. }
  252. /*
  253. ========================
  254. idSessionLocalCallbacks::PreMigrateInvite
  255. This is called just before we get invited to a migrated session
  256. If we return false, the invite will be ignored
  257. ========================
  258. */
  259. bool idSessionLocalCallbacks::PreMigrateInvite( idLobby & lobby ) {
  260. return sessionLocal->PreMigrateInvite( lobby );
  261. }
  262. /*
  263. ========================
  264. idSessionLocalCallbacks::ConnectAndMoveToLobby
  265. ========================
  266. */
  267. void idSessionLocalCallbacks::ConnectAndMoveToLobby( idLobby::lobbyType_t destLobbyType, const lobbyConnectInfo_t & connectInfo, bool waitForPartyOk ) {
  268. // See if we are already in the game lobby
  269. idLobby * lobby = sessionLocal->GetLobbyFromType( destLobbyType );
  270. if ( lobby == NULL ) {
  271. idLib::Printf( "RELIABLE_CONNECT_AND_MOVE_TO_LOBBY: Invalid lobby type.\n" );
  272. return;
  273. }
  274. if ( lobby->lobbyBackend != NULL && lobby->lobbyBackend->IsOwnerOfConnectInfo( connectInfo ) ) {
  275. idLib::Printf( "RELIABLE_CONNECT_AND_MOVE_TO_LOBBY: Already in lobby.\n" );
  276. return;
  277. }
  278. // See if we are in a game, or loading into a game. If so, ignore invites from our party host
  279. if ( destLobbyType == idLobby::TYPE_GAME || destLobbyType == idLobby::TYPE_GAME_STATE ) {
  280. if ( GetState() == idSession::INGAME || GetState() == idSession::LOADING ) {
  281. idLib::Printf( "RELIABLE_CONNECT_AND_MOVE_TO_LOBBY: In a different game, ignoring.\n" );
  282. return;
  283. }
  284. }
  285. // End current game lobby
  286. lobby->Shutdown();
  287. // waitForPartyOk will be true if the party host wants us to wait for his ok to stay in the lobby
  288. lobby->waitForPartyOk = waitForPartyOk;
  289. // Connect to new game lobby
  290. sessionLocal->ConnectAndMoveToLobby( *lobby, connectInfo, true ); // Consider this an invite if party host told us to connect
  291. }
  292. /*
  293. ========================
  294. idSessionLocalCallbacks::HandleServerQueryRequest
  295. ========================
  296. */
  297. void idSessionLocalCallbacks::HandleServerQueryRequest( lobbyAddress_t & remoteAddr, idBitMsg & msg, int msgType ) {
  298. sessionLocal->HandleServerQueryRequest( remoteAddr, msg, msgType );
  299. }
  300. /*
  301. ========================
  302. idSessionLocalCallbacks::HandleServerQueryAck
  303. ========================
  304. */
  305. void idSessionLocalCallbacks::HandleServerQueryAck( lobbyAddress_t & remoteAddr, idBitMsg & msg ) {
  306. sessionLocal->HandleServerQueryAck( remoteAddr, msg );
  307. }
  308. extern idCVar net_headlessServer;
  309. /*
  310. ========================
  311. idSessionLocalCallbacks::HandlePeerMatchParamUpdate
  312. ========================
  313. */
  314. void idSessionLocalCallbacks::HandlePeerMatchParamUpdate( int peer, int msg ) {
  315. if ( net_headlessServer.GetBool() ) {
  316. sessionLocal->storedPeer = peer;
  317. sessionLocal->storedMsgType = msg;
  318. }
  319. }
  320. /*
  321. ========================
  322. idSessionLocalCallbacks::CreateLobbyBackend
  323. ========================
  324. */
  325. idLobbyBackend * idSessionLocalCallbacks::CreateLobbyBackend( const idMatchParameters & p, float skillLevel, idLobbyBackend::lobbyBackendType_t lobbyType ) {
  326. return sessionLocal->CreateLobbyBackend( p, skillLevel, lobbyType );
  327. }
  328. /*
  329. ========================
  330. idSessionLocalCallbacks::FindLobbyBackend
  331. ========================
  332. */
  333. idLobbyBackend * idSessionLocalCallbacks::FindLobbyBackend( const idMatchParameters & p, int numPartyUsers, float skillLevel, idLobbyBackend::lobbyBackendType_t lobbyType ) {
  334. return sessionLocal->FindLobbyBackend( p, numPartyUsers, skillLevel, lobbyType );
  335. }
  336. /*
  337. ========================
  338. idSessionLocalCallbacks::JoinFromConnectInfo
  339. ========================
  340. */
  341. idLobbyBackend * idSessionLocalCallbacks::JoinFromConnectInfo( const lobbyConnectInfo_t & connectInfo , idLobbyBackend::lobbyBackendType_t lobbyType ) {
  342. return sessionLocal->JoinFromConnectInfo( connectInfo, lobbyType );
  343. }
  344. /*
  345. ========================
  346. idSessionLocalCallbacks::DestroyLobbyBackend
  347. ========================
  348. */
  349. void idSessionLocalCallbacks::DestroyLobbyBackend( idLobbyBackend * lobbyBackend ) {
  350. sessionLocal->DestroyLobbyBackend( lobbyBackend );
  351. }