16
16
python buildbsp.py --game tf2 --no-run --no-install --fast mymap.vmf
17
17
18
18
"""
19
-
20
19
import argparse
21
20
import sys
22
21
import os
25
24
import urllib .parse
26
25
import shutil
27
26
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
- }
60
27
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
+ }
73
59
74
60
def _make_arg_parser ():
75
61
parser = argparse .ArgumentParser (description = 'Build, install, and test a VMF map.' )
76
62
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 (),
78
64
help = "selects which game to use" )
79
65
parser .add_argument ('--no-run' , action = "store_true" ,
80
66
help = "don't run the game after building/installing" )
@@ -86,64 +72,83 @@ def _make_arg_parser():
86
72
help = "enable full HDR compile" )
87
73
parser .add_argument ('--final' , action = "store_true" ,
88
74
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)" )
91
79
92
80
return parser
93
81
94
82
def main ():
95
83
parser = _make_arg_parser ()
96
84
args = parser .parse_args ()
85
+ game = GAMES [args .game ]
86
+ username = args .username # May be None
97
87
vmf_file = os .path .abspath (args .map )
98
88
path , filename = os .path .split (vmf_file )
99
89
mapname = filename [:- 4 ]
100
90
mappath = os .path .join (path , mapname )
101
91
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 :
107
110
def cygwin2dos (path ):
108
111
return subprocess .check_output (["cygpath" , '-w' , '%s' % path ], universal_newlines = True ).strip ()
109
112
sourcesdk = subprocess .check_output (["cygpath" , sourcesdk ], universal_newlines = True ).strip ()
110
113
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 ))
126
125
mapsdir = os .path .join (gamedir , "maps" )
127
126
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" ))
131
136
132
137
# Make sure gamedir path seems legit
133
138
if not os .path .isfile (os .path .join (gamedir , "gameinfo.txt" )):
134
139
raise Exception ("Game directory does not contain a gameinfo.txt: %s" % gamedir )
135
-
136
- if win32 or cygwin :
140
+
141
+ if WIN32 or CYGWIN :
137
142
# Convert some paths if using Cygwin
138
- if cygwin :
143
+ if CYGWIN :
139
144
gamedir = cygwin2dos (gamedir )
140
145
mappath = cygwin2dos (mappath )
141
146
142
147
# Change working directory first because VBSP is dumb
143
148
os .chdir (os .path .join (sourcesdk , 'bin' , 'orangebox' ))
144
149
145
150
# Run the SDK tools
146
- vbsp_exe = os .path .join (sdkbin , "vbsp.exe" )
151
+ vbsp_exe = os .path .join (toolsdir , "vbsp.exe" )
147
152
code = subprocess .call ([vbsp_exe , '-game' , gamedir , mappath ])
148
153
print ("VBSP finished with status %s." % code )
149
154
@@ -159,14 +164,14 @@ def cygwin2dos(path):
159
164
print ("Looks like VBSP crashed, but I'm not sure why." )
160
165
exit (code )
161
166
162
- vvis_exe = os .path .join (sdkbin , "vvis.exe" )
167
+ vvis_exe = os .path .join (toolsdir , "vvis.exe" )
163
168
opts = [vvis_exe ]
164
169
if args .fast :
165
170
opts .append ('-fast' )
166
171
opts .extend (['-game' , gamedir , mappath ])
167
172
subprocess .call (opts )
168
173
169
- vrad_exe = os .path .join (sdkbin , "vrad.exe" )
174
+ vrad_exe = os .path .join (toolsdir , "vrad.exe" )
170
175
opts = [vrad_exe ]
171
176
if args .fast :
172
177
opts .extend (['-bounce' , '2' , '-noextra' ])
@@ -197,7 +202,7 @@ def cygwin2dos(path):
197
202
print ("Or, just run 'map %s' in the in-game console." % mapname )
198
203
else :
199
204
print ("Not launching game" )
200
- elif linux :
205
+ elif LINUX :
201
206
# Environment to use with wine calls
202
207
env = os .environ .copy ()
203
208
env ['WINEPREFIX' ] = os .path .expanduser ("~/.winesteam" )
@@ -216,25 +221,26 @@ def unix2wine(path):
216
221
#print("WINEDLLPATH is as follows: ", env['WINEDLLPATH'])
217
222
218
223
# 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" )
220
225
mapsdir = os .path .expanduser (mapsdir )
221
226
222
227
# 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'))
224
229
225
230
print ("Using -game dir: %s" % gamedir )
226
231
227
232
# We now need to set the VPROJECT env variable
228
233
env ['VPROJECT' ] = gamedir
229
234
230
235
# Run the SDK tools
231
- vbsp_exe = os .path .join (sdkbin , "vbsp.exe" )
236
+ vbsp_exe = os .path .join (toolsdir , "vbsp.exe" )
232
237
code = subprocess .call (['wine' , vbsp_exe , '-game' , gamedir , mappath ], env = env )
233
238
print ("VBSP finished with status %s." % code )
234
239
235
240
# Handle various exit status codes VBPS may have returned
236
241
if code == 1 :
237
- print ("\n Looks like SteamService isn't working. Try reopening (wine's copy of) Steam:" )
242
+ print ("\n Looks 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:" )
238
244
steambin = os .path .join (os .path .dirname (steamapps ), 'steam.exe' )
239
245
print ('\n WINEPREFIX="%s" wine "%s" -no-dwrite' % (env ['WINEPREFIX' ], steambin ))
240
246
exit (code )
@@ -247,7 +253,7 @@ def unix2wine(path):
247
253
print ("\n Looks like VBSP crashed, but I'm not sure why." )
248
254
exit (code )
249
255
250
- vvis_exe = os .path .join (sdkbin , "vvis.exe" )
256
+ vvis_exe = os .path .join (toolsdir , "vvis.exe" )
251
257
opts = ['wine' , vvis_exe ]
252
258
if args .fast :
253
259
opts .append ('-fast' )
@@ -258,7 +264,7 @@ def unix2wine(path):
258
264
print ("\n Looks like VVIS crashed, but I'm not sure why." )
259
265
exit (code )
260
266
261
- vrad_exe = os .path .join (sdkbin , "vrad.exe" )
267
+ vrad_exe = os .path .join (toolsdir , "vrad.exe" )
262
268
opts = ['wine' , vrad_exe ]
263
269
if args .fast :
264
270
opts .extend (['-bounce' , '2' , '-noextra' ])
@@ -282,7 +288,7 @@ def unix2wine(path):
282
288
# Launch the game (unless --no-run or --no-install)
283
289
if not args .no_run and not args .no_install :
284
290
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 )
286
292
print (run_url )
287
293
webbrowser .open (run_url )
288
294
else :
0 commit comments