Mounting multiple games
This code extends the Orange Box's AdditionalContentId system to support mounting three or more SteamApps. It can be used with the Ep1 codebase too.


filesystem->MountSteamContent( int -Id )
. Yes, you need to negate the number.Client (cdll_client_init.cpp)
Search for the MountAdditionalContent()
function. If you don't have it, just add it right above CHLClient::Init.
This is the code:
static void MountAdditionalContent()
{
KeyValues *pMainFile, *pExtraContent, *pContentKey;
bool bKVHack = true;
int nExtraContentId;
pMainFile = new KeyValues( "gameinfo.txt" );
if ( pMainFile->LoadFromFile( filesystem, VarArgs("%s/gameinfo.txt", engine->GetGameDirectory()), "MOD" ) )
{
pExtraContent = pMainFile->FindKey( "FileSystem" );
if ( pExtraContent )
{
pExtraContent = pExtraContent->FindKey( "AdditionalContentId" );
if ( pExtraContent )
{
pContentKey = pExtraContent->GetFirstSubKey();
if ( pContentKey )
{
do {
//HACK: key and value cannot be accesed uniformly
if( bKVHack )
nExtraContentId = V_atoi( pContentKey->GetName() );
else
nExtraContentId = pContentKey->GetInt();
if ( nExtraContentId == 0 )
{
if( !bKVHack && !pContentKey->GetNextKey() )
{
//the KeyValues will have been complaining about a missing key
Msg( "(Client) Dear user, this code has detected, that you've received one KeyValues Error in the console. Do not worry about it.\n" );
break;
}
Warning( "(Client) Extra content appId \'%s\' is no integer\n", bKVHack ? pContentKey->GetName() : pContentKey->GetString() );
}
else if ( nExtraContentId == INT_MAX || nExtraContentId == INT_MIN )
Warning( "(Client) Extra content appId \'%s\' is out of range\n", bKVHack ? pContentKey->GetName() : pContentKey->GetString() );
else if ( filesystem->MountSteamContent( -abs( nExtraContentId ) ) != FILESYSTEM_MOUNT_OK )
Warning( "(Client) Unable to mount extra content with appId: %i\n", nExtraContentId );
else
DevMsg( "(Client) Successfully mounted extra content with appId: %i\n", nExtraContentId );
//HACK
if( bKVHack ) {
bKVHack = false;
continue;
}
else
bKVHack = true;
pContentKey = pContentKey->GetNextKey();
} while( pContentKey );
}
else
{
nExtraContentId = pExtraContent->GetInt();
if ( !nExtraContentId )
Warning( "(Client) AdditionalContentId specified with invalid appId: %s\n", pExtraContent->GetString() );
else if ( filesystem->MountSteamContent( -abs( nExtraContentId ) ) != FILESYSTEM_MOUNT_OK )
Warning( "(Client) Unable to mount extra content with appId: %i\n", nExtraContentId );
else
DevMsg( "(Client) Successfully mounted extra content with appId: %i\n", nExtraContentId );
}
}
}
}
pMainFile->deleteThis();
}
If you did not have this function before, then call this function in CHLClient::Init right before
if ( CommandLine()->FindParm( "-textmode" ) )
g_bTextMode = true;
Server (gameinterface.cpp)
Again, search for the function "static void MountAdditionalContent()" and if you don't have it, just add it right above CServerGameDLL::DLLInit.
This is the code:
static void MountAdditionalContent()
{
KeyValues *pMainFile, *pExtraContent, *pContentKey;
bool bFileFound, bKVHack = true;
int nExtraContentId;
char gamePath[256];
engine->GetGameDir( gamePath, 256 );
Q_StripTrailingSlash( gamePath );
pMainFile = new KeyValues( "gameinfo.txt" );
//On linux because of case sensitiviy we need to check for both.
#ifdef _LINUX
bFileFound = pMainFile->LoadFromFile( filesystem, UTIL_VarArgs("%s/GameInfo.txt", gamePath), "MOD" );
if ( !bFileFound )
#endif
bFileFound = pMainFile->LoadFromFile( filesystem, UTIL_VarArgs("%s/gameinfo.txt", gamePath), "MOD" );
if ( bFileFound )
{
pExtraContent = pMainFile->FindKey( "FileSystem" );
if ( pExtraContent )
{
pExtraContent = pExtraContent->FindKey( "AdditionalContentId" );
if ( pExtraContent )
{
pContentKey = pExtraContent->GetFirstSubKey();
if ( pContentKey )
{
do {
//HACK: key and value cannot be accesed uniformly
if( bKVHack )
nExtraContentId = V_atoi( pContentKey->GetName() );
else
nExtraContentId = pContentKey->GetInt();
if ( nExtraContentId == 0 )
{
if( !bKVHack && !pContentKey->GetNextKey() )
{
//the KeyValues will have been complaining about a missing key
Msg( "(Server) Dear user, this code has detected, that you've received one KeyValues Error in the console. Do not worry about it.\n" );
break;
}
Warning( "(Server) Extra content appId \'%s\' is no integer\n", bKVHack ? pContentKey->GetName() : pContentKey->GetString() );
}
else if ( nExtraContentId == INT_MAX || nExtraContentId == INT_MIN )
Warning( "(Server) Extra content appId \'%s\' is out of range\n", bKVHack ? pContentKey->GetName() : pContentKey->GetString() );
else if ( filesystem->MountSteamContent( -abs( nExtraContentId ) ) != FILESYSTEM_MOUNT_OK )
Warning( "(Server) Unable to mount extra content with appId: %i\n", nExtraContentId );
else
DevMsg( "(Server) Successfully mounted extra content with appId: %i\n", nExtraContentId );
//HACK
if( bKVHack ) {
bKVHack = false;
continue;
}
else
bKVHack = true;
pContentKey = pContentKey->GetNextKey();
} while( pContentKey );
}
else
{
nExtraContentId = pExtraContent->GetInt();
if ( nExtraContentId == 0 )
Warning( "(Server) AdditionalContentId specified with invalid appId: %s\n", pExtraContent->GetString() );
else if ( filesystem->MountSteamContent( -abs( nExtraContentId ) ) != FILESYSTEM_MOUNT_OK )
Warning( "(Server) Unable to mount extra content with appId: %i\n", nExtraContentId );
else
DevMsg( "(Server) Successfully mounted extra content with appId: %i\n", nExtraContentId );
}
}
}
}
pMainFile->deleteThis();
}
If you did not have this function before, then call this function in CServerGameDLL::DLLInit right before gpGlobals = pGlobals;
.
Implementing in gameinfo.txt
Now you can mount content by adding this after the ToolsAppId:
AdditionalContentId { 220 //HL2 240 //CS Source 280 //HL Source 320 //HL2 Deatmatch 340 //HL2 Lost Coast 360 //HL Deathmatch: Source 380 //Ep1 400 //Portal 420 //Ep2 440 //TF2 500 // L4D - has content incompatibilities! 550 // L4D2 - as above! }
Don't forget to add the game's SearchPath too. Mounting a single id (AdditionalContentId 220
) is still supported.
Bugs
Really big numbers will crash the game with an Error-Dialog by Valve. You could check if the value is within a certain range to stop this from happening, but I think the user will get by himself that this value cannot be mounted.
A KeyValues-error will appear in the console when you mount an uneven number of games. Nope, neither me (z33ky) or Valve think it's unlucky to do that (or maybe I do..!), it's rather that the KeyValues-class will expect a value, when there is none left. You can fix this by modifying KeyValues::RecursiveLoadFromBuffer in tier1/KeyValues.cpp, compiling a new tier1.lib and link against that. You can either silently ignore that error (which means that it will be ignored for other KeyValues-buffers as well!) or think of how to ignore that error properly.
An alternative is to load the file contents into a buffer yourself, checking that out and cheating another value in, so the KeyValues-parser is happy when you then do LoadFromBuffer.
I (z33ky) decided not to do that so I don't have to merge that code and compile a new tier1.lib or copy it over when I port my code to an updated SDK version. And I found the alternative too messy for this little draw-back; it's probably the better way to go if you really don't want that error to show up though. Currently, the code simply tells the user not to worry about it.
