Counter Strike : Global Offensive Source Code
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.

418 lines
11 KiB

  1. #include "client_pch.h"
  2. #include "cl_splitscreen.h"
  3. #if defined( _PS3 )
  4. #include "tls_ps3.h"
  5. #define m_SplitSlot reinterpret_cast< SplitSlot_t *& >(GetTLSGlobals()->pEngineSplitSlot)
  6. #endif // _PS3
  7. // memdbgon must be the last include file in a .cpp file!!!
  8. #include "tier0/memdbgon.h"
  9. class CSplitScreen : public ISplitScreen
  10. {
  11. public:
  12. CSplitScreen();
  13. virtual bool Init();
  14. virtual void Shutdown();
  15. virtual bool AddSplitScreenUser( int nSlot, int nPlayerIndex );
  16. virtual bool AddBaseUser( int nSlot, int nPlayerIndex );
  17. virtual bool RemoveSplitScreenUser( int nSlot, int nPlayerIndex );
  18. virtual int GetActiveSplitScreenPlayerSlot();
  19. virtual int SetActiveSplitScreenPlayerSlot( int slot );
  20. virtual bool IsValidSplitScreenSlot( int nSlot );
  21. virtual int FirstValidSplitScreenSlot(); // -1 == invalid
  22. virtual int NextValidSplitScreenSlot( int nPreviousSlot ); // -1 == invalid
  23. virtual int GetNumSplitScreenPlayers();
  24. virtual int GetSplitScreenPlayerEntity( int nSlot );
  25. virtual INetChannel *GetSplitScreenPlayerNetChan( int nSlot );
  26. virtual bool IsDisconnecting( int nSlot );
  27. virtual void SetDisconnecting( int nSlot, bool bState );
  28. virtual bool SetLocalPlayerIsResolvable( char const *pchContext, int nLine, bool bResolvable );
  29. virtual bool IsLocalPlayerResolvable();
  30. CClientState &GetLocalPlayer( int nSlot = -1 );
  31. struct SplitSlot_t
  32. {
  33. SplitSlot_t() : m_nActiveSplitScreenPlayer( 0 ), m_bLocalPlayerResolvable( false ), m_bMainThread( false ) { }
  34. short m_nActiveSplitScreenPlayer;
  35. // Can a call to C_BasePlayer::GetLocalPlayer be resolved in client .dll (inside a setactivesplitscreenuser scope?)
  36. unsigned short m_bLocalPlayerResolvable : 1;
  37. unsigned short m_bMainThread : 1;
  38. unsigned short pad : 14;
  39. };
  40. private:
  41. int FindSplitPlayerSlot( int nPlayerEntityIndex );
  42. struct SplitPlayer_t
  43. {
  44. SplitPlayer_t() :
  45. m_bActive( false )
  46. {
  47. }
  48. bool m_bActive;
  49. CClientState m_Client;
  50. };
  51. SplitPlayer_t *m_SplitScreenPlayers[ MAX_SPLITSCREEN_CLIENTS ];
  52. int m_nActiveSplitScreenUserCount;
  53. #if defined( _PS3 )
  54. #elif !defined( _X360 )
  55. // Each thread (mainly an issue in the client .dll) can have it's own "active" context. The per thread data is allocated as needed
  56. #else
  57. // xbox uses 12 bit thread id key to do direct lookup
  58. SplitSlot_t m_SplitSlotTable[0x1000];
  59. #endif
  60. SplitSlot_t *GetSplitSlot();
  61. bool m_bInitialized;
  62. };
  63. static CTHREADLOCALPTR( CSplitScreen::SplitSlot_t ) s_SplitSlot;
  64. static CSplitScreen g_SplitScreenMgr;
  65. ISplitScreen *splitscreen = &g_SplitScreenMgr;
  66. CSplitScreen::CSplitScreen()
  67. {
  68. m_bInitialized = false;
  69. }
  70. #if defined( _X360 )
  71. inline int BucketForThreadId()
  72. {
  73. DWORD id = GetCurrentThreadId();
  74. // e.g.: 0xF9000028 -- or's the 9 and the 28 to give 12 bits (slot array is 0x1000 in size), the first nibble is(appears to be) always F so is masked off (0x0F00)
  75. return ( ( id >> 16 ) & 0x00000F00 ) | ( id & 0x000000FF );
  76. }
  77. #endif
  78. CSplitScreen::SplitSlot_t *CSplitScreen::GetSplitSlot()
  79. {
  80. #if defined( _X360 )
  81. // pix shows this function to be enormously expensive due to high frequency of inner loop calls
  82. // avoid conditionals and TLS, use a direct lookup instead
  83. return &m_SplitSlotTable[ BucketForThreadId() ];
  84. #else
  85. if ( !s_SplitSlot )
  86. {
  87. s_SplitSlot = new SplitSlot_t();
  88. }
  89. return s_SplitSlot;
  90. #endif
  91. }
  92. bool CSplitScreen::Init()
  93. {
  94. m_bInitialized = true;
  95. Assert( ThreadInMainThread() );
  96. SplitSlot_t *pSlot = GetSplitSlot();
  97. pSlot->m_bLocalPlayerResolvable = false;
  98. pSlot->m_nActiveSplitScreenPlayer = 0;
  99. pSlot->m_bMainThread = true;
  100. m_nActiveSplitScreenUserCount = 1;
  101. for ( int i = 0 ; i < MAX_SPLITSCREEN_CLIENTS; ++i )
  102. {
  103. MEM_ALLOC_CREDIT();
  104. m_SplitScreenPlayers[ i ] = new SplitPlayer_t();
  105. SplitPlayer_t *sp = m_SplitScreenPlayers[ i ];
  106. sp->m_bActive = ( i == 0 ) ? true : false;
  107. sp->m_Client.m_bSplitScreenUser = ( i != 0 ) ? true : false;
  108. }
  109. return true;
  110. }
  111. void CSplitScreen::Shutdown()
  112. {
  113. Assert( ThreadInMainThread() );
  114. for ( int i = 0; i < MAX_SPLITSCREEN_CLIENTS; ++i )
  115. {
  116. delete m_SplitScreenPlayers[ i ];
  117. m_SplitScreenPlayers[ i ] = NULL;
  118. }
  119. }
  120. bool CSplitScreen::AddBaseUser( int nSlot, int nPlayerIndex )
  121. {
  122. Assert( ThreadInMainThread() );
  123. SplitPlayer_t *sp = m_SplitScreenPlayers[ nSlot ];
  124. sp->m_bActive = true;
  125. sp->m_Client.m_nSplitScreenSlot = nSlot;
  126. return true;
  127. }
  128. bool CSplitScreen::AddSplitScreenUser( int nSlot, int nPlayerEntityIndex )
  129. {
  130. Assert( ThreadInMainThread() );
  131. SplitPlayer_t *sp = m_SplitScreenPlayers[ nSlot ];
  132. if ( sp->m_bActive == true )
  133. {
  134. Assert( sp->m_Client.m_nSplitScreenSlot == nSlot );
  135. Assert( sp->m_Client.m_nPlayerSlot == nPlayerEntityIndex - 1 );
  136. return true;
  137. }
  138. // Msg( "Attached %d to slot %d\n", nPlayerEntityIndex, nSlot );
  139. // 0.0.0.0:0 signifies a bot. It'll plumb all the way down to winsock calls but it won't make them.
  140. ns_address adr;
  141. adr.SetAddrType( NSAT_NETADR );
  142. adr.m_adr.SetIPAndPort( 0, 0 );
  143. char szName[ 256 ];
  144. Q_snprintf( szName, sizeof( szName), "SPLIT%d", nSlot );
  145. sp->m_bActive = true;
  146. sp->m_Client.Clear();
  147. sp->m_Client.m_nPlayerSlot = nPlayerEntityIndex - 1;
  148. sp->m_Client.m_nSplitScreenSlot = nSlot;
  149. sp->m_Client.m_NetChannel = NET_CreateNetChannel( NS_CLIENT, &adr, szName, &sp->m_Client, NULL, true );
  150. GetBaseLocalClient().m_NetChannel->AttachSplitPlayer( nSlot, sp->m_Client.m_NetChannel );
  151. sp->m_Client.m_nViewEntity = nPlayerEntityIndex;
  152. ++m_nActiveSplitScreenUserCount;
  153. SetDisconnecting( nSlot, false );
  154. ClientDLL_OnSplitScreenStateChanged();
  155. return true;
  156. }
  157. bool CSplitScreen::RemoveSplitScreenUser( int nSlot, int nPlayerIndex )
  158. {
  159. Assert( ThreadInMainThread() );
  160. // Msg( "Detached %d from slot %d\n", nPlayerIndex, nSlot );
  161. int idx = FindSplitPlayerSlot( nPlayerIndex );
  162. if ( idx != -1 )
  163. {
  164. SplitPlayer_t *sp = m_SplitScreenPlayers[ idx ];
  165. if ( sp->m_Client.m_NetChannel )
  166. {
  167. GetBaseLocalClient().m_NetChannel->DetachSplitPlayer( idx );
  168. sp->m_Client.m_NetChannel->Shutdown( "RemoveSplitScreenUser" );
  169. sp->m_Client.m_NetChannel = NULL;
  170. }
  171. sp->m_Client.m_nPlayerSlot = -1;
  172. sp->m_bActive = false;
  173. SetDisconnecting( nSlot, true );
  174. --m_nActiveSplitScreenUserCount;
  175. ClientDLL_OnSplitScreenStateChanged();
  176. }
  177. return true;
  178. }
  179. int CSplitScreen::GetActiveSplitScreenPlayerSlot()
  180. {
  181. #if !defined( SPLIT_SCREEN_STUBS )
  182. SplitSlot_t *pSlot = GetSplitSlot();
  183. int nSlot = pSlot->m_nActiveSplitScreenPlayer;
  184. #if defined( _DEBUG )
  185. if ( nSlot >= host_state.max_splitscreen_players_clientdll )
  186. {
  187. static bool warned = false;
  188. if ( !warned )
  189. {
  190. warned = true;
  191. Warning( "GetActiveSplitScreenPlayerSlot() returning bogus slot #%d\n", nSlot );
  192. }
  193. }
  194. #endif
  195. return nSlot;
  196. #else
  197. return 0;
  198. #endif
  199. }
  200. int CSplitScreen::SetActiveSplitScreenPlayerSlot( int slot )
  201. {
  202. #if !defined( SPLIT_SCREEN_STUBS )
  203. Assert( m_bInitialized );
  204. slot = clamp( slot, 0, host_state.max_splitscreen_players_clientdll - 1 );
  205. SplitSlot_t *pSlot = GetSplitSlot();
  206. Assert( pSlot );
  207. int old = pSlot->m_nActiveSplitScreenPlayer;
  208. if ( slot == old )
  209. return slot;
  210. pSlot->m_nActiveSplitScreenPlayer = slot;
  211. // Only change netchannel in main thread and only change vgui message context id in main thread (for now)
  212. if ( pSlot->m_bMainThread )
  213. {
  214. if ( m_SplitScreenPlayers[ slot ] && m_SplitScreenPlayers[ 0 ] )
  215. {
  216. INetChannel *nc = m_SplitScreenPlayers[ slot ]->m_Client.m_NetChannel;
  217. CBaseClientState &bcs = m_SplitScreenPlayers[ 0 ]->m_Client;
  218. if ( bcs.m_NetChannel && nc )
  219. {
  220. bcs.m_NetChannel->SetActiveChannel( nc );
  221. }
  222. }
  223. }
  224. return old;
  225. #else
  226. return 0;
  227. #endif
  228. }
  229. int CSplitScreen::GetNumSplitScreenPlayers()
  230. {
  231. return m_nActiveSplitScreenUserCount;
  232. }
  233. int CSplitScreen::GetSplitScreenPlayerEntity( int nSlot )
  234. {
  235. Assert( nSlot >= 0 && nSlot < host_state.max_splitscreen_players );
  236. Assert( host_state.max_splitscreen_players <= MAX_SPLITSCREEN_CLIENTS );
  237. if ( nSlot < 0 || nSlot >= host_state.max_splitscreen_players )
  238. return -1;
  239. if ( !m_SplitScreenPlayers[ nSlot ]->m_bActive )
  240. return -1;
  241. return m_SplitScreenPlayers[ nSlot ]->m_Client.m_nPlayerSlot + 1;
  242. }
  243. INetChannel *CSplitScreen::GetSplitScreenPlayerNetChan( int nSlot )
  244. {
  245. Assert( nSlot >= 0 && nSlot < host_state.max_splitscreen_players );
  246. Assert( host_state.max_splitscreen_players <= MAX_SPLITSCREEN_CLIENTS );
  247. if ( nSlot < 0 || nSlot >= host_state.max_splitscreen_players )
  248. return NULL;
  249. if ( !m_SplitScreenPlayers[ nSlot ]->m_bActive )
  250. return NULL;
  251. return m_SplitScreenPlayers[ nSlot ]->m_Client.m_NetChannel;
  252. }
  253. bool CSplitScreen::IsValidSplitScreenSlot( int nSlot )
  254. {
  255. Assert( host_state.max_splitscreen_players <= MAX_SPLITSCREEN_CLIENTS );
  256. if ( nSlot < 0 || nSlot >= host_state.max_splitscreen_players )
  257. return false;
  258. return m_SplitScreenPlayers[ nSlot ]->m_bActive;
  259. }
  260. int CSplitScreen::FirstValidSplitScreenSlot()
  261. {
  262. return 0;
  263. }
  264. int CSplitScreen::NextValidSplitScreenSlot( int nPreviousSlot )
  265. {
  266. for ( ;; )
  267. {
  268. ++nPreviousSlot;
  269. if ( nPreviousSlot >= host_state.max_splitscreen_players )
  270. {
  271. return -1;
  272. }
  273. if ( m_SplitScreenPlayers[ nPreviousSlot ]->m_bActive )
  274. {
  275. break;
  276. }
  277. }
  278. return nPreviousSlot;
  279. }
  280. int CSplitScreen::FindSplitPlayerSlot( int nPlayerEntityIndex )
  281. {
  282. int nPlayerSlot = nPlayerEntityIndex - 1;
  283. Assert( host_state.max_splitscreen_players <= MAX_SPLITSCREEN_CLIENTS );
  284. for ( int i = 1 ; i < host_state.max_splitscreen_players ; ++i )
  285. {
  286. if ( m_SplitScreenPlayers[ i ]->m_Client.m_nPlayerSlot == nPlayerSlot )
  287. {
  288. return i;
  289. }
  290. }
  291. return -1;
  292. }
  293. bool CSplitScreen::IsDisconnecting( int nSlot )
  294. {
  295. Assert( nSlot >= 0 && nSlot < host_state.max_splitscreen_players );
  296. Assert( host_state.max_splitscreen_players <= MAX_SPLITSCREEN_CLIENTS );
  297. if ( nSlot < 0 || nSlot >= host_state.max_splitscreen_players )
  298. return false;
  299. return ( m_SplitScreenPlayers[ nSlot ]->m_Client.m_nSignonState == SIGNONSTATE_NONE ) ? true : false;
  300. }
  301. void CSplitScreen::SetDisconnecting( int nSlot, bool bState )
  302. {
  303. Assert( nSlot >= 0 && nSlot < host_state.max_splitscreen_players );
  304. Assert( host_state.max_splitscreen_players <= MAX_SPLITSCREEN_CLIENTS );
  305. if ( nSlot < 0 || nSlot >= host_state.max_splitscreen_players )
  306. return;
  307. Assert( nSlot != 0 );
  308. m_SplitScreenPlayers[ nSlot ]->m_Client.m_nSignonState = bState ? SIGNONSTATE_NONE : SIGNONSTATE_FULL;
  309. }
  310. CClientState &CSplitScreen::GetLocalPlayer( int nSlot /*= -1*/ )
  311. {
  312. if ( nSlot == -1 )
  313. {
  314. Assert( IsLocalPlayerResolvable() );
  315. return m_SplitScreenPlayers[ GetActiveSplitScreenPlayerSlot() ]->m_Client;
  316. }
  317. return m_SplitScreenPlayers[ nSlot ]->m_Client;
  318. }
  319. bool CSplitScreen::SetLocalPlayerIsResolvable( char const *pchContext, int line, bool bResolvable )
  320. {
  321. SplitSlot_t *pSlot = GetSplitSlot();
  322. Assert( pSlot );
  323. bool bPrev = pSlot->m_bLocalPlayerResolvable;
  324. pSlot->m_bLocalPlayerResolvable = bResolvable;
  325. return bPrev;
  326. }
  327. bool CSplitScreen::IsLocalPlayerResolvable()
  328. {
  329. #if defined( SPLIT_SCREEN_STUBS )
  330. return true;
  331. #else
  332. SplitSlot_t *pSlot = GetSplitSlot();
  333. return pSlot->m_bLocalPlayerResolvable;
  334. #endif
  335. }
  336. // Singleton client state
  337. CClientState &GetLocalClient( int nSlot /*= -1*/ )
  338. {
  339. return g_SplitScreenMgr.GetLocalPlayer( nSlot );
  340. }
  341. CClientState &GetBaseLocalClient()
  342. {
  343. return g_SplitScreenMgr.GetLocalPlayer( 0 );
  344. }