diff options
Diffstat (limited to 'devtools/mapbuild')
| -rw-r--r-- | devtools/mapbuild/buildMaps.pl | 895 | ||||
| -rw-r--r-- | devtools/mapbuild/buildall.bat | 22 | ||||
| -rw-r--r-- | devtools/mapbuild/buildmod.bat | 74 | ||||
| -rw-r--r-- | devtools/mapbuild/readme.txt | 45 | ||||
| -rw-r--r-- | devtools/mapbuild/syncChangedMaps.pl | 103 |
5 files changed, 1139 insertions, 0 deletions
diff --git a/devtools/mapbuild/buildMaps.pl b/devtools/mapbuild/buildMaps.pl new file mode 100644 index 0000000..06c3f9d --- /dev/null +++ b/devtools/mapbuild/buildMaps.pl @@ -0,0 +1,895 @@ +#========= Copyright 1996-2008, Valve Corporation, All rights reserved. ========== +# +# Master script for performing automated compiles of bsp's with cubemaps, reslists, +# and nodegraphs. Compiles can be triggered manually by adding the vmf's absolute +# path to mapbuild\forcelist.txt, or by adding the "autocompile" keyword +# to the checkin comments for a vmf. +# +#================================================================================= + +use strict; +use warnings; +use Net::SMTP; + +my $maxCompiles = 3; # Maximum number of simultaneous compiles. +my $running = 0; +my @g_CompileList; +my @finalizeList; +my @g_Finalizing; +my @g_CompileThreads; +my %g_ToolArgs; +my %g_RunningCompiles; +my @g_OutputFiles; +my $g_FilenameIdx = 0; +my @g_MainDirs; +my $g_MainDirIdx = 0; +my @g_MapsToBuild; + +my $buildbsp = 1; # Compile the bsp with vbsp, vvis, and vrad +my $buildreslists = 1; # Build reslists for the map +my $buildnodegraphs = 1; # Build the nodegraph (.ain) for the map + +my $rootdir = "..\\..\\..\\.."; # Relative path from this script to one level above the "main" directory + +system( "echo. > finalize.txt" ); + +$SIG{CHLD}='IGNORE'; + +# Generate filenames for the compile output files + +for ( my $ct = 0; $ct < $maxCompiles * 2; ++$ct ) +{ + my $filename = join( '', "$rootdir\\main1\\src\\devtools\\mapbuild\\buildqueue", $g_FilenameIdx++, ".txt" ); + push( @g_OutputFiles, $filename ); +} + +# Generate main directory names for each simultaneous compile + +for ( my $ct = 1; $ct <= $maxCompiles; ++$ct ) +{ + my $filename = join( '', "main", $ct ); + push( @g_MainDirs, $filename ); +} + + +# Loop for infinity. If changes are made to this script, the process needs to +# be killed and restarted for those changes to take effect. Note, any maps that +# are compiling or waiting in the queue will NOT be picked up again when the script +# is restarted. Any maps that were compiling or waiting to compile when the process +# was stopped will need to be started manually by adding the vmf absolute path +# to forcelist.txt. A map can be manually added to the queue at any time while the script +# is running by adding the vmf absolute path to forcelist.txt. + +while ( 1 ) +{ + +# Generate a list of maps that need to be built. For vmf's that have been changed in +# Perforce, the checkin comments are parsed and if the word "autocompile" +# is found then that map will be added to the build list. All maps that are listed in +# forcelist.txt are also added to the build list. + +syncChangedMaps(); + + +# Append the maps to the build queue while filtering out duplicates. We only +# want to filter duplicates out of the queue - if a map is already compiling +# then it's reasonable to add it to the queue again because it must have changed +# since the current build began. + +my $updated = 0; +for my $map ( @g_MapsToBuild ) +{ + if ( $map =~ /(\w*.vmf)/ ) + { + # make sure it's not already in the queue + + my $duplicate = 0; + my $testname = $1; + for ( @g_CompileList ) + { + if ( /$testname/ ) + { + $duplicate = 1; + last; + } + } + + system( "echo. >> log.txt" ); + + if ( $duplicate != 1 ) + { + print( "Adding to compile list $map\n" ); + system( "echo Adding to compile list $map >> log.txt" ); + push( @g_CompileList, $map ); + $updated = 1; + } + else + { + print( "Duplicate map found: $testname\n" ); + system( "echo Duplicate map found: $testname >> log.txt" ); + } + } +} + +splice( @g_MapsToBuild ); + +# If there is a build slot open, start building the next map + +StartCompiles(); + +sleep( 60 ); # Time is arbitrary - just don't spam Perforce + +# Load in the finalize queue + +open( INFILE, "<finalize.txt" ); +my @finalizeList = <INFILE>; +close( INFILE ); + +system( "echo. > finalize.txt" ); + +# Finalize the maps (cubemaps, reslist, nodegraph, and checkin). The finalize list +# also contains the compile output (times,args,etc.) that need to go in the checkin comments. + +my $maindir = ""; +my $mod = ""; +my $mapname = ""; +my $strTime = ""; +my $vbspargs = ""; +my $vvisargs = ""; +my $vradargs = ""; +my $outfile = ""; + +for ( @finalizeList ) +{ + if ( /Finalize: (\w*) (\w*)/ ) + { + $mod = $1; + $mapname = $2; + next; + } + elsif ( /maindir: (.*)/ ) + { + $maindir = $1; + next; + } + elsif ( /vbspargs: (.*)/ ) + { + $vbspargs = $1; + next; + } + elsif ( /vvisargs: (.*)/ ) + { + $vvisargs = $1; + next; + } + elsif ( /vradargs: (.*)/ ) + { + $vradargs = $1; + next; + } + elsif ( /time: (.*)/ ) + { + $strTime = $1; + next; + } + elsif ( /outfile: (.*)/ ) + { + # This is the final entry for a map + $outfile = $1; + } + else + { + next; + } + + print( "\nFinalizing $mod\\$mapname.bsp\n" ); + system( "echo Finalizing $mapname.bsp >> log.txt" ); + + # A compile slot has opened up + + --$running; + push( @g_Finalizing, $mapname ); + + # Let another compile start in the just-emptied slot. + + StartCompiles(); + + my $ldrResult = 0; + my $hdrResult = 0; + + # HACK: Make sure a zombie process can't hold up the system + + system( "taskkill /im hl2.exe /f" ); + + chdir( "$rootdir\\$maindir\\src\\devtools\\mapbuild" ); + + # Get the Perforce client and owner names for the checkin file + my $g_client; + my $g_owner; + + my @clientspec = readpipe "p4 client -o"; + + for ( @clientspec ) + { + if ( /^Client:[\s*](.*)/ ) + { + $g_client = $1; + print( "Using client $g_client\n" ); + } + elsif ( /^Owner:[\s*](.*)/ ) + { + $g_owner = $1; + print( "Using owner $g_owner\n" ); + } + } + + if ( $buildbsp == 1 ) + { + # Build cubemaps + + system( "echo. >> $outfile" ); + system( "time /t >> $outfile" ); + system( "echo Building cubemaps for $mapname. >> $outfile" ); + + if ( $vradargs =~ /-hdr|-both/ ) + { + $hdrResult = system( "$rootdir\\$maindir\\game\\hl2.exe -allowdebug -game $mod -window -w 1152 -h 864 +mat_picmip 0 -dev +mat_hdr_level 2 +sv_cheats 1 +map $mapname -buildcubemaps" ); + } + + if ( $vradargs =~ /-ldr|-both/ ) + { + $ldrResult = system( "$rootdir\\$maindir\\game\\hl2.exe -allowdebug -game $mod -window -w 1152 -h 864 +mat_picmip 0 -dev +mat_hdr_level 0 +sv_cheats 1 +map $mapname -buildcubemaps" ); + } + } + + if ( $buildreslists == 1 ) + { + system( "echo. >> $outfile" ); + system( "time /t >> $outfile" ); + system( "echo Building reslists and nodegraph for $mapname. >> $outfile" ); + + system( "del /s $rootdir\\$maindir\\game\\$mod\\reslists_temp\\*.lst" ); + + system( "p4 edit $rootdir\\$maindir\\game\\$mod\\reslists_xbox\\$mapname.lst" ); + system( "p4 add $rootdir\\$maindir\\game\\$mod\\reslists_xbox\\$mapname.lst" ); + system( "del $rootdir\\$maindir\\game\\$mod\\reslists_xbox\\$mapname.lst" ); + my $extraflags = ""; +# if ( $mod =~ /portal/ ) +# { +# $extraflags = "-tempcontent"; +# } + system( "del \/s $rootdir\\$maindir\\game\\modelsounds.cache" ); + system( "$rootdir\\$maindir\\game\\hl2.exe -allowdebug -game $mod -window -dev -makereslists makereslists_xbox.txt $extraflags +map $mapname" ); + } + elsif ( $buildnodegraphs == 1 ) + { + # system( "p4 edit $rootdir\\$maindir\\game\\$mod\\maps\\graphs\\$mapname.ain >> log.txt" ); + + # TODO: Build nodegraphs method. Currently we get this for free when building reslists. + } + + # Generate the checkin file + + system( "echo. >> $outfile" ); + system( "time /t >> $outfile" ); + system( "echo Submitting to Perforce. >> $outfile" ); + + open( OUTFILE, ">checkin.txt" ); + print( OUTFILE "Change: new\n\n" ); + + print( OUTFILE "Client: $g_client\n\n" ); + print( OUTFILE "User: $g_owner\n\n" ); + + print( OUTFILE "Status: new\n\n" ); + print( OUTFILE "Description: Autobuild for map $mapname.bsp\n" ); + print( OUTFILE "\n" ); + if ( $hdrResult == 1 ) + { + print( OUTFILE "\tWARNING! There was an error while building HDR cubemaps.\n" ); + print( OUTFILE "\tHDR Cubemaps may need to be rebuilt before using this map.\n\n" ); + } + if ( $ldrResult == 1 ) + { + print( OUTFILE "\tWARNING! There was an error while building LDR cubemaps.\n" ); + print( OUTFILE "\tLDR Cubemaps may need to be rebuilt before using this map.\n\n" ); + } + + if ( $buildbsp == 1 ) + { + print( OUTFILE "\tCOMPILE ARGS\n" ); + print( OUTFILE "\tvbsp:\t",$vbspargs,"\n" ); + print( OUTFILE "\tvvis:\t",$vvisargs,"\n" ); + print( OUTFILE "\tvrad:\t",$vradargs,"\n" ); + print( OUTFILE "\n" ); + print( OUTFILE "\tBuild time: $strTime\n" ); + print( OUTFILE "\n" ); + } + + # Add the comments from the last checkin of this map + + system( "p4 changes -m 1 -s submitted -l $rootdir\\$maindir\\content\\$mod\\maps\\$mapname.vmf > comments.txt" ); + + open(INFILE, "comments.txt"); + my @comments = <INFILE>; + close(INFILE); + + if ( $buildbsp == 1 ) + { + print( OUTFILE "\tCheckin Comments: " ); + print( OUTFILE @comments,"\n\n" ); + print( OUTFILE "\tRebuilt bsp\n" ); + } + if ( $buildnodegraphs == 1 ) + { + print( OUTFILE "\n\tRebuilt nodegraph\n" ); + } + if ( $buildreslists == 1 ) + { + print( OUTFILE "\n\tRebuilt reslist\n" ); + } + + print( OUTFILE "Files:\n" ); + if ( $buildbsp == 1 ) + { + print( OUTFILE "\t//ValveGames/staging/game/$mod/maps/$mapname.bsp\n" ); + } + if ( $buildnodegraphs == 1 ) + { + # Copy up to fileserver + system( "copy $rootdir\\$maindir\\game\\$mod\\maps\\graphs\\$mapname.ain \"\\\\fileserver\\user\\xbox\\xbox_orange\\graphs\\$mod\\$mapname.ain\"" ); + } + if ( $buildreslists == 1 ) + { + print( OUTFILE "\t//ValveGames/staging/game/$mod/reslists_xbox/$mapname.lst\n" ); + } + close( OUTFILE ); + + # Check in the map + + system( "p4 submit -i < checkin.txt >> log.txt" ); + + # Send the build output in an email to the submitter + + my $email = "kerry\@valvesoftware.com"; + for ( @comments ) + { + if ( /Change \d* on \S* by (\w*)@/ ) + { + $email = join( '@', $1, "valvesoftware.com" ); + } + } + + open(INFILE, "$outfile"); + my @compileoutput = <INFILE>; + close(INFILE); + + my$smtp; + $smtp = Net::SMTP->new('exchange2.valvesoftware.com'); + + $smtp->mail("Mapbuilder"); + $smtp->to("$email"); + + $smtp->data(); + $smtp->datasend( "To: $email\n" ); + $smtp->datasend( "Subject: $mapname has finished compiling.\n" ); + $smtp->datasend( @comments ); + $smtp->datasend( @compileoutput ); + $smtp->dataend(); + + $smtp->quit; + + delete( $g_RunningCompiles{$outfile} ); + splice( @g_Finalizing ); + + chdir( "$rootdir\\main1\\src\\devtools\\mapbuild" ); +} + + +} # end while(1) + + +#---------------------------------------- +# Start a compile if there is an open slot +#---------------------------------------- +sub StartCompiles +{ + my $idx = 0; + + print( "Current maplist:\n" ); + for ( @g_CompileList ) + { + print( "$_\n" ); + } + + my $skipCt = 0; + while ( @g_CompileList > $skipCt && $running < $maxCompiles ) + { + my $nextMap = splice( @g_CompileList, 0, 1 ); + + # Make sure this map isn't already compiling in another slot + + my $duplicate = 0; + for ( values %g_RunningCompiles ) + { + $nextMap =~ /(\w*).vmf/; + + if ( /^$1$/ ) + { + system( "echo $nextMap already compiling - skipping for now. >> log.txt" ); + + push @g_CompileList, $nextMap; + ++$skipCt; + $duplicate = 1; + last; + } + } + + if ( $duplicate == 0 ) + { + Compile( $nextMap ); + + ++$running; + ++$idx; + + my $mapct = @g_CompileList; + system( "echo Maps waiting: $mapct >> log.txt" ); + } + } + + UpdateStats(); +} + +#----------------------------------------------------------- +# Compile a single map - this subroutine forks the process +#----------------------------------------------------------- +sub Compile +{ + # sync the rest of the content and bins. + + my $maindir = $g_MainDirs[$g_MainDirIdx]; + $g_MainDirIdx = ( $g_MainDirIdx + 1 ) % $maxCompiles; + + chdir( "$rootdir\\$maindir\\src\\devtools\\mapbuild" ); + + system( "p4 sync $rootdir\\$maindir\\game\\..." ); + system( "p4 sync $rootdir\\$maindir\\src\\..." ); + + + # parse the map and mod name from the full path + + my $fullpath = shift; + my $mod; + my $mapname; + if ( $fullpath =~ /(\w*)\\maps\\(\w*).vmf/ ) + { + $mod = $1; + $mapname = $2; + } + else + { + print( "Error getting map name and mod\n" ); + exit(); + } + + # Get the next output file. All output from the build tools + # (vbsp,vvis,vrad) will be redirected to this file. + + my $outfile = undef; + for ( @g_OutputFiles ) + { + unless ( defined $g_RunningCompiles{$_} ) + { + $outfile = $_; + last; + } + } + unless ( defined $outfile ) + { + my $filename = join( '', "buildqueue", $g_FilenameIdx++, ".txt" ); + push( @g_OutputFiles, $filename ); + $outfile = $filename; + } + + $g_RunningCompiles{$outfile} = $mapname; + + # Force-sync the vmf and bsp + + system( "echo Compiling $mod\\$mapname.bsp > $outfile" ); + system( "echo. >> log.txt" ); + system( "date /t >> log.txt" ); + system( "time /t >> log.txt" ); + system( "echo Compling $mod\\$mapname.bsp >> log.txt" ); + + system( "p4 sync -f ..\\..\\..\\content\\$mod\\maps\\$mapname.vmf >> log.txt" ); + system( "p4 sync -f $rootdir\\$maindir\\content\\$mod\\maps\\$mapname.vmf >> log.txt" ); + system( "p4 sync -f $rootdir\\$maindir\\game\\$mod\\maps\\$mapname.bsp >> log.txt" ); + + if ( $buildbsp == 1 ) + { + print "\nCompiling $mod\\$mapname.bsp\n"; + + # load the map compile args for this mod + + if( !open( INFILE, "$rootdir\\$maindir\\game\\$mod\\scripts\\mapautocompile.txt") ) + { + print( "Error opening autocompile.txt\n" ); + } + my @lines = <INFILE>; + close (INFILE); + + my $tool; + my $rest; + my $toolmap; + for ( @lines ) + { + if ( /map: (.*)/ ) + { + $toolmap = $1; + } + elsif ( /\s+(.+)/ ) + { + ($tool, $rest) = split ( /:\s*/, $1, 2 ); + $g_ToolArgs{$toolmap}{$tool} = $rest; + } + } + + # Open the bsp for edit + + system( "p4 edit $rootdir\\$maindir\\game\\$mod\\maps\\$mapname.bsp" ); + system( "p4 add $rootdir\\$maindir\\game\\$mod\\maps\\$mapname.bsp" ); + system( "del $rootdir\\$maindir\\game\\$mod\\maps\\$mapname.bsp" ); + } + + chdir( "$rootdir\\main1\\src\\devtools\\mapbuild" ); + + #-------------------------------------- + # fork this compile to a new process + #-------------------------------------- + + my $pid = fork(); + print "fork failed!" unless defined $pid; + if ( $pid != 0 ) + { + # I am the parent + return; + } + + my $strTime = ""; + my $vbspargs = ""; + my $vvisargs = ""; + my $vradargs = ""; + + chdir( "$rootdir\\$maindir\\src\\devtools\\mapbuild" ); + + if ( $buildbsp == 1 ) + { + # Compile + + # delete the bsp to ensure that vbsp succeeded + system( "del $rootdir\\$maindir\\content\\$mod\\maps\\$mapname.bsp" ); + + my $startTime = time; + + $vbspargs = compileTool( $maindir, $mod, $mapname, "vbsp", $outfile ); + $vvisargs = compileTool( $maindir, $mod, $mapname, "vvis", $outfile ); + $vradargs = compileTool( $maindir, $mod, $mapname, "vrad", $outfile ); + + my $totalTime = time - $startTime; + my $hours = int($totalTime / 3600); + $totalTime -= ($hours * 3600); + my $minutes = int( $totalTime / 60 ); + $totalTime -= ($minutes * 60); + my $seconds = $totalTime; + $strTime = sprintf( "%02d:%02d:%02d", $hours, $minutes, $seconds ); + } + + chdir( "$rootdir\\main1\\src\\devtools\\mapbuild" ); + + # Add the map's name to the finalize list + + while( !open( OUTFILE, ">>finalize.txt" ) ) {} + print OUTFILE "Finalize: $mod $mapname\n"; + print OUTFILE "maindir: $maindir\n"; + + if ( $buildbsp == 1 ) + { + # Output the compile args and time + + print OUTFILE "vbspargs: $vbspargs\n"; + print OUTFILE "vvisargs: $vvisargs\n"; + print OUTFILE "vradargs: $vradargs\n"; + print OUTFILE "time: $strTime\n"; + } + + print OUTFILE "outfile: $outfile\n"; + + close( OUTFILE ); + + # This child is finished + + exit(); +} + +#---------------------------------------- +# Run a map build tool (vbsp, vvis, vrad) +#---------------------------------------- +sub compileTool +{ + my $maindir = shift; + my $mod = shift; + my $map = shift; + my $tool = shift; + my $outfile = shift; + + # Load the compile arguments that were parsed from mapautocompile.txt. + # If the map has its own custom args, then use those - otherwise, + # just use the default set. + + my $toolArgs = $g_ToolArgs{"default"}{$tool}; + if ( exists $g_ToolArgs{$map}{$tool} ) + { + $toolArgs = $g_ToolArgs{$map}{$tool}; + } + + system( "echo. >> $outfile" ); + system( "time /t >> $outfile" ); + system( "echo Starting $tool for $map >> $outfile" ); + system( "echo. >> $outfile" ); + + # HACK: There is a crash bug when using -both in vrad. If the -both + # flags is present, remove it and do two vrad compiles with -ldr and -hdr. + + if ( $tool =~ /vrad/ && $toolArgs =~ /-both/ ) + { + system( "echo Found -both argument. Splitting into -ldr and -hdr >> $outfile" ); + my $newArgs = $toolArgs; + $newArgs =~ s/-both //; + + system( "$rootdir\\$maindir\\game\\bin\\$tool -vproject $rootdir\\$maindir\\game\\$mod $newArgs -ldr $rootdir\\$maindir\\content\\$mod\\maps\\$map >> $outfile" ); + system( "$rootdir\\$maindir\\game\\bin\\$tool -vproject $rootdir\\$maindir\\game\\$mod $newArgs -hdr $rootdir\\$maindir\\content\\$mod\\maps\\$map >> $outfile" ); + } + else + { + system( "$rootdir\\$maindir\\game\\bin\\$tool -vproject $rootdir\\$maindir\\game\\$mod $toolArgs $rootdir\\$maindir\\content\\$mod\\maps\\$map >> $outfile" ); + } + + # copy the new bsp into the maps directory + + system( "copy /Y $rootdir\\$maindir\\content\\$mod\\maps\\$map.bsp $rootdir\\$maindir\\game\\$mod\\maps" ); + + system( "echo. >> $outfile" ); + system( "echo Finished >> $outfile" ); + + return $toolArgs; +} + + +#----------------------------------- +# Check if this map should build now +#----------------------------------- +sub syncChangedMaps +{ + # TODO: Get these mod names from a file? + my @mods = ( "ep2", "portal", "episodic", "hl2", "tf" ); + + # Query perforce for changed maps by doing a speculative sync. This generates a list + # of files that it WOULD sync, without actually doing the sync. + # BUG: This doesn't return new files that have been added - don't know why yet. + + my @maps; + for my $mod ( @mods ) + { + push ( @maps, readpipe "p4 sync -n ..\\..\\..\\content\\$mod\\maps\\*.vmf" ); + } + + # forcelist.txt allows maps to be manually added to the build. + + open(INFILE, "forcelist.txt"); + my @forcemaps = <INFILE>; + close( INFILE ); + + system( "echo. > forcelist.txt" ); + + # Run through the list of maps to see if any should be built + + for( @maps) + { + if( /updating (.*)|adding (.*)/ ) + { + # Check if this map should be built now + if ( shouldBuild( $1 ) == 0 ) + { + next; + } + + # Add this map name to the build list + + system( "p4 sync -f $1" ); + push( @g_MapsToBuild, $1 ); + system( "echo Adding to the build list: $1 >> log.txt" ); + } + } + + for( @forcemaps ) + { + if ( /\.vmf/ ) + { + # Add this map name to the build list + + $_ =~ /(.*)/; + system( "p4 sync -f $1" ); + push( @g_MapsToBuild, $1 ); + system( "echo Forcing add to the build list: $1 >> log.txt" ); + } + } +} + + +#----------------------------------- +# Check if this map should build now +#----------------------------------- +sub shouldBuild +{ + my $map = shift; + + # if the map line contains the force flag, build the map + + if ( $map =~ /-forcebuild/ ) + { + return 1; + } + + # Get the comments from the last checkin of this map + # and look for the autocompile keyword + + my @comments = readpipe "p4 changes -m 1 -s submitted -l $map"; + + for( @comments ) + { + if ( /autocompile/i ) + { + return 1; + } + } + + return 0; +} + + +#---------------------------------------------------------------------------------- +# Hacky little routine to build an html file that contains the current state +# of the autocompiler (running, maps building, maps waiting, etc.) and a frameset +# that shows the compile output for each map that's currently building. This way +# users of the autocompiler can keep an eye on their map's progress. +#---------------------------------------------------------------------------------- +sub UpdateStats +{ + my $noCompiles = 0; + my $noWaiting = 0; + if ( keys( %g_RunningCompiles ) == 0 && @g_Finalizing == 0 ) + { + $noCompiles = 1; + } + if ( @g_CompileList == 0 ) + { + $noWaiting = 1; + } + + # Start building the main html page + + open( OUTFILE, ">buildlist.htm" ); + print( OUTFILE "<html>\n" ); + print( OUTFILE "<META HTTP-EQUIV=\"Refresh\" CONTENT=\"10; URL=buildlist.htm\">\n" ); + print( OUTFILE "Mapbuilder state: <font color=#009900><b>" ); + if ( $noCompiles == 1 && $noWaiting == 1 ) + { + print( OUTFILE "Ready</b></font><br>\n" ); + } + else + { + print( OUTFILE "RUNNING</b></font><br>\n" ); + } + print( OUTFILE "Max simultaneous compiles: $maxCompiles\n" ); + print( OUTFILE "<p>\n" ); + print( OUTFILE "<b>Active:</b>\n<p>\n" ); + + # Show a list of maps that are currently compiling + + my $filect = 0; + if ( $noCompiles == 1 ) + { + print( OUTFILE "None" ); + } + else + { + for ( @g_OutputFiles ) + { + if ( defined( $g_RunningCompiles{$_} ) ) + { + my $testname = $g_RunningCompiles{$_}; + my $filename = $_; + if ( $filename =~ /(\w*\.txt)/ ) + { + $filename = $1; + } + my $finishing = 0; + + for ( @g_Finalizing ) + { + if ( /$testname/ ) + { + $finishing = 1; + } + } + + print( OUTFILE "<a href=\"$filename\" TARGET=\"_parent\">$testname.bsp</a>" ); + ++$filect; + + if ( $finishing == 1 ) + { + print( OUTFILE " - Finishing (cubemaps, reslist, nodegraph.)\n<br>\n" ); + } + else + { + print( OUTFILE " - Compiling\n<br>\n" ); + } + } + } + } + + # Show a list of maps that are waiting to compile + + print( OUTFILE "<p>\n<b>Waiting:</b>\n<p>\n" ); + if ( $noWaiting == 1 ) + { + print( OUTFILE "None" ); + } + else + { + for ( @g_CompileList ) + { + /(\w*).vmf/; + print( OUTFILE "$1.bsp\n<br>\n" ); + } + } + print( OUTFILE "</html>\n" ); + close( OUTFILE ); + + # create the main page + + open( OUTFILE, ">buildqueue.htm" ); + print( OUTFILE "<html>\n" ); + print( OUTFILE "<META HTTP-EQUIV=\"Refresh\" CONTENT=\"30; URL=buildqueue.htm\">\n" ); + my $size = 0; + if ( $filect != 0 ) + { + print( OUTFILE "<frameset rows=\"30%" ); + $size = 70 / $filect; + } + else + { + print( OUTFILE "<frameset rows=\"100%" ); + } + for ( my $ct = 0; $ct < $filect; ++$ct ) + { + print( OUTFILE ",$size%" ); + } + print( OUTFILE "\">\n" ); + print( OUTFILE "<frame src=\"buildlist.htm\">\n" ); + for ( @g_OutputFiles ) + { + if ( defined( $g_RunningCompiles{$_} ) ) + { + my $filename = $_; + if ( $filename =~ /(\w*\.txt)/ ) + { + $filename = $1; + } + print( OUTFILE "<frame src=\"$filename\">\n" ); + } + } + print( OUTFILE "</frameset>\n" ); + print( OUTFILE "</html>\n" ); + + close( OUTFILE ); +}
\ No newline at end of file diff --git a/devtools/mapbuild/buildall.bat b/devtools/mapbuild/buildall.bat new file mode 100644 index 0000000..471ac1c --- /dev/null +++ b/devtools/mapbuild/buildall.bat @@ -0,0 +1,22 @@ +echo off +:start + +@rem Sleep time is arbitrary, it just prevents us from spamming Perforce. + +call buildmod ep2 -all + +sleep 15 + +call buildmod portal -all + +sleep 15 + +call buildmod hl2 -all + +sleep 15 + +call buildmod tf -all + +sleep 15 + +goto start diff --git a/devtools/mapbuild/buildmod.bat b/devtools/mapbuild/buildmod.bat new file mode 100644 index 0000000..ef6b4f0 --- /dev/null +++ b/devtools/mapbuild/buildmod.bat @@ -0,0 +1,74 @@ +echo off + +if "%1" == "" goto usage + +@rem ********************************************************************** +@REM If the mapbuild directory ever moves out of src\devools, +@REM update this relative path to the tree's "main" directory. +@rem ********************************************************************** +set maindir=..\..\.. + +if not exist %maindir%\game\%1 goto usage + +set vproject=%maindir%\game\%1 + +@rem ********************************************************************** +@rem build options are -reslist, -nodegraph, -bsp, and -forcebuild. +@rem The "-forcebuild" flag is used to build all changed maps, even if they +@rem didn't use the 'autocompile' keyword. This is currently being used +@rem to build only reslists and nodegraphs of changed maps each night. +@rem ********************************************************************** + +set defaultflags=%2 +set buildflags=%defaultflags% + +set TIME= +for /F "tokens=1-4 delims=:., " %%a in ("%TIME%") do set TIME=%%a%%b%%c +@rem if %TIME% GTR 030000 set buildflags="-forcebuild -reslist -nodegraph" +@rem if %TIME% GTR 060000 set buildflags=%defaultflags% + +@rem ********************************************************************** +@rem Generate a list of changed vmf's without actually syncing them +@rem ********************************************************************** + +p4 sync -n %maindir%\content\%1\maps\*.vmf >> %1_buildlist.txt + +@rem ********************************************************************** +@rem Sync specified vmf's only. If "-forcebuild" flag is set, +@rem all changed vmf's will be synced. Otherwise, only maps +@rem that had the "autocompile" keyword in the checkin comments +@rem will be synced. +@rem ********************************************************************** + +syncChangedMaps.pl %1 %buildflags% + +if errorlevel 1 goto end + +@rem ********************************************************************** +@rem Sync all other files +@rem ********************************************************************** + +p4 sync %maindir%\game\... +p4 sync %maindir%\src\... + +@rem ********************************************************************** +@rem Build bsp's, cubemaps, and checkin +@rem ********************************************************************** + +echo compiling %1 maps >> log.txt +time /t >> log.txt + +buildMaps.pl -mod %1 -maindir %maindir% %buildflags% %2 + +echo Finished %1 >> log.txt +time /t >> log.txt +echo. >> log.txt +echo. >> log.txt + +goto end + +:usage +echo Usage: buildmod [modname] + +:end +echo > %1_buildlist.txt diff --git a/devtools/mapbuild/readme.txt b/devtools/mapbuild/readme.txt new file mode 100644 index 0000000..5e27dd9 --- /dev/null +++ b/devtools/mapbuild/readme.txt @@ -0,0 +1,45 @@ +These map autobuild scripts are set up to detect whever a vmf has been checked into Perforce, then check out the bsp, rebuild the bsp with cubemaps, and check it back in. It's a work-in-progress, so there is room for improvements (better error-handling, building on multiple machines, etc.). + +If you have any questions or problems, contact me at [email protected] + + +To set up a build machine to use the autobuild scripts: + + +1. The build machine must have a Perforce client, and be fully synced to the game tree and the content/<mod>/maps directory. To test that your environment is setup correctly, the build machine must be able to do the following: + +a) Run your full game in ldr and hdr. +b) Compile a bsp from a vmf (Run vbsp, vvis, vrad - make sure 'vproject' is set to the correct mod.) +c) Check files in and out from the tree that you will be building. +c) Run 'p4.exe' from the command line. (Make sure the build machine's DEFAULT Perforce client is set to the Client that will be doing the checkins.) +d) Run 'perl.exe' from the command line + + +2. Sync to the 'mapbuild' directory, located at src/devtools/mapbuild. + + +3. Copy 'mapautocompile.txt'from game/hl2/scripts into your game/<mod>/scripts directory. Delete all map entries except for "default". + +This file specifies the command line arguments that should be passed to vbsp, vvis, and vrad. +The 'defualt' block of arguments is used for any maps that don't have their own arguments specified. +If a map has its own arguments, they will override the default arguments. +NOTE: This file isn't required - without it, the tools will run with no command line arguments. This file should be checked into Perforce to let level designers tweak their own compile settings. + + +4. For each mod you want to autobuild, add this line to 'buildall.bat': + +call buildmod <mod> <relative path to gamedir> + +NOTE: The 'Sleep' call is just there to prevent the script from spamming Perforce. The sleep time is entirely up to you. + + +5. In 'buildMaps.pl', search for the comment "# Generate the checkin file". This is where the text file is created that Perforce needs to do a checkin from the command line. You will need to update the following fields to match the setup of the build machine: + +'Client: <default client>' +'User: <name>' +'File: <file path in Perforce>' + +NOTE: To see exactly what the values of these fields should be, you can type 'p4 submit' on the command line to see a Perforce-generated checkin file. + + +6. Run 'buildall.bat'. Check in a vmf and make sure the entire process runs successfully. diff --git a/devtools/mapbuild/syncChangedMaps.pl b/devtools/mapbuild/syncChangedMaps.pl new file mode 100644 index 0000000..9122947 --- /dev/null +++ b/devtools/mapbuild/syncChangedMaps.pl @@ -0,0 +1,103 @@ +use strict; + +my $forceBuild = 0; +if ( $ARGV[1] =~ /-forcebuild/ ) +{ + $forceBuild = 1; +} + +# Read in the list of changed maps + +my $filename = "buildlist.txt"; + +open(INFILE, $filename); +my @maps = <INFILE>; +close( INFILE ); + +# forcelist.txt allows maps to be manually added to the build. + +open(INFILE, "forcelist.txt"); +my @forcemaps = <INFILE>; +close( INFILE ); + +my $retval = 1; + +system( "echo. > $filename" ); +system( "echo. > forcelist.txt" ); + +# Run through the list of maps to see if any should be built + +for( @maps) +{ + if( /updating (.*)|adding (.*)/ ) + { + # Check if this map should be built now + if ( shouldBuild( $1 ) == 0 ) + { + next; + } + + # Add this map name to the build list + + system( "p4 sync -f $1 >> $filename" ); + system( "echo Adding to the build list: $1 >> log.txt" ); + $retval = 0; + } +} + +for( @forcemaps ) +{ + if ( /\.vmf/ ) + { + # Add this map name to the build list + + $_ =~ /(.*)/; + system( "p4 sync -f $1 >> $filename" ); + system( "echo Forcing add to the build list: $1 >> log.txt" ); + $retval = 0; + } +} + +exit $retval; + +#----------------------------------- +# Check if this map should build now +#----------------------------------- +sub shouldBuild +{ + my $map = shift; + + # if command line flag was set, build the map + + if ( $forceBuild == 1 ) + { + return 1; + } + + # if the map line contains the force flag, build the map + + if ( $map =~ /-forcebuild/ ) + { + return 1; + } + + # Dump the comments from the last checkin of this map + + system( "p4 changes -m 1 -s submitted -l $map > comments.txt" ); + + # parse comments for the autocompile keyword + + open(INFILE, "comments.txt"); + my @comments = <INFILE>; + close(INFILE); + + for( @comments ) + { + if ( /autocompile/i ) + { + return 1; + } + } + + return 0; +} |