Monday, September 15, 2014

Porting simple Half-Life 2-based singleplayer mods to Source SDK Base 2013 in 3 steps

If you have a lot of custom code, there are probably some compelling reasons NOT to try to upgrade your existing code to Source 2013 unless you have a lot of free time to hand-merge everything... but if you just have a mod consisting of maps running on Half-Life 2 or the episodes, the relatively easy update to Source SDK Base 2013 gives you better performance (the Steampipe VPK-based loading is much faster than the old GCF system), integrated VR support, and maybe most importantly, it is a freely available "standalone" release to anyone with a Steam account.

The process is basically 2 steps, but I added a 3rd pretty crucial "step" that came up in my own mod...

EDIT, 18 September 2014: so it turns out that Valve includes pre-compiled "sourcetest" binaries, by default, based on whatever platform you downloaded Source 2013 to... so, if you have a simple mod that doesn't make use of any Half-Life 2 or episodic gameplay features, you can easily port your mod by editing your gameinfo.txt. You can just copy from my gameinfo.txt below (for step 2) or update it however you wish, the most important part is the line "gamebin sourcetest/bin" which tells Steam to load Valve's default sourcetest game code.

1) Nicolas Kirsch has kindly put together a "mod template" with pre-compiled client / server binaries based on revision 13 of the Source SDK 2013. This was arguably the worst part of the process, and now someone has done it for you! You can start with this and just add your files, or just pluck the client.dll and server.dll from \bin\.

2) Update your gameinfo.txt to use all the Source 2013 conventions. For reference, mine looks like this:

"GameInfo"
{
	game 		"Radiator vol. 1"
	gamelogo	1
	supportsvr	1

	type		singleplayer_only
	developer	"Robert Yang"
	developer_url	"http://www.radiator.debacle.us"
	icon		"icon"

	FileSystem
	{
		SteamAppId				243730		// This sets the app ID in Steam
		
		//
		// The code that loads this file automatically does a few things here:
		//
		// 1. For each "Game" search path, it adds a "GameBin" path, in \bin
		// 2. For each "Game" search path, it adds another "Game" path in front of it with _ at the end.
		//    For example: c:\hl2\cstrike on a french machine would get a c:\hl2\cstrike_french path added to it.
		// 3. For the first "Game" search path, it adds a search path called "MOD".
		// 4. For the first "Game" search path, it adds a search path called "DEFAULT_WRITE_PATH".
		//

		//
		// Search paths are relative to the base directory, which is where hl2.exe is found.
		//
		// |gameinfo_path| points at the directory where gameinfo.txt is.
		// We always want to mount that directory relative to gameinfo.txt, so
		// people can mount stuff in c:\mymod, and the main game resources are in
		// someplace like c:\program files\valve\steam\steamapps\half-life 2.
		//
		SearchPaths
		{
			// First, mount all user customizations.  This will search for VPKs and subfolders
			// and mount them in alphabetical order.  The easiest way to distribute a mod is to
			// pack up the custom content into a VPK.  To "install" a mod, just drop it in this
			// folder.
			//
			// Note that this folder is scanned only when the game is booted.
			game+mod			ep2/custom/*
			game+mod			episodic/custom/*
			game+mod			hl2/custom/*

			// Now search loose files.  We'll set the directory containing the gameinfo.txt file
			// as the first "mod" search path (after any user customizations).  This is also the one
			// that's used when writing to the "mod" path.
			game+mod+mod_write+default_write_path		|gameinfo_path|.
			gamebin				|gameinfo_path|bin

			// We search VPK files before ordinary folders, because most files will be found in
			// VPK and we can avoid making thousands of file system calls to attempt to open files
			// in folders where they don't exist.  (Searching a VPK is much faster than making an operating
			// system call.)
			game_lv				hl2/hl2_lv.vpk
			game+mod			ep2/ep2_english.vpk
			game+mod			ep2/ep2_pak.vpk
			game				|all_source_engine_paths|episodic/ep1_english.vpk
			game				|all_source_engine_paths|episodic/ep1_pak.vpk
			game				|all_source_engine_paths|hl2/hl2_english.vpk
			game				|all_source_engine_paths|hl2/hl2_pak.vpk
			game				|all_source_engine_paths|hl2/hl2_textures.vpk
			game				|all_source_engine_paths|hl2/hl2_sound_vo_english.vpk
			game				|all_source_engine_paths|hl2/hl2_sound_misc.vpk
			game				|all_source_engine_paths|hl2/hl2_misc.vpk
			platform			|all_source_engine_paths|platform/platform_misc.vpk

			// Add the HL2 directory as a game search path.  This is also where where writes
			// to the "game" path go.
			game+game_write		ep2

			// Where the game's binaries are
			gamebin				sourcetest/bin

			// Last, mount in shared HL2 loose files
			game				|all_source_engine_paths|episodic
			game				|all_source_engine_paths|hl2
			platform			|all_source_engine_paths|platform
		}
	}
}

3) Beware that "buildcubemaps" is currently broken in the engine; it does not properly overwrite the default VBSP cubemaps. You will need to use Kirsch's fix here: a custom version of VBSP that does not generate default cubemaps, and a renamed default cubemap VTF for the engine to use when it finds no cubemaps in your BSP.