Browse Source

Merge branch 'master' into shaderpipeline

rdb 4 years ago
parent
commit
fd4c68e9f5
100 changed files with 1865 additions and 2309 deletions
  1. 15 18
      .github/workflows/ci.yml
  2. 534 0
      .pylintrc
  3. 7 0
      .travis.yml
  4. 5 1
      BACKERS.md
  5. 45 20
      CMakeLists.txt
  6. 4 4
      README.md
  7. 4 0
      cmake/macros/AddFlexTarget.cmake
  8. 12 2
      cmake/macros/PackageConfig.cmake
  9. 24 0
      cmake/modules/FindFFMPEG.cmake
  10. 1 1
      contrib/src/ai/aiBehaviors.h
  11. 3 8
      contrib/src/sceneeditor/SideWindow.py
  12. 0 1
      contrib/src/sceneeditor/collisionWindow.py
  13. 3 8
      contrib/src/sceneeditor/controllerWindow.py
  14. 1 7
      contrib/src/sceneeditor/dataHolder.py
  15. 3 7
      contrib/src/sceneeditor/lightingPanel.py
  16. 1 1
      contrib/src/sceneeditor/quad.py
  17. 2 12
      contrib/src/sceneeditor/sceneEditor.py
  18. 2 8
      contrib/src/sceneeditor/seAnimPanel.py
  19. 2 9
      contrib/src/sceneeditor/seBlendAnimPanel.py
  20. 1 0
      contrib/src/sceneeditor/seCameraControl.py
  21. 2 6
      contrib/src/sceneeditor/seColorEntry.py
  22. 0 1
      contrib/src/sceneeditor/seFileSaver.py
  23. 1 0
      contrib/src/sceneeditor/seGrid.py
  24. 0 1
      contrib/src/sceneeditor/seLights.py
  25. 3 2
      contrib/src/sceneeditor/seManipulation.py
  26. 6 10
      contrib/src/sceneeditor/seMopathRecorder.py
  27. 3 8
      contrib/src/sceneeditor/seParticlePanel.py
  28. 0 1
      contrib/src/sceneeditor/seParticles.py
  29. 3 8
      contrib/src/sceneeditor/sePlacer.py
  30. 3 8
      contrib/src/sceneeditor/seSceneGraphExplorer.py
  31. 1 1
      contrib/src/sceneeditor/seSelection.py
  32. 2 2
      contrib/src/sceneeditor/seSession.py
  33. 4 8
      contrib/src/sceneeditor/seTree.py
  34. 75 76
      direct/src/actor/Actor.py
  35. 3 7
      direct/src/actor/DistributedActor.py
  36. 35 40
      direct/src/cluster/ClusterClient.py
  37. 0 1
      direct/src/cluster/ClusterConfig.py
  38. 10 19
      direct/src/cluster/ClusterMsgs.py
  39. 28 30
      direct/src/cluster/ClusterServer.py
  40. 2 148
      direct/src/controls/BattleWalker.py
  41. 9 9
      direct/src/controls/ControlManager.py
  42. 2 0
      direct/src/controls/DevWalker.py
  43. 10 8
      direct/src/controls/GravityWalker.py
  44. 2 0
      direct/src/controls/InputState.py
  45. 7 5
      direct/src/controls/NonPhysicsWalker.py
  46. 0 5
      direct/src/controls/ObserverWalker.py
  47. 98 135
      direct/src/controls/PhysicsWalker.py
  48. 1 0
      direct/src/controls/TwoDWalker.py
  49. 1 3
      direct/src/dcparser/dcClass_ext.cxx
  50. 1 1
      direct/src/dcparser/dcPacker_ext.cxx
  51. 2 15
      direct/src/directbase/TestStart.py
  52. 5 17
      direct/src/directbase/ThreeUpStart.py
  53. 17 16
      direct/src/directdevices/DirectDeviceManager.py
  54. 3 6
      direct/src/directdevices/DirectFastrak.py
  55. 13 16
      direct/src/directdevices/DirectJoybox.py
  56. 12 13
      direct/src/directdevices/DirectRadamec.py
  57. 3 3
      direct/src/directnotify/DirectNotify.py
  58. 11 26
      direct/src/directnotify/Logger.py
  59. 11 12
      direct/src/directnotify/Notifier.py
  60. 1 1
      direct/src/directnotify/RotatingLog.py
  61. 22 17
      direct/src/directscripts/eggcacher.py
  62. 2 2
      direct/src/directscripts/extract_docs.py
  63. 0 986
      direct/src/directscripts/gendocs.py
  64. 8 8
      direct/src/directtools/DirectCameraControl.py
  65. 3 4
      direct/src/directtools/DirectGeometry.py
  66. 0 1
      direct/src/directtools/DirectGlobals.py
  67. 4 3
      direct/src/directtools/DirectGrid.py
  68. 8 9
      direct/src/directtools/DirectLights.py
  69. 58 57
      direct/src/directtools/DirectManipulation.py
  70. 12 12
      direct/src/directtools/DirectSelection.py
  71. 15 16
      direct/src/directtools/DirectSession.py
  72. 2 2
      direct/src/directtools/DirectUtil.py
  73. 3 1
      direct/src/directutil/DistributedLargeBlobSender.py
  74. 2 2
      direct/src/directutil/DistributedLargeBlobSenderAI.py
  75. 23 24
      direct/src/directutil/Mopath.py
  76. 138 24
      direct/src/dist/FreezeTool.py
  77. 154 197
      direct/src/dist/commands.py
  78. 1 1
      direct/src/dist/icon.py
  79. 198 0
      direct/src/dist/installers.py
  80. 3 4
      direct/src/dist/pefile.py
  81. 1 1
      direct/src/dist/pfreeze.py
  82. 3 1
      direct/src/distributed/AsyncRequest.py
  83. 7 4
      direct/src/distributed/CRCache.py
  84. 0 1
      direct/src/distributed/CRDataCache.py
  85. 4 4
      direct/src/distributed/CartesianGridBase.py
  86. 9 8
      direct/src/distributed/ClientRepository.py
  87. 18 16
      direct/src/distributed/ClientRepositoryBase.py
  88. 4 4
      direct/src/distributed/ClockDelta.py
  89. 9 7
      direct/src/distributed/ConnectionRepository.py
  90. 13 10
      direct/src/distributed/DistributedCamera.py
  91. 2 2
      direct/src/distributed/DistributedCameraOV.py
  92. 9 9
      direct/src/distributed/DistributedCartesianGrid.py
  93. 9 9
      direct/src/distributed/DistributedCartesianGridAI.py
  94. 4 8
      direct/src/distributed/DistributedNode.py
  95. 3 5
      direct/src/distributed/DistributedNodeAI.py
  96. 4 6
      direct/src/distributed/DistributedNodeUD.py
  97. 23 28
      direct/src/distributed/DistributedObject.py
  98. 27 32
      direct/src/distributed/DistributedObjectAI.py
  99. 1 8
      direct/src/distributed/DistributedObjectBase.py
  100. 0 1
      direct/src/distributed/DistributedObjectGlobal.py

+ 15 - 18
.github/workflows/ci.yml

@@ -92,16 +92,13 @@ jobs:
     - name: Install dependencies (macOS)
     - name: Install dependencies (macOS)
       if: runner.os == 'macOS'
       if: runner.os == 'macOS'
       run: |
       run: |
-        curl -O https://www.panda3d.org/download/panda3d-1.10.7/panda3d-1.10.7-tools-mac.tar.gz
-        tar -xf panda3d-1.10.7-tools-mac.tar.gz
-        mv panda3d-1.10.7/thirdparty thirdparty
-        rmdir panda3d-1.10.7
+        curl -O https://www.panda3d.org/download/panda3d-1.10.8/panda3d-1.10.8-tools-mac.tar.gz
+        tar -xf panda3d-1.10.8-tools-mac.tar.gz
+        mv panda3d-1.10.8/thirdparty thirdparty
+        rmdir panda3d-1.10.8
 
 
-        mkdir -p build/Frameworks
-        cp -R thirdparty/darwin-libs-a/nvidiacg/Cg.framework build/Frameworks/Cg.framework
-
-        mkdir -p "build/${{ matrix.config }}/Frameworks"
-        cp -R thirdparty/darwin-libs-a/nvidiacg/Cg.framework "build/${{ matrix.config }}/Frameworks/Cg.framework"
+        # Temporary hack so that pzip can run, since we are about to remove Cg anyway.
+        install_name_tool -id "$(pwd)/thirdparty/darwin-libs-a/nvidiacg/lib/libCg.dylib" thirdparty/darwin-libs-a/nvidiacg/lib/libCg.dylib
 
 
         brew install ccache
         brew install ccache
 
 
@@ -127,16 +124,16 @@ jobs:
       uses: actions/cache@v1
       uses: actions/cache@v1
       with:
       with:
         path: thirdparty
         path: thirdparty
-        key: ci-cmake-${{ runner.OS }}-thirdparty-v1.10.7-r1
+        key: ci-cmake-${{ runner.OS }}-thirdparty-v1.10.9-r1
     - name: Install dependencies (Windows)
     - name: Install dependencies (Windows)
       if: runner.os == 'Windows'
       if: runner.os == 'Windows'
       shell: powershell
       shell: powershell
       run: |
       run: |
         if (!(Test-Path thirdparty/win-libs-vc14-x64)) {
         if (!(Test-Path thirdparty/win-libs-vc14-x64)) {
           $wc = New-Object System.Net.WebClient
           $wc = New-Object System.Net.WebClient
-          $wc.DownloadFile("https://www.panda3d.org/download/panda3d-1.10.7/panda3d-1.10.7-tools-win64.zip", "thirdparty-tools.zip")
+          $wc.DownloadFile("https://www.panda3d.org/download/panda3d-1.10.9/panda3d-1.10.9-tools-win64.zip", "thirdparty-tools.zip")
           Expand-Archive -Path thirdparty-tools.zip
           Expand-Archive -Path thirdparty-tools.zip
-          Move-Item -Path thirdparty-tools/panda3d-1.10.7/thirdparty -Destination .
+          Move-Item -Path thirdparty-tools/panda3d-1.10.9/thirdparty -Destination .
         }
         }
 
 
     - name: ccache (non-Windows)
     - name: ccache (non-Windows)
@@ -350,16 +347,16 @@ jobs:
       shell: powershell
       shell: powershell
       run: |
       run: |
         $wc = New-Object System.Net.WebClient
         $wc = New-Object System.Net.WebClient
-        $wc.DownloadFile("https://www.panda3d.org/download/panda3d-1.10.7/panda3d-1.10.7-tools-win64.zip", "thirdparty-tools.zip")
+        $wc.DownloadFile("https://www.panda3d.org/download/panda3d-1.10.8/panda3d-1.10.8-tools-win64.zip", "thirdparty-tools.zip")
         Expand-Archive -Path thirdparty-tools.zip
         Expand-Archive -Path thirdparty-tools.zip
-        Move-Item -Path thirdparty-tools/panda3d-1.10.7/thirdparty -Destination .
+        Move-Item -Path thirdparty-tools/panda3d-1.10.8/thirdparty -Destination .
     - name: Get thirdparty packages (macOS)
     - name: Get thirdparty packages (macOS)
       if: runner.os == 'macOS'
       if: runner.os == 'macOS'
       run: |
       run: |
-        curl -O https://www.panda3d.org/download/panda3d-1.10.7/panda3d-1.10.7-tools-mac.tar.gz
-        tar -xf panda3d-1.10.7-tools-mac.tar.gz
-        mv panda3d-1.10.7/thirdparty thirdparty
-        rmdir panda3d-1.10.7
+        curl -O https://www.panda3d.org/download/panda3d-1.10.8/panda3d-1.10.8-tools-mac.tar.gz
+        tar -xf panda3d-1.10.8-tools-mac.tar.gz
+        mv panda3d-1.10.8/thirdparty thirdparty
+        rmdir panda3d-1.10.8
         (cd thirdparty/darwin-libs-a && rm -rf rocket)
         (cd thirdparty/darwin-libs-a && rm -rf rocket)
     - name: Set up Python 3.9
     - name: Set up Python 3.9
       uses: actions/setup-python@v1
       uses: actions/setup-python@v1

+ 534 - 0
.pylintrc

@@ -0,0 +1,534 @@
+[MASTER]
+
+# A comma-separated list of package or module names from where C extensions may
+# be loaded. Extensions are loading into the active Python interpreter and may
+# run arbitrary code.
+extension-pkg-whitelist=panda3d
+
+# Specify a score threshold to be exceeded before program exits with error.
+fail-under=10.0
+
+# Add files or directories to the blacklist. They should be base names, not
+# paths.
+ignore=thirdparty
+
+# Add files or directories matching the regex patterns to the blacklist. The
+# regex matches against base names, not paths.
+ignore-patterns=
+
+# Python code to execute, usually for sys.path manipulation such as
+# pygtk.require().
+#init-hook=
+
+# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the
+# number of processors available to use.
+jobs=1
+
+# Control the amount of potential inferred values when inferring a single
+# object. This can help the performance when dealing with large functions or
+# complex, nested conditions.
+limit-inference-results=100
+
+# List of plugins (as comma separated values of python module names) to load,
+# usually to register additional checkers.
+load-plugins=
+
+# Pickle collected data for later comparisons.
+persistent=yes
+
+# When enabled, pylint would attempt to guess common misconfiguration and emit
+# user-friendly hints instead of false-positive error messages.
+suggestion-mode=yes
+
+# Allow loading of arbitrary C extensions. Extensions are imported into the
+# active Python interpreter and may run arbitrary code.
+unsafe-load-any-extension=no
+
+
+[MESSAGES CONTROL]
+
+# Only show warnings with the listed confidence levels. Leave empty to show
+# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED.
+confidence=
+
+# Disable the message, report, category or checker with the given id(s). You
+# can either give multiple identifiers separated by comma (,) or put this
+# option multiple times (only on the command line, not in the configuration
+# file where it should appear only once). You can also use "--disable=all" to
+# disable everything first and then reenable specific checks. For example, if
+# you want to run only the similarities checker, you can use "--disable=all
+# --enable=similarities". If you want to run only the classes checker, but have
+# no Warning level messages displayed, use "--disable=all --enable=classes
+# --disable=W".
+disable=attribute-defined-outside-init,
+        broad-except,
+        comparison-with-callable,
+        dangerous-default-value,
+        global-statement,
+        import-outside-toplevel,
+        invalid-name,
+        line-too-long,
+        misplaced-comparison-constant,
+        missing-class-docstring,
+        missing-function-docstring,
+        missing-module-docstring,
+        protected-access,
+        r,
+        raise-missing-from,
+        redefined-builtin,
+        redefined-outer-name,
+        too-many-lines,
+        unused-argument,
+        unused-variable,
+        unused-wildcard-import,
+        using-constant-test,
+        wildcard-import,
+        wrong-import-order,
+        wrong-import-position
+
+# Enable the message, report, category or checker with the given id(s). You can
+# either give multiple identifier separated by comma (,) or put this option
+# multiple time (only on the command line, not in the configuration file where
+# it should appear only once). See also the "--disable" option for examples.
+enable=c-extension-no-member
+
+
+[REPORTS]
+
+# Python expression which should return a score less than or equal to 10. You
+# have access to the variables 'error', 'warning', 'refactor', and 'convention'
+# which contain the number of messages in each category, as well as 'statement'
+# which is the total number of statements analyzed. This score is used by the
+# global evaluation report (RP0004).
+evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
+
+# Template used to display messages. This is a python new-style format string
+# used to format the message information. See doc for all details.
+#msg-template=
+
+# Set the output format. Available formats are text, parseable, colorized, json
+# and msvs (visual studio). You can also give a reporter class, e.g.
+# mypackage.mymodule.MyReporterClass.
+output-format=text
+
+# Tells whether to display a full report or only the messages.
+reports=no
+
+# Activate the evaluation score.
+score=yes
+
+
+[REFACTORING]
+
+# Maximum number of nested blocks for function / method body
+max-nested-blocks=5
+
+# Complete name of functions that never returns. When checking for
+# inconsistent-return-statements if a never returning function is called then
+# it will be considered as an explicit return statement and no message will be
+# printed.
+never-returning-functions=sys.exit
+
+
+[FORMAT]
+
+# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
+expected-line-ending-format=LF
+
+# Regexp for a line that is allowed to be longer than the limit.
+ignore-long-lines=^\s*(# )?<?https?://\S+>?$
+
+# Number of spaces of indent required inside a hanging or continued line.
+indent-after-paren=4
+
+# String used as indentation unit. This is usually "    " (4 spaces) or "\t" (1
+# tab).
+indent-string='    '
+
+# Maximum number of characters on a single line.
+max-line-length=100
+
+# Maximum number of lines in a module.
+max-module-lines=1000
+
+# Allow the body of a class to be on the same line as the declaration if body
+# contains single statement.
+single-line-class-stmt=no
+
+# Allow the body of an if to be on the same line as the test if there is no
+# else.
+single-line-if-stmt=no
+
+
+[SPELLING]
+
+# Limits count of emitted suggestions for spelling mistakes.
+max-spelling-suggestions=4
+
+# Spelling dictionary name. Available dictionaries: none. To make it work,
+# install the python-enchant package.
+spelling-dict=
+
+# List of comma separated words that should not be checked.
+spelling-ignore-words=
+
+# A path to a file that contains the private dictionary; one word per line.
+spelling-private-dict-file=
+
+# Tells whether to store unknown words to the private dictionary (see the
+# --spelling-private-dict-file option) instead of raising a message.
+spelling-store-unknown-words=no
+
+
+[TYPECHECK]
+
+# List of decorators that produce context managers, such as
+# contextlib.contextmanager. Add to this list to register other decorators that
+# produce valid context managers.
+contextmanager-decorators=contextlib.contextmanager
+
+# List of members which are set dynamically and missed by pylint inference
+# system, and so shouldn't trigger E1101 when accessed. Python regular
+# expressions are accepted.
+generated-members=
+
+# Tells whether missing members accessed in mixin class should be ignored. A
+# mixin class is detected if its name ends with "mixin" (case insensitive).
+ignore-mixin-members=yes
+
+# Tells whether to warn about missing members when the owner of the attribute
+# is inferred to be None.
+ignore-none=yes
+
+# This flag controls whether pylint should warn about no-member and similar
+# checks whenever an opaque object is returned when inferring. The inference
+# can return multiple potential results while evaluating a Python object, but
+# some branches might not be evaluated, which results in partial inference. In
+# that case, it might be useful to still emit no-member and other checks for
+# the rest of the inferred objects.
+ignore-on-opaque-inference=yes
+
+# List of class names for which member attributes should not be checked (useful
+# for classes with dynamically set attributes). This supports the use of
+# qualified names.
+ignored-classes=optparse.Values,thread._local,_thread._local,direct.showbase.PythonUtil.ScratchPad
+
+# List of module names for which member attributes should not be checked
+# (useful for modules/projects where namespaces are manipulated during runtime
+# and thus existing member attributes cannot be deduced by static analysis). It
+# supports qualified module names, as well as Unix pattern matching.
+ignored-modules=
+
+# Show a hint with possible names when a member name was not found. The aspect
+# of finding the hint is based on edit distance.
+missing-member-hint=yes
+
+# The minimum edit distance a name should have in order to be considered a
+# similar match for a missing member name.
+missing-member-hint-distance=1
+
+# The total number of similar names that should be taken in consideration when
+# showing a hint for a missing member.
+missing-member-max-choices=1
+
+# List of decorators that change the signature of a decorated function.
+signature-mutators=
+
+
+[SIMILARITIES]
+
+# Ignore comments when computing similarities.
+ignore-comments=yes
+
+# Ignore docstrings when computing similarities.
+ignore-docstrings=yes
+
+# Ignore imports when computing similarities.
+ignore-imports=no
+
+# Minimum lines number of a similarity.
+min-similarity-lines=4
+
+
+[VARIABLES]
+
+# List of additional names supposed to be defined in builtins. Remember that
+# you should avoid defining new builtins when possible.
+additional-builtins=base,simbase,__dev__,onScreenDebug,globalClock,render,hidden,cluster
+
+# Tells whether unused global variables should be treated as a violation.
+allow-global-unused-variables=yes
+
+# List of strings which can identify a callback function by name. A callback
+# name must start or end with one of those strings.
+callbacks=cb_,
+          _cb
+
+# A regular expression matching the name of dummy variables (i.e. expected to
+# not be used).
+dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_
+
+# Argument names that match this expression will be ignored. Default to name
+# with leading underscore.
+ignored-argument-names=_.*|^ignored_|^unused_
+
+# Tells whether we should check for unused import in __init__ files.
+init-import=no
+
+# List of qualified module names which can have objects that can redefine
+# builtins.
+redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io
+
+
+[MISCELLANEOUS]
+
+# List of note tags to take in consideration, separated by a comma.
+notes=FIXME,
+      XXX,
+      TODO
+
+# Regular expression of note tags to take in consideration.
+#notes-rgx=
+
+
+[BASIC]
+
+# Naming style matching correct argument names.
+argument-naming-style=snake_case
+
+# Regular expression matching correct argument names. Overrides argument-
+# naming-style.
+#argument-rgx=
+
+# Naming style matching correct attribute names.
+attr-naming-style=snake_case
+
+# Regular expression matching correct attribute names. Overrides attr-naming-
+# style.
+#attr-rgx=
+
+# Bad variable names which should always be refused, separated by a comma.
+bad-names=foo,
+          bar,
+          baz,
+          toto,
+          tutu,
+          tata
+
+# Bad variable names regexes, separated by a comma. If names match any regex,
+# they will always be refused
+bad-names-rgxs=
+
+# Naming style matching correct class attribute names.
+class-attribute-naming-style=any
+
+# Regular expression matching correct class attribute names. Overrides class-
+# attribute-naming-style.
+#class-attribute-rgx=
+
+# Naming style matching correct class names.
+class-naming-style=PascalCase
+
+# Regular expression matching correct class names. Overrides class-naming-
+# style.
+#class-rgx=
+
+# Naming style matching correct constant names.
+const-naming-style=UPPER_CASE
+
+# Regular expression matching correct constant names. Overrides const-naming-
+# style.
+#const-rgx=
+
+# Minimum line length for functions/classes that require docstrings, shorter
+# ones are exempt.
+docstring-min-length=-1
+
+# Naming style matching correct function names.
+function-naming-style=snake_case
+
+# Regular expression matching correct function names. Overrides function-
+# naming-style.
+#function-rgx=
+
+# Good variable names which should always be accepted, separated by a comma.
+good-names=i,
+           j,
+           k,
+           ex,
+           Run,
+           _
+
+# Good variable names regexes, separated by a comma. If names match any regex,
+# they will always be accepted
+good-names-rgxs=
+
+# Include a hint for the correct naming format with invalid-name.
+include-naming-hint=no
+
+# Naming style matching correct inline iteration names.
+inlinevar-naming-style=any
+
+# Regular expression matching correct inline iteration names. Overrides
+# inlinevar-naming-style.
+#inlinevar-rgx=
+
+# Naming style matching correct method names.
+method-naming-style=snake_case
+
+# Regular expression matching correct method names. Overrides method-naming-
+# style.
+#method-rgx=
+
+# Naming style matching correct module names.
+module-naming-style=snake_case
+
+# Regular expression matching correct module names. Overrides module-naming-
+# style.
+#module-rgx=
+
+# Colon-delimited sets of names that determine each other's naming style when
+# the name regexes allow several styles.
+name-group=
+
+# Regular expression which should only match function or class names that do
+# not require a docstring.
+no-docstring-rgx=^_
+
+# List of decorators that produce properties, such as abc.abstractproperty. Add
+# to this list to register other decorators that produce valid properties.
+# These decorators are taken in consideration only for invalid-name.
+property-classes=abc.abstractproperty
+
+# Naming style matching correct variable names.
+variable-naming-style=snake_case
+
+# Regular expression matching correct variable names. Overrides variable-
+# naming-style.
+#variable-rgx=
+
+
+[LOGGING]
+
+# The type of string formatting that logging methods do. `old` means using %
+# formatting, `new` is for `{}` formatting.
+logging-format-style=old
+
+# Logging modules to check that the string format arguments are in logging
+# function parameter format.
+logging-modules=logging
+
+
+[STRING]
+
+# This flag controls whether inconsistent-quotes generates a warning when the
+# character used as a quote delimiter is used inconsistently within a module.
+check-quote-consistency=no
+
+# This flag controls whether the implicit-str-concat should generate a warning
+# on implicit string concatenation in sequences defined over several lines.
+check-str-concat-over-line-jumps=no
+
+
+[DESIGN]
+
+# Maximum number of arguments for function / method.
+max-args=5
+
+# Maximum number of attributes for a class (see R0902).
+max-attributes=7
+
+# Maximum number of boolean expressions in an if statement (see R0916).
+max-bool-expr=5
+
+# Maximum number of branch for function / method body.
+max-branches=12
+
+# Maximum number of locals for function / method body.
+max-locals=15
+
+# Maximum number of parents for a class (see R0901).
+max-parents=7
+
+# Maximum number of public methods for a class (see R0904).
+max-public-methods=20
+
+# Maximum number of return / yield for function / method body.
+max-returns=6
+
+# Maximum number of statements in function / method body.
+max-statements=50
+
+# Minimum number of public methods for a class (see R0903).
+min-public-methods=2
+
+
+[IMPORTS]
+
+# List of modules that can be imported at any level, not just the top level
+# one.
+allow-any-import-level=
+
+# Allow wildcard imports from modules that define __all__.
+allow-wildcard-with-all=no
+
+# Analyse import fallback blocks. This can be used to support both Python 2 and
+# 3 compatible code, which means that the block might have code that exists
+# only in one or another interpreter, leading to false positives when analysed.
+analyse-fallback-blocks=no
+
+# Deprecated modules which should not be used, separated by a comma.
+deprecated-modules=optparse,tkinter.tix
+
+# Create a graph of external dependencies in the given file (report RP0402 must
+# not be disabled).
+ext-import-graph=
+
+# Create a graph of every (i.e. internal and external) dependencies in the
+# given file (report RP0402 must not be disabled).
+import-graph=
+
+# Create a graph of internal dependencies in the given file (report RP0402 must
+# not be disabled).
+int-import-graph=
+
+# Force import order to recognize a module as part of the standard
+# compatibility libraries.
+known-standard-library=
+
+# Force import order to recognize a module as part of a third party library.
+known-third-party=enchant
+
+# Couples of modules and preferred modules, separated by a comma.
+preferred-modules=
+
+
+[CLASSES]
+
+# List of method names used to declare (i.e. assign) instance attributes.
+defining-attr-methods=__init__,
+                      __new__,
+                      setUp,
+                      __post_init__
+
+# List of member names, which should be excluded from the protected access
+# warning.
+exclude-protected=_asdict,
+                  _fields,
+                  _replace,
+                  _source,
+                  _make
+
+# List of valid names for the first argument in a class method.
+valid-classmethod-first-arg=cls
+
+# List of valid names for the first argument in a metaclass class method.
+valid-metaclass-classmethod-first-arg=cls
+
+
+[EXCEPTIONS]
+
+# Exceptions that will emit a warning when being caught. Defaults to
+# "BaseException, Exception".
+overgeneral-exceptions=BaseException,
+                       Exception

+ 7 - 0
.travis.yml

@@ -0,0 +1,7 @@
+language: cpp
+branches:
+  only:
+    - release/1.10.x
+    - release/1.9.x
+script:
+    - echo "Build disabled on master branch."

+ 5 - 1
BACKERS.md

@@ -10,11 +10,12 @@ This is a list of all the people who are contributing financially to Panda3D.  I
 
 
 ## Bronze Sponsors
 ## Bronze Sponsors
 
 
-![Bronze Sponsors](https://opencollective.com/panda3d/tiers/bronze-sponsor.svg?avatarHeight=48&width=600)
+[<img src="https://www.panda3d.org/wp-content/uploads/2021/02/changecrab_logo.png" alt="ChangeCrab" height="48">](https://changecrab.com/) ![Bronze Sponsors](https://opencollective.com/panda3d/tiers/bronze-sponsor.svg?avatarHeight=48&width=600)
 
 
 * [Mitchell Stokes](https://opencollective.com/mitchell-stokes)
 * [Mitchell Stokes](https://opencollective.com/mitchell-stokes)
 * [Daniel Stokes](https://opencollective.com/daniel-stokes)
 * [Daniel Stokes](https://opencollective.com/daniel-stokes)
 * [David Rose](https://opencollective.com/david-rose)
 * [David Rose](https://opencollective.com/david-rose)
+* [ChangeCrab](https://changecrab.com)
 
 
 ## Benefactors
 ## Benefactors
 
 
@@ -22,6 +23,8 @@ This is a list of all the people who are contributing financially to Panda3D.  I
 
 
 * Sam Edwards
 * Sam Edwards
 * Max Voss
 * Max Voss
+* Hawkheart
+* Dan Mlodecki
 
 
 ## Enthusiasts
 ## Enthusiasts
 
 
@@ -30,6 +33,7 @@ This is a list of all the people who are contributing financially to Panda3D.  I
 * Eric Thomson
 * Eric Thomson
 * Kyle Roach
 * Kyle Roach
 * Brian Lach
 * Brian Lach
+* C0MPU73R
 
 
 ## Backers
 ## Backers
 
 

+ 45 - 20
CMakeLists.txt

@@ -32,23 +32,32 @@ a CMake < 3.9. Making a guess if this is a multi-config generator.")
   endif()
   endif()
 endif()
 endif()
 
 
-# Define the type of build we are setting up.
-set(_configs Standard Release RelWithDebInfo Debug MinSizeRel)
-if(CMAKE_CXX_COMPILER_ID MATCHES "(AppleClang|Clang|GCC)")
-  list(APPEND _configs Coverage)
-endif()
-
+# Set the default CMAKE_BUILD_TYPE before calling project().
 if(IS_MULTICONFIG)
 if(IS_MULTICONFIG)
   message(STATUS "Using multi-configuration generator")
   message(STATUS "Using multi-configuration generator")
 else()
 else()
-  # Set the default CMAKE_BUILD_TYPE before calling project().
   if(NOT CMAKE_BUILD_TYPE)
   if(NOT CMAKE_BUILD_TYPE)
     set(CMAKE_BUILD_TYPE Standard CACHE STRING "Choose the type of build." FORCE)
     set(CMAKE_BUILD_TYPE Standard CACHE STRING "Choose the type of build." FORCE)
     message(STATUS "Using default build type ${CMAKE_BUILD_TYPE}")
     message(STATUS "Using default build type ${CMAKE_BUILD_TYPE}")
   else()
   else()
     message(STATUS "Using build type ${CMAKE_BUILD_TYPE}")
     message(STATUS "Using build type ${CMAKE_BUILD_TYPE}")
   endif()
   endif()
-  set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS ${_configs})
+endif()
+
+# Set defaults for macOS, must be before project().
+if(APPLE)
+  set(CMAKE_OSX_DEPLOYMENT_TARGET "10.9" CACHE STRING "Minimum macOS version to target")
+  set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++")
+
+  if(CMAKE_VERSION VERSION_LESS "3.19" AND NOT CMAKE_OSX_SYSROOT)
+    # Older CMake chose SDK based on deployment target, against Apple's recommendations.
+    # However, we need to use the latest to be able to target arm64.
+    if(IS_DIRECTORY "/Library/Developer/CommandLineTools/SDKs/MacOSX11.1.sdk")
+      set(CMAKE_OSX_SYSROOT "/Library/Developer/CommandLineTools/SDKs/MacOSX11.1.sdk" CACHE STRING "")
+    elseif(IS_DIRECTORY "/Library/Developer/CommandLineTools/SDKs/MacOSX11.0.sdk")
+      set(CMAKE_OSX_SYSROOT "/Library/Developer/CommandLineTools/SDKs/MacOSX11.0.sdk" CACHE STRING "")
+    endif()
+  endif()
 endif()
 endif()
 
 
 # Figure out the version
 # Figure out the version
@@ -59,6 +68,18 @@ project(Panda3D VERSION ${_version})
 unset(_version)
 unset(_version)
 unset(_s)
 unset(_s)
 
 
+# Determine the possible build types.  Must be *after* calling project().
+set(_configs Standard Release RelWithDebInfo Debug MinSizeRel)
+if(CMAKE_CXX_COMPILER_ID MATCHES "(AppleClang|Clang|GCC)")
+  list(APPEND _configs Coverage)
+endif()
+
+if(IS_MULTICONFIG)
+  set(CMAKE_CONFIGURATION_TYPES "${_configs}" CACHE STRING "" FORCE)
+else()
+  set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS ${_configs})
+endif()
+
 enable_testing()
 enable_testing()
 
 
 string(REPLACE "$(EFFECTIVE_PLATFORM_NAME)" "" PANDA_CFG_INTDIR "${CMAKE_CFG_INTDIR}")
 string(REPLACE "$(EFFECTIVE_PLATFORM_NAME)" "" PANDA_CFG_INTDIR "${CMAKE_CFG_INTDIR}")
@@ -131,26 +152,30 @@ if(BUILD_MODELS)
     "${CMAKE_CURRENT_SOURCE_DIR}/models/"
     "${CMAKE_CURRENT_SOURCE_DIR}/models/"
     "${PROJECT_BINARY_DIR}/${PANDA_CFG_INTDIR}/models"
     "${PROJECT_BINARY_DIR}/${PANDA_CFG_INTDIR}/models"
     *.egg)
     *.egg)
-  run_pzip(dmodels
-    "${CMAKE_CURRENT_SOURCE_DIR}/dmodels/src/"
-    "${PROJECT_BINARY_DIR}/${PANDA_CFG_INTDIR}/models"
-    *.egg)
 
 
   add_custom_command(TARGET models
   add_custom_command(TARGET models
                      POST_BUILD
                      POST_BUILD
                      COMMAND ${CMAKE_COMMAND}
                      COMMAND ${CMAKE_COMMAND}
-                             -DSOURCE="${CMAKE_CURRENT_SOURCE_DIR}/models/maps/"
-                             -DDESTINATION="${PANDA_OUTPUT_DIR}/models/maps"
+                             -DSOURCE="${CMAKE_CURRENT_SOURCE_DIR}/models/audio/"
+                             -DDESTINATION="${PANDA_OUTPUT_DIR}/models/audio"
                              -P ${PROJECT_SOURCE_DIR}/cmake/scripts/CopyPattern.cmake
                              -P ${PROJECT_SOURCE_DIR}/cmake/scripts/CopyPattern.cmake
-                     COMMENT "Copying models/maps")
-  add_custom_command(TARGET dmodels
+                     COMMENT "Copying models/audio")
+
+  add_custom_command(TARGET models
                      POST_BUILD
                      POST_BUILD
                      COMMAND ${CMAKE_COMMAND}
                      COMMAND ${CMAKE_COMMAND}
-                             -DSOURCE="${CMAKE_CURRENT_SOURCE_DIR}/dmodels/src/"
-                             -DDESTINATION="${PANDA_OUTPUT_DIR}/models"
-                             -DFILES_MATCHING="PATTERN;*.rgb;PATTERN;*.png;PATTERN;*.jpg;PATTERN;*.wav"
+                             -DSOURCE="${CMAKE_CURRENT_SOURCE_DIR}/models/icons/"
+                             -DDESTINATION="${PANDA_OUTPUT_DIR}/models/icons"
                              -P ${PROJECT_SOURCE_DIR}/cmake/scripts/CopyPattern.cmake
                              -P ${PROJECT_SOURCE_DIR}/cmake/scripts/CopyPattern.cmake
-                     COMMENT "Copying dmodels' assets")
+                     COMMENT "Copying models/icons")
+
+  add_custom_command(TARGET models
+                     POST_BUILD
+                     COMMAND ${CMAKE_COMMAND}
+                             -DSOURCE="${CMAKE_CURRENT_SOURCE_DIR}/models/maps/"
+                             -DDESTINATION="${PANDA_OUTPUT_DIR}/models/maps"
+                             -P ${PROJECT_SOURCE_DIR}/cmake/scripts/CopyPattern.cmake
+                     COMMENT "Copying models/maps")
 
 
   install(DIRECTORY "${PANDA_OUTPUT_DIR}/models"
   install(DIRECTORY "${PANDA_OUTPUT_DIR}/models"
     COMPONENT Models DESTINATION ${CMAKE_INSTALL_DATADIR}/panda3d)
     COMPONENT Models DESTINATION ${CMAKE_INSTALL_DATADIR}/panda3d)

+ 4 - 4
README.md

@@ -24,7 +24,7 @@ Installing Panda3D
 ==================
 ==================
 
 
 The latest Panda3D SDK can be downloaded from
 The latest Panda3D SDK can be downloaded from
-[this page](https://www.panda3d.org/download/sdk-1-10-7/).
+[this page](https://www.panda3d.org/download/sdk-1-10-9/).
 If you are familiar with installing Python packages, you can use
 If you are familiar with installing Python packages, you can use
 the following command:
 the following command:
 
 
@@ -64,8 +64,8 @@ depending on whether you are on a 32-bit or 64-bit system, or you can
 [click here](https://github.com/rdb/panda3d-thirdparty) for instructions on
 [click here](https://github.com/rdb/panda3d-thirdparty) for instructions on
 building them from source.
 building them from source.
 
 
-- https://www.panda3d.org/download/panda3d-1.10.7/panda3d-1.10.7-tools-win64.zip
-- https://www.panda3d.org/download/panda3d-1.10.7/panda3d-1.10.7-tools-win32.zip
+- https://www.panda3d.org/download/panda3d-1.10.9/panda3d-1.10.9-tools-win64.zip
+- https://www.panda3d.org/download/panda3d-1.10.9/panda3d-1.10.9-tools-win32.zip
 
 
 After acquiring these dependencies, you can build Panda3D from the command
 After acquiring these dependencies, you can build Panda3D from the command
 prompt using the following command.  Change the `--msvc-version` option based
 prompt using the following command.  Change the `--msvc-version` option based
@@ -136,7 +136,7 @@ macOS
 -----
 -----
 
 
 On macOS, you will need to download a set of precompiled thirdparty packages in order to
 On macOS, you will need to download a set of precompiled thirdparty packages in order to
-compile Panda3D, which can be acquired from [here](https://www.panda3d.org/download/panda3d-1.10.7/panda3d-1.10.7-tools-mac.tar.gz).
+compile Panda3D, which can be acquired from [here](https://www.panda3d.org/download/panda3d-1.10.9/panda3d-1.10.9-tools-mac.tar.gz).
 
 
 After placing the thirdparty directory inside the panda3d source directory,
 After placing the thirdparty directory inside the panda3d source directory,
 you may build Panda3D using a command like the following:
 you may build Panda3D using a command like the following:

+ 4 - 0
cmake/macros/AddFlexTarget.cmake

@@ -74,4 +74,8 @@ function(add_flex_target output_cxx input_lxx)
       ${commands}
       ${commands}
       DEPENDS ${depends})
       DEPENDS ${depends})
   endif()
   endif()
+
+  if(MSVC)
+    set_source_files_properties(${outputs} PROPERTIES COMPILE_DEFINITIONS YY_NO_UNISTD_H=1)
+  endif()
 endfunction(add_flex_target)
 endfunction(add_flex_target)

+ 12 - 2
cmake/macros/PackageConfig.cmake

@@ -167,6 +167,12 @@ function(package_option name)
 
 
   set(PANDA_PACKAGE_DEFAULT_${name} "${default}" PARENT_SCOPE)
   set(PANDA_PACKAGE_DEFAULT_${name} "${default}" PARENT_SCOPE)
 
 
+  if(${found_as}_FOUND OR ${FOUND_AS}_FOUND)
+    set(PANDA_PACKAGE_FOUND_${name} ON PARENT_SCOPE)
+  else()
+    set(PANDA_PACKAGE_FOUND_${name} OFF PARENT_SCOPE)
+  endif()
+
   # Create the INTERFACE library used to depend on this package.
   # Create the INTERFACE library used to depend on this package.
   add_library(PKG::${name} INTERFACE IMPORTED GLOBAL)
   add_library(PKG::${name} INTERFACE IMPORTED GLOBAL)
 
 
@@ -279,15 +285,19 @@ function(show_packages)
   foreach(package ${_ALL_CONFIG_PACKAGES})
   foreach(package ${_ALL_CONFIG_PACKAGES})
     set(desc "${PANDA_PACKAGE_DESC_${package}}")
     set(desc "${PANDA_PACKAGE_DESC_${package}}")
     set(note "${PANDA_PACKAGE_NOTE_${package}}")
     set(note "${PANDA_PACKAGE_NOTE_${package}}")
-    if(HAVE_${package})
+
+    if(HAVE_${package} AND PANDA_PACKAGE_FOUND_${package})
       if(NOT note STREQUAL "")
       if(NOT note STREQUAL "")
         message("+ ${desc} (${note})")
         message("+ ${desc} (${note})")
       else()
       else()
         message("+ ${desc}")
         message("+ ${desc}")
       endif()
       endif()
 
 
+    elseif(HAVE_${package})
+      message("! ${desc} (enabled but not found)")
+
     else()
     else()
-      if(NOT ${package}_FOUND)
+      if(NOT PANDA_PACKAGE_FOUND_${package})
         set(reason "not found")
         set(reason "not found")
       elseif(NOT PANDA_PACKAGE_DEFAULT_${package})
       elseif(NOT PANDA_PACKAGE_DEFAULT_${package})
         set(reason "not requested")
         set(reason "not requested")

+ 24 - 0
cmake/modules/FindFFMPEG.cmake

@@ -89,16 +89,36 @@ if(APPLE)
   # When statically built for Apple, FFMPEG may have dependencies on these
   # When statically built for Apple, FFMPEG may have dependencies on these
   # additional frameworks and libraries.
   # additional frameworks and libraries.
 
 
+  find_library(APPLE_AUDIOTOOLBOX_LIBRARY AudioToolbox)
+  if(APPLE_AUDIOTOOLBOX_LIBRARY)
+    list(APPEND FFMPEG_LIBRARIES "${APPLE_AUDIOTOOLBOX_LIBRARY}")
+  endif()
+
+  find_library(APPLE_COREMEDIA_LIBRARY CoreMedia)
+  if(APPLE_COREMEDIA_LIBRARY)
+    list(APPEND FFMPEG_LIBRARIES "${APPLE_COREMEDIA_LIBRARY}")
+  endif()
+
   find_library(APPLE_COREVIDEO_LIBRARY CoreVideo)
   find_library(APPLE_COREVIDEO_LIBRARY CoreVideo)
   if(APPLE_COREVIDEO_LIBRARY)
   if(APPLE_COREVIDEO_LIBRARY)
     list(APPEND FFMPEG_LIBRARIES "${APPLE_COREVIDEO_LIBRARY}")
     list(APPEND FFMPEG_LIBRARIES "${APPLE_COREVIDEO_LIBRARY}")
   endif()
   endif()
 
 
+  find_library(APPLE_SECURITY_LIBRARY Security)
+  if(APPLE_SECURITY_LIBRARY)
+    list(APPEND FFMPEG_LIBRARIES "${APPLE_SECURITY_LIBRARY}")
+  endif()
+
   find_library(APPLE_VDA_LIBRARY VideoDecodeAcceleration)
   find_library(APPLE_VDA_LIBRARY VideoDecodeAcceleration)
   if(APPLE_VDA_LIBRARY)
   if(APPLE_VDA_LIBRARY)
     list(APPEND FFMPEG_LIBRARIES "${APPLE_VDA_LIBRARY}")
     list(APPEND FFMPEG_LIBRARIES "${APPLE_VDA_LIBRARY}")
   endif()
   endif()
 
 
+  find_library(APPLE_VIDEOTOOLBOX_LIBRARY VideoToolbox)
+  if(APPLE_VIDEOTOOLBOX_LIBRARY)
+    list(APPEND FFMPEG_LIBRARIES "${APPLE_VIDEOTOOLBOX_LIBRARY}")
+  endif()
+
   find_library(APPLE_ICONV_LIBRARY iconv)
   find_library(APPLE_ICONV_LIBRARY iconv)
   if(APPLE_ICONV_LIBRARY)
   if(APPLE_ICONV_LIBRARY)
     list(APPEND FFMPEG_LIBRARIES "${APPLE_ICONV_LIBRARY}")
     list(APPEND FFMPEG_LIBRARIES "${APPLE_ICONV_LIBRARY}")
@@ -108,6 +128,10 @@ if(APPLE)
   if(APPLE_BZ2_LIBRARY)
   if(APPLE_BZ2_LIBRARY)
     list(APPEND FFMPEG_LIBRARIES "${APPLE_BZ2_LIBRARY}")
     list(APPEND FFMPEG_LIBRARIES "${APPLE_BZ2_LIBRARY}")
   endif()
   endif()
+
+  mark_as_advanced(APPLE_AUDIOTOOLBOX_LIBRARY APPLE_COREMEDIA_LIBRARY
+    APPLE_COREVIDEO_LIBRARY APPLE_SECURITY_LIBRARY APPLE_VDA_LIBRARY
+    APPLE_VIDEOTOOLBOX_LIBRARY APPLE_ICONV_LIBRARY APPLE_BZ2_LIBRARY)
 endif()
 endif()
 
 
 mark_as_advanced(FFMPEG_LIBRARY_DIR)
 mark_as_advanced(FFMPEG_LIBRARY_DIR)

+ 1 - 1
contrib/src/ai/aiBehaviors.h

@@ -151,7 +151,7 @@ PUBLISHED:
 
 
   void obstacle_avoidance(float feeler_length = 1.0);
   void obstacle_avoidance(float feeler_length = 1.0);
 
 
-  void path_follow(float follow_wt);
+  void path_follow(float follow_wt = 1.0f);
   void add_to_path(LVecBase3 pos);
   void add_to_path(LVecBase3 pos);
   void start_follow(std::string type = "normal");
   void start_follow(std::string type = "normal");
 
 

+ 3 - 8
contrib/src/sceneeditor/SideWindow.py

@@ -7,14 +7,9 @@ from direct.tkwidgets.VectorWidgets import ColorEntry
 from direct.showbase.TkGlobal import spawnTkLoop
 from direct.showbase.TkGlobal import spawnTkLoop
 import seSceneGraphExplorer
 import seSceneGraphExplorer
 
 
-import Pmw, sys
-
-if sys.version_info >= (3, 0):
-    from tkinter import Frame, IntVar, Checkbutton, Toplevel
-    import tkinter
-else:
-    from Tkinter import Frame, IntVar, Checkbutton, Toplevel
-    import Tkinter as tkinter
+import Pmw
+from tkinter import Frame, IntVar, Checkbutton, Toplevel
+import tkinter
 
 
 
 
 class sideWindow(AppShell):
 class sideWindow(AppShell):

+ 0 - 1
contrib/src/sceneeditor/collisionWindow.py

@@ -9,7 +9,6 @@ from seColorEntry import *
 from direct.tkwidgets import VectorWidgets
 from direct.tkwidgets import VectorWidgets
 from direct.tkwidgets import Floater
 from direct.tkwidgets import Floater
 from direct.tkwidgets import Slider
 from direct.tkwidgets import Slider
-import string, math, types
 from panda3d.core import *
 from panda3d.core import *
 
 
 
 

+ 3 - 8
contrib/src/sceneeditor/controllerWindow.py

@@ -4,14 +4,9 @@
 #################################################################
 #################################################################
 
 
 from direct.tkwidgets.AppShell import AppShell
 from direct.tkwidgets.AppShell import AppShell
-import sys, Pmw
-
-if sys.version_info >= (3, 0):
-    from tkinter import Frame, Label, Button
-    import tkinter
-else:
-    from Tkinter import Frame, Label, Button
-    import Tkinter as tkinter
+import Pmw
+from tkinter import Frame, Label, Button
+import tkinter
 
 
 # Define the Category
 # Define the Category
 KEYBOARD = 'Keyboard-'
 KEYBOARD = 'Keyboard-'

+ 1 - 7
contrib/src/sceneeditor/dataHolder.py

@@ -5,12 +5,7 @@ from direct.showbase.TkGlobal import*
 import Pmw
 import Pmw
 from direct.tkwidgets import Dial
 from direct.tkwidgets import Dial
 from direct.tkwidgets import Floater
 from direct.tkwidgets import Floater
-
-if sys.version_info >= (3, 0):
-    from tkinter.filedialog import askopenfilename
-else:
-    from tkFileDialog import askopenfilename
-
+from tkinter.filedialog import askopenfilename
 
 
 #############################
 #############################
 # Scene Editor Python Files #
 # Scene Editor Python Files #
@@ -27,7 +22,6 @@ from direct.actor import Actor
 # Core Python Modules         #
 # Core Python Modules         #
 ###############################
 ###############################
 import os
 import os
-import string
 import sys
 import sys
 
 
 import seParticleEffect
 import seParticleEffect

+ 3 - 7
contrib/src/sceneeditor/lightingPanel.py

@@ -7,15 +7,11 @@ from direct.tkwidgets.AppShell import AppShell
 from seColorEntry import *
 from seColorEntry import *
 from direct.tkwidgets.VectorWidgets import Vector3Entry
 from direct.tkwidgets.VectorWidgets import Vector3Entry
 from direct.tkwidgets.Slider import Slider
 from direct.tkwidgets.Slider import Slider
-import sys, math, types, Pmw
+import Pmw
 from panda3d.core import *
 from panda3d.core import *
 
 
-if sys.version_info >= (3, 0):
-    from tkinter import Frame, Button, Menubutton, Menu
-    import tkinter
-else:
-    from Tkinter import Frame, Button, Menubutton, Menu
-    import Tkinter as tkinter
+from tkinter import Frame, Button, Menubutton, Menu
+import tkinter
 
 
 
 
 class lightingPanel(AppShell):
 class lightingPanel(AppShell):

+ 1 - 1
contrib/src/sceneeditor/quad.py

@@ -10,7 +10,7 @@ from direct.showbase.ShowBaseGlobal import *
 from direct.interval.IntervalGlobal import *
 from direct.interval.IntervalGlobal import *
 from direct.showbase.DirectObject import DirectObject
 from direct.showbase.DirectObject import DirectObject
 from panda3d.core import *
 from panda3d.core import *
-import math
+from direct.task import Task
 
 
 
 
 class ViewPort:
 class ViewPort:

+ 2 - 12
contrib/src/sceneeditor/sceneEditor.py

@@ -8,13 +8,8 @@ from direct.showbase.ShowBase import ShowBase
 ShowBase()
 ShowBase()
 
 
 from direct.showbase.TkGlobal import spawnTkLoop
 from direct.showbase.TkGlobal import spawnTkLoop
-
-if sys.version_info >= (3, 0):
-    from tkinter import *
-    from tkinter.filedialog import *
-else:
-    from Tkinter import *
-    from tkFileDialog import *
+from tkinter import *
+from tkinter.filedialog import *
 
 
 from direct.directtools.DirectGlobals import *
 from direct.directtools.DirectGlobals import *
 from direct.tkwidgets.AppShell import*
 from direct.tkwidgets.AppShell import*
@@ -36,17 +31,12 @@ from seBlendAnimPanel import *
 from controllerWindow import *
 from controllerWindow import *
 from AlignTool import *
 from AlignTool import *
 
 
-
-
-import os
-import string
 from direct.tkwidgets import Dial
 from direct.tkwidgets import Dial
 from direct.tkwidgets import Floater
 from direct.tkwidgets import Floater
 from direct.tkwidgets import Slider
 from direct.tkwidgets import Slider
 from direct.actor import Actor
 from direct.actor import Actor
 import seAnimPanel
 import seAnimPanel
 from direct.task import Task
 from direct.task import Task
-import math
 
 
 #################################################################
 #################################################################
 # All scene and windows object will be stored in here.
 # All scene and windows object will be stored in here.

+ 2 - 8
contrib/src/sceneeditor/seAnimPanel.py

@@ -5,15 +5,9 @@
 # Import Tkinter, Pmw, and the floater code from this directory tree.
 # Import Tkinter, Pmw, and the floater code from this directory tree.
 from direct.tkwidgets.AppShell import *
 from direct.tkwidgets.AppShell import *
 from direct.showbase.TkGlobal import *
 from direct.showbase.TkGlobal import *
-import string
-import math
-import types
 from direct.task import Task
 from direct.task import Task
 
 
-if sys.version_info >= (3, 0):
-    from tkinter.simpledialog import askfloat
-else:
-    from tkSimpleDialog import askfloat
+from tkinter.simpledialog import askfloat
 
 
 FRAMES = 0
 FRAMES = 0
 SECONDS = 1
 SECONDS = 1
@@ -407,7 +401,7 @@ class AnimPanel(AppShell):
         #################################################################
         #################################################################
         if self.animName in self['animList']:
         if self.animName in self['animList']:
             # Convert scale value to float
             # Convert scale value to float
-            frame = string.atof(frame)
+            frame = float(frame)
             # Now convert t to seconds for offset calculations
             # Now convert t to seconds for offset calculations
             if self.unitsVar.get() == FRAMES:
             if self.unitsVar.get() == FRAMES:
                 frame = frame / self.fps
                 frame = frame / self.fps

+ 2 - 9
contrib/src/sceneeditor/seBlendAnimPanel.py

@@ -5,15 +5,8 @@
 # Import Tkinter, Pmw, and the floater code from this directory tree.
 # Import Tkinter, Pmw, and the floater code from this directory tree.
 from direct.tkwidgets.AppShell import *
 from direct.tkwidgets.AppShell import *
 from direct.showbase.TkGlobal import *
 from direct.showbase.TkGlobal import *
-import string
-import math
-import types
 from direct.task import Task
 from direct.task import Task
-
-if sys.version_info >= (3, 0):
-    from tkinter.simpledialog import askfloat
-else:
-    from tkSimpleDialog import askfloat
+from tkinter.simpledialog import askfloat
 
 
 FRAMES = 0
 FRAMES = 0
 SECONDS = 1
 SECONDS = 1
@@ -431,7 +424,7 @@ class BlendAnimPanel(AppShell):
         #################################################################
         #################################################################
         if (self.animNameA in self['animList'])and(self.animNameB in self['animList']):
         if (self.animNameA in self['animList'])and(self.animNameB in self['animList']):
             # Convert scale value to float
             # Convert scale value to float
-            frame = string.atof(frame)
+            frame = float(frame)
             # Now convert t to seconds for offset calculations
             # Now convert t to seconds for offset calculations
             if self.unitsVar.get() == FRAMES:
             if self.unitsVar.get() == FRAMES:
                 frame = frame / self.fps
                 frame = frame / self.fps

+ 1 - 0
contrib/src/sceneeditor/seCameraControl.py

@@ -16,6 +16,7 @@ from direct.directtools.DirectUtil import *
 from seGeometry import *
 from seGeometry import *
 from direct.directtools.DirectGlobals import *
 from direct.directtools.DirectGlobals import *
 from direct.task import Task
 from direct.task import Task
+import math
 
 
 CAM_MOVE_DURATION = 1.2
 CAM_MOVE_DURATION = 1.2
 COA_MARKER_SF = 0.0075
 COA_MARKER_SF = 0.0075

+ 2 - 6
contrib/src/sceneeditor/seColorEntry.py

@@ -12,13 +12,9 @@
 from direct.tkwidgets import Valuator
 from direct.tkwidgets import Valuator
 from direct.tkwidgets import Floater
 from direct.tkwidgets import Floater
 from direct.tkwidgets import Slider
 from direct.tkwidgets import Slider
-import sys, Pmw
+import Pmw
 from direct.tkwidgets.VectorWidgets import VectorEntry
 from direct.tkwidgets.VectorWidgets import VectorEntry
-
-if sys.version_info >= (3, 0):
-    from tkinter.colorchooser import askcolor
-else:
-    from tkColorChooser import askcolor
+from tkinter.colorchooser import askcolor
 
 
 
 
 class seColorEntry(VectorEntry):
 class seColorEntry(VectorEntry):

+ 0 - 1
contrib/src/sceneeditor/seFileSaver.py

@@ -8,7 +8,6 @@ from panda3d.core import *
 from direct.showbase.ShowBaseGlobal import *
 from direct.showbase.ShowBaseGlobal import *
 import os
 import os
 import shutil
 import shutil
-import string
 
 
 ####################################################################################################################################################
 ####################################################################################################################################################
 #### These modules are modified versions of Disney's equivalent modules
 #### These modules are modified versions of Disney's equivalent modules

+ 1 - 0
contrib/src/sceneeditor/seGrid.py

@@ -14,6 +14,7 @@
 from direct.showbase.DirectObject import *
 from direct.showbase.DirectObject import *
 from direct.directtools.DirectUtil import *
 from direct.directtools.DirectUtil import *
 from seGeometry import *
 from seGeometry import *
+import math
 
 
 class DirectGrid(NodePath,DirectObject):
 class DirectGrid(NodePath,DirectObject):
     def __init__(self):
     def __init__(self):

+ 0 - 1
contrib/src/sceneeditor/seLights.py

@@ -5,7 +5,6 @@
 from direct.showbase.DirectObject import *
 from direct.showbase.DirectObject import *
 from direct.directtools import DirectUtil
 from direct.directtools import DirectUtil
 from panda3d.core import *
 from panda3d.core import *
-import string
 
 
 
 
 class seLight(NodePath):
 class seLight(NodePath):

+ 3 - 2
contrib/src/sceneeditor/seManipulation.py

@@ -17,6 +17,7 @@ from direct.directtools.DirectGlobals import *
 from direct.directtools.DirectUtil import *
 from direct.directtools.DirectUtil import *
 from seGeometry import *
 from seGeometry import *
 from direct.task import Task
 from direct.task import Task
+import math
 
 
 class DirectManipulationControl(DirectObject):
 class DirectManipulationControl(DirectObject):
     def __init__(self):
     def __init__(self):
@@ -601,7 +602,7 @@ class ObjectHandles(NodePath,DirectObject):
         self.reparentTo(hidden)
         self.reparentTo(hidden)
 
 
     def enableHandles(self, handles):
     def enableHandles(self, handles):
-        if type(handles) == types.ListType:
+        if type(handles) is list:
             for handle in handles:
             for handle in handles:
                 self.enableHandle(handle)
                 self.enableHandle(handle)
         elif handles == 'x':
         elif handles == 'x':
@@ -642,7 +643,7 @@ class ObjectHandles(NodePath,DirectObject):
             self.zDiscGroup.reparentTo(self.zHandles)
             self.zDiscGroup.reparentTo(self.zHandles)
 
 
     def disableHandles(self, handles):
     def disableHandles(self, handles):
-        if type(handles) == types.ListType:
+        if type(handles) is list:
             for handle in handles:
             for handle in handles:
                 self.disableHandle(handle)
                 self.disableHandle(handle)
         elif handles == 'x':
         elif handles == 'x':

+ 6 - 10
contrib/src/sceneeditor/seMopathRecorder.py

@@ -25,16 +25,12 @@ from direct.tkwidgets.Slider import Slider
 from direct.tkwidgets.EntryScale import EntryScale
 from direct.tkwidgets.EntryScale import EntryScale
 from direct.tkwidgets.VectorWidgets import Vector2Entry, Vector3Entry
 from direct.tkwidgets.VectorWidgets import Vector2Entry, Vector3Entry
 from direct.tkwidgets.VectorWidgets import ColorEntry
 from direct.tkwidgets.VectorWidgets import ColorEntry
-import os, string, sys, Pmw
+import os, Pmw
+import math
 
 
-if sys.version_info >= (3, 0):
-    from tkinter import Button, Frame, Radiobutton, Checkbutton, Label
-    from tkinter import StringVar, BooleanVar, Entry, Scale
-    import tkinter
-else:
-    from Tkinter import Button, Frame, Radiobutton, Checkbutton, Label
-    from Tkinter import StringVar, BooleanVar, Entry, Scale
-    import Tkinter as tkinter
+from tkinter import Button, Frame, Radiobutton, Checkbutton, Label
+from tkinter import StringVar, BooleanVar, Entry, Scale
+import tkinter
 
 
 
 
 PRF_UTILITIES = [
 PRF_UTILITIES = [
@@ -374,7 +370,7 @@ class MopathRecorder(AppShell, DirectObject):
         self.speedEntry.bind(
         self.speedEntry.bind(
             '<Return>',
             '<Return>',
             lambda e = None, s = self: s.setSpeedScale(
             lambda e = None, s = self: s.setSpeedScale(
-            string.atof(s.speedVar.get())))
+            float(s.speedVar.get())))
         self.speedEntry.pack(side = tkinter.LEFT, expand = 0)
         self.speedEntry.pack(side = tkinter.LEFT, expand = 0)
         frame.pack(fill = tkinter.X, expand = 1)
         frame.pack(fill = tkinter.X, expand = 1)
 
 

+ 3 - 8
contrib/src/sceneeditor/seParticlePanel.py

@@ -2,7 +2,7 @@
 
 
 # Import Tkinter, Pmw, and the floater code from this directory tree.
 # Import Tkinter, Pmw, and the floater code from this directory tree.
 from direct.tkwidgets.AppShell import AppShell
 from direct.tkwidgets.AppShell import AppShell
-import os, Pmw, sys
+import os, Pmw
 from direct.tkwidgets.Dial import AngleDial
 from direct.tkwidgets.Dial import AngleDial
 from direct.tkwidgets.Floater import Floater
 from direct.tkwidgets.Floater import Floater
 from direct.tkwidgets.Slider import Slider
 from direct.tkwidgets.Slider import Slider
@@ -13,13 +13,8 @@ import seForceGroup
 import seParticles
 import seParticles
 import seParticleEffect
 import seParticleEffect
 
 
-
-if sys.version_info >= (3, 0):
-    from tkinter.filedialog import *
-    from tkinter.simpledialog import askstring
-else:
-    from tkFileDialog import *
-    from tkSimpleDialog import askstring
+from tkinter.filedialog import *
+from tkinter.simpledialog import askstring
 
 
 
 
 class ParticlePanel(AppShell):
 class ParticlePanel(AppShell):

+ 0 - 1
contrib/src/sceneeditor/seParticles.py

@@ -3,7 +3,6 @@ from panda3d.physics import *
 from direct.particles.ParticleManagerGlobal import *
 from direct.particles.ParticleManagerGlobal import *
 from direct.showbase.PhysicsManagerGlobal import *
 from direct.showbase.PhysicsManagerGlobal import *
 #import OrientedParticleFactory
 #import OrientedParticleFactory
-import string
 import os
 import os
 from direct.directnotify import DirectNotifyGlobal
 from direct.directnotify import DirectNotifyGlobal
 import sys
 import sys

+ 3 - 8
contrib/src/sceneeditor/sePlacer.py

@@ -6,14 +6,9 @@ from direct.tkwidgets.AppShell import AppShell
 from direct.tkwidgets.Dial import AngleDial
 from direct.tkwidgets.Dial import AngleDial
 from direct.tkwidgets.Floater import Floater
 from direct.tkwidgets.Floater import Floater
 from panda3d.core import *
 from panda3d.core import *
-import sys, Pmw
-
-if sys.version_info >= (3, 0):
-    from tkinter import Button, Menubutton, Menu, StringVar
-    import tkinter
-else:
-    from Tkinter import Button, Menubutton, Menu, StringVar
-    import Tkinter as tkinter
+import Pmw
+from tkinter import Button, Menubutton, Menu, StringVar
+import tkinter
 
 
 """
 """
 TODO:
 TODO:

+ 3 - 8
contrib/src/sceneeditor/seSceneGraphExplorer.py

@@ -11,14 +11,9 @@
 from direct.showbase.DirectObject import DirectObject
 from direct.showbase.DirectObject import DirectObject
 from seTree import TreeNode, TreeItem
 from seTree import TreeNode, TreeItem
 
 
-import Pmw, sys
-
-if sys.version_info >= (3, 0):
-    from tkinter import IntVar, Frame, Label
-    import tkinter
-else:
-    from Tkinter import IntVar, Frame, Label
-    import Tkinter as tkinter
+import Pmw
+from tkinter import IntVar, Frame, Label
+import tkinter
 
 
 # changing these strings requires changing sceneEditor.py SGE_ strs too!
 # changing these strings requires changing sceneEditor.py SGE_ strs too!
 # This list of items will be showed on the pop out window when user right click on
 # This list of items will be showed on the pop out window when user right click on

+ 1 - 1
contrib/src/sceneeditor/seSelection.py

@@ -548,7 +548,7 @@ class SelectionRay(SelectionQueue):
         if xy:
         if xy:
             mx = xy[0]
             mx = xy[0]
             my = xy[1]
             my = xy[1]
-        elif direct:
+        elif base.direct:
             mx = SEditor.dr.mouseX
             mx = SEditor.dr.mouseX
             my = SEditor.dr.mouseY
             my = SEditor.dr.mouseY
         else:
         else:

+ 2 - 2
contrib/src/sceneeditor/seSession.py

@@ -20,9 +20,9 @@ from seGeometry import *
 from direct.tkpanels import Placer
 from direct.tkpanels import Placer
 from direct.tkwidgets import Slider
 from direct.tkwidgets import Slider
 from direct.gui import OnscreenText
 from direct.gui import OnscreenText
-import types
-import string
+from direct.task import Task
 from direct.showbase import Loader
 from direct.showbase import Loader
+import math
 
 
 class SeSession(DirectObject):  ### Customized DirectSession
 class SeSession(DirectObject):  ### Customized DirectSession
 
 

+ 4 - 8
contrib/src/sceneeditor/seTree.py

@@ -12,16 +12,12 @@
 #
 #
 #################################################################
 #################################################################
 
 
-import os, sys, string, Pmw
+import os, Pmw
 from direct.showbase.DirectObject import DirectObject
 from direct.showbase.DirectObject import DirectObject
 from panda3d.core import *
 from panda3d.core import *
 
 
-if sys.version_info >= (3, 0):
-    import tkinter
-    from tkinter import IntVar, Menu, PhotoImage, Label, Frame, Entry
-else:
-    import Tkinter as tkinter
-    from Tkinter import IntVar, Menu, PhotoImage, Label, Frame, Entry
+import tkinter
+from tkinter import IntVar, Menu, PhotoImage, Label, Frame, Entry
 
 
 # Initialize icon directory
 # Initialize icon directory
 ICONDIR = getModelPath().findFile(Filename('icons')).toOsSpecific()
 ICONDIR = getModelPath().findFile(Filename('icons')).toOsSpecific()
@@ -221,7 +217,7 @@ class TreeNode:
             self.children[key] = child
             self.children[key] = child
             self.kidKeys.append(key)
             self.kidKeys.append(key)
         # Remove unused children
         # Remove unused children
-        for key in self.children.keys():
+        for key in list(self.children.keys()):
             if key not in self.kidKeys:
             if key not in self.kidKeys:
                 del(self.children[key])
                 del(self.children[key])
         cx = x+20
         cx = x+20

+ 75 - 76
direct/src/actor/Actor.py

@@ -11,7 +11,7 @@ from panda3d.core import Loader as PandaLoader
 from direct.showbase.DirectObject import DirectObject
 from direct.showbase.DirectObject import DirectObject
 from direct.showbase.Loader import Loader
 from direct.showbase.Loader import Loader
 from direct.directnotify import DirectNotifyGlobal
 from direct.directnotify import DirectNotifyGlobal
-
+import warnings
 
 
 class Actor(DirectObject, NodePath):
 class Actor(DirectObject, NodePath):
     """
     """
@@ -163,11 +163,10 @@ class Actor(DirectObject, NodePath):
             #the actor for a few frames, otherwise it has no effect
             #the actor for a few frames, otherwise it has no effect
             a.fixBounds()
             a.fixBounds()
         """
         """
-        try:
-            self.Actor_initialized
+        if hasattr(self, 'Actor_initialized'):
             return
             return
-        except:
-            self.Actor_initialized = 1
+
+        self.Actor_initialized = 1
 
 
         # initialize our NodePath essence
         # initialize our NodePath essence
         NodePath.__init__(self)
         NodePath.__init__(self)
@@ -252,9 +251,9 @@ class Actor(DirectObject, NodePath):
             # make sure we have models
             # make sure we have models
             if models:
             if models:
                 # do we have a dictionary of models?
                 # do we have a dictionary of models?
-                if type(models) == dict:
+                if isinstance(models, dict):
                     # if this is a dictionary of dictionaries
                     # if this is a dictionary of dictionaries
-                    if type(models[next(iter(models))]) == dict:
+                    if isinstance(models[next(iter(models))], dict):
                         # then it must be a multipart actor w/LOD
                         # then it must be a multipart actor w/LOD
                         self.setLODNode(node = lodNode)
                         self.setLODNode(node = lodNode)
                         # preserve numerical order for lod's
                         # preserve numerical order for lod's
@@ -271,7 +270,7 @@ class Actor(DirectObject, NodePath):
                                                modelName, lodName, copy = copy,
                                                modelName, lodName, copy = copy,
                                                okMissing = okMissing)
                                                okMissing = okMissing)
                     # then if there is a dictionary of dictionaries of anims
                     # then if there is a dictionary of dictionaries of anims
-                    elif type(anims[next(iter(anims))]) == dict:
+                    elif isinstance(anims[next(iter(anims))], dict):
                         # then this is a multipart actor w/o LOD
                         # then this is a multipart actor w/o LOD
                         for partName in models:
                         for partName in models:
                             # pass in each part
                             # pass in each part
@@ -297,10 +296,10 @@ class Actor(DirectObject, NodePath):
             if anims:
             if anims:
                 if len(anims) >= 1:
                 if len(anims) >= 1:
                     # if so, does it have a dictionary of dictionaries?
                     # if so, does it have a dictionary of dictionaries?
-                    if type(anims[next(iter(anims))]) == dict:
+                    if isinstance(anims[next(iter(anims))], dict):
                         # are the models a dict of dicts too?
                         # are the models a dict of dicts too?
-                        if type(models) == dict:
-                            if type(models[next(iter(models))]) == dict:
+                        if isinstance(models, dict):
+                            if isinstance(models[next(iter(models))], dict):
                                 # then we have a multi-part w/ LOD
                                 # then we have a multi-part w/ LOD
                                 sortedKeys = list(models.keys())
                                 sortedKeys = list(models.keys())
                                 sortedKeys.sort()
                                 sortedKeys.sort()
@@ -313,7 +312,7 @@ class Actor(DirectObject, NodePath):
                                 # then it must be multi-part w/o LOD
                                 # then it must be multi-part w/o LOD
                                 for partName in anims:
                                 for partName in anims:
                                     self.loadAnims(anims[partName], partName)
                                     self.loadAnims(anims[partName], partName)
-                    elif type(models) == dict:
+                    elif isinstance(models, dict):
                         # then we have single-part w/ LOD
                         # then we have single-part w/ LOD
                         sortedKeys = list(models.keys())
                         sortedKeys = list(models.keys())
                         sortedKeys.sort()
                         sortedKeys.sort()
@@ -344,50 +343,46 @@ class Actor(DirectObject, NodePath):
             self.__geomNode.node().setFinal(1)
             self.__geomNode.node().setFinal(1)
 
 
     def delete(self):
     def delete(self):
-        try:
-            self.Actor_deleted
-            return
-        except:
+        if not hasattr(self, 'Actor_deleted'):
             self.Actor_deleted = 1
             self.Actor_deleted = 1
             self.cleanup()
             self.cleanup()
 
 
     def copyActor(self, other, overwrite=False):
     def copyActor(self, other, overwrite=False):
-            # act like a copy constructor
-            self.gotName = other.gotName
-
-            # copy the scene graph elements of other
-            if overwrite:
-                otherCopy = other.copyTo(NodePath())
-                otherCopy.detachNode()
-                # assign these elements to ourselve (overwrite)
-                self.assign(otherCopy)
-            else:
-                # just copy these to ourselves
-                otherCopy = other.copyTo(self)
-            # masad: check if otherCopy has a geomNode as its first child
-            # if actor is initialized with flattenable, then otherCopy, not
-            # its first child, is the geom node; check __init__, for reference
-            if other.getGeomNode().getName() == other.getName():
-                self.setGeomNode(otherCopy)
-            else:
-                self.setGeomNode(otherCopy.getChild(0))
-
-            # copy the switches for lods
-            self.switches = other.switches
-            self.__LODNode = self.find('**/+LODNode')
-            self.__hasLOD = 0
-            if not self.__LODNode.isEmpty():
-                self.__hasLOD = 1
+        # act like a copy constructor
+        self.gotName = other.gotName
+
+        # copy the scene graph elements of other
+        if overwrite:
+            otherCopy = other.copyTo(NodePath())
+            otherCopy.detachNode()
+            # assign these elements to ourselve (overwrite)
+            self.assign(otherCopy)
+        else:
+            # just copy these to ourselves
+            otherCopy = other.copyTo(self)
+        # masad: check if otherCopy has a geomNode as its first child
+        # if actor is initialized with flattenable, then otherCopy, not
+        # its first child, is the geom node; check __init__, for reference
+        if other.getGeomNode().getName() == other.getName():
+            self.setGeomNode(otherCopy)
+        else:
+            self.setGeomNode(otherCopy.getChild(0))
 
 
+        # copy the switches for lods
+        self.switches = other.switches
+        self.__LODNode = self.find('**/+LODNode')
+        self.__hasLOD = 0
+        if not self.__LODNode.isEmpty():
+            self.__hasLOD = 1
 
 
-            # copy the part dictionary from other
-            self.__copyPartBundles(other)
-            self.__copySubpartDict(other)
-            self.__subpartsComplete = other.__subpartsComplete
 
 
-            # copy the anim dictionary from other
-            self.__copyAnimControls(other)
+        # copy the part dictionary from other
+        self.__copyPartBundles(other)
+        self.__copySubpartDict(other)
+        self.__subpartsComplete = other.__subpartsComplete
 
 
+        # copy the anim dictionary from other
+        self.__copyAnimControls(other)
 
 
     def __cmp__(self, other):
     def __cmp__(self, other):
         # Actor inherits from NodePath, which inherits a definition of
         # Actor inherits from NodePath, which inherits a definition of
@@ -514,7 +509,7 @@ class Actor(DirectObject, NodePath):
         self.stop(None)
         self.stop(None)
         self.clearPythonData()
         self.clearPythonData()
         self.flush()
         self.flush()
-        if(self.__geomNode):
+        if self.__geomNode:
             self.__geomNode.removeNode()
             self.__geomNode.removeNode()
             self.__geomNode = None
             self.__geomNode = None
         if not self.isEmpty():
         if not self.isEmpty():
@@ -597,12 +592,10 @@ class Actor(DirectObject, NodePath):
                         'l':1,
                         'l':1,
                         'f':0}
                         'f':0}
 
 
-                """
-                sx = smap.get(x[0], None)
-
-                if sx is None:
-                    self.notify.error('Invalid lodName: %s' % x)
-                """
+                #sx = smap.get(x[0], None)
+                #
+                #if sx is None:
+                #    self.notify.error('Invalid lodName: %s' % x)
                 return smap[x[0]]
                 return smap[x[0]]
             else:
             else:
                 return int(x)
                 return int(x)
@@ -1471,7 +1464,7 @@ class Actor(DirectObject, NodePath):
             #iterate through for a specific part
             #iterate through for a specific part
             for lodData in self.__partBundleDict.values():
             for lodData in self.__partBundleDict.values():
                 partData = lodData.get(partName)
                 partData = lodData.get(partName)
-                if(partData):
+                if partData:
                     char = partData.partBundleNP
                     char = partData.partBundleNP
                     char.node().update()
                     char.node().update()
                     geomNodes = char.findAllMatches("**/+GeomNode")
                     geomNodes = char.findAllMatches("**/+GeomNode")
@@ -1659,6 +1652,8 @@ class Actor(DirectObject, NodePath):
 
 
         This method is deprecated.  You should use setBlend() instead.
         This method is deprecated.  You should use setBlend() instead.
         """
         """
+        if __debug__:
+            warnings.warn("This method is deprecated.  You should use setBlend() instead.", DeprecationWarning, stacklevel=2)
         self.setBlend(animBlend = True, blendType = blendType, partName = partName)
         self.setBlend(animBlend = True, blendType = blendType, partName = partName)
 
 
     def disableBlend(self, partName = None):
     def disableBlend(self, partName = None):
@@ -1668,6 +1663,8 @@ class Actor(DirectObject, NodePath):
 
 
         This method is deprecated.  You should use setBlend() instead.
         This method is deprecated.  You should use setBlend() instead.
         """
         """
+        if __debug__:
+            warnings.warn("This method is deprecated.  You should use setBlend() instead.", DeprecationWarning, stacklevel=2)
         self.setBlend(animBlend = False, partName = partName)
         self.setBlend(animBlend = False, partName = partName)
 
 
     def setControlEffect(self, animName, effect,
     def setControlEffect(self, animName, effect,
@@ -1693,11 +1690,20 @@ class Actor(DirectObject, NodePath):
         else:
         else:
             lodName = 'lodRoot'
             lodName = 'lodRoot'
 
 
-        try:
-            return self.__animControlDict[lodName][partName][animName].filename
-        except:
+        partDict = self.__animControlDict.get(lodName)
+        if partDict is None:
+            return None
+
+        animDict = partDict.get(partName)
+        if animDict is None:
+            return None
+
+        anim = animDict.get(animName)
+        if anim is None:
             return None
             return None
 
 
+        return anim.filename
+
     def getAnimControl(self, animName, partName=None, lodName=None,
     def getAnimControl(self, animName, partName=None, lodName=None,
                        allowAsyncBind = True):
                        allowAsyncBind = True):
         """
         """
@@ -1731,7 +1737,6 @@ class Actor(DirectObject, NodePath):
             if anim is None:
             if anim is None:
                 # anim was not present
                 # anim was not present
                 assert Actor.notify.debug("couldn't find anim: %s" % (animName))
                 assert Actor.notify.debug("couldn't find anim: %s" % (animName))
-                pass
             else:
             else:
                 # bind the animation first if we need to
                 # bind the animation first if we need to
                 if not anim.animControl:
                 if not anim.animControl:
@@ -1852,7 +1857,6 @@ class Actor(DirectObject, NodePath):
                         if anim is None:
                         if anim is None:
                             # anim was not present
                             # anim was not present
                             assert Actor.notify.debug("couldn't find anim: %s" % (animName))
                             assert Actor.notify.debug("couldn't find anim: %s" % (animName))
-                            pass
                         else:
                         else:
                             # bind the animation first if we need to
                             # bind the animation first if we need to
                             animControl = anim.animControl
                             animControl = anim.animControl
@@ -1987,7 +1991,7 @@ class Actor(DirectObject, NodePath):
 
 
         node = bundleNP.node()
         node = bundleNP.node()
         # A model loaded from disk will always have just one bundle.
         # A model loaded from disk will always have just one bundle.
-        assert(node.getNumBundles() == 1)
+        assert node.getNumBundles() == 1
         bundleHandle = node.getBundleHandle(0)
         bundleHandle = node.getBundleHandle(0)
 
 
         if self.mergeLODBundles:
         if self.mergeLODBundles:
@@ -2003,7 +2007,7 @@ class Actor(DirectObject, NodePath):
         bundleDict[partName] = Actor.PartDef(bundleNP, bundleHandle, partModel)
         bundleDict[partName] = Actor.PartDef(bundleNP, bundleHandle, partModel)
 
 
 
 
-    def makeSubpart(self, partName, includeJoints, excludeJoints = [],
+    def makeSubpart(self, partName, includeJoints, excludeJoints = (),
                     parent="modelRoot", overlapping = False):
                     parent="modelRoot", overlapping = False):
 
 
         """Defines a new "part" of the Actor that corresponds to the
         """Defines a new "part" of the Actor that corresponds to the
@@ -2128,10 +2132,7 @@ class Actor(DirectObject, NodePath):
             lodNames = ['common']
             lodNames = ['common']
         elif lodName == 'all':
         elif lodName == 'all':
             reload = False
             reload = False
-            lodNames = list(self.switches.keys())
-            lodNames.sort()
-            for i in range(0, len(lodNames)):
-                lodNames[i] = str(lodNames[i])
+            lodNames = list(map(str, sorted(self.switches.keys())))
         else:
         else:
             lodNames = [lodName]
             lodNames = [lodName]
 
 
@@ -2140,11 +2141,10 @@ class Actor(DirectObject, NodePath):
 
 
         firstLoad = True
         firstLoad = True
         if not reload:
         if not reload:
-            try:
-                self.__animControlDict[lodNames[0]][partName]
+            if lodNames[0] in self.__animControlDict and \
+               partName in self.__animControlDict[lodNames[0]]:
                 firstLoad = False
                 firstLoad = False
-            except:
-                pass
+
         for lName in lodNames:
         for lName in lodNames:
             if firstLoad:
             if firstLoad:
                 self.__animControlDict.setdefault(lName, {})
                 self.__animControlDict.setdefault(lName, {})
@@ -2438,7 +2438,7 @@ class Actor(DirectObject, NodePath):
         bundles in our part bundle dict that have matching names, and
         bundles in our part bundle dict that have matching names, and
         store the resulting anim controls in our own part bundle dict"""
         store the resulting anim controls in our own part bundle dict"""
 
 
-        assert(other.mergeLODBundles == self.mergeLODBundles)
+        assert other.mergeLODBundles == self.mergeLODBundles
 
 
         for lodName in other.__animControlDict:
         for lodName in other.__animControlDict:
             self.__animControlDict[lodName] = {}
             self.__animControlDict[lodName] = {}
@@ -2509,11 +2509,10 @@ class Actor(DirectObject, NodePath):
         for lodName, animList in self.getAnimBlends(animName, partName, lodName):
         for lodName, animList in self.getAnimBlends(animName, partName, lodName):
             print('LOD %s:' % (lodName))
             print('LOD %s:' % (lodName))
             for animName, blendList in animList:
             for animName, blendList in animList:
-
-                list = []
+                strings = []
                 for partName, effect in blendList:
                 for partName, effect in blendList:
-                    list.append('%s:%.3f' % (partName, effect))
-                print('  %s: %s' % (animName, ', '.join(list)))
+                    strings.append('%s:%.3f' % (partName, effect))
+                print('  %s: %s' % (animName, ', '.join(strings)))
 
 
     def osdAnimBlends(self, animName=None, partName=None, lodName=None):
     def osdAnimBlends(self, animName=None, partName=None, lodName=None):
         if not onScreenDebug.enabled:
         if not onScreenDebug.enabled:

+ 3 - 7
direct/src/actor/DistributedActor.py

@@ -8,9 +8,7 @@ from . import Actor
 
 
 class DistributedActor(DistributedNode.DistributedNode, Actor.Actor):
 class DistributedActor(DistributedNode.DistributedNode, Actor.Actor):
     def __init__(self, cr):
     def __init__(self, cr):
-        try:
-            self.DistributedActor_initialized
-        except:
+        if not hasattr(self, 'DistributedActor_initialized'):
             self.DistributedActor_initialized = 1
             self.DistributedActor_initialized = 1
             Actor.Actor.__init__(self)
             Actor.Actor.__init__(self)
             DistributedNode.DistributedNode.__init__(self, cr)
             DistributedNode.DistributedNode.__init__(self, cr)
@@ -20,14 +18,12 @@ class DistributedActor(DistributedNode.DistributedNode, Actor.Actor):
 
 
     def disable(self):
     def disable(self):
         # remove all anims, on all parts and all lods
         # remove all anims, on all parts and all lods
-        if (not self.isEmpty()):
+        if not self.isEmpty():
             Actor.Actor.unloadAnims(self, None, None, None)
             Actor.Actor.unloadAnims(self, None, None, None)
         DistributedNode.DistributedNode.disable(self)
         DistributedNode.DistributedNode.disable(self)
 
 
     def delete(self):
     def delete(self):
-        try:
-            self.DistributedActor_deleted
-        except:
+        if not hasattr(self, 'DistributedActor_deleted'):
             self.DistributedActor_deleted = 1
             self.DistributedActor_deleted = 1
             DistributedNode.DistributedNode.delete(self)
             DistributedNode.DistributedNode.delete(self)
             Actor.Actor.delete(self)
             Actor.Actor.delete(self)

+ 35 - 40
direct/src/cluster/ClusterClient.py

@@ -6,6 +6,7 @@ from .ClusterConfig import *
 from direct.directnotify import DirectNotifyGlobal
 from direct.directnotify import DirectNotifyGlobal
 from direct.showbase import DirectObject
 from direct.showbase import DirectObject
 from direct.task import Task
 from direct.task import Task
+from direct.task.TaskManagerGlobal import taskMgr
 import os
 import os
 
 
 
 
@@ -70,7 +71,7 @@ class ClusterClient(DirectObject.DirectObject):
             server = DisplayConnection(
             server = DisplayConnection(
                 self.qcm, serverConfig.serverName,
                 self.qcm, serverConfig.serverName,
                 serverConfig.serverMsgPort, self.msgHandler)
                 serverConfig.serverMsgPort, self.msgHandler)
-            if server == None:
+            if server is None:
                 self.notify.error('Could not open %s on %s port %d' %
                 self.notify.error('Could not open %s on %s port %d' %
                                   (serverConfig.serverConfigName,
                                   (serverConfig.serverConfigName,
                                    serverConfig.serverName,
                                    serverConfig.serverName,
@@ -138,15 +139,14 @@ class ClusterClient(DirectObject.DirectObject):
             object     = pair[1]
             object     = pair[1]
             name       = self.controlMappings[object][0]
             name       = self.controlMappings[object][0]
             serverList = self.controlMappings[object][1]
             serverList = self.controlMappings[object][1]
-            if (object in self.objectMappings):
+            if object in self.objectMappings:
                 self.moveObject(self.objectMappings[object],name,serverList,
                 self.moveObject(self.objectMappings[object],name,serverList,
                                 self.controlOffsets[object], self.objectHasColor[object])
                                 self.controlOffsets[object], self.objectHasColor[object])
         self.sendNamedMovementDone()
         self.sendNamedMovementDone()
         return Task.cont
         return Task.cont
 
 
     def sendNamedMovementDone(self, serverList = None):
     def sendNamedMovementDone(self, serverList = None):
-
-        if (serverList == None):
+        if serverList is None:
             serverList = range(len(self.serverList))
             serverList = range(len(self.serverList))
 
 
         for server in serverList:
         for server in serverList:
@@ -155,7 +155,6 @@ class ClusterClient(DirectObject.DirectObject):
 
 
 
 
     def redoSortedPriorities(self):
     def redoSortedPriorities(self):
-
         self.sortedControlMappings = []
         self.sortedControlMappings = []
         for key in self.controlMappings:
         for key in self.controlMappings:
             self.sortedControlMappings.append([self.controlPriorities[key],
             self.sortedControlMappings.append([self.controlPriorities[key],
@@ -169,7 +168,7 @@ class ClusterClient(DirectObject.DirectObject):
         hpr = nodePath.getHpr(render)
         hpr = nodePath.getHpr(render)
         scale = nodePath.getScale(render)
         scale = nodePath.getScale(render)
         hidden = nodePath.isHidden()
         hidden = nodePath.isHidden()
-        if (hasColor):
+        if hasColor:
             color = nodePath.getColor()
             color = nodePath.getColor()
         else:
         else:
             color = [1,1,1,1]
             color = [1,1,1,1]
@@ -193,7 +192,7 @@ class ClusterClient(DirectObject.DirectObject):
 
 
     def moveSelectedTask(self, state):
     def moveSelectedTask(self, state):
         # Update cluster if current display is a cluster client
         # Update cluster if current display is a cluster client
-        if (last is not None):
+        if last is not None:
             self.notify.debug('moving selected node path')
             self.notify.debug('moving selected node path')
             xyz = Point3(0)
             xyz = Point3(0)
             hpr = VBase3(0)
             hpr = VBase3(0)
@@ -204,24 +203,24 @@ class ClusterClient(DirectObject.DirectObject):
         return Task.cont
         return Task.cont
 
 
 
 
-    def addNamedObjectMapping(self,object,name,hasColor = True):
-        if (name not in self.objectMappings):
+    def addNamedObjectMapping(self, object, name, hasColor = True):
+        if name not in self.objectMappings:
             self.objectMappings[name] = object
             self.objectMappings[name] = object
             self.objectHasColor[name] = hasColor
             self.objectHasColor[name] = hasColor
         else:
         else:
             self.notify.debug('attempt to add duplicate named object: '+name)
             self.notify.debug('attempt to add duplicate named object: '+name)
 
 
     def removeObjectMapping(self,name):
     def removeObjectMapping(self,name):
-        if (name in self.objectMappings):
+        if name in self.objectMappings:
             self.objectMappings.pop(name)
             self.objectMappings.pop(name)
 
 
 
 
-    def addControlMapping(self,objectName,controlledName, serverList = None,
+    def addControlMapping(self, objectName, controlledName, serverList = None,
                           offset = None, priority = 0):
                           offset = None, priority = 0):
-        if (objectName not in self.controlMappings):
-            if (serverList == None):
+        if objectName not in self.controlMappings:
+            if serverList is None:
                 serverList = range(len(self.serverList))
                 serverList = range(len(self.serverList))
-            if (offset == None):
+            if offset is None:
                 offset = Vec3(0,0,0)
                 offset = Vec3(0,0,0)
 
 
             self.controlMappings[objectName] = [controlledName,serverList]
             self.controlMappings[objectName] = [controlledName,serverList]
@@ -233,30 +232,29 @@ class ClusterClient(DirectObject.DirectObject):
             for item in oldList:
             for item in oldList:
                 mergedList.append(item)
                 mergedList.append(item)
             for item in serverList:
             for item in serverList:
-                if (item not in mergedList):
+                if item not in mergedList:
                     mergedList.append(item)
                     mergedList.append(item)
 
 
         self.redoSortedPriorities()
         self.redoSortedPriorities()
             #self.notify.debug('attempt to add duplicate controlled object: '+name)
             #self.notify.debug('attempt to add duplicate controlled object: '+name)
 
 
-    def setControlMappingOffset(self,objectName,offset):
-        if (objectName in self.controlMappings):
+    def setControlMappingOffset(self, objectName, offset):
+        if objectName in self.controlMappings:
             self.controlOffsets[objectName] = offset
             self.controlOffsets[objectName] = offset
 
 
-    def removeControlMapping(self,name, serverList = None):
-        if (name in self.controlMappings):
-
-            if (serverList == None):
+    def removeControlMapping(self, name, serverList = None):
+        if name in self.controlMappings:
+            if serverList is None:
                 self.controlMappings.pop(name)
                 self.controlMappings.pop(name)
                 self.controlPriorities.pop(name)
                 self.controlPriorities.pop(name)
             else:
             else:
-                list = self.controlMappings[key][1]
+                oldList = self.controlMappings[key][1]
                 newList = []
                 newList = []
-                for server in list:
-                    if (server not in serverList):
+                for server in oldList:
+                    if server not in serverList:
                         newList.append(server)
                         newList.append(server)
                 self.controlMappings[key][1] = newList
                 self.controlMappings[key][1] = newList
-                if (len(newList) == 0):
+                if len(newList) == 0:
                     self.controlMappings.pop(name)
                     self.controlMappings.pop(name)
                     self.controlPriorities.pop(name)
                     self.controlPriorities.pop(name)
         self.redoSortedPriorities()
         self.redoSortedPriorities()
@@ -303,7 +301,7 @@ class ClusterClient(DirectObject.DirectObject):
             tag = self.taggedObjects[name]
             tag = self.taggedObjects[name]
             function = tag["selectFunction"]
             function = tag["selectFunction"]
             args     = tag["selectArgs"]
             args     = tag["selectArgs"]
-            if (function != None):
+            if function is not None:
                 function(*args)
                 function(*args)
         else:
         else:
             self(self.getNodePathFindCmd(nodePath) + '.select()', 0)
             self(self.getNodePathFindCmd(nodePath) + '.select()', 0)
@@ -315,7 +313,7 @@ class ClusterClient(DirectObject.DirectObject):
             tag = self.taggedObjects[name]
             tag = self.taggedObjects[name]
             function = tag["deselectFunction"]
             function = tag["deselectFunction"]
             args     = tag["deselectArgs"]
             args     = tag["deselectArgs"]
-            if (function != None):
+            if function is not None:
                 function(*args)
                 function(*args)
             self.startMoveSelectedTask()
             self.startMoveSelectedTask()
         self(self.getNodePathFindCmd(nodePath) + '.deselect()', 0)
         self(self.getNodePathFindCmd(nodePath) + '.deselect()', 0)
@@ -348,24 +346,23 @@ class ClusterClient(DirectObject.DirectObject):
 
 
 
 
     def handleDatagram(self,dgi,type,server):
     def handleDatagram(self,dgi,type,server):
-        if (type == CLUSTER_NONE):
+        if type == CLUSTER_NONE:
             pass
             pass
-        elif (type == CLUSTER_NAMED_OBJECT_MOVEMENT):
+        elif type == CLUSTER_NAMED_OBJECT_MOVEMENT:
             self.serverQueues[server].append(self.msgHandler.parseNamedMovementDatagram(dgi))
             self.serverQueues[server].append(self.msgHandler.parseNamedMovementDatagram(dgi))
             #self.handleNamedMovement(dgi)
             #self.handleNamedMovement(dgi)
         # when we recieve a 'named movement done' packet from a server we handle
         # when we recieve a 'named movement done' packet from a server we handle
         # all of its messages
         # all of its messages
-        elif (type == CLUSTER_NAMED_MOVEMENT_DONE):
+        elif type == CLUSTER_NAMED_MOVEMENT_DONE:
             self.handleMessageQueue(server)
             self.handleMessageQueue(server)
         else:
         else:
             self.notify.warning("Received unsupported packet type:" % type)
             self.notify.warning("Received unsupported packet type:" % type)
         return type
         return type
 
 
     def handleMessageQueue(self,server):
     def handleMessageQueue(self,server):
-
-        list = self.serverQueues[server]
+        queue = self.serverQueues[server]
         # handle all messages in the queue
         # handle all messages in the queue
-        for data in list:
+        for data in queue:
             #print dgi
             #print dgi
             self.handleNamedMovement(data)
             self.handleNamedMovement(data)
 
 
@@ -378,14 +375,14 @@ class ClusterClient(DirectObject.DirectObject):
 
 
         (name,x, y, z, h, p, r, sx, sy, sz,red,g,b,a, hidden) = data
         (name,x, y, z, h, p, r, sx, sy, sz,red,g,b,a, hidden) = data
         #print "name"
         #print "name"
-        #if (name == "camNode"):
+        #if name == "camNode":
         #    print x,y,z,h,p,r, sx, sy, sz,red,g,b,a, hidden
         #    print x,y,z,h,p,r, sx, sy, sz,red,g,b,a, hidden
-        if (name in self.objectMappings):
+        if name in self.objectMappings:
             self.objectMappings[name].setPosHpr(render, x, y, z, h, p, r)
             self.objectMappings[name].setPosHpr(render, x, y, z, h, p, r)
             self.objectMappings[name].setScale(render,sx,sy,sz)
             self.objectMappings[name].setScale(render,sx,sy,sz)
-            if (self.objectHasColor[name]):
+            if self.objectHasColor[name]:
                 self.objectMappings[name].setColor(red,g,b,a)
                 self.objectMappings[name].setColor(red,g,b,a)
-            if (hidden):
+            if hidden:
                 self.objectMappings[name].hide()
                 self.objectMappings[name].hide()
             else:
             else:
                 self.objectMappings[name].show()
                 self.objectMappings[name].show()
@@ -451,7 +448,7 @@ class DisplayConnection:
         self.tcpConn = qcm.openTCPClientConnection(
         self.tcpConn = qcm.openTCPClientConnection(
             serverName, port, gameServerTimeoutMs)
             serverName, port, gameServerTimeoutMs)
         # Test for bad connection
         # Test for bad connection
-        if self.tcpConn == None:
+        if self.tcpConn is None:
             return None
             return None
         else:
         else:
             self.tcpConn.setNoDelay(1)
             self.tcpConn.setNoDelay(1)
@@ -680,5 +677,3 @@ class DummyClusterClient(DirectObject.DirectObject):
         if fLocally:
         if fLocally:
             # Execute locally
             # Execute locally
             exec(commandString, __builtins__)
             exec(commandString, __builtins__)
-
-

+ 0 - 1
direct/src/cluster/ClusterConfig.py

@@ -165,4 +165,3 @@ ClientConfigs = {
                               }
                               }
                              ],
                              ],
     }
     }
-

+ 10 - 19
direct/src/cluster/ClusterMsgs.py

@@ -59,17 +59,17 @@ class ClusterMsgHandler:
         if qcr.dataAvailable():
         if qcr.dataAvailable():
             datagram = NetDatagram()
             datagram = NetDatagram()
             if qcr.getData(datagram):
             if qcr.getData(datagram):
-                (dgi, type) = self.readHeader(datagram)
+                (dgi, dtype) = self.readHeader(datagram)
             else:
             else:
                 dgi = None
                 dgi = None
-                type = CLUSTER_NONE
+                dtype = CLUSTER_NONE
                 self.notify.warning("getData returned false")
                 self.notify.warning("getData returned false")
         else:
         else:
             datagram = None
             datagram = None
             dgi = None
             dgi = None
-            type = CLUSTER_NONE
+            dtype = CLUSTER_NONE
         # Note, return datagram to keep a handle on the data
         # Note, return datagram to keep a handle on the data
-        return (datagram, dgi, type)
+        return (datagram, dgi, dtype)
 
 
     def blockingRead(self, qcr):
     def blockingRead(self, qcr):
         """
         """
@@ -85,19 +85,19 @@ class ClusterMsgHandler:
         # Data is available, create a datagram iterator
         # Data is available, create a datagram iterator
         datagram = NetDatagram()
         datagram = NetDatagram()
         if qcr.getData(datagram):
         if qcr.getData(datagram):
-            (dgi, type) = self.readHeader(datagram)
+            (dgi, dtype) = self.readHeader(datagram)
         else:
         else:
-            (dgi, type) = (None, CLUSTER_NONE)
+            (dgi, dtype) = (None, CLUSTER_NONE)
             self.notify.warning("getData returned false")
             self.notify.warning("getData returned false")
         # Note, return datagram to keep a handle on the data
         # Note, return datagram to keep a handle on the data
-        return (datagram, dgi, type)
+        return (datagram, dgi, dtype)
 
 
     def readHeader(self, datagram):
     def readHeader(self, datagram):
         dgi = PyDatagramIterator(datagram)
         dgi = PyDatagramIterator(datagram)
         number = dgi.getUint32()
         number = dgi.getUint32()
-        type = dgi.getUint8()
-        self.notify.debug("Packet %d type %d received" % (number, type))
-        return (dgi, type)
+        dtype = dgi.getUint8()
+        self.notify.debug("Packet %d type %d received" % (number, dtype))
+        return (dgi, dtype)
 
 
     def makeCamOffsetDatagram(self, xyz, hpr):
     def makeCamOffsetDatagram(self, xyz, hpr):
         datagram = PyDatagram()
         datagram = PyDatagram()
@@ -298,12 +298,3 @@ class ClusterMsgHandler:
         dt=dgi.getFloat32()
         dt=dgi.getFloat32()
         self.notify.debug('time data=%f %f' % (frameTime, dt))
         self.notify.debug('time data=%f %f' % (frameTime, dt))
         return (frameCount, frameTime, dt)
         return (frameCount, frameTime, dt)
-
-
-
-
-
-
-
-
-

+ 28 - 30
direct/src/cluster/ClusterServer.py

@@ -4,6 +4,7 @@ from direct.distributed.MsgTypes import *
 from direct.directnotify import DirectNotifyGlobal
 from direct.directnotify import DirectNotifyGlobal
 from direct.showbase import DirectObject
 from direct.showbase import DirectObject
 from direct.task import Task
 from direct.task import Task
+from direct.task.TaskManagerGlobal import taskMgr
 
 
 # NOTE: This assumes the following variables are set via bootstrap command line
 # NOTE: This assumes the following variables are set via bootstrap command line
 # arguments on server startup:
 # arguments on server startup:
@@ -100,16 +101,16 @@ class ClusterServer(DirectObject.DirectObject):
         return Task.cont
         return Task.cont
 
 
 
 
-    def addNamedObjectMapping(self,object,name,hasColor = True,
+    def addNamedObjectMapping(self, object, name, hasColor = True,
                               priority = 0):
                               priority = 0):
-        if (name not in self.objectMappings):
+        if name not in self.objectMappings:
             self.objectMappings[name] = object
             self.objectMappings[name] = object
             self.objectHasColor[name] = hasColor
             self.objectHasColor[name] = hasColor
         else:
         else:
             self.notify.debug('attempt to add duplicate named object: '+name)
             self.notify.debug('attempt to add duplicate named object: '+name)
 
 
-    def removeObjectMapping(self,name):
-        if (name in self.objectMappings):
+    def removeObjectMapping(self, name):
+        if name in self.objectMappings:
             self.objectMappings.pop(name)
             self.objectMappings.pop(name)
 
 
 
 
@@ -123,11 +124,11 @@ class ClusterServer(DirectObject.DirectObject):
         self.sortedControlMappings.sort()
         self.sortedControlMappings.sort()
 
 
 
 
-    def addControlMapping(self,objectName,controlledName, offset = None,
+    def addControlMapping(self, objectName, controlledName, offset = None,
                           priority = 0):
                           priority = 0):
-        if (objectName not in self.controlMappings):
+        if objectName not in self.controlMappings:
             self.controlMappings[objectName] = controlledName
             self.controlMappings[objectName] = controlledName
-            if (offset == None):
+            if offset is None:
                 offset = Vec3(0,0,0)
                 offset = Vec3(0,0,0)
             self.controlOffsets[objectName]  = offset
             self.controlOffsets[objectName]  = offset
             self.controlPriorities[objectName] = priority
             self.controlPriorities[objectName] = priority
@@ -135,13 +136,13 @@ class ClusterServer(DirectObject.DirectObject):
         else:
         else:
             self.notify.debug('attempt to add duplicate controlled object: '+name)
             self.notify.debug('attempt to add duplicate controlled object: '+name)
 
 
-    def setControlMappingOffset(self,objectName,offset):
-        if (objectName in self.controlMappings):
+    def setControlMappingOffset(self, objectName, offset):
+        if objectName in self.controlMappings:
             self.controlOffsets[objectName] = offset
             self.controlOffsets[objectName] = offset
 
 
 
 
-    def removeControlMapping(self,name):
-        if (name in self.controlMappings):
+    def removeControlMapping(self, name):
+        if name in self.controlMappings:
             self.controlMappings.pop(name)
             self.controlMappings.pop(name)
             self.controlPriorities.pop(name)
             self.controlPriorities.pop(name)
         self.redoSortedPriorities()
         self.redoSortedPriorities()
@@ -156,7 +157,7 @@ class ClusterServer(DirectObject.DirectObject):
         for pair in self.sortedControlPriorities:
         for pair in self.sortedControlPriorities:
             object = pair[1]
             object = pair[1]
             name   = self.controlMappings[object]
             name   = self.controlMappings[object]
-            if (object in self.objectMappings):
+            if object in self.objectMappings:
                 self.moveObject(self.objectMappings[object],name,self.controlOffsets[object],
                 self.moveObject(self.objectMappings[object],name,self.controlOffsets[object],
                                 self.objectHasColor[object])
                                 self.objectHasColor[object])
 
 
@@ -165,7 +166,6 @@ class ClusterServer(DirectObject.DirectObject):
 
 
 
 
     def sendNamedMovementDone(self):
     def sendNamedMovementDone(self):
-
         self.notify.debug("named movement done")
         self.notify.debug("named movement done")
         datagram = self.msgHandler.makeNamedMovementDone()
         datagram = self.msgHandler.makeNamedMovementDone()
         self.cw.send(datagram,self.lastConnection)
         self.cw.send(datagram,self.lastConnection)
@@ -176,7 +176,7 @@ class ClusterServer(DirectObject.DirectObject):
         xyz = nodePath.getPos(render) + offset
         xyz = nodePath.getPos(render) + offset
         hpr = nodePath.getHpr(render)
         hpr = nodePath.getHpr(render)
         scale = nodePath.getScale(render)
         scale = nodePath.getScale(render)
-        if (hasColor):
+        if hasColor:
             color = nodePath.getColor()
             color = nodePath.getColor()
         else:
         else:
             color = [1,1,1,1]
             color = [1,1,1,1]
@@ -243,34 +243,34 @@ class ClusterServer(DirectObject.DirectObject):
 
 
     def handleDatagram(self, dgi, type):
     def handleDatagram(self, dgi, type):
         """ Process a datagram depending upon type flag """
         """ Process a datagram depending upon type flag """
-        if (type == CLUSTER_NONE):
+        if type == CLUSTER_NONE:
             pass
             pass
-        elif (type == CLUSTER_EXIT):
+        elif type == CLUSTER_EXIT:
             print('GOT EXIT')
             print('GOT EXIT')
             import sys
             import sys
             sys.exit()
             sys.exit()
-        elif (type == CLUSTER_CAM_OFFSET):
+        elif type == CLUSTER_CAM_OFFSET:
             self.handleCamOffset(dgi)
             self.handleCamOffset(dgi)
-        elif (type == CLUSTER_CAM_FRUSTUM):
+        elif type == CLUSTER_CAM_FRUSTUM:
             self.handleCamFrustum(dgi)
             self.handleCamFrustum(dgi)
-        elif (type == CLUSTER_CAM_MOVEMENT):
+        elif type == CLUSTER_CAM_MOVEMENT:
             self.handleCamMovement(dgi)
             self.handleCamMovement(dgi)
-        elif (type == CLUSTER_SELECTED_MOVEMENT):
+        elif type == CLUSTER_SELECTED_MOVEMENT:
             self.handleSelectedMovement(dgi)
             self.handleSelectedMovement(dgi)
-        elif (type == CLUSTER_COMMAND_STRING):
+        elif type == CLUSTER_COMMAND_STRING:
             self.handleCommandString(dgi)
             self.handleCommandString(dgi)
-        elif (type == CLUSTER_SWAP_READY):
+        elif type == CLUSTER_SWAP_READY:
             pass
             pass
-        elif (type == CLUSTER_SWAP_NOW):
+        elif type == CLUSTER_SWAP_NOW:
             self.notify.debug('swapping')
             self.notify.debug('swapping')
             base.graphicsEngine.flipFrame()
             base.graphicsEngine.flipFrame()
-        elif (type == CLUSTER_TIME_DATA):
+        elif type == CLUSTER_TIME_DATA:
             self.notify.debug('time data')
             self.notify.debug('time data')
             self.handleTimeData(dgi)
             self.handleTimeData(dgi)
-        elif (type == CLUSTER_NAMED_OBJECT_MOVEMENT):
+        elif type == CLUSTER_NAMED_OBJECT_MOVEMENT:
             self.messageQueue.append(self.msgHandler.parseNamedMovementDatagram(dgi))
             self.messageQueue.append(self.msgHandler.parseNamedMovementDatagram(dgi))
             #self.handleNamedMovement(dgi)
             #self.handleNamedMovement(dgi)
-        elif (type == CLUSTER_NAMED_MOVEMENT_DONE):
+        elif type == CLUSTER_NAMED_MOVEMENT_DONE:
             #print "got done",self.messageQueue
             #print "got done",self.messageQueue
             #if (len(self.messageQueue) > 0):
             #if (len(self.messageQueue) > 0):
             #    print self.messageQueue[0]
             #    print self.messageQueue[0]
@@ -297,11 +297,11 @@ class ClusterServer(DirectObject.DirectObject):
     def handleNamedMovement(self, data):
     def handleNamedMovement(self, data):
         """ Update cameraJig position to reflect latest position """
         """ Update cameraJig position to reflect latest position """
         (name,x, y, z, h, p, r,sx,sy,sz, red, g, b, a, hidden) = data
         (name,x, y, z, h, p, r,sx,sy,sz, red, g, b, a, hidden) = data
-        if (name in self.objectMappings):
+        if name in self.objectMappings:
             self.objectMappings[name].setPosHpr(render, x, y, z, h, p, r)
             self.objectMappings[name].setPosHpr(render, x, y, z, h, p, r)
             self.objectMappings[name].setScale(render,sx,sy,sz)
             self.objectMappings[name].setScale(render,sx,sy,sz)
             self.objectMappings[name].setColor(red,g,b,a)
             self.objectMappings[name].setColor(red,g,b,a)
-            if (hidden):
+            if hidden:
                 self.objectMappings[name].hide()
                 self.objectMappings[name].hide()
             else:
             else:
                 self.objectMappings[name].show()
                 self.objectMappings[name].show()
@@ -346,5 +346,3 @@ class ClusterServer(DirectObject.DirectObject):
             exec(command, __builtins__)
             exec(command, __builtins__)
         except:
         except:
             pass
             pass
-
-

+ 2 - 148
direct/src/controls/BattleWalker.py

@@ -1,5 +1,6 @@
 
 
 from direct.showbase.InputStateGlobal import inputState
 from direct.showbase.InputStateGlobal import inputState
+from direct.showbase.MessengerGlobal import messenger
 from direct.task.Task import Task
 from direct.task.Task import Task
 from panda3d.core import *
 from panda3d.core import *
 from . import GravityWalker
 from . import GravityWalker
@@ -54,7 +55,7 @@ class BattleWalker(GravityWalker.GravityWalker):
 
 
         debugRunning = inputState.isSet("debugRunning")
         debugRunning = inputState.isSet("debugRunning")
 
 
-        if(debugRunning):
+        if debugRunning:
             self.speed*=base.debugRunningMultiplier
             self.speed*=base.debugRunningMultiplier
             self.slideSpeed*=base.debugRunningMultiplier
             self.slideSpeed*=base.debugRunningMultiplier
             self.rotationSpeed*=1.25
             self.rotationSpeed*=1.25
@@ -139,150 +140,3 @@ class BattleWalker(GravityWalker.GravityWalker):
         if self.moving or jump:
         if self.moving or jump:
             messenger.send("avatarMoving")
             messenger.send("avatarMoving")
         return Task.cont
         return Task.cont
-
-    if 0:
-        def handleAvatarControls(self, task):
-            # If targetNp is not available, revert back to GravityWalker.handleAvatarControls.
-            # This situation occurs when the target dies, but we aren't switched out of
-            # battle walker control mode.
-
-            targetNp = self.avatarNodePath.currentTarget
-            if not BattleStrafe or targetNp == None or targetNp.isEmpty():
-                return GravityWalker.GravityWalker.handleAvatarControls(self, task)
-
-            # get the button states:
-            run = inputState.isSet("run")
-            forward = inputState.isSet("forward")
-            reverse = inputState.isSet("reverse")
-            turnLeft = inputState.isSet("turnLeft")
-            turnRight = inputState.isSet("turnRight")
-            slide = inputState.isSet("slide")
-            jump = inputState.isSet("jump")
-            # Determine what the speeds are based on the buttons:
-            self.advanceSpeed=(forward and self.avatarControlForwardSpeed or
-                               reverse and -self.avatarControlReverseSpeed)
-            if run and self.advanceSpeed>0.0:
-                self.advanceSpeed*=2.0 #*#
-            # Should fSlide be renamed slideButton?
-            self.slideSpeed=.15*(turnLeft and -self.avatarControlForwardSpeed or
-                                 turnRight and self.avatarControlForwardSpeed)
-            print('slideSpeed: %s' % self.slideSpeed)
-            self.rotationSpeed=0
-            self.speed=0
-
-            debugRunning = inputState.isSet("debugRunning")
-            if debugRunning:
-                self.advanceSpeed*=4.0
-                self.slideSpeed*=4.0
-                self.rotationSpeed*=1.25
-
-            if self.needToDeltaPos:
-                self.setPriorParentVector()
-                self.needToDeltaPos = 0
-            if self.wantDebugIndicator:
-                self.displayDebugInfo()
-            if self.lifter.isOnGround():
-                if self.isAirborne:
-                    self.isAirborne = 0
-                    assert self.debugPrint("isAirborne 0 due to isOnGround() true")
-                    impact = self.lifter.getImpactVelocity()
-                    if impact < -30.0:
-                        messenger.send("jumpHardLand")
-                        self.startJumpDelay(0.3)
-                    else:
-                        messenger.send("jumpLand")
-                        if impact < -5.0:
-                            self.startJumpDelay(0.2)
-                        # else, ignore the little potholes.
-                assert self.isAirborne == 0
-                self.priorParent = Vec3.zero()
-                if jump and self.mayJump:
-                    # The jump button is down and we're close
-                    # enough to the ground to jump.
-                    self.lifter.addVelocity(self.avatarControlJumpForce)
-                    messenger.send("jumpStart")
-                    self.isAirborne = 1
-                    assert self.debugPrint("isAirborne 1 due to jump")
-            else:
-                if self.isAirborne == 0:
-                    assert self.debugPrint("isAirborne 1 due to isOnGround() false")
-                self.isAirborne = 1
-
-            self.__oldPosDelta = self.avatarNodePath.getPosDelta(render)
-            # How far did we move based on the amount of time elapsed?
-            self.__oldDt = ClockObject.getGlobalClock().getDt()
-            dt=self.__oldDt
-
-            # Before we do anything with position or orientation, make the avatar
-            # face it's target.  Only allow rMax degrees rotation per frame, so
-            # we don't get an unnatural spinning effect
-            curH = self.avatarNodePath.getH()
-            self.avatarNodePath.headsUp(targetNp)
-            newH = self.avatarNodePath.getH()
-            delH = reduceAngle(newH-curH)
-            rMax = 10
-            if delH < -rMax:
-                self.avatarNodePath.setH(curH-rMax)
-                self.rotationSpeed=-self.avatarControlRotateSpeed
-            elif delH > rMax:
-                self.avatarNodePath.setH(curH+rMax)
-                self.rotationSpeed=self.avatarControlRotateSpeed
-
-            # Check to see if we're moving at all:
-            self.moving = self.speed or self.slideSpeed or self.rotationSpeed or (self.priorParent!=Vec3.zero())
-            if self.moving:
-                distance = dt * self.speed
-                slideDistance = dt * self.slideSpeed
-                print('slideDistance: %s' % slideDistance)
-                rotation = dt * self.rotationSpeed
-
-                # Take a step in the direction of our previous heading.
-                self.vel=Vec3(Vec3.forward() * distance +
-                              Vec3.right() * slideDistance)
-                if self.vel != Vec3.zero() or self.priorParent != Vec3.zero():
-                    if 1:
-                        # rotMat is the rotation matrix corresponding to
-                        # our previous heading.
-                        rotMat=Mat3.rotateMatNormaxis(self.avatarNodePath.getH(), Vec3.up())
-                        step=(self.priorParent * dt) + rotMat.xform(self.vel)
-                        self.avatarNodePath.setFluidPos(Point3(
-                                self.avatarNodePath.getPos()+step))
-                self.avatarNodePath.setH(self.avatarNodePath.getH()+rotation)
-            else:
-                self.vel.set(0.0, 0.0, 0.0)
-
-            """
-            # Check to see if we're moving at all:
-            self.moving = self.advanceSpeed or self.slideSpeed or self.rotationSpeed or (self.priorParent!=Vec3.zero())
-            if self.moving:
-                distance = dt * self.advanceSpeed
-                slideDistance = dt * self.slideSpeed
-                rotation = dt * self.rotationSpeed
-
-                # Prevent avatar from getting too close to target
-                d = self.avatarNodePath.getPos(targetNp)
-                # TODO:  make min distance adjust for current weapon
-                if (d[0]*d[0]+d[1]*d[1] < 6.0 and distance > 0):
-                    # move the avatar sideways instead of forward
-                    slideDistance += .2
-                    distance = 0
-
-                # Take a step in the direction of our previous heading.
-                self.vel=Vec3(Vec3.forward() * distance +
-                              Vec3.right() * slideDistance)
-                if self.vel != Vec3.zero() or self.priorParent != Vec3.zero():
-                    # rotMat is the rotation matrix corresponding to
-                    # our previous heading.
-                    rotMat=Mat3.rotateMatNormaxis(self.avatarNodePath.getH(), Vec3.up())
-                    step=rotMat.xform(self.vel) + (self.priorParent * dt)
-                    self.avatarNodePath.setFluidPos(Point3(
-                        self.avatarNodePath.getPos()+step))
-                self.avatarNodePath.setH(self.avatarNodePath.getH()+rotation)
-            else:
-                self.vel.set(0.0, 0.0, 0.0)
-            """
-            if self.moving or jump:
-                messenger.send("avatarMoving")
-            return Task.cont
-
-

+ 9 - 9
direct/src/controls/ControlManager.py

@@ -1,5 +1,6 @@
 
 
 from direct.showbase.InputStateGlobal import inputState
 from direct.showbase.InputStateGlobal import inputState
+from direct.showbase.MessengerGlobal import messenger
 #from DirectGui import *
 #from DirectGui import *
 #from PythonUtil import *
 #from PythonUtil import *
 #from IntervalGlobal import *
 #from IntervalGlobal import *
@@ -292,18 +293,18 @@ class ControlManager:
         self.forceAvJumpToken.release()
         self.forceAvJumpToken.release()
         self.forceAvJumpToken = None
         self.forceAvJumpToken = None
 
 
-    def monitor(self, foo):
+    def monitor(self, _):
         #assert self.debugPrint("monitor()")
         #assert self.debugPrint("monitor()")
         #if 1:
         #if 1:
         #    airborneHeight=self.avatar.getAirborneHeight()
         #    airborneHeight=self.avatar.getAirborneHeight()
         #    onScreenDebug.add("airborneHeight", "% 10.4f"%(airborneHeight,))
         #    onScreenDebug.add("airborneHeight", "% 10.4f"%(airborneHeight,))
-        if 0:
-            onScreenDebug.add("InputState forward", "%d"%(inputState.isSet("forward")))
-            onScreenDebug.add("InputState reverse", "%d"%(inputState.isSet("reverse")))
-            onScreenDebug.add("InputState turnLeft", "%d"%(inputState.isSet("turnLeft")))
-            onScreenDebug.add("InputState turnRight", "%d"%(inputState.isSet("turnRight")))
-            onScreenDebug.add("InputState slideLeft", "%d"%(inputState.isSet("slideLeft")))
-            onScreenDebug.add("InputState slideRight", "%d"%(inputState.isSet("slideRight")))
+        #if 0:
+        #    onScreenDebug.add("InputState forward", "%d"%(inputState.isSet("forward")))
+        #    onScreenDebug.add("InputState reverse", "%d"%(inputState.isSet("reverse")))
+        #    onScreenDebug.add("InputState turnLeft", "%d"%(inputState.isSet("turnLeft")))
+        #    onScreenDebug.add("InputState turnRight", "%d"%(inputState.isSet("turnRight")))
+        #    onScreenDebug.add("InputState slideLeft", "%d"%(inputState.isSet("slideLeft")))
+        #    onScreenDebug.add("InputState slideRight", "%d"%(inputState.isSet("slideRight")))
         return Task.cont
         return Task.cont
 
 
     def setWASDTurn(self, turn):
     def setWASDTurn(self, turn):
@@ -343,4 +344,3 @@ class ControlManager:
 
 
             inputState.set("turnLeft", False, inputSource=inputState.WASD)
             inputState.set("turnLeft", False, inputSource=inputState.WASD)
             inputState.set("turnRight", False, inputSource=inputState.WASD)
             inputState.set("turnRight", False, inputSource=inputState.WASD)
-

+ 2 - 0
direct/src/controls/DevWalker.py

@@ -19,7 +19,9 @@ animations based on walker events.
 from direct.showbase.InputStateGlobal import inputState
 from direct.showbase.InputStateGlobal import inputState
 from direct.directnotify import DirectNotifyGlobal
 from direct.directnotify import DirectNotifyGlobal
 from direct.showbase import DirectObject
 from direct.showbase import DirectObject
+from direct.showbase.MessengerGlobal import messenger
 from direct.task.Task import Task
 from direct.task.Task import Task
+from direct.task.TaskManagerGlobal import taskMgr
 from panda3d.core import *
 from panda3d.core import *
 
 
 
 

+ 10 - 8
direct/src/controls/GravityWalker.py

@@ -19,10 +19,12 @@ from direct.directnotify.DirectNotifyGlobal import directNotify
 from direct.showbase import DirectObject
 from direct.showbase import DirectObject
 from direct.controls.ControlManager import CollisionHandlerRayStart
 from direct.controls.ControlManager import CollisionHandlerRayStart
 from direct.showbase.InputStateGlobal import inputState
 from direct.showbase.InputStateGlobal import inputState
+from direct.showbase.MessengerGlobal import messenger
 from direct.task.Task import Task
 from direct.task.Task import Task
-from panda3d.core import *
+from direct.task.TaskManagerGlobal import taskMgr
 from direct.extensions_native import VBase3_extensions
 from direct.extensions_native import VBase3_extensions
 from direct.extensions_native import VBase4_extensions
 from direct.extensions_native import VBase4_extensions
+from panda3d.core import *
 import math
 import math
 
 
 
 
@@ -142,7 +144,7 @@ class GravityWalker(DirectObject.DirectObject):
         cSphereNode.setIntoCollideMask(BitMask32.allOff())
         cSphereNode.setIntoCollideMask(BitMask32.allOff())
 
 
         # set up collision mechanism
         # set up collision mechanism
-        if config.GetBool('want-fluid-pusher', 0):
+        if ConfigVariableBool('want-fluid-pusher', 0):
             self.pusher = CollisionHandlerFluidPusher()
             self.pusher = CollisionHandlerFluidPusher()
         else:
         else:
             self.pusher = CollisionHandlerPusher()
             self.pusher = CollisionHandlerPusher()
@@ -284,11 +286,11 @@ class GravityWalker(DirectObject.DirectObject):
             # make sure we have a shadow traverser
             # make sure we have a shadow traverser
             base.initShadowTrav()
             base.initShadowTrav()
             if active:
             if active:
-                if 1:
-                    # Please let skyler or drose know if this is causing a problem
-                    # This is a bit of a hack fix:
-                    self.avatarNodePath.setP(0.0)
-                    self.avatarNodePath.setR(0.0)
+                # Please let skyler or drose know if this is causing a problem
+                # This is a bit of a hack fix:
+                self.avatarNodePath.setP(0.0)
+                self.avatarNodePath.setR(0.0)
+
                 self.cTrav.addCollider(self.cWallSphereNodePath, self.pusher)
                 self.cTrav.addCollider(self.cWallSphereNodePath, self.pusher)
                 if self.wantFloorSphere:
                 if self.wantFloorSphere:
                     self.cTrav.addCollider(self.cFloorSphereNodePath, self.pusherFloor)
                     self.cTrav.addCollider(self.cFloorSphereNodePath, self.pusherFloor)
@@ -440,7 +442,7 @@ class GravityWalker(DirectObject.DirectObject):
             self.slideSpeed *= GravityWalker.DiagonalFactor
             self.slideSpeed *= GravityWalker.DiagonalFactor
 
 
         debugRunning = inputState.isSet("debugRunning")
         debugRunning = inputState.isSet("debugRunning")
-        if(debugRunning):
+        if debugRunning:
             self.speed*=base.debugRunningMultiplier
             self.speed*=base.debugRunningMultiplier
             self.slideSpeed*=base.debugRunningMultiplier
             self.slideSpeed*=base.debugRunningMultiplier
             self.rotationSpeed*=1.25
             self.rotationSpeed*=1.25

+ 2 - 0
direct/src/controls/InputState.py

@@ -1,6 +1,8 @@
 from direct.directnotify import DirectNotifyGlobal
 from direct.directnotify import DirectNotifyGlobal
 from direct.showbase import DirectObject
 from direct.showbase import DirectObject
 from direct.showbase.PythonUtil import SerialNumGen
 from direct.showbase.PythonUtil import SerialNumGen
+from direct.showbase.MessengerGlobal import messenger
+
 
 
 # internal class, don't create these on your own
 # internal class, don't create these on your own
 class InputStateToken:
 class InputStateToken:

+ 7 - 5
direct/src/controls/NonPhysicsWalker.py

@@ -20,7 +20,9 @@ from direct.directnotify import DirectNotifyGlobal
 from direct.showbase import DirectObject
 from direct.showbase import DirectObject
 from direct.controls.ControlManager import CollisionHandlerRayStart
 from direct.controls.ControlManager import CollisionHandlerRayStart
 from direct.showbase.InputStateGlobal import inputState
 from direct.showbase.InputStateGlobal import inputState
+from direct.showbase.MessengerGlobal import messenger
 from direct.task.Task import Task
 from direct.task.Task import Task
+from direct.task.TaskManagerGlobal import taskMgr
 from panda3d.core import *
 from panda3d.core import *
 
 
 class NonPhysicsWalker(DirectObject.DirectObject):
 class NonPhysicsWalker(DirectObject.DirectObject):
@@ -184,7 +186,7 @@ class NonPhysicsWalker(DirectObject.DirectObject):
         tempCTrav = CollisionTraverser("oneTimeCollide")
         tempCTrav = CollisionTraverser("oneTimeCollide")
         tempCTrav.addCollider(self.cSphereNodePath, self.pusher)
         tempCTrav.addCollider(self.cSphereNodePath, self.pusher)
         tempCTrav.addCollider(self.cRayNodePath, self.lifter)
         tempCTrav.addCollider(self.cRayNodePath, self.lifter)
-        tempCTrav.traverse(render)
+        tempCTrav.traverse(base.render)
 
 
     def addBlastForce(self, vector):
     def addBlastForce(self, vector):
         pass
         pass
@@ -268,12 +270,12 @@ class NonPhysicsWalker(DirectObject.DirectObject):
         else:
         else:
             self.vel.set(0.0, 0.0, 0.0)
             self.vel.set(0.0, 0.0, 0.0)
 
 
-        self.__oldPosDelta = self.avatarNodePath.getPosDelta(render)
+        self.__oldPosDelta = self.avatarNodePath.getPosDelta(base.render)
         self.__oldDt = dt
         self.__oldDt = dt
 
 
-        try:
-            self.worldVelocity = self.__oldPosDelta*(1/self.__oldDt)
-        except:
+        if self.__oldDt != 0:
+            self.worldVelocity = self.__oldPosDelta * (1 / self.__oldDt)
+        else:
             # divide by zero
             # divide by zero
             self.worldVelocity = 0
             self.worldVelocity = 0
 
 

+ 0 - 5
direct/src/controls/ObserverWalker.py

@@ -31,9 +31,6 @@ class ObserverWalker(NonPhysicsWalker.NonPhysicsWalker):
         """
         """
         Set up the avatar for collisions
         Set up the avatar for collisions
         """
         """
-        """
-        Set up the avatar for collisions
-        """
         assert not avatarNodePath.isEmpty()
         assert not avatarNodePath.isEmpty()
 
 
         self.cTrav = collisionTraverser
         self.cTrav = collisionTraverser
@@ -102,11 +99,9 @@ class ObserverWalker(NonPhysicsWalker.NonPhysicsWalker):
         Activate the arrow keys, etc.
         Activate the arrow keys, etc.
         """
         """
         assert self.debugPrint("enableAvatarControls")
         assert self.debugPrint("enableAvatarControls")
-        pass
 
 
     def disableAvatarControls(self):
     def disableAvatarControls(self):
         """
         """
         Ignore the arrow keys, etc.
         Ignore the arrow keys, etc.
         """
         """
         assert self.debugPrint("disableAvatarControls")
         assert self.debugPrint("disableAvatarControls")
-        pass

+ 98 - 135
direct/src/controls/PhysicsWalker.py

@@ -20,12 +20,14 @@ from direct.directnotify import DirectNotifyGlobal
 from direct.showbase import DirectObject
 from direct.showbase import DirectObject
 from direct.controls.ControlManager import CollisionHandlerRayStart
 from direct.controls.ControlManager import CollisionHandlerRayStart
 from direct.showbase.InputStateGlobal import inputState
 from direct.showbase.InputStateGlobal import inputState
+from direct.showbase.MessengerGlobal import messenger
 from direct.task.Task import Task
 from direct.task.Task import Task
-from panda3d.core import *
-from panda3d.physics import *
+from direct.task.TaskManagerGlobal import taskMgr
 from direct.extensions_native import Mat3_extensions
 from direct.extensions_native import Mat3_extensions
 from direct.extensions_native import VBase3_extensions
 from direct.extensions_native import VBase3_extensions
 from direct.extensions_native import VBase4_extensions
 from direct.extensions_native import VBase4_extensions
+from panda3d.core import *
+from panda3d.physics import *
 import math
 import math
 
 
 #import LineStream
 #import LineStream
@@ -131,11 +133,6 @@ class PhysicsWalker(DirectObject.DirectObject):
             assert onScreenDebug.add("height", height.getZ())
             assert onScreenDebug.add("height", height.getZ())
             return height.getZ() - self.floorOffset
             return height.getZ() - self.floorOffset
         else: # useCollisionHandlerQueue
         else: # useCollisionHandlerQueue
-            """
-            returns the height of the avatar above the ground.
-            If there is no floor below the avatar, 0.0 is returned.
-            aka get airborne height.
-            """
             height = 0.0
             height = 0.0
             #*#self.cRayTrav.traverse(render)
             #*#self.cRayTrav.traverse(render)
             if self.cRayQueue.getNumEntries() != 0:
             if self.cRayQueue.getNumEntries() != 0:
@@ -239,7 +236,7 @@ class PhysicsWalker(DirectObject.DirectObject):
         self.floorOffset = floorOffset = 7.0
         self.floorOffset = floorOffset = 7.0
 
 
         self.avatarNodePath = self.setupPhysics(avatarNodePath)
         self.avatarNodePath = self.setupPhysics(avatarNodePath)
-        if 0 or self.useHeightRay:
+        if self.useHeightRay:
             #self.setupRay(floorBitmask, avatarRadius)
             #self.setupRay(floorBitmask, avatarRadius)
             self.setupRay(floorBitmask, 0.0)
             self.setupRay(floorBitmask, 0.0)
         self.setupSphere(wallBitmask|floorBitmask, avatarRadius)
         self.setupSphere(wallBitmask|floorBitmask, avatarRadius)
@@ -257,14 +254,14 @@ class PhysicsWalker(DirectObject.DirectObject):
         self.cSphereNodePath.show()
         self.cSphereNodePath.show()
         if indicator:
         if indicator:
             # Indicator Node:
             # Indicator Node:
-            change=render.attachNewNode("change")
+            change = render.attachNewNode("change")
             #change.setPos(Vec3(1.0, 1.0, 1.0))
             #change.setPos(Vec3(1.0, 1.0, 1.0))
             #change.setHpr(0.0, 0.0, 0.0)
             #change.setHpr(0.0, 0.0, 0.0)
             change.setScale(0.1)
             change.setScale(0.1)
             #change.setColor(Vec4(1.0, 1.0, 1.0, 1.0))
             #change.setColor(Vec4(1.0, 1.0, 1.0, 1.0))
             indicator.reparentTo(change)
             indicator.reparentTo(change)
 
 
-            indicatorNode=render.attachNewNode("physVelocityIndicator")
+            indicatorNode = render.attachNewNode("physVelocityIndicator")
             #indicatorNode.setScale(0.1)
             #indicatorNode.setScale(0.1)
             #indicatorNode.setP(90.0)
             #indicatorNode.setP(90.0)
             indicatorNode.setPos(self.avatarNodePath, 0.0, 0.0, 6.0)
             indicatorNode.setPos(self.avatarNodePath, 0.0, 0.0, 6.0)
@@ -273,7 +270,7 @@ class PhysicsWalker(DirectObject.DirectObject):
 
 
             self.physVelocityIndicator=indicatorNode
             self.physVelocityIndicator=indicatorNode
             # Contact Node:
             # Contact Node:
-            contactIndicatorNode=render.attachNewNode("physContactIndicator")
+            contactIndicatorNode = render.attachNewNode("physContactIndicator")
             contactIndicatorNode.setScale(0.25)
             contactIndicatorNode.setScale(0.25)
             contactIndicatorNode.setP(90.0)
             contactIndicatorNode.setP(90.0)
             contactIndicatorNode.setPos(self.avatarNodePath, 0.0, 0.0, 5.0)
             contactIndicatorNode.setPos(self.avatarNodePath, 0.0, 0.0, 5.0)
@@ -472,137 +469,103 @@ class PhysicsWalker(DirectObject.DirectObject):
                 onScreenDebug.add("posDelta1",
                 onScreenDebug.add("posDelta1",
                     self.avatarNodePath.getPosDelta(render).pPrintValues())
                     self.avatarNodePath.getPosDelta(render).pPrintValues())
 
 
-                if 0:
-                    onScreenDebug.add("posDelta3",
-                        render.getRelativeVector(
-                            self.avatarNodePath,
-                            self.avatarNodePath.getPosDelta(render)).pPrintValues())
-
-                if 0:
-                    onScreenDebug.add("gravity",
-                        self.gravity.getLocalVector().pPrintValues())
-                    onScreenDebug.add("priorParent",
-                        self.priorParent.getLocalVector().pPrintValues())
-                    onScreenDebug.add("avatarViscosity",
-                        "% 10.4f"%(self.avatarViscosity.getCoef(),))
-
-                    onScreenDebug.add("physObject pos",
-                        physObject.getPosition().pPrintValues())
-                    onScreenDebug.add("physObject hpr",
-                        physObject.getOrientation().getHpr().pPrintValues())
-                    onScreenDebug.add("physObject orien",
-                        physObject.getOrientation().pPrintValues())
-
-                if 1:
-                    onScreenDebug.add("physObject vel",
-                        physObject.getVelocity().pPrintValues())
-                    onScreenDebug.add("physObject len",
-                        "% 10.4f"%physObject.getVelocity().length())
-
-                if 0:
-                    onScreenDebug.add("posDelta4",
-                        self.priorParentNp.getRelativeVector(
-                            render,
-                            self.avatarNodePath.getPosDelta(render)).pPrintValues())
-
-                if 1:
-                    onScreenDebug.add("priorParent",
-                        self.priorParent.getLocalVector().pPrintValues())
-
-                if 0:
-                    onScreenDebug.add("priorParent po",
-                        self.priorParent.getVector(physObject).pPrintValues())
-
-                if 0:
-                    onScreenDebug.add("__posDelta",
-                        self.__oldPosDelta.pPrintValues())
-
-                if 1:
-                    onScreenDebug.add("contact",
-                        contact.pPrintValues())
-                    #onScreenDebug.add("airborneHeight", "% 10.4f"%(
-                    #    self.getAirborneHeight(),))
-
-                if 0:
-                    onScreenDebug.add("__oldContact",
-                        contact.pPrintValues())
-                    onScreenDebug.add("__oldAirborneHeight", "% 10.4f"%(
-                        self.getAirborneHeight(),))
-        airborneHeight=self.getAirborneHeight()
+                #onScreenDebug.add("posDelta3",
+                #    render.getRelativeVector(
+                #        self.avatarNodePath,
+                #        self.avatarNodePath.getPosDelta(render)).pPrintValues())
+
+                #onScreenDebug.add("gravity",
+                #    self.gravity.getLocalVector().pPrintValues())
+                #onScreenDebug.add("priorParent",
+                #    self.priorParent.getLocalVector().pPrintValues())
+                #onScreenDebug.add("avatarViscosity",
+                #    "% 10.4f"%(self.avatarViscosity.getCoef(),))
+                #
+                #onScreenDebug.add("physObject pos",
+                #    physObject.getPosition().pPrintValues())
+                #onScreenDebug.add("physObject hpr",
+                #    physObject.getOrientation().getHpr().pPrintValues())
+                #onScreenDebug.add("physObject orien",
+                #    physObject.getOrientation().pPrintValues())
+
+                onScreenDebug.add("physObject vel",
+                    physObject.getVelocity().pPrintValues())
+                onScreenDebug.add("physObject len",
+                    "% 10.4f"%physObject.getVelocity().length())
+
+                #onScreenDebug.add("posDelta4",
+                #    self.priorParentNp.getRelativeVector(
+                #        render,
+                #        self.avatarNodePath.getPosDelta(render)).pPrintValues())
+
+                onScreenDebug.add("priorParent",
+                    self.priorParent.getLocalVector().pPrintValues())
+
+                #onScreenDebug.add("priorParent po",
+                #    self.priorParent.getVector(physObject).pPrintValues())
+
+                #onScreenDebug.add("__posDelta",
+                #    self.__oldPosDelta.pPrintValues())
+
+                onScreenDebug.add("contact",
+                    contact.pPrintValues())
+                #onScreenDebug.add("airborneHeight", "% 10.4f"%(
+                #    self.getAirborneHeight(),))
+
+                #onScreenDebug.add("__oldContact",
+                #    contact.pPrintValues())
+                #onScreenDebug.add("__oldAirborneHeight", "% 10.4f"%(
+                #    self.getAirborneHeight(),))
+        airborneHeight = self.getAirborneHeight()
         if airborneHeight > self.highMark:
         if airborneHeight > self.highMark:
             self.highMark = airborneHeight
             self.highMark = airborneHeight
             if __debug__:
             if __debug__:
                 onScreenDebug.add("highMark", "% 10.4f"%(self.highMark,))
                 onScreenDebug.add("highMark", "% 10.4f"%(self.highMark,))
         #if airborneHeight < 0.1: #contact!=Vec3.zero():
         #if airborneHeight < 0.1: #contact!=Vec3.zero():
-        if 1:
-            if (airborneHeight > self.avatarRadius*0.5
-                    or physObject.getVelocity().getZ() > 0.0
-                    ): # Check stair angles before changing this.
-                # ...the avatar is airborne (maybe a lot or a tiny amount).
-                self.isAirborne = 1
-            else:
-                # ...the avatar is very close to the ground (close enough to be
-                # considered on the ground).
-                if self.isAirborne and physObject.getVelocity().getZ() <= 0.0:
-                    # ...the avatar has landed.
-                    contactLength = contact.length()
-                    if contactLength>self.__hardLandingForce:
-                        #print "jumpHardLand"
-                        messenger.send("jumpHardLand")
-                    else:
-                        #print "jumpLand"
-                        messenger.send("jumpLand")
-                    self.priorParent.setVector(Vec3.zero())
-                    self.isAirborne = 0
-                elif jump:
-                    #print "jump"
-                    #self.__jumpButton=0
-                    messenger.send("jumpStart")
-                    if 0:
-                        # ...jump away from walls and with with the slope normal.
-                        jumpVec=Vec3(contact+Vec3.up())
-                        #jumpVec=Vec3(rotAvatarToPhys.xform(jumpVec))
-                        jumpVec.normalize()
-                    else:
-                        # ...jump straight up, even if next to a wall.
-                        jumpVec=Vec3.up()
-                    jumpVec*=self.avatarControlJumpForce
-                    physObject.addImpulse(Vec3(jumpVec))
-                    self.isAirborne = 1 # Avoid double impulse before fully airborne.
-                else:
-                    self.isAirborne = 0
-            if __debug__:
-                onScreenDebug.add("isAirborne", "%d"%(self.isAirborne,))
+        if (airborneHeight > self.avatarRadius*0.5
+                or physObject.getVelocity().getZ() > 0.0
+                ): # Check stair angles before changing this.
+            # ...the avatar is airborne (maybe a lot or a tiny amount).
+            self.isAirborne = 1
         else:
         else:
-            if contact!=Vec3.zero():
-                # ...the avatar has touched something (but might not be on the ground).
+            # ...the avatar is very close to the ground (close enough to be
+            # considered on the ground).
+            if self.isAirborne and physObject.getVelocity().getZ() <= 0.0:
+                # ...the avatar has landed.
                 contactLength = contact.length()
                 contactLength = contact.length()
-                contact.normalize()
-                angle=contact.dot(Vec3.up())
-                if angle>self.__standableGround:
-                    # ...avatar is on standable ground.
-                    if self.__oldContact==Vec3.zero():
-                    #if self.__oldAirborneHeight > 0.1: #self.__oldContact==Vec3.zero():
-                        # ...avatar was airborne.
-                        self.jumpCount-=1
-                        if contactLength>self.__hardLandingForce:
-                            messenger.send("jumpHardLand")
-                        else:
-                            messenger.send("jumpLand")
-                    elif jump:
-                        self.jumpCount+=1
-                        #self.__jumpButton=0
-                        messenger.send("jumpStart")
-                        jump=Vec3(contact+Vec3.up())
-                        #jump=Vec3(rotAvatarToPhys.xform(jump))
-                        jump.normalize()
-                        jump*=self.avatarControlJumpForce
-                        physObject.addImpulse(Vec3(jump))
-
-        if contact!=self.__oldContact:
+                if contactLength>self.__hardLandingForce:
+                    #print "jumpHardLand"
+                    messenger.send("jumpHardLand")
+                else:
+                    #print "jumpLand"
+                    messenger.send("jumpLand")
+                self.priorParent.setVector(Vec3.zero())
+                self.isAirborne = 0
+            elif jump:
+                #print "jump"
+                #self.__jumpButton = 0
+                messenger.send("jumpStart")
+
+                ## ...jump away from walls and with with the slope normal.
+                #jumpVec=Vec3(contact+Vec3.up())
+                ##jumpVec=Vec3(rotAvatarToPhys.xform(jumpVec))
+                #jumpVec.normalize()
+
+                # ...jump straight up, even if next to a wall.
+                jumpVec=Vec3.up()
+
+                jumpVec *= self.avatarControlJumpForce
+                physObject.addImpulse(Vec3(jumpVec))
+                self.isAirborne = 1 # Avoid double impulse before fully airborne.
+            else:
+                self.isAirborne = 0
+        if __debug__:
+            onScreenDebug.add("isAirborne", "%d"%(self.isAirborne,))
+
+        if contact != self.__oldContact:
             # We must copy the vector to preserve it:
             # We must copy the vector to preserve it:
-            self.__oldContact=Vec3(contact)
-        self.__oldAirborneHeight=airborneHeight
+            self.__oldContact = Vec3(contact)
+        self.__oldAirborneHeight = airborneHeight
 
 
         moveToGround = Vec3.zero()
         moveToGround = Vec3.zero()
         if not self.useHeightRay or self.isAirborne:
         if not self.useHeightRay or self.isAirborne:
@@ -741,7 +704,7 @@ class PhysicsWalker(DirectObject.DirectObject):
     if __debug__:
     if __debug__:
         def setupAvatarPhysicsIndicator(self):
         def setupAvatarPhysicsIndicator(self):
             if self.wantDebugIndicator:
             if self.wantDebugIndicator:
-                indicator=loader.loadModel('phase_5/models/props/dagger')
+                indicator = base.loader.loadModel('phase_5/models/props/dagger')
                 #self.walkControls.setAvatarPhysicsIndicator(indicator)
                 #self.walkControls.setAvatarPhysicsIndicator(indicator)
 
 
         def debugPrint(self, message):
         def debugPrint(self, message):

+ 1 - 0
direct/src/controls/TwoDWalker.py

@@ -3,6 +3,7 @@ TwoDWalker.py is for controlling the avatars in a 2D scroller game environment.
 """
 """
 
 
 from .GravityWalker import *
 from .GravityWalker import *
+from direct.showbase.MessengerGlobal import messenger
 from panda3d.core import ConfigVariableBool
 from panda3d.core import ConfigVariableBool
 
 
 
 

+ 1 - 3
direct/src/dcparser/dcClass_ext.cxx

@@ -400,9 +400,7 @@ pack_required_field(DCPacker &packer, PyObject *distobj,
     PyObject_GetAttrString(distobj, (char *)getter_name.c_str());
     PyObject_GetAttrString(distobj, (char *)getter_name.c_str());
   nassertr(func != nullptr, false);
   nassertr(func != nullptr, false);
 
 
-  PyObject *empty_args = PyTuple_New(0);
-  PyObject *result = PyObject_CallObject(func, empty_args);
-  Py_DECREF(empty_args);
+  PyObject *result = PyObject_CallNoArgs(func);
   Py_DECREF(func);
   Py_DECREF(func);
   if (result == nullptr) {
   if (result == nullptr) {
     // We don't set this as an exception, since presumably the Python method
     // We don't set this as an exception, since presumably the Python method

+ 1 - 1
direct/src/dcparser/dcPacker_ext.cxx

@@ -309,7 +309,7 @@ unpack_class_object(const DCClass *dclass) {
   if (!dclass->has_constructor()) {
   if (!dclass->has_constructor()) {
     // If the class uses a default constructor, go ahead and create the Python
     // If the class uses a default constructor, go ahead and create the Python
     // object for it now.
     // object for it now.
-    object = PyObject_CallObject(class_def, nullptr);
+    object = PyObject_CallNoArgs(class_def);
     if (object == nullptr) {
     if (object == nullptr) {
       return nullptr;
       return nullptr;
     }
     }

+ 2 - 15
direct/src/directbase/TestStart.py

@@ -7,22 +7,9 @@ from direct.showbase import ShowBase
 base = ShowBase.ShowBase()
 base = ShowBase.ShowBase()
 
 
 # Put an axis in the world:
 # Put an axis in the world:
-loader.loadModel("models/misc/xyzAxis").reparentTo(render)
+base.loader.loadModel("models/misc/xyzAxis").reparentTo(render)
 
 
-if 0:
-    # Hack:
-    # Enable drive mode but turn it off, and reset the camera
-    # This is here because ShowBase sets up a drive interface, this
-    # can be removed if ShowBase is changed to not set that up.
-    base.useDrive()
-    base.disableMouse()
-    if base.mouseInterface:
-        base.mouseInterface.reparentTo(base.dataUnused)
-    if base.mouse2cam:
-        base.mouse2cam.reparentTo(base.dataUnused)
-    # end of hack.
-
-camera.setPosHpr(0, -10.0, 0, 0, 0, 0)
+base.camera.setPosHpr(0, -10.0, 0, 0, 0, 0)
 base.camLens.setFov(52.0)
 base.camLens.setFov(52.0)
 base.camLens.setNearFar(1.0, 10000.0)
 base.camLens.setNearFar(1.0, 10000.0)
 
 

+ 5 - 17
direct/src/directbase/ThreeUpStart.py

@@ -5,25 +5,13 @@ from panda3d.core import *
 
 
 from direct.showbase.PythonUtil import *
 from direct.showbase.PythonUtil import *
 from direct.showbase import ThreeUpShow
 from direct.showbase import ThreeUpShow
-ThreeUpShow.ThreeUpShow()
+
+base = ThreeUpShow.ThreeUpShow()
 
 
 # Put an axis in the world:
 # Put an axis in the world:
-loader.loadModel("models/misc/xyzAxis").reparentTo(render)
-
-if 0:
-    # Hack:
-    # Enable drive mode but turn it off, and reset the camera
-    # This is here because ShowBase sets up a drive interface, this
-    # can be removed if ShowBase is changed to not set that up.
-    base.useDrive()
-    base.disableMouse()
-    if base.mouseInterface:
-        base.mouseInterface.reparentTo(base.dataUnused)
-    if base.mouse2cam:
-        base.mouse2cam.reparentTo(base.dataUnused)
-    # end of hack.
-
-camera.setPosHpr(0, -10.0, 0, 0, 0, 0)
+base.loader.loadModel("models/misc/xyzAxis").reparentTo(render)
+
+base.camera.setPosHpr(0, -10.0, 0, 0, 0, 0)
 base.camLens.setFov(52.0)
 base.camLens.setFov(52.0)
 base.camLens.setNearFar(1.0, 10000.0)
 base.camLens.setNearFar(1.0, 10000.0)
 
 

+ 17 - 16
direct/src/directdevices/DirectDeviceManager.py

@@ -14,7 +14,7 @@ class DirectDeviceManager(VrpnClient, DirectObject):
     def __init__(self, server = None):
     def __init__(self, server = None):
 
 
         # Determine which server to use
         # Determine which server to use
-        if server != None:
+        if server is not None:
             # One given as constructor argument
             # One given as constructor argument
             self.server = server
             self.server = server
         else:
         else:
@@ -77,10 +77,10 @@ class DirectButtons(ButtonNode, DirectObject):
         return self.nodePath
         return self.nodePath
 
 
     def __repr__(self):
     def __repr__(self):
-        str = self.name + ': '
+        string = self.name + ': '
         for val in self:
         for val in self:
-            str = str + '%d' % val + ' '
-        return str
+            string = string + '%d' % val + ' '
+        return string
 
 
 class DirectAnalogs(AnalogNode, DirectObject):
 class DirectAnalogs(AnalogNode, DirectObject):
     analogCount = 0
     analogCount = 0
@@ -149,12 +149,12 @@ class DirectAnalogs(AnalogNode, DirectObject):
         aMin = self.analogMin
         aMin = self.analogMin
         center = self.analogCenter
         center = self.analogCenter
         deadband = self.analogDeadband
         deadband = self.analogDeadband
-        range = self.analogRange
+
         # Zero out values in deadband
         # Zero out values in deadband
-        if (abs(rawValue-center) <= deadband):
+        if abs(rawValue - center) <= deadband:
             return 0.0
             return 0.0
         # Clamp value between aMin and aMax and scale around center
         # Clamp value between aMin and aMax and scale around center
-        if (rawValue >= center):
+        if rawValue >= center:
             # Convert positive values to range 0 to 1
             # Convert positive values to range 0 to 1
             val = min(rawValue * sf, aMax)
             val = min(rawValue * sf, aMax)
             percentVal = ((val - (center + deadband))/
             percentVal = ((val - (center + deadband))/
@@ -165,7 +165,7 @@ class DirectAnalogs(AnalogNode, DirectObject):
             percentVal = -((val - (center - deadband))/
             percentVal = -((val - (center - deadband))/
                            float(aMin - (center - deadband)))
                            float(aMin - (center - deadband)))
         # Normalize values to given minVal and maxVal range
         # Normalize values to given minVal and maxVal range
-        return (((maxVal - minVal) * ((percentVal + 1)/2.0)) + minVal)
+        return ((maxVal - minVal) * ((percentVal + 1)/2.0)) + minVal
 
 
     def normalizeChannel(self, chan, minVal = -1, maxVal = 1, sf = 1.0):
     def normalizeChannel(self, chan, minVal = -1, maxVal = 1, sf = 1.0):
         try:
         try:
@@ -180,13 +180,14 @@ class DirectAnalogs(AnalogNode, DirectObject):
         return self.nodePath
         return self.nodePath
 
 
     def __repr__(self):
     def __repr__(self):
-        str = self.name + ': '
+        string = self.name + ': '
         for val in self:
         for val in self:
-            str = str + '%.3f' % val + ' '
-        return str
+            string = string + '%.3f' % val + ' '
+        return string
 
 
 class DirectTracker(TrackerNode, DirectObject):
 class DirectTracker(TrackerNode, DirectObject):
     trackerCount = 0
     trackerCount = 0
+
     def __init__(self, vrpnClient, device):
     def __init__(self, vrpnClient, device):
         # Keep track of number of trackers created
         # Keep track of number of trackers created
         DirectTracker.trackerCount += 1
         DirectTracker.trackerCount += 1
@@ -257,10 +258,10 @@ class DirectDials(DialNode, DirectObject):
         return self.nodePath
         return self.nodePath
 
 
     def __repr__(self):
     def __repr__(self):
-        str = self.name + ': '
+        string = self.name + ': '
         for i in range(self.getNumDials()):
         for i in range(self.getNumDials()):
-            str = str + '%.3f' % self[i] + ' '
-        return str
+            string = string + '%.3f' % self[i] + ' '
+        return string
 
 
 class DirectTimecodeReader(AnalogNode, DirectObject):
 class DirectTimecodeReader(AnalogNode, DirectObject):
     timecodeReaderCount = 0
     timecodeReaderCount = 0
@@ -316,5 +317,5 @@ class DirectTimecodeReader(AnalogNode, DirectObject):
                 self.totalSeconds)
                 self.totalSeconds)
 
 
     def __repr__(self):
     def __repr__(self):
-        str = ('%s: %d:%d:%d:%d' % ((self.name,) + self.getTime()[:-1]))
-        return str
+        string = ('%s: %d:%d:%d:%d' % ((self.name,) + self.getTime()[:-1]))
+        return string

+ 3 - 6
direct/src/directdevices/DirectFastrak.py

@@ -1,14 +1,12 @@
 """ Class used to create and control radamec device """
 """ Class used to create and control radamec device """
-from math import *
 from direct.showbase.DirectObject import DirectObject
 from direct.showbase.DirectObject import DirectObject
+from direct.task.Task import Task
+from direct.task.TaskManagerGlobal import taskMgr
 from .DirectDeviceManager import *
 from .DirectDeviceManager import *
 
 
 from direct.directnotify import DirectNotifyGlobal
 from direct.directnotify import DirectNotifyGlobal
 
 
-"""
-TODO:
-Handle interaction between widget, followSelectedTask and updateTask
-"""
+#TODO: Handle interaction between widget, followSelectedTask and updateTask
 
 
 # ANALOGS
 # ANALOGS
 NULL_AXIS = -1
 NULL_AXIS = -1
@@ -65,4 +63,3 @@ class DirectFastrak(DirectObject):
                                3.280839895013123 * pos[1],
                                3.280839895013123 * pos[1],
                                3.280839895013123 * pos[0])
                                3.280839895013123 * pos[0])
         self.notify.debug("Tracker(%d) Pos = %s" % (self.deviceNo, repr(self.trackerPos)))
         self.notify.debug("Tracker(%d) Pos = %s" % (self.deviceNo, repr(self.trackerPos)))
-

+ 13 - 16
direct/src/directdevices/DirectJoybox.py

@@ -4,12 +4,10 @@ from .DirectDeviceManager import *
 from direct.directtools.DirectUtil import *
 from direct.directtools.DirectUtil import *
 from direct.gui import OnscreenText
 from direct.gui import OnscreenText
 from direct.task import Task
 from direct.task import Task
+from direct.task.TaskManagerGlobal import taskMgr
 import math
 import math
 
 
-"""
-TODO:
-Handle interaction between widget, followSelectedTask and updateTask
-"""
+#TODO: Handle interaction between widget, followSelectedTask and updateTask
 
 
 # BUTTONS
 # BUTTONS
 L_STICK = 0
 L_STICK = 0
@@ -39,10 +37,11 @@ class DirectJoybox(DirectObject):
     joyboxCount = 0
     joyboxCount = 0
     xyzMultiplier = 1.0
     xyzMultiplier = 1.0
     hprMultiplier = 1.0
     hprMultiplier = 1.0
+
     def __init__(self, device = 'CerealBox', nodePath = base.direct.camera,
     def __init__(self, device = 'CerealBox', nodePath = base.direct.camera,
                  headingNP = base.direct.camera):
                  headingNP = base.direct.camera):
         # See if device manager has been initialized
         # See if device manager has been initialized
-        if base.direct.deviceManager == None:
+        if base.direct.deviceManager is None:
             base.direct.deviceManager = DirectDeviceManager()
             base.direct.deviceManager = DirectDeviceManager()
         # Set name
         # Set name
         DirectJoybox.joyboxCount += 1
         DirectJoybox.joyboxCount += 1
@@ -172,12 +171,12 @@ class DirectJoybox(DirectObject):
         for chan in range(len(self.analogs)):
         for chan in range(len(self.analogs)):
             val = self.analogs.getControlState(chan)
             val = self.analogs.getControlState(chan)
             # Zero out values in deadband
             # Zero out values in deadband
-            if (val < 0):
+            if val < 0:
                 val = min(val + ANALOG_DEADBAND, 0.0)
                 val = min(val + ANALOG_DEADBAND, 0.0)
             else:
             else:
                 val = max(val - ANALOG_DEADBAND, 0.0)
                 val = max(val - ANALOG_DEADBAND, 0.0)
             # Scale up rotating knob values
             # Scale up rotating knob values
-            if (chan == L_TWIST) or (chan == R_TWIST):
+            if chan == L_TWIST or chan == R_TWIST:
                 val *= 3.0
                 val *= 3.0
             # Now clamp value between minVal and maxVal
             # Now clamp value between minVal and maxVal
             val = CLAMP(val, JOYBOX_MIN, JOYBOX_MAX)
             val = CLAMP(val, JOYBOX_MIN, JOYBOX_MAX)
@@ -240,7 +239,7 @@ class DirectJoybox(DirectObject):
 
 
     def joyboxFly(self):
     def joyboxFly(self):
         # Do nothing if no nodePath selected
         # Do nothing if no nodePath selected
-        if self.nodePath == None:
+        if self.nodePath is None:
             return
             return
         hprScale = ((self.aList[L_SLIDE] + 1.0) *
         hprScale = ((self.aList[L_SLIDE] + 1.0) *
                     50.0 * DirectJoybox.hprMultiplier)
                     50.0 * DirectJoybox.hprMultiplier)
@@ -264,14 +263,14 @@ class DirectJoybox(DirectObject):
         # if we are using a heading nodepath, we want
         # if we are using a heading nodepath, we want
         # to drive in the direction we are facing,
         # to drive in the direction we are facing,
         # however, we don't want the z component to change
         # however, we don't want the z component to change
-        if (self.useHeadingNP and self.headingNP != None):
+        if self.useHeadingNP and self.headingNP is not None:
             oldZ = pos.getZ()
             oldZ = pos.getZ()
             pos = self.nodePath.getRelativeVector(self.headingNP,
             pos = self.nodePath.getRelativeVector(self.headingNP,
                                                   pos)
                                                   pos)
             pos.setZ(oldZ)
             pos.setZ(oldZ)
             # if we are using a heading NP we might want to rotate
             # if we are using a heading NP we might want to rotate
             # in place around that NP
             # in place around that NP
-            if (self.rotateInPlace):
+            if self.rotateInPlace:
                 parent = self.nodePath.getParent()
                 parent = self.nodePath.getParent()
                 self.floatingNP.reparentTo(parent)
                 self.floatingNP.reparentTo(parent)
                 self.floatingNP.setPos(self.headingNP,0,0,0)
                 self.floatingNP.setPos(self.headingNP,0,0,0)
@@ -389,7 +388,7 @@ class DirectJoybox(DirectObject):
 
 
     def spaceFly(self):
     def spaceFly(self):
         # Do nothing if no nodePath selected
         # Do nothing if no nodePath selected
-        if self.nodePath == None:
+        if self.nodePath is None:
             return
             return
         hprScale = (self.normalizeChannel(L_SLIDE, 0.1, 100) *
         hprScale = (self.normalizeChannel(L_SLIDE, 0.1, 100) *
                     DirectJoybox.hprMultiplier)
                     DirectJoybox.hprMultiplier)
@@ -408,7 +407,7 @@ class DirectJoybox(DirectObject):
 
 
     def planetFly(self):
     def planetFly(self):
         # Do nothing if no nodePath selected
         # Do nothing if no nodePath selected
-        if self.nodePath == None:
+        if self.nodePath is None:
             return
             return
         hprScale = (self.normalizeChannel(L_SLIDE, 0.1, 100) *
         hprScale = (self.normalizeChannel(L_SLIDE, 0.1, 100) *
                     DirectJoybox.hprMultiplier)
                     DirectJoybox.hprMultiplier)
@@ -459,7 +458,7 @@ class DirectJoybox(DirectObject):
 
 
     def orbitFly(self):
     def orbitFly(self):
         # Do nothing if no nodePath selected
         # Do nothing if no nodePath selected
-        if self.nodePath == None:
+        if self.nodePath is None:
             return
             return
         hprScale = (self.normalizeChannel(L_SLIDE, 0.1, 100) *
         hprScale = (self.normalizeChannel(L_SLIDE, 0.1, 100) *
                     DirectJoybox.hprMultiplier)
                     DirectJoybox.hprMultiplier)
@@ -502,7 +501,7 @@ class DirectJoybox(DirectObject):
     # correct the ranges of the two twist axes of the joybox.
     # correct the ranges of the two twist axes of the joybox.
     def normalizeChannel(self, chan, minVal = -1, maxVal = 1):
     def normalizeChannel(self, chan, minVal = -1, maxVal = 1):
         try:
         try:
-            if (chan == L_TWIST) or (chan == R_TWIST):
+            if chan == L_TWIST or chan == R_TWIST:
                 # These channels have reduced range
                 # These channels have reduced range
                 return self.analogs.normalize(
                 return self.analogs.normalize(
                     self.analogs.getControlState(chan), minVal, maxVal, 3.0)
                     self.analogs.getControlState(chan), minVal, maxVal, 3.0)
@@ -511,5 +510,3 @@ class DirectJoybox(DirectObject):
                     self.analogs.getControlState(chan), minVal, maxVal)
                     self.analogs.getControlState(chan), minVal, maxVal)
         except IndexError:
         except IndexError:
             return 0.0
             return 0.0
-
-

+ 12 - 13
direct/src/directdevices/DirectRadamec.py

@@ -1,15 +1,13 @@
 """ Class used to create and control radamec device """
 """ Class used to create and control radamec device """
 from math import *
 from math import *
 from direct.showbase.DirectObject import DirectObject
 from direct.showbase.DirectObject import DirectObject
+from direct.task.Task import Task
+from direct.task.TaskManagerGlobal import taskMgr
 from .DirectDeviceManager import *
 from .DirectDeviceManager import *
 
 
 from direct.directnotify import DirectNotifyGlobal
 from direct.directnotify import DirectNotifyGlobal
 
 
-
-"""
-TODO:
-Handle interaction between widget, followSelectedTask and updateTask
-"""
+#TODO: Handle interaction between widget, followSelectedTask and updateTask
 
 
 # ANALOGS
 # ANALOGS
 RAD_PAN = 0
 RAD_PAN = 0
@@ -23,7 +21,7 @@ class DirectRadamec(DirectObject):
 
 
     def __init__(self, device = 'Analog0', nodePath = None):
     def __init__(self, device = 'Analog0', nodePath = None):
         # See if device manager has been initialized
         # See if device manager has been initialized
-        if base.direct.deviceManager == None:
+        if base.direct.deviceManager is None:
             base.direct.deviceManager = DirectDeviceManager()
             base.direct.deviceManager = DirectDeviceManager()
         # Set name
         # Set name
         self.name = 'Radamec-' + repr(DirectRadamec.radamecCount)
         self.name = 'Radamec-' + repr(DirectRadamec.radamecCount)
@@ -73,11 +71,12 @@ class DirectRadamec(DirectObject):
     # Normalize to the range [-minVal, maxVal] based on some hard-coded
     # Normalize to the range [-minVal, maxVal] based on some hard-coded
     # max/min numbers of the Radamec device
     # max/min numbers of the Radamec device
     def normalizeChannel(self, chan, minVal = -1, maxVal = 1):
     def normalizeChannel(self, chan, minVal = -1, maxVal = 1):
-        try:
-            maxRange = self.maxRange[chan]
-            minRange = self.minRange[chan]
-        except IndexError:
+        if chan < 0 or chan >= min(len(self.maxRange), len(self.minRange)):
             raise RuntimeError("can't normalize this channel (channel %d)" % chan)
             raise RuntimeError("can't normalize this channel (channel %d)" % chan)
-        range = maxRange - minRange
-        clampedVal = CLAMP(self.aList[chan], minRange, maxRange)
-        return ((maxVal - minVal) * (clampedVal - minRange) / range) + minVal
+
+        maxRange = self.maxRange[chan]
+        minRange = self.minRange[chan]
+
+        diff = maxRange - minRange
+        clampedVal = max(min(self.aList[chan], maxRange), maxRange)
+        return ((maxVal - minVal) * (clampedVal - minRange) / diff) + minVal

+ 3 - 3
direct/src/directnotify/DirectNotify.py

@@ -41,17 +41,17 @@ class DirectNotify:
         """getCategory(self, string)
         """getCategory(self, string)
         Return the category with given name if present, None otherwise
         Return the category with given name if present, None otherwise
         """
         """
-        return (self.__categories.get(categoryName, None))
+        return self.__categories.get(categoryName, None)
 
 
     def newCategory(self, categoryName, logger=None):
     def newCategory(self, categoryName, logger=None):
         """newCategory(self, string)
         """newCategory(self, string)
         Make a new notify category named categoryName. Return new category
         Make a new notify category named categoryName. Return new category
         if no such category exists, else return existing category
         if no such category exists, else return existing category
         """
         """
-        if (categoryName not in self.__categories):
+        if categoryName not in self.__categories:
             self.__categories[categoryName] = Notifier.Notifier(categoryName, logger)
             self.__categories[categoryName] = Notifier.Notifier(categoryName, logger)
             self.setDconfigLevel(categoryName)
             self.setDconfigLevel(categoryName)
-        return (self.getCategory(categoryName))
+        return self.getCategory(categoryName)
 
 
     def setDconfigLevel(self, categoryName):
     def setDconfigLevel(self, categoryName):
         """
         """

+ 11 - 26
direct/src/directnotify/Logger.py

@@ -4,6 +4,7 @@
 import time
 import time
 import math
 import math
 
 
+
 class Logger:
 class Logger:
     def __init__(self, fileName="log"):
     def __init__(self, fileName="log"):
         """
         """
@@ -14,18 +15,17 @@ class Logger:
         self.__logFile = None
         self.__logFile = None
         self.__logFileName = fileName
         self.__logFileName = fileName
 
 
-    def setTimeStamp(self, bool):
+    def setTimeStamp(self, enable):
         """
         """
         Toggle time stamp printing with log entries on and off
         Toggle time stamp printing with log entries on and off
         """
         """
-        self.__timeStamp = bool
+        self.__timeStamp = enable
 
 
     def getTimeStamp(self):
     def getTimeStamp(self):
         """
         """
         Return whether or not we are printing time stamps with log entries
         Return whether or not we are printing time stamps with log entries
         """
         """
-        return(self.__timeStamp)
-
+        return self.__timeStamp
 
 
     # logging control
     # logging control
 
 
@@ -38,13 +38,12 @@ class Logger:
     def log(self, entryString):
     def log(self, entryString):
         """log(self, string)
         """log(self, string)
         Print the given string to the log file"""
         Print the given string to the log file"""
-        if (self.__logFile == None):
+        if self.__logFile is None:
             self.__openLogFile()
             self.__openLogFile()
-        if (self.__timeStamp):
+        if self.__timeStamp:
             self.__logFile.write(self.__getTimeStamp())
             self.__logFile.write(self.__getTimeStamp())
         self.__logFile.write(entryString + '\n')
         self.__logFile.write(entryString + '\n')
 
 
-
     # logging functions
     # logging functions
 
 
     def __openLogFile(self):
     def __openLogFile(self):
@@ -61,7 +60,7 @@ class Logger:
         """
         """
         Close the error/warning output file
         Close the error/warning output file
         """
         """
-        if (self.__logFile != None):
+        if self.__logFile is not None:
             self.__logFile.close()
             self.__logFile.close()
 
 
     def __getTimeStamp(self):
     def __getTimeStamp(self):
@@ -70,22 +69,8 @@ class Logger:
         """
         """
         t = time.time()
         t = time.time()
         dt = t - self.__startTime
         dt = t - self.__startTime
-        if (dt >= 86400):
-            days = int(math.floor(dt/86400))
-            dt = dt%86400
-        else:
-            days = 0
-        if (dt >= 3600):
-            hours = int(math.floor(dt/3600))
-            dt = dt%3600
-        else:
-            hours = 0
-        if (dt >= 60):
-            minutes = int(math.floor(dt/60))
-            dt = dt%60
-        else:
-            minutes = 0
+        days, dt = divmod(dt, 86400)
+        hours, dt = divmod(dt, 3600)
+        minutes, dt = divmod(dt, 60)
         seconds = int(math.ceil(dt))
         seconds = int(math.ceil(dt))
-        return("%02d:%02d:%02d:%02d: " % (days, hours, minutes, seconds))
-
-
+        return "%02d:%02d:%02d:%02d: " % (days, hours, minutes, seconds)

+ 11 - 12
direct/src/directnotify/Notifier.py

@@ -32,7 +32,7 @@ class Notifier:
         """
         """
         self.__name = name
         self.__name = name
 
 
-        if (logger==None):
+        if logger is None:
             self.__logger = defaultLogger
             self.__logger = defaultLogger
         else:
         else:
             self.__logger = logger
             self.__logger = logger
@@ -144,17 +144,17 @@ class Notifier:
             self.__print(string)
             self.__print(string)
         return 1 # to allow assert myNotify.warning("blah")
         return 1 # to allow assert myNotify.warning("blah")
 
 
-    def setWarning(self, bool):
+    def setWarning(self, enable):
         """
         """
         Enable/Disable the printing of warning messages
         Enable/Disable the printing of warning messages
         """
         """
-        self.__warning = bool
+        self.__warning = enable
 
 
     def getWarning(self):
     def getWarning(self):
         """
         """
         Return whether the printing of warning messages is on or off
         Return whether the printing of warning messages is on or off
         """
         """
-        return(self.__warning)
+        return self.__warning
 
 
     # debug funcs
     # debug funcs
     def debug(self, debugString):
     def debug(self, debugString):
@@ -171,11 +171,11 @@ class Notifier:
             self.__print(string)
             self.__print(string)
         return 1 # to allow assert myNotify.debug("blah")
         return 1 # to allow assert myNotify.debug("blah")
 
 
-    def setDebug(self, bool):
+    def setDebug(self, enable):
         """
         """
         Enable/Disable the printing of debug messages
         Enable/Disable the printing of debug messages
         """
         """
-        self.__debug = bool
+        self.__debug = enable
 
 
     def getDebug(self):
     def getDebug(self):
         """
         """
@@ -204,11 +204,11 @@ class Notifier:
         """
         """
         return self.__info
         return self.__info
 
 
-    def setInfo(self, bool):
+    def setInfo(self, enable):
         """
         """
         Enable/Disable informational message  printing
         Enable/Disable informational message  printing
         """
         """
-        self.__info = bool
+        self.__info = enable
 
 
     # log funcs
     # log funcs
     def __log(self, logEntry):
     def __log(self, logEntry):
@@ -222,13 +222,13 @@ class Notifier:
         """
         """
         Return 1 if logging enabled, 0 otherwise
         Return 1 if logging enabled, 0 otherwise
         """
         """
-        return (self.__logging)
+        return self.__logging
 
 
-    def setLogging(self, bool):
+    def setLogging(self, enable):
         """
         """
         Set the logging flag to int (1=on, 0=off)
         Set the logging flag to int (1=on, 0=off)
         """
         """
-        self.__logging = bool
+        self.__logging = enable
 
 
     def __print(self, string):
     def __print(self, string):
         """
         """
@@ -297,4 +297,3 @@ class Notifier:
             self.__log(string)
             self.__log(string)
             self.__print(string)
             self.__print(string)
         return 1 # to allow assert self.notify.debugCall("blah")
         return 1 # to allow assert self.notify.debugCall("blah")
-

+ 1 - 1
direct/src/directnotify/RotatingLog.py

@@ -4,7 +4,7 @@ import time
 
 
 class RotatingLog:
 class RotatingLog:
     """
     """
-    A file() (or open()) replacement that will automatically open and write
+    An `open()` replacement that will automatically open and write
     to a new file if the prior file is too large or after a time interval.
     to a new file if the prior file is too large or after a time interval.
     """
     """
 
 

+ 22 - 17
direct/src/directscripts/eggcacher.py

@@ -8,7 +8,9 @@
 #
 #
 ##############################################################################
 ##############################################################################
 
 
-import os,sys,gc
+import os
+import sys
+import gc
 from panda3d.core import *
 from panda3d.core import *
 
 
 class EggCacher:
 class EggCacher:
@@ -18,7 +20,7 @@ class EggCacher:
         self.bamcache = BamCache.getGlobalPtr()
         self.bamcache = BamCache.getGlobalPtr()
         self.pandaloader = Loader()
         self.pandaloader = Loader()
         self.loaderopts = LoaderOptions(LoaderOptions.LF_no_ram_cache)
         self.loaderopts = LoaderOptions(LoaderOptions.LF_no_ram_cache)
-        if (self.bamcache.getActive() == 0):
+        if not self.bamcache.getActive():
             print("The model cache is not currently active.")
             print("The model cache is not currently active.")
             print("You must set a model-cache-dir in your config file.")
             print("You must set a model-cache-dir in your config file.")
             sys.exit(1)
             sys.exit(1)
@@ -29,42 +31,44 @@ class EggCacher:
     def parseArgs(self, args):
     def parseArgs(self, args):
         self.concise = 0
         self.concise = 0
         self.pzkeep = 0
         self.pzkeep = 0
-        while len(args):
-            if (args[0]=="--concise"):
+        while len(args) > 0:
+            if args[0] == "--concise":
                 self.concise = 1
                 self.concise = 1
                 args = args[1:]
                 args = args[1:]
-            elif (args[0]=="--pzkeep"):
+            elif args[0] == "--pzkeep":
                 self.pzkeep = 1
                 self.pzkeep = 1
                 args = args[1:]
                 args = args[1:]
             else:
             else:
                 break
                 break
-        if (len(args) < 1):
+        if len(args) < 1:
             print("Usage: eggcacher options file-or-directory")
             print("Usage: eggcacher options file-or-directory")
             sys.exit(1)
             sys.exit(1)
         self.paths = args
         self.paths = args
 
 
     def scanPath(self, eggs, path):
     def scanPath(self, eggs, path):
-        if (os.path.exists(path)==0):
+        if not os.path.exists(path):
             print("No such file or directory: " + path)
             print("No such file or directory: " + path)
             return
             return
-        if (os.path.isdir(path)):
+        if os.path.isdir(path):
             for f in os.listdir(path):
             for f in os.listdir(path):
                 self.scanPath(eggs, os.path.join(path,f))
                 self.scanPath(eggs, os.path.join(path,f))
             return
             return
-        if (path.endswith(".egg")):
+        if path.endswith(".egg"):
             size = os.path.getsize(path)
             size = os.path.getsize(path)
             eggs.append((path,size))
             eggs.append((path,size))
             return
             return
-        if (path.endswith(".egg.pz") or path.endswith(".egg.gz")):
+        if path.endswith(".egg.pz") or path.endswith(".egg.gz"):
             size = os.path.getsize(path)
             size = os.path.getsize(path)
-            if (self.pzkeep): eggs.append((path,size))
-            else: eggs.append((path[:-3],size))
+            if self.pzkeep:
+                eggs.append((path, size))
+            else:
+                eggs.append((path[:-3], size))
 
 
     def scanPaths(self, paths):
     def scanPaths(self, paths):
         eggs = []
         eggs = []
         for path in paths:
         for path in paths:
-            abs = os.path.abspath(path)
-            self.scanPath(eggs,path)
+            #abs = os.path.abspath(path)
+            self.scanPath(eggs, path)
         return eggs
         return eggs
 
 
     def processFiles(self, files):
     def processFiles(self, files):
@@ -72,15 +76,16 @@ class EggCacher:
         for (path, size) in files:
         for (path, size) in files:
             total += size
             total += size
         progress = 0
         progress = 0
-        for (path,size) in files:
+        for (path, size) in files:
             fn = Filename.fromOsSpecific(path)
             fn = Filename.fromOsSpecific(path)
             cached = self.bamcache.lookup(fn, "bam")
             cached = self.bamcache.lookup(fn, "bam")
             percent = (progress * 100) / total
             percent = (progress * 100) / total
             report = path
             report = path
-            if (self.concise): report = os.path.basename(report)
+            if self.concise:
+                report = os.path.basename(report)
             print("Preprocessing Models %2d%% %s" % (percent, report))
             print("Preprocessing Models %2d%% %s" % (percent, report))
             sys.stdout.flush()
             sys.stdout.flush()
-            if (cached) and (cached.hasData()==0):
+            if cached and not cached.hasData():
                 self.pandaloader.loadSync(fn, self.loaderopts)
                 self.pandaloader.loadSync(fn, self.loaderopts)
             gc.collect()
             gc.collect()
             ModelPool.releaseAllModels()
             ModelPool.releaseAllModels()

+ 2 - 2
direct/src/directscripts/extract_docs.py

@@ -10,8 +10,8 @@ from __future__ import print_function
 __all__ = []
 __all__ = []
 
 
 import os
 import os
-from distutils import sysconfig
-import panda3d, pandac
+import panda3d
+import pandac
 from panda3d.interrogatedb import *
 from panda3d.interrogatedb import *
 
 
 
 

+ 0 - 986
direct/src/directscripts/gendocs.py

@@ -1,986 +0,0 @@
-########################################################################
-#
-# Documentation generator for panda.
-#
-# How to use this module:
-#
-#   from direct.directscripts import gendocs
-#   gendocs.generate(version, indirlist, directdirlist, docdir, header, footer, urlprefix, urlsuffix)
-#
-#   - version is the panda version number
-#
-#   - indirlist is the name of a directory, or a list of directories,
-#     containing the "xxx.in" files that interrogate generates.  No
-#     slash at end.
-#
-#   - directdirlist is the name of a directory, or a list of
-#     directories, containing the source code for "direct," as well as
-#     for other Python-based trees that should be included in the
-#     documentation pages.  No slash at end.
-#
-#   - docdir is the name of a directory into which HTML files
-#     will be emitted.  No slash at end.
-#
-#   - header is a string that will be placed at the front of
-#     every HTML page.
-#
-#   - footer is a string that will be placed at the end of
-#     every HTML page.
-#
-#   - urlprefix is a string that will be appended to the front of
-#     every URL.
-#
-#   - urlsuffix is a string that will be appended to the end of
-#     every URL.
-#
-########################################################################
-#
-# The major subsystems are:
-#
-# * The module that loads interrogate databases.
-#
-# * The module that loads python parse-trees.
-#
-# * The "code database", which provides a single access point
-#   for both interrogate databases and python parse trees.
-#
-# * The HTML generator.
-#
-########################################################################
-
-import os, sys, parser, symbol, token, re
-
-########################################################################
-#
-# assorted utility functions
-#
-########################################################################
-
-SECHEADER = re.compile("^[A-Z][a-z]+\\s*:")
-JUNKHEADER = re.compile("^((Function)|(Access))\\s*:")
-IMPORTSTAR = re.compile("^from\\s+([a-zA-Z0-9_.]+)\\s+import\\s+[*]\\s*$")
-IDENTIFIER = re.compile("[a-zA-Z0-9_]+")
-FILEHEADER = re.compile(
-r"""^// Filename: [a-zA-Z.]+
-// Created by:  [a-zA-Z. ()0-9]+(
-//)?
-////////////////////////////////////////////////////////////////////
-//
-// PANDA 3D SOFTWARE
-// Copyright \(c\) Carnegie Mellon University.  All rights reserved.
-//
-// All use of this software is subject to the terms of the revised BSD
-// license.  You should have received a copy of this license along
-// with this source code in a file named "LICENSE."
-//
-////////////////////////////////////////////////////////////////////""")
-
-def readFile(fn):
-    try:
-        srchandle = open(fn, "r")
-        data = srchandle.read()
-        srchandle.close()
-        return data
-    except:
-        sys.exit("Cannot read "+fn)
-
-def writeFile(wfile, data):
-    try:
-        dsthandle = open(wfile, "wb")
-        dsthandle.write(data)
-        dsthandle.close()
-    except:
-        sys.exit("Cannot write "+wfile)
-
-def writeFileLines(wfile, lines):
-    try:
-        dsthandle = open(wfile, "wb")
-        for x in lines:
-            dsthandle.write(x)
-            dsthandle.write("\n")
-        dsthandle.close()
-    except:
-        sys.exit("Cannot write "+wfile)
-
-def findFiles(dirlist, ext, ign, list):
-    if isinstance(dirlist, str):
-        dirlist = [dirlist]
-    for dir in dirlist:
-        for file in os.listdir(dir):
-            full = dir + "/" + file
-            if full not in ign and file not in ign:
-                if (os.path.isfile(full)):
-                    if (file.endswith(ext)):
-                        list.append(full)
-                elif (os.path.isdir(full)):
-                    findFiles(full, ext, ign, list)
-
-def pathToModule(result):
-    if (result[-3:]==".py"): result=result[:-3]
-    result = result.replace("/src/","/")
-    result = result.replace("/",".")
-    return result
-
-def textToHTML(comment, sep, delsection=None):
-    sections = [""]
-    included = {}
-    for line in comment.split("\n"):
-        line = line.lstrip(" ").lstrip(sep).lstrip(" ").rstrip("\r").rstrip(" ")
-        if (line == ""):
-            sections.append("")
-        elif (line[0]=="*") or (line[0]=="-"):
-            sections.append(line)
-            sections.append("")
-        elif (SECHEADER.match(line)):
-            sections.append(line)
-        else:
-            sections[-1] = sections[-1] + " " + line
-    total = ""
-    for sec in sections:
-        if (sec != ""):
-            sec = sec.replace("&","&amp;")
-            sec = sec.replace("<","&lt;")
-            sec = sec.replace(">","&gt;")
-            sec = sec.replace("  "," ")
-            sec = sec.replace("  "," ")
-            if (delsection != None) and (delsection.match(sec)):
-                included[sec] = 1
-            if sec not in included:
-                included[sec] = 1
-                total = total + sec + "<br>\n"
-    return total
-
-def linkTo(link, text):
-    return '<a href="' + link + '">' + text + '</a>'
-
-def convertToPythonFn(fn):
-    result = ""
-    lastc = 0
-    for c in fn:
-        if (c!="_"):
-            if (lastc=="_"):
-                result = result + c.upper()
-            else:
-                result = result + c
-        lastc = c
-    return result
-
-def removeFileLicense(content):
-    # Removes the license part at the top of a file.
-    return re.sub(FILEHEADER, "", content).strip()
-
-########################################################################
-#
-# Interrogate Database Tokenizer
-#
-########################################################################
-
-class InterrogateTokenizer:
-    """
-    A big string, with a "parse pointer", and routines to
-    extract integers and strings.  The token syntax is that
-    used by interrogate databases.
-    """
-
-    def __init__(self, fn):
-        self.fn = fn
-        self.pos = 0
-        self.data = readFile(fn)
-
-    def readint(self):
-        neg = 0
-        while (self.data[self.pos].isspace()):
-            self.pos += 1
-        if (self.data[self.pos] == "-"):
-            neg = 1
-            self.pos += 1
-        if (self.data[self.pos].isdigit()==0):
-            print("File position " + str(self.pos))
-            print("Text: " + self.data[self.pos:self.pos+50])
-            sys.exit("Syntax error in interrogate file format 0")
-        value = 0
-        while (self.data[self.pos].isdigit()):
-            value = value*10 + int(self.data[self.pos])
-            self.pos += 1
-        if (neg): value = -value
-        return value
-
-    def readstring(self):
-        length = self.readint()
-        if (self.data[self.pos].isspace()==0):
-            sys.exit("Syntax error in interrogate file format 1")
-        self.pos += 1
-        body = self.data[self.pos:self.pos+length]
-        if (len(body) != length):
-            sys.exit("Syntax error in interrogate file format 2")
-        self.pos += length
-        return body
-
-########################################################################
-#
-# Interrogate Database Storage/Parsing
-#
-########################################################################
-
-def parseInterrogateIntVec(tokzr):
-    length = tokzr.readint()
-    result = []
-    for i in range(length):
-        result.append(tokzr.readint())
-    return result
-
-class InterrogateFunction:
-    def __init__(self, tokzr, db):
-        self.db = db
-        self.index = tokzr.readint()
-        self.componentname = tokzr.readstring()
-        self.flags = tokzr.readint()
-        self.classindex = tokzr.readint()
-        self.scopedname = tokzr.readstring()
-        self.cwrappers = parseInterrogateIntVec(tokzr)
-        self.pythonwrappers = parseInterrogateIntVec(tokzr)
-        self.comment = tokzr.readstring()
-        self.prototype = tokzr.readstring()
-
-class InterrogateEnumValue:
-    def __init__(self, tokzr):
-        self.name = tokzr.readstring()
-        self.scopedname = tokzr.readstring()
-        self.value = tokzr.readint()
-
-class InterrogateDerivation:
-    def __init__(self, tokzr):
-        self.flags = tokzr.readint()
-        self.base = tokzr.readint()
-        self.upcast = tokzr.readint()
-        self.downcast = tokzr.readint()
-
-class InterrogateType:
-    def __init__(self, tokzr, db):
-        self.db = db
-        self.index = tokzr.readint()
-        self.componentname = tokzr.readstring()
-        self.flags = tokzr.readint()
-        self.scopedname = tokzr.readstring()
-        self.truename = tokzr.readstring()
-        self.outerclass = tokzr.readint()
-        self.atomictype = tokzr.readint()
-        self.wrappedtype = tokzr.readint()
-        self.constructors = parseInterrogateIntVec(tokzr)
-        self.destructor = tokzr.readint()
-        self.elements = parseInterrogateIntVec(tokzr)
-        self.methods = parseInterrogateIntVec(tokzr)
-        self.casts = parseInterrogateIntVec(tokzr)
-        self.derivations = []
-        nderivations = tokzr.readint()
-        for i in range(nderivations):
-            self.derivations.append(InterrogateDerivation(tokzr))
-        self.enumvalues = []
-        nenumvalues = tokzr.readint()
-        for i in range(nenumvalues):
-            self.enumvalues.append(InterrogateEnumValue(tokzr))
-        self.nested = parseInterrogateIntVec(tokzr)
-        self.comment = tokzr.readstring()
-
-class InterrogateParameter:
-    def __init__(self, tokzr):
-        self.name = tokzr.readstring()
-        self.parameterflags = tokzr.readint()
-        self.type = tokzr.readint()
-
-class InterrogateWrapper:
-    def __init__(self, tokzr, db):
-        self.db = db
-        self.index = tokzr.readint()
-        self.componentname = tokzr.readstring()
-        self.flags = tokzr.readint()
-        self.function = tokzr.readint()
-        self.returntype = tokzr.readint()
-        self.returnvaluedestructor = tokzr.readint()
-        self.uniquename = tokzr.readstring()
-        self.parameters = []
-        nparameters = tokzr.readint()
-        for i in range(nparameters):
-            self.parameters.append(InterrogateParameter(tokzr))
-
-class InterrogateDatabase:
-    def __init__(self, tokzr):
-        self.fn = tokzr.fn
-        self.magic = tokzr.readint()
-        version1 = tokzr.readint()
-        version2 = tokzr.readint()
-        if (version1 != 2) or (version2 != 2):
-            sys.exit("This program only understands interrogate file format 2.2")
-        self.library = tokzr.readstring()
-        self.libhash = tokzr.readstring()
-        self.module = tokzr.readstring()
-        self.functions = {}
-        self.wrappers = {}
-        self.types = {}
-        self.namedtypes = {}
-        count_functions = tokzr.readint()
-        for i in range(count_functions):
-            fn = InterrogateFunction(tokzr, self)
-            self.functions[fn.index] = fn
-        count_wrappers = tokzr.readint()
-        for i in range(count_wrappers):
-            wr = InterrogateWrapper(tokzr, self)
-            self.wrappers[wr.index] = wr
-        count_types = tokzr.readint()
-        for i in range(count_types):
-            tp = InterrogateType(tokzr, self)
-            self.types[tp.index] = tp
-            self.namedtypes[tp.scopedname] = tp
-
-########################################################################
-#
-# Pattern Matching for Python Parse Trees
-#
-########################################################################
-
-def printTree(tree, indent):
-    spacing = "                                                        "[:indent]
-    if isinstance(tree, tuple) and isinstance(tree[0], int):
-        if tree[0] in symbol.sym_name:
-            for i in range(len(tree)):
-                if (i==0):
-                    print(spacing + "(symbol." + symbol.sym_name[tree[0]] + ",")
-                else:
-                    printTree(tree[i], indent+1)
-            print(spacing + "),")
-        elif tree[0] in token.tok_name:
-            print(spacing + "(token." + token.tok_name[tree[0]] + ", '" + tree[1] + "'),")
-        else:
-            print(spacing + str(tree))
-    else:
-        print(spacing + str(tree))
-
-
-COMPOUND_STMT_PATTERN = (
-    symbol.stmt,
-    (symbol.compound_stmt, ['compound'])
-    )
-
-
-DOCSTRING_STMT_PATTERN = (
-    symbol.stmt,
-    (symbol.simple_stmt,
-     (symbol.small_stmt,
-      (symbol.expr_stmt,
-       (symbol.testlist,
-        (symbol.test,
-         (symbol.or_test,
-           (symbol.and_test,
-            (symbol.not_test,
-             (symbol.comparison,
-              (symbol.expr,
-               (symbol.xor_expr,
-                (symbol.and_expr,
-                 (symbol.shift_expr,
-                  (symbol.arith_expr,
-                   (symbol.term,
-                    (symbol.factor,
-                     (symbol.power,
-                      (symbol.atom,
-                       (token.STRING, ['docstring'])
-                       ))))))))))))))))),
-     (token.NEWLINE, '')
-     ))
-
-DERIVATION_PATTERN = (
-    symbol.test,
-    (symbol.or_test,
-     (symbol.and_test,
-      (symbol.not_test,
-       (symbol.comparison,
-        (symbol.expr,
-         (symbol.xor_expr,
-          (symbol.and_expr,
-           (symbol.shift_expr,
-            (symbol.arith_expr,
-             (symbol.term,
-              (symbol.factor,
-               (symbol.power,
-                (symbol.atom,
-                 (token.NAME, ['classname'])
-   ))))))))))))))
-
-ASSIGNMENT_STMT_PATTERN = (
-    symbol.stmt,
-    (symbol.simple_stmt,
-     (symbol.small_stmt,
-      (symbol.expr_stmt,
-       (symbol.testlist,
-        (symbol.test,
-         (symbol.or_test,
-           (symbol.and_test,
-            (symbol.not_test,
-             (symbol.comparison,
-              (symbol.expr,
-               (symbol.xor_expr,
-                (symbol.and_expr,
-                 (symbol.shift_expr,
-                  (symbol.arith_expr,
-                   (symbol.term,
-                    (symbol.factor,
-                     (symbol.power,
-                      (symbol.atom,
-                       (token.NAME, ['varname']),
-       ))))))))))))))),
-       (token.EQUAL, '='),
-       (symbol.testlist, ['rhs']))),
-     (token.NEWLINE, ''),
-   ))
-
-class ParseTreeInfo:
-    docstring = ''
-    name = ''
-
-    def __init__(self, tree, name, file):
-        """
-        The code can be a string (in which case it is parsed), or it
-        can be in parse tree form already.
-        """
-        self.name = name
-        self.file = file
-        self.class_info = {}
-        self.function_info = {}
-        self.assign_info = {}
-        self.derivs = {}
-        if isinstance(tree, str):
-            try:
-                tree = parser.suite(tree+"\n").totuple()
-                if (tree):
-                    found, vars = self.match(DOCSTRING_STMT_PATTERN, tree[1])
-                    if found:
-                        self.docstring = vars["docstring"]
-            except:
-                print("CAUTION --- Parse failed: " + name)
-        if isinstance(tree, tuple):
-            self.extract_info(tree)
-
-    def match(self, pattern, data, vars=None):
-        """
-        pattern
-            Pattern to match against, possibly containing variables.
-        data
-            Data to be checked and against which variables are extracted.
-        vars
-            Dictionary of variables which have already been found.  If not
-            provided, an empty dictionary is created.
-
-        The `pattern' value may contain variables of the form ['varname']
-        which are allowed to parseTreeMatch anything.  The value that is
-        parseTreeMatched is returned as part of a dictionary which maps
-        'varname' to the parseTreeMatched value.  'varname' is not required
-        to be a string object, but using strings makes patterns and the code
-        which uses them more readable.  This function returns two values: a
-        boolean indicating whether a parseTreeMatch was found and a
-        dictionary mapping variable names to their associated values.
-        """
-        if vars is None:
-            vars = {}
-        if type(pattern) is list:       # 'variables' are ['varname']
-            vars[pattern[0]] = data
-            return 1, vars
-        if type(pattern) is not tuple:
-            return (pattern == data), vars
-        if len(data) != len(pattern):
-            return 0, vars
-        for pattern, data in map(None, pattern, data):
-            same, vars = self.match(pattern, data, vars)
-            if not same:
-                break
-        return same, vars
-
-    def extract_info(self, tree):
-        # extract docstring
-        found = 0
-        if len(tree) == 2:
-            found, vars = self.match(DOCSTRING_STMT_PATTERN[1], tree[1])
-        elif len(tree) >= 4:
-            found, vars = self.match(DOCSTRING_STMT_PATTERN, tree[3])
-        if found:
-            self.docstring = eval(vars['docstring'])
-        # discover inner definitions
-        for node in tree[1:]:
-            found, vars = self.match(ASSIGNMENT_STMT_PATTERN, node)
-            if found:
-                self.assign_info[vars['varname']] = 1
-            found, vars = self.match(COMPOUND_STMT_PATTERN, node)
-            if found:
-                cstmt = vars['compound']
-                if cstmt[0] == symbol.funcdef:
-                    name = cstmt[2][1]
-                    # Workaround for a weird issue with static and classmethods
-                    if name == "def":
-                        name = cstmt[3][1]
-                        self.function_info[name] = ParseTreeInfo(cstmt and cstmt[-1] or None, name, self.file)
-                        self.function_info[name].prototype = self.extract_tokens("", cstmt[4])
-                    else:
-                        self.function_info[name] = ParseTreeInfo(cstmt and cstmt[-1] or None, name, self.file)
-                        self.function_info[name].prototype = self.extract_tokens("", cstmt[3])
-                elif cstmt[0] == symbol.classdef:
-                    name = cstmt[2][1]
-                    self.class_info[name] = ParseTreeInfo(cstmt and cstmt[-1] or None, name, self.file)
-                    self.extract_derivs(self.class_info[name], cstmt)
-
-    def extract_derivs(self, classinfo, tree):
-        if (len(tree)==8):
-            derivs = tree[4]
-            for deriv in derivs[1:]:
-                found, vars = self.match(DERIVATION_PATTERN, deriv)
-                if (found):
-                    classinfo.derivs[vars["classname"]] = 1
-
-    def extract_tokens(self, str, tree):
-        if (isinstance(tree, tuple)):
-            if tree[0] in token.tok_name:
-                str = str + tree[1]
-                if (tree[1]==","): str=str+" "
-            elif tree[0] in symbol.sym_name:
-                for sub in tree[1:]:
-                    str = self.extract_tokens(str, sub)
-        return str
-
-########################################################################
-#
-# The code database contains:
-#
-#  - a list of InterrogateDatabase objects representing C++ modules.
-#  - a list of ParseTreeInfo objects representing python modules.
-#
-# Collectively, these make up all the data about all the code.
-#
-########################################################################
-
-class CodeDatabase:
-    def __init__(self, cxxlist, pylist):
-        self.types = {}
-        self.funcs = {}
-        self.goodtypes = {}
-        self.funcExports = {}
-        self.typeExports = {}
-        self.varExports = {}
-        self.globalfn = []
-        self.formattedprotos = {}
-        print("Reading C++ source files")
-        for cxx in cxxlist:
-            tokzr = InterrogateTokenizer(cxx)
-            idb = InterrogateDatabase(tokzr)
-            for type in idb.types.values():
-                if (type.flags & 8192) or type.scopedname not in self.types:
-                    self.types[type.scopedname] = type
-                if (type.flags & 8192) and (type.atomictype == 0) and (type.scopedname.count(" ")==0) and (type.scopedname.count(":")==0):
-                    self.goodtypes[type.scopedname] = type
-                    self.typeExports.setdefault("pandac.PandaModules", []).append(type.scopedname)
-            for func in idb.functions.values():
-                type = idb.types.get(func.classindex)
-                func.pyname = convertToPythonFn(func.componentname)
-                if (type == None):
-                    self.funcs["GLOBAL."+func.pyname] = func
-                    self.globalfn.append("GLOBAL."+func.pyname)
-                    self.funcExports.setdefault("pandac.PandaModules", []).append(func.pyname)
-                else:
-                    self.funcs[type.scopedname+"."+func.pyname] = func
-        print("Reading Python sources files")
-        for py in pylist:
-            pyinf = ParseTreeInfo(readFile(py), py, py)
-            mod = pathToModule(py)
-            for type in pyinf.class_info.keys():
-                typinf = pyinf.class_info[type]
-                self.types[type] = typinf
-                self.goodtypes[type] = typinf
-                self.typeExports.setdefault(mod, []).append(type)
-                for func in typinf.function_info.keys():
-                    self.funcs[type+"."+func] = typinf.function_info[func]
-            for func in pyinf.function_info.keys():
-                self.funcs["GLOBAL."+func] = pyinf.function_info[func]
-                self.globalfn.append("GLOBAL."+func)
-                self.funcExports.setdefault(mod, []).append(func)
-            for var in pyinf.assign_info.keys():
-                self.varExports.setdefault(mod, []).append(var)
-
-    def getClassList(self):
-        return list(self.goodtypes.keys())
-
-    def getGlobalFunctionList(self):
-        return self.globalfn
-
-    def getClassComment(self, cn):
-        type = self.types.get(cn)
-        if (isinstance(type, InterrogateType)):
-            return textToHTML(type.comment,"/")
-        elif (isinstance(type, ParseTreeInfo)):
-            return textToHTML(type.docstring,"#")
-        else:
-            return ""
-
-    def getClassParents(self, cn):
-        type = self.types.get(cn)
-        if (isinstance(type, InterrogateType)):
-            parents = []
-            for deriv in type.derivations:
-                basetype = type.db.types[deriv.base]
-                parents.append(basetype.scopedname)
-            return parents
-        elif (isinstance(type, ParseTreeInfo)):
-            return list(type.derivs.keys())
-        else:
-            return []
-
-    def getClassConstants(self, cn):
-        type = self.types.get(cn)
-        if (isinstance(type, InterrogateType)):
-            result = []
-            for subtype in type.nested:
-                enumtype = type.db.types[subtype]
-                if (len(enumtype.enumvalues)):
-                    for enumvalue in enumtype.enumvalues:
-                        name = convertToPythonFn(enumvalue.name)
-                        result.append((name, "("+enumtype.componentname+")"))
-                    result.append(("",""))
-            return result
-        else:
-            return []
-
-    def buildInheritance(self, inheritance, cn):
-        if (inheritance.count(cn) == 0):
-            inheritance.append(cn)
-            for parent in self.getClassParents(cn):
-                self.buildInheritance(inheritance, parent)
-
-    def getInheritance(self, cn):
-        inheritance = []
-        self.buildInheritance(inheritance, cn)
-        return inheritance
-
-    def getClassImport(self, cn):
-        type = self.types.get(cn)
-        if (isinstance(type, InterrogateType)):
-            return "pandac.PandaModules"
-        else:
-            return pathToModule(type.file)
-
-    def getClassConstructors(self, cn):
-        # Only detects C++ constructors, not Python constructors, since
-        # those are treated as ordinary methods.
-        type = self.types.get(cn)
-        result = []
-        if (isinstance(type, InterrogateType)):
-            for constructor in type.constructors:
-                func = type.db.functions[constructor]
-                if (func.classindex == type.index):
-                    result.append(type.scopedname+"."+func.pyname)
-        return result
-
-    def getClassMethods(self, cn):
-        type = self.types.get(cn)
-        result = []
-        if (isinstance(type, InterrogateType)):
-            for method in type.methods:
-                func = type.db.functions[method]
-                if (func.classindex == type.index):
-                    result.append(type.scopedname+"."+func.pyname)
-        elif (isinstance(type, ParseTreeInfo)):
-            for method in type.function_info.keys():
-                result.append(type.name + "." + method)
-        return result
-
-    def getFunctionName(self, fn):
-        func = self.funcs.get(fn)
-        if (isinstance(func, InterrogateFunction)):
-            return func.pyname
-        elif (isinstance(func, ParseTreeInfo)):
-            return func.name
-        else:
-            return fn
-
-    def getFunctionImport(self, fn):
-        func = self.funcs.get(fn)
-        if (isinstance(func, InterrogateFunction)):
-            return "pandac.PandaModules"
-        else:
-            return pathToModule(func.file)
-
-    def getFunctionPrototype(self, fn, urlprefix, urlsuffix):
-        func = self.funcs.get(fn)
-        if (isinstance(func, InterrogateFunction)):
-            if fn in self.formattedprotos:
-                proto = self.formattedprotos[fn]
-            else:
-                proto = func.prototype
-                proto = proto.replace(" inline "," ")
-                if (proto.startswith("inline ")): proto = proto[7:]
-                proto = proto.replace("basic_string< char >", "string")
-                proto = textToHTML(proto,"")
-                if "." in fn:
-                    for c in self.goodtypes.keys():
-                        if c != fn.split(".")[0] and (c in proto):
-                            proto = re.sub("\\b%s\\b" % c, linkTo(urlprefix+c+urlsuffix, c), proto)
-                self.formattedprotos[fn] = proto
-            return proto
-        elif (isinstance(func, ParseTreeInfo)):
-            return textToHTML("def "+func.name+func.prototype,"")
-        return fn
-
-    def getFunctionComment(self, fn):
-        func = self.funcs.get(fn)
-        if (isinstance(func, InterrogateFunction)):
-            return textToHTML(removeFileLicense(func.comment), "/", JUNKHEADER)
-        elif (isinstance(func, ParseTreeInfo)):
-            return textToHTML(func.docstring, "#")
-        return fn
-
-    def isFunctionPython(self, fn):
-        func = self.funcs.get(fn)
-        if (isinstance(func, InterrogateFunction)):
-            return False
-        elif (isinstance(func, ParseTreeInfo)):
-            return True
-        return False
-
-    def getFuncExports(self, mod):
-        return self.funcExports.get(mod, [])
-
-    def getTypeExports(self, mod):
-        return self.typeExports.get(mod, [])
-
-    def getVarExports(self, mod):
-        return self.varExports.get(mod, [])
-
-########################################################################
-#
-# The "Class Rename Dictionary" - Yech.
-#
-########################################################################
-
-CLASS_RENAME_DICT = {
-    # No longer used, now empty.
-}
-
-
-########################################################################
-#
-# HTML generation
-#
-########################################################################
-
-def makeCodeDatabase(indirlist, directdirlist):
-    if isinstance(directdirlist, str):
-        directdirlist = [directdirlist]
-    ignore = {}
-    ignore["__init__.py"] = 1
-    for directdir in directdirlist:
-        ignore[directdir + "/src/directscripts"] = 1
-        ignore[directdir + "/src/extensions"] = 1
-        ignore[directdir + "/src/extensions_native"] = 1
-        ignore[directdir + "/src/ffi"] = 1
-        ignore[directdir + "/built"] = 1
-    cxxfiles = []
-    pyfiles = []
-    findFiles(indirlist,     ".in", ignore, cxxfiles)
-    findFiles(directdirlist, ".py", ignore, pyfiles)
-    return CodeDatabase(cxxfiles, pyfiles)
-
-def generateFunctionDocs(code, method, urlprefix, urlsuffix):
-    name = code.getFunctionName(method)
-    proto = code.getFunctionPrototype(method, urlprefix, urlsuffix)
-    comment = code.getFunctionComment(method)
-    if (comment == ""): comment = "Undocumented function.<br>\n"
-    chunk = '<table bgcolor="e8e8e8" border=0 cellspacing=0 cellpadding=5 width="100%"><tr><td>' + "\n"
-    chunk = chunk + '<a name="' + name + '"><b>' + name + "</b></a><br>\n"
-    chunk = chunk + proto + "<br>\n"
-    chunk = chunk + comment
-    chunk = chunk + "</td></tr></table><br>\n"
-    return chunk
-
-def generateLinkTable(link, text, cols, urlprefix, urlsuffix):
-    column = (len(link)+cols-1)/cols
-    percent = 100 / cols
-    result = '<table width="100%">\n'
-    for i in range(column):
-        line = ""
-        for j in range(cols):
-            slot = i + column*j
-            linkval = ""
-            textval = ""
-            if (slot < len(link)): linkval = link[slot]
-            if (slot < len(text)): textval = text[slot]
-            if (i==0):
-                line = line + '<td width="' + str(percent) + '%">' + linkTo(urlprefix+linkval+urlsuffix, textval) + "</td>"
-            else:
-                line = line + '<td>' + linkTo(urlprefix+linkval+urlsuffix, textval) + "</td>"
-        result = result + "<tr>" + line + "</tr>\n"
-    result = result + "</table>\n"
-    return result
-
-def generate(pversion, indirlist, directdirlist, docdir, header, footer, urlprefix, urlsuffix):
-    code = makeCodeDatabase(indirlist, directdirlist)
-    classes = code.getClassList()[:]
-    classes.sort(None, str.lower)
-    xclasses = classes[:]
-    print("Generating HTML pages")
-    for type in classes:
-        body = "<h1>" + type + "</h1>\n"
-        comment = code.getClassComment(type)
-        body = body + "<ul>\nfrom " + code.getClassImport(type) + " import " + type + "</ul>\n\n"
-        body = body + "<ul>\n" + comment + "</ul>\n\n"
-        inheritance = code.getInheritance(type)
-        body = body + "<h2>Inheritance:</h2>\n<ul>\n"
-        for inh in inheritance:
-            line = "  " + linkTo(urlprefix+inh+urlsuffix, inh) + ": "
-            for parent in code.getClassParents(inh):
-                line = line + linkTo(urlprefix+parent+urlsuffix, parent) + " "
-            body = body + line + "<br>\n"
-        body = body + "</ul>\n"
-        for sclass in inheritance:
-            methods = code.getClassMethods(sclass)[:]
-            methods.sort(None, str.lower)
-            constructors = code.getClassConstructors(sclass)
-            if (len(methods) > 0 or len(constructors) > 0):
-                body = body + "<h2>Methods of "+sclass+":</h2>\n<ul>\n"
-                if len(constructors) > 0:
-                    fn = code.getFunctionName(constructors[0])
-                    body = body + '<a href="#' + fn + '">' + fn + " (Constructor)</a><br>\n"
-                for method in methods:
-                    fn = code.getFunctionName(method)
-                    body = body + '<a href="#' + fn + '">' + fn + "</a><br>\n"
-                body = body + "</ul>\n"
-        for sclass in inheritance:
-            enums = code.getClassConstants(sclass)[:]
-            if (len(enums) > 0):
-                body = body + "<h2>Constants in "+sclass+":</h2>\n<ul><table>\n"
-                for (value, comment) in enums:
-                    body = body + "<tr><td>" + value + "</td><td>" + comment + "</td></tr>\n"
-                body = body + "</table></ul>"
-        for sclass in inheritance:
-            constructors = code.getClassConstructors(sclass)
-            for constructor in constructors:
-                body = body + generateFunctionDocs(code, constructor, urlprefix, urlsuffix)
-            methods = code.getClassMethods(sclass)[:]
-            methods.sort(None, str.lower)
-            for method in methods:
-                body = body + generateFunctionDocs(code, method, urlprefix, urlsuffix)
-        body = header + body + footer
-        writeFile(docdir + "/" + type + ".html", body)
-        if type in CLASS_RENAME_DICT:
-            modtype = CLASS_RENAME_DICT[type]
-            writeFile(docdir + "/" + modtype + ".html", body)
-            xclasses.append(modtype)
-    xclasses.sort(None, str.lower)
-
-    index = "<h1>List of Classes - Panda " + pversion + "</h1>\n"
-    index = index + generateLinkTable(xclasses, xclasses, 3, urlprefix, urlsuffix)
-    fnlist = code.getGlobalFunctionList()[:]
-    fnlist.sort(None, str.lower)
-    fnnames = []
-    for i in range(len(fnlist)):
-        fnnames.append(code.getFunctionName(fnlist[i]))
-    index = header + index + footer
-    writeFile(docdir + "/classes.html", index)
-
-    index = "<h1>List of Global Functions - Panda " + pversion + "</h1>\n"
-    index = index + generateLinkTable(fnnames, fnnames, 3,"#","")
-    for func in fnlist:
-        index = index + generateFunctionDocs(code, func, urlprefix, urlsuffix)
-    index = header + index + footer
-    writeFile(docdir + "/functions.html", index)
-
-    table = {}
-    for type in classes:
-        for method in code.getClassMethods(type)[:]:
-            name = code.getFunctionName(method)
-            prefix = name[0].upper()
-            if prefix not in table:
-                table[prefix] = {}
-            if name not in table[prefix]:
-                table[prefix][name] = []
-            table[prefix][name].append(type)
-
-    index = "<h1>List of Methods - Panda " + pversion + "</h1>\n"
-
-    prefixes = list(table.keys())
-    prefixes.sort(None, str.lower)
-    for prefix in prefixes:
-        index = index + linkTo("#"+prefix, prefix) + " "
-    index = index + "<br><br>"
-    for prefix in prefixes:
-        index = index + '<a name="' + prefix + '">' + "\n"
-        names = list(table[prefix].keys())
-        names.sort(None, str.lower)
-        for name in names:
-            line = '<b>' + name + ":</b><ul>\n"
-            ctypes = table[prefix][name]
-            ctypes.sort(None, str.lower)
-            for type in ctypes:
-                line = line + "<li>" + linkTo(urlprefix+type+urlsuffix+"#"+name, type) + "\n"
-            line = line + "</ul>\n"
-            index = index + line + "\n"
-    index = header + index + footer
-    writeFile(docdir + "/methods.html", index)
-
-    index = "<h1>Panda " + pversion + "</h1>\n"
-    index = index + "<ul>\n"
-    index = index + "<li>" + linkTo(urlprefix+"classes"+urlsuffix, "List of all Classes") + "\n"
-    index = index + "</ul>\n"
-    index = index + "<ul>\n"
-    index = index + "<li>" + linkTo(urlprefix+"functions"+urlsuffix, "List of all Global Functions") + "\n"
-    index = index + "</ul>\n"
-    index = index + "<ul>\n"
-    index = index + "<li>" + linkTo(urlprefix+"methods"+urlsuffix, "List of all Methods (very long)") + "\n"
-    index = index + "</ul>\n"
-    writeFile(docdir + "/index.html", index)
-
-
-########################################################################
-#
-# IMPORT repair
-#
-########################################################################
-
-def expandImports(indirlist, directdirlist, fixdirlist):
-    code = makeCodeDatabase(indirlist, directdirlist)
-    fixfiles = []
-    findFiles(fixdirlist, ".py", {}, fixfiles)
-    for fixfile in fixfiles:
-        if (os.path.isfile(fixfile+".orig")):
-            text = readFile(fixfile+".orig")
-        else:
-            text = readFile(fixfile)
-            writeFile(fixfile+".orig", text)
-        text = text.replace("\r","")
-        lines = text.split("\n")
-        used = {}
-        for id in IDENTIFIER.findall(text):
-            used[id] = 1
-        result = []
-        for line in lines:
-            mat = IMPORTSTAR.match(line)
-            if (mat):
-                module = mat.group(1)
-                if (fixfile.count("/")!=0) and (module.count(".")==0):
-                    modfile = os.path.dirname(fixfile)+"/"+module+".py"
-                    if (os.path.isfile(modfile)):
-                        module = pathToModule(modfile)
-                typeExports = code.getTypeExports(module)
-                funcExports = code.getFuncExports(module)
-                varExports = code.getVarExports(module)
-                if (len(typeExports)+len(funcExports)+len(varExports)==0):
-                    result.append(line)
-                    print(fixfile + " : " + module + " : no exports")
-                else:
-                    print(fixfile + " : " + module + " : repairing")
-                    for x in funcExports:
-                        fn = code.getFunctionName(x)
-                        if fn in used:
-                            result.append("from "+module+" import "+fn)
-                    for x in typeExports:
-                        if x in used:
-                            result.append("from "+module+" import "+x)
-                    for x in varExports:
-                        if x in used:
-                            result.append("from "+module+" import "+x)
-            else:
-                result.append(line)
-        writeFileLines(fixfile, result)

+ 8 - 8
direct/src/directtools/DirectCameraControl.py

@@ -6,6 +6,7 @@ from .DirectSelection import SelectionRay
 from direct.interval.IntervalGlobal import Sequence, Func
 from direct.interval.IntervalGlobal import Sequence, Func
 from direct.directnotify import DirectNotifyGlobal
 from direct.directnotify import DirectNotifyGlobal
 from direct.task import Task
 from direct.task import Task
+from direct.task.TaskManagerGlobal import taskMgr
 
 
 CAM_MOVE_DURATION = 1.2
 CAM_MOVE_DURATION = 1.2
 COA_MARKER_SF = 0.0075
 COA_MARKER_SF = 0.0075
@@ -22,7 +23,7 @@ class DirectCameraControl(DirectObject):
         self.orthoViewRoll = 0.0
         self.orthoViewRoll = 0.0
         self.lastView = 0
         self.lastView = 0
         self.coa = Point3(0, 100, 0)
         self.coa = Point3(0, 100, 0)
-        self.coaMarker = loader.loadModel('models/misc/sphere')
+        self.coaMarker = base.loader.loadModel('models/misc/sphere')
         self.coaMarker.setName('DirectCameraCOAMarker')
         self.coaMarker.setName('DirectCameraCOAMarker')
         self.coaMarker.setTransparency(1)
         self.coaMarker.setTransparency(1)
         self.coaMarker.setColor(1, 0, 0, 0)
         self.coaMarker.setColor(1, 0, 0, 0)
@@ -150,7 +151,7 @@ class DirectCameraControl(DirectObject):
     def __startManipulateCamera(self, func = None, task = None, ival = None):
     def __startManipulateCamera(self, func = None, task = None, ival = None):
         self.__stopManipulateCamera()
         self.__stopManipulateCamera()
         if func:
         if func:
-            assert(task is None)
+            assert task is None
             task = Task.Task(func)
             task = Task.Task(func)
         if task:
         if task:
             self.manipulateCameraTask = taskMgr.add(task, 'manipulateCamera')
             self.manipulateCameraTask = taskMgr.add(task, 'manipulateCamera')
@@ -366,7 +367,7 @@ class DirectCameraControl(DirectObject):
                                 moveDir[2],
                                 moveDir[2],
                                 hVal,
                                 hVal,
                                 0.0, 0.0)
                                 0.0, 0.0)
-        if (self.lockRoll == True):
+        if self.lockRoll:
             # flatten roll
             # flatten roll
             base.direct.camera.setR(0)
             base.direct.camera.setR(0)
 
 
@@ -449,7 +450,7 @@ class DirectCameraControl(DirectObject):
                                  (deltaX * base.direct.dr.fovH),
                                  (deltaX * base.direct.dr.fovH),
                                  (-deltaY * base.direct.dr.fovV),
                                  (-deltaY * base.direct.dr.fovV),
                                  0.0)
                                  0.0)
-            if (self.lockRoll == True):
+            if self.lockRoll:
                 # flatten roll
                 # flatten roll
                 base.direct.camera.setR(0)
                 base.direct.camera.setR(0)
             self.camManipRef.setPos(self.coaMarkerPos)
             self.camManipRef.setPos(self.coaMarkerPos)
@@ -466,7 +467,7 @@ class DirectCameraControl(DirectObject):
                                     (deltaY * 180.0),
                                     (deltaY * 180.0),
                                     0.0)
                                     0.0)
 
 
-            if (self.lockRoll == True):
+            if self.lockRoll:
                 # flatten roll
                 # flatten roll
                 self.camManipRef.setR(0)
                 self.camManipRef.setR(0)
             base.direct.camera.setTransform(self.camManipRef, wrt)
             base.direct.camera.setTransform(self.camManipRef, wrt)
@@ -491,7 +492,7 @@ class DirectCameraControl(DirectObject):
         deltaAngle = angle - state.lastAngle
         deltaAngle = angle - state.lastAngle
         state.lastAngle = angle
         state.lastAngle = angle
         self.camManipRef.setHpr(self.camManipRef, 0, 0, deltaAngle)
         self.camManipRef.setHpr(self.camManipRef, 0, 0, deltaAngle)
-        if (self.lockRoll == True):
+        if self.lockRoll:
             # flatten roll
             # flatten roll
             self.camManipRef.setR(0)
             self.camManipRef.setR(0)
         base.direct.camera.setTransform(self.camManipRef, wrt)
         base.direct.camera.setTransform(self.camManipRef, wrt)
@@ -576,7 +577,7 @@ class DirectCameraControl(DirectObject):
         if not coaDist:
         if not coaDist:
             coaDist = Vec3(self.coa - ZERO_POINT).length()
             coaDist = Vec3(self.coa - ZERO_POINT).length()
         # Place the marker in render space
         # Place the marker in render space
-        if ref == None:
+        if ref is None:
             # KEH: use the current display region
             # KEH: use the current display region
             # ref = base.cam
             # ref = base.cam
             ref = base.direct.drList.getCurrentDr().cam
             ref = base.direct.drList.getCurrentDr().cam
@@ -892,4 +893,3 @@ class DirectCameraControl(DirectObject):
 
 
     def removeManipulateCameraTask(self):
     def removeManipulateCameraTask(self):
         self.__stopManipulateCamera()
         self.__stopManipulateCamera()
-

+ 3 - 4
direct/src/directtools/DirectGeometry.py

@@ -163,7 +163,7 @@ def getCrankAngle(center):
     # origin) in screen space
     # origin) in screen space
     x = base.direct.dr.mouseX - center[0]
     x = base.direct.dr.mouseX - center[0]
     y = base.direct.dr.mouseY - center[2]
     y = base.direct.dr.mouseY - center[2]
-    return (180 + rad2Deg(math.atan2(y, x)))
+    return 180 + rad2Deg(math.atan2(y, x))
 
 
 def relHpr(nodePath, base, h, p, r):
 def relHpr(nodePath, base, h, p, r):
     # Compute nodePath2newNodePath relative to base coordinate system
     # Compute nodePath2newNodePath relative to base coordinate system
@@ -205,9 +205,9 @@ def qSlerp(startQuat, endQuat, t):
         startQ.setJ(-1 * startQ.getJ())
         startQ.setJ(-1 * startQ.getJ())
         startQ.setK(-1 * startQ.getK())
         startQ.setK(-1 * startQ.getK())
         startQ.setR(-1 * startQ.getR())
         startQ.setR(-1 * startQ.getR())
-    if ((1.0 + cosOmega) > Q_EPSILON):
+    if (1.0 + cosOmega) > Q_EPSILON:
         # usual case
         # usual case
-        if ((1.0 - cosOmega) > Q_EPSILON):
+        if (1.0 - cosOmega) > Q_EPSILON:
             # usual case
             # usual case
             omega = math.acos(cosOmega)
             omega = math.acos(cosOmega)
             sinOmega = math.sin(omega)
             sinOmega = math.sin(omega)
@@ -240,4 +240,3 @@ def qSlerp(startQuat, endQuat, t):
         destQuat.setK(startScale * startQ.getK() +
         destQuat.setK(startScale * startQ.getK() +
                       endScale * endQuat.getK())
                       endScale * endQuat.getK())
     return destQuat
     return destQuat
-

+ 0 - 1
direct/src/directtools/DirectGlobals.py

@@ -59,4 +59,3 @@ def LE_showInOneCam(nodePath, thisCamName):
     for camName in LE_CAM_MASKS:
     for camName in LE_CAM_MASKS:
         if camName != thisCamName:
         if camName != thisCamName:
             nodePath.hide(LE_CAM_MASKS[camName])
             nodePath.hide(LE_CAM_MASKS[camName])
-

+ 4 - 3
direct/src/directtools/DirectGrid.py

@@ -4,6 +4,7 @@ from direct.showbase.DirectObject import DirectObject
 from .DirectUtil import *
 from .DirectUtil import *
 from .DirectGeometry import *
 from .DirectGeometry import *
 
 
+
 class DirectGrid(NodePath, DirectObject):
 class DirectGrid(NodePath, DirectObject):
     def __init__(self,gridSize=100.0,gridSpacing=5.0,planeColor=(0.5,0.5,0.5,0.5),parent = None):
     def __init__(self,gridSize=100.0,gridSpacing=5.0,planeColor=(0.5,0.5,0.5,0.5),parent = None):
         # Initialize superclass
         # Initialize superclass
@@ -13,7 +14,7 @@ class DirectGrid(NodePath, DirectObject):
 
 
         # Load up grid parts to initialize grid object
         # Load up grid parts to initialize grid object
         # Polygon used to mark grid plane
         # Polygon used to mark grid plane
-        self.gridBack = loader.loadModel('models/misc/gridBack')
+        self.gridBack = base.loader.loadModel('models/misc/gridBack')
         self.gridBack.reparentTo(self)
         self.gridBack.reparentTo(self)
         self.gridBack.setColor(*planeColor)
         self.gridBack.setColor(*planeColor)
 
 
@@ -35,7 +36,7 @@ class DirectGrid(NodePath, DirectObject):
         self.centerLines.setThickness(3)
         self.centerLines.setThickness(3)
 
 
         # Small marker to hilight snap-to-grid point
         # Small marker to hilight snap-to-grid point
-        self.snapMarker = loader.loadModel('models/misc/sphere')
+        self.snapMarker = base.loader.loadModel('models/misc/sphere')
         self.snapMarker.node().setName('gridSnapMarker')
         self.snapMarker.node().setName('gridSnapMarker')
         self.snapMarker.reparentTo(self)
         self.snapMarker.reparentTo(self)
         self.snapMarker.setColor(1, 0, 0, 1)
         self.snapMarker.setColor(1, 0, 0, 1)
@@ -107,7 +108,7 @@ class DirectGrid(NodePath, DirectObject):
         center.create()
         center.create()
         minor.create()
         minor.create()
         major.create()
         major.create()
-        if (self.gridBack):
+        if self.gridBack:
             self.gridBack.setScale(scaledSize)
             self.gridBack.setScale(scaledSize)
 
 
     def setXyzSnap(self, fSnap):
     def setXyzSnap(self, fSnap):

+ 8 - 9
direct/src/directtools/DirectLights.py

@@ -1,5 +1,7 @@
 
 
 from panda3d.core import *
 from panda3d.core import *
+from direct.showbase.MessengerGlobal import messenger
+
 
 
 class DirectLight(NodePath):
 class DirectLight(NodePath):
     def __init__(self, light, parent):
     def __init__(self, light, parent):
@@ -58,21 +60,21 @@ class DirectLights(NodePath):
         nameList.sort()
         nameList.sort()
         return nameList
         return nameList
 
 
-    def create(self, type):
-        type = type.lower()
-        if type == 'ambient':
+    def create(self, ltype):
+        ltype = ltype.lower()
+        if ltype == 'ambient':
             self.ambientCount += 1
             self.ambientCount += 1
             light = AmbientLight('ambient-' + repr(self.ambientCount))
             light = AmbientLight('ambient-' + repr(self.ambientCount))
             light.setColor(VBase4(.3, .3, .3, 1))
             light.setColor(VBase4(.3, .3, .3, 1))
-        elif type == 'directional':
+        elif ltype == 'directional':
             self.directionalCount += 1
             self.directionalCount += 1
             light = DirectionalLight('directional-' + repr(self.directionalCount))
             light = DirectionalLight('directional-' + repr(self.directionalCount))
             light.setColor(VBase4(1))
             light.setColor(VBase4(1))
-        elif type == 'point':
+        elif ltype == 'point':
             self.pointCount += 1
             self.pointCount += 1
             light = PointLight('point-' + repr(self.pointCount))
             light = PointLight('point-' + repr(self.pointCount))
             light.setColor(VBase4(1))
             light.setColor(VBase4(1))
-        elif type == 'spot':
+        elif ltype == 'spot':
             self.spotCount += 1
             self.spotCount += 1
             light = Spotlight('spot-' + repr(self.spotCount))
             light = Spotlight('spot-' + repr(self.spotCount))
             light.setColor(VBase4(1))
             light.setColor(VBase4(1))
@@ -130,6 +132,3 @@ class DirectLights(NodePath):
         Turn off the given directLight
         Turn off the given directLight
         """
         """
         render.clearLight(directLight)
         render.clearLight(directLight)
-
-
-

+ 58 - 57
direct/src/directtools/DirectManipulation.py

@@ -1,4 +1,5 @@
 from direct.showbase.DirectObject import DirectObject
 from direct.showbase.DirectObject import DirectObject
+from direct.showbase.MessengerGlobal import messenger
 from .DirectGlobals import *
 from .DirectGlobals import *
 from .DirectUtil import *
 from .DirectUtil import *
 from .DirectGeometry import *
 from .DirectGeometry import *
@@ -6,6 +7,7 @@ from .DirectSelection import SelectionRay
 from direct.task import Task
 from direct.task import Task
 from copy import deepcopy
 from copy import deepcopy
 
 
+
 class DirectManipulationControl(DirectObject):
 class DirectManipulationControl(DirectObject):
     def __init__(self):
     def __init__(self):
         # Create the grid
         # Create the grid
@@ -201,7 +203,7 @@ class DirectManipulationControl(DirectObject):
             ((abs (endY - startY)) < 0.01)):
             ((abs (endY - startY)) < 0.01)):
             return
             return
 
 
-        self.marquee = LineNodePath(render2d, 'marquee', 0.5, VBase4(.8, .6, .6, 1))
+        self.marquee = LineNodePath(base.render2d, 'marquee', 0.5, VBase4(.8, .6, .6, 1))
         self.marqueeInfo = (startX, startY, endX, endY)
         self.marqueeInfo = (startX, startY, endX, endY)
         self.marquee.drawLines([
         self.marquee.drawLines([
             [(startX, 0, startY), (startX, 0, endY)],
             [(startX, 0, startY), (startX, 0, endY)],
@@ -252,7 +254,7 @@ class DirectManipulationControl(DirectObject):
                 lens.extrude((endX, endY), nlr, flr)
                 lens.extrude((endX, endY), nlr, flr)
                 lens.extrude((startX, endY), nll, fll)
                 lens.extrude((startX, endY), nll, fll)
 
 
-                marqueeFrustum = BoundingHexahedron(fll, flr, fur, ful, nll, nlr, nur, nul);
+                marqueeFrustum = BoundingHexahedron(fll, flr, fur, ful, nll, nlr, nur, nul)
                 marqueeFrustum.xform(base.direct.cam.getNetTransform().getMat())
                 marqueeFrustum.xform(base.direct.cam.getNetTransform().getMat())
 
 
                 base.marqueeFrustum = marqueeFrustum
                 base.marqueeFrustum = marqueeFrustum
@@ -273,13 +275,13 @@ class DirectManipulationControl(DirectObject):
 ##                     elif (skipFlags & SKIP_BACKFACE) and base.direct.iRay.isEntryBackfacing():
 ##                     elif (skipFlags & SKIP_BACKFACE) and base.direct.iRay.isEntryBackfacing():
 ##                         # Skip, if backfacing poly
 ##                         # Skip, if backfacing poly
 ##                         pass
 ##                         pass
-                    elif ((skipFlags & SKIP_CAMERA) and
-                          (camera in geom.getAncestors())):
+                    elif (skipFlags & SKIP_CAMERA) and \
+                         (base.camera in geom.getAncestors()):
                         # Skip if parented to a camera.
                         # Skip if parented to a camera.
                         continue
                         continue
                     # Can pick unpickable, use the first visible node
                     # Can pick unpickable, use the first visible node
-                    elif ((skipFlags & SKIP_UNPICKABLE) and
-                          (geom.getName() in base.direct.iRay.unpickable)):
+                    elif (skipFlags & SKIP_UNPICKABLE) and \
+                         (geom.getName() in base.direct.iRay.unpickable):
                         # Skip if in unpickable list
                         # Skip if in unpickable list
                         continue
                         continue
 
 
@@ -486,7 +488,7 @@ class DirectManipulationControl(DirectObject):
         for tag in self.unmovableTagList:
         for tag in self.unmovableTagList:
             for selected in objects:
             for selected in objects:
                 unmovableTag = selected.getTag(tag)
                 unmovableTag = selected.getTag(tag)
-                if (unmovableTag):
+                if unmovableTag:
                     # check value of unmovableTag to see if it is
                     # check value of unmovableTag to see if it is
                     # completely uneditable or if it allows only certain
                     # completely uneditable or if it allows only certain
                     # types of editing
                     # types of editing
@@ -498,7 +500,7 @@ class DirectManipulationControl(DirectObject):
         selectedList = base.direct.selected.getSelectedAsList()
         selectedList = base.direct.selected.getSelectedAsList()
         # See if any of the selected are completely uneditable
         # See if any of the selected are completely uneditable
         editTypes = self.getEditTypes(selectedList)
         editTypes = self.getEditTypes(selectedList)
-        if (editTypes & EDIT_TYPE_UNEDITABLE == EDIT_TYPE_UNEDITABLE):
+        if (editTypes & EDIT_TYPE_UNEDITABLE) == EDIT_TYPE_UNEDITABLE:
             return
             return
         self.currEditTypes = editTypes
         self.currEditTypes = editTypes
         if selectedList:
         if selectedList:
@@ -633,7 +635,7 @@ class DirectManipulationControl(DirectObject):
                 base.direct.widget.getMat(base.direct.selected.last))
                 base.direct.widget.getMat(base.direct.selected.last))
         else:
         else:
             # Move the objects with the widget
             # Move the objects with the widget
-                base.direct.selected.moveWrtWidgetAll()
+            base.direct.selected.moveWrtWidgetAll()
         # Continue
         # Continue
         return Task.cont
         return Task.cont
 
 
@@ -811,11 +813,11 @@ class DirectManipulationControl(DirectObject):
         widgetAxis.normalize()
         widgetAxis.normalize()
         if type == 'top?':
         if type == 'top?':
             # Check sign of angle between two vectors
             # Check sign of angle between two vectors
-            return (widgetDir.dot(widgetAxis) < 0.)
+            return widgetDir.dot(widgetAxis) < 0.
         elif type == 'edge?':
         elif type == 'edge?':
             # Checking to see if we are viewing edge-on
             # Checking to see if we are viewing edge-on
             # Check angle between two vectors
             # Check angle between two vectors
-            return(abs(widgetDir.dot(widgetAxis)) < .2)
+            return abs(widgetDir.dot(widgetAxis)) < .2
 
 
     ### FREE MANIPULATION METHODS ###
     ### FREE MANIPULATION METHODS ###
     def xlateCamXZ(self, state):
     def xlateCamXZ(self, state):
@@ -1043,7 +1045,7 @@ class DirectManipulationControl(DirectObject):
         entry = base.direct.iRay.pickGeom(
         entry = base.direct.iRay.pickGeom(
             skipFlags = SKIP_HIDDEN | SKIP_BACKFACE | SKIP_CAMERA)
             skipFlags = SKIP_HIDDEN | SKIP_BACKFACE | SKIP_CAMERA)
         # MRM: Need to handle moving COA
         # MRM: Need to handle moving COA
-        if (entry != None) and (base.direct.selected.last != None):
+        if entry is not None and base.direct.selected.last is not None:
             # Record undo point
             # Record undo point
             base.direct.pushUndo(base.direct.selected)
             base.direct.pushUndo(base.direct.selected)
             # Record wrt matrix
             # Record wrt matrix
@@ -1064,9 +1066,9 @@ class ObjectHandles(NodePath, DirectObject):
         NodePath.__init__(self)
         NodePath.__init__(self)
 
 
         # Load up object handles model and assign it to self
         # Load up object handles model and assign it to self
-        self.assign(loader.loadModel('models/misc/objectHandles'))
+        self.assign(base.loader.loadModel('models/misc/objectHandles'))
         self.setName(name)
         self.setName(name)
-        self.scalingNode = self.getChild(0)
+        self.scalingNode = NodePath(self)
         self.scalingNode.setName('ohScalingNode')
         self.scalingNode.setName('ohScalingNode')
         self.ohScalingFactor = 1.0
         self.ohScalingFactor = 1.0
         self.directScalingFactor = 1.0
         self.directScalingFactor = 1.0
@@ -1206,7 +1208,7 @@ class ObjectHandles(NodePath, DirectObject):
         self.reparentTo(hidden)
         self.reparentTo(hidden)
 
 
     def enableHandles(self, handles):
     def enableHandles(self, handles):
-        if type(handles) == list:
+        if isinstance(handles, list):
             for handle in handles:
             for handle in handles:
                 self.enableHandle(handle)
                 self.enableHandle(handle)
         elif handles == 'x':
         elif handles == 'x':
@@ -1255,7 +1257,7 @@ class ObjectHandles(NodePath, DirectObject):
             self.zScaleGroup.reparentTo(self.zHandles)
             self.zScaleGroup.reparentTo(self.zHandles)
 
 
     def disableHandles(self, handles):
     def disableHandles(self, handles):
-        if type(handles) == list:
+        if isinstance(handles, list):
             for handle in handles:
             for handle in handles:
                 self.disableHandle(handle)
                 self.disableHandle(handle)
         elif handles == 'x':
         elif handles == 'x':
@@ -1368,7 +1370,7 @@ class ObjectHandles(NodePath, DirectObject):
         self.setScalingFactor(1)
         self.setScalingFactor(1)
 
 
     def setScalingFactor(self, scaleFactor):
     def setScalingFactor(self, scaleFactor):
-        self.ohScalingFactor = self.ohScalingFactor * scaleFactor
+        self.ohScalingFactor = scaleFactor
         self.scalingNode.setScale(self.ohScalingFactor * self.directScalingFactor)
         self.scalingNode.setScale(self.ohScalingFactor * self.directScalingFactor)
 
 
     def getScalingFactor(self):
     def getScalingFactor(self):
@@ -1640,7 +1642,7 @@ class ObjectHandles(NodePath, DirectObject):
         # by comparing lineDir with plane normals.  The plane with the
         # by comparing lineDir with plane normals.  The plane with the
         # largest dotProduct is most "normal"
         # largest dotProduct is most "normal"
         if axis == 'x':
         if axis == 'x':
-            if (abs(lineDir.dot(Y_AXIS)) > abs(lineDir.dot(Z_AXIS))):
+            if abs(lineDir.dot(Y_AXIS)) > abs(lineDir.dot(Z_AXIS)):
                 self.hitPt.assign(
                 self.hitPt.assign(
                     planeIntersect(lineOrigin, lineDir, ORIGIN, Y_AXIS))
                     planeIntersect(lineOrigin, lineDir, ORIGIN, Y_AXIS))
             else:
             else:
@@ -1650,7 +1652,7 @@ class ObjectHandles(NodePath, DirectObject):
             self.hitPt.setY(0)
             self.hitPt.setY(0)
             self.hitPt.setZ(0)
             self.hitPt.setZ(0)
         elif axis == 'y':
         elif axis == 'y':
-            if (abs(lineDir.dot(X_AXIS)) > abs(lineDir.dot(Z_AXIS))):
+            if abs(lineDir.dot(X_AXIS)) > abs(lineDir.dot(Z_AXIS)):
                 self.hitPt.assign(
                 self.hitPt.assign(
                     planeIntersect(lineOrigin, lineDir, ORIGIN, X_AXIS))
                     planeIntersect(lineOrigin, lineDir, ORIGIN, X_AXIS))
             else:
             else:
@@ -1660,7 +1662,7 @@ class ObjectHandles(NodePath, DirectObject):
             self.hitPt.setX(0)
             self.hitPt.setX(0)
             self.hitPt.setZ(0)
             self.hitPt.setZ(0)
         elif axis == 'z':
         elif axis == 'z':
-            if (abs(lineDir.dot(X_AXIS)) > abs(lineDir.dot(Y_AXIS))):
+            if abs(lineDir.dot(X_AXIS)) > abs(lineDir.dot(Y_AXIS)):
                 self.hitPt.assign(
                 self.hitPt.assign(
                     planeIntersect(lineOrigin, lineDir, ORIGIN, X_AXIS))
                     planeIntersect(lineOrigin, lineDir, ORIGIN, X_AXIS))
             else:
             else:
@@ -1731,40 +1733,39 @@ class ObjectHandles(NodePath, DirectObject):
         return self.hitPt
         return self.hitPt
 
 
 def drawBox(lines, center, sideLength):
 def drawBox(lines, center, sideLength):
-
-        l = sideLength * 0.5
-        lines.moveTo(center[0] + l, center[1] + l, center[2] + l)
-        lines.drawTo(center[0] + l, center[1] + l, center[2] - l)
-        lines.drawTo(center[0] + l, center[1] - l, center[2] - l)
-        lines.drawTo(center[0] + l, center[1] - l, center[2] + l)
-        lines.drawTo(center[0] + l, center[1] + l, center[2] + l)
-
-        lines.moveTo(center[0] - l, center[1] + l, center[2] + l)
-        lines.drawTo(center[0] - l, center[1] + l, center[2] - l)
-        lines.drawTo(center[0] - l, center[1] - l, center[2] - l)
-        lines.drawTo(center[0] - l, center[1] - l, center[2] + l)
-        lines.drawTo(center[0] - l, center[1] + l, center[2] + l)
-
-        lines.moveTo(center[0] + l, center[1] + l, center[2] + l)
-        lines.drawTo(center[0] + l, center[1] + l, center[2] - l)
-        lines.drawTo(center[0] - l, center[1] + l, center[2] - l)
-        lines.drawTo(center[0] - l, center[1] + l, center[2] + l)
-        lines.drawTo(center[0] + l, center[1] + l, center[2] + l)
-
-        lines.moveTo(center[0] + l, center[1] - l, center[2] + l)
-        lines.drawTo(center[0] + l, center[1] - l, center[2] - l)
-        lines.drawTo(center[0] - l, center[1] - l, center[2] - l)
-        lines.drawTo(center[0] - l, center[1] - l, center[2] + l)
-        lines.drawTo(center[0] + l, center[1] - l, center[2] + l)
-
-        lines.moveTo(center[0] + l, center[1] + l, center[2] + l)
-        lines.drawTo(center[0] - l, center[1] + l, center[2] + l)
-        lines.drawTo(center[0] - l, center[1] - l, center[2] + l)
-        lines.drawTo(center[0] + l, center[1] - l, center[2] + l)
-        lines.drawTo(center[0] + l, center[1] + l, center[2] + l)
-
-        lines.moveTo(center[0] + l, center[1] + l, center[2] - l)
-        lines.drawTo(center[0] - l, center[1] + l, center[2] - l)
-        lines.drawTo(center[0] - l, center[1] - l, center[2] - l)
-        lines.drawTo(center[0] + l, center[1] - l, center[2] - l)
-        lines.drawTo(center[0] + l, center[1] + l, center[2] - l)
+    l = sideLength * 0.5
+    lines.moveTo(center[0] + l, center[1] + l, center[2] + l)
+    lines.drawTo(center[0] + l, center[1] + l, center[2] - l)
+    lines.drawTo(center[0] + l, center[1] - l, center[2] - l)
+    lines.drawTo(center[0] + l, center[1] - l, center[2] + l)
+    lines.drawTo(center[0] + l, center[1] + l, center[2] + l)
+
+    lines.moveTo(center[0] - l, center[1] + l, center[2] + l)
+    lines.drawTo(center[0] - l, center[1] + l, center[2] - l)
+    lines.drawTo(center[0] - l, center[1] - l, center[2] - l)
+    lines.drawTo(center[0] - l, center[1] - l, center[2] + l)
+    lines.drawTo(center[0] - l, center[1] + l, center[2] + l)
+
+    lines.moveTo(center[0] + l, center[1] + l, center[2] + l)
+    lines.drawTo(center[0] + l, center[1] + l, center[2] - l)
+    lines.drawTo(center[0] - l, center[1] + l, center[2] - l)
+    lines.drawTo(center[0] - l, center[1] + l, center[2] + l)
+    lines.drawTo(center[0] + l, center[1] + l, center[2] + l)
+
+    lines.moveTo(center[0] + l, center[1] - l, center[2] + l)
+    lines.drawTo(center[0] + l, center[1] - l, center[2] - l)
+    lines.drawTo(center[0] - l, center[1] - l, center[2] - l)
+    lines.drawTo(center[0] - l, center[1] - l, center[2] + l)
+    lines.drawTo(center[0] + l, center[1] - l, center[2] + l)
+
+    lines.moveTo(center[0] + l, center[1] + l, center[2] + l)
+    lines.drawTo(center[0] - l, center[1] + l, center[2] + l)
+    lines.drawTo(center[0] - l, center[1] - l, center[2] + l)
+    lines.drawTo(center[0] + l, center[1] - l, center[2] + l)
+    lines.drawTo(center[0] + l, center[1] + l, center[2] + l)
+
+    lines.moveTo(center[0] + l, center[1] + l, center[2] - l)
+    lines.drawTo(center[0] - l, center[1] + l, center[2] - l)
+    lines.drawTo(center[0] - l, center[1] - l, center[2] - l)
+    lines.drawTo(center[0] + l, center[1] - l, center[2] - l)
+    lines.drawTo(center[0] + l, center[1] + l, center[2] - l)

+ 12 - 12
direct/src/directtools/DirectSelection.py

@@ -1,4 +1,5 @@
 from direct.showbase.DirectObject import DirectObject
 from direct.showbase.DirectObject import DirectObject
+from direct.showbase.MessengerGlobal import messenger
 from .DirectGlobals import *
 from .DirectGlobals import *
 from .DirectUtil import *
 from .DirectUtil import *
 from .DirectGeometry import *
 from .DirectGeometry import *
@@ -95,7 +96,7 @@ class SelectedNodePaths(DirectObject):
             dnp = self.getDeselectedDict(id)
             dnp = self.getDeselectedDict(id)
             if dnp:
             if dnp:
                 # Remove it from the deselected dictionary
                 # Remove it from the deselected dictionary
-                del(self.deselectedDict[id])
+                del self.deselectedDict[id]
                 # Show its bounding box
                 # Show its bounding box
                 dnp.highlight()
                 dnp.highlight()
             else:
             else:
@@ -125,7 +126,7 @@ class SelectedNodePaths(DirectObject):
             # Hide its bounding box
             # Hide its bounding box
             dnp.dehighlight()
             dnp.dehighlight()
             # Remove it from the selected dictionary
             # Remove it from the selected dictionary
-            del(self.selectedDict[id])
+            del self.selectedDict[id]
             if dnp in self.selectedList: # [gjeon]
             if dnp in self.selectedList: # [gjeon]
                 self.selectedList.remove(dnp)
                 self.selectedList.remove(dnp)
             # And keep track of it in the deselected dictionary
             # And keep track of it in the deselected dictionary
@@ -308,7 +309,7 @@ class DirectBoundingBox:
         # Create a line segments object for the bbox
         # Create a line segments object for the bbox
         lines = LineNodePath(hidden)
         lines = LineNodePath(hidden)
         lines.node().setName('bboxLines')
         lines.node().setName('bboxLines')
-        if (bboxColor):
+        if bboxColor:
             lines.setColor(VBase4(*bboxColor))
             lines.setColor(VBase4(*bboxColor))
         else:
         else:
             lines.setColor(VBase4(1., 0., 0., 1.))
             lines.setColor(VBase4(1., 0., 0., 1.))
@@ -352,7 +353,7 @@ class DirectBoundingBox:
         return lines
         return lines
 
 
     def setBoxColorScale(self, r, g, b, a):
     def setBoxColorScale(self, r, g, b, a):
-        if (self.lines):
+        if self.lines:
             self.lines.reset()
             self.lines.reset()
             self.lines = None
             self.lines = None
         self.lines = self.createBBoxLines((r, g, b, a))
         self.lines = self.createBBoxLines((r, g, b, a))
@@ -529,7 +530,7 @@ class SelectionQueue(CollisionHandlerQueue):
             # Well, no way to tell.  Assume we're not backfacing.
             # Well, no way to tell.  Assume we're not backfacing.
             return 0
             return 0
 
 
-        if direct:
+        if base.direct:
             cam = base.direct.cam
             cam = base.direct.cam
         else:
         else:
             cam = base.cam
             cam = base.cam
@@ -564,13 +565,13 @@ class SelectionQueue(CollisionHandlerQueue):
             elif (skipFlags & SKIP_BACKFACE) and self.isEntryBackfacing(entry):
             elif (skipFlags & SKIP_BACKFACE) and self.isEntryBackfacing(entry):
                 # Skip, if backfacing poly
                 # Skip, if backfacing poly
                 pass
                 pass
-            elif ((skipFlags & SKIP_CAMERA) and
-                  (camera in nodePath.getAncestors())):
+            elif (skipFlags & SKIP_CAMERA) and \
+                 (base.camera in nodePath.getAncestors()):
                 # Skip if parented to a camera.
                 # Skip if parented to a camera.
                 pass
                 pass
             # Can pick unpickable, use the first visible node
             # Can pick unpickable, use the first visible node
-            elif ((skipFlags & SKIP_UNPICKABLE) and
-                  (nodePath.getName() in self.unpickable)):
+            elif (skipFlags & SKIP_UNPICKABLE) and\
+                 (nodePath.getName() in self.unpickable):
                 # Skip if in unpickable list
                 # Skip if in unpickable list
                 pass
                 pass
             elif base.direct and\
             elif base.direct and\
@@ -602,7 +603,7 @@ class SelectionRay(SelectionQueue):
         if xy:
         if xy:
             mx = xy[0]
             mx = xy[0]
             my = xy[1]
             my = xy[1]
-        elif direct:
+        elif base.direct:
             mx = base.direct.dr.mouseX
             mx = base.direct.dr.mouseX
             my = base.direct.dr.mouseY
             my = base.direct.dr.mouseY
         else:
         else:
@@ -613,7 +614,7 @@ class SelectionRay(SelectionQueue):
             mx = base.mouseWatcherNode.getMouseX()
             mx = base.mouseWatcherNode.getMouseX()
             my = base.mouseWatcherNode.getMouseY()
             my = base.mouseWatcherNode.getMouseY()
 
 
-        if direct:
+        if base.direct:
             self.collider.setFromLens(base.direct.camNode, mx, my)
             self.collider.setFromLens(base.direct.camNode, mx, my)
         else:
         else:
             self.collider.setFromLens(base.camNode, mx, my)
             self.collider.setFromLens(base.camNode, mx, my)
@@ -793,4 +794,3 @@ class SelectionSphere(SelectionQueue):
             targetNodePath = render
             targetNodePath = render
         self.collideWithBitMask(bitMask)
         self.collideWithBitMask(bitMask)
         return self.pick(targetNodePath, skipFlags)
         return self.pick(targetNodePath, skipFlags)
-

+ 15 - 16
direct/src/directtools/DirectSession.py

@@ -4,6 +4,7 @@ from panda3d.core import *
 from .DirectUtil import *
 from .DirectUtil import *
 
 
 from direct.showbase.DirectObject import DirectObject
 from direct.showbase.DirectObject import DirectObject
+from direct.showbase.BulletinBoardGlobal import bulletinBoard as bboard
 from direct.task import Task
 from direct.task import Task
 
 
 from .DirectGlobals import DIRECT_NO_MOD
 from .DirectGlobals import DIRECT_NO_MOD
@@ -96,13 +97,13 @@ class DirectSession(DirectObject):
         self.joybox = None
         self.joybox = None
         self.radamec = None
         self.radamec = None
         self.fastrak = []
         self.fastrak = []
-        if base.config.GetBool('want-vrpn', 0):
+        if ConfigVariableBool('want-vrpn', False):
             from direct.directdevices import DirectDeviceManager
             from direct.directdevices import DirectDeviceManager
             self.deviceManager = DirectDeviceManager.DirectDeviceManager()
             self.deviceManager = DirectDeviceManager.DirectDeviceManager()
             # Automatically create any devices specified in config file
             # Automatically create any devices specified in config file
-            joybox = base.config.GetString('vrpn-joybox-device', '')
-            radamec = base.config.GetString('vrpn-radamec-device', '')
-            fastrak = base.config.GetString('vrpn-fastrak-device', '')
+            joybox = ConfigVariableString('vrpn-joybox-device', '').value
+            radamec = ConfigVariableString('vrpn-radamec-device', '').value
+            fastrak = ConfigVariableString('vrpn-fastrak-device', '').value
             if joybox:
             if joybox:
                 from direct.directdevices import DirectJoybox
                 from direct.directdevices import DirectJoybox
                 self.joybox = DirectJoybox.DirectJoybox(joybox)
                 self.joybox = DirectJoybox.DirectJoybox(joybox)
@@ -291,16 +292,15 @@ class DirectSession(DirectObject):
         self.passThroughKeys = ['v','b','l','p', 'r', 'shift-r', 's', 't','shift-a', 'w']
         self.passThroughKeys = ['v','b','l','p', 'r', 'shift-r', 's', 't','shift-a', 'w']
 
 
         if base.wantTk:
         if base.wantTk:
-            from direct.showbase import TkGlobal
             from direct.tkpanels import DirectSessionPanel
             from direct.tkpanels import DirectSessionPanel
-            self.panel = DirectSessionPanel.DirectSessionPanel(parent = tkroot)
+            self.panel = DirectSessionPanel.DirectSessionPanel(parent = base.tkRoot)
         try:
         try:
             # Has the clusterMode been set externally (i.e. via the
             # Has the clusterMode been set externally (i.e. via the
             # bootstrap application?
             # bootstrap application?
             self.clusterMode = clusterMode
             self.clusterMode = clusterMode
         except NameError:
         except NameError:
             # Has the clusterMode been set via a config variable?
             # Has the clusterMode been set via a config variable?
-            self.clusterMode = base.config.GetString("cluster-mode", '')
+            self.clusterMode = ConfigVariableString("cluster-mode", '').value
 
 
         if self.clusterMode == 'client':
         if self.clusterMode == 'client':
             self.cluster = createClusterClient()
             self.cluster = createClusterClient()
@@ -385,14 +385,12 @@ class DirectSession(DirectObject):
     def oobe(self):
     def oobe(self):
         # If oobeMode was never set, set it to false and create the
         # If oobeMode was never set, set it to false and create the
         # structures we need to implement OOBE.
         # structures we need to implement OOBE.
-        try:
-            self.oobeMode
-        except:
+        if not hasattr(self, 'oobeMode'):
             self.oobeMode = 0
             self.oobeMode = 0
 
 
             self.oobeCamera = hidden.attachNewNode('oobeCamera')
             self.oobeCamera = hidden.attachNewNode('oobeCamera')
 
 
-            self.oobeVis = loader.loadModel('models/misc/camera')
+            self.oobeVis = base.loader.loadModel('models/misc/camera')
             if self.oobeVis:
             if self.oobeVis:
                 self.oobeVis.node().setFinal(1)
                 self.oobeVis.node().setFinal(1)
 
 
@@ -669,6 +667,8 @@ class DirectSession(DirectObject):
         if not taskMgr.hasTaskNamed('resizeObjectHandles'):
         if not taskMgr.hasTaskNamed('resizeObjectHandles'):
             dnp = self.selected.last
             dnp = self.selected.last
             if dnp:
             if dnp:
+                direct = base.direct
+
                 if self.manipulationControl.fMultiView:
                 if self.manipulationControl.fMultiView:
                     for i in range(3):
                     for i in range(3):
                         sf = 30.0 * direct.drList[i].orthoFactor
                         sf = 30.0 * direct.drList[i].orthoFactor
@@ -708,7 +708,7 @@ class DirectSession(DirectObject):
             else:
             else:
                 self.widget.showWidget()
                 self.widget.showWidget()
             editTypes = self.manipulationControl.getEditTypes([dnp])
             editTypes = self.manipulationControl.getEditTypes([dnp])
-            if (editTypes & EDIT_TYPE_UNEDITABLE == EDIT_TYPE_UNEDITABLE):
+            if (editTypes & EDIT_TYPE_UNEDITABLE) == EDIT_TYPE_UNEDITABLE:
                 self.manipulationControl.disableWidgetMove()
                 self.manipulationControl.disableWidgetMove()
             else:
             else:
                 self.manipulationControl.enableWidgetMove()
                 self.manipulationControl.enableWidgetMove()
@@ -1069,8 +1069,10 @@ class DirectSession(DirectObject):
         for iRay in self.iRayList:
         for iRay in self.iRayList:
             iRay.removeUnpickable(item)
             iRay.removeUnpickable(item)
 
 
+
 class DisplayRegionContext(DirectObject):
 class DisplayRegionContext(DirectObject):
     regionCount = 0
     regionCount = 0
+
     def __init__(self, cam):
     def __init__(self, cam):
         self.cam = cam
         self.cam = cam
         self.camNode = self.cam.node()
         self.camNode = self.cam.node()
@@ -1091,10 +1093,7 @@ class DisplayRegionContext(DirectObject):
         # one display region per camera, since we are defining a
         # one display region per camera, since we are defining a
         # display region on a per-camera basis.  See note in
         # display region on a per-camera basis.  See note in
         # DisplayRegionList.__init__()
         # DisplayRegionList.__init__()
-        try:
-            self.dr = self.camNode.getDr(0)
-        except:
-            self.dr = self.camNode.getDisplayRegion(0)
+        self.dr = self.camNode.getDisplayRegion(0)
         left = self.dr.getLeft()
         left = self.dr.getLeft()
         right = self.dr.getRight()
         right = self.dr.getRight()
         bottom = self.dr.getBottom()
         bottom = self.dr.getBottom()

+ 2 - 2
direct/src/directtools/DirectUtil.py

@@ -2,6 +2,7 @@
 from .DirectGlobals import *
 from .DirectGlobals import *
 from panda3d.core import VBase4
 from panda3d.core import VBase4
 from direct.task.Task import Task
 from direct.task.Task import Task
+from direct.task.TaskManagerGlobal import taskMgr
 
 
 # Routines to adjust values
 # Routines to adjust values
 def ROUND_TO(value, divisor):
 def ROUND_TO(value, divisor):
@@ -19,7 +20,7 @@ def getTkColorString(color):
     Print out a Tk compatible version of a color string
     Print out a Tk compatible version of a color string
     """
     """
     def toHex(intVal):
     def toHex(intVal):
-        val = int(round(intVal))
+        val = int(intVal)
         if val < 16:
         if val < 16:
             return "0" + hex(val)[2:]
             return "0" + hex(val)[2:]
         else:
         else:
@@ -84,4 +85,3 @@ def getFileData(filename, separator = ','):
             data = [s.strip() for s in l.split(separator)]
             data = [s.strip() for s in l.split(separator)]
             fileData.append(data)
             fileData.append(data)
     return fileData
     return fileData
-

+ 3 - 1
direct/src/directutil/DistributedLargeBlobSender.py

@@ -2,8 +2,10 @@
 
 
 from direct.distributed import DistributedObject
 from direct.distributed import DistributedObject
 from direct.directnotify import DirectNotifyGlobal
 from direct.directnotify import DirectNotifyGlobal
+from direct.showbase.MessengerGlobal import messenger
 from . import LargeBlobSenderConsts
 from . import LargeBlobSenderConsts
 
 
+
 class DistributedLargeBlobSender(DistributedObject.DistributedObject):
 class DistributedLargeBlobSender(DistributedObject.DistributedObject):
     """DistributedLargeBlobSender: for sending large chunks of data through
     """DistributedLargeBlobSender: for sending large chunks of data through
     the DC system"""
     the DC system"""
@@ -56,7 +58,7 @@ class DistributedLargeBlobSender(DistributedObject.DistributedObject):
         except OSError:
         except OSError:
             DistributedLargeBlobSender.notify.error(
             DistributedLargeBlobSender.notify.error(
                 'could not access %s' % bPath)
                 'could not access %s' % bPath)
-        f = file(filename, 'rb')
+        f = open(filename, 'rb')
         self.blob = f.read()
         self.blob = f.read()
         f.close()
         f.close()
         os.unlink(filename)
         os.unlink(filename)

+ 2 - 2
direct/src/directutil/DistributedLargeBlobSenderAI.py

@@ -42,7 +42,7 @@ class DistributedLargeBlobSenderAI(DistributedObjectAI.DistributedObjectAI):
                     break
                     break
             # NOTE: there's a small chance of a race condition here, if
             # NOTE: there's a small chance of a race condition here, if
             # the file is created by another AI just after the stat fails
             # the file is created by another AI just after the stat fails
-            f = file(filename, 'wb')
+            f = open(filename, 'wb')
             f.write(s)
             f.write(s)
             f.close()
             f.close()
             os.chdir(origDir)
             os.chdir(origDir)
@@ -50,7 +50,7 @@ class DistributedLargeBlobSenderAI(DistributedObjectAI.DistributedObjectAI):
                                       'setFilename', [filename])
                                       'setFilename', [filename])
         else:
         else:
             chunkSize = LargeBlobSenderConsts.ChunkSize
             chunkSize = LargeBlobSenderConsts.ChunkSize
-            while len(s):
+            while len(s) > 0:
                 self.sendUpdateToAvatarId(self.targetAvId,
                 self.sendUpdateToAvatarId(self.targetAvId,
                                           'setChunk', [s[:chunkSize]])
                                           'setChunk', [s[:chunkSize]])
                 s = s[chunkSize:]
                 s = s[chunkSize:]

+ 23 - 24
direct/src/directutil/Mopath.py

@@ -1,4 +1,5 @@
 from direct.showbase.DirectObject import DirectObject
 from direct.showbase.DirectObject import DirectObject
+from direct.showbase.MessengerGlobal import messenger
 from direct.directtools.DirectGeometry import *
 from direct.directtools.DirectGeometry import *
 
 
 from panda3d.core import NodePath, LineSegs
 from panda3d.core import NodePath, LineSegs
@@ -8,7 +9,7 @@ class Mopath(DirectObject):
     nameIndex = 1
     nameIndex = 1
 
 
     def __init__(self, name = None, fluid = 1, objectToLoad = None, upVectorNodePath = None, reverseUpVector = False):
     def __init__(self, name = None, fluid = 1, objectToLoad = None, upVectorNodePath = None, reverseUpVector = False):
-        if (name == None):
+        if name is None:
             name = 'mopath%d' % self.nameIndex
             name = 'mopath%d' % self.nameIndex
             self.nameIndex = self.nameIndex + 1
             self.nameIndex = self.nameIndex + 1
         self.name = name
         self.name = name
@@ -35,24 +36,23 @@ class Mopath(DirectObject):
         return self.maxT * self.timeScale
         return self.maxT * self.timeScale
 
 
     def loadFile(self, filename, fReset = 1):
     def loadFile(self, filename, fReset = 1):
-        nodePath = loader.loadModel(filename)
+        nodePath = base.loader.loadModel(filename)
         if nodePath:
         if nodePath:
             self.loadNodePath(nodePath)
             self.loadNodePath(nodePath)
             nodePath.removeNode()
             nodePath.removeNode()
         else:
         else:
             print('Mopath: no data in file: %s' % filename)
             print('Mopath: no data in file: %s' % filename)
 
 
-
     def loadNodePath(self, nodePath, fReset = 1):
     def loadNodePath(self, nodePath, fReset = 1):
         if fReset:
         if fReset:
             self.reset()
             self.reset()
 
 
         self.__extractCurves(nodePath)
         self.__extractCurves(nodePath)
-        if (self.tNurbsCurve != []):
+        if self.tNurbsCurve != []:
             self.maxT = self.tNurbsCurve[-1].getMaxT()
             self.maxT = self.tNurbsCurve[-1].getMaxT()
-        elif (self.xyzNurbsCurve != None):
+        elif self.xyzNurbsCurve is not None:
             self.maxT = self.xyzNurbsCurve.getMaxT()
             self.maxT = self.xyzNurbsCurve.getMaxT()
-        elif (self.hprNurbsCurve != None):
+        elif self.hprNurbsCurve is not None:
             self.maxT = self.hprNurbsCurve.getMaxT()
             self.maxT = self.hprNurbsCurve.getMaxT()
         else:
         else:
             print('Mopath: no valid curves in nodePath: %s' % nodePath)
             print('Mopath: no valid curves in nodePath: %s' % nodePath)
@@ -74,11 +74,11 @@ class Mopath(DirectObject):
             elif node.getCurveType() == PCTHPR:
             elif node.getCurveType() == PCTHPR:
                 self.hprNurbsCurve = node
                 self.hprNurbsCurve = node
             elif node.getCurveType() == PCTNONE:
             elif node.getCurveType() == PCTNONE:
-                if (self.xyzNurbsCurve == None):
+                if self.xyzNurbsCurve is None:
                     self.xyzNurbsCurve = node
                     self.xyzNurbsCurve = node
                 else:
                 else:
                     print('Mopath: got a PCT_NONE curve and an XYZ Curve in nodePath: %s' % nodePath)
                     print('Mopath: got a PCT_NONE curve and an XYZ Curve in nodePath: %s' % nodePath)
-            elif (node.getCurveType() == PCTT):
+            elif node.getCurveType() == PCTT:
                 self.tNurbsCurve.append(node)
                 self.tNurbsCurve.append(node)
         else:
         else:
             # Iterate over children if any
             # Iterate over children if any
@@ -97,29 +97,29 @@ class Mopath(DirectObject):
 
 
     def getFinalState(self):
     def getFinalState(self):
         pos = Point3(0)
         pos = Point3(0)
-        if (self.xyzNurbsCurve != None):
+        if self.xyzNurbsCurve is not None:
             self.xyzNurbsCurve.getPoint(self.maxT, pos)
             self.xyzNurbsCurve.getPoint(self.maxT, pos)
         hpr = Point3(0)
         hpr = Point3(0)
-        if (self.hprNurbsCurve != None):
+        if self.hprNurbsCurve is not None:
             self.hprNurbsCurve.getPoint(self.maxT, hpr)
             self.hprNurbsCurve.getPoint(self.maxT, hpr)
         return (pos, hpr)
         return (pos, hpr)
 
 
     def goTo(self, node, time):
     def goTo(self, node, time):
-        if (self.xyzNurbsCurve == None) and (self.hprNurbsCurve == None):
+        if self.xyzNurbsCurve is None and self.hprNurbsCurve is None:
             print('Mopath: Mopath has no curves')
             print('Mopath: Mopath has no curves')
             return
             return
         time /= self.timeScale
         time /= self.timeScale
         self.playbackTime = self.calcTime(CLAMP(time, 0.0, self.maxT))
         self.playbackTime = self.calcTime(CLAMP(time, 0.0, self.maxT))
-        if (self.xyzNurbsCurve != None):
+        if self.xyzNurbsCurve is not None:
             self.xyzNurbsCurve.getPoint(self.playbackTime, self.posPoint)
             self.xyzNurbsCurve.getPoint(self.playbackTime, self.posPoint)
             if self.fluid:
             if self.fluid:
                 node.setFluidPos(self.posPoint)
                 node.setFluidPos(self.posPoint)
             else:
             else:
                 node.setPos(self.posPoint)
                 node.setPos(self.posPoint)
-        if (self.hprNurbsCurve != None):
+        if self.hprNurbsCurve is not None:
             self.hprNurbsCurve.getPoint(self.playbackTime, self.hprPoint)
             self.hprNurbsCurve.getPoint(self.playbackTime, self.hprPoint)
             node.setHpr(self.hprPoint)
             node.setHpr(self.hprPoint)
-        elif (self.fFaceForward and (self.xyzNurbsCurve != None)):
+        elif self.fFaceForward and self.xyzNurbsCurve is not None:
             if self.faceForwardDelta:
             if self.faceForwardDelta:
                 # Look at a point a bit ahead in parametric time.
                 # Look at a point a bit ahead in parametric time.
                 t = min(self.playbackTime + self.faceForwardDelta, self.xyzNurbsCurve.getMaxT())
                 t = min(self.playbackTime + self.faceForwardDelta, self.xyzNurbsCurve.getMaxT())
@@ -133,18 +133,18 @@ class Mopath(DirectObject):
 
 
             # use the self.upVectorNodePath position if it exists to
             # use the self.upVectorNodePath position if it exists to
             # create an up vector for lookAt
             # create an up vector for lookAt
-            if (self.upVectorNodePath is None):
+            if self.upVectorNodePath is None:
                 node.lookAt(lookPoint)
                 node.lookAt(lookPoint)
             else:
             else:
-                if (self.reverseUpVector == False):
-                     node.lookAt(lookPoint,
-                                 self.upVectorNodePath.getPos() - self.posPoint)
+                if not self.reverseUpVector:
+                    node.lookAt(lookPoint,
+                                self.upVectorNodePath.getPos() - self.posPoint)
                 else:
                 else:
-                     node.lookAt(lookPoint,
-                                 self.posPoint - self.upVectorNodePath.getPos())
+                    node.lookAt(lookPoint,
+                                self.posPoint - self.upVectorNodePath.getPos())
 
 
     def play(self, node, time = 0.0, loop = 0):
     def play(self, node, time = 0.0, loop = 0):
-        if (self.xyzNurbsCurve == None) and (self.hprNurbsCurve == None):
+        if self.xyzNurbsCurve is None and self.hprNurbsCurve is None:
             print('Mopath: Mopath has no curves')
             print('Mopath: Mopath has no curves')
             return
             return
         self.node = node
         self.node = node
@@ -161,11 +161,11 @@ class Mopath(DirectObject):
         time = globalClock.getFrameTime()
         time = globalClock.getFrameTime()
         dTime = time - task.lastTime
         dTime = time - task.lastTime
         task.lastTime = time
         task.lastTime = time
-        if (self.loop):
+        if self.loop:
             cTime = (task.currentTime + dTime) % self.getMaxT()
             cTime = (task.currentTime + dTime) % self.getMaxT()
         else:
         else:
             cTime = task.currentTime + dTime
             cTime = task.currentTime + dTime
-        if ((self.loop == 0) and (cTime > self.getMaxT())):
+        if self.loop == 0 and cTime > self.getMaxT():
             self.stop()
             self.stop()
             messenger.send(self.name + '-done')
             messenger.send(self.name + '-done')
             self.node = None
             self.node = None
@@ -187,4 +187,3 @@ class Mopath(DirectObject):
             ls.drawTo(p)
             ls.drawTo(p)
 
 
         return NodePath(ls.create())
         return NodePath(ls.create())
-

+ 138 - 24
direct/src/dist/FreezeTool.py

@@ -12,6 +12,7 @@ import io
 import distutils.sysconfig as sysconf
 import distutils.sysconfig as sysconf
 import zipfile
 import zipfile
 import importlib
 import importlib
+import warnings
 
 
 from . import pefile
 from . import pefile
 
 
@@ -61,7 +62,7 @@ except ImportError:
     def pytest_imports():
     def pytest_imports():
         return []
         return []
 
 
-hiddenImports = {
+defaultHiddenImports = {
     'pytest': pytest_imports(),
     'pytest': pytest_imports(),
     'pkg_resources': [
     'pkg_resources': [
         'pkg_resources.*.*',
         'pkg_resources.*.*',
@@ -77,8 +78,29 @@ hiddenImports = {
         'numpy.core._dtype_ctypes',
         'numpy.core._dtype_ctypes',
         'numpy.core._methods',
         'numpy.core._methods',
     ],
     ],
+    'pandas.compat': ['lzma', 'cmath'],
+    'pandas._libs.tslibs.conversion': ['pandas._libs.tslibs.base'],
 }
 }
 
 
+
+# These are modules that import other modules but shouldn't pick them up as
+# dependencies (usually because they are optional).  This prevents picking up
+# unwanted dependencies.
+ignoreImports = {
+    'direct.showbase.PythonUtil': ['pstats', 'profile'],
+
+    'toml.encoder': ['numpy'],
+}
+
+if sys.version_info >= (3, 8):
+    # importlib.metadata is a "provisional" module introduced in Python 3.8 that
+    # conditionally pulls in dependency-rich packages like "email" and "pep517"
+    # (the latter of which is a thirdparty package!)  But it's only imported in
+    # one obscure corner, so we don't want to pull it in by default.
+    ignoreImports['importlib._bootstrap_external'] = ['importlib.metadata']
+    ignoreImports['importlib.metadata'] = ['pep517']
+
+
 # These are overrides for specific modules.
 # These are overrides for specific modules.
 overrideModules = {
 overrideModules = {
     # Used by the warnings module, among others, to get line numbers.  Since
     # Used by the warnings module, among others, to get line numbers.  Since
@@ -163,23 +185,23 @@ class CompilationEnvironment:
         if self.platform.startswith('win'):
         if self.platform.startswith('win'):
             self.Python = sysconf.PREFIX
             self.Python = sysconf.PREFIX
 
 
-            if ('VCINSTALLDIR' in os.environ):
+            if 'VCINSTALLDIR' in os.environ:
                 self.MSVC = os.environ['VCINSTALLDIR']
                 self.MSVC = os.environ['VCINSTALLDIR']
-            elif (Filename('/c/Program Files/Microsoft Visual Studio 9.0/VC').exists()):
+            elif Filename('/c/Program Files/Microsoft Visual Studio 9.0/VC').exists():
                 self.MSVC = Filename('/c/Program Files/Microsoft Visual Studio 9.0/VC').toOsSpecific()
                 self.MSVC = Filename('/c/Program Files/Microsoft Visual Studio 9.0/VC').toOsSpecific()
-            elif (Filename('/c/Program Files (x86)/Microsoft Visual Studio 9.0/VC').exists()):
+            elif Filename('/c/Program Files (x86)/Microsoft Visual Studio 9.0/VC').exists():
                 self.MSVC = Filename('/c/Program Files (x86)/Microsoft Visual Studio 9.0/VC').toOsSpecific()
                 self.MSVC = Filename('/c/Program Files (x86)/Microsoft Visual Studio 9.0/VC').toOsSpecific()
-            elif (Filename('/c/Program Files/Microsoft Visual Studio .NET 2003/Vc7').exists()):
+            elif Filename('/c/Program Files/Microsoft Visual Studio .NET 2003/Vc7').exists():
                 self.MSVC = Filename('/c/Program Files/Microsoft Visual Studio .NET 2003/Vc7').toOsSpecific()
                 self.MSVC = Filename('/c/Program Files/Microsoft Visual Studio .NET 2003/Vc7').toOsSpecific()
             else:
             else:
                 print('Could not locate Microsoft Visual C++ Compiler! Try running from the Visual Studio Command Prompt.')
                 print('Could not locate Microsoft Visual C++ Compiler! Try running from the Visual Studio Command Prompt.')
                 sys.exit(1)
                 sys.exit(1)
 
 
-            if ('WindowsSdkDir' in os.environ):
+            if 'WindowsSdkDir' in os.environ:
                 self.PSDK = os.environ['WindowsSdkDir']
                 self.PSDK = os.environ['WindowsSdkDir']
-            elif (platform.architecture()[0] == '32bit' and Filename('/c/Program Files/Microsoft Platform SDK for Windows Server 2003 R2').exists()):
+            elif platform.architecture()[0] == '32bit' and Filename('/c/Program Files/Microsoft Platform SDK for Windows Server 2003 R2').exists():
                 self.PSDK = Filename('/c/Program Files/Microsoft Platform SDK for Windows Server 2003 R2').toOsSpecific()
                 self.PSDK = Filename('/c/Program Files/Microsoft Platform SDK for Windows Server 2003 R2').toOsSpecific()
-            elif (os.path.exists(os.path.join(self.MSVC, 'PlatformSDK'))):
+            elif os.path.exists(os.path.join(self.MSVC, 'PlatformSDK')):
                 self.PSDK = os.path.join(self.MSVC, 'PlatformSDK')
                 self.PSDK = os.path.join(self.MSVC, 'PlatformSDK')
             else:
             else:
                 print('Could not locate the Microsoft Windows Platform SDK! Try running from the Visual Studio Command Prompt.')
                 print('Could not locate the Microsoft Windows Platform SDK! Try running from the Visual Studio Command Prompt.')
@@ -197,7 +219,7 @@ class CompilationEnvironment:
                 self.suffix64 = '\\amd64'
                 self.suffix64 = '\\amd64'
 
 
             # If it is run by makepanda, it handles the MSVC and PlatformSDK paths itself.
             # If it is run by makepanda, it handles the MSVC and PlatformSDK paths itself.
-            if ('MAKEPANDA' in os.environ):
+            if 'MAKEPANDA' in os.environ:
                 self.compileObjExe = 'cl /wd4996 /Fo%(basename)s.obj /nologo /c %(MD)s /Zi /O2 /Ob2 /EHsc /Zm300 /W3 /I"%(pythonIPath)s" %(filename)s'
                 self.compileObjExe = 'cl /wd4996 /Fo%(basename)s.obj /nologo /c %(MD)s /Zi /O2 /Ob2 /EHsc /Zm300 /W3 /I"%(pythonIPath)s" %(filename)s'
                 self.compileObjDll = self.compileObjExe
                 self.compileObjDll = self.compileObjExe
                 self.linkExe = 'link /nologo /MAP:NUL /FIXED:NO /OPT:REF /STACK:4194304 /INCREMENTAL:NO /LIBPATH:"%(python)s\\libs"  /out:%(basename)s.exe %(basename)s.obj'
                 self.linkExe = 'link /nologo /MAP:NUL /FIXED:NO /OPT:REF /STACK:4194304 /INCREMENTAL:NO /LIBPATH:"%(python)s\\libs"  /out:%(basename)s.exe %(basename)s.obj'
@@ -211,7 +233,7 @@ class CompilationEnvironment:
                 self.linkDll = 'link /nologo /DLL /MAP:NUL /FIXED:NO /OPT:REF /INCREMENTAL:NO /LIBPATH:"%(PSDK)s\\lib" /LIBPATH:"%(MSVC)s\\lib%(suffix64)s" /LIBPATH:"%(python)s\\libs"  /out:%(basename)s%(dllext)s.pyd %(basename)s.obj'
                 self.linkDll = 'link /nologo /DLL /MAP:NUL /FIXED:NO /OPT:REF /INCREMENTAL:NO /LIBPATH:"%(PSDK)s\\lib" /LIBPATH:"%(MSVC)s\\lib%(suffix64)s" /LIBPATH:"%(python)s\\libs"  /out:%(basename)s%(dllext)s.pyd %(basename)s.obj'
 
 
         elif self.platform.startswith('osx_'):
         elif self.platform.startswith('osx_'):
-            # OSX
+            # macOS
             proc = self.platform.split('_', 1)[1]
             proc = self.platform.split('_', 1)[1]
             if proc == 'i386':
             if proc == 'i386':
                 self.arch = '-arch i386'
                 self.arch = '-arch i386'
@@ -219,6 +241,8 @@ class CompilationEnvironment:
                 self.arch = '-arch ppc'
                 self.arch = '-arch ppc'
             elif proc == 'amd64':
             elif proc == 'amd64':
                 self.arch = '-arch x86_64'
                 self.arch = '-arch x86_64'
+            elif proc in ('arm64', 'aarch64'):
+                self.arch = '-arch arm64'
             self.compileObjExe = "gcc -c %(arch)s -o %(basename)s.o -O2 -I%(pythonIPath)s %(filename)s"
             self.compileObjExe = "gcc -c %(arch)s -o %(basename)s.o -O2 -I%(pythonIPath)s %(filename)s"
             self.compileObjDll = "gcc -fPIC -c %(arch)s -o %(basename)s.o -O2 -I%(pythonIPath)s %(filename)s"
             self.compileObjDll = "gcc -fPIC -c %(arch)s -o %(basename)s.o -O2 -I%(pythonIPath)s %(filename)s"
             self.linkExe = "gcc %(arch)s -o %(basename)s %(basename)s.o -framework Python"
             self.linkExe = "gcc %(arch)s -o %(basename)s %(basename)s.o -framework Python"
@@ -233,7 +257,7 @@ class CompilationEnvironment:
             self.linkExe = "%(CC)s -o %(basename)s %(basename)s.o -L/usr/local/lib -lpython%(pythonVersion)s"
             self.linkExe = "%(CC)s -o %(basename)s %(basename)s.o -L/usr/local/lib -lpython%(pythonVersion)s"
             self.linkDll = "%(LDSHARED)s -o %(basename)s.so %(basename)s.o -L/usr/local/lib -lpython%(pythonVersion)s"
             self.linkDll = "%(LDSHARED)s -o %(basename)s.so %(basename)s.o -L/usr/local/lib -lpython%(pythonVersion)s"
 
 
-            if (os.path.isdir("/usr/PCBSD/local/lib")):
+            if os.path.isdir("/usr/PCBSD/local/lib"):
                 self.linkExe += " -L/usr/PCBSD/local/lib"
                 self.linkExe += " -L/usr/PCBSD/local/lib"
                 self.linkDll += " -L/usr/PCBSD/local/lib"
                 self.linkDll += " -L/usr/PCBSD/local/lib"
 
 
@@ -636,7 +660,9 @@ okMissing = [
     'EasyDialogs', 'SOCKS', 'ic', 'rourl2path', 'termios', 'vms_lib',
     'EasyDialogs', 'SOCKS', 'ic', 'rourl2path', 'termios', 'vms_lib',
     'OverrideFrom23._Res', 'email', 'email.Utils', 'email.Generator',
     'OverrideFrom23._Res', 'email', 'email.Utils', 'email.Generator',
     'email.Iterators', '_subprocess', 'gestalt', 'java.lang',
     'email.Iterators', '_subprocess', 'gestalt', 'java.lang',
-    'direct.extensions_native.extensions_darwin',
+    'direct.extensions_native.extensions_darwin', '_manylinux',
+    'collections.Iterable', 'collections.Mapping', 'collections.MutableMapping',
+    'collections.Sequence', 'numpy_distutils',
     ]
     ]
 
 
 # Since around macOS 10.15, Apple's codesigning process has become more strict.
 # Since around macOS 10.15, Apple's codesigning process has become more strict.
@@ -751,7 +777,7 @@ class Freezer:
             return 'ModuleDef(%s)' % (', '.join(args))
             return 'ModuleDef(%s)' % (', '.join(args))
 
 
     def __init__(self, previous = None, debugLevel = 0,
     def __init__(self, previous = None, debugLevel = 0,
-                 platform = None, path=None):
+                 platform = None, path=None, hiddenImports=None):
         # Normally, we are freezing for our own platform.  Change this
         # Normally, we are freezing for our own platform.  Change this
         # if untrue.
         # if untrue.
         self.platform = platform or PandaSystem.getPlatform()
         self.platform = platform or PandaSystem.getPlatform()
@@ -825,6 +851,11 @@ class Freezer:
                 if path:
                 if path:
                     modulefinder.AddPackagePath(moduleName, path[0])
                     modulefinder.AddPackagePath(moduleName, path[0])
 
 
+        # Module with non-obvious dependencies
+        self.hiddenImports = defaultHiddenImports.copy()
+        if hiddenImports is not None:
+            self.hiddenImports.update(hiddenImports)
+
         # Suffix/extension for Python C extension modules
         # Suffix/extension for Python C extension modules
         if self.platform == PandaSystem.getPlatform():
         if self.platform == PandaSystem.getPlatform():
             self.moduleSuffixes = imp.get_suffixes()
             self.moduleSuffixes = imp.get_suffixes()
@@ -986,7 +1017,7 @@ class Freezer:
 
 
         # Scan the directory, looking for .py files.
         # Scan the directory, looking for .py files.
         modules = []
         modules = []
-        for basename in os.listdir(pathname):
+        for basename in sorted(os.listdir(pathname)):
             if basename.endswith('.py') and basename != '__init__.py':
             if basename.endswith('.py') and basename != '__init__.py':
                 modules.append(basename[:-3])
                 modules.append(basename[:-3])
 
 
@@ -998,8 +1029,8 @@ class Freezer:
         if not newName:
         if not newName:
             newName = moduleName
             newName = moduleName
 
 
-        assert(moduleName.endswith('.*'))
-        assert(newName.endswith('.*'))
+        assert moduleName.endswith('.*')
+        assert newName.endswith('.*')
 
 
         mdefs = {}
         mdefs = {}
 
 
@@ -1009,7 +1040,7 @@ class Freezer:
         parentNames = [(parentName, newParentName)]
         parentNames = [(parentName, newParentName)]
 
 
         if parentName.endswith('.*'):
         if parentName.endswith('.*'):
-            assert(newParentName.endswith('.*'))
+            assert newParentName.endswith('.*')
             # Another special case.  The parent name "*" means to
             # Another special case.  The parent name "*" means to
             # return all possible directories within a particular
             # return all possible directories within a particular
             # directory.
             # directory.
@@ -1020,7 +1051,7 @@ class Freezer:
             modulePath = self.getModulePath(topName)
             modulePath = self.getModulePath(topName)
             if modulePath:
             if modulePath:
                 for dirname in modulePath:
                 for dirname in modulePath:
-                    for basename in os.listdir(dirname):
+                    for basename in sorted(os.listdir(dirname)):
                         if os.path.exists(os.path.join(dirname, basename, '__init__.py')):
                         if os.path.exists(os.path.join(dirname, basename, '__init__.py')):
                             parentName = '%s.%s' % (topName, basename)
                             parentName = '%s.%s' % (topName, basename)
                             newParentName = '%s.%s' % (newTopName, basename)
                             newParentName = '%s.%s' % (newTopName, basename)
@@ -1166,7 +1197,7 @@ class Freezer:
 
 
         # Check if any new modules we found have "hidden" imports
         # Check if any new modules we found have "hidden" imports
         for origName in list(self.mf.modules.keys()):
         for origName in list(self.mf.modules.keys()):
-            hidden = hiddenImports.get(origName, [])
+            hidden = self.hiddenImports.get(origName, [])
             for modname in hidden:
             for modname in hidden:
                 if modname.endswith('.*'):
                 if modname.endswith('.*'):
                     mdefs = self._gatherSubmodules(modname, implicit = True)
                     mdefs = self._gatherSubmodules(modname, implicit = True)
@@ -1342,7 +1373,7 @@ class Freezer:
         for moduleName, module in list(self.mf.modules.items()):
         for moduleName, module in list(self.mf.modules.items()):
             if module.__code__:
             if module.__code__:
                 co = self.mf.replace_paths_in_code(module.__code__)
                 co = self.mf.replace_paths_in_code(module.__code__)
-                module.__code__ = co;
+                module.__code__ = co
 
 
     def __addPyc(self, multifile, filename, code, compressionLevel):
     def __addPyc(self, multifile, filename, code, compressionLevel):
         if code:
         if code:
@@ -1721,7 +1752,7 @@ class Freezer:
         return target
         return target
 
 
     def generateRuntimeFromStub(self, target, stub_file, use_console, fields={},
     def generateRuntimeFromStub(self, target, stub_file, use_console, fields={},
-                                log_append=False):
+                                log_append=False, log_filename_strftime=False):
         self.__replacePaths()
         self.__replacePaths()
 
 
         # We must have a __main__ module to make an exe file.
         # We must have a __main__ module to make an exe file.
@@ -1904,9 +1935,12 @@ class Freezer:
             # A null entry marks the end of the module table.
             # A null entry marks the end of the module table.
             blob += struct.pack(entry_layout, 0, 0, 0)
             blob += struct.pack(entry_layout, 0, 0, 0)
 
 
+            # These flags should match the enum in deploy-stub.c
             flags = 0
             flags = 0
             if log_append:
             if log_append:
                 flags |= 1
                 flags |= 1
+            if log_filename_strftime:
+                flags |= 2
 
 
             # Compose the header we will be writing to the stub, to tell it
             # Compose the header we will be writing to the stub, to tell it
             # where to find the module data blob, as well as other variables.
             # where to find the module data blob, as well as other variables.
@@ -1952,7 +1986,7 @@ class Freezer:
 
 
         if append_offset:
         if append_offset:
             # This is for legacy deploy-stub.
             # This is for legacy deploy-stub.
-            print("WARNING: Could not find blob header. Is deploy-stub outdated?")
+            warnings.warn("Could not find blob header. Is deploy-stub outdated?")
             blob += struct.pack('<Q', blob_offset)
             blob += struct.pack('<Q', blob_offset)
 
 
         with open(target, 'wb') as f:
         with open(target, 'wb') as f:
@@ -2215,7 +2249,7 @@ class Freezer:
 
 
                 strings = macho_data[stroff:stroff+strsize]
                 strings = macho_data[stroff:stroff+strsize]
 
 
-                for i in range(nsyms):
+                for j in range(nsyms):
                     strx, type, sect, desc, value = struct.unpack_from(nlist_struct, macho_data, symoff)
                     strx, type, sect, desc, value = struct.unpack_from(nlist_struct, macho_data, symoff)
                     symoff += nlist_size
                     symoff += nlist_size
                     name = strings[strx : strings.find(b'\0', strx)]
                     name = strings[strx : strings.find(b'\0', strx)]
@@ -2488,11 +2522,19 @@ class PandaModuleFinder(modulefinder.ModuleFinder):
         if name in self.badmodules:
         if name in self.badmodules:
             self._add_badmodule(name, caller)
             self._add_badmodule(name, caller)
             return
             return
+
+        if level <= 0 and caller and caller.__name__ in ignoreImports:
+            if name in ignoreImports[caller.__name__]:
+                return
+
         try:
         try:
             self.import_hook(name, caller, level=level)
             self.import_hook(name, caller, level=level)
         except ImportError as msg:
         except ImportError as msg:
             self.msg(2, "ImportError:", str(msg))
             self.msg(2, "ImportError:", str(msg))
             self._add_badmodule(name, caller)
             self._add_badmodule(name, caller)
+        except SyntaxError as msg:
+            self.msg(2, "SyntaxError:", str(msg))
+            self._add_badmodule(name, caller)
         else:
         else:
             if fromlist:
             if fromlist:
                 for sub in fromlist:
                 for sub in fromlist:
@@ -2506,6 +2548,78 @@ class PandaModuleFinder(modulefinder.ModuleFinder):
                         self.msg(2, "ImportError:", str(msg))
                         self.msg(2, "ImportError:", str(msg))
                         self._add_badmodule(fullname, caller)
                         self._add_badmodule(fullname, caller)
 
 
+    def scan_code(self, co, m):
+        code = co.co_code
+        # This was renamed to scan_opcodes in Python 3.6
+        if hasattr(self, 'scan_opcodes_25'):
+            scanner = self.scan_opcodes_25
+        else:
+            scanner = self.scan_opcodes
+
+        for what, args in scanner(co):
+            if what == "store":
+                name, = args
+                m.globalnames[name] = 1
+            elif what in ("import", "absolute_import"):
+                fromlist, name = args
+                have_star = 0
+                if fromlist is not None:
+                    if "*" in fromlist:
+                        have_star = 1
+                    fromlist = [f for f in fromlist if f != "*"]
+                if what == "absolute_import":
+                    level = 0
+                else:
+                    level = -1
+                self._safe_import_hook(name, m, fromlist, level=level)
+                if have_star:
+                    # We've encountered an "import *". If it is a Python module,
+                    # the code has already been parsed and we can suck out the
+                    # global names.
+                    mm = None
+                    if m.__path__:
+                        # At this point we don't know whether 'name' is a
+                        # submodule of 'm' or a global module. Let's just try
+                        # the full name first.
+                        mm = self.modules.get(m.__name__ + "." + name)
+                    if mm is None:
+                        mm = self.modules.get(name)
+                    if mm is not None:
+                        m.globalnames.update(mm.globalnames)
+                        m.starimports.update(mm.starimports)
+                        if mm.__code__ is None:
+                            m.starimports[name] = 1
+                    else:
+                        m.starimports[name] = 1
+            elif what == "relative_import":
+                level, fromlist, name = args
+                parent = self.determine_parent(m, level=level)
+                if name:
+                    self._safe_import_hook(name, m, fromlist, level=level)
+                else:
+                    self._safe_import_hook(parent.__name__, None, fromlist, level=0)
+
+                if fromlist and "*" in fromlist:
+                    if name:
+                        mm = self.modules.get(parent.__name__ + "." + name)
+                    else:
+                        mm = self.modules.get(parent.__name__)
+
+                    if mm is not None:
+                        m.globalnames.update(mm.globalnames)
+                        m.starimports.update(mm.starimports)
+                        if mm.__code__ is None:
+                            m.starimports[name] = 1
+                    else:
+                        m.starimports[name] = 1
+            else:
+                # We don't expect anything else from the generator.
+                raise RuntimeError(what)
+
+        for c in co.co_consts:
+            if isinstance(c, type(co)):
+                self.scan_code(c, m)
+
     def find_module(self, name, path=None, parent=None):
     def find_module(self, name, path=None, parent=None):
         """ Finds a module with the indicated name on the given search path
         """ Finds a module with the indicated name on the given search path
         (or self.path if None).  Returns a tuple like (fp, path, stuff), where
         (or self.path if None).  Returns a tuple like (fp, path, stuff), where
@@ -2593,7 +2707,7 @@ class PandaModuleFinder(modulefinder.ModuleFinder):
             except OSError:
             except OSError:
                 self.msg(2, "can't list directory", dir)
                 self.msg(2, "can't list directory", dir)
                 continue
                 continue
-            for name in names:
+            for name in sorted(names):
                 mod = None
                 mod = None
                 for suff in self.suffixes:
                 for suff in self.suffixes:
                     n = len(suff)
                     n = len(suff)

+ 154 - 197
direct/src/dist/commands.py

@@ -4,9 +4,9 @@ See the :ref:`distribution` section of the programming manual for information
 on how to use these commands.
 on how to use these commands.
 """
 """
 
 
-import collections
 import os
 import os
 import plistlib
 import plistlib
+import pkg_resources
 import sys
 import sys
 import subprocess
 import subprocess
 import zipfile
 import zipfile
@@ -16,7 +16,6 @@ import stat
 import struct
 import struct
 import imp
 import imp
 import string
 import string
-import time
 import tempfile
 import tempfile
 
 
 import setuptools
 import setuptools
@@ -24,20 +23,11 @@ import distutils.log
 
 
 from . import FreezeTool
 from . import FreezeTool
 from . import pefile
 from . import pefile
+from . import installers
 from .icon import Icon
 from .icon import Icon
 import panda3d.core as p3d
 import panda3d.core as p3d
 
 
 
 
-if sys.version_info < (3, 0):
-    # Warn the user.  They might be using Python 2 by accident.
-    print("=================================================================")
-    print("WARNING: You are using Python 2, which has reached the end of its")
-    print("WARNING: life as of January 1, 2020.  Please upgrade to Python 3.")
-    print("=================================================================")
-    sys.stdout.flush()
-    time.sleep(4.0)
-
-
 def _parse_list(input):
 def _parse_list(input):
     if isinstance(input, str):
     if isinstance(input, str):
         input = input.strip().replace(',', '\n')
         input = input.strip().replace(',', '\n')
@@ -104,14 +94,18 @@ PACKAGE_DATA_DIRS = {
         ('cefpython3/Chromium Embedded Framework.framework/Resources', 'Chromium Embedded Framework.framework/Resources', {}),
         ('cefpython3/Chromium Embedded Framework.framework/Resources', 'Chromium Embedded Framework.framework/Resources', {}),
         ('cefpython3/Chromium Embedded Framework.framework/Chromium Embedded Framework', '', {'PKG_DATA_MAKE_EXECUTABLE'}),
         ('cefpython3/Chromium Embedded Framework.framework/Chromium Embedded Framework', '', {'PKG_DATA_MAKE_EXECUTABLE'}),
     ],
     ],
+    'pytz': [('pytz/zoneinfo/*', 'zoneinfo', ())],
 }
 }
 
 
 # Some dependencies have extra directories that need to be scanned for DLLs.
 # Some dependencies have extra directories that need to be scanned for DLLs.
 # This dictionary maps wheel basenames (ie. the part of the .whl basename
 # This dictionary maps wheel basenames (ie. the part of the .whl basename
-# before the first hyphen) to a list of directories inside the .whl.
+# before the first hyphen) to a list of tuples, the first value being the
+# directory inside the wheel, the second being which wheel to look in (or
+# None to look in its own wheel).
 
 
 PACKAGE_LIB_DIRS = {
 PACKAGE_LIB_DIRS = {
-    'scipy':  ['scipy/extra-dll'],
+    'scipy':  [('scipy/extra-dll', None)],
+    'PyQt5':  [('PyQt5/Qt5/bin', 'PyQt5_Qt5')],
 }
 }
 
 
 SITE_PY = u"""
 SITE_PY = u"""
@@ -196,6 +190,7 @@ class build_apps(setuptools.Command):
         self.extra_prc_data = ''
         self.extra_prc_data = ''
         self.default_prc_dir = None
         self.default_prc_dir = None
         self.log_filename = None
         self.log_filename = None
+        self.log_filename_strftime = True
         self.log_append = False
         self.log_append = False
         self.requirements_path = os.path.join(os.getcwd(), 'requirements.txt')
         self.requirements_path = os.path.join(os.getcwd(), 'requirements.txt')
         self.use_optimized_wheels = True
         self.use_optimized_wheels = True
@@ -213,13 +208,17 @@ class build_apps(setuptools.Command):
             'dciman32.dll', 'comdlg32.dll', 'comctl32.dll', 'ole32.dll',
             'dciman32.dll', 'comdlg32.dll', 'comctl32.dll', 'ole32.dll',
             'oleaut32.dll', 'gdiplus.dll', 'winmm.dll', 'iphlpapi.dll',
             'oleaut32.dll', 'gdiplus.dll', 'winmm.dll', 'iphlpapi.dll',
             'msvcrt.dll', 'kernelbase.dll', 'msimg32.dll', 'msacm32.dll',
             'msvcrt.dll', 'kernelbase.dll', 'msimg32.dll', 'msacm32.dll',
-            'setupapi.dll', 'version.dll',
+            'setupapi.dll', 'version.dll', 'userenv.dll', 'netapi32.dll',
+            'crypt32.dll', 'bcrypt.dll',
 
 
             # manylinux1/linux
             # manylinux1/linux
             'libdl.so.*', 'libstdc++.so.*', 'libm.so.*', 'libgcc_s.so.*',
             'libdl.so.*', 'libstdc++.so.*', 'libm.so.*', 'libgcc_s.so.*',
             'libpthread.so.*', 'libc.so.*', 'ld-linux-x86-64.so.*',
             'libpthread.so.*', 'libc.so.*', 'ld-linux-x86-64.so.*',
-            'libgl.so.*', 'libx11.so.*', 'libreadline.so.*', 'libncursesw.so.*',
-            'libbz2.so.*', 'libz.so.*', 'liblzma.so.*', 'librt.so.*', 'libutil.so.*',
+            'libgl.so.*', 'libx11.so.*', 'libncursesw.so.*', 'libz.so.*',
+            'librt.so.*', 'libutil.so.*', 'libnsl.so.1', 'libXext.so.6',
+            'libXrender.so.1', 'libICE.so.6', 'libSM.so.6', 'libEGL.so.1',
+            'libOpenGL.so.0', 'libGLdispatch.so.0', 'libGLX.so.0',
+            'libgobject-2.0.so.0', 'libgthread-2.0.so.0', 'libglib-2.0.so.0',
 
 
             # macOS
             # macOS
             '/usr/lib/libc++.1.dylib',
             '/usr/lib/libc++.1.dylib',
@@ -229,15 +228,47 @@ class build_apps(setuptools.Command):
             '/usr/lib/libSystem.*.dylib',
             '/usr/lib/libSystem.*.dylib',
             '/usr/lib/libbz2.*.dylib',
             '/usr/lib/libbz2.*.dylib',
             '/usr/lib/libedit.*.dylib',
             '/usr/lib/libedit.*.dylib',
+            '/usr/lib/libffi.dylib',
+            '/usr/lib/libauditd.0.dylib',
+            '/usr/lib/libgermantok.dylib',
+            '/usr/lib/liblangid.dylib',
+            '/usr/lib/libarchive.2.dylib',
+            '/usr/lib/libipsec.A.dylib',
+            '/usr/lib/libpanel.5.4.dylib',
+            '/usr/lib/libiodbc.2.1.18.dylib',
+            '/usr/lib/libhunspell-1.2.0.0.0.dylib',
+            '/usr/lib/libsqlite3.dylib',
+            '/usr/lib/libpam.1.dylib',
+            '/usr/lib/libtidy.A.dylib',
+            '/usr/lib/libDHCPServer.A.dylib',
+            '/usr/lib/libpam.2.dylib',
+            '/usr/lib/libXplugin.1.dylib',
+            '/usr/lib/libxslt.1.dylib',
+            '/usr/lib/libiodbcinst.2.1.18.dylib',
+            '/usr/lib/libBSDPClient.A.dylib',
+            '/usr/lib/libsandbox.1.dylib',
+            '/usr/lib/libform.5.4.dylib',
+            '/usr/lib/libbsm.0.dylib',
+            '/usr/lib/libMatch.1.dylib',
+            '/usr/lib/libresolv.9.dylib',
+            '/usr/lib/libcharset.1.dylib',
+            '/usr/lib/libxml2.2.dylib',
+            '/usr/lib/libiconv.2.dylib',
+            '/usr/lib/libScreenReader.dylib',
+            '/usr/lib/libdtrace.dylib',
+            '/usr/lib/libicucore.A.dylib',
+            '/usr/lib/libsasl2.2.dylib',
+            '/usr/lib/libpcap.A.dylib',
+            '/usr/lib/libexslt.0.dylib',
+            '/usr/lib/libcurl.4.dylib',
+            '/usr/lib/libncurses.5.4.dylib',
+            '/usr/lib/libxar.1.dylib',
+            '/usr/lib/libmenu.5.4.dylib',
             '/System/Library/**',
             '/System/Library/**',
         ]
         ]
 
 
-        if sys.version_info >= (3, 5):
-            # Python 3.5+ requires at least Windows Vista to run anyway, so we
-            # shouldn't warn about DLLs that are shipped with Vista.
-            self.exclude_dependencies += ['bcrypt.dll']
-
         self.package_data_dirs = {}
         self.package_data_dirs = {}
+        self.hidden_imports = {}
 
 
         # We keep track of the zip files we've opened.
         # We keep track of the zip files we've opened.
         self._zip_files = {}
         self._zip_files = {}
@@ -271,6 +302,10 @@ class build_apps(setuptools.Command):
         self.platforms = _parse_list(self.platforms)
         self.platforms = _parse_list(self.platforms)
         self.plugins = _parse_list(self.plugins)
         self.plugins = _parse_list(self.plugins)
         self.extra_prc_files = _parse_list(self.extra_prc_files)
         self.extra_prc_files = _parse_list(self.extra_prc_files)
+        self.hidden_imports = {
+            key: _parse_list(value)
+            for key, value in _parse_dict(self.hidden_imports).items()
+        }
 
 
         if self.default_prc_dir is None:
         if self.default_prc_dir is None:
             self.default_prc_dir = '<auto>etc' if not self.embed_prc_data else ''
             self.default_prc_dir = '<auto>etc' if not self.embed_prc_data else ''
@@ -366,7 +401,8 @@ class build_apps(setuptools.Command):
             abi_tag += 'm'
             abi_tag += 'm'
 
 
         whldir = os.path.join(whlcache, '_'.join((platform, abi_tag)))
         whldir = os.path.join(whlcache, '_'.join((platform, abi_tag)))
-        os.makedirs(whldir, exist_ok=True)
+        if not os.path.isdir(whldir):
+            os.makedirs(whldir)
 
 
         # Remove any .zip files. These are built from a VCS and block for an
         # Remove any .zip files. These are built from a VCS and block for an
         # interactive prompt on subsequent downloads.
         # interactive prompt on subsequent downloads.
@@ -469,11 +505,7 @@ class build_apps(setuptools.Command):
             icon.makeICNS(os.path.join(resdir, 'iconfile.icns'))
             icon.makeICNS(os.path.join(resdir, 'iconfile.icns'))
 
 
         with open(os.path.join(contentsdir, 'Info.plist'), 'wb') as f:
         with open(os.path.join(contentsdir, 'Info.plist'), 'wb') as f:
-            if hasattr(plistlib, 'dump'):
-                plistlib.dump(plist, f)
-            else:
-                plistlib.writePlist(plist, f)
-
+            plistlib.dump(plist, f)
 
 
     def build_runtimes(self, platform, use_wheels):
     def build_runtimes(self, platform, use_wheels):
         """ Builds the distributions for the given platform. """
         """ Builds the distributions for the given platform. """
@@ -486,6 +518,7 @@ class build_apps(setuptools.Command):
 
 
         path = sys.path[:]
         path = sys.path[:]
         p3dwhl = None
         p3dwhl = None
+        wheelpaths = []
 
 
         if use_wheels:
         if use_wheels:
             wheelpaths = self.download_wheels(platform)
             wheelpaths = self.download_wheels(platform)
@@ -581,6 +614,12 @@ class build_apps(setuptools.Command):
                     # by default.  Switch it up if FMOD is not included.
                     # by default.  Switch it up if FMOD is not included.
                     if value not in self.plugins and value == 'p3fmod_audio' and 'p3openal_audio' in self.plugins:
                     if value not in self.plugins and value == 'p3fmod_audio' and 'p3openal_audio' in self.plugins:
                         self.warn("Missing audio plugin p3fmod_audio referenced in PRC data, replacing with p3openal_audio")
                         self.warn("Missing audio plugin p3fmod_audio referenced in PRC data, replacing with p3openal_audio")
+                        value = 'p3openal_audio'
+
+                if var == 'aux-display':
+                    # Silently remove aux-display lines for missing plugins.
+                    if value not in self.plugins:
+                        continue
 
 
                 for plugin in check_plugins:
                 for plugin in check_plugins:
                     if plugin in value and plugin not in self.plugins:
                     if plugin in value and plugin not in self.plugins:
@@ -633,13 +672,22 @@ class build_apps(setuptools.Command):
                     # Also look for more specific per-package cases, defined in
                     # Also look for more specific per-package cases, defined in
                     # PACKAGE_LIB_DIRS at the top of this file.
                     # PACKAGE_LIB_DIRS at the top of this file.
                     extra_dirs = PACKAGE_LIB_DIRS.get(whl_name, [])
                     extra_dirs = PACKAGE_LIB_DIRS.get(whl_name, [])
-                    for extra_dir in extra_dirs:
-                        search_path.append(os.path.join(whl, extra_dir.replace('/', os.path.sep)))
+                    for extra_dir, search_in in extra_dirs:
+                        if not search_in:
+                            search_path.append(os.path.join(whl, extra_dir.replace('/', os.path.sep)))
+                        else:
+                            for whl2 in wheelpaths:
+                                if os.path.basename(whl2).startswith(search_in + '-'):
+                                    search_path.append(os.path.join(whl2, extra_dir.replace('/', os.path.sep)))
 
 
             return search_path
             return search_path
 
 
         def create_runtime(appname, mainscript, use_console):
         def create_runtime(appname, mainscript, use_console):
-            freezer = FreezeTool.Freezer(platform=platform, path=path)
+            freezer = FreezeTool.Freezer(
+                platform=platform,
+                path=path,
+                hiddenImports=self.hidden_imports
+            )
             freezer.addModule('__main__', filename=mainscript)
             freezer.addModule('__main__', filename=mainscript)
             freezer.addModule('site', filename='site.py', text=SITE_PY)
             freezer.addModule('site', filename='site.py', text=SITE_PY)
             for incmod in self.include_modules.get(appname, []) + self.include_modules.get('*', []):
             for incmod in self.include_modules.get(appname, []) + self.include_modules.get('*', []):
@@ -678,6 +726,10 @@ class build_apps(setuptools.Command):
             else:
             else:
                 temp_file = None
                 temp_file = None
 
 
+            use_strftime = self.log_filename_strftime
+            if not self.log_filename or '%' not in self.log_filename:
+                use_strftime = False
+
             freezer.generateRuntimeFromStub(target_path, stub_file, use_console, {
             freezer.generateRuntimeFromStub(target_path, stub_file, use_console, {
                 'prc_data': prcexport if self.embed_prc_data else None,
                 'prc_data': prcexport if self.embed_prc_data else None,
                 'default_prc_dir': self.default_prc_dir,
                 'default_prc_dir': self.default_prc_dir,
@@ -690,7 +742,7 @@ class build_apps(setuptools.Command):
                 'prc_executable_args_envvar': None,
                 'prc_executable_args_envvar': None,
                 'main_dir': None,
                 'main_dir': None,
                 'log_filename': self.expand_path(self.log_filename, platform),
                 'log_filename': self.expand_path(self.log_filename, platform),
-            }, self.log_append)
+            }, self.log_append, use_strftime)
             stub_file.close()
             stub_file.close()
 
 
             if temp_file:
             if temp_file:
@@ -760,6 +812,7 @@ class build_apps(setuptools.Command):
         for module, source_path in freezer_extras:
         for module, source_path in freezer_extras:
             if source_path is not None:
             if source_path is not None:
                 # Rename panda3d/core.pyd to panda3d.core.pyd
                 # Rename panda3d/core.pyd to panda3d.core.pyd
+                source_path = os.path.normpath(source_path)
                 basename = os.path.basename(source_path)
                 basename = os.path.basename(source_path)
                 if '.' in module:
                 if '.' in module:
                     basename = module.rsplit('.', 1)[0] + '.' + basename
                     basename = module.rsplit('.', 1)[0] + '.' + basename
@@ -769,6 +822,20 @@ class build_apps(setuptools.Command):
                 if len(parts) >= 3 and '-' in parts[-2]:
                 if len(parts) >= 3 and '-' in parts[-2]:
                     parts = parts[:-2] + parts[-1:]
                     parts = parts[:-2] + parts[-1:]
                     basename = '.'.join(parts)
                     basename = '.'.join(parts)
+
+                # Was this not found in a wheel?  Then we may have a problem,
+                # since it may be for the current platform instead of the target
+                # platform.
+                if use_wheels:
+                    found_in_wheel = False
+                    for whl in wheelpaths:
+                        whl = os.path.normpath(whl)
+                        if source_path.lower().startswith(os.path.join(whl, '').lower()):
+                            found_in_wheel = True
+                            break
+
+                    if not found_in_wheel:
+                        self.warn('{} was not found in any downloaded wheel, is a dependency missing from requirements.txt?'.format(basename))
             else:
             else:
                 # Builtin module, but might not be builtin in wheel libs, so double check
                 # Builtin module, but might not be builtin in wheel libs, so double check
                 if module in whl_modules:
                 if module in whl_modules:
@@ -872,6 +939,27 @@ class build_apps(setuptools.Command):
             return check_pattern(fname, include_copy_list) and \
             return check_pattern(fname, include_copy_list) and \
                 not check_pattern(fname, ignore_copy_list)
                 not check_pattern(fname, ignore_copy_list)
 
 
+        def skip_directory(src):
+            # Provides a quick-out for directory checks.  NOT recursive.
+            fn = p3d.Filename.from_os_specific(os.path.normpath(src))
+            path = fn.get_fullpath()
+            fn.make_absolute()
+            abspath = fn.get_fullpath()
+
+            for pattern in ignore_copy_list:
+                if not pattern.pattern.endswith('/*') and \
+                   not pattern.pattern.endswith('/**'):
+                    continue
+
+                pattern_dir = p3d.Filename(pattern.pattern).get_dirname()
+                if abspath.startswith(pattern_dir + '/'):
+                    return True
+
+                if path.startswith(pattern_dir + '/'):
+                    return True
+
+            return False
+
         def copy_file(src, dst):
         def copy_file(src, dst):
             src = os.path.normpath(src)
             src = os.path.normpath(src)
             dst = os.path.normpath(dst)
             dst = os.path.normpath(dst)
@@ -911,7 +999,12 @@ class build_apps(setuptools.Command):
 
 
         rootdir = os.getcwd()
         rootdir = os.getcwd()
         for dirname, subdirlist, filelist in os.walk(rootdir):
         for dirname, subdirlist, filelist in os.walk(rootdir):
+            subdirlist.sort()
             dirpath = os.path.relpath(dirname, rootdir)
             dirpath = os.path.relpath(dirname, rootdir)
+            if skip_directory(dirpath):
+                self.announce('skipping directory {}'.format(dirpath))
+                continue
+
             for fname in filelist:
             for fname in filelist:
                 src = os.path.join(dirpath, fname)
                 src = os.path.join(dirpath, fname)
                 dst = os.path.join(builddir, update_path(src))
                 dst = os.path.join(builddir, update_path(src))
@@ -1240,6 +1333,14 @@ class bdist_apps(setuptools.Command):
         # Everything else defaults to ['zip']
         # Everything else defaults to ['zip']
     }
     }
 
 
+    DEFAULT_INSTALLER_FUNCS = {
+        'zip': installers.create_zip,
+        'gztar': installers.create_gztar,
+        'bztar': installers.create_bztar,
+        'xztar': installers.create_xztar,
+        'nsis': installers.create_nsis,
+    }
+
     description = 'bundle built Panda3D applications into distributable forms'
     description = 'bundle built Panda3D applications into distributable forms'
     user_options = build_apps.user_options + [
     user_options = build_apps.user_options + [
         ('dist-dir=', 'd', 'directory to put final built distributions in'),
         ('dist-dir=', 'd', 'directory to put final built distributions in'),
@@ -1253,6 +1354,8 @@ class bdist_apps(setuptools.Command):
         self.installers = {}
         self.installers = {}
         self.dist_dir = os.path.join(os.getcwd(), 'dist')
         self.dist_dir = os.path.join(os.getcwd(), 'dist')
         self.skip_build = False
         self.skip_build = False
+        self.installer_functions = {}
+        self._current_platform = None
         for opt in self._build_apps_options():
         for opt in self._build_apps_options():
             setattr(self, opt, None)
             setattr(self, opt, None)
 
 
@@ -1264,145 +1367,19 @@ class bdist_apps(setuptools.Command):
             for key, value in _parse_dict(self.installers).items()
             for key, value in _parse_dict(self.installers).items()
         }
         }
 
 
-    def _get_archive_basedir(self):
-        return self.distribution.get_name()
-
-    def create_zip(self, basename, build_dir):
-        import zipfile
-
-        base_dir = self._get_archive_basedir()
-
-        with zipfile.ZipFile(basename+'.zip', 'w', compression=zipfile.ZIP_DEFLATED) as zf:
-            zf.write(build_dir, base_dir)
-
-            for dirpath, dirnames, filenames in os.walk(build_dir):
-                for name in sorted(dirnames):
-                    path = os.path.normpath(os.path.join(dirpath, name))
-                    zf.write(path, path.replace(build_dir, base_dir, 1))
-                for name in filenames:
-                    path = os.path.normpath(os.path.join(dirpath, name))
-                    if os.path.isfile(path):
-                        zf.write(path, path.replace(build_dir, base_dir, 1))
-
-    def create_tarball(self, basename, build_dir, tar_compression):
-        import tarfile
-
-        base_dir = self._get_archive_basedir()
-        build_cmd = self.get_finalized_command('build_apps')
-        binary_names = list(build_cmd.console_apps.keys()) + list(build_cmd.gui_apps.keys())
-
-        def tarfilter(tarinfo):
-            if tarinfo.isdir() or os.path.basename(tarinfo.name) in binary_names:
-                tarinfo.mode = 0o755
-            else:
-                tarinfo.mode = 0o644
-            return tarinfo
-
-        with tarfile.open('{}.tar.{}'.format(basename, tar_compression), 'w|{}'.format(tar_compression)) as tf:
-            tf.add(build_dir, base_dir, filter=tarfilter)
-
-    def create_nsis(self, basename, build_dir, is_64bit):
-        # Get a list of build applications
-        build_cmd = self.get_finalized_command('build_apps')
-        apps = build_cmd.gui_apps.copy()
-        apps.update(build_cmd.console_apps)
-        apps = [
-            '{}.exe'.format(i)
-            for i in apps
-        ]
-
-        shortname = self.distribution.get_name()
+        tmp = self.DEFAULT_INSTALLER_FUNCS.copy()
+        tmp.update(self.installer_functions)
+        tmp.update({
+            entrypoint.name: entrypoint.load()
+            for entrypoint in pkg_resources.iter_entry_points('panda3d.bdist_apps.installers')
+        })
+        self.installer_functions = tmp
 
 
-        # Create the .nsi installer script
-        nsifile = p3d.Filename(build_cmd.build_base, shortname + ".nsi")
-        nsifile.unlink()
-        nsi = open(nsifile.to_os_specific(), "w")
+    def get_archive_basedir(self):
+        return self.distribution.get_name()
 
 
-        # Some global info
-        nsi.write('Name "%s"\n' % shortname)
-        nsi.write('OutFile "%s"\n' % os.path.join(self.dist_dir, basename+'.exe'))
-        if is_64bit:
-            nsi.write('InstallDir "$PROGRAMFILES64\\%s"\n' % shortname)
-        else:
-            nsi.write('InstallDir "$PROGRAMFILES\\%s"\n' % shortname)
-        nsi.write('SetCompress auto\n')
-        nsi.write('SetCompressor lzma\n')
-        nsi.write('ShowInstDetails nevershow\n')
-        nsi.write('ShowUninstDetails nevershow\n')
-        nsi.write('InstType "Typical"\n')
-
-        # Tell Vista that we require admin rights
-        nsi.write('RequestExecutionLevel admin\n')
-        nsi.write('\n')
-
-        # TODO offer run and desktop shortcut after we figure out how to deal
-        # with multiple apps
-
-        nsi.write('!include "MUI2.nsh"\n')
-        nsi.write('!define MUI_ABORTWARNING\n')
-        nsi.write('\n')
-        nsi.write('Var StartMenuFolder\n')
-        nsi.write('!insertmacro MUI_PAGE_WELCOME\n')
-        # TODO license file
-        nsi.write('!insertmacro MUI_PAGE_DIRECTORY\n')
-        nsi.write('!insertmacro MUI_PAGE_STARTMENU Application $StartMenuFolder\n')
-        nsi.write('!insertmacro MUI_PAGE_INSTFILES\n')
-        nsi.write('!insertmacro MUI_PAGE_FINISH\n')
-        nsi.write('!insertmacro MUI_UNPAGE_WELCOME\n')
-        nsi.write('!insertmacro MUI_UNPAGE_CONFIRM\n')
-        nsi.write('!insertmacro MUI_UNPAGE_INSTFILES\n')
-        nsi.write('!insertmacro MUI_UNPAGE_FINISH\n')
-        nsi.write('!insertmacro MUI_LANGUAGE "English"\n')
-
-        # This section defines the installer.
-        nsi.write('Section "" SecCore\n')
-        nsi.write('  SetOutPath "$INSTDIR"\n')
-        curdir = ""
-        nsi_dir = p3d.Filename.fromOsSpecific(build_cmd.build_base)
-        build_root_dir = p3d.Filename.fromOsSpecific(build_dir)
-        for root, dirs, files in os.walk(build_dir):
-            for name in files:
-                basefile = p3d.Filename.fromOsSpecific(os.path.join(root, name))
-                file = p3d.Filename(basefile)
-                file.makeAbsolute()
-                file.makeRelativeTo(nsi_dir)
-                outdir = p3d.Filename(basefile)
-                outdir.makeAbsolute()
-                outdir.makeRelativeTo(build_root_dir)
-                outdir = outdir.getDirname().replace('/', '\\')
-                if curdir != outdir:
-                    nsi.write('  SetOutPath "$INSTDIR\\%s"\n' % outdir)
-                    curdir = outdir
-                nsi.write('  File "%s"\n' % (file.toOsSpecific()))
-        nsi.write('  SetOutPath "$INSTDIR"\n')
-        nsi.write('  WriteUninstaller "$INSTDIR\\Uninstall.exe"\n')
-        nsi.write('  ; Start menu items\n')
-        nsi.write('  !insertmacro MUI_STARTMENU_WRITE_BEGIN Application\n')
-        nsi.write('    CreateDirectory "$SMPROGRAMS\\$StartMenuFolder"\n')
-        for app in apps:
-            nsi.write('    CreateShortCut "$SMPROGRAMS\\$StartMenuFolder\\%s.lnk" "$INSTDIR\\%s"\n' % (shortname, app))
-        nsi.write('    CreateShortCut "$SMPROGRAMS\\$StartMenuFolder\\Uninstall.lnk" "$INSTDIR\\Uninstall.exe"\n')
-        nsi.write('  !insertmacro MUI_STARTMENU_WRITE_END\n')
-        nsi.write('SectionEnd\n')
-
-        # This section defines the uninstaller.
-        nsi.write('Section Uninstall\n')
-        nsi.write('  RMDir /r "$INSTDIR"\n')
-        nsi.write('  ; Desktop icon\n')
-        nsi.write('  Delete "$DESKTOP\\%s.lnk"\n' % shortname)
-        nsi.write('  ; Start menu items\n')
-        nsi.write('  !insertmacro MUI_STARTMENU_GETFOLDER Application $StartMenuFolder\n')
-        nsi.write('  RMDir /r "$SMPROGRAMS\\$StartMenuFolder"\n')
-        nsi.write('SectionEnd\n')
-        nsi.close()
-
-        cmd = ['makensis']
-        for flag in ["V2"]:
-            cmd.append(
-                '{}{}'.format('/' if sys.platform.startswith('win') else '-', flag)
-            )
-        cmd.append(nsifile.to_os_specific())
-        subprocess.check_call(cmd)
+    def get_current_platform(self):
+        return self._current_platform
 
 
     def run(self):
     def run(self):
         build_cmd = self.distribution.get_command_obj('build_apps')
         build_cmd = self.distribution.get_command_obj('build_apps')
@@ -1424,35 +1401,15 @@ class bdist_apps(setuptools.Command):
             build_dir = os.path.join(build_base, platform)
             build_dir = os.path.join(build_base, platform)
             basename = '{}_{}'.format(self.distribution.get_fullname(), platform)
             basename = '{}_{}'.format(self.distribution.get_fullname(), platform)
             installers = self.installers.get(platform, self.DEFAULT_INSTALLERS.get(platform, ['zip']))
             installers = self.installers.get(platform, self.DEFAULT_INSTALLERS.get(platform, ['zip']))
+            self._current_platform = platform
 
 
             for installer in installers:
             for installer in installers:
                 self.announce('\nBuilding {} for platform: {}'.format(installer, platform), distutils.log.INFO)
                 self.announce('\nBuilding {} for platform: {}'.format(installer, platform), distutils.log.INFO)
+                if installer not in self.installer_functions:
+                    self.announce(
+                        '\tUnknown installer: {}'.format(installer),
+                        distutils.log.ERROR
+                    )
+                    continue
 
 
-                if installer == 'zip':
-                    self.create_zip(basename, build_dir)
-                elif installer in ('gztar', 'bztar', 'xztar'):
-                    compress = installer.replace('tar', '')
-                    if compress == 'bz':
-                        compress = 'bz2'
-
-                    self.create_tarball(basename, build_dir, compress)
-                elif installer == 'nsis':
-                    if not platform.startswith('win'):
-                        self.announce(
-                            '\tNSIS installer not supported for platform: {}'.format(platform),
-                            distutils.log.ERROR
-                        )
-                        continue
-                    try:
-                        subprocess.call(['makensis', '--version'])
-                    except OSError:
-                        self.announce(
-                            '\tCould not find makensis tool that is required to build NSIS installers',
-                            distutils.log.ERROR
-                        )
-                        # continue
-                    is_64bit = platform == 'win_amd64'
-                    self.create_nsis(basename, build_dir, is_64bit)
-
-                else:
-                    self.announce('\tUnknown installer: {}'.format(installer), distutils.log.ERROR)
+                self.installer_functions[installer](self, basename, build_dir)

+ 1 - 1
direct/src/dist/icon.py

@@ -150,7 +150,7 @@ class Icon:
 
 
         # ICO files only support resolutions up to 256x256.
         # ICO files only support resolutions up to 256x256.
         count = 0
         count = 0
-        for size in self.images.keys():
+        for size in self.images:
             if size < 256:
             if size < 256:
                 count += 1
                 count += 1
             if size <= 256:
             if size <= 256:

+ 198 - 0
direct/src/dist/installers.py

@@ -0,0 +1,198 @@
+import distutils.log
+import os
+import subprocess
+import sys
+import tarfile
+import zipfile
+import struct
+
+import panda3d.core as p3d
+
+def create_zip(command, basename, build_dir):
+    base_dir = command.get_archive_basedir()
+
+    with zipfile.ZipFile(basename+'.zip', 'w', compression=zipfile.ZIP_DEFLATED) as zf:
+        zf.write(build_dir, base_dir)
+
+        for dirpath, dirnames, filenames in os.walk(build_dir):
+            dirnames.sort()
+            for name in dirnames:
+                path = os.path.normpath(os.path.join(dirpath, name))
+                zf.write(path, path.replace(build_dir, base_dir, 1))
+            for name in filenames:
+                path = os.path.normpath(os.path.join(dirpath, name))
+                if os.path.isfile(path):
+                    zf.write(path, path.replace(build_dir, base_dir, 1))
+
+
+def create_tarball(command, basename, build_dir, tar_compression):
+    base_dir = command.get_archive_basedir()
+    build_cmd = command.get_finalized_command('build_apps')
+    binary_names = list(build_cmd.console_apps.keys()) + list(build_cmd.gui_apps.keys())
+
+    source_date = os.environ.get('SOURCE_DATE_EPOCH', '').strip()
+    if source_date:
+        max_mtime = int(source_date)
+    else:
+        max_mtime = None
+
+    def tarfilter(tarinfo):
+        if tarinfo.isdir() or os.path.basename(tarinfo.name) in binary_names:
+            tarinfo.mode = 0o755
+        else:
+            tarinfo.mode = 0o644
+
+        # This isn't interesting information to retain for distribution.
+        tarinfo.uid = 0
+        tarinfo.gid = 0
+        tarinfo.uname = ""
+        tarinfo.gname = ""
+
+        if max_mtime is not None and tarinfo.mtime >= max_mtime:
+            tarinfo.mtime = max_mtime
+
+        return tarinfo
+
+    filename = '{}.tar.{}'.format(basename, tar_compression)
+    with tarfile.open(filename, 'w|{}'.format(tar_compression)) as tf:
+        tf.add(build_dir, base_dir, filter=tarfilter)
+
+    if tar_compression == 'gz' and max_mtime is not None:
+        # Python provides no elegant way to overwrite the gzip timestamp.
+        with open(filename, 'r+b') as fp:
+            fp.seek(4)
+            fp.write(struct.pack("<L", max_mtime))
+
+
+def create_gztar(command, basename, build_dir):
+    return create_tarball(command, basename, build_dir, 'gz')
+
+
+def create_bztar(command, basename, build_dir):
+    return create_tarball(command, basename, build_dir, 'bz2')
+
+
+def create_xztar(command, basename, build_dir):
+    return create_tarball(command, basename, build_dir, 'xz')
+
+
+def create_nsis(command, basename, build_dir):
+    platform = command.get_current_platform()
+    if not platform.startswith('win'):
+        command.announce(
+            '\tNSIS installer not supported for platform: {}'.format(platform),
+            distutils.log.ERROR
+        )
+        return
+    try:
+        subprocess.call(['makensis', '--version'])
+    except OSError:
+        command.announce(
+            '\tCould not find makensis tool that is required to build NSIS installers',
+            distutils.log.ERROR
+        )
+        return
+
+    is_64bit = platform == 'win_amd64'
+    # Get a list of build applications
+    build_cmd = command.get_finalized_command('build_apps')
+    apps = build_cmd.gui_apps.copy()
+    apps.update(build_cmd.console_apps)
+    apps = [
+        '{}.exe'.format(i)
+        for i in apps
+    ]
+
+    shortname = command.distribution.get_name()
+
+    # Create the .nsi installer script
+    nsifile = p3d.Filename(build_cmd.build_base, shortname + ".nsi")
+    nsifile.unlink()
+    nsi = open(nsifile.to_os_specific(), "w")
+
+    # Some global info
+    nsi.write('Name "%s"\n' % shortname)
+    nsi.write('OutFile "%s"\n' % os.path.join(command.dist_dir, basename+'.exe'))
+    if is_64bit:
+        nsi.write('InstallDir "$PROGRAMFILES64\\%s"\n' % shortname)
+    else:
+        nsi.write('InstallDir "$PROGRAMFILES\\%s"\n' % shortname)
+    nsi.write('SetCompress auto\n')
+    nsi.write('SetCompressor lzma\n')
+    nsi.write('ShowInstDetails nevershow\n')
+    nsi.write('ShowUninstDetails nevershow\n')
+    nsi.write('InstType "Typical"\n')
+
+    # Tell Vista that we require admin rights
+    nsi.write('RequestExecutionLevel admin\n')
+    nsi.write('\n')
+
+    # TODO offer run and desktop shortcut after we figure out how to deal
+    # with multiple apps
+
+    nsi.write('!include "MUI2.nsh"\n')
+    nsi.write('!define MUI_ABORTWARNING\n')
+    nsi.write('\n')
+    nsi.write('Var StartMenuFolder\n')
+    nsi.write('!insertmacro MUI_PAGE_WELCOME\n')
+    # TODO license file
+    nsi.write('!insertmacro MUI_PAGE_DIRECTORY\n')
+    nsi.write('!insertmacro MUI_PAGE_STARTMENU Application $StartMenuFolder\n')
+    nsi.write('!insertmacro MUI_PAGE_INSTFILES\n')
+    nsi.write('!insertmacro MUI_PAGE_FINISH\n')
+    nsi.write('!insertmacro MUI_UNPAGE_WELCOME\n')
+    nsi.write('!insertmacro MUI_UNPAGE_CONFIRM\n')
+    nsi.write('!insertmacro MUI_UNPAGE_INSTFILES\n')
+    nsi.write('!insertmacro MUI_UNPAGE_FINISH\n')
+    nsi.write('!insertmacro MUI_LANGUAGE "English"\n')
+
+    # This section defines the installer.
+    nsi.write('Section "" SecCore\n')
+    nsi.write('  SetOutPath "$INSTDIR"\n')
+    curdir = ""
+    nsi_dir = p3d.Filename.fromOsSpecific(build_cmd.build_base)
+    build_root_dir = p3d.Filename.fromOsSpecific(build_dir)
+    for root, dirs, files in os.walk(build_dir):
+        dirs.sort()
+        for name in files:
+            basefile = p3d.Filename.fromOsSpecific(os.path.join(root, name))
+            file = p3d.Filename(basefile)
+            file.makeAbsolute()
+            file.makeRelativeTo(nsi_dir)
+            outdir = p3d.Filename(basefile)
+            outdir.makeAbsolute()
+            outdir.makeRelativeTo(build_root_dir)
+            outdir = outdir.getDirname().replace('/', '\\')
+            if curdir != outdir:
+                nsi.write('  SetOutPath "$INSTDIR\\%s"\n' % outdir)
+                curdir = outdir
+            nsi.write('  File "%s"\n' % (file.toOsSpecific()))
+    nsi.write('  SetOutPath "$INSTDIR"\n')
+    nsi.write('  WriteUninstaller "$INSTDIR\\Uninstall.exe"\n')
+    nsi.write('  ; Start menu items\n')
+    nsi.write('  !insertmacro MUI_STARTMENU_WRITE_BEGIN Application\n')
+    nsi.write('    CreateDirectory "$SMPROGRAMS\\$StartMenuFolder"\n')
+    for app in apps:
+        nsi.write('    CreateShortCut "$SMPROGRAMS\\$StartMenuFolder\\%s.lnk" "$INSTDIR\\%s"\n' % (shortname, app))
+    nsi.write('    CreateShortCut "$SMPROGRAMS\\$StartMenuFolder\\Uninstall.lnk" "$INSTDIR\\Uninstall.exe"\n')
+    nsi.write('  !insertmacro MUI_STARTMENU_WRITE_END\n')
+    nsi.write('SectionEnd\n')
+
+    # This section defines the uninstaller.
+    nsi.write('Section Uninstall\n')
+    nsi.write('  RMDir /r "$INSTDIR"\n')
+    nsi.write('  ; Desktop icon\n')
+    nsi.write('  Delete "$DESKTOP\\%s.lnk"\n' % shortname)
+    nsi.write('  ; Start menu items\n')
+    nsi.write('  !insertmacro MUI_STARTMENU_GETFOLDER Application $StartMenuFolder\n')
+    nsi.write('  RMDir /r "$SMPROGRAMS\\$StartMenuFolder"\n')
+    nsi.write('SectionEnd\n')
+    nsi.close()
+
+    cmd = ['makensis']
+    for flag in ["V2"]:
+        cmd.append(
+            '{}{}'.format('/' if sys.platform.startswith('win') else '-', flag)
+        )
+    cmd.append(nsifile.to_os_specific())
+    subprocess.check_call(cmd)

+ 3 - 4
direct/src/dist/pefile.py

@@ -242,10 +242,7 @@ class VersionInfoResource(object):
         length, value_length = unpack('<HH', data[0:4])
         length, value_length = unpack('<HH', data[0:4])
         offset = 40 + value_length + (value_length & 1)
         offset = 40 + value_length + (value_length & 1)
         dwords = array('I')
         dwords = array('I')
-        if sys.version_info >= (3, 2):
-            dwords.frombytes(bytes(data[40:offset]))
-        else:
-            dwords.fromstring(bytes(data[40:offset]))
+        dwords.frombytes(bytes(data[40:offset]))
 
 
         if len(dwords) > 0:
         if len(dwords) > 0:
             self.signature = dwords[0]
             self.signature = dwords[0]
@@ -490,6 +487,8 @@ class ResourceTable(object):
             entry.data = data
             entry.data = data
             entry.code_page = code_page
             entry.code_page = code_page
 
 
+        return entry
+
 
 
 class PEFile(object):
 class PEFile(object):
 
 

+ 1 - 1
direct/src/dist/pfreeze.py

@@ -105,7 +105,7 @@ def main(args=None):
         elif opt == '-h':
         elif opt == '-h':
             usage(0)
             usage(0)
         else:
         else:
-            print('illegal option: ' + flag)
+            print('illegal option: ' + opt)
             sys.exit(1)
             sys.exit(1)
 
 
     if not basename:
     if not basename:

+ 3 - 1
direct/src/distributed/AsyncRequest.py

@@ -1,6 +1,7 @@
 #from otp.ai.AIBaseGlobal import *
 #from otp.ai.AIBaseGlobal import *
 from direct.directnotify import DirectNotifyGlobal
 from direct.directnotify import DirectNotifyGlobal
 from direct.showbase.DirectObject import DirectObject
 from direct.showbase.DirectObject import DirectObject
+from direct.showbase.MessengerGlobal import messenger
 from .ConnectionRepository import *
 from .ConnectionRepository import *
 from panda3d.core import ConfigVariableDouble, ConfigVariableInt, ConfigVariableBool
 from panda3d.core import ConfigVariableDouble, ConfigVariableInt, ConfigVariableBool
 
 
@@ -254,7 +255,8 @@ class AsyncRequest(DirectObject):
                         print("\n\nself.avatarId =", self.avatarId)
                         print("\n\nself.avatarId =", self.avatarId)
                     print("\nself.neededObjects =", self.neededObjects)
                     print("\nself.neededObjects =", self.neededObjects)
                     print("\ntimed out after %s seconds.\n\n"%(task.delayTime,))
                     print("\ntimed out after %s seconds.\n\n"%(task.delayTime,))
-                    import pdb; pdb.set_trace()
+                    import pdb
+                    pdb.set_trace()
             self.delete()
             self.delete()
             return Task.done
             return Task.done
 
 

+ 7 - 4
direct/src/distributed/CRCache.py

@@ -1,8 +1,11 @@
 """CRCache module: contains the CRCache class"""
 """CRCache module: contains the CRCache class"""
 
 
 from direct.directnotify import DirectNotifyGlobal
 from direct.directnotify import DirectNotifyGlobal
+from direct.showbase.MessengerGlobal import messenger
+from direct.showbase.PythonUtil import safeRepr, itype
 from . import DistributedObject
 from . import DistributedObject
 
 
+
 class CRCache:
 class CRCache:
     notify = DirectNotifyGlobal.directNotify.newCategory("CRCache")
     notify = DirectNotifyGlobal.directNotify.newCategory("CRCache")
 
 
@@ -41,7 +44,7 @@ class CRCache:
         for distObj in delayDeleted:
         for distObj in delayDeleted:
             if distObj.getDelayDeleteCount() != 0:
             if distObj.getDelayDeleteCount() != 0:
                 delayDeleteLeaks.append(distObj)
                 delayDeleteLeaks.append(distObj)
-        if len(delayDeleteLeaks):
+        if len(delayDeleteLeaks) > 0:
             s = 'CRCache.flush:'
             s = 'CRCache.flush:'
             for obj in delayDeleteLeaks:
             for obj in delayDeleteLeaks:
                 s += ('\n  could not delete %s (%s), delayDeletes=%s' %
                 s += ('\n  could not delete %s (%s), delayDeletes=%s' %
@@ -76,7 +79,7 @@ class CRCache:
                 # if the cache is full, pop the oldest item
                 # if the cache is full, pop the oldest item
                 oldestDistObj = self.fifo.pop(0)
                 oldestDistObj = self.fifo.pop(0)
                 # and remove it from the dictionary
                 # and remove it from the dictionary
-                del(self.dict[oldestDistObj.getDoId()])
+                del self.dict[oldestDistObj.getDoId()]
                 # and delete it
                 # and delete it
                 oldestDistObj.deleteOrDelay()
                 oldestDistObj.deleteOrDelay()
                 if oldestDistObj.getDelayDeleteCount() <= 0:
                 if oldestDistObj.getDelayDeleteCount() <= 0:
@@ -93,7 +96,7 @@ class CRCache:
             # Find the object
             # Find the object
             distObj = self.dict[doId]
             distObj = self.dict[doId]
             # Remove it from the dictionary
             # Remove it from the dictionary
-            del(self.dict[doId])
+            del self.dict[doId]
             # Remove it from the fifo
             # Remove it from the fifo
             self.fifo.remove(distObj)
             self.fifo.remove(distObj)
             # return the distObj
             # return the distObj
@@ -111,7 +114,7 @@ class CRCache:
         # Look it up
         # Look it up
         distObj = self.dict[doId]
         distObj = self.dict[doId]
         # Remove it from the dict and fifo
         # Remove it from the dict and fifo
-        del(self.dict[doId])
+        del self.dict[doId]
         self.fifo.remove(distObj)
         self.fifo.remove(distObj)
         # and delete it
         # and delete it
         distObj.deleteOrDelay()
         distObj.deleteOrDelay()

+ 0 - 1
direct/src/distributed/CRDataCache.py

@@ -114,4 +114,3 @@ if __debug__:
     dc._stopMemLeakCheck()
     dc._stopMemLeakCheck()
     dc.destroy()
     dc.destroy()
     del dc
     del dc
-

+ 4 - 4
direct/src/distributed/CartesianGridBase.py

@@ -29,8 +29,8 @@ class CartesianGridBase:
         # Compute which zone we are in
         # Compute which zone we are in
         zoneId = int(self.startingZone + ((row * self.gridSize) + col))
         zoneId = int(self.startingZone + ((row * self.gridSize) + col))
 
 
-        if (wantRowAndCol):
-            return (zoneId,col,row)
+        if wantRowAndCol:
+            return (zoneId, col, row)
         else:
         else:
             return zoneId
             return zoneId
 
 
@@ -113,9 +113,9 @@ class CartesianGridBase:
             else:
             else:
                 # in a middle column, only look at top and bottom rows
                 # in a middle column, only look at top and bottom rows
                 possibleRows = []
                 possibleRows = []
-                if (topOffset == radius):
+                if topOffset == radius:
                     possibleRows.append(0)
                     possibleRows.append(0)
-                if (bottomOffset == radius):
+                if bottomOffset == radius:
                     possibleRows.append(bottomOffset + topOffset)
                     possibleRows.append(bottomOffset + topOffset)
             #print "on column %s and looking at rows %s"%(currCol,possibleRows)
             #print "on column %s and looking at rows %s"%(currCol,possibleRows)
             for currRow in possibleRows:
             for currRow in possibleRows:

+ 9 - 8
direct/src/distributed/ClientRepository.py

@@ -2,10 +2,11 @@
 
 
 from .ClientRepositoryBase import ClientRepositoryBase
 from .ClientRepositoryBase import ClientRepositoryBase
 from direct.directnotify import DirectNotifyGlobal
 from direct.directnotify import DirectNotifyGlobal
+from direct.showbase.MessengerGlobal import messenger
 from .MsgTypesCMU import *
 from .MsgTypesCMU import *
 from .PyDatagram import PyDatagram
 from .PyDatagram import PyDatagram
 from .PyDatagramIterator import PyDatagramIterator
 from .PyDatagramIterator import PyDatagramIterator
-from panda3d.core import UniqueIdAllocator
+from panda3d.core import UniqueIdAllocator, Notify
 
 
 
 
 class ClientRepository(ClientRepositoryBase):
 class ClientRepository(ClientRepositoryBase):
@@ -68,7 +69,7 @@ class ClientRepository(ClientRepositoryBase):
         zone = di.getUint32()
         zone = di.getUint32()
         for obj in self.doId2do.values():
         for obj in self.doId2do.values():
             if obj.zoneId == zone:
             if obj.zoneId == zone:
-                if (self.isLocalId(obj.doId)):
+                if self.isLocalId(obj.doId):
                     self.resendGenerate(obj)
                     self.resendGenerate(obj)
 
 
     def resendGenerate(self, obj):
     def resendGenerate(self, obj):
@@ -114,12 +115,12 @@ class ClientRepository(ClientRepositoryBase):
             # repeat-generate, synthesized for the benefit of someone
             # repeat-generate, synthesized for the benefit of someone
             # else who just entered the zone.  Accept the new updates,
             # else who just entered the zone.  Accept the new updates,
             # but don't make a formal generate.
             # but don't make a formal generate.
-            assert(self.notify.debug("performing generate-update for %s %s" % (dclass.getName(), doId)))
+            assert self.notify.debug("performing generate-update for %s %s" % (dclass.getName(), doId))
             dclass.receiveUpdateBroadcastRequired(distObj, di)
             dclass.receiveUpdateBroadcastRequired(distObj, di)
             dclass.receiveUpdateOther(distObj, di)
             dclass.receiveUpdateOther(distObj, di)
             return
             return
 
 
-        assert(self.notify.debug("performing generate for %s %s" % (dclass.getName(), doId)))
+        assert self.notify.debug("performing generate for %s %s" % (dclass.getName(), doId))
         dclass.startGenerate()
         dclass.startGenerate()
         # Create a new distributed object, and put it in the dictionary
         # Create a new distributed object, and put it in the dictionary
         distObj = self.generateWithRequiredOtherFields(dclass, doId, di, 0, zoneId)
         distObj = self.generateWithRequiredOtherFields(dclass, doId, di, 0, zoneId)
@@ -200,7 +201,7 @@ class ClientRepository(ClientRepositoryBase):
         if not dclass:
         if not dclass:
             self.notify.error("Unknown distributed class: %s" % (distObj.__class__))
             self.notify.error("Unknown distributed class: %s" % (distObj.__class__))
         classDef = dclass.getClassDef()
         classDef = dclass.getClassDef()
-        if classDef == None:
+        if classDef is None:
             self.notify.error("Could not create an undefined %s object." % (
             self.notify.error("Could not create an undefined %s object." % (
                 dclass.getName()))
                 dclass.getName()))
 
 
@@ -289,13 +290,13 @@ class ClientRepository(ClientRepositoryBase):
         """ Returns true if this doId is one that we're the owner of,
         """ Returns true if this doId is one that we're the owner of,
         false otherwise. """
         false otherwise. """
 
 
-        return ((doId >= self.doIdBase) and (doId < self.doIdLast))
+        return doId >= self.doIdBase and doId < self.doIdLast
 
 
     def haveCreateAuthority(self):
     def haveCreateAuthority(self):
         """ Returns true if this client has been assigned a range of
         """ Returns true if this client has been assigned a range of
         doId's it may use to create objects, false otherwise. """
         doId's it may use to create objects, false otherwise. """
 
 
-        return (self.doIdLast > self.doIdBase)
+        return self.doIdLast > self.doIdBase
 
 
     def getAvatarIdFromSender(self):
     def getAvatarIdFromSender(self):
         """ Returns the doIdBase of the client that originally sent
         """ Returns the doIdBase of the client that originally sent
@@ -306,7 +307,7 @@ class ClientRepository(ClientRepositoryBase):
     def handleDatagram(self, di):
     def handleDatagram(self, di):
         if self.notify.getDebug():
         if self.notify.getDebug():
             print("ClientRepository received datagram:")
             print("ClientRepository received datagram:")
-            di.getDatagram().dumpHex(ostream)
+            di.getDatagram().dumpHex(Notify.out())
 
 
         msgType = self.getMsgType()
         msgType = self.getMsgType()
         self.currentSenderId = None
         self.currentSenderId = None

+ 18 - 16
direct/src/distributed/ClientRepositoryBase.py

@@ -1,16 +1,18 @@
 from panda3d.core import *
 from panda3d.core import *
 from panda3d.direct import *
 from panda3d.direct import *
-from .MsgTypes import *
 from direct.task import Task
 from direct.task import Task
+from direct.task.TaskManagerGlobal import taskMgr
 from direct.directnotify import DirectNotifyGlobal
 from direct.directnotify import DirectNotifyGlobal
-from . import CRCache
 from direct.distributed.CRDataCache import CRDataCache
 from direct.distributed.CRDataCache import CRDataCache
 from direct.distributed.ConnectionRepository import ConnectionRepository
 from direct.distributed.ConnectionRepository import ConnectionRepository
-from direct.showbase import PythonUtil
+from direct.showbase.PythonUtil import safeRepr, itype, makeList
+from direct.showbase.MessengerGlobal import messenger
+from .MsgTypes import *
+from . import CRCache
 from . import ParentMgr
 from . import ParentMgr
 from . import RelatedObjectMgr
 from . import RelatedObjectMgr
-import time
 from .ClockDelta import *
 from .ClockDelta import *
+import time
 
 
 
 
 class ClientRepositoryBase(ConnectionRepository):
 class ClientRepositoryBase(ConnectionRepository):
@@ -31,7 +33,7 @@ class ClientRepositoryBase(ConnectionRepository):
         ConnectionRepository.__init__(self, connectMethod, base.config, hasOwnerView = True, threadedNet = threadedNet)
         ConnectionRepository.__init__(self, connectMethod, base.config, hasOwnerView = True, threadedNet = threadedNet)
         self.dcSuffix = dcSuffix
         self.dcSuffix = dcSuffix
         if hasattr(self, 'setVerbose'):
         if hasattr(self, 'setVerbose'):
-            if self.config.GetBool('verbose-clientrepository'):
+            if ConfigVariableBool('verbose-clientrepository', False):
                 self.setVerbose(1)
                 self.setVerbose(1)
 
 
         self.context=100000
         self.context=100000
@@ -40,7 +42,7 @@ class ClientRepositoryBase(ConnectionRepository):
         self.deferredGenerates = []
         self.deferredGenerates = []
         self.deferredDoIds = {}
         self.deferredDoIds = {}
         self.lastGenerate = 0
         self.lastGenerate = 0
-        self.setDeferInterval(base.config.GetDouble('deferred-generate-interval', 0.2))
+        self.setDeferInterval(ConfigVariableDouble('deferred-generate-interval', 0.2).value)
         self.noDefer = False  # Set this True to temporarily disable deferring.
         self.noDefer = False  # Set this True to temporarily disable deferring.
 
 
         self.recorder = base.recorder
         self.recorder = base.recorder
@@ -67,7 +69,7 @@ class ClientRepositoryBase(ConnectionRepository):
 
 
         # Keep track of how recently we last sent a heartbeat message.
         # Keep track of how recently we last sent a heartbeat message.
         # We want to keep these coming at heartbeatInterval seconds.
         # We want to keep these coming at heartbeatInterval seconds.
-        self.heartbeatInterval = base.config.GetDouble('heartbeat-interval', 10)
+        self.heartbeatInterval = ConfigVariableDouble('heartbeat-interval', 10).value
         self.heartbeatStarted = 0
         self.heartbeatStarted = 0
         self.lastHeartbeat = 0
         self.lastHeartbeat = 0
 
 
@@ -155,7 +157,7 @@ class ClientRepositoryBase(ConnectionRepository):
         # Look up the dclass
         # Look up the dclass
         assert parentId == self.GameGlobalsId or parentId in self.doId2do
         assert parentId == self.GameGlobalsId or parentId in self.doId2do
         dclass = self.dclassesByNumber[classId]
         dclass = self.dclassesByNumber[classId]
-        assert(self.notify.debug("performing generate for %s %s" % (dclass.getName(), doId)))
+        assert self.notify.debug("performing generate for %s %s" % (dclass.getName(), doId))
         dclass.startGenerate()
         dclass.startGenerate()
         # Create a new distributed object, and put it in the dictionary
         # Create a new distributed object, and put it in the dictionary
         distObj = self.generateWithRequiredOtherFields(dclass, doId, di, parentId, zoneId)
         distObj = self.generateWithRequiredOtherFields(dclass, doId, di, parentId, zoneId)
@@ -189,7 +191,7 @@ class ClientRepositoryBase(ConnectionRepository):
                 for dg, di in updates:
                 for dg, di in updates:
                     # non-DC updates that need to be played back in-order are
                     # non-DC updates that need to be played back in-order are
                     # stored as (msgType, (dg, di))
                     # stored as (msgType, (dg, di))
-                    if type(di) is tuple:
+                    if isinstance(di, tuple):
                         msgType = dg
                         msgType = dg
                         dg, di = di
                         dg, di = di
                         self.replayDeferredGenerate(msgType, (dg, di))
                         self.replayDeferredGenerate(msgType, (dg, di))
@@ -248,7 +250,7 @@ class ClientRepositoryBase(ConnectionRepository):
             # ...it is not in the dictionary or the cache.
             # ...it is not in the dictionary or the cache.
             # Construct a new one
             # Construct a new one
             classDef = dclass.getClassDef()
             classDef = dclass.getClassDef()
-            if classDef == None:
+            if classDef is None:
                 self.notify.error("Could not create an undefined %s object." % (dclass.getName()))
                 self.notify.error("Could not create an undefined %s object." % (dclass.getName()))
             distObj = classDef(self)
             distObj = classDef(self)
             distObj.dclass = dclass
             distObj.dclass = dclass
@@ -296,7 +298,7 @@ class ClientRepositoryBase(ConnectionRepository):
             # ...it is not in the dictionary or the cache.
             # ...it is not in the dictionary or the cache.
             # Construct a new one
             # Construct a new one
             classDef = dclass.getClassDef()
             classDef = dclass.getClassDef()
-            if classDef == None:
+            if classDef is None:
                 self.notify.error("Could not create an undefined %s object." % (dclass.getName()))
                 self.notify.error("Could not create an undefined %s object." % (dclass.getName()))
             distObj = classDef(self)
             distObj = classDef(self)
             distObj.dclass = dclass
             distObj.dclass = dclass
@@ -339,7 +341,7 @@ class ClientRepositoryBase(ConnectionRepository):
             # ...it is not in the dictionary or the cache.
             # ...it is not in the dictionary or the cache.
             # Construct a new one
             # Construct a new one
             classDef = dclass.getOwnerClassDef()
             classDef = dclass.getOwnerClassDef()
-            if classDef == None:
+            if classDef is None:
                 self.notify.error("Could not create an undefined %s object. Have you created an owner view?" % (dclass.getName()))
                 self.notify.error("Could not create an undefined %s object. Have you created an owner view?" % (dclass.getName()))
             distObj = classDef(self)
             distObj = classDef(self)
             distObj.dclass = dclass
             distObj.dclass = dclass
@@ -452,7 +454,7 @@ class ClientRepositoryBase(ConnectionRepository):
             # a dict and adding the avatar handles to that dict when they are created
             # a dict and adding the avatar handles to that dict when they are created
             # then change/remove the old method. I didn't do that because I couldn't think
             # then change/remove the old method. I didn't do that because I couldn't think
             # of a use for it. -JML
             # of a use for it. -JML
-            try :
+            try:
                 handle = self.identifyAvatar(doId)
                 handle = self.identifyAvatar(doId)
                 if handle:
                 if handle:
                     dclass = self.dclassesByName[handle.dclassName]
                     dclass = self.dclassesByName[handle.dclassName]
@@ -477,7 +479,7 @@ class ClientRepositoryBase(ConnectionRepository):
     def handleGoGetLost(self, di):
     def handleGoGetLost(self, di):
         # The server told us it's about to drop the connection on us.
         # The server told us it's about to drop the connection on us.
         # Get ready!
         # Get ready!
-        if (di.getRemainingSize() > 0):
+        if di.getRemainingSize() > 0:
             self.bootedIndex = di.getUint16()
             self.bootedIndex = di.getUint16()
             self.bootedText = di.getString()
             self.bootedText = di.getString()
 
 
@@ -495,7 +497,7 @@ class ClientRepositoryBase(ConnectionRepository):
 
 
     def handleServerHeartbeat(self, di):
     def handleServerHeartbeat(self, di):
         # Got a heartbeat message from the server.
         # Got a heartbeat message from the server.
-        if base.config.GetBool('server-heartbeat-info', 1):
+        if ConfigVariableBool('server-heartbeat-info', True):
             self.notify.info("Server heartbeat.")
             self.notify.info("Server heartbeat.")
 
 
     def handleSystemMessage(self, di):
     def handleSystemMessage(self, di):
@@ -579,7 +581,7 @@ class ClientRepositoryBase(ConnectionRepository):
         return worldNP
         return worldNP
 
 
     def isLive(self):
     def isLive(self):
-        if base.config.GetBool('force-live', 0):
+        if ConfigVariableBool('force-live', False):
             return True
             return True
         return not (__dev__ or launcher.isTestServer())
         return not (__dev__ or launcher.isTestServer())
 
 

+ 4 - 4
direct/src/distributed/ClockDelta.py

@@ -78,7 +78,7 @@ class ClockDelta(DirectObject.DirectObject):
         # representing infinite uncertainty, if we have never received
         # representing infinite uncertainty, if we have never received
         # a time measurement.
         # a time measurement.
 
 
-        if self.uncertainty == None:
+        if self.uncertainty is None:
             return None
             return None
 
 
         now = self.globalClock.getRealTime()
         now = self.globalClock.getRealTime()
@@ -190,7 +190,7 @@ class ClockDelta(DirectObject.DirectObject):
         the new measurement was used, false if it was discarded.
         the new measurement was used, false if it was discarded.
         """
         """
         oldUncertainty = self.getUncertainty()
         oldUncertainty = self.getUncertainty()
-        if oldUncertainty != None:
+        if oldUncertainty is not None:
             self.notify.info(
             self.notify.info(
                 'previous delta at %.3f s, +/- %.3f s.' %
                 'previous delta at %.3f s, +/- %.3f s.' %
                 (self.delta, oldUncertainty))
                 (self.delta, oldUncertainty))
@@ -241,14 +241,14 @@ class ClockDelta(DirectObject.DirectObject):
         minutes of the current local time given in now, or
         minutes of the current local time given in now, or
         getRealTime() if now is not specified.
         getRealTime() if now is not specified.
         """
         """
-        if now == None:
+        if now is None:
             now = self.globalClock.getRealTime()
             now = self.globalClock.getRealTime()
 
 
         # Are we in non-real-time mode (i.e. filming a movie)?  If you
         # Are we in non-real-time mode (i.e. filming a movie)?  If you
         # set movie-network-time 1, then we'll circumvent this logic
         # set movie-network-time 1, then we'll circumvent this logic
         # and always return now.
         # and always return now.
         if self.globalClock.getMode() == ClockObject.MNonRealTime and \
         if self.globalClock.getMode() == ClockObject.MNonRealTime and \
-           base.config.GetBool('movie-network-time', False):
+           ConfigVariableBool('movie-network-time', False):
             return now
             return now
 
 
         # First, determine what network time we have for 'now'.
         # First, determine what network time we have for 'now'.

+ 9 - 7
direct/src/distributed/ConnectionRepository.py

@@ -1,10 +1,12 @@
 from panda3d.core import *
 from panda3d.core import *
 from panda3d.direct import *
 from panda3d.direct import *
 from direct.task import Task
 from direct.task import Task
+from direct.task.TaskManagerGlobal import taskMgr
 from direct.directnotify.DirectNotifyGlobal import directNotify
 from direct.directnotify.DirectNotifyGlobal import directNotify
 from direct.distributed.DoInterestManager import DoInterestManager
 from direct.distributed.DoInterestManager import DoInterestManager
 from direct.distributed.DoCollectionManager import DoCollectionManager
 from direct.distributed.DoCollectionManager import DoCollectionManager
 from direct.showbase import GarbageReport
 from direct.showbase import GarbageReport
+from direct.showbase.MessengerGlobal import messenger
 from .PyDatagramIterator import PyDatagramIterator
 from .PyDatagramIterator import PyDatagramIterator
 
 
 import inspect
 import inspect
@@ -175,7 +177,7 @@ class ConnectionRepository(
         def applyFieldValues(distObj, dclass, values):
         def applyFieldValues(distObj, dclass, values):
             for i in range(dclass.getNumInheritedFields()):
             for i in range(dclass.getNumInheritedFields()):
                 field = dclass.getInheritedField(i)
                 field = dclass.getInheritedField(i)
-                if field.asMolecularField() == None:
+                if field.asMolecularField() is None:
                     value = values.get(field.getName(), None)
                     value = values.get(field.getName(), None)
                     if value is None and field.isRequired():
                     if value is None and field.isRequired():
                         # Gee, this could be better.  What would really be
                         # Gee, this could be better.  What would really be
@@ -214,7 +216,7 @@ class ConnectionRepository(
 
 
         # Construct a new one
         # Construct a new one
         classDef = dclass.getClassDef()
         classDef = dclass.getClassDef()
-        if classDef == None:
+        if classDef is None:
             self.notify.error("Could not create an undefined %s object."%(
             self.notify.error("Could not create an undefined %s object."%(
                 dclass.getName()))
                 dclass.getName()))
         distObj = classDef(self)
         distObj = classDef(self)
@@ -252,7 +254,7 @@ class ConnectionRepository(
             dcFileNames = [dcFileNames]
             dcFileNames = [dcFileNames]
 
 
         dcImports = {}
         dcImports = {}
-        if dcFileNames == None:
+        if dcFileNames is None:
             readResult = dcFile.readAll()
             readResult = dcFile.readAll()
             if not readResult:
             if not readResult:
                 self.notify.error("Could not read dc file.")
                 self.notify.error("Could not read dc file.")
@@ -322,7 +324,7 @@ class ConnectionRepository(
                 classDef = dcImports.get(className)
                 classDef = dcImports.get(className)
 
 
             # Also try it without the dcSuffix.
             # Also try it without the dcSuffix.
-            if classDef == None:
+            if classDef is None:
                 className = dclass.getName()
                 className = dclass.getName()
                 classDef = dcImports.get(className)
                 classDef = dcImports.get(className)
             if classDef is None:
             if classDef is None:
@@ -379,7 +381,7 @@ class ConnectionRepository(
             # in the DC file.
             # in the DC file.
             for i in range(dcFile.getNumClasses()):
             for i in range(dcFile.getNumClasses()):
                 dclass = dcFile.getClass(i)
                 dclass = dcFile.getClass(i)
-                if ((dclass.getName()+ownerDcSuffix) in ownerImportSymbols):
+                if (dclass.getName()+ownerDcSuffix) in ownerImportSymbols:
                     number = dclass.getNumber()
                     number = dclass.getNumber()
                     className = dclass.getName() + ownerDcSuffix
                     className = dclass.getName() + ownerDcSuffix
 
 
@@ -583,7 +585,7 @@ class ConnectionRepository(
         # available.  Returns the HTTPClient (also self.http), or None
         # available.  Returns the HTTPClient (also self.http), or None
         # if not set.
         # if not set.
 
 
-        if self.http == None:
+        if self.http is None:
             try:
             try:
                 self.http = HTTPClient()
                 self.http = HTTPClient()
             except:
             except:
@@ -662,7 +664,7 @@ class ConnectionRepository(
             self.setSimulatedDisconnect(0)
             self.setSimulatedDisconnect(0)
 
 
     def uniqueName(self, idString):
     def uniqueName(self, idString):
-        return ("%s-%s" % (idString, self.uniqueId))
+        return "%s-%s" % (idString, self.uniqueId)
 
 
 class GCTrigger:
 class GCTrigger:
     # used to trigger garbage collection
     # used to trigger garbage collection

+ 13 - 10
direct/src/distributed/DistributedCamera.py

@@ -4,6 +4,11 @@ from direct.fsm.FSM import FSM
 from direct.interval.IntervalGlobal import *
 from direct.interval.IntervalGlobal import *
 from direct.distributed.DistributedObject import DistributedObject
 from direct.distributed.DistributedObject import DistributedObject
 
 
+
+_camera_id = ConfigVariableInt('camera-id', -1)
+_aware_of_cameras = ConfigVariableInt('aware-of-cameras', 0)
+
+
 class Fixture(NodePath, FSM):
 class Fixture(NodePath, FSM):
     def __init__(self, id, parent, pos, hpr, fov):
     def __init__(self, id, parent, pos, hpr, fov):
         NodePath.__init__(self, 'cam-%s' % id)
         NodePath.__init__(self, 'cam-%s' % id)
@@ -12,7 +17,7 @@ class Fixture(NodePath, FSM):
         self.lens = PerspectiveLens()
         self.lens = PerspectiveLens()
         self.lens.setFov(base.camLens.getFov())
         self.lens.setFov(base.camLens.getFov())
 
 
-        model = loader.loadModel('models/misc/camera', okMissing = True)
+        model = base.loader.loadModel('models/misc/camera', okMissing = True)
         model.reparentTo(self)
         model.reparentTo(self)
 
 
         self.reparentTo(parent)
         self.reparentTo(parent)
@@ -60,15 +65,13 @@ class Fixture(NodePath, FSM):
 
 
     def setRecordingInProgress(self, inProgress):
     def setRecordingInProgress(self, inProgress):
         self.recordingInProgress = inProgress
         self.recordingInProgress = inProgress
-        if self.recordingInProgress and \
-           base.config.GetInt('camera-id', -1) >= 0:
+        if self.recordingInProgress and _camera_id.value >= 0:
             self.hide()
             self.hide()
         else:
         else:
             self.show()
             self.show()
 
 
     def show(self):
     def show(self):
-        if base.config.GetBool('aware-of-cameras',0) and \
-           not self.recordingInProgress:
+        if _aware_of_cameras and not self.recordingInProgress:
             NodePath.show(self)
             NodePath.show(self)
 
 
     def getScaleIval(self):
     def getScaleIval(self):
@@ -99,7 +102,7 @@ class Fixture(NodePath, FSM):
 
 
     def enterStandby(self):
     def enterStandby(self):
         self.show()
         self.show()
-        if self.id == base.config.GetInt('camera-id', -1):
+        if self.id == _camera_id.value:
             self.setColorScale(3,0,0,1)
             self.setColorScale(3,0,0,1)
             self.getScaleIval().loop()
             self.getScaleIval().loop()
         else:
         else:
@@ -116,7 +119,7 @@ class Fixture(NodePath, FSM):
             self.scaleIval.finish()
             self.scaleIval.finish()
 
 
     def enterRecording(self):
     def enterRecording(self):
-        if base.config.GetInt('camera-id', -1) == self.id:
+        if _camera_id.value == self.id:
             self.demand('Using')
             self.demand('Using')
         else:
         else:
             self.show()
             self.show()
@@ -129,8 +132,8 @@ class Fixture(NodePath, FSM):
 
 
     def enterUsing(self, args = []):
     def enterUsing(self, args = []):
         localAvatar.b_setGameState('Camera')
         localAvatar.b_setGameState('Camera')
-        camera.setPosHpr(0,0,0,0,0,0)
-        camera.reparentTo(self)
+        base.camera.setPosHpr(0,0,0,0,0,0)
+        base.camera.reparentTo(self)
         self.hide()
         self.hide()
 
 
         base.cam.node().setLens(self.lens)
         base.cam.node().setLens(self.lens)
@@ -177,7 +180,7 @@ class DistributedCamera(DistributedObject):
         DistributedObject.__init__(self, cr)
         DistributedObject.__init__(self, cr)
         self.parent = None
         self.parent = None
         self.fixtures = {}
         self.fixtures = {}
-        self.cameraId = base.config.GetInt('camera-id',0)
+        self.cameraId = _camera_id.value
 
 
     def __getitem__(self, index):
     def __getitem__(self, index):
         return self.fixtures.get(index)
         return self.fixtures.get(index)

+ 2 - 2
direct/src/distributed/DistributedCameraOV.py

@@ -24,7 +24,7 @@ class DistributedCameraOV(DistributedObjectOV):
         self.fixtures = fixtures
         self.fixtures = fixtures
 
 
     def storeToFile(self, name):
     def storeToFile(self, name):
-        f = file('cameras-%s.txt' % name, 'w')
+        f = open('cameras-%s.txt' % name, 'w')
         f.writelines(self.getObject().pack())
         f.writelines(self.getObject().pack())
         f.close()
         f.close()
 
 
@@ -35,7 +35,7 @@ class DistributedCameraOV(DistributedObjectOV):
 
 
     def loadFromFile(self, name):
     def loadFromFile(self, name):
         self.b_setFixtures([])
         self.b_setFixtures([])
-        f = file('cameras-%s.txt' % name, 'r');
+        f = open('cameras-%s.txt' % name, 'r')
         for line in f.readlines():
         for line in f.readlines():
             pos,hpr,fov = self.unpackFixture(line)
             pos,hpr,fov = self.unpackFixture(line)
             self.addFixture([pos[0],pos[1],pos[2],
             self.addFixture([pos[0],pos[1],pos[2],

+ 9 - 9
direct/src/distributed/DistributedCartesianGrid.py

@@ -81,7 +81,7 @@ class DistributedCartesianGrid(DistributedNode, CartesianGridBase):
 
 
     def handleChildArrive(self, child, zoneId):
     def handleChildArrive(self, child, zoneId):
         DistributedNode.handleChildArrive(self, child, zoneId)
         DistributedNode.handleChildArrive(self, child, zoneId)
-        if (zoneId >= self.startingZone):
+        if zoneId >= self.startingZone:
             if not child.gridParent:
             if not child.gridParent:
                 child.gridParent = GridParent(child)
                 child.gridParent = GridParent(child)
             child.gridParent.setGridParent(self, zoneId)
             child.gridParent.setGridParent(self, zoneId)
@@ -91,7 +91,7 @@ class DistributedCartesianGrid(DistributedNode, CartesianGridBase):
 
 
     def handleChildArriveZone(self, child, zoneId):
     def handleChildArriveZone(self, child, zoneId):
         DistributedNode.handleChildArrive(self, child, zoneId)
         DistributedNode.handleChildArrive(self, child, zoneId)
-        if (zoneId >= self.startingZone):
+        if zoneId >= self.startingZone:
             if not child.gridParent:
             if not child.gridParent:
                 child.gridParent = GridParent(child)
                 child.gridParent = GridParent(child)
             child.gridParent.setGridParent(self, zoneId)
             child.gridParent.setGridParent(self, zoneId)
@@ -150,21 +150,21 @@ class DistributedCartesianGrid(DistributedNode, CartesianGridBase):
 
 
         # sometimes we also need to remove vis avatar from
         # sometimes we also need to remove vis avatar from
         # my parent if it is also a grid
         # my parent if it is also a grid
-        if (clearAll):
+        if clearAll:
             if event is not None:
             if event is not None:
                 parentEvent = eventGroup.newEvent('%s.parent.removeInterest' % self.doId)
                 parentEvent = eventGroup.newEvent('%s.parent.removeInterest' % self.doId)
             else:
             else:
                 parentEvent = None
                 parentEvent = None
 
 
             ##HACK BANDAID FOR PVP INSTANCES
             ##HACK BANDAID FOR PVP INSTANCES
-            if(hasattr(self.cr.doId2do[self.parentId],"worldGrid")):
+            if hasattr(self.cr.doId2do[self.parentId], "worldGrid"):
                 self.cr.doId2do[self.parentId].worldGrid.stopProcessVisibility(event=parentEvent)
                 self.cr.doId2do[self.parentId].worldGrid.stopProcessVisibility(event=parentEvent)
 
 
     def processVisibility(self, task):
     def processVisibility(self, task):
-        if self.visAvatar == None:
+        if self.visAvatar is None:
             # no avatar to process visibility for
             # no avatar to process visibility for
             return Task.done
             return Task.done
-        if(self.visAvatar.isDisabled()):
+        if self.visAvatar.isDisabled():
             self.visAvatar = None
             self.visAvatar = None
             return Task.done
             return Task.done
         if self.visAvatar.gameFSM.state == 'Cutscene':
         if self.visAvatar.gameFSM.state == 'Cutscene':
@@ -192,7 +192,7 @@ class DistributedCartesianGrid(DistributedNode, CartesianGridBase):
         zoneId = int(self.startingZone + ((row * self.gridSize) + col))
         zoneId = int(self.startingZone + ((row * self.gridSize) + col))
         assert self.notify.debug("processVisibility: %s: row: %s col: %s zoneId: %s" %
         assert self.notify.debug("processVisibility: %s: row: %s col: %s zoneId: %s" %
                                  (self.doId, row, col, zoneId))
                                  (self.doId, row, col, zoneId))
-        if (zoneId == self.visZone):
+        if zoneId == self.visZone:
             assert self.notify.debug(
             assert self.notify.debug(
                 "processVisibility: %s: interest did not change" % (self.doId))
                 "processVisibility: %s: interest did not change" % (self.doId))
             if self.visDirty:
             if self.visDirty:
@@ -314,7 +314,7 @@ class DistributedCartesianGrid(DistributedNode, CartesianGridBase):
 
 
             # Load up grid parts to initialize grid object
             # Load up grid parts to initialize grid object
             # Polygon used to mark grid plane
             # Polygon used to mark grid plane
-            # self.gridBack = loader.loadModel('models/misc/gridBack')
+            # self.gridBack = base.loader.loadModel('models/misc/gridBack')
             # self.gridBack.reparentTo(self)
             # self.gridBack.reparentTo(self)
             # self.gridBack.setColor(0.2, 0.2, 0.2, 0.5)
             # self.gridBack.setColor(0.2, 0.2, 0.2, 0.5)
 
 
@@ -397,7 +397,7 @@ class DistributedCartesianGrid(DistributedNode, CartesianGridBase):
             dx = self.cellWidth * self.gridSize * .5
             dx = self.cellWidth * self.gridSize * .5
             for i in range(self.gridSize):
             for i in range(self.gridSize):
                 for j in range(self.gridSize):
                 for j in range(self.gridSize):
-                    marker = loader.loadModel("models/misc/smiley")
+                    marker = base.loader.loadModel("models/misc/smiley")
                     marker.reparentTo(self.markerParent)
                     marker.reparentTo(self.markerParent)
                     marker.setPos(i * self.cellWidth - dx,
                     marker.setPos(i * self.cellWidth - dx,
                                   j * self.cellWidth - dx,
                                   j * self.cellWidth - dx,

+ 9 - 9
direct/src/distributed/DistributedCartesianGridAI.py

@@ -3,6 +3,7 @@ from panda3d.core import *
 from panda3d.direct import *
 from panda3d.direct import *
 from direct.directnotify.DirectNotifyGlobal import directNotify
 from direct.directnotify.DirectNotifyGlobal import directNotify
 from direct.task import Task
 from direct.task import Task
+from direct.task.TaskManagerGlobal import taskMgr
 from .DistributedNodeAI import DistributedNodeAI
 from .DistributedNodeAI import DistributedNodeAI
 from .CartesianGridBase import CartesianGridBase
 from .CartesianGridBase import CartesianGridBase
 
 
@@ -56,7 +57,7 @@ class DistributedCartesianGridAI(DistributedNodeAI, CartesianGridBase):
         # Put the avatar on the grid
         # Put the avatar on the grid
         self.handleAvatarZoneChange(av, useZoneId)
         self.handleAvatarZoneChange(av, useZoneId)
 
 
-        if (not self.updateTaskStarted) and startAutoUpdate:
+        if startAutoUpdate and not self.updateTaskStarted:
             self.startUpdateGridTask()
             self.startUpdateGridTask()
 
 
     def removeObjectFromGrid(self, av):
     def removeObjectFromGrid(self, av):
@@ -97,27 +98,27 @@ class DistributedCartesianGridAI(DistributedNodeAI, CartesianGridBase):
     def updateGridTask(self, task=None):
     def updateGridTask(self, task=None):
         # Run through all grid objects and update their parents if needed
         # Run through all grid objects and update their parents if needed
         missingObjs = []
         missingObjs = []
-        for avId in self.gridObjects.keys():
+        for avId in list(self.gridObjects.keys()):
             av = self.gridObjects[avId]
             av = self.gridObjects[avId]
             # handle a missing object after it is already gone?
             # handle a missing object after it is already gone?
-            if (av.isEmpty()):
+            if av.isEmpty():
                 task.setDelay(1.0)
                 task.setDelay(1.0)
                 del self.gridObjects[avId]
                 del self.gridObjects[avId]
                 continue
                 continue
             pos = av.getPos()
             pos = av.getPos()
-            if ((pos[0] < 0 or pos[1] < 0) or
-                (pos[0] > self.cellWidth or pos[1] > self.cellWidth)):
+            if (pos[0] < 0 or pos[1] < 0) or \
+               (pos[0] > self.cellWidth or pos[1] > self.cellWidth):
                 # we are out of the bounds of this current cell
                 # we are out of the bounds of this current cell
                 self.handleAvatarZoneChange(av)
                 self.handleAvatarZoneChange(av)
         # Do this every second, not every frame
         # Do this every second, not every frame
-        if (task):
+        if task:
             task.setDelay(1.0)
             task.setDelay(1.0)
         return Task.again
         return Task.again
 
 
     def handleAvatarZoneChange(self, av, useZoneId=-1):
     def handleAvatarZoneChange(self, av, useZoneId=-1):
         # Calculate zone id
         # Calculate zone id
         # Get position of av relative to this grid
         # Get position of av relative to this grid
-        if (useZoneId == -1):
+        if useZoneId == -1:
             pos = av.getPos(self)
             pos = av.getPos(self)
             zoneId = self.getZoneFromXYZ(pos)
             zoneId = self.getZoneFromXYZ(pos)
         else:
         else:
@@ -137,9 +138,8 @@ class DistributedCartesianGridAI(DistributedNodeAI, CartesianGridBase):
 
 
     def handleSetLocation(self, av, parentId, zoneId):
     def handleSetLocation(self, av, parentId, zoneId):
         pass
         pass
-        #if (av.parentId != parentId):
+        #if av.parentId != parentId:
             # parent changed, need to look up instance tree
             # parent changed, need to look up instance tree
             # to see if avatar's named area location information
             # to see if avatar's named area location information
             # changed
             # changed
             #av.requestRegionUpdateTask(regionegionUid)
             #av.requestRegionUpdateTask(regionegionUid)
-

+ 4 - 8
direct/src/distributed/DistributedNode.py

@@ -9,9 +9,7 @@ class DistributedNode(DistributedObject.DistributedObject, NodePath):
     """Distributed Node class:"""
     """Distributed Node class:"""
 
 
     def __init__(self, cr):
     def __init__(self, cr):
-        try:
-            self.DistributedNode_initialized
-        except:
+        if not hasattr(self, 'DistributedNode_initialized'):
             self.DistributedNode_initialized = 1
             self.DistributedNode_initialized = 1
             self.gotStringParentToken = 0
             self.gotStringParentToken = 0
             DistributedObject.DistributedObject.__init__(self, cr)
             DistributedObject.DistributedObject.__init__(self, cr)
@@ -28,9 +26,7 @@ class DistributedNode(DistributedObject.DistributedObject, NodePath):
             DistributedObject.DistributedObject.disable(self)
             DistributedObject.DistributedObject.disable(self)
 
 
     def delete(self):
     def delete(self):
-        try:
-            self.DistributedNode_deleted
-        except:
+        if not hasattr(self, 'DistributedNode_deleted'):
             self.DistributedNode_deleted = 1
             self.DistributedNode_deleted = 1
             if not self.isEmpty():
             if not self.isEmpty():
                 self.removeNode()
                 self.removeNode()
@@ -78,7 +74,7 @@ class DistributedNode(DistributedObject.DistributedObject, NodePath):
     ### setParent ###
     ### setParent ###
 
 
     def b_setParent(self, parentToken):
     def b_setParent(self, parentToken):
-        if type(parentToken) == str:
+        if isinstance(parentToken, str):
             self.setParentStr(parentToken)
             self.setParentStr(parentToken)
         else:
         else:
             self.setParent(parentToken)
             self.setParent(parentToken)
@@ -86,7 +82,7 @@ class DistributedNode(DistributedObject.DistributedObject, NodePath):
         self.d_setParent(parentToken)
         self.d_setParent(parentToken)
 
 
     def d_setParent(self, parentToken):
     def d_setParent(self, parentToken):
-        if type(parentToken) == str:
+        if isinstance(parentToken, str):
             self.sendUpdate("setParentStr", [parentToken])
             self.sendUpdate("setParentStr", [parentToken])
         else:
         else:
             self.sendUpdate("setParent", [parentToken])
             self.sendUpdate("setParent", [parentToken])

+ 3 - 5
direct/src/distributed/DistributedNodeAI.py

@@ -6,9 +6,7 @@ from . import GridParent
 class DistributedNodeAI(DistributedObjectAI.DistributedObjectAI, NodePath):
 class DistributedNodeAI(DistributedObjectAI.DistributedObjectAI, NodePath):
     def __init__(self, air, name=None):
     def __init__(self, air, name=None):
         # Be careful not to create multiple NodePath objects
         # Be careful not to create multiple NodePath objects
-        try:
-            self.DistributedNodeAI_initialized
-        except:
+        if not hasattr(self, 'DistributedNodeAI_initialized'):
             self.DistributedNodeAI_initialized = 1
             self.DistributedNodeAI_initialized = 1
             DistributedObjectAI.DistributedObjectAI.__init__(self, air)
             DistributedObjectAI.DistributedObjectAI.__init__(self, air)
             if name is None:
             if name is None:
@@ -47,14 +45,14 @@ class DistributedNodeAI(DistributedObjectAI.DistributedObjectAI, NodePath):
     ### setParent ###
     ### setParent ###
 
 
     def b_setParent(self, parentToken):
     def b_setParent(self, parentToken):
-        if type(parentToken) == str:
+        if isinstance(parentToken, str):
             self.setParentStr(parentToken)
             self.setParentStr(parentToken)
         else:
         else:
             self.setParent(parentToken)
             self.setParent(parentToken)
         self.d_setParent(parentToken)
         self.d_setParent(parentToken)
 
 
     def d_setParent(self, parentToken):
     def d_setParent(self, parentToken):
-        if type(parentToken) == type(''):
+        if isinstance(parentToken, str):
             self.sendUpdate("setParentStr", [parentToken])
             self.sendUpdate("setParentStr", [parentToken])
         else:
         else:
             self.sendUpdate("setParent", [parentToken])
             self.sendUpdate("setParent", [parentToken])

+ 4 - 6
direct/src/distributed/DistributedNodeUD.py

@@ -3,30 +3,28 @@ from .DistributedObjectUD import DistributedObjectUD
 class DistributedNodeUD(DistributedObjectUD):
 class DistributedNodeUD(DistributedObjectUD):
     def __init__(self, air, name=None):
     def __init__(self, air, name=None):
         # Be careful not to create multiple NodePath objects
         # Be careful not to create multiple NodePath objects
-        try:
-            self.DistributedNodeUD_initialized
-        except:
+        if not hasattr(self, 'DistributedNodeUD_initialized'):
             self.DistributedNodeUD_initialized = 1
             self.DistributedNodeUD_initialized = 1
             DistributedObjectUD.__init__(self, air)
             DistributedObjectUD.__init__(self, air)
             if name is None:
             if name is None:
                 name = self.__class__.__name__
                 name = self.__class__.__name__
 
 
     def b_setParent(self, parentToken):
     def b_setParent(self, parentToken):
-        if type(parentToken) == str:
+        if isinstance(parentToken, str):
             self.setParentStr(parentToken)
             self.setParentStr(parentToken)
         else:
         else:
             self.setParent(parentToken)
             self.setParent(parentToken)
         self.d_setParent(parentToken)
         self.d_setParent(parentToken)
 
 
     def d_setParent(self, parentToken):
     def d_setParent(self, parentToken):
-        if type(parentToken) == type(''):
+        if isinstance(parentToken, str):
             self.sendUpdate("setParentStr", [parentToken])
             self.sendUpdate("setParentStr", [parentToken])
         else:
         else:
             self.sendUpdate("setParent", [parentToken])
             self.sendUpdate("setParent", [parentToken])
 
 
     def setParentStr(self, parentToken):
     def setParentStr(self, parentToken):
         self.notify.debugCall()
         self.notify.debugCall()
-        if len(parentTokenStr) > 0:
+        if len(parentToken) > 0:
             self.do_setParent(parentToken)
             self.do_setParent(parentToken)
 
 
     def setParent(self, parentToken):
     def setParent(self, parentToken):

+ 23 - 28
direct/src/distributed/DistributedObject.py

@@ -2,6 +2,7 @@
 
 
 from panda3d.core import *
 from panda3d.core import *
 from panda3d.direct import *
 from panda3d.direct import *
+from direct.showbase.MessengerGlobal import messenger
 from direct.directnotify.DirectNotifyGlobal import directNotify
 from direct.directnotify.DirectNotifyGlobal import directNotify
 from direct.distributed.DistributedObjectBase import DistributedObjectBase
 from direct.distributed.DistributedObjectBase import DistributedObjectBase
 #from PyDatagram import PyDatagram
 #from PyDatagram import PyDatagram
@@ -43,9 +44,7 @@ class DistributedObject(DistributedObjectBase):
 
 
     def __init__(self, cr):
     def __init__(self, cr):
         assert self.notify.debugStateCall(self)
         assert self.notify.debugStateCall(self)
-        try:
-            self.DistributedObject_initialized
-        except:
+        if not hasattr(self, 'DistributedObject_initialized'):
             self.DistributedObject_initialized = 1
             self.DistributedObject_initialized = 1
             DistributedObjectBase.__init__(self, cr)
             DistributedObjectBase.__init__(self, cr)
 
 
@@ -96,7 +95,7 @@ class DistributedObject(DistributedObjectBase):
                     flags.append("cacheable")
                     flags.append("cacheable")
 
 
                 flagStr = ""
                 flagStr = ""
-                if len(flags):
+                if len(flags) > 0:
                     flagStr = " (%s)" % (" ".join(flags))
                     flagStr = " (%s)" % (" ".join(flags))
 
 
                 print("%sfrom DistributedObject doId:%s, parent:%s, zone:%s%s" % (
                 print("%sfrom DistributedObject doId:%s, parent:%s, zone:%s%s" % (
@@ -125,8 +124,8 @@ class DistributedObject(DistributedObjectBase):
                     if field is not None:
                     if field is not None:
                         p = DCPacker()
                         p = DCPacker()
                         p.setUnpackData(field.getDefaultValue())
                         p.setUnpackData(field.getDefaultValue())
-                        len = p.rawUnpackUint16()/4
-                        for i in range(len):
+                        length = p.rawUnpackUint16() // 4
+                        for i in range(length):
                             zone = int(p.rawUnpackUint32())
                             zone = int(p.rawUnpackUint32())
                             autoInterests.add(zone)
                             autoInterests.add(zone)
                     autoInterests.update(autoInterests)
                     autoInterests.update(autoInterests)
@@ -142,9 +141,9 @@ class DistributedObject(DistributedObjectBase):
         _getAutoInterests = None
         _getAutoInterests = None
         return list(autoInterests)
         return list(autoInterests)
 
 
-    def setNeverDisable(self, bool):
-        assert bool == 1 or bool == 0
-        self.neverDisable = bool
+    def setNeverDisable(self, boolean):
+        assert boolean == 1 or boolean == 0
+        self.neverDisable = boolean
 
 
     def getNeverDisable(self):
     def getNeverDisable(self):
         return self.neverDisable
         return self.neverDisable
@@ -156,31 +155,31 @@ class DistributedObject(DistributedObjectBase):
             self._cachedData = self.cr.doDataCache.popCachedData(self.doId)
             self._cachedData = self.cr.doDataCache.popCachedData(self.doId)
 
 
     def setCachedData(self, name, data):
     def setCachedData(self, name, data):
-        assert type(name) == type('')
+        assert isinstance(name, str)
         # ownership of the data passes to the repository data cache
         # ownership of the data passes to the repository data cache
         self.cr.doDataCache.setCachedData(self.doId, name, data)
         self.cr.doDataCache.setCachedData(self.doId, name, data)
 
 
     def hasCachedData(self, name):
     def hasCachedData(self, name):
-        assert type(name) == type('')
+        assert isinstance(name, str)
         if not hasattr(self, '_cachedData'):
         if not hasattr(self, '_cachedData'):
             return False
             return False
         return name in self._cachedData
         return name in self._cachedData
 
 
     def getCachedData(self, name):
     def getCachedData(self, name):
-        assert type(name) == type('')
+        assert isinstance(name, str)
         # ownership of the data passes to the caller of this method
         # ownership of the data passes to the caller of this method
         data = self._cachedData[name]
         data = self._cachedData[name]
         del self._cachedData[name]
         del self._cachedData[name]
         return data
         return data
 
 
     def flushCachedData(self, name):
     def flushCachedData(self, name):
-        assert type(name) == type('')
+        assert isinstance(name, str)
         # call this to throw out cached data from a previous instantiation
         # call this to throw out cached data from a previous instantiation
         self._cachedData[name].flush()
         self._cachedData[name].flush()
 
 
-    def setCacheable(self, bool):
-        assert bool == 1 or bool == 0
-        self.cacheable = bool
+    def setCacheable(self, boolean):
+        assert boolean == 1 or boolean == 0
+        self.cacheable = boolean
 
 
     def getCacheable(self):
     def getCacheable(self):
         return self.cacheable
         return self.cacheable
@@ -278,14 +277,13 @@ class DistributedObject(DistributedObjectBase):
         Inheritors should redefine this to take appropriate action on disable
         Inheritors should redefine this to take appropriate action on disable
         """
         """
         assert self.notify.debug('disable(): %s' % (self.doId))
         assert self.notify.debug('disable(): %s' % (self.doId))
-        pass
 
 
     def isDisabled(self):
     def isDisabled(self):
         """
         """
         Returns true if the object has been disabled and/or deleted,
         Returns true if the object has been disabled and/or deleted,
         or if it is brand new and hasn't yet been generated.
         or if it is brand new and hasn't yet been generated.
         """
         """
-        return (self.activeState < ESGenerating)
+        return self.activeState < ESGenerating
 
 
     def isGenerated(self):
     def isGenerated(self):
         """
         """
@@ -293,17 +291,14 @@ class DistributedObject(DistributedObjectBase):
         and not yet disabled.
         and not yet disabled.
         """
         """
         assert self.notify.debugStateCall(self)
         assert self.notify.debugStateCall(self)
-        return (self.activeState == ESGenerated)
+        return self.activeState == ESGenerated
 
 
     def delete(self):
     def delete(self):
         """
         """
         Inheritors should redefine this to take appropriate action on delete
         Inheritors should redefine this to take appropriate action on delete
         """
         """
         assert self.notify.debug('delete(): %s' % (self.doId))
         assert self.notify.debug('delete(): %s' % (self.doId))
-        try:
-            self.DistributedObject_deleted
-        except:
-            self.DistributedObject_deleted = 1
+        self.DistributedObject_deleted = 1
 
 
     def generate(self):
     def generate(self):
         """
         """
@@ -374,10 +369,10 @@ class DistributedObject(DistributedObjectBase):
         self.cr.sendDeleteMsg(self.doId)
         self.cr.sendDeleteMsg(self.doId)
 
 
     def taskName(self, taskString):
     def taskName(self, taskString):
-        return ("%s-%s" % (taskString, self.doId))
+        return "%s-%s" % (taskString, self.doId)
 
 
     def uniqueName(self, idString):
     def uniqueName(self, idString):
-        return ("%s-%s" % (idString, self.doId))
+        return "%s-%s" % (idString, self.doId)
 
 
     def getCallbackContext(self, callback, extraArgs = []):
     def getCallbackContext(self, callback, extraArgs = []):
         # Some objects implement a back-and-forth handshake operation
         # Some objects implement a back-and-forth handshake operation
@@ -429,7 +424,7 @@ class DistributedObject(DistributedObjectBase):
         if tuple:
         if tuple:
             callback, extraArgs = tuple
             callback, extraArgs = tuple
             completeArgs = args + extraArgs
             completeArgs = args + extraArgs
-            if callback != None:
+            if callback is not None:
                 callback(*completeArgs)
                 callback(*completeArgs)
             del self.__callbacks[context]
             del self.__callbacks[context]
         else:
         else:
@@ -470,9 +465,9 @@ class DistributedObject(DistributedObjectBase):
         # doneBarrier() twice, or we have not received a barrier
         # doneBarrier() twice, or we have not received a barrier
         # context from the AI.  I think in either case it's ok to
         # context from the AI.  I think in either case it's ok to
         # silently ignore the error.
         # silently ignore the error.
-        if self.__barrierContext != None:
+        if self.__barrierContext is not None:
             context, aiName = self.__barrierContext
             context, aiName = self.__barrierContext
-            if name == None or name == aiName:
+            if name is None or name == aiName:
                 assert self.notify.debug('doneBarrier(%s, %s)' % (context, aiName))
                 assert self.notify.debug('doneBarrier(%s, %s)' % (context, aiName))
                 self.sendUpdate("setBarrierReady", [context])
                 self.sendUpdate("setBarrierReady", [context])
                 self.__barrierContext = None
                 self.__barrierContext = None

+ 27 - 32
direct/src/distributed/DistributedObjectAI.py

@@ -2,6 +2,7 @@
 
 
 from direct.directnotify.DirectNotifyGlobal import directNotify
 from direct.directnotify.DirectNotifyGlobal import directNotify
 from direct.distributed.DistributedObjectBase import DistributedObjectBase
 from direct.distributed.DistributedObjectBase import DistributedObjectBase
+from direct.showbase.MessengerGlobal import messenger
 from direct.showbase import PythonUtil
 from direct.showbase import PythonUtil
 from panda3d.core import *
 from panda3d.core import *
 from panda3d.direct import *
 from panda3d.direct import *
@@ -13,9 +14,7 @@ class DistributedObjectAI(DistributedObjectBase):
     QuietZone = 1
     QuietZone = 1
 
 
     def __init__(self, air):
     def __init__(self, air):
-        try:
-            self.DistributedObjectAI_initialized
-        except:
+        if not hasattr(self, 'DistributedObjectAI_initialized'):
             self.DistributedObjectAI_initialized = 1
             self.DistributedObjectAI_initialized = 1
             DistributedObjectBase.__init__(self, air)
             DistributedObjectBase.__init__(self, air)
 
 
@@ -66,11 +65,11 @@ class DistributedObjectAI(DistributedObjectBase):
                 flags = []
                 flags = []
                 if self.__generated:
                 if self.__generated:
                     flags.append("generated")
                     flags.append("generated")
-                if self.air == None:
+                if self.air is None:
                     flags.append("deleted")
                     flags.append("deleted")
 
 
                 flagStr = ""
                 flagStr = ""
-                if len(flags):
+                if len(flags) > 0:
                     flagStr = " (%s)" % (" ".join(flags))
                     flagStr = " (%s)" % (" ".join(flags))
 
 
                 print("%sfrom DistributedObject doId:%s, parent:%s, zone:%s%s" % (
                 print("%sfrom DistributedObject doId:%s, parent:%s, zone:%s%s" % (
@@ -119,24 +118,23 @@ class DistributedObjectAI(DistributedObjectBase):
                 # self.doId may not exist.  The __dict__ syntax works around that.
                 # self.doId may not exist.  The __dict__ syntax works around that.
                 assert self.notify.debug('delete(): %s' % (self.__dict__.get("doId")))
                 assert self.notify.debug('delete(): %s' % (self.__dict__.get("doId")))
 
 
-                if not self._DOAI_requestedDelete:
-                    # this logs every delete that was not requested by us.
-                    # TODO: this currently prints warnings for deletes of objects
-                    # that we did not create. We need to add a 'locally created'
-                    # flag to every object to filter these out.
-                    """
-                    DistributedObjectAI.notify.warning(
-                        'delete() called but requestDelete never called for %s: %s'
-                        % (self.__dict__.get('doId'), self.__class__.__name__))
-                        """
-                    """
-                    # print a stack trace so we can detect whether this is the
-                    # result of a network msg.
-                    # this is slow.
-                    from direct.showbase.PythonUtil import StackTrace
-                    DistributedObjectAI.notify.warning(
-                        'stack trace: %s' % StackTrace())
-                        """
+                #if not self._DOAI_requestedDelete:
+                #    # this logs every delete that was not requested by us.
+                #    # TODO: this currently prints warnings for deletes of objects
+                #    # that we did not create. We need to add a 'locally created'
+                #    # flag to every object to filter these out.
+                #
+                #    DistributedObjectAI.notify.warning(
+                #        'delete() called but requestDelete never called for %s: %s'
+                #        % (self.__dict__.get('doId'), self.__class__.__name__))
+                #
+                #    # print a stack trace so we can detect whether this is the
+                #    # result of a network msg.
+                #    # this is slow.
+                #    from direct.showbase.PythonUtil import StackTrace
+                #    DistributedObjectAI.notify.warning(
+                #        'stack trace: %s' % StackTrace())
+
                 self._DOAI_requestedDelete = False
                 self._DOAI_requestedDelete = False
 
 
                 self.releaseZoneData()
                 self.releaseZoneData()
@@ -167,7 +165,7 @@ class DistributedObjectAI(DistributedObjectBase):
         Returns true if the object has been deleted,
         Returns true if the object has been deleted,
         or if it is brand new and hasnt yet been generated.
         or if it is brand new and hasnt yet been generated.
         """
         """
-        return self.air == None
+        return self.air is None
 
 
     def isGenerated(self):
     def isGenerated(self):
         """
         """
@@ -195,7 +193,6 @@ class DistributedObjectAI(DistributedObjectBase):
         Called after the object has been generated and all
         Called after the object has been generated and all
         of its required fields filled in. Overwrite when needed.
         of its required fields filled in. Overwrite when needed.
         """
         """
-        pass
 
 
     def b_setLocation(self, parentId, zoneId):
     def b_setLocation(self, parentId, zoneId):
         self.d_setLocation(parentId, zoneId)
         self.d_setLocation(parentId, zoneId)
@@ -206,14 +203,13 @@ class DistributedObjectAI(DistributedObjectBase):
 
 
     def setLocation(self, parentId, zoneId):
     def setLocation(self, parentId, zoneId):
         # Prevent Duplicate SetLocations for being Called
         # Prevent Duplicate SetLocations for being Called
-        if (self.parentId == parentId) and (self.zoneId == zoneId):
+        if self.parentId == parentId and self.zoneId == zoneId:
             return
             return
 
 
         oldParentId = self.parentId
         oldParentId = self.parentId
         oldZoneId = self.zoneId
         oldZoneId = self.zoneId
         self.air.storeObjectLocation(self, parentId, zoneId)
         self.air.storeObjectLocation(self, parentId, zoneId)
-        if ((oldParentId != parentId) or
-            (oldZoneId != zoneId)):
+        if oldParentId != parentId or oldZoneId != zoneId:
             self.releaseZoneData()
             self.releaseZoneData()
             messenger.send(self.getZoneChangeEvent(), [zoneId, oldZoneId])
             messenger.send(self.getZoneChangeEvent(), [zoneId, oldZoneId])
             # if we are not going into the quiet zone, send a 'logical' zone
             # if we are not going into the quiet zone, send a 'logical' zone
@@ -476,10 +472,10 @@ class DistributedObjectAI(DistributedObjectBase):
         self._DOAI_requestedDelete = True
         self._DOAI_requestedDelete = True
 
 
     def taskName(self, taskString):
     def taskName(self, taskString):
-        return ("%s-%s" % (taskString, self.doId))
+        return "%s-%s" % (taskString, self.doId)
 
 
     def uniqueName(self, idString):
     def uniqueName(self, idString):
-        return ("%s-%s" % (idString, self.doId))
+        return "%s-%s" % (idString, self.doId)
 
 
     def validate(self, avId, bool, msg):
     def validate(self, avId, bool, msg):
         if not bool:
         if not bool:
@@ -542,7 +538,7 @@ class DistributedObjectAI(DistributedObjectBase):
         avId = self.air.getAvatarIdFromSender()
         avId = self.air.getAvatarIdFromSender()
         assert self.notify.debug('setBarrierReady(%s, %s)' % (context, avId))
         assert self.notify.debug('setBarrierReady(%s, %s)' % (context, avId))
         barrier = self.__barriers.get(context)
         barrier = self.__barriers.get(context)
-        if barrier == None:
+        if barrier is None:
             # This may be None if a client was slow and missed an
             # This may be None if a client was slow and missed an
             # earlier timeout.  Too bad.
             # earlier timeout.  Too bad.
             return
             return
@@ -569,7 +565,6 @@ class DistributedObjectAI(DistributedObjectBase):
 
 
     def _retrieveCachedData(self):
     def _retrieveCachedData(self):
         """ This is a no-op on the AI. """
         """ This is a no-op on the AI. """
-        pass
 
 
     def setAI(self, aiChannel):
     def setAI(self, aiChannel):
         self.air.setAI(self.doId, aiChannel)
         self.air.setAI(self.doId, aiChannel)

+ 1 - 8
direct/src/distributed/DistributedObjectBase.py

@@ -49,7 +49,6 @@ class DistributedObjectBase(DirectObject):
         """
         """
         assert self.notify.debugCall()
         assert self.notify.debugCall()
         # Inheritors should override
         # Inheritors should override
-        pass
 
 
     def handleChildArriveZone(self, childObj, zoneId):
     def handleChildArriveZone(self, childObj, zoneId):
         """
         """
@@ -60,7 +59,6 @@ class DistributedObjectBase(DirectObject):
         """
         """
         assert self.notify.debugCall()
         assert self.notify.debugCall()
         # Inheritors should override
         # Inheritors should override
-        pass
 
 
     def handleChildLeave(self, childObj, zoneId):
     def handleChildLeave(self, childObj, zoneId):
         """
         """
@@ -69,7 +67,6 @@ class DistributedObjectBase(DirectObject):
         """
         """
         assert self.notify.debugCall()
         assert self.notify.debugCall()
         # Inheritors should override
         # Inheritors should override
-        pass
 
 
     def handleChildLeaveZone(self, childObj, zoneId):
     def handleChildLeaveZone(self, childObj, zoneId):
         """
         """
@@ -79,12 +76,10 @@ class DistributedObjectBase(DirectObject):
         """
         """
         assert self.notify.debugCall()
         assert self.notify.debugCall()
         # Inheritors should override
         # Inheritors should override
-        pass
 
 
     def handleQueryObjectChildrenLocalDone(self, context):
     def handleQueryObjectChildrenLocalDone(self, context):
         assert self.notify.debugCall()
         assert self.notify.debugCall()
         # Inheritors should override
         # Inheritors should override
-        pass
 
 
     def getParentObj(self):
     def getParentObj(self):
         if self.parentId is None:
         if self.parentId is None:
@@ -92,12 +87,10 @@ class DistributedObjectBase(DirectObject):
         return self.cr.doId2do.get(self.parentId)
         return self.cr.doId2do.get(self.parentId)
 
 
     def hasParentingRules(self):
     def hasParentingRules(self):
-        return self.dclass.getFieldByName('setParentingRules') != None
+        return self.dclass.getFieldByName('setParentingRules') is not None
 
 
     def delete(self):
     def delete(self):
         """
         """
         Override this to handle cleanup right before this object
         Override this to handle cleanup right before this object
         gets deleted.
         gets deleted.
         """
         """
-
-        pass

+ 0 - 1
direct/src/distributed/DistributedObjectGlobal.py

@@ -21,4 +21,3 @@ class DistributedObjectGlobal(DistributedObject):
         DistributedObject.__init__(self, cr)
         DistributedObject.__init__(self, cr)
         self.parentId = 0
         self.parentId = 0
         self.zoneId = 0
         self.zoneId = 0
-

Some files were not shown because too many files changed in this diff