Advertisement
Guest User

Untitled

a guest
Jul 24th, 2014
184
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 7.94 KB | None | 0 0
  1. import argparse
  2. import hashlib
  3. import os
  4. import shutil
  5. import subprocess
  6.  
  7. from pandac.PandaModules import *
  8. import pytz
  9.  
  10.  
  11. parser = argparse.ArgumentParser()
  12. parser.add_argument('--distribution', default='en',
  13.                     help='The distribution token.')
  14. parser.add_argument('--build-dir', default='build',
  15.                     help='The directory in which to store the build files.')
  16. parser.add_argument('--src-dir', default='..',
  17.                     help='The directory of the Toontown Infinite source code.')
  18. parser.add_argument('--server-ver', default='tti-REVISION',
  19.                     help='The server version of this build.\n'
  20.                          'REVISION tokens will be replaced with the current Git revision string.')
  21. parser.add_argument('--build-mfs', action='store_true',
  22.                     help='When present, multifiles will be built.')
  23. parser.add_argument('--resources-dir', default='../resources',
  24.                     help='The directory of the Toontown Infinite resources.')
  25. parser.add_argument('modules', nargs='*', default=['shared', 'infinite'],
  26.                     help='The Toontown Infinite modules to be included in the build.')
  27. args = parser.parse_args()
  28.  
  29. print 'Preparing the client...'
  30.  
  31. # Create a clean build directory for us to store our build material:
  32. if not os.path.exists(args.build_dir):
  33.     os.mkdir(args.build_dir)
  34. print 'Build directory = {0}'.format(args.build_dir)
  35.  
  36. # This next part is only required if the invoker wants to include the Git
  37. # revision string in their server version:
  38. revision = ''
  39. if 'REVISION' in args.server_ver:
  40.     # If we don't have Git on our path, let's attempt to add it:
  41.     paths = (
  42.         '{0}\\Git\\bin'.format(os.environ['ProgramFiles']),
  43.         '{0}\\Git\\cmd'.format(os.environ['ProgramFiles'])
  44.     )
  45.     for path in paths:
  46.         if path not in os.environ['PATH']:
  47.             os.environ['PATH'] += ';' + path
  48.  
  49.     # Now, let's get that revision string:
  50.     revision = subprocess.Popen(
  51.         ['git', 'rev-parse', 'HEAD'],
  52.         stdout=subprocess.PIPE,
  53.         cwd=args.src_dir).stdout.read().strip()[:7]
  54.  
  55. # Replace any REVISION tokens in the server version:
  56. serverVersion = args.server_ver.replace('REVISION', revision)
  57. print 'serverVersion = {0}'.format(serverVersion)
  58.  
  59. # Copy the provided Toontown Infinite modules:
  60.  
  61. # NonRepeatableRandomSourceUD.py, and NonRepeatableRandomSourceAI.py are
  62. # required to be included. This is because they are explicitly imported by the
  63. # DC file:
  64. includes = ('NonRepeatableRandomSourceUD.py', 'NonRepeatableRandomSourceAI.py')
  65.  
  66. # This is a list of explicitly excluded files:
  67. excludes = ('ServiceStart.py')
  68.  
  69. def minify(f):
  70.     """
  71.    Returns the "minified" file data with removed __debug__ code blocks.
  72.    """
  73.  
  74.     data = ''
  75.  
  76.     debugBlock = False  # Marks when we're in a __debug__ code block.
  77.     elseBlock = False  # Marks when we're in an else code block.
  78.  
  79.     # The number of spaces in which the __debug__ condition is indented:
  80.     indentLevel = 0
  81.  
  82.     for line in f:
  83.         thisIndentLevel = len(line) - len(line.lstrip())
  84.         if ('if __debug__:' not in line) and (not debugBlock):
  85.             data += line
  86.             continue
  87.         elif 'if __debug__:' in line:
  88.             debugBlock = True
  89.             indentLevel = thisIndentLevel
  90.             continue
  91.         if thisIndentLevel <= indentLevel:
  92.             if 'else' in line:
  93.                 elseBlock = True
  94.                 continue
  95.             if 'elif' in line:
  96.                 line = line[:thisIndentLevel] + line[thisIndentLevel+2:]
  97.             data += line
  98.             debugBlock = False
  99.             elseBlock = False
  100.             indentLevel = 0
  101.             continue
  102.         if elseBlock:
  103.             data += line[4:]
  104.  
  105.     return data
  106.  
  107. for module in args.modules:
  108.     print 'Writing module...', module
  109.     for root, folders, files in os.walk(os.path.join(args.src_dir, module)):
  110.         outputDir = root.replace(args.src_dir, args.build_dir)
  111.         if not os.path.exists(outputDir):
  112.             os.mkdir(outputDir)
  113.         for filename in files:
  114.             if filename not in includes:
  115.                 if not filename.endswith('.py'):
  116.                     continue
  117.                 if filename.endswith('UD.py'):
  118.                     continue
  119.                 if filename.endswith('AI.py'):
  120.                     continue
  121.                 if filename in excludes:
  122.                     continue
  123.             with open(os.path.join(root, filename), 'r') as f:
  124.                 data = minify(f)
  125.             with open(os.path.join(outputDir, filename), 'w') as f:
  126.                 f.write(data)
  127.  
  128. # Let's write game_data.py now. game_data.py is a compile-time generated
  129. # collection of data that will be used by the game at runtime. It contains the
  130. # PRC file data, (stripped) DC file, and time zone info.
  131.  
  132. # First, we need the PRC file data:
  133. configFileName = 'config_{0}.prc'.format(args.distribution)
  134. configData = []
  135. with open(os.path.join(args.src_dir, 'config', configFileName)) as f:
  136.     data = f.read()
  137.     configData.append(data.replace('SERVER_VERSION', serverVersion))
  138. print 'Using config file: {0}'.format(configFileName)
  139.  
  140. # Next, we need the (stripped) DC file:
  141. dcFile = DCFile()
  142. filepath = os.path.join(args.src_dir, 'astron')
  143. for filename in os.listdir(filepath):
  144.     if filename.endswith('.dc'):
  145.         dcFile.read(Filename.fromOsSpecific(os.path.join(filepath, filename)))
  146. dcStream = StringStream()
  147. dcFile.write(dcStream, True)
  148. dcData = dcStream.getData()
  149.  
  150. # Now, collect our timezone info:
  151. zoneInfo = {}
  152. for timezone in pytz.all_timezones:
  153.     zoneInfo['zoneinfo/' + timezone] = pytz.open_resource(timezone).read()
  154.  
  155. # Finally, write our data to game_data.py:
  156. print 'Writing game_data.py...'
  157. gameData = '''\
  158. CONFIG = %r
  159. DC = %r
  160. ZONEINFO = %r'''
  161. with open(os.path.join(args.build_dir, 'game_data.py'), 'w') as f:
  162.     f.write(gameData % (configData, dcData, zoneInfo))
  163.  
  164.  
  165. def getDirectoryMD5Hash(directory):
  166.     def _updateChecksum(checksum, dirname, filenames):
  167.         for filename in sorted(filenames):
  168.             path = os.path.join(dirname, filename)
  169.             if os.path.isfile(path):
  170.                 fh = open(path, 'rb')
  171.                 while True:
  172.                     buf = fh.read(4096)
  173.                     if not buf:
  174.                         break
  175.                     checksum.update(buf)
  176.                 fh.close()
  177.     checksum = hashlib.md5()
  178.     directory = os.path.normpath(directory)
  179.     if os.path.exists(directory):
  180.         if os.path.isdir(directory):
  181.             os.path.walk(directory, _updateChecksum, checksum)
  182.         elif os.path.isfile(directory):
  183.             _updateChecksum(
  184.                 checksum, os.path.dirname(directory),
  185.                 os.path.basename(directory))
  186.     return checksum.hexdigest()
  187.  
  188.  
  189. # We have all of the code gathered together. Let's create the multifiles now:
  190. if args.build_mfs:
  191.     print 'Building multifiles...'
  192.     dest = os.path.join(args.build_dir, 'resources')
  193.     if not os.path.exists(dest):
  194.         os.mkdir(dest)
  195.     dest = os.path.realpath(dest)
  196.     os.chdir(args.resources_dir)
  197.     if not os.path.exists('local-patcher.ver'):
  198.         with open('local-patcher.ver', 'w') as f:
  199.             f.write('RESOURCES = {}')
  200.     with open('local-patcher.ver', 'r') as f:
  201.         exec(f.read())
  202.     for phase in os.listdir('.'):
  203.         if not phase.startswith('phase_'):
  204.             continue
  205.         if not os.path.isdir(phase):
  206.             continue
  207.         phaseMd5 = getDirectoryMD5Hash(phase)
  208.         if phase in RESOURCES:
  209.             if RESOURCES[phase] == phaseMd5:
  210.                 continue
  211.         filename = phase + '.mf'
  212.         print 'Writing...', filename
  213.         filepath = os.path.join(dest, filename)
  214.         os.system('multify -c -f {0} {1}'.format(filepath, phase))
  215.         RESOURCES[phase] = phaseMd5
  216.     with open('local-patcher.ver', 'w') as f:
  217.         f.write('RESOURCES = %r' % RESOURCES)
  218.  
  219. print 'Done preparing the client.'
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement