Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1096 lines
33 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "gcsdk/gcsdk_auto.h"
  8. #include "tf_party.h"
  9. #include "rtime.h"
  10. #include "gcsdk/accountdetails.h"
  11. #include "tf_quickplay_shared.h"
  12. #include "tf_match_description.h"
  13. #ifdef GC
  14. #include "tf_matchmaker.h"
  15. #include "tf_lobbymanager.h"
  16. #endif
  17. using namespace GCSDK;
  18. //---------------------------------------------------------------------------------
  19. #ifdef GC
  20. IMPLEMENT_CLASS_MEMPOOL( CTFParty, 1000, UTLMEMORYPOOL_GROW_SLOW );
  21. IMPLEMENT_CLASS_MEMPOOL( CTFPartyInvite, 1000, UTLMEMORYPOOL_GROW_SLOW );
  22. extern GCConVar tf_mm_abandon_penalty_ignore;
  23. extern GCConVar tf_mm_abandon_penalty_low_priority_max;
  24. // Keeping this because I'm not 100% sure I'm not going to break things removing this.
  25. GCConVar tf_mm_force_competitive_access_update( "tf_mm_force_competitive_access_update", "0", 0, "If set, force a update of party member's competitive status every time we update the party. Expensive, and would only wallpaper issues with the competitive acccess system." );
  26. #endif
  27. //---------------------------------------------------------------------------------
  28. CTFParty::CTFParty()
  29. #ifdef CLIENT_DLL
  30. : m_bOffLine( false )
  31. #endif
  32. {
  33. }
  34. CTFParty::~CTFParty()
  35. {
  36. }
  37. const CSteamID CTFParty::GetLeader() const
  38. {
  39. CSteamID steamID( Obj().leader_id() );
  40. return steamID;
  41. }
  42. const CSteamID CTFParty::GetMember( int i ) const
  43. {
  44. Assert( i >= 0 && i < Obj().member_ids_size() );
  45. if ( i < 0 || i >= Obj().member_ids_size() )
  46. return k_steamIDNil;
  47. return Obj().member_ids( i );
  48. }
  49. int CTFParty::GetMemberIndexBySteamID( const CSteamID &steamID ) const
  50. {
  51. for ( int i = 0; i < Obj().member_ids_size(); i++ )
  52. {
  53. if ( Obj().member_ids( i ) == steamID.ConvertToUint64() )
  54. return i;
  55. }
  56. return -1;
  57. }
  58. const CSteamID CTFParty::GetPendingInvite( int i ) const
  59. {
  60. Assert( i >= 0 && i < Obj().pending_invites_size() );
  61. if ( i < 0 || i >= Obj().pending_invites_size() )
  62. return k_steamIDNil;
  63. return Obj().pending_invites( i );
  64. }
  65. int CTFParty::GetPendingInviteIndexBySteamID( const CSteamID &steamID ) const
  66. {
  67. for ( int i = 0; i < Obj().pending_invites_size(); i++ )
  68. {
  69. if ( Obj().pending_invites( i ) == steamID.ConvertToUint64() )
  70. return i;
  71. }
  72. return -1;
  73. }
  74. void CTFParty::SpewDebug()
  75. {
  76. CRTime time( GetStartedMatchmakingTime() );
  77. CRTime now( CRTime::RTime32TimeCur() );
  78. char time_buf[k_RTimeRenderBufferSize];
  79. char now_buf[k_RTimeRenderBufferSize];
  80. Msg( "TFParty: ID:%016llx %d member(s) LeaderID: %s\n", GetGroupID(), GetNumMembers(), GetLeader().Render() );
  81. Msg( " State: %d Started matchmaking: %s (%d seconds ago, now is %s)\n", (int) GetState(), time.Render( time_buf ), CRTime::RTime32TimeCur() - GetStartedMatchmakingTime(), now.Render( now_buf ) );
  82. //Msg( " GameMode: %u\n", Obj().game_mode() );
  83. for ( int i = 0; i < GetNumMembers(); i++ )
  84. {
  85. Msg( " Member[%d] %s%s\n", i, GetMember( i ).Render(), GetMember( i ) == GetLeader() ? " [Leader]" : "" );
  86. }
  87. Dump();
  88. }
  89. #ifdef GC
  90. void CTFParty::SetWebAPIDebugValues( CWebAPIValues *pPartyObject )
  91. {
  92. pPartyObject->SetChildUInt64Value( "party_id", GetGroupID() );
  93. pPartyObject->SetChildStringValue( "match_group", GetMatchGroupName( GetMatchGroup() ) );
  94. pPartyObject->SetChildStringValue( "state", CSOTFParty_State_Name( GetState() ).c_str() );
  95. CWebAPIValues *pMemberArray = pPartyObject->CreateChildArray( "members", "member" );
  96. for ( int i = 0 ; i < GetNumMembers() ; ++i )
  97. {
  98. pMemberArray->AddChildObjectToArray()->SetUInt64Value( GetMember( i ).ConvertToUint64() );
  99. }
  100. }
  101. #endif
  102. EMatchGroup CTFParty::GetMatchGroup() const
  103. {
  104. switch ( GetMatchmakingMode() )
  105. {
  106. case TF_Matchmaking_MVM:
  107. return GetSearchPlayForBraggingRights() ? k_nMatchGroup_MvM_MannUp : k_nMatchGroup_MvM_Practice;
  108. case TF_Matchmaking_LADDER:
  109. {
  110. EMatchGroup result = (EMatchGroup)Obj().search_ladder_game_type();
  111. Assert( result >= k_nMatchGroup_Ladder_First && result <= k_nMatchGroup_Ladder_Last );
  112. return result;
  113. }
  114. case TF_Matchmaking_CASUAL:
  115. return k_nMatchGroup_Casual_12v12;
  116. case TF_Matchmaking_INVALID:
  117. return k_nMatchGroup_Invalid;
  118. }
  119. AssertMsg1( false, "Matchmaking mode %d not yet implemented", GetMatchmakingMode() );
  120. return k_nMatchGroup_Invalid;
  121. }
  122. void CTFParty::GetSearchChallenges( CMvMMissionSet &challenges ) const
  123. {
  124. challenges.Clear();
  125. for ( int i = 0 ; i < Obj().search_mvm_missions_size() ; ++i )
  126. {
  127. int idxChallenge = GetItemSchema()->FindMvmMissionByName( Obj().search_mvm_missions( i ).c_str() );
  128. if ( idxChallenge >= 0 )
  129. challenges.SetMissionBySchemaIndex( idxChallenge, true );
  130. }
  131. }
  132. #ifdef USE_MVM_TOUR
  133. const char *CTFParty::GetSearchMannUpTourName() const
  134. {
  135. if ( !GetSearchPlayForBraggingRights() )
  136. return NULL;
  137. return Obj().search_mvm_mannup_tour().c_str();
  138. }
  139. int CTFParty::GetSearchMannUpTourIndex() const
  140. {
  141. if ( !GetSearchPlayForBraggingRights() )
  142. return k_iMvmTourIndex_NotMannedUp;
  143. return GetItemSchema()->FindMvmTourByName( Obj().search_mvm_mannup_tour().c_str() );
  144. }
  145. #endif // USE_MVM_TOUR
  146. bool CTFParty::BAnyMemberWithoutTicket() const
  147. {
  148. Assert( Obj().members_size() == GetNumMembers() );
  149. for ( int i = 0 ; i < Obj().members_size() ; ++i )
  150. {
  151. if ( !Obj().members(i).owns_ticket() )
  152. return true;
  153. }
  154. return false;
  155. }
  156. bool CTFParty::BAnyMemberWithoutCompetitiveAccess() const
  157. {
  158. Assert( Obj().members_size() == GetNumMembers() );
  159. for ( int i = 0; i < Obj().members_size(); ++i )
  160. {
  161. if ( !Obj().members( i ).competitive_access() )
  162. return true;
  163. }
  164. return false;
  165. }
  166. bool CTFParty::BAnyMemberWithLowPriority() const
  167. {
  168. // Wretched hive of scum and villainy
  169. return Obj().matchmaking_low_priority_time() > CRTime::RTime32TimeCur();
  170. }
  171. RTime32 CTFParty::GetStartedMatchmakingTime() const
  172. {
  173. return (RTime32) Obj().started_matchmaking_time();
  174. }
  175. int CTFParty::GetNumSearchingPlayers( int group )
  176. {
  177. if ( Obj().searching_players_by_group_size() <= group )
  178. {
  179. return 0;
  180. }
  181. return Obj().searching_players_by_group( group );
  182. }
  183. int CTFParty::GetMaxNumSearchingPlayers()
  184. {
  185. if ( !Obj().searching_players_by_group_size() )
  186. {
  187. return 0;
  188. }
  189. uint32 n = 0;
  190. for ( int i = 0; i < Obj().searching_players_by_group_size(); ++i )
  191. {
  192. n = MAX( n, Obj().searching_players_by_group( i ) );
  193. }
  194. return n;
  195. }
  196. const CSteamID CTFPartyInvite::GetMember( int i ) const
  197. {
  198. Assert( i >= 0 && i < Obj().members_size() );
  199. if ( i < 0 || i >= Obj().members_size() )
  200. return k_steamIDNil;
  201. return Obj().members( i ).steam_id();
  202. }
  203. const char* CTFPartyInvite::GetMemberName( int i ) const
  204. {
  205. Assert( i >= 0 && i < Obj().members_size() );
  206. if ( i < 0 || i >= Obj().members_size() )
  207. return "Unknown";
  208. return Obj().members( i ).name().c_str();
  209. }
  210. uint16 CTFPartyInvite::GetMemberAvatar( int i ) const
  211. {
  212. Assert( i >= 0 && i < Obj().members_size() );
  213. if ( i < 0 || i >= Obj().members_size() )
  214. return 0;
  215. return Obj().members( i ).avatar();
  216. }
  217. #ifdef GC
  218. void CTFParty::AddMember( const CSteamID &steamID )
  219. {
  220. Assert( Obj().members_size() == Obj().member_ids_size() );
  221. EmitInfo( SPEW_GC, MATCHMAKING_SPEWLEVEL4, LOG_ALWAYS, "Party %016llx AddMember(%s)\n", Obj().party_id(), steamID.Render()) ;
  222. Obj().add_member_ids( steamID.ConvertToUint64() );
  223. Obj().add_members(); // leave all fields blank (default) for now. We'll populate them in yielding calls later
  224. DirtyParty();
  225. UpdatePreventMatchmakingDate();
  226. }
  227. void CTFParty::RemoveMember( const CSteamID &steamID )
  228. {
  229. Assert( Obj().members_size() == Obj().member_ids_size() );
  230. int index = GetMemberIndexBySteamID( steamID );
  231. if ( index == -1 )
  232. return;
  233. EmitInfo( SPEW_GC, MATCHMAKING_SPEWLEVEL4, LOG_ALWAYS, "Party %016llx RemoveMember(%s)\n", Obj().party_id(), steamID.Render()) ;
  234. // protobuf doesn't let you remove elements from the middle of the list so we have to do this dance
  235. Obj().mutable_member_ids()->SwapElements( index, Obj().member_ids_size() - 1 );
  236. Obj().mutable_member_ids()->RemoveLast();
  237. Obj().mutable_members()->SwapElements( index, Obj().members_size() - 1 );
  238. Obj().mutable_members()->RemoveLast();
  239. DirtyParty();
  240. UpdatePreventMatchmakingDate();
  241. }
  242. void CTFParty::AddPendingInvite( const CSteamID &steamID )
  243. {
  244. EmitInfo( SPEW_GC, MATCHMAKING_SPEWLEVEL4, LOG_ALWAYS, "Party %016llx AddPendingInvite(%s)\n", Obj().party_id(), steamID.Render()) ;
  245. Obj().add_pending_invites( steamID.ConvertToUint64() );
  246. DirtyParty();
  247. }
  248. void CTFParty::RemovePendingInvite( const CSteamID &steamID )
  249. {
  250. int index = GetPendingInviteIndexBySteamID( steamID );
  251. if ( index == -1 )
  252. return;
  253. EmitInfo( SPEW_GC, MATCHMAKING_SPEWLEVEL4, LOG_ALWAYS, "Party %016llx RemovePendingInvite(%s)\n", Obj().party_id(), steamID.Render()) ;
  254. // protobuf doesn't let you remove elements from the middle of the list so we have to do this dance
  255. Obj().mutable_pending_invites()->SwapElements( index, Obj().pending_invites_size() - 1 );
  256. Obj().mutable_pending_invites()->RemoveLast();
  257. DirtyParty();
  258. }
  259. void CTFParty::DirtyParty()
  260. {
  261. for ( int i = 0; i < GetNumMembers(); i++ )
  262. {
  263. CGCUserSession *pMemberSession = GGCBase()->FindUserSession( GetMember( i ) );
  264. // We may be in the process of mutating this party, don't assume we're in every member's SOCache or we hit
  265. // asserts all over debug during construction.
  266. //
  267. // Ideally, we'd have a better ScopedPartyOperation thing that only dirties a party once per transaction instead
  268. // of each mutation trying to re-set this flag.
  269. if ( !pMemberSession || !pMemberSession->GetSOCache()->FindSharedObject( *this ) )
  270. continue;
  271. pMemberSession->GetSOCache()->DirtyNetworkObject( this );
  272. }
  273. }
  274. void CTFParty::SetLeader( const CSteamID &steamID )
  275. {
  276. EmitInfo( SPEW_GC, MATCHMAKING_SPEWLEVEL4, LOG_ALWAYS, "Party %016llx SetLeader(%s)\n", Obj().party_id(), steamID.Render()) ;
  277. Obj().set_leader_id( steamID.ConvertToUint64() );
  278. DirtyParty();
  279. }
  280. void CTFParty::SetCustomPingTolerance( uint32 unCustomPingTolerance )
  281. {
  282. EmitInfo( SPEW_GC, MATCHMAKING_SPEWLEVEL4, LOG_ALWAYS, "Party %016llx SetCustomPingTolerance(%u)\n",
  283. Obj().party_id(), unCustomPingTolerance );
  284. // TODO We need a template to do this dance for these setters :-/ A lot of them are just dirtying unnecessarily
  285. if ( !Obj().has_custom_ping_tolerance() || Obj().custom_ping_tolerance() != unCustomPingTolerance )
  286. {
  287. Obj().set_custom_ping_tolerance( unCustomPingTolerance );
  288. DirtyParty();
  289. }
  290. }
  291. void CTFParty::SetGroupID( GCSDK::PlayerGroupID_t nGroupID )
  292. {
  293. EmitInfo( SPEW_GC, MATCHMAKING_SPEWLEVEL4, LOG_ALWAYS, "Party %016llx SetGroupID(%016llx)\n", nGroupID, nGroupID );
  294. Obj().set_party_id( nGroupID );
  295. DirtyParty();
  296. }
  297. void CTFParty::SetStartedMatchmakingTime( RTime32 time )
  298. {
  299. EmitInfo( SPEW_GC, MATCHMAKING_SPEWLEVEL4, LOG_ALWAYS, "Party %016llx SetStartedMatchmakingTime(%u)\n", Obj().party_id(), time );
  300. Obj().set_started_matchmaking_time( (uint32) time );
  301. DirtyParty();
  302. }
  303. void CTFParty::SetState( CSOTFParty_State newState )
  304. {
  305. // Setting a party in CSOTFParty_State_IN_MATCH should be done
  306. // through AssociatePartyWithLobby() down below
  307. Assert( newState != CSOTFParty_State_IN_MATCH );
  308. SetStateInternal( newState );
  309. }
  310. void CTFParty::SetStateInternal( CSOTFParty_State newState )
  311. {
  312. EmitInfo( SPEW_GC, MATCHMAKING_SPEWLEVEL4, LOG_ALWAYS, "Party %016llx SetState(%s)\n", Obj().party_id(), CSOTFParty_State_Name( newState ).c_str() );
  313. Obj().set_state( newState );
  314. // Make sure we have a valid wizard step, too
  315. if ( newState == CSOTFParty_State_UI )
  316. {
  317. if ( !Obj().has_wizard_step() || Obj().wizard_step() == TF_Matchmaking_WizardStep_SEARCHING )
  318. {
  319. switch ( GetMatchmakingMode() )
  320. {
  321. case TF_Matchmaking_MVM:
  322. Obj().set_wizard_step( TF_Matchmaking_WizardStep_MVM_PLAY_FOR_BRAGGING_RIGHTS );
  323. break;
  324. case TF_Matchmaking_LADDER:
  325. Obj().set_wizard_step( TF_Matchmaking_WizardStep_LADDER );
  326. break;
  327. case TF_Matchmaking_CASUAL:
  328. Obj().set_wizard_step( TF_Matchmaking_WizardStep_CASUAL );
  329. break;
  330. default:
  331. AssertMsg2( false, "Party %016llx invalid matchmaking mode %d", Obj().party_id(), (int)GetMatchmakingMode() );
  332. }
  333. }
  334. }
  335. else
  336. {
  337. Obj().set_wizard_step( TF_Matchmaking_WizardStep_SEARCHING );
  338. }
  339. DirtyParty();
  340. }
  341. void CTFParty::AssociatePartyWithLobby( CTFLobby* pLobby )
  342. {
  343. Assert( Obj().state() != CSOTFParty_State_IN_MATCH );
  344. Assert( Obj().associated_lobby_id() == 0 );
  345. Obj().set_associated_lobby_id( pLobby->GetGroupID() );
  346. SetStateInternal( CSOTFParty_State_IN_MATCH );
  347. DirtyParty();
  348. }
  349. void CTFParty::DissociatePartyFromLobby( CTFLobby* pLobby )
  350. {
  351. Assert( Obj().state() == CSOTFParty_State_IN_MATCH );
  352. Assert( Obj().associated_lobby_id() == pLobby->GetGroupID() );
  353. Obj().set_associated_lobby_id( 0 );
  354. SetStateInternal( CSOTFParty_State_UI );
  355. DirtyParty();
  356. }
  357. CTFLobby* CTFParty::GetAssociatedLobby() const
  358. {
  359. if ( Obj().associated_lobby_id() == 0 )
  360. return NULL;
  361. CTFLobby* pLobby = TFLobbyManager()->FindTFLobby( Obj().associated_lobby_id() );
  362. Assert( pLobby );
  363. return pLobby;
  364. }
  365. void CTFParty::SetUIStateAndWizardStep( TF_Matchmaking_WizardStep eWizardStep )
  366. {
  367. EmitInfo( SPEW_GC, MATCHMAKING_SPEWLEVEL4, LOG_ALWAYS, "Party %016llx SetUIStateAndWizardStep(%s)\n", Obj().party_id(), TF_Matchmaking_WizardStep_Name( eWizardStep ).c_str() );
  368. // Make sure the wizard step makes sense
  369. switch ( GetMatchmakingMode() )
  370. {
  371. default:
  372. AssertMsg2( false, "Party %016llx in invalid matchmaking mode %d", Obj().party_id(), (int)GetMatchmakingMode() );
  373. case TF_Matchmaking_MVM:
  374. #ifdef USE_MVM_TOUR
  375. Assert(
  376. eWizardStep == TF_Matchmaking_WizardStep_MVM_PLAY_FOR_BRAGGING_RIGHTS
  377. || eWizardStep == TF_Matchmaking_WizardStep_MVM_TOUR_OF_DUTY
  378. || eWizardStep == TF_Matchmaking_WizardStep_MVM_CHALLENGE
  379. );
  380. #else // new mm
  381. Assert(
  382. eWizardStep == TF_Matchmaking_WizardStep_MVM_PLAY_FOR_BRAGGING_RIGHTS
  383. || eWizardStep == TF_Matchmaking_WizardStep_MVM_CHALLENGE
  384. );
  385. #endif // USE_MVM_TOUR
  386. break;
  387. case TF_Matchmaking_LADDER:
  388. Assert( eWizardStep == TF_Matchmaking_WizardStep_LADDER );
  389. break;
  390. case TF_Matchmaking_CASUAL:
  391. Assert( eWizardStep == TF_Matchmaking_WizardStep_CASUAL );
  392. }
  393. Obj().set_state( CSOTFParty_State_UI );
  394. Obj().set_wizard_step( eWizardStep );
  395. DirtyParty();
  396. }
  397. void CTFParty::SetSteamLobbyID( const CSteamID &steamIDLobby )
  398. {
  399. EmitInfo( SPEW_GC, MATCHMAKING_SPEWLEVEL4, LOG_ALWAYS, "Party %016llx SetSteamLobbyID(%s)\n", Obj().party_id(), steamIDLobby.Render() );
  400. Obj().set_steam_lobby_id( steamIDLobby.ConvertToUint64() );
  401. DirtyParty();
  402. }
  403. void CTFParty::SetNumSearchingPlayers( int group, uint32 nPlayers )
  404. {
  405. bool changed = false;
  406. while ( Obj().searching_players_by_group_size() < group )
  407. {
  408. Obj().add_searching_players_by_group( 0 );
  409. changed = true;
  410. }
  411. if ( Obj().searching_players_by_group_size() == group )
  412. {
  413. Obj().add_searching_players_by_group( nPlayers );
  414. changed = true;
  415. }
  416. else
  417. {
  418. if ( Obj().searching_players_by_group( group ) != nPlayers )
  419. {
  420. Obj().set_searching_players_by_group( group, nPlayers );
  421. changed = true;
  422. }
  423. }
  424. if ( changed )
  425. {
  426. //EmitInfo( SPEW_GC, MATCHMAKING_SPEWLEVEL4, LOG_ALWAYS, "Party %016llx SetNumSearchingPlayers(%u,%d)\n", Obj().party_id(), group, nPlayers );
  427. DirtyParty();
  428. }
  429. }
  430. //void CTFParty::SetSearchKey( const char *key )
  431. //{
  432. // if ( key == NULL )
  433. // key = "";
  434. //
  435. // // Note: case sensitivity
  436. // if ( Q_strcmp( Obj().search_key().c_str(), key ) == 0 )
  437. // {
  438. // // No change
  439. // return;
  440. // }
  441. //
  442. // EmitInfo( SPEW_GC, MATCHMAKING_SPEWLEVEL4, LOG_ALWAYS, "Party %016llx SetSearchKey(%s)\n", Obj().party_id(), key ) ;
  443. // Obj().set_search_key( key );
  444. //
  445. // DirtyParty();
  446. //}
  447. void CTFParty::SetMatchmakingMode( TF_MatchmakingMode mode )
  448. {
  449. if ( mode != Obj().matchmaking_mode() )
  450. {
  451. EmitInfo( SPEW_GC, MATCHMAKING_SPEWLEVEL4, LOG_ALWAYS, "Party %016llx SetMatchmakingMode(\"%s\")\n", Obj().party_id(), TF_MatchmakingMode_Name( mode ).c_str() ) ;
  452. Obj().set_matchmaking_mode( mode );
  453. DirtyParty();
  454. }
  455. }
  456. void CTFParty::SetSearchChallenges( const ::google::protobuf::RepeatedPtrField< ::std::string> &challenges )
  457. {
  458. Obj().mutable_search_mvm_missions()->Clear();
  459. Obj().mutable_search_mvm_missions()->MergeFrom( challenges );
  460. DirtyParty();
  461. }
  462. void CTFParty::CheckRemoveInvalidSearchChallenges()
  463. {
  464. if ( GetMatchmakingMode() != TF_Matchmaking_MVM )
  465. {
  466. Obj().mutable_search_mvm_missions()->Clear();
  467. return;
  468. }
  469. #ifdef USE_MVM_TOUR
  470. int idxTour = GetSearchMannUpTourIndex();
  471. #endif // USE_MVM_TOUR
  472. bool bChanged = false;
  473. int i = 0;
  474. while ( i < Obj().search_mvm_missions_size() )
  475. {
  476. int idxChallenge = GetItemSchema()->FindMvmMissionByName( Obj().search_mvm_missions( i ).c_str() );
  477. bool bKeep = true;
  478. if ( idxChallenge >= 0 )
  479. {
  480. #ifdef USE_MVM_TOUR
  481. if ( idxTour != k_iMvmTourIndex_NotMannedUp )
  482. {
  483. if ( idxTour < 0 )
  484. {
  485. bKeep = false;
  486. }
  487. else if ( GetItemSchema()->FindMvmMissionInTour( idxTour, idxChallenge ) < 0 )
  488. {
  489. bKeep = false;
  490. }
  491. }
  492. #else // new mm
  493. // check if mission is valid if we're in mannup
  494. if ( GetSearchPlayForBraggingRights() )
  495. {
  496. const MvMMission_t& challenge = GetItemSchema()->GetMvmMissions()[ idxChallenge ];
  497. bKeep = challenge.m_unMannUpPoints > 0; // make sure mannup mission has valid point
  498. }
  499. #endif // USE_MVM_TOUR
  500. }
  501. else if ( Obj().search_mvm_missions_size() == 1 )
  502. {
  503. // Only retain a single "invalid" entry to stand for "no challenges selected"
  504. Obj().set_search_mvm_missions( 0, "invalid" ); // make sure we don't have a real mission name hanging around that might later actually get used.
  505. }
  506. else
  507. {
  508. bKeep = false;
  509. }
  510. if ( bKeep )
  511. {
  512. ++i;
  513. }
  514. else
  515. {
  516. // Remove it. Note that the order of this list doesn't matter.
  517. Obj().mutable_search_mvm_missions()->SwapElements( i, Obj().search_mvm_missions_size()-1 );
  518. Obj().mutable_search_mvm_missions()->RemoveLast();
  519. bChanged = true;
  520. }
  521. }
  522. if ( bChanged )
  523. {
  524. DirtyParty();
  525. }
  526. }
  527. void CTFParty::SetSearchQuickplayGameType( uint32 nType )
  528. {
  529. if ( Obj().search_quickplay_game_type() != nType )
  530. {
  531. EmitInfo( SPEW_GC, MATCHMAKING_SPEWLEVEL4, LOG_ALWAYS, "Party %016llx SetSearchQuickplayGameType(%d)\n", Obj().party_id(), nType ) ;
  532. Obj().set_search_quickplay_game_type( nType );
  533. DirtyParty();
  534. }
  535. }
  536. void CTFParty::SetSearchLadderGameType( uint32 nType )
  537. {
  538. if ( Obj().search_ladder_game_type() != nType )
  539. {
  540. if ( !IsLadderGroup( (EMatchGroup)nType ) )
  541. {
  542. AssertMsg( false, "Party %016llx attemptting to set invalid ladder search type %d\n", Obj().party_id(), nType );
  543. }
  544. else
  545. {
  546. EmitInfo( SPEW_GC, MATCHMAKING_SPEWLEVEL4, LOG_ALWAYS, "Party %016llx SetSearchLadderGameType(%d)\n", Obj().party_id(), nType );
  547. Obj().set_search_ladder_game_type( nType );
  548. DirtyParty();
  549. }
  550. }
  551. }
  552. void CTFParty::SetCasualSearchCriteria( const CMsgCasualMatchmakingSearchCriteria& msg )
  553. {
  554. Obj().mutable_search_casual()->CopyFrom( msg );
  555. DirtyParty();
  556. }
  557. //void CTFParty::SetMatchingPlayers( uint32 unMatchingPlayers )
  558. //{
  559. // EmitInfo( SPEW_GC, MATCHMAKING_SPEWLEVEL4, LOG_ALWAYS, "Party %016llx SetMatchingPlayers(%u)\n", Obj().party_id(), unMatchingPlayers ) ;
  560. //
  561. // Obj().set_matching_players( unMatchingPlayers );
  562. //
  563. // DirtyParty();
  564. //}
  565. //
  566. //void CTFParty::SetSearchFraction( float flFraction )
  567. //{
  568. // EmitInfo( SPEW_GC, MATCHMAKING_SPEWLEVEL4, LOG_ALWAYS, "Party %016llx SetSearchFraction(%f)\n", Obj().party_id(), flFraction ) ;
  569. //
  570. // Obj().set_search_fraction( flFraction );
  571. //
  572. // DirtyParty();
  573. //}
  574. void CTFParty::SetLateJoinOK( bool bLateJoinOK )
  575. {
  576. if ( Obj().search_late_join_ok() != bLateJoinOK )
  577. {
  578. EmitInfo( SPEW_GC, MATCHMAKING_SPEWLEVEL4, LOG_ALWAYS, "Party %016llx SetLateJoinOK(%d)\n", Obj().party_id(), bLateJoinOK ? 1 : 0 ) ;
  579. Obj().set_search_late_join_ok( bLateJoinOK );
  580. DirtyParty();
  581. }
  582. }
  583. void CTFParty::SetSearchPlayForBraggingRights( bool bPlayForBraggingRights )
  584. {
  585. if ( Obj().search_play_for_bragging_rights() != bPlayForBraggingRights )
  586. {
  587. EmitInfo( SPEW_GC, MATCHMAKING_SPEWLEVEL4, LOG_ALWAYS, "Party %016llx SetSearchPlayForBraggingRights(%d)\n", Obj().party_id(), bPlayForBraggingRights ? 1 : 0 ) ;
  588. Obj().set_search_play_for_bragging_rights( bPlayForBraggingRights );
  589. // If not playing for bragging rights, then clear tour of duty
  590. if ( !bPlayForBraggingRights )
  591. {
  592. Obj().clear_search_mvm_mannup_tour();
  593. }
  594. DirtyParty();
  595. }
  596. }
  597. #ifdef USE_MVM_TOUR
  598. void CTFParty::SetSearchMannUpTourName( const char *pszTourName )
  599. {
  600. if ( pszTourName == NULL )
  601. pszTourName = "";
  602. if ( V_stricmp( Obj().search_mvm_mannup_tour().c_str(), pszTourName ) != 0 )
  603. {
  604. EmitInfo( SPEW_GC, MATCHMAKING_SPEWLEVEL4, LOG_ALWAYS, "Party %016llx SetSearchMannUpTourName(%s)\n", Obj().party_id(), pszTourName );
  605. Obj().set_search_mvm_mannup_tour( pszTourName );
  606. DirtyParty();
  607. }
  608. }
  609. #endif // USE_MVM_TOUR
  610. void CTFParty::SetMemberUseSquadSurplus( int nMemberIndex, bool bUseVoucher )
  611. {
  612. if ( nMemberIndex < 0 || nMemberIndex >= GetNumMembers() )
  613. {
  614. Assert( nMemberIndex >= 0 );
  615. Assert( nMemberIndex < GetNumMembers() );
  616. return;
  617. }
  618. if ( Obj().members( nMemberIndex ).squad_surplus() != bUseVoucher )
  619. {
  620. EmitInfo( SPEW_GC, MATCHMAKING_SPEWLEVEL4, LOG_ALWAYS, "Party %016llx SetMemberUseSquadSurplus(%d, %d)\n", Obj().party_id(), nMemberIndex, bUseVoucher ? 1 : 0 ) ;
  621. Obj().mutable_members( nMemberIndex )->set_squad_surplus( bUseVoucher );
  622. DirtyParty();
  623. }
  624. }
  625. void CTFParty::YldUpdateMemberData( int nMemberIndex )
  626. {
  627. VPROF_BUDGET( "CTFParty::YldUpdateMemberData", VPROF_BUDGETGROUP_STEAM );
  628. Assert( Obj().members_size() == GetNumMembers() );
  629. if ( nMemberIndex < 0 || nMemberIndex >= GetNumMembers() )
  630. {
  631. Assert( nMemberIndex >= 0 );
  632. Assert( nMemberIndex < GetNumMembers() );
  633. return;
  634. }
  635. CSteamID steamIDMember( GetMember( nMemberIndex ) );
  636. CScopedSteamIDLock memberLock( steamIDMember );
  637. if ( !memberLock.BYieldingPerformLock( __FILE__, __LINE__) )
  638. {
  639. AssertMsg2( false, "CTFParty::YldUpdateMemberData: Failed to obtain lock for player %s in party %016llx",
  640. steamIDMember.Render(), GetGroupID() );
  641. return;
  642. }
  643. CTFSharedObjectCache *pSOCache = GGCTF()->YieldingFindOrLoadTFSOCache( steamIDMember );
  644. if ( !pSOCache )
  645. {
  646. AssertMsg2( false, "Party %016llx contains member %s, but we cannot load his SO cache?", GetGroupID(), steamIDMember.Render() );
  647. return;
  648. }
  649. bool bCompetitiveAccess = false;
  650. CTFUserSession *pUserSession = GGCTF()->FindTFUserSession( steamIDMember );
  651. // Don't bother doing this relatively expensive call for non-ladder groups
  652. if ( pUserSession && IsLadderGroup( GetMatchGroup() ) )
  653. {
  654. // Competitive access updates are moderately expensive and queued asynchronously. If one is queued, force it to
  655. // flush now
  656. if ( !pUserSession->BCompetitiveAccessUpToDate() || tf_mm_force_competitive_access_update.GetBool() )
  657. {
  658. // Ensure competitive beta access is up to date
  659. pUserSession->YieldingUpdateCompetitiveAccess();
  660. }
  661. Assert( pUserSession->BCompetitiveAccessUpToDate() );
  662. bCompetitiveAccess = pSOCache->GetGameAccountClient() && pSOCache->GetGameAccountClient()->Obj().competitive_access();
  663. }
  664. else if ( !pUserSession )
  665. {
  666. EmitInfo( SPEW_GC, MATCHMAKING_SPEWLEVEL4, LOG_ALWAYS, "CTFParty::YldUpdateMemberData: Player %s is in party %016llx, but has no session",
  667. steamIDMember.Render(), GetGroupID() );
  668. // Assume ineligible
  669. }
  670. bool bHasTicket = ( pSOCache->FindFirstMvmTicket() != NULL );
  671. bool bHasSquadSurplusVoucher = ( pSOCache->FindFirstMvmSquadSurplusVoucher() != NULL );
  672. bool bDirty = false;
  673. if ( Obj().members( nMemberIndex ).owns_ticket() != bHasTicket )
  674. {
  675. EmitInfo( SPEW_GC, MATCHMAKING_SPEWLEVEL4, LOG_ALWAYS, "Party %016llx Member[%s].set_owns_ticket(%d)\n", Obj().party_id(), steamIDMember.Render(), bHasTicket ? 1 : 0 ) ;
  676. Obj().mutable_members( nMemberIndex )->set_owns_ticket( bHasTicket );
  677. bDirty = true;
  678. }
  679. if ( Obj().members( nMemberIndex ).squad_surplus() && !bHasSquadSurplusVoucher )
  680. {
  681. EmitInfo( SPEW_GC, MATCHMAKING_SPEWLEVEL4, LOG_ALWAYS, "Party %016llx Member[%s].set_squad_surplus(0) --- no voucher in inventory\n", Obj().party_id(), steamIDMember.Render() ) ;
  682. Obj().mutable_members( nMemberIndex )->set_squad_surplus( false );
  683. bDirty = true;
  684. }
  685. if ( Obj().members( nMemberIndex ).competitive_access() != bCompetitiveAccess )
  686. {
  687. EmitInfo( SPEW_GC, MATCHMAKING_SPEWLEVEL4, LOG_ALWAYS, "Party %016llx Member[%s].set_competitive_access(0)\n", Obj().party_id(), steamIDMember.Render() );
  688. Obj().mutable_members( nMemberIndex )->set_competitive_access( bCompetitiveAccess );
  689. bDirty = true;
  690. }
  691. #ifdef USE_MVM_TOUR
  692. int idxTour = GetSearchMannUpTourIndex();
  693. if ( idxTour >= 0 )
  694. {
  695. uint32 nBadgeLevel = 0;
  696. uint32 nMissionCompletedMask = 0;
  697. CEconItem *pBadge = pSOCache->FindMvmBadgeForTour( idxTour );
  698. if ( pBadge != NULL )
  699. {
  700. extern uint32 GetItemDescriptionDisplayLevel( const IEconItemInterface *pEconItem );
  701. nBadgeLevel = GetItemDescriptionDisplayLevel( pBadge );
  702. static CSchemaAttributeDefHandle pAttribDef_MvmChallengeCompleted( CTFItemSchema::k_rchMvMChallengeCompletedMaskAttribName );
  703. Assert( pAttribDef_MvmChallengeCompleted );
  704. if ( pAttribDef_MvmChallengeCompleted )
  705. {
  706. if ( !pBadge->FindAttribute( pAttribDef_MvmChallengeCompleted, &nMissionCompletedMask ) )
  707. nMissionCompletedMask = 0;
  708. }
  709. }
  710. if ( Obj().members( nMemberIndex ).completed_missions() != nMissionCompletedMask )
  711. {
  712. EmitInfo( SPEW_GC, MATCHMAKING_SPEWLEVEL4, LOG_ALWAYS, "Party %016llx Member[%s].set_completed_missions(0x%X)\n", Obj().party_id(), steamIDMember.Render(), nMissionCompletedMask ) ;
  713. Obj().mutable_members( nMemberIndex )->set_completed_missions( nMissionCompletedMask );
  714. bDirty = true;
  715. }
  716. if ( Obj().members( nMemberIndex ).badge_level() != nBadgeLevel )
  717. {
  718. EmitInfo( SPEW_GC, MATCHMAKING_SPEWLEVEL4, LOG_ALWAYS, "Party %016llx Member[%s].set_badge_level(%d)\n", Obj().party_id(), steamIDMember.Render(), nBadgeLevel ) ;
  719. Obj().mutable_members( nMemberIndex )->set_badge_level( nBadgeLevel );
  720. bDirty = true;
  721. }
  722. }
  723. else
  724. #endif // USE_MVM_TOUR
  725. {
  726. Obj().mutable_members( nMemberIndex )->clear_badge_level();
  727. Obj().mutable_members( nMemberIndex )->clear_completed_missions();
  728. }
  729. CSOTFLadderData *pData = pSOCache->GetLadderDataForMatchGroup( Obj().search_ladder_game_type() );
  730. uint32 unRank = ( pData ? pData->Obj().rank() : 1u );
  731. if ( Obj().members( nMemberIndex ).ladder_rank() != unRank )
  732. {
  733. EmitInfo( SPEW_GC, MATCHMAKING_SPEWLEVEL4, LOG_ALWAYS, "Party %016llx Member[%s].set_ladder_rank(%d)\n", Obj().party_id(), steamIDMember.Render(), unRank );
  734. Obj().mutable_members( nMemberIndex )->set_ladder_rank( unRank );
  735. bDirty = true;
  736. }
  737. uint32 unExperience = ( pData ? pData->Obj().experience() : 1u );
  738. if ( Obj().members( nMemberIndex ).experience() != unExperience )
  739. {
  740. EmitInfo( SPEW_GC, MATCHMAKING_SPEWLEVEL4, LOG_ALWAYS, "Party %016llx Member[%s].set_experience(%d)\n", Obj().party_id(), steamIDMember.Render(), unExperience );
  741. Obj().mutable_members( nMemberIndex )->set_experience( unExperience );
  742. bDirty = true;
  743. }
  744. if ( pUserSession )
  745. {
  746. uint32 unSkillRating = pUserSession->GetSkillRatingForMatchGroup( GetMatchGroup() );
  747. if ( unSkillRating != Obj().members( nMemberIndex ).skillrating() )
  748. {
  749. EmitInfo( SPEW_GC, MATCHMAKING_SPEWLEVEL4, LOG_ALWAYS, "Party %016llx Member[%s].set_skillrating(%d)\n", Obj().party_id(), steamIDMember.Render(), unSkillRating );
  750. Obj().mutable_members( nMemberIndex )->set_skillrating( unSkillRating );
  751. bDirty = true;
  752. }
  753. }
  754. if ( bDirty )
  755. {
  756. DirtyParty();
  757. }
  758. }
  759. bool CTFParty::IsIntroModeEligible()
  760. {
  761. return true;
  762. /*for ( int m = 0; m < GetNumMembers(); m++ )
  763. {
  764. if ( Obj().member_initial_skills( m ) == DOTA_INITIAL_SKILL_NEWBIE ||
  765. Obj().member_initial_skills( m ) == DOTA_INITIAL_SKILL_FAMILIAR ||
  766. Obj().member_initial_skills( m ) == 0 )
  767. {
  768. return true;
  769. }
  770. }
  771. return false;*/
  772. }
  773. bool CTFParty::IsProModeEligible()
  774. {
  775. return true;
  776. /*for ( int m = 0; m < GetNumMembers(); m++ )
  777. {
  778. if ( ( Obj().member_initial_skills( m ) == DOTA_INITIAL_SKILL_NEWBIE && Obj().member_wins( m ) > 0 ) ||
  779. Obj().member_initial_skills( m ) == DOTA_INITIAL_SKILL_FAMILIAR ||
  780. Obj().member_initial_skills( m ) == DOTA_INITIAL_SKILL_EXPERIENCED ||
  781. Obj().member_initial_skills( m ) == 0 )
  782. {
  783. return true;
  784. }
  785. }
  786. return false;*/
  787. }
  788. void CTFParty::UpdatePreventMatchmakingDate()
  789. {
  790. uint32 unPreventDate = 0;
  791. uint32 unPreventAccountID = 0;
  792. uint32 unLowPriorityTime = 0;
  793. bool bDirty = false;
  794. EMMPenaltyPool ePenaltyPool = eMMPenaltyPool_Invalid;
  795. EMatchGroup matchGroup = GetMatchGroup();
  796. if ( matchGroup != k_nMatchGroup_Invalid )
  797. {
  798. const IMatchGroupDescription *pMatchDesc = GetMatchGroupDescription( matchGroup );
  799. Assert( pMatchDesc );
  800. ePenaltyPool = pMatchDesc ? pMatchDesc->m_params.m_ePenaltyPool : eMMPenaltyPool_Invalid;
  801. }
  802. // !FIXME! tf_mvm_matchmaking
  803. // // search all party members to find the one banned for the longest
  804. for ( int i = 0; i < GetNumMembers(); i++ )
  805. {
  806. CEconSharedObjectCache *pSOCache = GGCEcon()->YieldingFindOrLoadEconSOCache( GetMember( i ) );
  807. if ( !pSOCache )
  808. {
  809. continue;
  810. }
  811. CEconGameAccountClient* pGameAccountClient = pSOCache->GetGameAccountClient();
  812. uint32 memberPreventDate = 0;
  813. uint32 memberLowPriorityTime = 0;
  814. bool bIsBanned = false;
  815. bool bIsLowPriority = false;
  816. if ( pGameAccountClient )
  817. {
  818. bool bIgnoreAbandoned = tf_mm_abandon_penalty_ignore.GetBool();
  819. switch ( ePenaltyPool )
  820. {
  821. case eMMPenaltyPool_Casual:
  822. memberPreventDate = ( bIgnoreAbandoned ) ? 0 : pGameAccountClient->Obj().matchmaking_casual_ban_expiration();
  823. memberLowPriorityTime = bIgnoreAbandoned ? 0 : pGameAccountClient->Obj().matchmaking_casual_low_priority_expiration();
  824. break;
  825. case eMMPenaltyPool_Ranked:
  826. memberPreventDate = ( bIgnoreAbandoned ) ? 0 : pGameAccountClient->Obj().matchmaking_ranked_ban_expiration();
  827. memberLowPriorityTime = bIgnoreAbandoned ? 0 : pGameAccountClient->Obj().matchmaking_ranked_low_priority_expiration();
  828. break;
  829. case eMMPenaltyPool_Invalid:
  830. // Not in any match group yet, no ban
  831. Assert( matchGroup == k_nMatchGroup_Invalid );
  832. break;
  833. default: Assert( false );
  834. }
  835. bIsBanned = memberPreventDate > CRTime::RTime32TimeCur();
  836. bIsLowPriority = memberLowPriorityTime > CRTime::RTime32TimeCur();
  837. // mark the member as banned. the ban stays until the party is destroyed
  838. if ( !Obj().members(i).is_banned() )
  839. {
  840. EmitInfo( SPEW_GC, MATCHMAKING_SPEWLEVEL4, LOG_ALWAYS,
  841. "Party %016llx Member[%s].set_is_banned(%d)\n", Obj().party_id(), GetMember(i).Render(), bIsBanned ? 1 : 0 ) ;
  842. Obj().mutable_members(i)->set_is_banned( bIsBanned );
  843. bDirty = true;
  844. }
  845. if ( !Obj().members( i ).is_low_priority() )
  846. {
  847. EmitInfo( SPEW_GC, MATCHMAKING_SPEWLEVEL4, LOG_ALWAYS, "Party %016llx Member[%s].set_is_low_priority(%d)\n",
  848. Obj().party_id(), GetMember( i ).Render(), bIsLowPriority ? 1 : 0 );
  849. Obj().mutable_members( i )->set_is_low_priority( bIsLowPriority );
  850. bDirty = true;
  851. }
  852. }
  853. if ( memberPreventDate > unPreventDate )
  854. {
  855. unPreventDate = memberPreventDate;
  856. unPreventAccountID = GetMember( i ).GetAccountID();
  857. }
  858. if ( memberLowPriorityTime > unLowPriorityTime )
  859. {
  860. unLowPriorityTime = memberLowPriorityTime;
  861. }
  862. }
  863. if ( unPreventDate != Obj().matchmaking_ban_time() )
  864. {
  865. Obj().set_matchmaking_ban_time( unPreventDate );
  866. bDirty = true;
  867. }
  868. if ( unPreventAccountID != Obj().matchmaking_ban_account_id() )
  869. {
  870. Obj().set_matchmaking_ban_account_id( unPreventAccountID );
  871. bDirty = true;
  872. }
  873. if ( unLowPriorityTime != Obj().matchmaking_low_priority_time() )
  874. {
  875. Obj().set_matchmaking_low_priority_time( unLowPriorityTime );
  876. bDirty = true;
  877. }
  878. if ( bDirty )
  879. {
  880. DirtyParty();
  881. }
  882. }
  883. void CTFPartyInvite::YldInitFromPlayerGroup( GCSDK::IPlayerGroup *pPlayerGroup )
  884. {
  885. const char *szAccountName = GGCBase()->YieldingGetPersonaName( pPlayerGroup->GetLeader(), "Unknown Player" );
  886. SetGroupID( pPlayerGroup->GetGroupID() );
  887. SetSenderID( pPlayerGroup->GetLeader().ConvertToUint64() );
  888. SetSenderName( szAccountName );
  889. CTFParty *pParty = static_cast<CTFParty*>( pPlayerGroup );
  890. // Fill in party members and their avatars
  891. int nMembers = pParty->GetNumMembers();
  892. for ( int i = 0; i < nMembers; i++ )
  893. {
  894. const char *szPersonaName = GGCBase()->YieldingGetPersonaName( pParty->GetMember( i ), "Unknown Player" );
  895. uint16 unAvatar = 0;
  896. // !FIXME! tf_mvm_matchmaking
  897. // CGCSharedObjectCache *pCache = GGCTF()->YieldingFindOrLoadSOCache( pParty->GetMember( i ) );
  898. // if ( pCache )
  899. // {
  900. // CTFGameAccountClient iSharedObject;
  901. // iSharedObject.Obj().set_account_id( pParty->GetMember( i ).GetAccountID() );
  902. // CTFGameAccountClient *pPlayerSummary = static_cast<CTFGameAccountClient*>( pCache->FindSharedObject( iSharedObject ) );
  903. // if ( pPlayerSummary )
  904. // {
  905. // unAvatar = (uint16) pPlayerSummary->GetAvatar();
  906. // }
  907. // }
  908. AddMember( pParty->GetMember( i ), szPersonaName, unAvatar );
  909. }
  910. }
  911. void CTFPartyInvite::AddMember( const CSteamID &steamID, const char *szPersonaName, uint16 unAvatar )
  912. {
  913. EmitInfo( SPEW_GC, MATCHMAKING_SPEWLEVEL4, LOG_ALWAYS, "PartyInvite %016llx AddMember(%s, %s, %u)\n", GetGroupID(), steamID.Render(), szPersonaName ? szPersonaName : "", unAvatar );
  914. CSOTFPartyInvite_PartyMember *pMember = Obj().add_members();
  915. pMember->set_steam_id( steamID.ConvertToUint64() );
  916. pMember->set_name( szPersonaName ? szPersonaName : "Unknown" );
  917. pMember->set_avatar( unAvatar );
  918. }
  919. #endif