419 lines
11 KiB
C++
419 lines
11 KiB
C++
|
#include "client_pch.h"
|
||
|
#include "cl_splitscreen.h"
|
||
|
|
||
|
#if defined( _PS3 )
|
||
|
#include "tls_ps3.h"
|
||
|
#define m_SplitSlot reinterpret_cast< SplitSlot_t *& >(GetTLSGlobals()->pEngineSplitSlot)
|
||
|
#endif // _PS3
|
||
|
|
||
|
// memdbgon must be the last include file in a .cpp file!!!
|
||
|
#include "tier0/memdbgon.h"
|
||
|
|
||
|
class CSplitScreen : public ISplitScreen
|
||
|
{
|
||
|
public:
|
||
|
CSplitScreen();
|
||
|
|
||
|
virtual bool Init();
|
||
|
virtual void Shutdown();
|
||
|
|
||
|
virtual bool AddSplitScreenUser( int nSlot, int nPlayerIndex );
|
||
|
virtual bool AddBaseUser( int nSlot, int nPlayerIndex );
|
||
|
virtual bool RemoveSplitScreenUser( int nSlot, int nPlayerIndex );
|
||
|
virtual int GetActiveSplitScreenPlayerSlot();
|
||
|
virtual int SetActiveSplitScreenPlayerSlot( int slot );
|
||
|
|
||
|
virtual bool IsValidSplitScreenSlot( int nSlot );
|
||
|
virtual int FirstValidSplitScreenSlot(); // -1 == invalid
|
||
|
virtual int NextValidSplitScreenSlot( int nPreviousSlot ); // -1 == invalid
|
||
|
|
||
|
virtual int GetNumSplitScreenPlayers();
|
||
|
virtual int GetSplitScreenPlayerEntity( int nSlot );
|
||
|
virtual INetChannel *GetSplitScreenPlayerNetChan( int nSlot );
|
||
|
|
||
|
virtual bool IsDisconnecting( int nSlot );
|
||
|
virtual void SetDisconnecting( int nSlot, bool bState );
|
||
|
|
||
|
virtual bool SetLocalPlayerIsResolvable( char const *pchContext, int nLine, bool bResolvable );
|
||
|
virtual bool IsLocalPlayerResolvable();
|
||
|
|
||
|
CClientState &GetLocalPlayer( int nSlot = -1 );
|
||
|
|
||
|
struct SplitSlot_t
|
||
|
{
|
||
|
SplitSlot_t() : m_nActiveSplitScreenPlayer( 0 ), m_bLocalPlayerResolvable( false ), m_bMainThread( false ) { }
|
||
|
|
||
|
short m_nActiveSplitScreenPlayer;
|
||
|
// Can a call to C_BasePlayer::GetLocalPlayer be resolved in client .dll (inside a setactivesplitscreenuser scope?)
|
||
|
unsigned short m_bLocalPlayerResolvable : 1;
|
||
|
unsigned short m_bMainThread : 1;
|
||
|
unsigned short pad : 14;
|
||
|
};
|
||
|
|
||
|
private:
|
||
|
|
||
|
int FindSplitPlayerSlot( int nPlayerEntityIndex );
|
||
|
|
||
|
struct SplitPlayer_t
|
||
|
{
|
||
|
SplitPlayer_t() :
|
||
|
m_bActive( false )
|
||
|
{
|
||
|
}
|
||
|
|
||
|
bool m_bActive;
|
||
|
CClientState m_Client;
|
||
|
};
|
||
|
|
||
|
SplitPlayer_t *m_SplitScreenPlayers[ MAX_SPLITSCREEN_CLIENTS ];
|
||
|
int m_nActiveSplitScreenUserCount;
|
||
|
|
||
|
#if defined( _PS3 )
|
||
|
#elif !defined( _X360 )
|
||
|
// Each thread (mainly an issue in the client .dll) can have it's own "active" context. The per thread data is allocated as needed
|
||
|
#else
|
||
|
// xbox uses 12 bit thread id key to do direct lookup
|
||
|
SplitSlot_t m_SplitSlotTable[0x1000];
|
||
|
#endif
|
||
|
|
||
|
SplitSlot_t *GetSplitSlot();
|
||
|
|
||
|
bool m_bInitialized;
|
||
|
};
|
||
|
|
||
|
static CTHREADLOCALPTR( CSplitScreen::SplitSlot_t ) s_SplitSlot;
|
||
|
|
||
|
|
||
|
static CSplitScreen g_SplitScreenMgr;
|
||
|
ISplitScreen *splitscreen = &g_SplitScreenMgr;
|
||
|
|
||
|
CSplitScreen::CSplitScreen()
|
||
|
{
|
||
|
m_bInitialized = false;
|
||
|
}
|
||
|
|
||
|
#if defined( _X360 )
|
||
|
inline int BucketForThreadId()
|
||
|
{
|
||
|
DWORD id = GetCurrentThreadId();
|
||
|
// 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)
|
||
|
return ( ( id >> 16 ) & 0x00000F00 ) | ( id & 0x000000FF );
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
CSplitScreen::SplitSlot_t *CSplitScreen::GetSplitSlot()
|
||
|
{
|
||
|
#if defined( _X360 )
|
||
|
// pix shows this function to be enormously expensive due to high frequency of inner loop calls
|
||
|
// avoid conditionals and TLS, use a direct lookup instead
|
||
|
return &m_SplitSlotTable[ BucketForThreadId() ];
|
||
|
#else
|
||
|
if ( !s_SplitSlot )
|
||
|
{
|
||
|
s_SplitSlot = new SplitSlot_t();
|
||
|
}
|
||
|
return s_SplitSlot;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
bool CSplitScreen::Init()
|
||
|
{
|
||
|
m_bInitialized = true;
|
||
|
|
||
|
Assert( ThreadInMainThread() );
|
||
|
|
||
|
SplitSlot_t *pSlot = GetSplitSlot();
|
||
|
pSlot->m_bLocalPlayerResolvable = false;
|
||
|
pSlot->m_nActiveSplitScreenPlayer = 0;
|
||
|
pSlot->m_bMainThread = true;
|
||
|
m_nActiveSplitScreenUserCount = 1;
|
||
|
for ( int i = 0 ; i < MAX_SPLITSCREEN_CLIENTS; ++i )
|
||
|
{
|
||
|
MEM_ALLOC_CREDIT();
|
||
|
m_SplitScreenPlayers[ i ] = new SplitPlayer_t();
|
||
|
SplitPlayer_t *sp = m_SplitScreenPlayers[ i ];
|
||
|
sp->m_bActive = ( i == 0 ) ? true : false;
|
||
|
sp->m_Client.m_bSplitScreenUser = ( i != 0 ) ? true : false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void CSplitScreen::Shutdown()
|
||
|
{
|
||
|
Assert( ThreadInMainThread() );
|
||
|
for ( int i = 0; i < MAX_SPLITSCREEN_CLIENTS; ++i )
|
||
|
{
|
||
|
delete m_SplitScreenPlayers[ i ];
|
||
|
m_SplitScreenPlayers[ i ] = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool CSplitScreen::AddBaseUser( int nSlot, int nPlayerIndex )
|
||
|
{
|
||
|
Assert( ThreadInMainThread() );
|
||
|
SplitPlayer_t *sp = m_SplitScreenPlayers[ nSlot ];
|
||
|
sp->m_bActive = true;
|
||
|
sp->m_Client.m_nSplitScreenSlot = nSlot;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool CSplitScreen::AddSplitScreenUser( int nSlot, int nPlayerEntityIndex )
|
||
|
{
|
||
|
Assert( ThreadInMainThread() );
|
||
|
SplitPlayer_t *sp = m_SplitScreenPlayers[ nSlot ];
|
||
|
if ( sp->m_bActive == true )
|
||
|
{
|
||
|
Assert( sp->m_Client.m_nSplitScreenSlot == nSlot );
|
||
|
Assert( sp->m_Client.m_nPlayerSlot == nPlayerEntityIndex - 1 );
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// Msg( "Attached %d to slot %d\n", nPlayerEntityIndex, nSlot );
|
||
|
|
||
|
// 0.0.0.0:0 signifies a bot. It'll plumb all the way down to winsock calls but it won't make them.
|
||
|
ns_address adr;
|
||
|
adr.SetAddrType( NSAT_NETADR );
|
||
|
adr.m_adr.SetIPAndPort( 0, 0 );
|
||
|
|
||
|
char szName[ 256 ];
|
||
|
Q_snprintf( szName, sizeof( szName), "SPLIT%d", nSlot );
|
||
|
|
||
|
sp->m_bActive = true;
|
||
|
sp->m_Client.Clear();
|
||
|
sp->m_Client.m_nPlayerSlot = nPlayerEntityIndex - 1;
|
||
|
sp->m_Client.m_nSplitScreenSlot = nSlot;
|
||
|
sp->m_Client.m_NetChannel = NET_CreateNetChannel( NS_CLIENT, &adr, szName, &sp->m_Client, NULL, true );
|
||
|
GetBaseLocalClient().m_NetChannel->AttachSplitPlayer( nSlot, sp->m_Client.m_NetChannel );
|
||
|
sp->m_Client.m_nViewEntity = nPlayerEntityIndex;
|
||
|
++m_nActiveSplitScreenUserCount;
|
||
|
SetDisconnecting( nSlot, false );
|
||
|
|
||
|
ClientDLL_OnSplitScreenStateChanged();
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool CSplitScreen::RemoveSplitScreenUser( int nSlot, int nPlayerIndex )
|
||
|
{
|
||
|
Assert( ThreadInMainThread() );
|
||
|
// Msg( "Detached %d from slot %d\n", nPlayerIndex, nSlot );
|
||
|
|
||
|
int idx = FindSplitPlayerSlot( nPlayerIndex );
|
||
|
if ( idx != -1 )
|
||
|
{
|
||
|
SplitPlayer_t *sp = m_SplitScreenPlayers[ idx ];
|
||
|
if ( sp->m_Client.m_NetChannel )
|
||
|
{
|
||
|
GetBaseLocalClient().m_NetChannel->DetachSplitPlayer( idx );
|
||
|
sp->m_Client.m_NetChannel->Shutdown( "RemoveSplitScreenUser" );
|
||
|
sp->m_Client.m_NetChannel = NULL;
|
||
|
}
|
||
|
sp->m_Client.m_nPlayerSlot = -1;
|
||
|
sp->m_bActive = false;
|
||
|
SetDisconnecting( nSlot, true );
|
||
|
--m_nActiveSplitScreenUserCount;
|
||
|
ClientDLL_OnSplitScreenStateChanged();
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
int CSplitScreen::GetActiveSplitScreenPlayerSlot()
|
||
|
{
|
||
|
#if !defined( SPLIT_SCREEN_STUBS )
|
||
|
SplitSlot_t *pSlot = GetSplitSlot();
|
||
|
int nSlot = pSlot->m_nActiveSplitScreenPlayer;
|
||
|
#if defined( _DEBUG )
|
||
|
if ( nSlot >= host_state.max_splitscreen_players_clientdll )
|
||
|
{
|
||
|
static bool warned = false;
|
||
|
if ( !warned )
|
||
|
{
|
||
|
warned = true;
|
||
|
Warning( "GetActiveSplitScreenPlayerSlot() returning bogus slot #%d\n", nSlot );
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
return nSlot;
|
||
|
#else
|
||
|
return 0;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
int CSplitScreen::SetActiveSplitScreenPlayerSlot( int slot )
|
||
|
{
|
||
|
#if !defined( SPLIT_SCREEN_STUBS )
|
||
|
Assert( m_bInitialized );
|
||
|
|
||
|
slot = clamp( slot, 0, host_state.max_splitscreen_players_clientdll - 1 );
|
||
|
|
||
|
SplitSlot_t *pSlot = GetSplitSlot();
|
||
|
Assert( pSlot );
|
||
|
int old = pSlot->m_nActiveSplitScreenPlayer;
|
||
|
|
||
|
if ( slot == old )
|
||
|
return slot;
|
||
|
|
||
|
pSlot->m_nActiveSplitScreenPlayer = slot;
|
||
|
|
||
|
// Only change netchannel in main thread and only change vgui message context id in main thread (for now)
|
||
|
if ( pSlot->m_bMainThread )
|
||
|
{
|
||
|
if ( m_SplitScreenPlayers[ slot ] && m_SplitScreenPlayers[ 0 ] )
|
||
|
{
|
||
|
INetChannel *nc = m_SplitScreenPlayers[ slot ]->m_Client.m_NetChannel;
|
||
|
CBaseClientState &bcs = m_SplitScreenPlayers[ 0 ]->m_Client;
|
||
|
if ( bcs.m_NetChannel && nc )
|
||
|
{
|
||
|
bcs.m_NetChannel->SetActiveChannel( nc );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return old;
|
||
|
#else
|
||
|
return 0;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
int CSplitScreen::GetNumSplitScreenPlayers()
|
||
|
{
|
||
|
return m_nActiveSplitScreenUserCount;
|
||
|
}
|
||
|
|
||
|
int CSplitScreen::GetSplitScreenPlayerEntity( int nSlot )
|
||
|
{
|
||
|
Assert( nSlot >= 0 && nSlot < host_state.max_splitscreen_players );
|
||
|
Assert( host_state.max_splitscreen_players <= MAX_SPLITSCREEN_CLIENTS );
|
||
|
if ( nSlot < 0 || nSlot >= host_state.max_splitscreen_players )
|
||
|
return -1;
|
||
|
if ( !m_SplitScreenPlayers[ nSlot ]->m_bActive )
|
||
|
return -1;
|
||
|
return m_SplitScreenPlayers[ nSlot ]->m_Client.m_nPlayerSlot + 1;
|
||
|
}
|
||
|
|
||
|
INetChannel *CSplitScreen::GetSplitScreenPlayerNetChan( int nSlot )
|
||
|
{
|
||
|
Assert( nSlot >= 0 && nSlot < host_state.max_splitscreen_players );
|
||
|
Assert( host_state.max_splitscreen_players <= MAX_SPLITSCREEN_CLIENTS );
|
||
|
if ( nSlot < 0 || nSlot >= host_state.max_splitscreen_players )
|
||
|
return NULL;
|
||
|
if ( !m_SplitScreenPlayers[ nSlot ]->m_bActive )
|
||
|
return NULL;
|
||
|
return m_SplitScreenPlayers[ nSlot ]->m_Client.m_NetChannel;
|
||
|
}
|
||
|
|
||
|
bool CSplitScreen::IsValidSplitScreenSlot( int nSlot )
|
||
|
{
|
||
|
Assert( host_state.max_splitscreen_players <= MAX_SPLITSCREEN_CLIENTS );
|
||
|
if ( nSlot < 0 || nSlot >= host_state.max_splitscreen_players )
|
||
|
return false;
|
||
|
return m_SplitScreenPlayers[ nSlot ]->m_bActive;
|
||
|
}
|
||
|
|
||
|
int CSplitScreen::FirstValidSplitScreenSlot()
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int CSplitScreen::NextValidSplitScreenSlot( int nPreviousSlot )
|
||
|
{
|
||
|
for ( ;; )
|
||
|
{
|
||
|
++nPreviousSlot;
|
||
|
if ( nPreviousSlot >= host_state.max_splitscreen_players )
|
||
|
{
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if ( m_SplitScreenPlayers[ nPreviousSlot ]->m_bActive )
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
return nPreviousSlot;
|
||
|
}
|
||
|
|
||
|
int CSplitScreen::FindSplitPlayerSlot( int nPlayerEntityIndex )
|
||
|
{
|
||
|
int nPlayerSlot = nPlayerEntityIndex - 1;
|
||
|
|
||
|
Assert( host_state.max_splitscreen_players <= MAX_SPLITSCREEN_CLIENTS );
|
||
|
for ( int i = 1 ; i < host_state.max_splitscreen_players ; ++i )
|
||
|
{
|
||
|
if ( m_SplitScreenPlayers[ i ]->m_Client.m_nPlayerSlot == nPlayerSlot )
|
||
|
{
|
||
|
return i;
|
||
|
}
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
bool CSplitScreen::IsDisconnecting( int nSlot )
|
||
|
{
|
||
|
Assert( nSlot >= 0 && nSlot < host_state.max_splitscreen_players );
|
||
|
Assert( host_state.max_splitscreen_players <= MAX_SPLITSCREEN_CLIENTS );
|
||
|
if ( nSlot < 0 || nSlot >= host_state.max_splitscreen_players )
|
||
|
return false;
|
||
|
|
||
|
return ( m_SplitScreenPlayers[ nSlot ]->m_Client.m_nSignonState == SIGNONSTATE_NONE ) ? true : false;
|
||
|
}
|
||
|
|
||
|
void CSplitScreen::SetDisconnecting( int nSlot, bool bState )
|
||
|
{
|
||
|
Assert( nSlot >= 0 && nSlot < host_state.max_splitscreen_players );
|
||
|
Assert( host_state.max_splitscreen_players <= MAX_SPLITSCREEN_CLIENTS );
|
||
|
if ( nSlot < 0 || nSlot >= host_state.max_splitscreen_players )
|
||
|
return;
|
||
|
|
||
|
Assert( nSlot != 0 );
|
||
|
m_SplitScreenPlayers[ nSlot ]->m_Client.m_nSignonState = bState ? SIGNONSTATE_NONE : SIGNONSTATE_FULL;
|
||
|
}
|
||
|
|
||
|
CClientState &CSplitScreen::GetLocalPlayer( int nSlot /*= -1*/ )
|
||
|
{
|
||
|
if ( nSlot == -1 )
|
||
|
{
|
||
|
Assert( IsLocalPlayerResolvable() );
|
||
|
return m_SplitScreenPlayers[ GetActiveSplitScreenPlayerSlot() ]->m_Client;
|
||
|
}
|
||
|
return m_SplitScreenPlayers[ nSlot ]->m_Client;
|
||
|
}
|
||
|
|
||
|
bool CSplitScreen::SetLocalPlayerIsResolvable( char const *pchContext, int line, bool bResolvable )
|
||
|
{
|
||
|
SplitSlot_t *pSlot = GetSplitSlot();
|
||
|
Assert( pSlot );
|
||
|
bool bPrev = pSlot->m_bLocalPlayerResolvable;
|
||
|
pSlot->m_bLocalPlayerResolvable = bResolvable;
|
||
|
return bPrev;
|
||
|
}
|
||
|
|
||
|
bool CSplitScreen::IsLocalPlayerResolvable()
|
||
|
{
|
||
|
|
||
|
#if defined( SPLIT_SCREEN_STUBS )
|
||
|
|
||
|
return true;
|
||
|
|
||
|
#else
|
||
|
|
||
|
SplitSlot_t *pSlot = GetSplitSlot();
|
||
|
return pSlot->m_bLocalPlayerResolvable;
|
||
|
|
||
|
#endif
|
||
|
|
||
|
}
|
||
|
|
||
|
// Singleton client state
|
||
|
CClientState &GetLocalClient( int nSlot /*= -1*/ )
|
||
|
{
|
||
|
return g_SplitScreenMgr.GetLocalPlayer( nSlot );
|
||
|
}
|
||
|
|
||
|
CClientState &GetBaseLocalClient()
|
||
|
{
|
||
|
return g_SplitScreenMgr.GetLocalPlayer( 0 );
|
||
|
}
|