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.
672 lines
23 KiB
672 lines
23 KiB
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose:
|
|
//
|
|
//=============================================================================//
|
|
|
|
#include "cbase.h"
|
|
#include "cdll_int.h"
|
|
#include "gameinterface.h"
|
|
#include "mapentities.h"
|
|
#include "cs_gameinterface.h"
|
|
#include "ai_responsesystem.h"
|
|
#include "iachievementmgr.h"
|
|
#include "fmtstr.h"
|
|
#include "gametypes.h"
|
|
#include "matchmaking/imatchframework.h"
|
|
#include "cs_shareddefs.h"
|
|
#include "cs_gamerules.h"
|
|
#include "gametypes.h"
|
|
#include "engine/inetsupport.h"
|
|
#include "dedicated_server_ugc_manager.h"
|
|
#include "cs_player.h"
|
|
#include "server_log_http_dispatcher.h"
|
|
|
|
#include "netmessages.h"
|
|
#include "usermessages.h"
|
|
|
|
// NOTE: This has to be the last file included!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Convars
|
|
//
|
|
ConVar sv_workshop_allow_other_maps( "sv_workshop_allow_other_maps", "1", FCVAR_RELEASE, "When hosting a workshop collection, users can play other workshop map on this server when it is empty and then mapcycle into this server collection." );
|
|
static ConVar tv_allow_camera_man_steamid( "tv_allow_camera_man_steamid", "", FCVAR_RELEASE, "Allows tournament production cameraman to run csgo.exe -interactivecaster on SteamID 7650123456XXX and be the camera man." );
|
|
|
|
// #define SVGC_RESERVATION_DEBUG 1
|
|
|
|
// -------------------------------------------------------------------------------------------- //
|
|
// Mod-specific CServerGameClients implementation.
|
|
// -------------------------------------------------------------------------------------------- //
|
|
|
|
void CServerGameClients::GetPlayerLimits( int& minplayers, int& maxplayers, int &defaultMaxPlayers ) const
|
|
{
|
|
minplayers = 1; // allow single player for the test maps (but we default to multi)
|
|
maxplayers = MAX_PLAYERS;
|
|
|
|
defaultMaxPlayers = MAX_PLAYERS;
|
|
}
|
|
|
|
|
|
// -------------------------------------------------------------------------------------------- //
|
|
// Mod-specific CServerGameDLL implementation.
|
|
// -------------------------------------------------------------------------------------------- //
|
|
|
|
void CServerGameDLL::LevelInit_ParseAllEntities( const char *pMapEntities )
|
|
{
|
|
if ( Q_strcmp( STRING(gpGlobals->mapname), "cs_" ) )
|
|
{
|
|
// don't precache AI responses (hostages) if it's not a hostage rescure map
|
|
extern IResponseSystem *g_pResponseSystem;
|
|
g_pResponseSystem->PrecacheResponses( false );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Twitch.tv reservation updates
|
|
//
|
|
class ClientJob_EMsgGCCStrike15_v2_GC2ServerReservationUpdate : public GCSDK::CGCClientJob
|
|
{
|
|
public:
|
|
ClientJob_EMsgGCCStrike15_v2_GC2ServerReservationUpdate( GCSDK::CGCClient *pGCClient )
|
|
: GCSDK::CGCClientJob( pGCClient )
|
|
{
|
|
}
|
|
virtual bool BYieldingRunJobFromMsg( GCSDK::IMsgNetPacket *pNetPacket )
|
|
{
|
|
GCSDK::CProtoBufMsg<CMsgGCCStrike15_v2_GC2ServerReservationUpdate> msg( pNetPacket );
|
|
|
|
uint32 numTotalViewers = msg.Body().viewers_external_total();
|
|
uint32 numSteamLinkedViewers = msg.Body().viewers_external_steam();
|
|
|
|
engine->UpdateHltvExternalViewers( numTotalViewers, numSteamLinkedViewers );
|
|
|
|
return true;
|
|
}
|
|
};
|
|
GC_REG_CLIENT_JOB( ClientJob_EMsgGCCStrike15_v2_GC2ServerReservationUpdate, k_EMsgGCCStrike15_v2_GC2ServerReservationUpdate );
|
|
|
|
void GCCStrikeWelcomeMessageReceived( CMsgCStrike15Welcome const &msgCStrike )
|
|
{
|
|
}
|
|
|
|
bool Helper_FillServerReservationStateAndPlayers( CMsgGCCStrike15_v2_MatchmakingServerReservationResponse &msgbody )
|
|
{
|
|
if ( !engine->IsDedicatedServer() )
|
|
return false;
|
|
|
|
msgbody.set_server_version( ( ( INetSupport * ) g_pMatchFramework->GetMatchExtensions()->GetRegisteredExtensionInterface( INETSUPPORT_VERSION_STRING ) )->GetEngineBuildNumber() );
|
|
msgbody.set_map( STRING( gpGlobals->mapname ) );
|
|
static ConVarRef sv_steamdatagramtransport_port( "sv_steamdatagramtransport_port" );
|
|
|
|
// Expose information about our community server GOTV port so that clients could connect
|
|
static ConVarRef tv_advertise_watchable( "tv_advertise_watchable" );
|
|
static int s_nTvPort = 0; // make the TV port sticky: if we reported it non-zero once then keep reporting
|
|
CEngineHltvInfo_t engineHltvInfo;
|
|
if ( tv_advertise_watchable.GetBool() &&
|
|
engine->GetEngineHltvInfo( engineHltvInfo ) && engineHltvInfo.m_bBroadcastActive )
|
|
{
|
|
s_nTvPort = engineHltvInfo.m_nTvPort;
|
|
}
|
|
if ( s_nTvPort )
|
|
{
|
|
msgbody.mutable_tv_info()->set_tv_udp_port( s_nTvPort );
|
|
}
|
|
|
|
// Build the list of players who are actively playing on the game server
|
|
for ( int i = 1; i <= gpGlobals->maxClients; i++ )
|
|
{
|
|
CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
|
|
if ( pPlayer )
|
|
{
|
|
if ( pPlayer->IsBot() )
|
|
continue;
|
|
CSteamID steamIdPlayer;
|
|
if ( !pPlayer->GetSteamID( &steamIdPlayer ) )
|
|
continue;
|
|
if ( !steamIdPlayer.IsValid() )
|
|
continue;
|
|
switch ( pPlayer->GetTeamNumber() )
|
|
{
|
|
case TEAM_CT:
|
|
case TEAM_TERRORIST:
|
|
msgbody.add_reward_player_accounts( steamIdPlayer.GetAccountID() );
|
|
break;
|
|
default:
|
|
msgbody.add_idle_player_accounts( steamIdPlayer.GetAccountID() );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void CServerGameDLL::UpdateGCInformation()
|
|
{
|
|
/** Removed for partner depot **/
|
|
}
|
|
|
|
// Marks the queue matchmaking game as starting
|
|
void CServerGameDLL::ReportGCQueuedMatchStart( int32 iReservationStage, uint32 *puiConfirmedAccounts, int numConfirmedAccounts )
|
|
{
|
|
/** Removed for partner depot **/
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: A user has had their network id setup and validated
|
|
//-----------------------------------------------------------------------------
|
|
void CServerGameClients::NetworkIDValidated( const char *pszUserName, const char *pszNetworkID, CSteamID steamID )
|
|
{
|
|
/** Removed for partner depot **/
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Order workshop maps by MRU
|
|
//
|
|
static int Helper_SortWorkshopMapsMRU( const DedicatedServerUGCFileInfo_t * const *a, const DedicatedServerUGCFileInfo_t * const *b )
|
|
{
|
|
if ( (*a)->m_dblPlatFloatTimeReceived != (*b)->m_dblPlatFloatTimeReceived )
|
|
return ( (*a)->m_dblPlatFloatTimeReceived > (*b)->m_dblPlatFloatTimeReceived ) ? -1 : 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Matchmaking game data buffer to set into SteamGameServer()->SetGameData
|
|
//
|
|
void CServerGameDLL::GetMatchmakingGameData( char *buf, size_t bufSize )
|
|
{
|
|
char * const bufBase = buf;
|
|
int len = 0;
|
|
|
|
extern ConVar game_type;
|
|
extern ConVar game_mode;
|
|
|
|
// Put the game key
|
|
Q_snprintf( buf, bufSize, "g:csgo,gt:%u,gm:%u,", game_type.GetInt(), game_mode.GetInt() );
|
|
len = strlen( buf );
|
|
buf += len;
|
|
bufSize -= len;
|
|
|
|
|
|
if ( gpGlobals && !StringIsEmpty( gpGlobals->mapGroupName.ToCStr() ) )
|
|
{
|
|
const CUtlStringList* mapsInGroup = g_pGameTypes->GetMapGroupMapList( gpGlobals->mapGroupName.ToCStr() );
|
|
if ( mapsInGroup && g_pGameTypes->IsWorkshopMapGroup( gpGlobals->mapGroupName.ToCStr() ) )
|
|
{
|
|
if ( sv_workshop_allow_other_maps.GetBool() && ( bufSize >= 7 ) )
|
|
{ // Advertise support for other maps
|
|
Q_strncpy( buf, "wks:1,", 7 );
|
|
buf += 6;
|
|
bufSize -= 7;
|
|
}
|
|
|
|
CUtlVector< PublishedFileId_t > arrAdvertisedFileIds;
|
|
FOR_EACH_VEC( *mapsInGroup, i )
|
|
{
|
|
PublishedFileId_t id = DedicatedServerWorkshop().GetUGCMapPublishedFileID((*mapsInGroup)[i]);
|
|
CFmtStr szIdAsHexString( "%llx", id );
|
|
size_t len = szIdAsHexString.Length();
|
|
|
|
if ( bufSize <= len + 1 )
|
|
{
|
|
Warning( "GameData: Too many community maps installed, not advertising for map id \"%llu (0x%s)\"\n", id, szIdAsHexString.Access() );
|
|
continue;
|
|
}
|
|
|
|
Q_strncpy( buf, szIdAsHexString.Access(), len + 1 );
|
|
buf += len;
|
|
*( buf ++ ) = ',';
|
|
bufSize -= len + 1;
|
|
|
|
arrAdvertisedFileIds.AddToTail( id );
|
|
}
|
|
|
|
// Advertise maps that have been recently checked and downloaded from Workshop
|
|
if ( sv_workshop_allow_other_maps.GetBool() )
|
|
{
|
|
CUtlVector<const DedicatedServerUGCFileInfo_t *> arrInfoMaps;
|
|
DedicatedServerWorkshop().GetWorkshopMasWithValidUgcInformation( arrInfoMaps );
|
|
arrInfoMaps.Sort( Helper_SortWorkshopMapsMRU );
|
|
FOR_EACH_VEC( arrInfoMaps, iInfoMap )
|
|
{
|
|
PublishedFileId_t id = arrInfoMaps[iInfoMap]->fileId;
|
|
if ( arrAdvertisedFileIds.Find( id ) != arrAdvertisedFileIds.InvalidIndex() )
|
|
continue; // already advertised
|
|
|
|
CFmtStr szIdAsHexString( "%llx", id );
|
|
size_t len = szIdAsHexString.Length();
|
|
|
|
if ( bufSize <= len + 1 )
|
|
break; // Advertise only as much downloaded stuff as can fit
|
|
|
|
Q_strncpy( buf, szIdAsHexString.Access(), len + 1 );
|
|
buf += len;
|
|
*( buf ++ ) = ',';
|
|
bufSize -= len + 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Trim the last comma if anything was written
|
|
if ( buf > bufBase )
|
|
buf[ -1 ] = 0;
|
|
}
|
|
|
|
// this returns true if they were already in the list or were successfully added
|
|
// returns false if they were not added (not allowed to be a caster)
|
|
bool AddAccountToActiveCasters( const CSteamID &steamID )
|
|
{
|
|
// first check if they are already in the list
|
|
bool bAlreadyAdded = false;
|
|
for ( int j = 0; j < CSGameRules()->m_arrTournamentActiveCasterAccounts.Count(); j++ )
|
|
{
|
|
if ( steamID.GetAccountID() == CSGameRules()->m_arrTournamentActiveCasterAccounts[ j ] )
|
|
{
|
|
// this caster is already in the list so skip adding them, but allow them
|
|
bAlreadyAdded = true;
|
|
break;
|
|
}
|
|
if ( CSGameRules()->m_arrTournamentActiveCasterAccounts[ j ] )
|
|
{
|
|
// already have an active caster, so don't allow another
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if ( !bAlreadyAdded )
|
|
{
|
|
// not already added, so find an empty slot and put them in it
|
|
for (int j = 0; j < CSGameRules()->m_arrTournamentActiveCasterAccounts.Count(); j++ )
|
|
{
|
|
if ( CSGameRules()->m_arrTournamentActiveCasterAccounts[ j ] == 0 )
|
|
{
|
|
CSGameRules()->m_arrTournamentActiveCasterAccounts.Set( j, steamID.GetAccountID() );
|
|
if ( steamapicontext->SteamUser() && steamapicontext->SteamFriends() )
|
|
{
|
|
const char *pszName = steamapicontext->SteamFriends()->GetFriendPersonaName( steamID );
|
|
ConMsg( "Adding %s (ID:%d) to active caster list!\n", pszName, steamID.GetAccountID() );
|
|
}
|
|
else
|
|
{
|
|
ConMsg( "Adding ID:%d to active caster list!\n", steamID.GetAccountID() );
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// validate if player is a caster and is not playing in the current game, then add them to the active caster list
|
|
// returns false if they are not allow to be a caster
|
|
bool CServerGameDLL::ValidateAndAddActiveCaster( const CSteamID &steamID )
|
|
{
|
|
// check if they are a player in the current game. Note: players can be casters sometimes (and might be in the casters list below), but we don't want their voice data "public" when they are playing
|
|
for ( int i = 0; i < CCSGameRules::sm_QueuedServerReservation.account_ids().size(); i++ )
|
|
{
|
|
if ( steamID.GetAccountID() == CCSGameRules::sm_QueuedServerReservation.account_ids( i ) )
|
|
{
|
|
// this is a player
|
|
return false;
|
|
}
|
|
}
|
|
// they weren't in the player list, so now check the caster list
|
|
for ( int i = 0; i < CCSGameRules::sm_QueuedServerReservation.tournament_casters_account_ids().size(); i++ )
|
|
{
|
|
if ( steamID.GetAccountID() == CCSGameRules::sm_QueuedServerReservation.tournament_casters_account_ids( i ) )
|
|
{
|
|
// this is a caster
|
|
return AddAccountToActiveCasters( steamID );
|
|
}
|
|
}
|
|
if ( tv_allow_camera_man_steamid.GetString()[0] && engine->IsDedicatedServer() )
|
|
{
|
|
CSteamID steamidCameraMan( V_atoui64( tv_allow_camera_man_steamid.GetString() ) );
|
|
if ( steamidCameraMan.IsValid() && steamidCameraMan.BIndividualAccount() && steamidCameraMan.GetAccountID() &&
|
|
( steamidCameraMan.GetAccountID() == steamID.GetAccountID() ) )
|
|
{
|
|
return AddAccountToActiveCasters( steamID );
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Returns which encryption key to use for messages to be encrypted for TV
|
|
EncryptedMessageKeyType_t CServerGameDLL::GetMessageEncryptionKey( INetMessage *pMessage )
|
|
{
|
|
switch ( pMessage->GetType() )
|
|
{
|
|
case svc_VoiceData:
|
|
{
|
|
// check the voice data packets for being from an active caster and add the caster flag and use the public key
|
|
CSVCMsg_VoiceData_t *pVoiceData = ( CSVCMsg_VoiceData_t * ) pMessage;
|
|
CSteamID steamID( static_cast<uint64>( pVoiceData->xuid() ) );
|
|
if ( steamID.GetAccountID() )
|
|
{
|
|
for ( int j = 0; j < CSGameRules()->m_arrTournamentActiveCasterAccounts.Count(); j++ )
|
|
{
|
|
if ( steamID.GetAccountID() == CSGameRules()->m_arrTournamentActiveCasterAccounts[ j ] )
|
|
{
|
|
pVoiceData->set_caster( true );
|
|
return kEncryptedMessageKeyType_Public;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return kEncryptedMessageKeyType_Private;
|
|
|
|
case svc_UserMessage:
|
|
{
|
|
CSVCMsg_UserMessage_t *pUsrMessageHeader = ( CSVCMsg_UserMessage_t * ) pMessage;
|
|
switch ( pUsrMessageHeader->msg_type() )
|
|
{
|
|
case CS_UM_SayText:
|
|
{
|
|
CCSUsrMsg_SayText usrMsg;
|
|
if ( usrMsg.ParseFromArray( &pUsrMessageHeader->msg_data().at( 0 ), pUsrMessageHeader->msg_data().size() ) )
|
|
{
|
|
if ( usrMsg.textallchat() )
|
|
return kEncryptedMessageKeyType_Public;
|
|
}
|
|
}
|
|
return kEncryptedMessageKeyType_Private;
|
|
|
|
case CS_UM_SayText2:
|
|
{
|
|
CCSUsrMsg_SayText2 usrMsg;
|
|
if ( usrMsg.ParseFromArray( &pUsrMessageHeader->msg_data().at( 0 ), pUsrMessageHeader->msg_data().size() ) )
|
|
{
|
|
if ( usrMsg.textallchat() )
|
|
return kEncryptedMessageKeyType_Public;
|
|
}
|
|
}
|
|
return kEncryptedMessageKeyType_Private;
|
|
|
|
case CS_UM_TextMsg:
|
|
case CS_UM_RadioText:
|
|
case CS_UM_RawAudio:
|
|
case CS_UM_SendAudio:
|
|
return kEncryptedMessageKeyType_Private;
|
|
|
|
default:
|
|
return kEncryptedMessageKeyType_None;
|
|
}
|
|
}
|
|
return kEncryptedMessageKeyType_None;
|
|
|
|
case svc_EncryptedData:
|
|
default:
|
|
return kEncryptedMessageKeyType_None;
|
|
}
|
|
}
|
|
|
|
// If server game dll needs more time before server process quits then
|
|
// it should return true to hold game server reservation from this interface method.
|
|
// If this method returns false then the server process will clear the reservation
|
|
// and might shutdown to meet uptime or memory limit requirements.
|
|
bool CServerGameDLL::ShouldHoldGameServerReservation( float flTimeElapsedWithoutClients )
|
|
{
|
|
/** Removed for partner depot **/
|
|
return false; // let the server get unreserved
|
|
}
|
|
|
|
// Pure server validation failed for the given client, client supplied
|
|
// data is included in the payload
|
|
void CServerGameDLL::OnPureServerFileValidationFailure( edict_t *edictClient, const char *path, const char *fileName, uint32 crc, int32 hashType, int32 len, int packNumber, int packFileID )
|
|
{
|
|
/** Removed for partner depot **/
|
|
}
|
|
|
|
// Last chance validation on connect packet for the client, non-NULL return value
|
|
// causes the client connect to be aborted with the provided error
|
|
char const * CServerGameDLL::ClientConnectionValidatePreNetChan( bool bGameServer, char const *adr, int nAuthProtocol, uint64 ullSteamID )
|
|
{
|
|
/** Removed for partner depot **/
|
|
return NULL; // allow connections by default
|
|
}
|
|
|
|
// Network channel notification from engine to game server code
|
|
void CServerGameDLL::OnEngineClientNetworkEvent( edict_t *edictClient, uint64 ullSteamID, int nEventType, void *pvParam )
|
|
{
|
|
/** Removed for partner depot **/
|
|
}
|
|
|
|
// Game server notifying GC with its sync packet
|
|
void CServerGameDLL::EngineGotvSyncPacket( const CEngineGotvSyncPacket *pPkt )
|
|
{
|
|
/** Removed for partner depot **/
|
|
}
|
|
|
|
// GOTV client attempt redirect over SDR
|
|
bool CServerGameDLL::OnEngineClientProxiedRedirect( uint64 ullClient, const char *adrProxiedRedirect, const char *adrRegular )
|
|
{
|
|
/** Removed for partner depot **/
|
|
return false;
|
|
}
|
|
|
|
bool CServerGameDLL::LogForHTTPListeners( const char* szLogLine )
|
|
{
|
|
return GetServerLogHTTPDispatcher()->LogForHTTPListeners( szLogLine );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Called to apply lobby settings to a dedicated server
|
|
//-----------------------------------------------------------------------------
|
|
void CServerGameDLL::ApplyGameSettings( KeyValues *pKV )
|
|
{
|
|
if ( !pKV )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( engine )
|
|
{
|
|
DevMsg( "CServerGameDLL::ApplyGameSettings game settings payload received:\n" );
|
|
KeyValuesDumpAsDevMsg( pKV, 1 );
|
|
|
|
const char* pMapName = NULL;
|
|
const char* pMapNameFromKV = pKV->GetString( "game/map" );
|
|
const char* pGameType = pKV->GetString( "game/type" );
|
|
const char* pGameMode = pKV->GetString( "game/mode" );
|
|
const char* pMapGroupName = pKV->GetString( "game/mapgroupname", NULL );
|
|
const char* pMapGroupNameToValidate = NULL; // pMapGroupName is ok to be NULL; this variable lets us easily use a non null pMapGroupName or gpGlobals->mapGroupName
|
|
|
|
if ( !IsValveDS() &&
|
|
pMapNameFromKV && StringHasPrefix( pMapNameFromKV, "workshop" ) &&
|
|
pMapGroupName && Q_stristr( pMapGroupName, "@workshop" ) )
|
|
{
|
|
// A community server is getting reserved by a client for a workshop map,
|
|
// retain our current workshop collection if we are hosting one to preserve
|
|
// map rotation process
|
|
pMapGroupName = engine->IsDedicatedServer() ? STRING( gpGlobals->mapGroupName ) : pMapGroupName;
|
|
}
|
|
|
|
if ( pMapGroupName && (pMapGroupName[0] != '\0') && !pMapNameFromKV )
|
|
{
|
|
// if we have a mapgroup name, then we don't care about any map name from the pKV and we just want the first map from the mapgroup
|
|
pMapName = g_pGameTypes->GetRandomMap( pMapGroupName );
|
|
pMapGroupNameToValidate = pMapGroupName;
|
|
}
|
|
else
|
|
{
|
|
pMapGroupNameToValidate = ( pMapGroupName && (pMapGroupName[0] != '\0') ) ? pMapGroupName : STRING( gpGlobals->mapGroupName );
|
|
}
|
|
|
|
// make sure we are not using a bogus mapgroup name
|
|
if ( pMapGroupNameToValidate && !StringIsEmpty( pMapGroupNameToValidate ) && !g_pGameTypes->IsValidMapGroupName( pMapGroupNameToValidate ) )
|
|
{
|
|
Warning( "ApplyGameSettings: Invalid mapgroup name %s\n", pMapGroupNameToValidate );
|
|
return;
|
|
}
|
|
|
|
// only use the map name from the pKV if there was no mapgroup name in the pKV
|
|
if ( !pMapName )
|
|
{
|
|
pMapName = pMapNameFromKV;
|
|
}
|
|
|
|
// For team games we add the prefix "team" to the game type. This is to
|
|
// eliminate team game lobbies from searches for QuickMatch and Custom Match
|
|
char *teamStr = "team";
|
|
const char *pTeamPrefix = Q_strstr( pGameType, teamStr);
|
|
if ( pTeamPrefix == pGameType )
|
|
{
|
|
pGameType += Q_strlen( teamStr );
|
|
}
|
|
|
|
if ( pMapName && pMapName[0] != '\0' )
|
|
{
|
|
// validate map exists in the mapgroup
|
|
if ( !g_pGameTypes->IsValidMapInMapGroup( pMapGroupNameToValidate, pMapName ) )
|
|
{
|
|
Warning( "ApplyGameSettings: Map %s not part of Mapgroup %s\n", pMapName, pMapGroupNameToValidate );
|
|
}
|
|
|
|
int extraSpectators = 2;
|
|
|
|
if ( ( pGameType && pGameType[0] != '\0' ) &&
|
|
( pGameMode && pGameMode[0] != '\0' ) )
|
|
{
|
|
// make sure the mapgroup is in this game type & mode
|
|
if ( !g_pGameTypes->IsValidMapGroupForTypeAndMode( pMapGroupNameToValidate, pGameType, pGameMode ) )
|
|
{
|
|
Warning( "ApplyGameSettings: MapGroup %s not part of type %s mode %s\n", pMapGroupNameToValidate, pGameType, pGameMode );
|
|
}
|
|
|
|
// Get the bot difficulty setting before it gets reverted.
|
|
ConVarRef cvCustomBotDiff( "custom_bot_difficulty" );
|
|
int customBotDiff = cvCustomBotDiff.GetInt();
|
|
|
|
/*
|
|
// FIXME[pmf]: We don't want to reset all replicated convars unless we also re-exec game.cfg on the server,
|
|
// otherwise we'll overwrite all the game configuration convars specified in game.cfg
|
|
|
|
// Reset server enforced convars
|
|
g_pCVar->RevertFlaggedConVars( FCVAR_REPLICATED );
|
|
*/
|
|
|
|
// Cheats were disabled; revert all cheat cvars to their default values.
|
|
// This must be done heading into multiplayer games because people can play
|
|
// demos etc and set cheat cvars with sv_cheats 0.
|
|
g_pCVar->RevertFlaggedConVars( FCVAR_CHEAT );
|
|
|
|
// we know that the loading screen data is correct, so let the loading screen know
|
|
//g_pGameTypes->SetLoadingScreenDataIsCorrect( true );
|
|
g_pGameTypes->SetRunMapWithDefaultGametype( false );
|
|
// Set game_type and game_mode convars.
|
|
g_pGameTypes->SetGameTypeAndMode( pGameType, pGameMode );
|
|
|
|
extern ConVar game_online;
|
|
if ( char const *szOnline = pKV->GetString( "system/network", NULL ) )
|
|
{
|
|
game_online.SetValue( ( !V_stricmp( szOnline, "LIVE" ) ) ? 1 : 0 );
|
|
}
|
|
|
|
extern ConVar game_public;
|
|
if ( char const *szAccess = pKV->GetString( "system/access", NULL ) )
|
|
{
|
|
game_public.SetValue( ( !V_stricmp( szAccess, "public" ) ) ? 1 : 0 );
|
|
}
|
|
|
|
#ifndef CLIENT_DLL
|
|
if ( engine->IsDedicatedServer() )
|
|
game_online.SetValue( 1 );
|
|
#endif
|
|
// Special case: set the custom bot difficulty for offline games
|
|
if ( !game_online.GetBool() )
|
|
{
|
|
g_pGameTypes->SetCustomBotDifficulty( customBotDiff );
|
|
}
|
|
|
|
// Make sure that correct number of slots is set for the engine
|
|
{
|
|
int iType, iMode;
|
|
if ( g_pGameTypes->GetGameModeAndTypeIntsFromStrings( pGameType, pGameMode, iType, iMode ) )
|
|
{
|
|
int iMaxPlayersForTypeMode = g_pGameTypes->GetMaxPlayersForTypeAndMode( iType, iMode );
|
|
pKV->SetInt( "members/numSlots", iMaxPlayersForTypeMode );
|
|
}
|
|
}
|
|
|
|
// Make sure the settings keys have extra spectator info
|
|
pKV->SetInt( "members/numExtraSpectatorSlots", extraSpectators );
|
|
}
|
|
|
|
CFmtStr command;
|
|
|
|
if ( pMapGroupName )
|
|
{
|
|
command.AppendFormat( "mapgroup %s\n", pMapGroupName );
|
|
}
|
|
command.AppendFormat( "nextlevel %s\n", pMapName ); // gamerules will clean it up when they construct for the next map
|
|
command.AppendFormat( "map %s reserved\n", pMapName );
|
|
|
|
Warning( "Executing server command:\n%s\n---\n", command.Access() );
|
|
engine->ServerCommand( command );
|
|
if ( engine->IsDedicatedServer() )
|
|
engine->ServerExecute();
|
|
}
|
|
}
|
|
}
|
|
|
|
const char * CServerGameClients::ClientNameHandler( uint64 xuid, const char *pchName )
|
|
{
|
|
CSteamID steamID( xuid );
|
|
|
|
// In tournament mode force names for the players according to the reservation
|
|
if ( steamID.IsValid() && steamID.BIndividualAccount() &&
|
|
CCSGameRules::sm_QueuedServerReservation.has_tournament_event() )
|
|
{
|
|
for ( int32 iTeam = 0; iTeam < CCSGameRules::sm_QueuedServerReservation.tournament_teams().size(); ++iTeam )
|
|
{
|
|
TournamentTeam const &ttTeam = CCSGameRules::sm_QueuedServerReservation.tournament_teams( iTeam );
|
|
for ( int32 iTeamPlayer = 0; iTeamPlayer < ttTeam.players().size(); ++iTeamPlayer )
|
|
{
|
|
TournamentPlayer const &ttPlayer = ttTeam.players( iTeamPlayer );
|
|
if ( ttPlayer.account_id() && ( ttPlayer.account_id() == steamID.GetAccountID() ) )
|
|
{
|
|
return ( ttPlayer.player_nick().c_str() );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Throttle name changes from clients (hacked clients can set the name convar at any rate)
|
|
extern CCSPlayer *ToCSPlayer( CBaseEntity *pEntity );
|
|
if ( CCSPlayer* pPlayer = ToCSPlayer( CBasePlayer::GetPlayerBySteamID( steamID ) ) )
|
|
{
|
|
if ( !pPlayer->CanChangeName() )
|
|
return pPlayer->GetPlayerName();
|
|
}
|
|
|
|
// Account not resolved, use whatever name they provided
|
|
return pchName;
|
|
}
|
|
|
|
void CServerGameClients::ClientSvcUserMessage( edict_t *pEntity, int nType, int nPassthrough, uint32 cbSize, const void *pvBuffer )
|
|
{
|
|
CCSPlayer *pPlayer = ToCSPlayer( GetContainingEntity( pEntity ) );
|
|
if ( !pPlayer )
|
|
return;
|
|
|
|
switch ( nType )
|
|
{
|
|
case CS_UM_PlayerDecalDigitalSignature:
|
|
{
|
|
CCSUsrMsg_PlayerDecalDigitalSignature msg;
|
|
if ( msg.ParseFromArray( pvBuffer, cbSize ) )
|
|
pPlayer->SprayPaint( msg );
|
|
}
|
|
return;
|
|
}
|
|
}
|