Skip to content

Commit 4cb4071

Browse files
committedJan 30, 2014
Buildbsp.py updates to support where the map compilers live these days
1 parent 0a65778 commit 4cb4071

File tree

1 file changed

+91
-85
lines changed

1 file changed

+91
-85
lines changed
 

‎tools/buildbsp.py

+91-85
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
python buildbsp.py --game tf2 --no-run --no-install --fast mymap.vmf
1717
1818
"""
19-
2019
import argparse
2120
import sys
2221
import os
@@ -25,56 +24,43 @@
2524
import urllib.parse
2625
import shutil
2726

28-
win32 = sys.platform.startswith('win32')
29-
cygwin = sys.platform.startswith('cygwin')
30-
linux = sys.platform.startswith('linux')
31-
darwin = False # Not supported yet
32-
33-
games = {
34-
'tf2': {
35-
'id': 440,
36-
'dir': os.path.join("Team Fortress 2", "tf"),
37-
'common': True # Game lives under "common" rather than "<username>"
38-
},
39-
'css': {
40-
'id': 240,
41-
'dir': os.path.join("Counter-Strike Source", "cstrike"),
42-
'common': False
43-
},
44-
'hl2': {
45-
'id': 220,
46-
'dir': os.path.join("Half-Life 2", "hl2"),
47-
'common': False
48-
},
49-
'hl2mp': {
50-
'id': 320,
51-
'dir': os.path.join("Half-Life 2 Deathmatch", "hl2mp"),
52-
'common': False
53-
},
54-
'gm': {
55-
'id': 4000,
56-
'dir': os.path.join("GarrysMod", "garrysmod"),
57-
'common': False
58-
}
59-
}
6027

61-
def get_game_dir(game, username=False):
62-
"""Returns joined game directory path relative to Steamapps"""
63-
if not games[game]['common'] and not username:
64-
raise RuntimeError("Can't determine this game's directory without username")
65-
if games[game]['common']:
66-
subdir = "common"
67-
else:
68-
subdir = "username"
69-
subsubdir = games[game]['dir']
70-
if win32 or cygwin:
71-
subsubdir = subsubdir.lower()
72-
return os.path.join(subdir, subsubdir)
28+
class Game:
29+
def __init__(self, id, dir, common, uses_sdk):
30+
self.id = id # Numeric Steam catalog ID number
31+
self.dir = dir # Path to inner game directory (containing gameinfo.txt)
32+
self.common = common # Game lives under "common" rather than "<username>"
33+
self.uses_sdk = uses_sdk # False if game ships with its own map compilers
34+
def get_game_dir(self, username=False):
35+
"""Returns joined game directory path relative to Steamapps"""
36+
if not self.common and not username:
37+
raise RuntimeError("Can't determine this game's directory without username")
38+
if self.common:
39+
subdir = "common"
40+
else:
41+
subdir = "username"
42+
subsubdir = self.dir
43+
if WIN32 or CYGWIN:
44+
subsubdir = subsubdir.lower()
45+
return os.path.join(subdir, subsubdir)
46+
47+
48+
WIN32 = sys.platform.startswith('win32')
49+
CYGWIN = sys.platform.startswith('cygwin')
50+
LINUX = sys.platform.startswith('linux')
51+
DARWIN = False # Not supported yet
52+
GAMES = {
53+
'tf2': Game(440, os.path.join("Team Fortress 2", "tf"), True, False),
54+
'css': Game(240, os.path.join("Counter-Strike Source", "cstrike"), False, False),
55+
'hl2': Game(220, os.path.join("Half-Life 2", "hl2"), False, True),
56+
'hl2mp': Game(320, os.path.join("Half-Life 2 Deathmatch", "hl2mp"), False, False),
57+
'gm': Game(4000, os.path.join("GarrysMod", "garrysmod"), False, True),
58+
}
7359

7460
def _make_arg_parser():
7561
parser = argparse.ArgumentParser(description='Build, install, and test a VMF map.')
7662
parser.add_argument('map')
77-
parser.add_argument('-g', '--game', default='tf2', choices=games.keys(),
63+
parser.add_argument('-g', '--game', default='tf2', choices=GAMES.keys(),
7864
help="selects which game to use")
7965
parser.add_argument('--no-run', action="store_true",
8066
help="don't run the game after building/installing")
@@ -86,64 +72,83 @@ def _make_arg_parser():
8672
help="enable full HDR compile")
8773
parser.add_argument('--final', action="store_true",
8874
help="use with --hdr for slow high-quality HDR compile")
89-
parser.add_argument('--sourcesdk',
90-
help="location of your sourcesdk folder (for linux/wine)")
75+
parser.add_argument('--steam-windows-path',
76+
help="path to your (Windows) Steam folder (for games not dependent on SDK)")
77+
parser.add_argument('--username',
78+
help="your Steam username (needed for some games)")
9179

9280
return parser
9381

9482
def main():
9583
parser = _make_arg_parser()
9684
args = parser.parse_args()
85+
game = GAMES[args.game]
86+
username = args.username # May be None
9787
vmf_file = os.path.abspath(args.map)
9888
path, filename = os.path.split(vmf_file)
9989
mapname = filename[:-4]
10090
mappath = os.path.join(path, mapname)
10191
bsp_file = os.path.join(path, mapname + ".bsp")
102-
103-
# Get sourcesdk path
104-
if win32 or cygwin:
105-
sourcesdk = os.environ['sourcesdk']
106-
if cygwin:
92+
sourcesdk = None
93+
winsteam = args.steam_windows_path
94+
if not winsteam:
95+
winsteam = os.getenv('winsteam')
96+
97+
# We need to find out where the SteamApps directory is.
98+
if winsteam:
99+
steamapps = os.path.join(winsteam, "Steamapps")
100+
if not os.path.isdir(steamapps): # Try lowercase
101+
steamapps = os.path.join(winsteam, "steamapps")
102+
if not os.path.isdir(steamapps):
103+
raise Exception(
104+
"The provided Steam directory does not contain a Steamapps directory: %s" %
105+
os.path.abspath(winsteam)
106+
)
107+
elif WIN32 or CYGWIN:
108+
sourcesdk = os.getenv('sourcesdk')
109+
if CYGWIN:
107110
def cygwin2dos(path):
108111
return subprocess.check_output(["cygpath", '-w', '%s' % path], universal_newlines=True).strip()
109112
sourcesdk = subprocess.check_output(["cygpath", sourcesdk], universal_newlines=True).strip()
110113
sourcesdk = os.path.abspath(sourcesdk)
111-
elif linux:
112-
sourcesdk = args.sourcesdk
113-
if not sourcesdk:
114-
sourcesdk = os.getenv('sourcesdk')
115-
if not sourcesdk:
116-
print("You need to pass the --sourcesdk argument or set the $sourcesdk env variable.")
117-
exit(-1)
118-
sourcesdk = os.path.abspath(sourcesdk)
119-
120-
# Collect some other useful paths and info
121-
steamapps = os.path.dirname(os.path.dirname(sourcesdk)) # Full path to steamapps dir
122-
username = os.path.basename(os.path.dirname(sourcesdk))
123-
sdkbin = os.path.join(sourcesdk, "bin", "orangebox", "bin")
124-
game = games[args.game]
125-
gamedir = os.path.join(steamapps, get_game_dir(args.game, username))
114+
steamapps = os.path.dirname(os.path.dirname(sourcesdk))
115+
if not os.path.isdir(steamapps):
116+
raise Exception("Steamapps directory could not be found. Please specify using --steam-windows-path or see --help.")
117+
if not username:
118+
username = os.path.basename(os.path.dirname(sourcesdk))
119+
else:
120+
raise Exception("Unable to determine where your (Windows) Steam installation is located. See --help.")
121+
steamapps = os.path.abspath(steamapps)
122+
123+
# Prepare some useful paths
124+
gamedir = os.path.join(steamapps, game.get_game_dir(username))
126125
mapsdir = os.path.join(gamedir, "maps")
127126

128-
# TF2 SteamPipe workaround
129-
if args.game == 'tf2':
130-
sdkbin = os.path.join(os.path.dirname(gamedir), "bin")
127+
# Get path to correct bin tools directory (game or SDK)
128+
if game.uses_sdk:
129+
if not sourcesdk:
130+
# Try finding SDK within Steamapps
131+
# TODO
132+
raise Exception("Sorry, SDK games aren't implemented right now unless you're on Windows.")
133+
toolsdir = os.path.join(sourcesdk, "bin", "orangebox", "bin")
134+
else:
135+
toolsdir = os.path.abspath(os.path.join(gamedir, "..", "bin"))
131136

132137
# Make sure gamedir path seems legit
133138
if not os.path.isfile(os.path.join(gamedir, "gameinfo.txt")):
134139
raise Exception("Game directory does not contain a gameinfo.txt: %s" % gamedir)
135-
136-
if win32 or cygwin:
140+
141+
if WIN32 or CYGWIN:
137142
# Convert some paths if using Cygwin
138-
if cygwin:
143+
if CYGWIN:
139144
gamedir = cygwin2dos(gamedir)
140145
mappath = cygwin2dos(mappath)
141146

142147
# Change working directory first because VBSP is dumb
143148
os.chdir(os.path.join(sourcesdk, 'bin', 'orangebox'))
144149

145150
# Run the SDK tools
146-
vbsp_exe = os.path.join(sdkbin, "vbsp.exe")
151+
vbsp_exe = os.path.join(toolsdir, "vbsp.exe")
147152
code = subprocess.call([vbsp_exe, '-game', gamedir, mappath])
148153
print("VBSP finished with status %s." % code)
149154

@@ -159,14 +164,14 @@ def cygwin2dos(path):
159164
print("Looks like VBSP crashed, but I'm not sure why.")
160165
exit(code)
161166

162-
vvis_exe = os.path.join(sdkbin, "vvis.exe")
167+
vvis_exe = os.path.join(toolsdir, "vvis.exe")
163168
opts = [vvis_exe]
164169
if args.fast:
165170
opts.append('-fast')
166171
opts.extend(['-game', gamedir, mappath])
167172
subprocess.call(opts)
168173

169-
vrad_exe = os.path.join(sdkbin, "vrad.exe")
174+
vrad_exe = os.path.join(toolsdir, "vrad.exe")
170175
opts = [vrad_exe]
171176
if args.fast:
172177
opts.extend(['-bounce', '2', '-noextra'])
@@ -197,7 +202,7 @@ def cygwin2dos(path):
197202
print("Or, just run 'map %s' in the in-game console." % mapname)
198203
else:
199204
print("Not launching game")
200-
elif linux:
205+
elif LINUX:
201206
# Environment to use with wine calls
202207
env = os.environ.copy()
203208
env['WINEPREFIX'] = os.path.expanduser("~/.winesteam")
@@ -216,25 +221,26 @@ def unix2wine(path):
216221
#print("WINEDLLPATH is as follows: ", env['WINEDLLPATH'])
217222

218223
# Use native maps directory instead of the Wine installation's
219-
mapsdir = os.path.join('~', '.steam', 'steam', 'SteamApps', get_game_dir(args.game, username), "maps")
224+
mapsdir = os.path.join('~', '.steam', 'steam', 'SteamApps', game.get_game_dir(username), "maps")
220225
mapsdir = os.path.expanduser(mapsdir)
221226

222227
# Change working directory first because VBSP is dumb
223-
os.chdir(os.path.join(sourcesdk, 'bin', 'orangebox'))
228+
#os.chdir(os.path.join(sourcesdk, 'bin', 'orangebox'))
224229

225230
print("Using -game dir: %s" % gamedir)
226231

227232
# We now need to set the VPROJECT env variable
228233
env['VPROJECT'] = gamedir
229234

230235
# Run the SDK tools
231-
vbsp_exe = os.path.join(sdkbin, "vbsp.exe")
236+
vbsp_exe = os.path.join(toolsdir, "vbsp.exe")
232237
code = subprocess.call(['wine', vbsp_exe, '-game', gamedir, mappath], env=env)
233238
print("VBSP finished with status %s." % code)
234239

235240
# Handle various exit status codes VBPS may have returned
236241
if code == 1:
237-
print("\nLooks like SteamService isn't working. Try reopening (wine's copy of) Steam:")
242+
print("\nLooks like VBSP crashed, possibly due to invalid geometry in the map. Check the output above.")
243+
print("\It could also be related to SteamService isn't working. Try re(launching) wine's Steam:")
238244
steambin = os.path.join(os.path.dirname(steamapps), 'steam.exe')
239245
print('\nWINEPREFIX="%s" wine "%s" -no-dwrite' % (env['WINEPREFIX'], steambin))
240246
exit(code)
@@ -247,7 +253,7 @@ def unix2wine(path):
247253
print("\nLooks like VBSP crashed, but I'm not sure why.")
248254
exit(code)
249255

250-
vvis_exe = os.path.join(sdkbin, "vvis.exe")
256+
vvis_exe = os.path.join(toolsdir, "vvis.exe")
251257
opts = ['wine', vvis_exe]
252258
if args.fast:
253259
opts.append('-fast')
@@ -258,7 +264,7 @@ def unix2wine(path):
258264
print("\nLooks like VVIS crashed, but I'm not sure why.")
259265
exit(code)
260266

261-
vrad_exe = os.path.join(sdkbin, "vrad.exe")
267+
vrad_exe = os.path.join(toolsdir, "vrad.exe")
262268
opts = ['wine', vrad_exe]
263269
if args.fast:
264270
opts.extend(['-bounce', '2', '-noextra'])
@@ -282,7 +288,7 @@ def unix2wine(path):
282288
# Launch the game (unless --no-run or --no-install)
283289
if not args.no_run and not args.no_install:
284290
params = urllib.parse.quote("-dev -console -allowdebug +map %s" % mapname)
285-
run_url = "steam://run/%d//%s" % (game['id'], params)
291+
run_url = "steam://run/%d//%s" % (game.id, params)
286292
print(run_url)
287293
webbrowser.open(run_url)
288294
else:

0 commit comments

Comments
 (0)
Please sign in to comment.