#!/usr/bin/env python2 import argparse import hashlib import os from pandac.PandaModules import * import shutil parser = argparse.ArgumentParser() parser.add_argument('--distribution', default='en', help='The distribution string.') parser.add_argument('--build-dir', default='build', help='The directory in which to store the build files.') parser.add_argument('--src-dir', default='..', help='The directory of the Toontown Unlimited source code.') parser.add_argument('--server-ver', default='unlimited-dev', help='The server version of this build.') parser.add_argument('--build-mfs', action='store_true', help='When present, the resource multifiles will be built.') parser.add_argument('--resources-dir', default='../resources', help='The directory of the Toontown Unlimited resources.') parser.add_argument('--config-dir', default='../config/release', help='The directory of the Toontown Unlimited configuration files.') parser.add_argument('--include', '-i', action='append', help='Explicitly include this file in the build.') parser.add_argument('--exclude', '-x', action='append', help='Explicitly exclude this file from the build.') parser.add_argument('--vfs', action='append', help='Add this file to the virtual file system at runtime.') parser.add_argument('modules', nargs='*', default=['otp', 'toontown'], help='The Toontown Unlimited modules to be included in the build.') args = parser.parse_args() print 'Preparing the client...' # Create a clean directory to store the build files in: if os.path.exists(args.build_dir): shutil.rmtree(args.build_dir) os.mkdir(args.build_dir) print 'Build directory = ' + args.build_dir # Copy the provided Toontown Unlimited modules: def minify(f): """ Returns the "minified" file data with removed __debug__ code blocks. """ data = '' debugBlock = False # Marks when we're in a __debug__ code block. elseBlock = False # Marks when we're in an else code block. # The number of spaces in which the __debug__ condition is indented: indentLevel = 0 for line in f: thisIndentLevel = len(line) - len(line.lstrip()) if ('if __debug__:' not in line) and (not debugBlock): data += line continue elif 'if __debug__:' in line: debugBlock = True indentLevel = thisIndentLevel continue if thisIndentLevel <= indentLevel: if 'else' in line: elseBlock = True continue if 'elif' in line: line = line[:thisIndentLevel] + line[thisIndentLevel+2:] data += line debugBlock = False elseBlock = False indentLevel = 0 continue if elseBlock: data += line[4:] return data for module in args.modules: print 'Writing module...', module for root, folders, files in os.walk(os.path.join(args.src_dir, module)): outputDir = root.replace(args.src_dir, args.build_dir) if not os.path.exists(outputDir): os.mkdir(outputDir) for filename in files: if filename not in args.include: if not filename.endswith('.py'): continue if filename.endswith('UD.py'): continue if filename.endswith('AI.py'): continue if filename in args.exclude: continue with open(os.path.join(root, filename), 'r') as f: data = minify(f) with open(os.path.join(outputDir, filename), 'w') as f: f.write(data) # Let's write game_data.py now. game_data.py is a compile-time generated # collection of data that will be used by the game at runtime. It contains the # PRC file data, and (stripped) DC file: # First, we need to add the configuration pages: configData = [] with open('../config/general.prc') as f: configData.append(f.read()) configFileName = args.distribution + '.prc' configFilePath = os.path.join(args.config_dir, configFileName) print 'Using configuration file: ' + configFilePath with open(configFilePath) as f: data = f.readlines() # Replace server-version definitions with the desired server version: for i, line in enumerate(data): if 'server-version' in line: data[i] = 'server-version ' + args.server_ver # Add our virtual file system data: data.append('\n# Virtual file system...\nmodel-path /\n') for filepath in args.vfs: data.append('vfs-mount %s /\n' % filepath) configData.append('\n'.join(data)) # Next, we need the DC file: dcData = '' filepath = os.path.join(args.src_dir, 'astron/dclass') for filename in os.listdir(filepath): if filename.endswith('.dc'): fullpath = str(Filename.fromOsSpecific(os.path.join(filepath, filename))) print 'Reading %s...' % fullpath with open(fullpath, 'r') as f: data = f.read() for line in data.split('\n'): if 'import' in line: data = data.replace(line + '\n', '') dcData += data # Finally, write our data to game_data.py: print 'Writing game_data.py...' gameData = 'CONFIG = %r\nDC = %r\n' with open(os.path.join(args.build_dir, 'game_data.py'), 'wb') as f: f.write(gameData % (configData, dcData.strip())) # We have all of the code gathered together. Let's create the multifiles now: if args.build_mfs: print 'Building multifiles...' dest = os.path.join(args.build_dir, 'resources') if not os.path.exists(dest): os.mkdir(dest) dest = os.path.realpath(dest) os.chdir(args.resources_dir) for phase in os.listdir('.'): if not phase.startswith('phase_'): continue if not os.path.isdir(phase): continue filename = phase + '.mf' print 'Writing...', filename filepath = os.path.join(dest, filename) os.system('multify -c -f "%s" "%s"' % (filepath, phase)) print 'Done preparing the client.'