333 lines
9.8 KiB
C++
333 lines
9.8 KiB
C++
|
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
|||
|
//
|
|||
|
// Purpose:
|
|||
|
//
|
|||
|
//===========================================================================//
|
|||
|
|
|||
|
#include "mm_framework.h"
|
|||
|
|
|||
|
#include "fmtstr.h"
|
|||
|
#include "netmessages_signon.h"
|
|||
|
// memdbgon must be the last include file in a .cpp file!!!
|
|||
|
#include "tier0/memdbgon.h"
|
|||
|
|
|||
|
//
|
|||
|
// CMatchSessionOfflineCustom
|
|||
|
//
|
|||
|
// Implementation of an offline session
|
|||
|
// that allows customization before the actual
|
|||
|
// game commences (like playing commentary mode
|
|||
|
// or playing single-player)
|
|||
|
//
|
|||
|
|
|||
|
CMatchSessionOfflineCustom::CMatchSessionOfflineCustom( KeyValues *pSettings ) :
|
|||
|
m_pSettings( pSettings->MakeCopy() ),
|
|||
|
m_autodelete_pSettings( m_pSettings ),
|
|||
|
m_eState( STATE_INIT ),
|
|||
|
m_bExpectingServerReload( false )
|
|||
|
{
|
|||
|
DevMsg( "Created CMatchSessionOfflineCustom:\n" );
|
|||
|
KeyValuesDumpAsDevMsg( m_pSettings, 1 );
|
|||
|
|
|||
|
InitializeGameSettings();
|
|||
|
}
|
|||
|
|
|||
|
CMatchSessionOfflineCustom::~CMatchSessionOfflineCustom()
|
|||
|
{
|
|||
|
DevMsg( "Destroying CMatchSessionOfflineCustom:\n" );
|
|||
|
KeyValuesDumpAsDevMsg( m_pSettings, 1 );
|
|||
|
}
|
|||
|
|
|||
|
KeyValues * CMatchSessionOfflineCustom::GetSessionSettings()
|
|||
|
{
|
|||
|
return m_pSettings;
|
|||
|
}
|
|||
|
|
|||
|
void CMatchSessionOfflineCustom::UpdateSessionSettings( KeyValues *pSettings )
|
|||
|
{
|
|||
|
// Extend the update keys
|
|||
|
g_pMMF->GetMatchTitleGameSettingsMgr()->ExtendGameSettingsUpdateKeys( m_pSettings, pSettings );
|
|||
|
m_pSettings->MergeFrom( pSettings );
|
|||
|
|
|||
|
// Broadcast the update to everybody interested
|
|||
|
MatchSession_BroadcastSessionSettingsUpdate( pSettings );
|
|||
|
}
|
|||
|
|
|||
|
void CMatchSessionOfflineCustom::UpdateTeamProperties( KeyValues *pTeamProperties )
|
|||
|
{
|
|||
|
}
|
|||
|
|
|||
|
void CMatchSessionOfflineCustom::Command( KeyValues *pCommand )
|
|||
|
{
|
|||
|
char const *szCommand = pCommand->GetName();
|
|||
|
|
|||
|
if ( !Q_stricmp( "Start", szCommand ) && m_eState < STATE_RUNNING )
|
|||
|
{
|
|||
|
m_eState = STATE_RUNNING;
|
|||
|
|
|||
|
OnGamePrepareLobbyForGame();
|
|||
|
|
|||
|
UpdateSessionSettings( KeyValues::AutoDeleteInline( KeyValues::FromString(
|
|||
|
"update",
|
|||
|
" update { "
|
|||
|
" server { "
|
|||
|
" server listen "
|
|||
|
" } "
|
|||
|
" } "
|
|||
|
) ) );
|
|||
|
|
|||
|
g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( new KeyValues(
|
|||
|
"OnProfilesWriteOpportunity", "reason", "sessionstart"
|
|||
|
) );
|
|||
|
|
|||
|
bool bResult = g_pMatchFramework->GetMatchTitle()->StartServerMap( m_pSettings );
|
|||
|
if ( !bResult )
|
|||
|
{
|
|||
|
Warning( "Failed to start server map!\n" );
|
|||
|
KeyValuesDumpAsDevMsg( m_pSettings, 1 );
|
|||
|
Assert( 0 );
|
|||
|
g_pMatchEventsSubscription->BroadcastEvent( new KeyValues( "OnMatchSessionUpdate", "state", "error", "error", "nomap" ) );
|
|||
|
}
|
|||
|
Msg( "Succeeded in starting server map!\n" );
|
|||
|
return;
|
|||
|
}
|
|||
|
if ( !Q_stricmp( "QueueConnect", szCommand ) )
|
|||
|
{
|
|||
|
char const *szConnectAddress = pCommand->GetString( "adronline", "0.0.0.0" );
|
|||
|
uint64 uiReservationId = pCommand->GetUint64( "reservationid" );
|
|||
|
bool bAutoCloseSession = pCommand->GetBool( "auto_close_session" );
|
|||
|
Assert( bAutoCloseSession );
|
|||
|
if ( bAutoCloseSession )
|
|||
|
{
|
|||
|
// Switch the state
|
|||
|
m_eState = STATE_RUNNING;
|
|||
|
|
|||
|
MatchSession_PrepareClientForConnect( m_pSettings, uiReservationId );
|
|||
|
|
|||
|
// Close the session, potentially resetting a bunch of state
|
|||
|
if ( bAutoCloseSession )
|
|||
|
g_pMatchFramework->CloseSession();
|
|||
|
|
|||
|
// Determine reservation settings required
|
|||
|
g_pMatchExtensions->GetINetSupport()->UpdateClientReservation( uiReservationId, 0ull );
|
|||
|
|
|||
|
// Issue the connect command
|
|||
|
g_pMatchExtensions->GetIVEngineClient()->StartLoadingScreenForCommand( CFmtStr( "connect %s", szConnectAddress ) );
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Let the title-specific matchmaking handle the command
|
|||
|
//
|
|||
|
CUtlVector< KeyValues * > arrPlayersUpdated;
|
|||
|
arrPlayersUpdated.SetCount( m_pSettings->GetInt( "members/numPlayers", 0 ) );
|
|||
|
memset( arrPlayersUpdated.Base(), 0, arrPlayersUpdated.Count() * sizeof( KeyValues * ) );
|
|||
|
|
|||
|
g_pMMF->GetMatchTitleGameSettingsMgr()->ExecuteCommand( pCommand, GetSessionSystemData(), m_pSettings, arrPlayersUpdated.Base() );
|
|||
|
|
|||
|
// Now notify the framework about player updated
|
|||
|
for ( int k = 0; k < arrPlayersUpdated.Count(); ++ k )
|
|||
|
{
|
|||
|
if ( !arrPlayersUpdated[k] )
|
|||
|
break;
|
|||
|
|
|||
|
// Notify the framework about player updated
|
|||
|
KeyValues *kvEvent = new KeyValues( "OnPlayerUpdated" );
|
|||
|
kvEvent->SetUint64( "xuid", arrPlayersUpdated[k]->GetUint64( "xuid" ) );
|
|||
|
g_pMatchEventsSubscription->BroadcastEvent( kvEvent );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Send the command as event for handling
|
|||
|
//
|
|||
|
KeyValues *pEvent = pCommand->MakeCopy();
|
|||
|
pEvent->SetName( CFmtStr( "Command::%s", pCommand->GetName() ) );
|
|||
|
g_pMatchEventsSubscription->BroadcastEvent( pEvent );
|
|||
|
}
|
|||
|
|
|||
|
uint64 CMatchSessionOfflineCustom::GetSessionID()
|
|||
|
{
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
void CMatchSessionOfflineCustom::Update()
|
|||
|
{
|
|||
|
switch ( m_eState )
|
|||
|
{
|
|||
|
case STATE_INIT:
|
|||
|
m_eState = STATE_CONFIG;
|
|||
|
|
|||
|
// Let everybody know that the session is now ready
|
|||
|
g_pMatchEventsSubscription->BroadcastEvent( new KeyValues( "OnMatchSessionUpdate", "state", "ready", "transition", "offlineinit" ) );
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void CMatchSessionOfflineCustom::Destroy()
|
|||
|
{
|
|||
|
if ( m_eState == STATE_RUNNING )
|
|||
|
{
|
|||
|
g_pMatchExtensions->GetIVEngineClient()->ExecuteClientCmd( "disconnect" );
|
|||
|
|
|||
|
g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( new KeyValues(
|
|||
|
"OnProfilesWriteOpportunity", "reason", "sessionend"
|
|||
|
) );
|
|||
|
}
|
|||
|
|
|||
|
delete this;
|
|||
|
}
|
|||
|
|
|||
|
void CMatchSessionOfflineCustom::DebugPrint()
|
|||
|
{
|
|||
|
DevMsg( "CMatchSessionOfflineCustom [ state=%d ]\n", m_eState );
|
|||
|
KeyValuesDumpAsDevMsg( m_pSettings, 1 );
|
|||
|
}
|
|||
|
|
|||
|
void CMatchSessionOfflineCustom::OnEvent( KeyValues *pEvent )
|
|||
|
{
|
|||
|
char const *szEvent = pEvent->GetName();
|
|||
|
|
|||
|
if ( !Q_stricmp( "OnEngineClientSignonStateChange", szEvent ) )
|
|||
|
{
|
|||
|
int iOldState = pEvent->GetInt( "old", 0 );
|
|||
|
int iNewState = pEvent->GetInt( "new", 0 );
|
|||
|
|
|||
|
if ( iOldState >= SIGNONSTATE_CONNECTED &&
|
|||
|
iNewState < SIGNONSTATE_CONNECTED )
|
|||
|
{
|
|||
|
// Disconnecting from server
|
|||
|
DevMsg( "OnEngineClientSignonStateChange\n" );
|
|||
|
if ( m_bExpectingServerReload )
|
|||
|
{
|
|||
|
m_bExpectingServerReload = false;
|
|||
|
DevMsg( " session was expecting server reload...\n" );
|
|||
|
return;
|
|||
|
}
|
|||
|
g_pMatchEventsSubscription->BroadcastEvent( new KeyValues( "mmF->CloseSession" ) );
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
else if ( !Q_stricmp( "OnEngineClientSignonStatePrepareChange", szEvent ) )
|
|||
|
{
|
|||
|
char const *szReason = pEvent->GetString( "reason" );
|
|||
|
if ( !Q_stricmp( "reload", szReason ) )
|
|||
|
{
|
|||
|
Assert( !m_bExpectingServerReload );
|
|||
|
m_bExpectingServerReload = true;
|
|||
|
return;
|
|||
|
}
|
|||
|
else if ( !Q_stricmp( "load", szReason ) )
|
|||
|
{
|
|||
|
char const *szLevelName = g_pMatchExtensions->GetIVEngineClient()->GetLevelName();
|
|||
|
if ( szLevelName && szLevelName[0] && g_pMatchExtensions->GetIVEngineClient()->IsConnected() )
|
|||
|
{
|
|||
|
Assert( !m_bExpectingServerReload );
|
|||
|
m_bExpectingServerReload = true;
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
else if ( !Q_stricmp( "OnEngineEndGame", szEvent ) )
|
|||
|
{
|
|||
|
DevMsg( "OnEngineEndGame\n" );
|
|||
|
|
|||
|
// Issue the disconnect command
|
|||
|
g_pMatchExtensions->GetIVEngineClient()->ExecuteClientCmd( "disconnect" );
|
|||
|
|
|||
|
g_pMatchEventsSubscription->BroadcastEvent( new KeyValues( "mmF->CloseSession" ) );
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void CMatchSessionOfflineCustom::InitializeGameSettings()
|
|||
|
{
|
|||
|
// Since the session can be created with a minimal amount of data available
|
|||
|
// the session object is responsible for initializing the missing data to defaults
|
|||
|
// or saved values or values from gamer progress/profile or etc...
|
|||
|
|
|||
|
if ( KeyValues *kv = m_pSettings->FindKey( "system", true ) )
|
|||
|
{
|
|||
|
kv->SetString( "network", "offline" );
|
|||
|
kv->SetString( "access", "public" );
|
|||
|
}
|
|||
|
|
|||
|
if ( KeyValues *kv = m_pSettings->FindKey( "options", true ) )
|
|||
|
{
|
|||
|
kv->SetString( "server", "listen" );
|
|||
|
}
|
|||
|
|
|||
|
if ( KeyValues *pMembers = m_pSettings->FindKey( "members", true ) )
|
|||
|
{
|
|||
|
pMembers->SetInt( "numMachines", 1 );
|
|||
|
|
|||
|
int numPlayers = 1;
|
|||
|
#ifdef _GAMECONSOLE
|
|||
|
numPlayers = XBX_GetNumGameUsers();
|
|||
|
#endif
|
|||
|
pMembers->SetInt( "numPlayers", numPlayers );
|
|||
|
pMembers->SetInt( "numSlots", numPlayers );
|
|||
|
|
|||
|
if ( KeyValues *pMachine = pMembers->FindKey( "machine0", true ) )
|
|||
|
{
|
|||
|
IPlayerLocal *pPriPlayer = g_pPlayerManager->GetLocalPlayer( XBX_GetPrimaryUserId() );
|
|||
|
|
|||
|
pMachine->SetUint64( "id", ( pPriPlayer ? pPriPlayer->GetXUID() : INVALID_XUID ) );
|
|||
|
pMachine->SetUint64( "flags", MatchSession_GetMachineFlags() );
|
|||
|
pMachine->SetInt( "numPlayers", numPlayers );
|
|||
|
pMachine->SetUint64( "dlcmask", g_pMatchFramework->GetMatchSystem()->GetDlcManager()->GetDataInfo()->GetUint64( "@info/installed" ) );
|
|||
|
pMachine->SetString( "tuver", MatchSession_GetTuInstalledString() );
|
|||
|
pMachine->SetInt( "ping", 0 );
|
|||
|
|
|||
|
for ( int k = 0; k < numPlayers; ++ k )
|
|||
|
{
|
|||
|
if ( KeyValues *pPlayer = pMachine->FindKey( CFmtStr( "player%d", k ), true ) )
|
|||
|
{
|
|||
|
int iController = 0;
|
|||
|
#ifdef _GAMECONSOLE
|
|||
|
iController = XBX_GetUserId( k );
|
|||
|
#endif
|
|||
|
IPlayerLocal *player = g_pPlayerManager->GetLocalPlayer( iController );
|
|||
|
if ( player )
|
|||
|
{
|
|||
|
pPlayer->SetUint64( "xuid", player->GetXUID() );
|
|||
|
pPlayer->SetString( "name", player->GetName() );
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Let the title extend the game settings
|
|||
|
g_pMMF->GetMatchTitleGameSettingsMgr()->InitializeGameSettings( m_pSettings, "host" );
|
|||
|
|
|||
|
DevMsg( "CMatchSessionOfflineCustom::InitializeGameSettings adjusted settings:\n" );
|
|||
|
KeyValuesDumpAsDevMsg( m_pSettings, 1 );
|
|||
|
}
|
|||
|
|
|||
|
void CMatchSessionOfflineCustom::OnGamePrepareLobbyForGame()
|
|||
|
{
|
|||
|
// Remember which players will get updated
|
|||
|
CUtlVector< KeyValues * > arrPlayersUpdated;
|
|||
|
arrPlayersUpdated.SetCount( m_pSettings->GetInt( "members/numPlayers", 0 ) );
|
|||
|
memset( arrPlayersUpdated.Base(), 0, arrPlayersUpdated.Count() * sizeof( KeyValues * ) );
|
|||
|
|
|||
|
g_pMMF->GetMatchTitleGameSettingsMgr()->PrepareLobbyForGame( m_pSettings, arrPlayersUpdated.Base() );
|
|||
|
|
|||
|
// Notify the framework of the updates
|
|||
|
for ( int k = 0; k < arrPlayersUpdated.Count(); ++ k )
|
|||
|
{
|
|||
|
if ( !arrPlayersUpdated[k] )
|
|||
|
break;
|
|||
|
|
|||
|
// Notify the framework about player updated
|
|||
|
KeyValues *kvEvent = new KeyValues( "OnPlayerUpdated" );
|
|||
|
kvEvent->SetUint64( "xuid", arrPlayersUpdated[k]->GetUint64( "xuid" ) );
|
|||
|
g_pMatchEventsSubscription->BroadcastEvent( kvEvent );
|
|||
|
}
|
|||
|
|
|||
|
// Let the title prepare for connect
|
|||
|
MatchSession_PrepareClientForConnect( m_pSettings );
|
|||
|
}
|