Direct Notify not found (although it exists)


I have a Python script that is supposed to build up my client.

Running them one after another you cleanse .pyc files, prepare the client, write files, build client and DONE.

I am trying to prepare_client.

Code of the Python file:

import argparse
import hashlib
import os
import shutil
import subprocess

from pandac.PandaModules import *
import pytz

parser = argparse.ArgumentParser()
parser.add_argument('--distribution', default='en',
                    help='The distribution token.')
parser.add_argument('--build-dir', default='build',
                    help='The directory in which to store the build files.')
parser.add_argument('--src-dir', default='toontown',
                    help='The directory of the Toontown source code.')
parser.add_argument('--server-ver', default='tti-REVISION',
                    help='The server version of this build.\n'
                         'REVISION tokens will be replaced with the current Git revision string.')
parser.add_argument('--build-mfs', action='store_true',
                    help='When present, multifiles will be built.')
parser.add_argument('--resources-dir', default='RetroResources',
                    help='The directory of the Toontown resources.')
parser.add_argument('modules', nargs='*', default=['shared', 'infinite'],
                    help='The Toontown modules to be included in the build.')
args = parser.parse_args()

print 'Preparing the client...'

# Create a clean build directory for us to store our build material:
if not os.path.exists(args.build_dir):
print 'Build directory = {0}'.format(args.build_dir)

# This next part is only required if the invoker wants to include the Git
# revision string in their server version:
revision = ''
if 'REVISION' in args.server_ver:
    # If we don't have Git on our path, let's attempt to add it:
    paths = (
    for path in paths:
        if path not in os.environ['PATH']:
            os.environ['PATH'] += ';' + path

    # Now, let's get that revision string:
    revision = subprocess.Popen(
        ['git', 'rev-parse', 'HEAD'],

# Replace any REVISION tokens in the server version:
serverVersion = args.server_ver.replace('REVISION', revision)
print 'serverVersion = {0}'.format(serverVersion)

# Copy the provided Toontown modules:

#, and are
# required to be included. This is because they are explicitly imported by the
# DC file:
includes = ('', '')

# This is a list of explicitly excluded files:
excludes = ('')

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
        elif 'if __debug__:' in line:
            debugBlock = True
            indentLevel = thisIndentLevel
        if thisIndentLevel <= indentLevel:
            if 'else' in line:
                elseBlock = True
            if 'elif' in line:
                line = line[:thisIndentLevel] + line[thisIndentLevel+2:]
            data += line
            debugBlock = False
            elseBlock = False
            indentLevel = 0
        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):
        for filename in files:
            if filename not in includes:
                if not filename.endswith('.py'):
                if filename.endswith(''):
                if filename.endswith(''):
                if filename in excludes:
            with open(os.path.join(root, filename), 'r') as f:
                data = minify(f)
            with open(os.path.join(outputDir, filename), 'w') as f:

# Let's write now. is a compile-time generated
# collection of data that will be used by the game at runtime. It contains the
# PRC file data, (stripped) DC file, and time zone info.

# First, we need the PRC file data:
configFileName = 'config_{0}.prc'.format(args.distribution)
configData = []
with open(os.path.join(args.src_dir, 'config', configFileName)) as f:
    data =
    configData.append(data.replace('SERVER_VERSION', serverVersion))
print 'Using config file: {0}'.format(configFileName)

# Next, we need the (stripped) DC file:
dcFile = DCFile()
filepath = os.path.join(args.src_dir, 'astron')
for filename in os.listdir(filepath):
    if filename.endswith('.dc'):, filename)))
dcStream = StringStream()
dcFile.write(dcStream, True)
dcData = dcStream.getData()

# Now, collect our timezone info:
zoneInfo = {}
for timezone in pytz.all_timezones:
    zoneInfo['zoneinfo/' + timezone] = pytz.open_resource(timezone).read()

# Finally, write our data to
print 'Writing'
gameData = '''\
DC = %r
ZONEINFO = %r'''
with open(os.path.join(args.build_dir, ''), 'w') as f:
    f.write(gameData % (configData, dcData, zoneInfo))

