Mounting multiple games: Difference between revisions
(I just edited so the code is shorter they can figure out the rest of what they should do.) |
(scripted mounting) |
||
Line 1: | Line 1: | ||
<div style="margin:1em auto;padding:.3em 1em;border:1px solid #B8B5A8;background:#E6E2D2;width:30em;min-height:26px;text-align:center;-moz-border-radius:.5em;-webkit-border-radius:.5em;border-radius:.5em;"><strong style="display:block;">This guide has been obsoleted by the Orange Box.</strong> Use gameinfo.txt's new [[Gameinfo.txt#Mounting content|<code>AdditonalContentID</code>]] feature instead.</div> | <div style="margin:1em auto;padding:.3em 1em;border:1px solid #B8B5A8;background:#E6E2D2;width:30em;min-height:26px;text-align:center;-moz-border-radius:.5em;-webkit-border-radius:.5em;border-radius:.5em;"><strong style="display:block;">This guide has partly been obsoleted by the Orange Box.</strong> Use gameinfo.txt's new [[Gameinfo.txt#Mounting content|<code>AdditonalContentID</code>]] feature instead.</div> | ||
Mounting additional content will not make other games weapons, NPCs and what not available, but allows you use the content, such as maps, models, textures.. The info_player_*** will not work (unless it's identical to the spawn-points in your mod), so you'll have to fix this in CBasePlayer::EntSelectSpawnPoint. | |||
{{warning|Your mod will run regardless of whether or not the user owns the game that you mount content for. If the game isn't owned the content will not be mounted, and error signs will appear everywhere instead!}} | |||
= Mounting = | |||
== Scripted Mounting == | |||
This feature builds on top of Tony Sergis AdditionalAppId-code, but it can be used in older SDK versions, too. | |||
This will however allow to mount multiple AppIds. | |||
=== cdll_client_init.cpp - client === | |||
Search for the function "static void MountAdditionalContent()". If you don't have it, just add it right above CHLClient::Init. | |||
This is the code: | |||
<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(); | |||
}</code> | |||
If you did not have this function before, then call this function in CHLClient::Init right before | |||
<code>if ( CommandLine()->FindParm( "-textmode" ) ) | |||
g_bTextMode = true;</code> | |||
=== gameinterface.cpp - server === | |||
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: | |||
<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(); | |||
}</code> | |||
If you did not have this function before, then call this function in CServerGameDLL::DLLInit right before | |||
<code>gpGlobals = pGlobals;</code> | |||
=== gameinfo.txt - scripting === | |||
Now you can mount content by adding this after the ToolsAppId | |||
<code>AdditionalContentId | |||
{ | |||
220 //HL2 | |||
240 //CS:S | |||
280 //HL:S | |||
320 //HL2:DM | |||
340 //HL2:LC | |||
360 //HL:SDM | |||
380 //Ep1 | |||
400 //Portal | |||
420 //Ep2 | |||
440 //TF2 | |||
}</code> | |||
Add/Remove what you need/don't need. The code also allowed 'old-style' mounting with a single Id | |||
<code>AdditionalContentId 220</code> | |||
=== 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 ([[User:Z33ky|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 ([[User:Z33ky|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. | |||
== Hard-coded Mounting == | |||
The following short section of code allows you to mount additional files from the following games: | The following short section of code allows you to mount additional files from the following games: | ||
Line 11: | Line 223: | ||
* '''Half Life 2 (fixes),''' | * '''Half Life 2 (fixes),''' | ||
Firstly for cdll_client_int.cpp (CLIENTSIDE) | |||
After the big list of conditions to return false in CHLClient::Init - and paste this: | After the big list of conditions to return false in CHLClient::Init - and paste this: | ||
Line 28: | Line 238: | ||
filesystem->AddSearchPath("hl1mp", "GAME"); | filesystem->AddSearchPath("hl1mp", "GAME"); | ||
filesystem->AddSearchPath("lostcoast", "GAME"); | filesystem->AddSearchPath("lostcoast", "GAME"); | ||
filesystem->MountSteamContent(-220); //Half-Life 2 | |||
filesystem->MountSteamContent(-320); //Half-Life 2: Deathmatch | |||
filesystem->MountSteamContent(-380); //Half-Life 2: Episode One | |||
filesystem->MountSteamContent(-300); //Day of Defeat: Source | |||
filesystem->MountSteamContent(-240); //Counter-Strike: Source | |||
filesystem->MountSteamContent(-440); //Team Fortress 2 | |||
filesystem->MountSteamContent(-400); //Portal | |||
filesystem->MountSteamContent(-420); //Half-Life 2: Episode Two | |||
filesystem->MountSteamContent(-280); //Half-Life: Source | |||
filesystem->MountSteamContent(-360); //Half-Life: Source Deathmatch | |||
filesystem->MountSteamContent(-340); //Half-Life 2: Lost Coast | |||
{{note|The above code mounts all Source games. Simply comment out the games you don't want to mount, if any.}} | {{note|The above code mounts all Source games. Simply comment out the games you don't want to mount, if any.}} | ||
Line 39: | Line 260: | ||
{{tip|This works in debug mode. (By keeping the - sign in front of the app number.}} | {{tip|This works in debug mode. (By keeping the - sign in front of the app number.}} | ||
== Add Order == | === Add Order === | ||
You can also select how the path should be added to the directory table, to affect the priority given to files of the same name: | You can also select how the path should be added to the directory table, to affect the priority given to files of the same name: | ||
Line 45: | Line 266: | ||
filesystem->AddSearchPath("dod", "GAME", PATH_ADD_TO_HEAD); //Add to the Head | filesystem->AddSearchPath("dod", "GAME", PATH_ADD_TO_HEAD); //Add to the Head | ||
== See also | == Bugs == | ||
filesystem->MountSteamContent returns FILESYSTEM_MOUNT_OK, even if the content is not available. There's nothing you can do about it, apart from poking some guy from Valvesoftware. | |||
= See also = | |||
* [[IFileSystem]] | * [[IFileSystem]] | ||
* [[IFileSystem#Mount_Steam_Application_Content|Mount Steam Application Content]] | * [[IFileSystem#Mount_Steam_Application_Content|Mount Steam Application Content]] |
Revision as of 13:56, 8 December 2009
AdditonalContentID
feature instead.Mounting additional content will not make other games weapons, NPCs and what not available, but allows you use the content, such as maps, models, textures.. The info_player_*** will not work (unless it's identical to the spawn-points in your mod), so you'll have to fix this in CBasePlayer::EntSelectSpawnPoint.

Mounting
Scripted Mounting
This feature builds on top of Tony Sergis AdditionalAppId-code, but it can be used in older SDK versions, too.
This will however allow to mount multiple AppIds.
cdll_client_init.cpp - client
Search for the function "static void MountAdditionalContent()". 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;
gameinterface.cpp - server
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;
gameinfo.txt - scripting
Now you can mount content by adding this after the ToolsAppId
AdditionalContentId
{
220 //HL2
240 //CS:S
280 //HL:S
320 //HL2:DM
340 //HL2:LC
360 //HL:SDM
380 //Ep1
400 //Portal
420 //Ep2
440 //TF2
}
Add/Remove what you need/don't need. The code also allowed 'old-style' mounting with a single Id
AdditionalContentId 220
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.
Hard-coded Mounting
The following short section of code allows you to mount additional files from the following games:
- Counter Strike Source,
- Hl2MP,
- Episode 1,
- Episode 2,
- Team Fortress 2
- Portal
- Day of Defeat Source,
- Half Life 2 (fixes),
Firstly for cdll_client_int.cpp (CLIENTSIDE)
After the big list of conditions to return false in CHLClient::Init - and paste this:
filesystem->AddSearchPath("hl2mp", "GAME"); filesystem->AddSearchPath("hl2", "GAME"); filesystem->AddSearchPath("episodic", "GAME"); filesystem->AddSearchPath("dod", "GAME"); filesystem->AddSearchPath("cstrike", "GAME"); filesystem->AddSearchPath("tf", "GAME"); filesystem->AddSearchPath("portal", "GAME"); filesystem->AddSearchPath("ep2", "GAME"); filesystem->AddSearchPath("hl1", "GAME"); filesystem->AddSearchPath("hl1mp", "GAME"); filesystem->AddSearchPath("lostcoast", "GAME"); filesystem->MountSteamContent(-220); //Half-Life 2 filesystem->MountSteamContent(-320); //Half-Life 2: Deathmatch filesystem->MountSteamContent(-380); //Half-Life 2: Episode One filesystem->MountSteamContent(-300); //Day of Defeat: Source filesystem->MountSteamContent(-240); //Counter-Strike: Source filesystem->MountSteamContent(-440); //Team Fortress 2 filesystem->MountSteamContent(-400); //Portal filesystem->MountSteamContent(-420); //Half-Life 2: Episode Two filesystem->MountSteamContent(-280); //Half-Life: Source filesystem->MountSteamContent(-360); //Half-Life: Source Deathmatch filesystem->MountSteamContent(-340); //Half-Life 2: Lost Coast

Now open GameInterface.cpp (SERVERSIDE)
After the list of conditions to return false in CServerGameDLL::DLLInit - and paste the same code above.
Compile or rebuild and run your game.

Add Order
You can also select how the path should be added to the directory table, to affect the priority given to files of the same name:
filesystem->AddSearchPath("dod", "GAME", PATH_ADD_TO_TAIL); //Add to the Tail filesystem->AddSearchPath("dod", "GAME", PATH_ADD_TO_HEAD); //Add to the Head
Bugs
filesystem->MountSteamContent returns FILESYSTEM_MOUNT_OK, even if the content is not available. There's nothing you can do about it, apart from poking some guy from Valvesoftware.