def getDirectoryMD5Hash(directory):
    def _updateChecksum(checksum, dirname, filenames):
        for filename in sorted(filenames):
            path = os.path.join(dirname, filename)
            if os.path.isfile(path):
                fh = open(path, 'rb')
                while True:
                    buf =
                    if not buf:
    checksum = hashlib.md5()
    directory = os.path.normpath(directory)
    if os.path.exists(directory):
        if os.path.isdir(directory):
            os.path.walk(directory, _updateChecksum, checksum)
        elif os.path.isfile(directory):
                checksum, os.path.dirname(directory),
    return checksum.hexdigest()

# 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):
    dest = os.path.realpath(dest)
    if not os.path.exists('local-patcher.ver'):
        with open('local-patcher.ver', 'w') as f:
            f.write('RESOURCES = {}')
    with open('local-patcher.ver', 'r') as f:
    for phase in os.listdir('.'):
        if not phase.startswith('phase_'):
        if not os.path.isdir(phase):
        phaseMd5 = getDirectoryMD5Hash(phase)
        if phase in RESOURCES:
            if RESOURCES[phase] == phaseMd5:
        filename = phase + '.mf'
        print 'Writing...', filename
        filepath = os.path.join(dest, filename)
        os.system('multify -c -f {0} {1}'.format(filepath, phase))
        RESOURCES[phase] = phaseMd5
    with open('local-patcher.ver', 'w') as f:
        f.write('RESOURCES = %r' % RESOURCES)

print 'Done preparing the client.'

Here’s the error code I get:

Traceback (most recent call last):
  File "", line 7, in <module>
    from pandac.PandaModules import *
  File "C:\Panda3D-1.9.0\pandac\", line 20, in <module>
    from panda3dDirectModules import *
  File "C:\Panda3D-1.9.0\pandac\", line 5, in <module>
    from direct.directnotify.DirectNotifyGlobal import directNotify
ImportError: No module named directnotify.DirectNotifyGlobal

Panda3D-1.9.0/direct/directnotify/ exists… I do not understand the error.

When trying build:

import argparse
import os

parser = argparse.ArgumentParser()
parser.add_argument('--panda3d-dir', default='C:/Panda3D-1.9.0',
                    help='The path to the Panda3D build to use for this distribution.')
parser.add_argument('--main-module', default='infinite.base.ClientStartDist',
                    help='The path to the instantiation module.')
parser.add_argument('--build-secret', default='dev',
                    help='The secret that will be used to encrypt the build.')
parser.add_argument('modules', nargs='*', default=['shared', 'infinite'],
                    help='The Toontown modules to be included in the build.')
args = parser.parse_args()

print 'Building the client...'


cmd = os.path.join(args.panda3d_dir, 'python/ppython.exe')  # ppython
cmd += ' -m direct.showutil.pfreeze'  # pfreeze
args.modules.extend(['direct', 'pandac'])
for module in args.modules:
    cmd += ' -i {0}.*.*'.format(module)
cmd += ' -i {0}.*'.format('encodings')
cmd += ' -i {0}'.format('base64')
cmd += ' -i {0}'.format('site')
cmd += ' -o GameData.pyd'  # The filename of the built *.PYD.
cmd += ' {0}'.format(args.main_module)  # "main" module


print 'Done building the client.'

I get this error:

Building the client...
Traceback (most recent call last):
  File "", line 18, in <module>
WindowsError: [Error 2] The system cannot find the file specified: 'build' is in my Panda3D-1.9.0/python/lib directory, and exists. I do not understand the issue. It seems like the Python path is wrong?

Any pointers would be useful. Thanks.


Another issue.

When trying to prepare_client:

Preparing the client...
Build directory = build
Traceback (most recent call last):
  File "", line 53, in <module>
  File "C:\Panda3D-1.9.0\python\lib\", line 711, in __init__
    errread, errwrite)
  File "C:\Panda3D-1.9.0\python\lib\", line 948, in _execute_child
WindowsError: [Error 2] The system cannot find the file specified

I may be missing something, but the code pointed out by the error appears to be attempting to change the working directory to one named “build”–nothing about a file named “” seems to be mentioned in the error. Does the “build” directory exist?

The build directory is created when I run, but that errors out, so nothing is inside it.

The build won’t work because prepare_files isn’t working… :\