Browse Source

Merge branch 'master' into webgl-port

rdb 2 years ago
parent
commit
fcf3de60c2
100 changed files with 1821 additions and 1136 deletions
  1. 6 0
      .clang-tidy
  2. 8 8
      .github/workflows/ci.yml
  3. 39 0
      .github/workflows/lint.yml
  4. 77 0
      .github/workflows/review.yml
  5. 513 313
      .pylintrc
  6. 1 0
      BACKERS.md
  7. 3 3
      README.md
  8. 3 2
      contrib/src/ai/aiWorld.cxx
  9. 23 1
      direct/src/actor/Actor.py
  10. 26 26
      direct/src/cluster/ClusterClient.py
  11. 13 13
      direct/src/cluster/ClusterConfig.py
  12. 1 1
      direct/src/cluster/ClusterMsgs.py
  13. 35 19
      direct/src/cluster/ClusterServer.py
  14. 1 1
      direct/src/controls/BattleWalker.py
  15. 2 5
      direct/src/controls/ControlManager.py
  16. 1 1
      direct/src/controls/DevWalker.py
  17. 18 3
      direct/src/controls/GravityWalker.py
  18. 14 3
      direct/src/controls/InputState.py
  19. 14 1
      direct/src/controls/NonPhysicsWalker.py
  20. 7 1
      direct/src/controls/ObserverWalker.py
  21. 30 6
      direct/src/controls/PhysicsWalker.py
  22. 5 2
      direct/src/controls/TwoDWalker.py
  23. 1 1
      direct/src/dcparser/dcClassParameter.h
  24. 1 2
      direct/src/directbase/TestStart.py
  25. 1 2
      direct/src/directbase/ThreeUpStart.py
  26. 15 8
      direct/src/directdevices/DirectDeviceManager.py
  27. 2 1
      direct/src/directdevices/DirectFastrak.py
  28. 29 29
      direct/src/directdevices/DirectJoybox.py
  29. 1 2
      direct/src/directdevices/DirectRadamec.py
  30. 2 2
      direct/src/directnotify/DirectNotify.py
  31. 13 13
      direct/src/directnotify/Notifier.py
  32. 9 1
      direct/src/directscripts/eggcacher.py
  33. 8 3
      direct/src/directscripts/extract_docs.py
  34. 32 31
      direct/src/directtools/DirectCameraControl.py
  35. 28 5
      direct/src/directtools/DirectGeometry.py
  36. 4 4
      direct/src/directtools/DirectGlobals.py
  37. 4 4
      direct/src/directtools/DirectGrid.py
  38. 11 1
      direct/src/directtools/DirectLights.py
  39. 73 49
      direct/src/directtools/DirectManipulation.py
  40. 49 27
      direct/src/directtools/DirectSelection.py
  41. 53 36
      direct/src/directtools/DirectSession.py
  42. 0 2
      direct/src/directtools/DirectUtil.py
  43. 3 1
      direct/src/directutil/LargeBlobSenderConsts.py
  44. 22 11
      direct/src/directutil/Mopath.py
  45. 2 0
      direct/src/directutil/Verify.py
  46. 64 70
      direct/src/dist/FreezeTool.py
  47. 4 3
      direct/src/dist/_android.py
  48. 5 4
      direct/src/dist/commands.py
  49. 1 4
      direct/src/dist/icon.py
  50. 2 2
      direct/src/dist/installers.py
  51. 5 3
      direct/src/dist/pefile.py
  52. 3 2
      direct/src/distributed/AsyncRequest.py
  53. 13 1
      direct/src/distributed/ClientRepository.py
  54. 16 16
      direct/src/distributed/ClientRepositoryBase.py
  55. 8 6
      direct/src/distributed/ConnectionRepository.py
  56. 3 4
      direct/src/distributed/DistributedCamera.py
  57. 0 2
      direct/src/distributed/DistributedCameraOV.py
  58. 4 5
      direct/src/distributed/DistributedCartesianGrid.py
  59. 0 3
      direct/src/distributed/DistributedCartesianGridAI.py
  60. 3 5
      direct/src/distributed/DistributedObject.py
  61. 0 2
      direct/src/distributed/DistributedObjectAI.py
  62. 0 2
      direct/src/distributed/DistributedObjectUD.py
  63. 33 6
      direct/src/distributed/DistributedSmoothNode.py
  64. 5 2
      direct/src/distributed/DistributedSmoothNodeBase.py
  65. 13 6
      direct/src/distributed/DoInterestManager.py
  66. 1 1
      direct/src/distributed/GridChild.py
  67. 1 3
      direct/src/distributed/GridParent.py
  68. 2 2
      direct/src/distributed/MsgTypes.py
  69. 1 1
      direct/src/distributed/MsgTypesCMU.py
  70. 3 3
      direct/src/distributed/NetMessenger.py
  71. 23 3
      direct/src/distributed/PyDatagram.py
  72. 23 3
      direct/src/distributed/PyDatagramIterator.py
  73. 2 2
      direct/src/distributed/SampleObject.py
  74. 33 3
      direct/src/distributed/ServerRepository.py
  75. 2 2
      direct/src/distributed/TimeManager.py
  76. 2 2
      direct/src/distributed/TimeManagerAI.py
  77. 58 58
      direct/src/extensions_native/CInterval_extensions.py
  78. 21 21
      direct/src/extensions_native/HTTPChannel_extensions.py
  79. 147 146
      direct/src/extensions_native/NodePath_extensions.py
  80. 1 1
      direct/src/filter/CommonFilters.py
  81. 3 3
      direct/src/filter/FilterManager.py
  82. 4 8
      direct/src/fsm/ClassicFSM.py
  83. 2 3
      direct/src/fsm/FSM.py
  84. 1 1
      direct/src/fsm/FourState.py
  85. 4 1
      direct/src/fsm/FourStateAI.py
  86. 1 1
      direct/src/fsm/SampleFSM.py
  87. 12 12
      direct/src/fsm/StatePush.py
  88. 5 3
      direct/src/gui/DirectButton.py
  89. 4 4
      direct/src/gui/DirectCheckBox.py
  90. 6 5
      direct/src/gui/DirectCheckButton.py
  91. 15 10
      direct/src/gui/DirectDialog.py
  92. 10 8
      direct/src/gui/DirectEntry.py
  93. 4 7
      direct/src/gui/DirectEntryScroll.py
  94. 2 2
      direct/src/gui/DirectFrame.py
  95. 15 7
      direct/src/gui/DirectGuiBase.py
  96. 9 3
      direct/src/gui/DirectGuiGlobals.py
  97. 3 2
      direct/src/gui/DirectGuiTest.py
  98. 5 3
      direct/src/gui/DirectLabel.py
  99. 5 5
      direct/src/gui/DirectOptionMenu.py
  100. 6 5
      direct/src/gui/DirectRadioButton.py

+ 6 - 0
.clang-tidy

@@ -0,0 +1,6 @@
+---
+Checks: 'clang-diagnostic-*,clang-analyzer-*,performance-*,portability-*,modernize-use-nullptr'
+WarningsAsErrors: ''
+HeaderFilterRegex: ''
+FormatStyle: none
+...

+ 8 - 8
.github/workflows/ci.yml

@@ -92,10 +92,10 @@ jobs:
     - name: Install dependencies (macOS)
       if: runner.os == 'macOS'
       run: |
-        curl -O https://www.panda3d.org/download/panda3d-1.10.13/panda3d-1.10.13-tools-mac.tar.gz
-        tar -xf panda3d-1.10.13-tools-mac.tar.gz
-        mv panda3d-1.10.13/thirdparty thirdparty
-        rmdir panda3d-1.10.13
+        curl -O https://www.panda3d.org/download/panda3d-1.10.14/panda3d-1.10.14-tools-mac.tar.gz
+        tar -xf panda3d-1.10.14-tools-mac.tar.gz
+        mv panda3d-1.10.14/thirdparty thirdparty
+        rmdir panda3d-1.10.14
 
         # 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
@@ -381,10 +381,10 @@ jobs:
     - name: Get thirdparty packages (macOS)
       if: runner.os == 'macOS'
       run: |
-        curl -O https://www.panda3d.org/download/panda3d-1.10.13/panda3d-1.10.13-tools-mac.tar.gz
-        tar -xf panda3d-1.10.13-tools-mac.tar.gz
-        mv panda3d-1.10.13/thirdparty thirdparty
-        rmdir panda3d-1.10.13
+        curl -O https://www.panda3d.org/download/panda3d-1.10.14/panda3d-1.10.14-tools-mac.tar.gz
+        tar -xf panda3d-1.10.14-tools-mac.tar.gz
+        mv panda3d-1.10.14/thirdparty thirdparty
+        rmdir panda3d-1.10.14
         (cd thirdparty/darwin-libs-a && rm -rf rocket)
 
     - name: Set up Python 3.11

+ 39 - 0
.github/workflows/lint.yml

@@ -0,0 +1,39 @@
+name: Lint
+on: [pull_request]
+
+jobs:
+  clang-tidy:
+    runs-on: ubuntu-20.04
+    steps:
+    - uses: actions/checkout@v3
+      with:
+        fetch-depth: 2
+    - name: Install clang-tidy
+      run: |
+        sudo apt-get update
+        sudo apt-get install -y clang-tidy build-essential pkg-config libpng-dev libjpeg-dev libtiff-dev zlib1g-dev libssl-dev libx11-dev libgl1-mesa-dev libxrandr-dev libxxf86dga-dev libxcursor-dev libfreetype6-dev libvorbis-dev libeigen3-dev libopenal-dev libode-dev libbullet-dev libgtk-3-dev libassimp-dev libopenexr-dev
+    - name: Prepare compile_commands.json
+      run: |
+        cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_UNITY_BUILD=OFF -DHAVE_PYTHON=OFF -DINTERROGATE_PYTHON_INTERFACE=OFF
+    - name: Copy prebuilt files
+      run: |
+        for fn in **/*.prebuilt; do
+          echo mkdir -p $(dirname "cmake/$fn");
+          echo cp "$fn" "cmake/${fn%.*}";
+        done
+    - name: Create results directory
+      run: |
+        mkdir clang-tidy-result
+    - name: Analyze
+      run: |
+        git diff -U0 HEAD^ | clang-tidy-diff -p1 -path build -export-fixes clang-tidy-result/fixes.yml
+    - name: Save PR metadata
+      run: |
+        echo ${{ github.event.number }} > clang-tidy-result/pr-id.txt
+        echo ${{ github.event.pull_request.head.repo.full_name }} > clang-tidy-result/pr-head-repo.txt
+        echo ${{ github.event.pull_request.head.ref }} > clang-tidy-result/pr-head-ref.txt
+    - name: Upload results
+      uses: actions/upload-artifact@v2
+      with:
+        name: clang-tidy-result
+        path: clang-tidy-result/

+ 77 - 0
.github/workflows/review.yml

@@ -0,0 +1,77 @@
+name: Post PR Review
+
+on:
+  workflow_run:
+    workflows: ["Lint"]
+    types: [completed]
+
+jobs:
+  clang-tidy-results:
+    # Trigger the job only if the previous (insecure) workflow completed successfully
+    if: ${{ github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' }}
+    runs-on: ubuntu-20.04
+    steps:
+    - name: Download analysis results
+      uses: actions/[email protected]
+      with:
+        script: |
+          let artifacts = await github.actions.listWorkflowRunArtifacts({
+              owner: context.repo.owner,
+              repo: context.repo.repo,
+              run_id: ${{ github.event.workflow_run.id }},
+          });
+          let matchArtifact = artifacts.data.artifacts.filter((artifact) => {
+              return artifact.name == "clang-tidy-result"
+          })[0];
+          let download = await github.actions.downloadArtifact({
+              owner: context.repo.owner,
+              repo: context.repo.repo,
+              artifact_id: matchArtifact.id,
+              archive_format: "zip",
+          });
+          let fs = require("fs");
+          fs.writeFileSync("${{github.workspace}}/clang-tidy-result.zip", Buffer.from(download.data));
+    - name: Set environment variables
+      run: |
+        mkdir clang-tidy-result
+        unzip clang-tidy-result.zip -d clang-tidy-result
+        echo "pr_id=$(cat clang-tidy-result/pr-id.txt)" >> $GITHUB_ENV
+        echo "pr_head_repo=$(cat clang-tidy-result/pr-head-repo.txt)" >> $GITHUB_ENV
+        echo "pr_head_ref=$(cat clang-tidy-result/pr-head-ref.txt)" >> $GITHUB_ENV
+    - uses: actions/checkout@v2
+      with:
+        repository: ${{ env.pr_head_repo }}
+        ref: ${{ env.pr_head_ref }}
+        persist-credentials: false
+    - name: Redownload analysis results
+      uses: actions/[email protected]
+      with:
+        script: |
+          let artifacts = await github.actions.listWorkflowRunArtifacts({
+              owner: context.repo.owner,
+              repo: context.repo.repo,
+              run_id: ${{github.event.workflow_run.id}},
+          });
+          let matchArtifact = artifacts.data.artifacts.filter((artifact) => {
+              return artifact.name == "clang-tidy-result"
+          })[0];
+          let download = await github.actions.downloadArtifact({
+              owner: context.repo.owner,
+              repo: context.repo.repo,
+              artifact_id: matchArtifact.id,
+              archive_format: "zip",
+          });
+          let fs = require("fs");
+          fs.writeFileSync("${{github.workspace}}/clang-tidy-result.zip", Buffer.from(download.data));
+    - name: Extract analysis results
+      run: |
+        mkdir clang-tidy-result
+        unzip clang-tidy-result.zip -d clang-tidy-result
+    - name: Run clang-tidy-pr-comments action
+      uses: platisd/clang-tidy-pr-comments@master
+      with:
+        github_token: ${{ github.token }}
+        clang_tidy_fixes: clang-tidy-result/fixes.yml
+        pull_request_id: ${{ env.pr_id }}
+        request_changes: true
+        suggestions_per_comment: 10

+ 513 - 313
.pylintrc

@@ -1,27 +1,73 @@
-[MASTER]
+[MAIN]
+
+# 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
+
+# Load and enable all available extensions. Use --list-extensions to see a list
+# all available extensions.
+#enable-all-extensions=
+
+# In error mode, messages with a category besides ERROR or FATAL are
+# suppressed, and no reports are done by default. Error mode is compatible with
+# disabling specific errors.
+#errors-only=
+
+# Always return a 0 (non-error) status code, even if lint errors are found.
+# This is primarily useful in continuous integration scripts.
+#exit-zero=
 
 # 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
+extension-pkg-allow-list=panda3d,wx
 
-# Specify a score threshold to be exceeded before program exits with error.
+# 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. (This is an alternative name to extension-pkg-allow-list
+# for backward compatibility.)
+extension-pkg-whitelist=panda3d,wx
+
+# Return non-zero exit code if any of these messages/categories are detected,
+# even if score is above --fail-under value. Syntax same as enable. Messages
+# specified are enabled, while categories only check already-enabled messages.
+fail-on=
+
+# Specify a score threshold under which the program will exit with error.
 fail-under=10.0
 
-# Add files or directories to the blacklist. They should be base names, not
-# paths.
+# Interpret the stdin as a python script, whose filename needs to be passed as
+# the module_or_package argument.
+#from-stdin=
+
+# Files or directories to be skipped. 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.
+# Add files or directories matching the regular expressions patterns to the
+# ignore-list. The regex matches against paths and can be in Posix or Windows
+# format. Because '\\' represents the directory delimiter on Windows systems,
+# it can't be used as an escape character.
+ignore-paths=
+
+# Files or directories matching the regular expression patterns are skipped.
+# The regex matches against base names, not paths. The default value ignores
+# Emacs file locks
 ignore-patterns=
 
+# 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=
+
 # 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.
+# number of processors available to use, and will cap the count on Windows to
+# avoid hangs.
 jobs=1
 
 # Control the amount of potential inferred values when inferring a single
@@ -36,259 +82,23 @@ load-plugins=
 # Pickle collected data for later comparisons.
 persistent=yes
 
+# Minimum Python version to use for version dependent checks. Will default to
+# the version used to run pylint.
+py-version=3.6
+
+# Discover python modules and packages in the file system subtree.
+recursive=no
+
 # 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=
-
+#unsafe-load-any-extension=no
 
-[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=
+# In verbose mode, extra non-checker-related info will be displayed.
+#verbose=
 
 
 [BASIC]
@@ -297,13 +107,15 @@ notes=FIXME,
 argument-naming-style=snake_case
 
 # Regular expression matching correct argument names. Overrides argument-
-# naming-style.
+# naming-style. If left empty, argument names will be checked with the set
+# 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. If left empty, attribute names will be checked with the set naming
 # style.
 #attr-rgx=
 
@@ -323,20 +135,30 @@ bad-names-rgxs=
 class-attribute-naming-style=any
 
 # Regular expression matching correct class attribute names. Overrides class-
-# attribute-naming-style.
+# attribute-naming-style. If left empty, class attribute names will be checked
+# with the set naming style.
 #class-attribute-rgx=
 
+# Naming style matching correct class constant names.
+class-const-naming-style=UPPER_CASE
+
+# Regular expression matching correct class constant names. Overrides class-
+# const-naming-style. If left empty, class constant names will be checked with
+# the set naming style.
+#class-const-rgx=
+
 # Naming style matching correct class names.
 class-naming-style=PascalCase
 
 # Regular expression matching correct class names. Overrides class-naming-
-# style.
+# style. If left empty, class names will be checked with the set 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. If left empty, constant names will be checked with the set naming
 # style.
 #const-rgx=
 
@@ -348,7 +170,8 @@ docstring-min-length=-1
 function-naming-style=snake_case
 
 # Regular expression matching correct function names. Overrides function-
-# naming-style.
+# naming-style. If left empty, function names will be checked with the set
+# naming style.
 #function-rgx=
 
 # Good variable names which should always be accepted, separated by a comma.
@@ -370,21 +193,22 @@ include-naming-hint=no
 inlinevar-naming-style=any
 
 # Regular expression matching correct inline iteration names. Overrides
-# inlinevar-naming-style.
+# inlinevar-naming-style. If left empty, inline iteration names will be checked
+# with the set 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.
+# style. If left empty, method names will be checked with the set 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.
+# style. If left empty, module names will be checked with the set naming style.
 #module-rgx=
 
 # Colon-delimited sets of names that determine each other's naming style when
@@ -400,38 +224,55 @@ no-docstring-rgx=^_
 # These decorators are taken in consideration only for invalid-name.
 property-classes=abc.abstractproperty
 
+# Regular expression matching correct type variable names. If left empty, type
+# variable names will be checked with the set naming style.
+#typevar-rgx=
+
 # Naming style matching correct variable names.
 variable-naming-style=snake_case
 
 # Regular expression matching correct variable names. Overrides variable-
-# naming-style.
+# naming-style. If left empty, variable names will be checked with the set
+# 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
-
+[CLASSES]
 
-[STRING]
+# Warn about protected attribute access inside special methods
+check-protected-access-in-special-methods=no
 
-# 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
+# List of method names used to declare (i.e. assign) instance attributes.
+defining-attr-methods=__init__,
+                      __new__,
+                      setUp,
+                      __post_init__
 
-# 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
+# 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
 
 
 [DESIGN]
 
+# List of regular expressions of class ancestor names to ignore when counting
+# public methods (see R0903)
+exclude-too-few-public-methods=
+
+# List of qualified class names to ignore when counting class parents (see
+# R0901)
+ignored-parents=
+
 # Maximum number of arguments for function / method.
 max-args=5
 
@@ -463,6 +304,42 @@ max-statements=50
 min-public-methods=2
 
 
+[EXCEPTIONS]
+
+# Exceptions that will emit a warning when caught.
+overgeneral-exceptions=builtins.BaseException,builtins.Exception
+
+
+[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
+
+
 [IMPORTS]
 
 # List of modules that can be imported at any level, not just the top level
@@ -472,24 +349,20 @@ 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
+deprecated-modules=optparse,tkinter.tix,pandac.PandaModules,direct.directbase.DirectStart
 
-# Create a graph of external dependencies in the given file (report RP0402 must
-# not be disabled).
+# Output a graph (.gv or any supported image format) of external dependencies
+# to 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).
+# Output a graph (.gv or any supported image format) of all (i.e. internal and
+# external) dependencies to 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).
+# Output a graph (.gv or any supported image format) of internal dependencies
+# to the given file (report RP0402 must not be disabled).
 int-import-graph=
 
 # Force import order to recognize a module as part of the standard
@@ -503,32 +376,359 @@ known-third-party=enchant
 preferred-modules=
 
 
-[CLASSES]
+[LOGGING]
 
-# List of method names used to declare (i.e. assign) instance attributes.
-defining-attr-methods=__init__,
-                      __new__,
-                      setUp,
-                      __post_init__
+# The type of string formatting that logging methods do. `old` means using %
+# formatting, `new` is for `{}` formatting.
+logging-format-style=old
 
-# List of member names, which should be excluded from the protected access
-# warning.
-exclude-protected=_asdict,
-                  _fields,
-                  _replace,
-                  _source,
-                  _make
+# Logging modules to check that the string format arguments are in logging
+# function parameter format.
+logging-modules=logging
 
-# 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
+[MESSAGES CONTROL]
 
+# Only show warnings with the listed confidence levels. Leave empty to show
+# all. Valid levels: HIGH, CONTROL_FLOW, INFERENCE, INFERENCE_FAILURE,
+# UNDEFINED.
+confidence=HIGH,
+           CONTROL_FLOW,
+           INFERENCE,
+           INFERENCE_FAILURE,
+           UNDEFINED
 
-[EXCEPTIONS]
+# 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 re-enable 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=raw-checker-failed,
+        bad-inline-option,
+        locally-disabled,
+        file-ignored,
+        suppressed-message,
+        useless-suppression,
+        deprecated-pragma,
+        use-symbolic-message-instead,
+        consider-using-f-string,
+        attribute-defined-outside-init,
+        comparison-with-callable,
+        dangerous-default-value,
+        global-statement,
+        import-outside-toplevel,
+        invalid-name,
+        line-too-long,
+        missing-class-docstring,
+        missing-function-docstring,
+        missing-module-docstring,
+        protected-access,
+        no-classmethod-decorator,
+        no-staticmethod-decorator,
+        useless-object-inheritance,
+        property-with-parameters,
+        too-many-ancestors,
+        too-many-instance-attributes,
+        too-few-public-methods,
+        too-many-public-methods,
+        too-many-return-statements,
+        too-many-branches,
+        too-many-arguments,
+        too-many-locals,
+        too-many-statements,
+        too-many-boolean-expressions,
+        consider-merging-isinstance,
+        too-many-nested-blocks,
+        simplifiable-if-statement,
+        redefined-argument-from-local,
+        no-else-return,
+        consider-using-ternary,
+        trailing-comma-tuple,
+        stop-iteration-return,
+        simplify-boolean-expression,
+        inconsistent-return-statements,
+        useless-return,
+        consider-swap-variables,
+        consider-using-join,
+        consider-using-in,
+        consider-using-get,
+        chained-comparison,
+        consider-using-dict-comprehension,
+        consider-using-set-comprehension,
+        simplifiable-if-expression,
+        no-else-raise,
+        unnecessary-comprehension,
+        consider-using-sys-exit,
+        no-else-break,
+        no-else-continue,
+        super-with-arguments,
+        simplifiable-condition,
+        condition-evals-to-constant,
+        consider-using-generator,
+        use-a-generator,
+        consider-using-min-builtin,
+        consider-using-max-builtin,
+        consider-using-with,
+        unnecessary-dict-index-lookup,
+        use-list-literal,
+        use-dict-literal,
+        unnecessary-list-index-lookup,
+        literal-comparison,
+        comparison-with-itself,
+        comparison-of-constants,
+        cyclic-import,
+        consider-using-from-import,
+        duplicate-code,
+        raise-missing-from,
+        redefined-builtin,
+        redefined-outer-name,
+        too-many-lines,
+        unused-argument,
+        unused-variable,
+        unused-wildcard-import,
+        using-constant-test,
+        wrong-import-order,
+        wrong-import-position,
+        not-callable,
+        wildcard-import
+
+# 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
 
-# Exceptions that will emit a warning when being caught. Defaults to
-# "BaseException, Exception".
-overgeneral-exceptions=BaseException,
-                       Exception
+
+[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=
+
+
+[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
+
+
+[REPORTS]
+
+# Python expression which should return a score less than or equal to 10. You
+# have access to the variables 'fatal', 'error', 'warning', 'refactor',
+# 'convention', and 'info' 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=
+
+# Tells whether to display a full report or only the messages.
+reports=no
+
+# Activate the evaluation score.
+score=yes
+
+
+[SIMILARITIES]
+
+# Comments are removed from the similarity computation
+ignore-comments=yes
+
+# Docstrings are removed from the similarity computation
+ignore-docstrings=yes
+
+# Imports are removed from the similarity computation
+ignore-imports=no
+
+# Signatures are removed from the similarity computation
+ignore-signatures=yes
+
+# Minimum lines number of a similarity.
+min-similarity-lines=4
+
+
+[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 be considered directives if they
+# appear at the beginning of a comment and should not be checked.
+spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy:
+
+# 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
+
+
+[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
+
+
+[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=base.cr,
+                  base.le,
+                  base.localAvatar,
+                  Pmw.AboutDialog,
+                  Pmw.Balloon,
+                  Pmw.ButtonBox,
+                  Pmw.Color,
+                  Pmw.ComboBox,
+                  Pmw.EntryField,
+                  Pmw.Group,
+                  Pmw.INITOPT,
+                  Pmw.MegaArchetype,
+                  Pmw.MegaToplevel,
+                  Pmw.MegaWidget,
+                  Pmw.MenuBar,
+                  Pmw.MessageBar,
+                  Pmw.NoteBook,
+                  Pmw.OK,
+                  Pmw.OptionMenu,
+                  Pmw.OptionMenu,
+                  Pmw.PARTIAL,
+                  Pmw.PanedWidget,
+                  Pmw.ScrolledCanvas,
+                  Pmw.ScrolledFrame,
+                  Pmw.ScrolledListBox,
+                  Pmw.ScrolledText,
+                  Pmw.aboutcontact,
+                  Pmw.aboutcopyright,
+                  Pmw.aboutversion,
+                  Pmw.forwardmethods,
+                  Pmw.integervalidator,
+                  Pmw.popgrab,
+                  Pmw.pushgrab,
+                  Pmw.realvalidator,
+                  Pmw.setgeometryanddeiconify
+
+# 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 symbolic message names to ignore for Mixin members.
+ignored-checks-for-mixins=no-member,
+                          not-async-context-manager,
+                          not-context-manager,
+                          attribute-defined-outside-init
+
+# 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
+
+# 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
+
+# Regex pattern to define which classes are considered mixins.
+mixin-class-rgx=.*[Mm]ixin
+
+# List of decorators that change the signature of a decorated function.
+signature-mutators=
+
+
+[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,
+                    render,
+                    hidden,
+                    cluster,
+                    launcher,
+                    taskMgr,
+                    localAvatar,
+                    game
+
+# Tells whether unused global variables should be treated as a violation.
+allow-global-unused-variables=yes
+
+# List of names allowed to shadow builtins
+allowed-redefined-builtins=
+
+# 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.
+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,direct.showbase.ShowBase,direct.showbase.PythonUtil

+ 1 - 0
BACKERS.md

@@ -27,6 +27,7 @@ This is a list of all the people who are contributing financially to Panda3D.  I
 * Maxwell Dreytser
 * SureBet
 * Gyedo Jeon
+* GameDev JONI
 
 ## Backers
 

+ 3 - 3
README.md

@@ -106,14 +106,14 @@ python3 makepanda/makepanda.py --everything --installer --no-egl --no-gles --no-
 You will probably see some warnings saying that it's unable to find several
 dependency packages.  You should determine which ones you want to include in
 your build and install the respective development packages.  You may visit
-[this manual page](https://www.panda3d.org/manual/?title=Third-party_dependencies_and_license_info)
+[this manual page](https://docs.panda3d.org/1.11/python/distribution/thirdparty-licenses)
 for an overview of the various dependencies.
 
 If you are on Ubuntu, this command should cover the most frequently
 used third-party packages:
 
 ```bash
-sudo apt-get install build-essential pkg-config fakeroot python3-dev libpng-dev libjpeg-dev libtiff-dev zlib1g-dev libssl-dev libx11-dev libgl1-mesa-dev libxrandr-dev libxxf86dga-dev libxcursor-dev bison flex libfreetype6-dev libvorbis-dev libeigen3-dev libopenal-dev libode-dev libbullet-dev nvidia-cg-toolkit libgtk2.0-dev libassimp-dev libopenexr-dev
+sudo apt-get install build-essential pkg-config fakeroot python3-dev libpng-dev libjpeg-dev libtiff-dev zlib1g-dev libssl-dev libx11-dev libgl1-mesa-dev libxrandr-dev libxxf86dga-dev libxcursor-dev bison flex libfreetype6-dev libvorbis-dev libeigen3-dev libopenal-dev libode-dev libbullet-dev nvidia-cg-toolkit libgtk-3-dev libassimp-dev libopenexr-dev
 ```
 
 Once Panda3D has built, you can either install the .deb or .rpm package that
@@ -136,7 +136,7 @@ macOS
 -----
 
 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.13/panda3d-1.10.13-tools-mac.tar.gz).
+compile Panda3D, which can be acquired from [here](https://www.panda3d.org/download/panda3d-1.10.14/panda3d-1.10.14-tools-mac.tar.gz).
 
 After placing the thirdparty directory inside the panda3d source directory,
 you may build Panda3D using a command like the following:

+ 3 - 2
contrib/src/ai/aiWorld.cxx

@@ -99,8 +99,9 @@ Flock AIWorld::get_flock(unsigned int flock_id) {
       return *_flock_pool[i];
     }
   }
-  Flock *null_flock = nullptr;
-  return *null_flock;
+  static Flock null_flock(0, 0.0, 0.0, 0, 0, 0);
+  nassertr(false, null_flock);
+  return null_flock;
 }
 
 /**

+ 23 - 1
direct/src/actor/Actor.py

@@ -6,7 +6,29 @@ to learn more about loading animated models.
 
 __all__ = ['Actor']
 
-from panda3d.core import *
+from panda3d.core import (
+    AnimBundleNode,
+    AnimControlCollection,
+    Character,
+    ConfigVariableBool,
+    DecalEffect,
+    Filename,
+    GlobPattern,
+    LineStream,
+    LoaderOptions,
+    LODNode,
+    ModelNode,
+    MovingPartBase,
+    MovingPartMatrix,
+    NodePath,
+    PandaNode,
+    PartBundle,
+    PartSubset,
+    Point3,
+    TransformState,
+    Vec3,
+    autoBind,
+)
 from panda3d.core import Loader as PandaLoader
 from direct.showbase.DirectObject import DirectObject
 from direct.showbase.Loader import Loader

+ 26 - 26
direct/src/cluster/ClusterClient.py

@@ -1,8 +1,26 @@
 """ClusterClient: Master for multi-piping or PC clusters."""
 
-from panda3d.core import *
-from .ClusterMsgs import *
-from .ClusterConfig import *
+from panda3d.core import (
+    ClockObject,
+    ConnectionWriter,
+    Point3,
+    QueuedConnectionManager,
+    QueuedConnectionReader,
+    VBase3,
+    Vec3,
+    decomposeMatrix,
+)
+from .ClusterMsgs import (
+    CLUSTER_DAEMON_PORT,
+    CLUSTER_NAMED_MOVEMENT_DONE,
+    CLUSTER_NAMED_OBJECT_MOVEMENT,
+    CLUSTER_NONE,
+    CLUSTER_SERVER_PORT,
+    CLUSTER_SWAP_READY,
+    SERVER_STARTUP_STRING,
+    ClusterMsgHandler,
+)
+from .ClusterConfig import ClientConfigs
 from direct.directnotify import DirectNotifyGlobal
 from direct.showbase import DirectObject
 from direct.task import Task
@@ -95,7 +113,6 @@ class ClusterClient(DirectObject.DirectObject):
         self.notify.debug('post startMoveCam')
         self.startMoveSelectedTask()
 
-
     def startReaderPollTask(self):
         """ Task to handle datagrams from server """
         # Run this task just after the listener poll task
@@ -112,7 +129,6 @@ class ClusterClient(DirectObject.DirectObject):
 
         return Task.cont
 
-
     def startControlObjectTask(self):
         self.notify.debug("moving control objects")
         taskMgr.add(self.controlObjectTask,"controlObjectTask",50)
@@ -134,7 +150,6 @@ class ClusterClient(DirectObject.DirectObject):
         self.notify.debug('adding move cam')
         taskMgr.add(self.moveCameraTask, "moveCamTask", 49)
 
-
     def controlObjectTask(self, task):
         for pair in self.sortedControlMappings:
             object     = pair[1]
@@ -153,8 +168,6 @@ class ClusterClient(DirectObject.DirectObject):
         for server in serverList:
             self.serverList[server].sendNamedMovementDone()
 
-
-
     def redoSortedPriorities(self):
         self.sortedControlMappings = []
         for key in self.controlMappings:
@@ -176,7 +189,6 @@ class ClusterClient(DirectObject.DirectObject):
         for server in serverList:
             self.serverList[server].sendMoveNamedObject(xyz,hpr,scale,color,hidden,object)
 
-
     def moveCameraTask(self, task):
         self.moveCamera(
             base.camera.getPos(render),
@@ -203,7 +215,6 @@ class ClusterClient(DirectObject.DirectObject):
                 server.sendMoveSelected(xyz, hpr, scale)
         return Task.cont
 
-
     def addNamedObjectMapping(self, object, name, hasColor = True):
         if name not in self.objectMappings:
             self.objectMappings[name] = object
@@ -215,7 +226,6 @@ class ClusterClient(DirectObject.DirectObject):
         if name in self.objectMappings:
             self.objectMappings.pop(name)
 
-
     def addControlMapping(self, objectName, controlledName, serverList = None,
                           offset = None, priority = 0):
         if objectName not in self.controlMappings:
@@ -260,7 +270,6 @@ class ClusterClient(DirectObject.DirectObject):
                     self.controlPriorities.pop(name)
         self.redoSortedPriorities()
 
-
     def getNodePathFindCmd(self, nodePath):
         pathString = repr(nodePath)
         index = pathString.find('/')
@@ -280,7 +289,6 @@ class ClusterClient(DirectObject.DirectObject):
         else:
             return pathString
 
-
     def addObjectTag(self,object,selectFunction,deselectFunction,selectArgs,deselectArgs):
         newTag = {}
         newTag["selectFunction"] = selectFunction
@@ -289,12 +297,10 @@ class ClusterClient(DirectObject.DirectObject):
         newTag["deselectArgs"]     = deselectArgs
         self.taggedObjects[object] = newTag
 
-
     def removeObjectTag(self,object):
 
         self.taggedObjects.pop(object)
 
-
     def selectNodePath(self, nodePath):
         name = self.getNodePathName(nodePath)
         if name in self.taggedObjects:
@@ -307,7 +313,6 @@ class ClusterClient(DirectObject.DirectObject):
         else:
             self(self.getNodePathFindCmd(nodePath) + '.select()', 0)
 
-
     def deselectNodePath(self, nodePath):
         name = self.getNodePathName(nodePath)
         if name in self.taggedObjects:
@@ -345,7 +350,6 @@ class ClusterClient(DirectObject.DirectObject):
             # Execute locally
             exec(commandString, __builtins__)
 
-
     def handleDatagram(self,dgi,type,server):
         if type == CLUSTER_NONE:
             pass
@@ -370,7 +374,6 @@ class ClusterClient(DirectObject.DirectObject):
         # clear the queue
         self.serverQueues[server] = []
 
-
     def handleNamedMovement(self, data):
         """ Update cameraJig position to reflect latest position """
 
@@ -390,7 +393,6 @@ class ClusterClient(DirectObject.DirectObject):
         else:
             self.notify.debug("recieved unknown named object command: "+name)
 
-
     def exit(self):
         # Execute remotely
         for server in self.serverList:
@@ -457,8 +459,6 @@ class DisplayConnection:
             self.qcr.addConnection(self.tcpConn)
             self.cw=ConnectionWriter(qcm, 0)
 
-
-
     def poll(self):
         """ Non blocking task to read all available datagrams """
         dataGrams = []
@@ -473,9 +473,6 @@ class DisplayConnection:
 
         return dataGrams
 
-
-
-
     def sendCamOffset(self, xyz, hpr):
         ClusterClient.notify.debug("send cam offset...")
         ClusterClient.notify.debug(("packet %d xyz, hpr=%f %f %f %f %f %f" %
@@ -491,12 +488,11 @@ class DisplayConnection:
              (" fl, fs, fo=%0.3f, (%0.3f, %0.3f), (%0.3f, %0.3f)" %
               (focalLength, filmSize[0], filmSize[1],
                filmOffset[0], filmOffset[1])))
-            )
+        )
         datagram = self.msgHandler.makeCamFrustumDatagram(
             focalLength, filmSize, filmOffset)
         self.cw.send(datagram, self.tcpConn)
 
-
     def sendNamedMovementDone(self):
 
         datagram = self.msgHandler.makeNamedMovementDone()
@@ -566,6 +562,7 @@ class DisplayConnection:
             frameCount, frameTime, dt)
         self.cw.send(datagram, self.tcpConn)
 
+
 class ClusterConfigItem:
     def __init__(self, serverConfigName, serverName,
                  serverDaemonPort, serverMsgPort):
@@ -581,9 +578,11 @@ class ClusterConfigItem:
         self.focalLength = None
         self.filmSize = None
         self.filmOffset = None
+
     def setCamOffset(self, xyz, hpr):
         self.xyz = xyz
         self.hpr = hpr
+
     def setCamFrustum(self, focalLength, filmSize, filmOffset):
         self.fFrustum = 1
         self.focalLength = focalLength
@@ -671,6 +670,7 @@ def createClusterClient():
 class DummyClusterClient(DirectObject.DirectObject):
     """ Dummy class to handle command strings when not in cluster mode """
     notify = DirectNotifyGlobal.directNotify.newCategory("DummyClusterClient")
+
     def __init__(self):
         pass
 

+ 13 - 13
direct/src/cluster/ClusterConfig.py

@@ -1,5 +1,5 @@
 
-from .ClusterClient import *
+from panda3d.core import Vec3
 
 #: A dictionary of information for various cluster configurations.
 #: Dictionary is keyed on cluster-config string
@@ -26,7 +26,7 @@ ClientConfigs = {
                               'display mode': 'client',
                               'pos': Vec3(0),
                               'hpr': Vec3(0)}
-                             ],
+                            ],
     'two-server':          [{'display name': 'master',
                               'display mode': 'client',
                               'pos': Vec3(0),
@@ -35,7 +35,7 @@ ClientConfigs = {
                               'pos': Vec3(0),
                               'hpr': Vec3(0)
                               }
-                             ],
+                            ],
     'three-server':          [{'display name': 'master',
                               'display mode': 'client',
                               'pos': Vec3(0),
@@ -48,14 +48,14 @@ ClientConfigs = {
                               'pos': Vec3(0),
                               'hpr': Vec3(0)
                               }
-                             ],
+                              ],
     'mono-cave':   [{'display name': 'la',
                       'pos': Vec3(-0.105, -0.020, 5.000),
                       'hpr': Vec3(51.213, 0.000, 0.000),
                       'focal length': 0.809,
                       'film size': (1.000, 0.831),
                       'film offset': (0.000, 0.173),
-                      },
+                     },
                      {'display name': 'lb',
                       'display mode': 'client',
                       'pos': Vec3(-0.105, -0.020, 5.000),
@@ -71,7 +71,7 @@ ClientConfigs = {
                       'film size': (1.000, 0.830),
                       'film offset': (-0.000, 0.173),
                       },
-                     ],
+                    ],
     'seamless-cave':   [{'display name': 'master',
                           'display mode': 'client',
                           'pos': Vec3(-0.105, -0.020, 5.000),
@@ -79,7 +79,7 @@ ClientConfigs = {
                           'focal length': 0.815,
                           'film size': (1.000, 0.831),
                           'film offset': (0.000, 0.173),
-                          },
+                         },
                          {'display name': 'la',
                           'pos': Vec3(-0.105, -0.020, 5.000),
                           'hpr': Vec3(51.213, 0.000, 0.000),
@@ -122,12 +122,12 @@ ClientConfigs = {
                           'film size': (1.000, 0.831),
                           'film offset': (-0.000, 0.173),
                           },
-                         ],
+                        ],
     'ursula':              [{'display name': 'master',
                               'display mode': 'client',
                               'pos': Vec3(0),
                               'hpr': Vec3(0),
-                              },
+                             },
                              {'display name': 'l',
                               'pos': Vec3(-.105, 0, 0),
                               'hpr': Vec3(0, 0, 0),
@@ -144,11 +144,11 @@ ClientConfigs = {
                               #'film offset': (-0.105, -2),
                               'film offset': (-0.105, -1),
                               }
-                             ],
+                            ],
     'composite':           [{'display name': 'master',
                               'display mode': 'client',
                               'pos': Vec3(0),
-                              },
+                             },
                              {'display name': 'left',
                               'pos': Vec3(-0.105, -0.020, 5.000),
                               'hpr': Vec3(-0.370, 0.000, 0.000),
@@ -163,5 +163,5 @@ ClientConfigs = {
                               'film size': (1.000, 0.831),
                               'film offset': (0.000, 0.173),
                               }
-                             ],
-    }
+                            ],
+}

+ 1 - 1
direct/src/cluster/ClusterMsgs.py

@@ -3,7 +3,7 @@
 # This module is intended to supply routines and dataformats common to
 # both ClusterClient and ClusterServer.
 
-from panda3d.core import *
+from panda3d.core import NetDatagram
 from direct.distributed.PyDatagram import PyDatagram
 from direct.distributed.PyDatagramIterator import PyDatagramIterator
 import time

+ 35 - 19
direct/src/cluster/ClusterServer.py

@@ -1,10 +1,35 @@
-from panda3d.core import *
-from .ClusterMsgs import *
-from direct.distributed.MsgTypes import *
+from panda3d.core import (
+    ClockObject,
+    ConnectionWriter,
+    NetAddress,
+    PointerToConnection,
+    QueuedConnectionListener,
+    QueuedConnectionManager,
+    QueuedConnectionReader,
+    Vec3,
+)
+from .ClusterMsgs import (
+    CLUSTER_CAM_FRUSTUM,
+    CLUSTER_CAM_MOVEMENT,
+    CLUSTER_CAM_OFFSET,
+    CLUSTER_COMMAND_STRING,
+    CLUSTER_DAEMON_PORT,
+    CLUSTER_EXIT,
+    CLUSTER_NAMED_MOVEMENT_DONE,
+    CLUSTER_NAMED_OBJECT_MOVEMENT,
+    CLUSTER_NONE,
+    CLUSTER_SELECTED_MOVEMENT,
+    CLUSTER_SERVER_PORT,
+    CLUSTER_SWAP_NOW,
+    CLUSTER_SWAP_READY,
+    CLUSTER_TIME_DATA,
+    ClusterMsgHandler,
+)
 from direct.directnotify import DirectNotifyGlobal
 from direct.showbase import DirectObject
 from direct.task import Task
 from direct.task.TaskManagerGlobal import taskMgr
+import builtins
 
 # NOTE: This assumes the following variables are set via bootstrap command line
 # arguments on server startup:
@@ -15,6 +40,7 @@ from direct.task.TaskManagerGlobal import taskMgr
 # Also, I'm not sure multiple camera-group configurations are working for the
 # cluster system.
 
+
 class ClusterServer(DirectObject.DirectObject):
     notify = DirectNotifyGlobal.directNotify.newCategory("ClusterServer")
     MSG_NUM = 2000000
@@ -76,8 +102,6 @@ class ClusterServer(DirectObject.DirectObject):
             clusterDaemonPort = CLUSTER_DAEMON_PORT
         self.daemon.serverReady(clusterDaemonClient, clusterDaemonPort)
 
-
-
     def startListenerPollTask(self):
         # Run this task near the start of frame, sometime after the dataLoop
         taskMgr.add(self.listenerPollTask, "serverListenerPollTask", -40)
@@ -100,7 +124,6 @@ class ClusterServer(DirectObject.DirectObject):
                 self.notify.warning("getNewConnection returned false")
         return Task.cont
 
-
     def addNamedObjectMapping(self, object, name, hasColor = True,
                               priority = 0):
         if name not in self.objectMappings:
@@ -113,7 +136,6 @@ class ClusterServer(DirectObject.DirectObject):
         if name in self.objectMappings:
             self.objectMappings.pop(name)
 
-
     def redoSortedPriorities(self):
 
         self.sortedControlMappings = []
@@ -123,7 +145,6 @@ class ClusterServer(DirectObject.DirectObject):
 
         self.sortedControlMappings.sort()
 
-
     def addControlMapping(self, objectName, controlledName, offset = None,
                           priority = 0):
         if objectName not in self.controlMappings:
@@ -134,20 +155,18 @@ class ClusterServer(DirectObject.DirectObject):
             self.controlPriorities[objectName] = priority
             self.redoSortedPriorities()
         else:
-            self.notify.debug('attempt to add duplicate controlled object: '+name)
+            self.notify.debug('attempt to add duplicate controlled object: ' + objectName)
 
     def setControlMappingOffset(self, objectName, offset):
         if objectName in self.controlMappings:
             self.controlOffsets[objectName] = offset
 
-
     def removeControlMapping(self, name):
         if name in self.controlMappings:
             self.controlMappings.pop(name)
             self.controlPriorities.pop(name)
         self.redoSortedPriorities()
 
-
     def startControlObjectTask(self):
         self.notify.debug("moving control objects")
         taskMgr.add(self.controlObjectTask,"controlObjectTask",50)
@@ -164,7 +183,6 @@ class ClusterServer(DirectObject.DirectObject):
         self.sendNamedMovementDone()
         return Task.cont
 
-
     def sendNamedMovementDone(self):
         self.notify.debug("named movement done")
         datagram = self.msgHandler.makeNamedMovementDone()
@@ -308,12 +326,10 @@ class ClusterServer(DirectObject.DirectObject):
         else:
             self.notify.debug("recieved unknown named object command: "+name)
 
-
     def handleMessageQueue(self):
-
-        #print self.messageQueue
+        #print(self.messageQueue)
         for data in self.messageQueue:
-            #print "in queue",dgi
+            #print("in queue", dgi)
             self.handleNamedMovement(data)
 
         self.messageQueue = []
@@ -328,8 +344,8 @@ class ClusterServer(DirectObject.DirectObject):
         """ Update cameraJig position to reflect latest position """
         (x, y, z, h, p, r, sx, sy, sz) = self.msgHandler.parseSelectedMovementDatagram(
             dgi)
-        if last:
-            last.setPosHprScale(x, y, z, h, p, r, sx, sy, sz)
+        if getattr(builtins, 'last', None):
+            builtins.last.setPosHprScale(x, y, z, h, p, r, sx, sy, sz)
 
     def handleTimeData(self, dgi):
         """ Update cameraJig position to reflect latest position """
@@ -345,5 +361,5 @@ class ClusterServer(DirectObject.DirectObject):
         command = self.msgHandler.parseCommandStringDatagram(dgi)
         try:
             exec(command, __builtins__)
-        except:
+        except Exception:
             pass

+ 1 - 1
direct/src/controls/BattleWalker.py

@@ -2,7 +2,7 @@
 from direct.showbase.InputStateGlobal import inputState
 from direct.showbase.MessengerGlobal import messenger
 from direct.task.Task import Task
-from panda3d.core import *
+from panda3d.core import ClockObject, Mat3, Point3, Vec3
 from . import GravityWalker
 
 BattleStrafe = 0

+ 2 - 5
direct/src/controls/ControlManager.py

@@ -41,8 +41,6 @@ class ControlManager:
         #self.monitorTask = taskMgr.add(self.monitor, "ControlManager-%s"%(id(self)), priority=-1)
         self.forceAvJumpToken = None
 
-
-
         if self.passMessagesThrough: # for not breaking toontown
             ist=self.inputStateTokens
             ist.append(inputState.watchWithModifiers("forward", "arrow_up", inputSource=inputState.ArrowKeys))
@@ -50,7 +48,6 @@ class ControlManager:
             ist.append(inputState.watchWithModifiers("turnLeft", "arrow_left", inputSource=inputState.ArrowKeys))
             ist.append(inputState.watchWithModifiers("turnRight", "arrow_right", inputSource=inputState.ArrowKeys))
 
-
     def __str__(self):
         return 'ControlManager: using \'%s\'' % self.currentControlsName
 
@@ -325,7 +322,7 @@ class ControlManager:
             self.WASDTurnTokens = (
                 inputState.watchWithModifiers("turnLeft", "a", inputSource=inputState.WASD),
                 inputState.watchWithModifiers("turnRight", "d", inputSource=inputState.WASD),
-                )
+            )
 
             inputState.set("turnLeft", slideLeftWASDSet, inputSource=inputState.WASD)
             inputState.set("turnRight", slideRightWASDSet, inputSource=inputState.WASD)
@@ -337,7 +334,7 @@ class ControlManager:
             self.WASDTurnTokens = (
                 inputState.watchWithModifiers("slideLeft", "a", inputSource=inputState.WASD),
                 inputState.watchWithModifiers("slideRight", "d", inputSource=inputState.WASD),
-                )
+            )
 
             inputState.set("slideLeft", turnLeftWASDSet, inputSource=inputState.WASD)
             inputState.set("slideRight", turnRightWASDSet, inputSource=inputState.WASD)

+ 1 - 1
direct/src/controls/DevWalker.py

@@ -22,7 +22,7 @@ from direct.showbase import DirectObject
 from direct.showbase.MessengerGlobal import messenger
 from direct.task.Task import Task
 from direct.task.TaskManagerGlobal import taskMgr
-from panda3d.core import *
+from panda3d.core import ClockObject, ConfigVariableBool, ConfigVariableDouble, Mat3, Point3, Vec3
 
 
 class DevWalker(DirectObject.DirectObject):

+ 18 - 3
direct/src/controls/GravityWalker.py

@@ -22,9 +22,24 @@ from direct.showbase.InputStateGlobal import inputState
 from direct.showbase.MessengerGlobal import messenger
 from direct.task.Task import Task
 from direct.task.TaskManagerGlobal import taskMgr
-from direct.extensions_native import VBase3_extensions
-from direct.extensions_native import VBase4_extensions
-from panda3d.core import *
+from direct.extensions_native import VBase3_extensions # pylint: disable=unused-import
+from direct.extensions_native import VBase4_extensions # pylint: disable=unused-import
+from panda3d.core import (
+    BitMask32,
+    ClockObject,
+    CollisionHandlerEvent,
+    CollisionHandlerFluidPusher,
+    CollisionHandlerGravity,
+    CollisionHandlerPusher,
+    CollisionNode,
+    CollisionRay,
+    CollisionSphere,
+    CollisionTraverser,
+    ConfigVariableBool,
+    Mat3,
+    Point3,
+    Vec3,
+)
 import math
 
 

+ 14 - 3
direct/src/controls/InputState.py

@@ -8,36 +8,47 @@ from direct.showbase.MessengerGlobal import messenger
 class InputStateToken:
     _SerialGen = SerialNumGen()
     Inval = 'invalidatedToken'
+
     def __init__(self, inputState):
         self._id = InputStateToken._SerialGen.next()
         self._hash = self._id
         self._inputState = inputState
+
     def release(self):
         # subclasses will override
         assert False
+
     def isValid(self):
         return self._id != InputStateToken.Inval
+
     def invalidate(self):
         self._id = InputStateToken.Inval
+
     def __hash__(self):
         return self._hash
 
     #snake_case alias:
     is_valid = isValid
 
+
 class InputStateWatchToken(InputStateToken, DirectObject.DirectObject):
     def release(self):
         self._inputState._ignore(self)
         self.ignoreAll()
+
+
 class InputStateForceToken(InputStateToken):
     def release(self):
         self._inputState._unforce(self)
 
+
 class InputStateTokenGroup:
     def __init__(self):
         self._tokens = []
+
     def addToken(self, token):
         self._tokens.append(token)
+
     def release(self):
         for token in self._tokens:
             token.release()
@@ -46,6 +57,7 @@ class InputStateTokenGroup:
     #snake_case alias:
     add_token = addToken
 
+
 class InputState(DirectObject.DirectObject):
     """
     InputState is for tracking the on/off state of some events.
@@ -190,7 +202,6 @@ class InputState(DirectObject.DirectObject):
         # input state simply because we're not looking at it anymore.
         # self.set(name, False, inputSource)
 
-
     def force(self, name, value, inputSource):
         """
         Force isSet(name) to return 'value'.
@@ -213,7 +224,7 @@ class InputState(DirectObject.DirectObject):
                 self.notify.error(
                     "%s is trying to force '%s' to ON, but '%s' is already being forced OFF by %s" %
                     (inputSource, name, name, self._forcingOff[name])
-                    )
+                )
             self._forcingOn.setdefault(name, set())
             self._forcingOn[name].add(inputSource)
         else:
@@ -221,7 +232,7 @@ class InputState(DirectObject.DirectObject):
                 self.notify.error(
                     "%s is trying to force '%s' to OFF, but '%s' is already being forced ON by %s" %
                     (inputSource, name, name, self._forcingOn[name])
-                    )
+                )
             self._forcingOff.setdefault(name, set())
             self._forcingOff[name].add(inputSource)
         return token

+ 14 - 1
direct/src/controls/NonPhysicsWalker.py

@@ -23,7 +23,20 @@ from direct.showbase.InputStateGlobal import inputState
 from direct.showbase.MessengerGlobal import messenger
 from direct.task.Task import Task
 from direct.task.TaskManagerGlobal import taskMgr
-from panda3d.core import *
+from panda3d.core import (
+    BitMask32,
+    ClockObject,
+    CollisionHandlerFloor,
+    CollisionHandlerPusher,
+    CollisionNode,
+    CollisionRay,
+    CollisionSphere,
+    CollisionTraverser,
+    ConfigVariableBool,
+    Mat3,
+    Point3,
+    Vec3,
+)
 
 class NonPhysicsWalker(DirectObject.DirectObject):
     notify = DirectNotifyGlobal.directNotify.newCategory("NonPhysicsWalker")

+ 7 - 1
direct/src/controls/ObserverWalker.py

@@ -16,7 +16,13 @@ although it does send messages that allow a listener to play sounds or
 animations based on walker events.
 """
 
-from panda3d.core import *
+from panda3d.core import (
+    BitMask32,
+    CollisionHandlerPusher,
+    CollisionNode,
+    CollisionSphere,
+    CollisionTraverser,
+)
 from direct.directnotify import DirectNotifyGlobal
 from . import NonPhysicsWalker
 

+ 30 - 6
direct/src/controls/PhysicsWalker.py

@@ -23,15 +23,39 @@ from direct.showbase.InputStateGlobal import inputState
 from direct.showbase.MessengerGlobal import messenger
 from direct.task.Task import Task
 from direct.task.TaskManagerGlobal import taskMgr
-from direct.extensions_native import Mat3_extensions
-from direct.extensions_native import VBase3_extensions
-from direct.extensions_native import VBase4_extensions
-from panda3d.core import *
-from panda3d.physics import *
+from direct.extensions_native import Mat3_extensions # pylint: disable=unused-import
+from direct.extensions_native import VBase3_extensions # pylint: disable=unused-import
+from direct.extensions_native import VBase4_extensions # pylint: disable=unused-import
+from panda3d.core import (
+    BitMask32,
+    ClockObject,
+    CollisionHandlerFloor,
+    CollisionHandlerQueue,
+    CollisionNode,
+    CollisionRay,
+    CollisionSphere,
+    CollisionTraverser,
+    ConfigVariableBool,
+    LRotationf,
+    Mat3,
+    NodePath,
+    Point3,
+    Vec3,
+)
+from panda3d.physics import (
+    ActorNode,
+    ForceNode,
+    LinearEulerIntegrator,
+    LinearFrictionForce,
+    LinearVectorForce,
+    PhysicsCollisionHandler,
+    PhysicsManager,
+)
 import math
 
 #import LineStream
 
+
 class PhysicsWalker(DirectObject.DirectObject):
 
     notify = DirectNotifyGlobal.directNotify.newCategory("PhysicsWalker")
@@ -523,7 +547,7 @@ class PhysicsWalker(DirectObject.DirectObject):
                 onScreenDebug.add("highMark", "% 10.4f"%(self.highMark,))
         #if airborneHeight < 0.1: #contact!=Vec3.zero():
         if (airborneHeight > self.avatarRadius*0.5
-                or physObject.getVelocity().getZ() > 0.0
+                    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

+ 5 - 2
direct/src/controls/TwoDWalker.py

@@ -2,9 +2,12 @@
 TwoDWalker.py is for controlling the avatars in a 2D scroller game environment.
 """
 
-from .GravityWalker import *
+from .GravityWalker import GravityWalker
+from direct.directnotify.DirectNotifyGlobal import directNotify
+from direct.showbase.InputStateGlobal import inputState
 from direct.showbase.MessengerGlobal import messenger
-from panda3d.core import ConfigVariableBool
+from direct.task.Task import Task
+from panda3d.core import ConfigVariableBool, Vec3
 
 
 class TwoDWalker(GravityWalker):

+ 1 - 1
direct/src/dcparser/dcClassParameter.h

@@ -37,7 +37,7 @@ PUBLISHED:
   const DCClass *get_class() const;
 
 public:
-  virtual DCPackerInterface *get_nested_field(int n) const;
+  virtual DCPackerInterface *get_nested_field(int n) const final;
 
   virtual void output_instance(std::ostream &out, bool brief, const std::string &prename,
                                const std::string &name, const std::string &postname) const;

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

@@ -1,8 +1,7 @@
 print('TestStart: Starting up test environment.')
 
-from panda3d.core import *
+import direct.showbase.PythonUtil  # pylint: disable=unused-import
 
-from direct.showbase.PythonUtil import *
 from direct.showbase import ShowBase
 base = ShowBase.ShowBase()
 

+ 1 - 2
direct/src/directbase/ThreeUpStart.py

@@ -1,9 +1,8 @@
 
 print('ThreeUpStart: Starting up environment.')
 
-from panda3d.core import *
+import direct.showbase.PythonUtil  # pylint: disable=unused-import
 
-from direct.showbase.PythonUtil import *
 from direct.showbase import ThreeUpShow
 
 base = ThreeUpShow.ThreeUpShow()

+ 15 - 8
direct/src/directdevices/DirectDeviceManager.py

@@ -1,8 +1,15 @@
 """Class used to create and control VRPN devices."""
 
 from direct.showbase.DirectObject import DirectObject
-from panda3d.core import *
-from panda3d.vrpn import *
+from panda3d.core import (
+    AnalogNode,
+    ButtonNode,
+    ConfigVariableDouble,
+    ConfigVariableString,
+    DialNode,
+    TrackerNode,
+)
+from panda3d.vrpn import VrpnClient
 
 ANALOG_MIN = -0.95
 ANALOG_MAX = 0.95
@@ -52,7 +59,7 @@ class DirectButtons(ButtonNode, DirectObject):
         # Attach node to data graph
         try:
             self._base = base
-        except:
+        except NameError:
             self._base = simbase
         self.nodePath = self._base.dataRoot.attachNewNode(self)
 
@@ -101,7 +108,7 @@ class DirectAnalogs(AnalogNode, DirectObject):
         # Attach node to data graph
         try:
             self._base = base
-        except:
+        except NameError:
             self._base = simbase
         self.nodePath = self._base.dataRoot.attachNewNode(self)
 
@@ -169,7 +176,7 @@ class DirectAnalogs(AnalogNode, DirectObject):
 
     def normalizeChannel(self, chan, minVal = -1, maxVal = 1, sf = 1.0):
         try:
-            return self.normalize(self[chan], minVal, maxVal, sfx)
+            return self.normalize(self[chan], minVal, maxVal, sf)
         except IndexError:
             return 0.0
 
@@ -199,7 +206,7 @@ class DirectTracker(TrackerNode, DirectObject):
         # Attach node to data graph
         try:
             self._base = base
-        except:
+        except NameError:
             self._base = simbase
         self.nodePath = self._base.dataRoot.attachNewNode(self)
 
@@ -231,7 +238,7 @@ class DirectDials(DialNode, DirectObject):
         # Attach node to data graph
         try:
             self._base = base
-        except:
+        except NameError:
             self._base = simbase
         self.nodePath = self._base.dataRoot.attachNewNode(self)
 
@@ -282,7 +289,7 @@ class DirectTimecodeReader(AnalogNode, DirectObject):
         # Attach node to data graph
         try:
             self._base = base
-        except:
+        except NameError:
             self._base = simbase
         self.nodePath = self._base.dataRoot.attachNewNode(self)
 

+ 2 - 1
direct/src/directdevices/DirectFastrak.py

@@ -1,8 +1,9 @@
 """ Class used to create and control radamec device """
+from panda3d.core import Vec3
 from direct.showbase.DirectObject import DirectObject
 from direct.task.Task import Task
 from direct.task.TaskManagerGlobal import taskMgr
-from .DirectDeviceManager import *
+from .DirectDeviceManager import DirectDeviceManager
 
 from direct.directnotify import DirectNotifyGlobal
 

+ 29 - 29
direct/src/directdevices/DirectJoybox.py

@@ -1,11 +1,11 @@
 """ Class used to create and control joybox device """
 from direct.showbase.DirectObject import DirectObject
-from .DirectDeviceManager import *
-from direct.directtools.DirectUtil import *
+from .DirectDeviceManager import ANALOG_DEADBAND, ANALOG_MAX, ANALOG_MIN, DirectDeviceManager
+from direct.directtools.DirectUtil import CLAMP
 from direct.gui import OnscreenText
 from direct.task import Task
 from direct.task.TaskManagerGlobal import taskMgr
-from panda3d.core import ClockObject
+from panda3d.core import ButtonRegistry, ButtonThrower, ClockObject, NodePath, VBase3, Vec3
 
 import math
 
@@ -35,6 +35,7 @@ JOYBOX_RANGE = JOYBOX_MAX - JOYBOX_MIN
 
 JOYBOX_TREAD_SEPERATION = 1.0
 
+
 class DirectJoybox(DirectObject):
     joyboxCount = 0
     xyzMultiplier = 1.0
@@ -87,12 +88,10 @@ class DirectJoybox(DirectObject):
         # Spawn update task
         self.enable()
 
-
     def setHeadingNodePath(self,np):
 
         self.headingNP = np
 
-
     def enable(self):
         # Kill existing task
         self.disable()
@@ -126,18 +125,25 @@ class DirectJoybox(DirectObject):
 
     def getNodePath(self):
         return self.nodePath
+
     def setRefCS(self, refCS):
         self.refCS = refCS
+
     def getRefCS(self):
         return self.refCS
+
     def getEventName(self, index):
         return self.name + '-button-' + repr(index)
+
     def setXyzMultiplier(self, multiplier):
         DirectJoybox.xyzMultiplier = multiplier
+
     def getXyzMultiplier(self):
         return DirectJoybox.xyzMultiplier
+
     def setHprMultiplier(self, multiplier):
         DirectJoybox.hprMultiplier = multiplier
+
     def getHprMultiplier(self):
         return DirectJoybox.hprMultiplier
 
@@ -193,6 +199,7 @@ class DirectJoybox(DirectObject):
 
     def acceptSwitchModeEvent(self, button = R_UPPER):
         self.accept(self.getEventName(button), self.switchMode)
+
     def ignoreSwitchModeEvent(self, button = R_UPPER):
         self.ignore(self.getEventName(button))
 
@@ -208,7 +215,7 @@ class DirectJoybox(DirectObject):
             pass
 
     def showMode(self, modeText):
-        def hideText(state, s = self):
+        def hideText(state, s=self):
             s.readout.setText('')
             return Task.done
         taskMgr.remove(self.name + '-showMode')
@@ -220,6 +227,7 @@ class DirectJoybox(DirectObject):
     def acceptUprightCameraEvent(self, button = L_UPPER):
         self.accept(self.getEventName(button),
                     base.direct.cameraControl.orbitUprightCam)
+
     def ignoreUprightCameraEvent(self, button = L_UPPER):
         self.ignore(self.getEventName(button))
 
@@ -230,29 +238,29 @@ class DirectJoybox(DirectObject):
         self.showMode(self.modeName)
         self.enable()
 
-
-    def setUseHeadingNP(self,enabled):
-
+    def setUseHeadingNP(self, enabled):
         self.useHeadingNP = enabled
 
-    def setRotateInPlace(self,enabled):
-
+    def setRotateInPlace(self, enabled):
         self.rotateInPlace = enabled
 
     def joyboxFly(self):
         # Do nothing if no nodePath selected
         if self.nodePath is None:
             return
+
         hprScale = ((self.aList[L_SLIDE] + 1.0) *
                     50.0 * DirectJoybox.hprMultiplier)
         posScale = ((self.aList[R_SLIDE] + 1.0) *
                     50.0 * DirectJoybox.xyzMultiplier)
-        def getAxisVal(index, s = self):
+
+        def getAxisVal(index, s=self):
             try:
                 return s.aList[s.mapping[index]]
             except IndexError:
                 # If it is a null axis return 0
                 return 0.0
+
         x = getAxisVal(0) * self.modifier[0]
         y = getAxisVal(1) * self.modifier[1]
         z = getAxisVal(2) * self.modifier[2]
@@ -262,13 +270,13 @@ class DirectJoybox(DirectObject):
         p = getAxisVal(4) * self.modifier[4]
         r = getAxisVal(5) * self.modifier[5]
         hpr = Vec3(h, p, r) * (hprScale * self.deltaTime)
+
         # if we are using a heading nodepath, we want
         # to drive in the direction we are facing,
         # however, we don't want the z component to change
         if self.useHeadingNP and self.headingNP is not None:
             oldZ = pos.getZ()
-            pos = self.nodePath.getRelativeVector(self.headingNP,
-                                                  pos)
+            pos = self.nodePath.getRelativeVector(self.headingNP, pos)
             pos.setZ(oldZ)
             # if we are using a heading NP we might want to rotate
             # in place around that NP
@@ -282,7 +290,6 @@ class DirectJoybox(DirectObject):
                 self.nodePath.wrtReparentTo(parent)
                 hpr = Vec3(0,0,0)
 
-
         self.nodePath.setPosHpr(self.nodePath, pos, hpr)
 
     def joeMode(self):
@@ -291,25 +298,23 @@ class DirectJoybox(DirectObject):
         self.modifier = [1, 1, 1, -1, -1, 0]
         self.setMode(self.joyboxFly, 'Joe Mode')
 
-
     def basicMode(self):
         self.mapping = [NULL_AXIS, R_FWD_BACK, NULL_AXIS,
                         R_LEFT_RIGHT, NULL_AXIS, NULL_AXIS]
-        self.modifier = [0,1,0,-1,0,0]
-        self.setMode(self.joyboxFly,'Basic Mode')
+        self.modifier = [0, 1, 0, -1, 0, 0]
+        self.setMode(self.joyboxFly, 'Basic Mode')
 
     def fpsMode(self):
         self.mapping = [L_LEFT_RIGHT,R_FWD_BACK,L_FWD_BACK,
                         R_LEFT_RIGHT, NULL_AXIS, NULL_AXIS]
-        self.modifier = [1,1,1,-1,0,0]
-        self.setMode(self.joyboxFly,'FPS Mode')
+        self.modifier = [1, 1, 1, -1, 0, 0]
+        self.setMode(self.joyboxFly, 'FPS Mode')
 
     def tankMode(self):
-        self.setMode(self.tankFly,'Tank Mode')
+        self.setMode(self.tankFly, 'Tank Mode')
 
     def nullMode(self):
-        self.setMode(self.nullFly,'Null Mode')
-
+        self.setMode(self.nullFly, 'Null Mode')
 
     def lucMode(self):
         self.mapping = [R_LEFT_RIGHT, R_FWD_BACK, L_FWD_BACK,
@@ -362,7 +367,6 @@ class DirectJoybox(DirectObject):
     def spaceMode(self):
         self.setMode(self.spaceFly, 'Space Mode')
 
-
     def nullFly(self):
         return
 
@@ -384,10 +388,6 @@ class DirectJoybox(DirectObject):
         self.nodePath.setH(self.nodePath,dh)
         self.nodePath.setY(self.nodePath,dy)
 
-
-
-
-
     def spaceFly(self):
         # Do nothing if no nodePath selected
         if self.nodePath is None:
@@ -498,9 +498,9 @@ class DirectJoybox(DirectObject):
         # Restore the original hpr of the orbiter
         self.nodePath.setHpr(self.tempCS, 0, 0, 0)
 
-
     # We need to override the DirectAnalog normalizeChannel to
     # correct the ranges of the two twist axes of the joybox.
+
     def normalizeChannel(self, chan, minVal = -1, maxVal = 1):
         try:
             if chan == L_TWIST or chan == R_TWIST:

+ 1 - 2
direct/src/directdevices/DirectRadamec.py

@@ -1,9 +1,8 @@
 """ Class used to create and control radamec device """
-from math import *
 from direct.showbase.DirectObject import DirectObject
 from direct.task.Task import Task
 from direct.task.TaskManagerGlobal import taskMgr
-from .DirectDeviceManager import *
+from .DirectDeviceManager import DirectDeviceManager
 
 from direct.directnotify import DirectNotifyGlobal
 

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

@@ -5,6 +5,7 @@ DirectNotify module: this module contains the DirectNotify class
 from . import Notifier
 from . import Logger
 
+
 class DirectNotify:
     """
     DirectNotify class: this class contains methods for creating
@@ -15,7 +16,7 @@ class DirectNotify:
         """
         DirectNotify class keeps a dictionary of Notfiers
         """
-        self.__categories = { }
+        self.__categories = {}
         # create a default log file
         self.logger = Logger.Logger()
 
@@ -100,7 +101,6 @@ class DirectNotify:
             print("DirectNotify: unknown notify level: " + str(level)
                    + " for category: " + str(categoryName))
 
-
     def setDconfigLevels(self):
         for categoryName in self.getCategories():
             self.setDconfigLevel(categoryName)

+ 13 - 13
direct/src/directnotify/Notifier.py

@@ -126,10 +126,10 @@ class Notifier:
         Exception: error
         """
         message = str(errorString)
-        if Notifier.showTime.getValue():
-            string = (self.getTime() + str(exception) + ": " + self.__name + "(error): " + message)
+        if Notifier.showTime:
+            string = f'{self.getTime()}{exception!s}: {self.__name}(error): {message}'
         else:
-            string = (str(exception) + ": " + self.__name + "(error): " + message)
+            string = f'{exception!s}: {self.__name}(error): {message}'
         self.__log(string)
         raise exception(errorString)
 
@@ -140,10 +140,10 @@ class Notifier:
         """
         if self.__warning:
             message = str(warningString)
-            if Notifier.showTime.getValue():
-                string = (self.getTime() + self.__name + '(warning): ' + message)
+            if Notifier.showTime:
+                string = f'{self.getTime()}{self.__name}(warning): {message}'
             else:
-                string = (":" + self.__name + '(warning): ' + message)
+                string = f':{self.__name}(warning): {message}'
             self.__log(string)
             self.__print(string)
         return 1 # to allow assert myNotify.warning("blah")
@@ -167,10 +167,10 @@ class Notifier:
         """
         if self.__debug:
             message = str(debugString)
-            if Notifier.showTime.getValue():
-                string = (self.getTime() + self.__name + '(debug): ' + message)
+            if Notifier.showTime:
+                string = f'{self.getTime()}{self.__name}(debug): {message}'
             else:
-                string = (':' + self.__name + '(debug): ' + message)
+                string = f':{self.__name}(debug): {message}'
             self.__log(string)
             self.__print(string)
         return 1 # to allow assert myNotify.debug("blah")
@@ -194,10 +194,10 @@ class Notifier:
         """
         if self.__info:
             message = str(infoString)
-            if Notifier.showTime.getValue():
-                string = (self.getTime() + self.__name + ': ' + message)
+            if Notifier.showTime:
+                string = f'{self.getTime()}{self.__name}: {message}'
             else:
-                string = (':' + self.__name + ': ' + message)
+                string = f':{self.__name}: {message}'
             self.__log(string)
             self.__print(string)
         return 1 # to allow assert myNotify.info("blah")
@@ -271,7 +271,7 @@ class Notifier:
                         state = "%s, %s"%(state, stateObj.getName())
 
                 if hasattr(obj, 'doId'):
-                    doId = " doId:%s"%(obj.doId,)
+                    doId = f" doId:{obj.doId}"
             #if type(obj) == types.ClassType:
             #    name = "%s."%(obj.__class__.__name__,)
             string = ":%s:%s [%-7s] id(%s)%s %s"%(

+ 9 - 1
direct/src/directscripts/eggcacher.py

@@ -11,7 +11,15 @@
 import os
 import sys
 import gc
-from panda3d.core import *
+from panda3d.core import (
+    BamCache,
+    ExecutionEnvironment,
+    Filename,
+    Loader,
+    LoaderOptions,
+    ModelPool,
+    TexturePool,
+)
 
 class EggCacher:
     def __init__(self, args):

+ 8 - 3
direct/src/directscripts/extract_docs.py

@@ -16,11 +16,11 @@ from panda3d.interrogatedb import *
 
 
 if 'interrogate_element_is_sequence' not in globals():
-    def interrogate_element_is_sequence(element):
+    def interrogate_element_is_sequence(element): # pylint: disable=function-redefined
         return False
 
 if 'interrogate_element_is_mapping' not in globals():
-    def interrogate_element_is_mapping(element):
+    def interrogate_element_is_mapping(element): # pylint: disable=function-redefined
         return False
 
 
@@ -65,6 +65,7 @@ def comment(code):
     else:
         return ''
 
+
 def block_comment(code):
     code = code.strip()
 
@@ -77,6 +78,7 @@ def block_comment(code):
 
     return code
 
+
 def translateFunctionName(name):
     if name.startswith("__"):
         return name
@@ -93,6 +95,7 @@ def translateFunctionName(name):
             new += i[0].upper() + i[1:]
     return new
 
+
 def translateTypeName(name, mangle=True):
     # Equivalent to C++ classNameFromCppName
     class_name = ""
@@ -115,6 +118,7 @@ def translateTypeName(name, mangle=True):
 
     return class_name
 
+
 def translated_type_name(type, scoped=True):
     while interrogate_type_is_wrapped(type):
         if interrogate_type_is_const(type):
@@ -205,7 +209,7 @@ def processFunction(handle, function, isConstructor = False):
 
 def processType(handle, type):
     typename = translated_type_name(type, scoped=False)
-    derivations = [ translated_type_name(interrogate_type_get_derivation(type, n)) for n in range(interrogate_type_number_of_derivations(type)) ]
+    derivations = [translated_type_name(interrogate_type_get_derivation(type, n)) for n in range(interrogate_type_number_of_derivations(type))]
 
     if interrogate_type_has_comment(type):
         print(block_comment(interrogate_type_comment(type)), file=handle)
@@ -258,6 +262,7 @@ def processType(handle, type):
 
     print("};", file=handle)
 
+
 def processModule(handle, package):
     print("Processing module %s" % (package))
     print("namespace %s {" % package, file=handle)

+ 32 - 31
direct/src/directtools/DirectCameraControl.py

@@ -1,7 +1,9 @@
+import math
+from panda3d.core import BitMask32, Mat4, NodePath, Point3, VBase3, Vec3, Vec4, rad2Deg
 from direct.showbase.DirectObject import DirectObject
-from .DirectUtil import *
-from .DirectGeometry import *
-from .DirectGlobals import *
+from .DirectUtil import CLAMP, useDirectRenderStyle
+from .DirectGeometry import getCrankAngle, getScreenXY
+from . import DirectGlobals as DG
 from .DirectSelection import SelectionRay
 from direct.interval.IntervalGlobal import Sequence, Func
 from direct.directnotify import DirectNotifyGlobal
@@ -12,6 +14,7 @@ CAM_MOVE_DURATION = 1.2
 COA_MARKER_SF = 0.0075
 Y_AXIS = Vec3(0, 1, 0)
 
+
 class DirectCameraControl(DirectObject):
 
     notify = DirectNotifyGlobal.directNotify.newCategory('DirectCameraControl')
@@ -48,7 +51,7 @@ class DirectCameraControl(DirectObject):
             ['DIRECT-mouse2Up', self.mouseFlyStop],
             ['DIRECT-mouse3', self.mouseDollyStart],
             ['DIRECT-mouse3Up', self.mouseDollyStop],
-            ]
+        ]
 
         # [gjeon] moved all of the hotkeys to single place for easy remapping
 ##         self.keyEvents = [
@@ -99,7 +102,7 @@ class DirectCameraControl(DirectObject):
             ['DIRECT-removeManipulateCameraTask', self.removeManipulateCameraTask],
             ['DIRECT-zoomInCam', self.zoomCam, 0.5, t],
             ['DIRECT-zoomOutCam', self.zoomCam, -2.0, t],
-            ]
+        ]
         # set this to true to prevent the camera from rolling
         self.lockRoll = False
         # NIK - flag to determine whether to use maya camera controls
@@ -218,9 +221,9 @@ class DirectCameraControl(DirectObject):
             # Allow intersection with unpickable objects
             # And then spawn task to determine mouse mode
             # Don't intersect with hidden or backfacing objects
-            skipFlags = SKIP_HIDDEN | SKIP_BACKFACE
+            skipFlags = DG.SKIP_HIDDEN | DG.SKIP_BACKFACE
             # Skip camera (and its children), unless control key is pressed
-            skipFlags |= SKIP_CAMERA * (1 - base.getControl())
+            skipFlags |= DG.SKIP_CAMERA * (1 - base.getControl())
             self.computeCOA(base.direct.iRay.pickGeom(skipFlags = skipFlags))
             # Record reference point
             self.coaMarkerRef.setPosHprScale(base.cam, 0, 0, 0, 0, 0, 0, 1, 1, 1)
@@ -306,7 +309,7 @@ class DirectCameraControl(DirectObject):
 
     def XZTranslateTask(self, state):
         coaDist = Vec3(self.coaMarker.getPos(base.direct.camera)).length()
-        xlateSF = (coaDist / base.direct.dr.near)
+        xlateSF = coaDist / base.direct.dr.near
         base.direct.camera.setPos(base.direct.camera,
                              (-0.5 * base.direct.dr.mouseDeltaX *
                               base.direct.dr.nearWidth *
@@ -349,7 +352,7 @@ class DirectCameraControl(DirectObject):
         else:
             moveDir = Vec3(Y_AXIS)
 
-        if self.useMayaCamControls : # use maya controls
+        if self.useMayaCamControls: # use maya controls
             moveDir.assign(moveDir * ((base.direct.dr.mouseDeltaX -1.0 * base.direct.dr.mouseDeltaY)
                                     * state.zoomSF))
             hVal = 0.0
@@ -422,7 +425,7 @@ class DirectCameraControl(DirectObject):
 
         # Set at markers position in render coordinates
         self.camManipRef.setPos(self.coaMarkerPos)
-        self.camManipRef.setHpr(base.direct.camera, ZERO_POINT)
+        self.camManipRef.setHpr(base.direct.camera, DG.ZERO_POINT)
         t = Task.Task(self.mouseRotateTask)
         if abs(base.direct.dr.mouseX) > 0.9:
             t.constrainedDir = 'y'
@@ -454,7 +457,7 @@ class DirectCameraControl(DirectObject):
                 # flatten roll
                 base.direct.camera.setR(0)
             self.camManipRef.setPos(self.coaMarkerPos)
-            self.camManipRef.setHpr(base.direct.camera, ZERO_POINT)
+            self.camManipRef.setHpr(base.direct.camera, DG.ZERO_POINT)
         else:
             if base.direct.camera.getPos().getZ() >=0 or not self.switchDirBelowZero:
                 dirX = -1
@@ -478,7 +481,7 @@ class DirectCameraControl(DirectObject):
         self.__stopManipulateCamera()
         # Set at markers position in render coordinates
         self.camManipRef.setPos(self.coaMarkerPos)
-        self.camManipRef.setHpr(base.direct.camera, ZERO_POINT)
+        self.camManipRef.setHpr(base.direct.camera, DG.ZERO_POINT)
         t = Task.Task(self.mouseRollTask)
         t.coaCenter = getScreenXY(self.coaMarker)
         t.lastAngle = getCrankAngle(t.coaCenter)
@@ -565,7 +568,7 @@ class DirectCameraControl(DirectObject):
             base.direct.message('COA Distance: ' + repr(dist))
             coa.set(0, dist, 0)
         # Compute COA Dist
-        coaDist = Vec3(coa - ZERO_POINT).length()
+        coaDist = Vec3(coa - DG.ZERO_POINT).length()
         if coaDist < (1.1 * dr.near):
             coa.set(0, 100, 0)
             coaDist = 100
@@ -575,7 +578,7 @@ class DirectCameraControl(DirectObject):
     def updateCoa(self, ref2point, coaDist = None, ref = None):
         self.coa.set(ref2point[0], ref2point[1], ref2point[2])
         if not coaDist:
-            coaDist = Vec3(self.coa - ZERO_POINT).length()
+            coaDist = Vec3(self.coa - DG.ZERO_POINT).length()
         # Place the marker in render space
         if ref is None:
             # KEH: use the current display region
@@ -611,7 +614,7 @@ class DirectCameraControl(DirectObject):
                                          startColor = Vec4(1, 0, 0, 1),
                                          blendType = 'easeInOut'),
             Func(self.coaMarker.stash)
-            )
+        )
         self.coaMarkerColorIval.start()
 
     def homeCam(self):
@@ -642,19 +645,19 @@ class DirectCameraControl(DirectObject):
         # Transform camera z axis to render space
         mCam2Render = Mat4(Mat4.identMat()) # [gjeon] fixed to give required argument
         mCam2Render.assign(base.direct.camera.getMat(render))
-        zAxis = Vec3(mCam2Render.xformVec(Z_AXIS))
+        zAxis = Vec3(mCam2Render.xformVec(DG.Z_AXIS))
         zAxis.normalize()
         # Compute rotation angle needed to upright cam
-        orbitAngle = rad2Deg(math.acos(CLAMP(zAxis.dot(Z_AXIS), -1, 1)))
+        orbitAngle = rad2Deg(math.acos(CLAMP(zAxis.dot(DG.Z_AXIS), -1, 1)))
         # Check angle
         if orbitAngle < 0.1:
             # Already upright
             return
         # Compute orthogonal axis of rotation
-        rotAxis = Vec3(zAxis.cross(Z_AXIS))
+        rotAxis = Vec3(zAxis.cross(DG.Z_AXIS))
         rotAxis.normalize()
         # Find angle between rot Axis and render X_AXIS
-        rotAngle = rad2Deg(math.acos(CLAMP(rotAxis.dot(X_AXIS), -1, 1)))
+        rotAngle = rad2Deg(math.acos(CLAMP(rotAxis.dot(DG.X_AXIS), -1, 1)))
         # Determine sign or rotation angle
         if rotAxis[1] < 0:
             rotAngle *= -1
@@ -685,7 +688,7 @@ class DirectCameraControl(DirectObject):
         base.direct.pushUndo([base.direct.camera])
         # Determine marker location
         markerToCam = self.coaMarker.getPos(base.direct.camera)
-        dist = Vec3(markerToCam - ZERO_POINT).length()
+        dist = Vec3(markerToCam - DG.ZERO_POINT).length()
         scaledCenterVec = Y_AXIS * dist
         delta = markerToCam - scaledCenterVec
         self.camManipRef.setPosHpr(base.direct.camera, Point3(0), Point3(0))
@@ -697,7 +700,6 @@ class DirectCameraControl(DirectObject):
                         name = 'manipulateCamera')
         self.__startManipulateCamera(ival = ival)
 
-
     def zoomCam(self, zoomFactor, t):
         self.__stopManipulateCamera()
         # Record undo point
@@ -709,7 +711,7 @@ class DirectCameraControl(DirectObject):
         self.camManipRef.setPos(base.direct.camera, zoomPtToCam)
         # Move to that point
         ival = base.direct.camera.posInterval(CAM_MOVE_DURATION,
-                                              ZERO_POINT,
+                                              DG.ZERO_POINT,
                                               other = self.camManipRef,
                                               blendType = 'easeInOut')
         ival = Sequence(ival, Func(self.updateCoaMarkerSizeOnDeath),
@@ -746,20 +748,20 @@ class DirectCameraControl(DirectObject):
         elif view == 7:
             hprOffset.set(135., -35.264, 0.)
         # Position target
-        self.camManipRef.setPosHpr(self.coaMarker, ZERO_VEC,
+        self.camManipRef.setPosHpr(self.coaMarker, DG.ZERO_VEC,
                                    hprOffset)
         # Scale center vec by current distance to target
         offsetDistance = Vec3(base.direct.camera.getPos(self.camManipRef) -
-                              ZERO_POINT).length()
+                              DG.ZERO_POINT).length()
         scaledCenterVec = Y_AXIS * (-1.0 * offsetDistance)
         # Now put the camManipRef at that point
         self.camManipRef.setPosHpr(self.camManipRef,
                                    scaledCenterVec,
-                                   ZERO_VEC)
+                                   DG.ZERO_VEC)
         # Record view for next time around
         self.lastView = view
         ival = base.direct.camera.posHprInterval(CAM_MOVE_DURATION,
-                                                 pos = ZERO_POINT,
+                                                 pos = DG.ZERO_POINT,
                                                  hpr = VBase3(0, 0, self.orthoViewRoll),
                                                  other = self.camManipRef,
                                                  blendType = 'easeInOut')
@@ -767,7 +769,6 @@ class DirectCameraControl(DirectObject):
                         name = 'manipulateCamera')
         self.__startManipulateCamera(ival = ival)
 
-
     def swingCamAboutWidget(self, degrees, t):
         # Remove existing camera manipulation task
         self.__stopManipulateCamera()
@@ -776,9 +777,9 @@ class DirectCameraControl(DirectObject):
         base.direct.pushUndo([base.direct.camera])
 
         # Coincident with widget
-        self.camManipRef.setPos(self.coaMarker, ZERO_POINT)
+        self.camManipRef.setPos(self.coaMarker, DG.ZERO_POINT)
         # But aligned with render space
-        self.camManipRef.setHpr(ZERO_POINT)
+        self.camManipRef.setHpr(DG.ZERO_POINT)
 
         parent = base.direct.camera.getParent()
         base.direct.camera.wrtReparentTo(self.camManipRef)
@@ -820,7 +821,8 @@ class DirectCameraControl(DirectObject):
         try:
             self.camManipRef.setPos(base.direct.camera, deltaMove)
         except Exception:
-            self.notify.debug
+            #self.notify.debug
+            pass
 
         parent = base.direct.camera.getParent()
         base.direct.camera.wrtReparentTo(self.camManipRef)
@@ -831,7 +833,6 @@ class DirectCameraControl(DirectObject):
                         name = 'manipulateCamera')
         self.__startManipulateCamera(ival = ival)
 
-
     def moveToFit(self):
         # How big is the active widget?
         widgetScale = base.direct.widget.scalingNode.getScale(render)

+ 28 - 5
direct/src/directtools/DirectGeometry.py

@@ -1,9 +1,24 @@
-
-from panda3d.core import *
-from .DirectGlobals import *
-from .DirectUtil import *
+from panda3d.core import (
+    CSDefault,
+    GeomNode,
+    LineSegs,
+    Mat4,
+    NodePath,
+    Point3,
+    Quat,
+    VBase3,
+    VBase4,
+    Vec3,
+    composeMatrix,
+    decomposeMatrix,
+    deg2Rad,
+    rad2Deg,
+)
+from .DirectGlobals import Q_EPSILON, UNIT_VEC, ZERO_VEC
+from .DirectUtil import CLAMP
 import math
 
+
 class LineNodePath(NodePath):
     def __init__(self, parent = None, name = None,
                  thickness = 1.0, colorVec = VBase4(1)):
@@ -126,13 +141,16 @@ class LineNodePath(NodePath):
 ##
 ## Given a point in space, and a direction, find the point of intersection
 ## of that ray with a plane at the specified origin, with the specified normal
-def planeIntersect (lineOrigin, lineDir, planeOrigin, normal):
+
+
+def planeIntersect(lineOrigin, lineDir, planeOrigin, normal):
     t = 0
     offset = planeOrigin - lineOrigin
     t = offset.dot(normal) / lineDir.dot(normal)
     hitPt = lineDir * t
     return hitPt + lineOrigin
 
+
 def getNearProjectionPoint(nodePath):
     # Find the position of the projection of the specified node path
     # on the near plane
@@ -144,6 +162,7 @@ def getNearProjectionPoint(nodePath):
         # Object is coplaner with camera, just return something reasonable
         return Point3(0, base.direct.dr.near, 0)
 
+
 def getScreenXY(nodePath):
     # Where does the node path's projection fall on the near plane
     nearVec = getNearProjectionPoint(nodePath)
@@ -158,6 +177,7 @@ def getScreenXY(nodePath):
     # Return the resulting value
     return screenXY
 
+
 def getCrankAngle(center):
     # Used to compute current angle of mouse (relative to the coa's
     # origin) in screen space
@@ -165,6 +185,7 @@ def getCrankAngle(center):
     y = base.direct.dr.mouseY - center[2]
     return 180 + rad2Deg(math.atan2(y, x))
 
+
 def relHpr(nodePath, base, h, p, r):
     # Compute nodePath2newNodePath relative to base coordinate system
     # nodePath2base
@@ -188,6 +209,8 @@ def relHpr(nodePath, base, h, p, r):
     nodePath.setHpr(hpr)
 
 # Quaternion interpolation
+
+
 def qSlerp(startQuat, endQuat, t):
     startQ = Quat(startQuat)
     destQuat = Quat(Quat.identQuat())

+ 4 - 4
direct/src/directtools/DirectGlobals.py

@@ -51,11 +51,11 @@ LE_CAM_MASKS = {'persp':LE_PERSP_CAM_MASK,
                  'top':LE_TOP_CAM_MASK}
 
 def LE_showInAllCam(nodePath):
-    for camName in LE_CAM_MASKS:
-        nodePath.show(LE_CAM_MASKS[camName])
+    for camName, mask in LE_CAM_MASKS.items():
+        nodePath.show(mask)
 
 def LE_showInOneCam(nodePath, thisCamName):
     LE_showInAllCam(nodePath)
-    for camName in LE_CAM_MASKS:
+    for camName, mask in LE_CAM_MASKS.items():
         if camName != thisCamName:
-            nodePath.hide(LE_CAM_MASKS[camName])
+            nodePath.hide(mask)

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

@@ -1,8 +1,8 @@
-
-from panda3d.core import *
+import math
+from panda3d.core import NodePath, Point3, VBase4
 from direct.showbase.DirectObject import DirectObject
-from .DirectUtil import *
-from .DirectGeometry import *
+from .DirectUtil import ROUND_TO, useDirectRenderStyle
+from .DirectGeometry import LineNodePath
 
 
 class DirectGrid(NodePath, DirectObject):

+ 11 - 1
direct/src/directtools/DirectLights.py

@@ -1,5 +1,15 @@
 
-from panda3d.core import *
+from panda3d.core import (
+    AmbientLight,
+    DirectionalLight,
+    LightAttrib,
+    Material,
+    NodePath,
+    PerspectiveLens,
+    PointLight,
+    Spotlight,
+    VBase4,
+)
 from direct.showbase.MessengerGlobal import messenger
 
 

+ 73 - 49
direct/src/directtools/DirectManipulation.py

@@ -1,10 +1,32 @@
+import math
+from panda3d.core import (
+    BitMask32,
+    BoundingHexahedron,
+    CSDefault,
+    Mat4,
+    NodePath,
+    Point3,
+    VBase3,
+    VBase4,
+    Vec3,
+    decomposeMatrix,
+    deg2Rad,
+)
 from direct.showbase.DirectObject import DirectObject
 from direct.showbase.MessengerGlobal import messenger
-from .DirectGlobals import *
-from .DirectUtil import *
-from .DirectGeometry import *
+from . import DirectGlobals as DG
+from .DirectUtil import useDirectRenderStyle
+from .DirectGeometry import (
+    LineNodePath,
+    getCrankAngle,
+    getNearProjectionPoint,
+    getScreenXY,
+    planeIntersect,
+    relHpr,
+)
 from .DirectSelection import SelectionRay
 from direct.task import Task
+from direct.task.TaskManagerGlobal import taskMgr
 from copy import deepcopy
 
 
@@ -50,8 +72,8 @@ class DirectManipulationControl(DirectObject):
             ['DIRECT-widgetScaleDown', self.scaleWidget, 0.5],
             ['shift-f', self.objectHandles.growToFit],
             ['i', self.plantSelectedNodePath],
-            ]
-        self.defaultSkipFlags = SKIP_HIDDEN | SKIP_BACKFACE
+        ]
+        self.defaultSkipFlags = DG.SKIP_HIDDEN | DG.SKIP_BACKFACE
         self.optionalSkipFlags = 0
         self.unmovableTagList = []
 
@@ -115,7 +137,7 @@ class DirectManipulationControl(DirectObject):
            self.fScaling3D == 0:
 
             # Check for a widget hit point
-            entry = base.direct.iRay.pickWidget(skipFlags = SKIP_WIDGET)
+            entry = base.direct.iRay.pickWidget(skipFlags = DG.SKIP_WIDGET)
             # Did we hit a widget?
             if entry:
                 # Yes!
@@ -137,7 +159,7 @@ class DirectManipulationControl(DirectObject):
             if entry:
                 # Check to see if we are moving the object
                 # We are moving the object if we either wait long enough
-                taskMgr.doMethodLater(MANIPULATION_MOVE_DELAY,
+                taskMgr.doMethodLater(DG.MANIPULATION_MOVE_DELAY,
                                       self.switchToMoveMode,
                                       'manip-move-wait')
                 # Or we move far enough
@@ -170,8 +192,8 @@ class DirectManipulationControl(DirectObject):
         return Task.done
 
     def watchMouseTask(self, state):
-        if (((abs (state.initX - base.direct.dr.mouseX)) > 0.01) or
-            ((abs (state.initY - base.direct.dr.mouseY)) > 0.01)):
+        if (abs(state.initX - base.direct.dr.mouseX) > 0.01 or
+            abs(state.initY - base.direct.dr.mouseY) > 0.01):
             taskMgr.remove('manip-move-wait')
             self.mode = 'move'
             self.manipulateObject()
@@ -199,8 +221,8 @@ class DirectManipulationControl(DirectObject):
         endX = base.direct.dr.mouseX
         endY = base.direct.dr.mouseY
 
-        if (((abs (endX - startX)) < 0.01) and
-            ((abs (endY - startY)) < 0.01)):
+        if (abs(endX - startX) < 0.01 and
+            abs(endY - startY) < 0.01):
             return
 
         self.marquee = LineNodePath(base.render2d, 'marquee', 0.5, VBase4(.8, .6, .6, 1))
@@ -213,7 +235,7 @@ class DirectManipulationControl(DirectObject):
         self.marquee.create()
 
         if self.fMultiView:
-            LE_showInOneCam(self.marquee, base.direct.camera.getName())
+            DG.LE_showInOneCam(self.marquee, base.direct.camera.getName())
 
     def manipulationStop(self):
         taskMgr.remove('manipulateObject')
@@ -227,7 +249,7 @@ class DirectManipulationControl(DirectObject):
             # optionally specified things
             skipFlags = self.defaultSkipFlags | self.optionalSkipFlags
             # Skip camera (and its children), unless control key is pressed
-            skipFlags |= SKIP_CAMERA * (1 - base.getControl())
+            skipFlags |= DG.SKIP_CAMERA * (1 - base.getControl())
 
             if self.marquee:
                 self.marquee.removeNode()
@@ -269,18 +291,18 @@ class DirectManipulationControl(DirectObject):
 
                 selectionList = []
                 for geom in render.findAllMatches("**/+GeomNode"):
-                    if (skipFlags & SKIP_HIDDEN) and geom.isHidden():
+                    if (skipFlags & DG.SKIP_HIDDEN) and geom.isHidden():
                         # Skip if hidden node
                         continue
-##                     elif (skipFlags & SKIP_BACKFACE) and base.direct.iRay.isEntryBackfacing():
+##                     elif (skipFlags & DG.SKIP_BACKFACE) and base.direct.iRay.isEntryBackfacing():
 ##                         # Skip, if backfacing poly
 ##                         pass
-                    elif (skipFlags & SKIP_CAMERA) and \
+                    elif (skipFlags & DG.SKIP_CAMERA) and \
                          (base.camera in geom.getAncestors()):
                         # Skip if parented to a camera.
                         continue
                     # Can pick unpickable, use the first visible node
-                    elif (skipFlags & SKIP_UNPICKABLE) and \
+                    elif (skipFlags & DG.SKIP_UNPICKABLE) and \
                          (geom.getName() in base.direct.iRay.unpickable):
                         # Skip if in unpickable list
                         continue
@@ -500,7 +522,7 @@ class DirectManipulationControl(DirectObject):
         selectedList = base.direct.selected.getSelectedAsList()
         # See if any of the selected are completely uneditable
         editTypes = self.getEditTypes(selectedList)
-        if (editTypes & EDIT_TYPE_UNEDITABLE) == EDIT_TYPE_UNEDITABLE:
+        if (editTypes & DG.EDIT_TYPE_UNEDITABLE) == DG.EDIT_TYPE_UNEDITABLE:
             return
         self.currEditTypes = editTypes
         if selectedList:
@@ -568,13 +590,13 @@ class DirectManipulationControl(DirectObject):
             if self.constraint:
                 type = self.constraint[2:]
                 if self.useSeparateScaleHandles:
-                    if type == 'post' and not self.currEditTypes & EDIT_TYPE_UNMOVABLE:
+                    if type == 'post' and not self.currEditTypes & DG.EDIT_TYPE_UNMOVABLE:
                         self.xlate1D(state)
-                    elif type == 'disc' and not self.currEditTypes & EDIT_TYPE_UNMOVABLE:
+                    elif type == 'disc' and not self.currEditTypes & DG.EDIT_TYPE_UNMOVABLE:
                         self.xlate2D(state)
-                    elif type == 'ring' and not self.currEditTypes & EDIT_TYPE_UNROTATABLE:
+                    elif type == 'ring' and not self.currEditTypes & DG.EDIT_TYPE_UNROTATABLE:
                         self.rotate1D(state)
-                    elif type == 'scale' and not self.currEditTypes & EDIT_TYPE_UNSCALABLE:
+                    elif type == 'scale' and not self.currEditTypes & DG.EDIT_TYPE_UNSCALABLE:
                         if base.direct.fShift:
                             self.fScaling3D = 1
                             self.scale3D(state)
@@ -582,7 +604,7 @@ class DirectManipulationControl(DirectObject):
                             self.fScaling1D = 1
                             self.scale1D(state)
                 else:
-                    if base.direct.fControl and not self.currEditTypes & EDIT_TYPE_UNSCALABLE:
+                    if base.direct.fControl and not self.currEditTypes & DG.EDIT_TYPE_UNSCALABLE:
                         if type == 'post':
                             # [gjeon] non-uniform scaling
                             self.fScaling1D = 1
@@ -592,11 +614,11 @@ class DirectManipulationControl(DirectObject):
                             self.fScaling3D = 1
                             self.scale3D(state)
                     else:
-                        if type == 'post' and not self.currEditTypes & EDIT_TYPE_UNMOVABLE:
+                        if type == 'post' and not self.currEditTypes & DG.EDIT_TYPE_UNMOVABLE:
                             self.xlate1D(state)
-                        elif type == 'disc' and not self.currEditTypes & EDIT_TYPE_UNMOVABLE:
+                        elif type == 'disc' and not self.currEditTypes & DG.EDIT_TYPE_UNMOVABLE:
                             self.xlate2D(state)
-                        elif type == 'ring' and not self.currEditTypes & EDIT_TYPE_UNROTATABLE:
+                        elif type == 'ring' and not self.currEditTypes & DG.EDIT_TYPE_UNROTATABLE:
                             self.rotate1D(state)
             # No widget interaction, determine free manip mode
             elif self.fFreeManip and not self.useSeparateScaleHandles:
@@ -610,17 +632,17 @@ class DirectManipulationControl(DirectObject):
                     self.fScaling1D = 0
                     self.fScaling3D = 0
                 # Alt key switches to a scaling mode
-                if base.direct.fControl and not self.currEditTypes & EDIT_TYPE_UNSCALABLE:
+                if base.direct.fControl and not self.currEditTypes & DG.EDIT_TYPE_UNSCALABLE:
                     self.fScaling3D = 1
                     self.scale3D(state)
                 # Otherwise, manip mode depends on where you started
-                elif state.fMouseX and state.fMouseY and not self.currEditTypes & EDIT_TYPE_UNROTATABLE:
+                elif state.fMouseX and state.fMouseY and not self.currEditTypes & DG.EDIT_TYPE_UNROTATABLE:
                     # In the corner, spin around camera's axis
                     self.rotateAboutViewVector(state)
-                elif state.fMouseX or state.fMouseY and not self.currEditTypes & EDIT_TYPE_UNMOVABLE:
+                elif state.fMouseX or state.fMouseY and not self.currEditTypes & DG.EDIT_TYPE_UNMOVABLE:
                     # Mouse started elsewhere in the outer frame, rotate
                     self.rotate2D(state)
-                elif not self.currEditTypes & EDIT_TYPE_UNMOVABLE:
+                elif not self.currEditTypes & DG.EDIT_TYPE_UNMOVABLE:
                     # Mouse started in central region, xlate
                     # Mode depends on shift key
                     if base.direct.fShift or base.direct.fControl:
@@ -805,11 +827,11 @@ class DirectManipulationControl(DirectObject):
         widgetDir.normalize()
         # Convert specified widget axis to view space
         if axis == 'x':
-            widgetAxis = Vec3(mWidget2Cam.xformVec(X_AXIS))
+            widgetAxis = Vec3(mWidget2Cam.xformVec(DG.X_AXIS))
         elif axis == 'y':
-            widgetAxis = Vec3(mWidget2Cam.xformVec(Y_AXIS))
+            widgetAxis = Vec3(mWidget2Cam.xformVec(DG.Y_AXIS))
         elif axis == 'z':
-            widgetAxis = Vec3(mWidget2Cam.xformVec(Z_AXIS))
+            widgetAxis = Vec3(mWidget2Cam.xformVec(DG.Z_AXIS))
         widgetAxis.normalize()
         if type == 'top?':
             # Check sign of angle between two vectors
@@ -868,7 +890,7 @@ class DirectManipulationControl(DirectObject):
                 moveDir.assign(moveDir * -1)
             moveDir.normalize()
         else:
-            moveDir = Vec3(Y_AXIS)
+            moveDir = Vec3(DG.Y_AXIS)
         # Move selected objects
         dr = base.direct.dr
         # Scale move dir
@@ -1034,8 +1056,8 @@ class DirectManipulationControl(DirectObject):
             self.initScale *
             (self.objectHandles.getWidgetIntersectPt(
             self.manipRef, 'y').length() /
-             self.initScaleMag)
-            )
+                self.initScaleMag)
+        )
         base.direct.widget.setScale(currScale)
 
     ## Utility functions ##
@@ -1043,7 +1065,7 @@ class DirectManipulationControl(DirectObject):
         """ Move selected object to intersection point of cursor on scene """
         # Check for intersection
         entry = base.direct.iRay.pickGeom(
-            skipFlags = SKIP_HIDDEN | SKIP_BACKFACE | SKIP_CAMERA)
+            skipFlags = DG.SKIP_HIDDEN | DG.SKIP_BACKFACE | DG.SKIP_CAMERA)
         # MRM: Need to handle moving COA
         if entry is not None and base.direct.selected.last is not None:
             # Record undo point
@@ -1060,6 +1082,7 @@ class DirectManipulationControl(DirectObject):
             messenger.send('DIRECT_manipulateObjectCleanup',
                            [base.direct.selected.getSelectedAsList()])
 
+
 class ObjectHandles(NodePath, DirectObject):
     def __init__(self, name='objectHandles'):
         # Initialize the superclass
@@ -1642,32 +1665,32 @@ class ObjectHandles(NodePath, DirectObject):
         # by comparing lineDir with plane normals.  The plane with the
         # largest dotProduct is most "normal"
         if axis == 'x':
-            if abs(lineDir.dot(Y_AXIS)) > abs(lineDir.dot(Z_AXIS)):
+            if abs(lineDir.dot(DG.Y_AXIS)) > abs(lineDir.dot(DG.Z_AXIS)):
                 self.hitPt.assign(
-                    planeIntersect(lineOrigin, lineDir, ORIGIN, Y_AXIS))
+                    planeIntersect(lineOrigin, lineDir, DG.ORIGIN, DG.Y_AXIS))
             else:
                 self.hitPt.assign(
-                    planeIntersect(lineOrigin, lineDir, ORIGIN, Z_AXIS))
+                    planeIntersect(lineOrigin, lineDir, DG.ORIGIN, DG.Z_AXIS))
             # We really only care about the nearest point on the axis
             self.hitPt.setY(0)
             self.hitPt.setZ(0)
         elif axis == 'y':
-            if abs(lineDir.dot(X_AXIS)) > abs(lineDir.dot(Z_AXIS)):
+            if abs(lineDir.dot(DG.X_AXIS)) > abs(lineDir.dot(DG.Z_AXIS)):
                 self.hitPt.assign(
-                    planeIntersect(lineOrigin, lineDir, ORIGIN, X_AXIS))
+                    planeIntersect(lineOrigin, lineDir, DG.ORIGIN, DG.X_AXIS))
             else:
                 self.hitPt.assign(
-                    planeIntersect(lineOrigin, lineDir, ORIGIN, Z_AXIS))
+                    planeIntersect(lineOrigin, lineDir, DG.ORIGIN, DG.Z_AXIS))
             # We really only care about the nearest point on the axis
             self.hitPt.setX(0)
             self.hitPt.setZ(0)
         elif axis == 'z':
-            if abs(lineDir.dot(X_AXIS)) > abs(lineDir.dot(Y_AXIS)):
+            if abs(lineDir.dot(DG.X_AXIS)) > abs(lineDir.dot(DG.Y_AXIS)):
                 self.hitPt.assign(
-                    planeIntersect(lineOrigin, lineDir, ORIGIN, X_AXIS))
+                    planeIntersect(lineOrigin, lineDir, DG.ORIGIN, DG.X_AXIS))
             else:
                 self.hitPt.assign(
-                    planeIntersect(lineOrigin, lineDir, ORIGIN, Y_AXIS))
+                    planeIntersect(lineOrigin, lineDir, DG.ORIGIN, DG.Y_AXIS))
             # We really only care about the nearest point on the axis
             self.hitPt.setX(0)
             self.hitPt.setY(0)
@@ -1722,16 +1745,17 @@ class ObjectHandles(NodePath, DirectObject):
         # Find the hit point
         if plane == 'x':
             self.hitPt.assign(planeIntersect(
-                lineOrigin, lineDir, ORIGIN, X_AXIS))
+                lineOrigin, lineDir, DG.ORIGIN, DG.X_AXIS))
         elif plane == 'y':
             self.hitPt.assign(planeIntersect(
-                lineOrigin, lineDir, ORIGIN, Y_AXIS))
+                lineOrigin, lineDir, DG.ORIGIN, DG.Y_AXIS))
         elif plane == 'z':
             self.hitPt.assign(planeIntersect(
-                lineOrigin, lineDir, ORIGIN, Z_AXIS))
+                lineOrigin, lineDir, DG.ORIGIN, DG.Z_AXIS))
 
         return self.hitPt
 
+
 def drawBox(lines, center, sideLength):
     l = sideLength * 0.5
     lines.moveTo(center[0] + l, center[1] + l, center[2] + l)

+ 49 - 27
direct/src/directtools/DirectSelection.py

@@ -1,13 +1,33 @@
+from panda3d.core import (
+    BitMask32,
+    BoundingSphere,
+    CollisionHandlerQueue,
+    CollisionNode,
+    CollisionRay,
+    CollisionSegment,
+    CollisionSphere,
+    CollisionTraverser,
+    GeomNode,
+    Mat4,
+    NodePath,
+    Point3,
+    TransformState,
+    VBase4,
+    Vec3,
+    Vec4,
+)
 from direct.showbase.DirectObject import DirectObject
 from direct.showbase.MessengerGlobal import messenger
-from .DirectGlobals import *
-from .DirectUtil import *
-from .DirectGeometry import *
+from . import DirectGlobals as DG
+from .DirectUtil import useDirectRenderStyle
+from .DirectGeometry import LineNodePath
 
 COA_ORIGIN = 0
 COA_CENTER = 1
 
 # MRM: To do: handle broken node paths in selected and deselected dicts
+
+
 class DirectNodePath(NodePath):
     # A node path augmented with info, bounding box, and utility methods
     def __init__(self, nodePath, bboxColor=None):
@@ -47,6 +67,7 @@ class DirectNodePath(NodePath):
     def getMax(self):
         return self.bbox.getMax()
 
+
 class SelectedNodePaths(DirectObject):
     def __init__(self):
         self.reset()
@@ -427,7 +448,7 @@ class DirectBoundingBox:
                 'Max:\t\t%s\n' % self.vecAsString(self.max) +
                 'Center:\t\t%s\n' % self.vecAsString(self.center) +
                 'Radius:\t\t%.2f' % self.radius
-               )
+                )
 
 
 class SelectionQueue(CollisionHandlerQueue):
@@ -439,7 +460,7 @@ class SelectionQueue(CollisionHandlerQueue):
         # Current index and entry in collision queue
         self.index = -1
         self.entry = None
-        self.skipFlags = SKIP_NONE
+        self.skipFlags = DG.SKIP_NONE
         # Create a collision node path attached to the given NP
         self.collisionNodePath = NodePath(CollisionNode("collisionNP"))
         self.setParentNP(parentNP)
@@ -454,7 +475,7 @@ class SelectionQueue(CollisionHandlerQueue):
         # Let the traverser know about the collision node and the queue
         self.ct.addCollider(self.collisionNodePath, self)
         # List of objects that can't be selected
-        self.unpickable = UNPICKABLE
+        self.unpickable = DG.UNPICKABLE
         # Derived class must add Collider to complete initialization
 
     def setParentNP(self, parentNP):
@@ -548,10 +569,10 @@ class SelectionQueue(CollisionHandlerQueue):
         v.normalize()
         return v.dot(n) >= 0
 
-    def findNextCollisionEntry(self, skipFlags = SKIP_NONE):
+    def findNextCollisionEntry(self, skipFlags = DG.SKIP_NONE):
         return self.findCollisionEntry(skipFlags, self.index + 1)
 
-    def findCollisionEntry(self, skipFlags = SKIP_NONE, startIndex = 0):
+    def findCollisionEntry(self, skipFlags = DG.SKIP_NONE, startIndex = 0):
         # Init self.index and self.entry
         self.setCurrentIndex(-1)
         self.setCurrentEntry(None)
@@ -559,29 +580,29 @@ class SelectionQueue(CollisionHandlerQueue):
         for i in range(startIndex, self.getNumEntries()):
             entry = self.getEntry(i)
             nodePath = entry.getIntoNodePath()
-            if (skipFlags & SKIP_HIDDEN) and nodePath.isHidden():
+            if (skipFlags & DG.SKIP_HIDDEN) and nodePath.isHidden():
                 # Skip if hidden node
                 pass
-            elif (skipFlags & SKIP_BACKFACE) and self.isEntryBackfacing(entry):
+            elif (skipFlags & DG.SKIP_BACKFACE) and self.isEntryBackfacing(entry):
                 # Skip, if backfacing poly
                 pass
-            elif (skipFlags & SKIP_CAMERA) and \
+            elif (skipFlags & DG.SKIP_CAMERA) and \
                  (base.camera in nodePath.getAncestors()):
                 # Skip if parented to a camera.
                 pass
             # Can pick unpickable, use the first visible node
-            elif (skipFlags & SKIP_UNPICKABLE) and\
+            elif (skipFlags & DG.SKIP_UNPICKABLE) and \
                  (nodePath.getName() in self.unpickable):
                 # Skip if in unpickable list
                 pass
-            elif base.direct and\
-                 ((skipFlags & SKIP_WIDGET) and
-                (nodePath.getTag('WidgetName') != base.direct.widget.getName())):
+            elif base.direct and \
+                 ((skipFlags & DG.SKIP_WIDGET) and
+                 (nodePath.getTag('WidgetName') != base.direct.widget.getName())):
                 # Skip if this widget part is not belong to current widget
                 pass
-            elif base.direct and\
-                 ((skipFlags & SKIP_WIDGET) and base.direct.fControl and
-                (nodePath.getName()[2:] == 'ring')):
+            elif base.direct and \
+                 ((skipFlags & DG.SKIP_WIDGET) and base.direct.fControl and
+                 (nodePath.getName()[2:] == 'ring')):
                 # Skip when ununiformly scale in ortho view
                 pass
             else:
@@ -590,6 +611,7 @@ class SelectionQueue(CollisionHandlerQueue):
                 break
         return self.getCurrentEntry()
 
+
 class SelectionRay(SelectionQueue):
     def __init__(self, parentNP = None):
         if parentNP is None:
@@ -623,7 +645,7 @@ class SelectionRay(SelectionQueue):
 
     def pickBitMask(self, bitMask = BitMask32.allOff(),
                     targetNodePath = None,
-                    skipFlags = SKIP_ALL):
+                    skipFlags = DG.SKIP_ALL):
         if targetNodePath is None:
             targetNodePath = render
         self.collideWithBitMask(bitMask)
@@ -631,7 +653,7 @@ class SelectionRay(SelectionQueue):
         # Determine collision entry
         return self.findCollisionEntry(skipFlags)
 
-    def pickGeom(self, targetNodePath = None, skipFlags = SKIP_ALL,
+    def pickGeom(self, targetNodePath = None, skipFlags = DG.SKIP_ALL,
                  xy = None):
         if targetNodePath is None:
             targetNodePath = render
@@ -640,7 +662,7 @@ class SelectionRay(SelectionQueue):
         # Determine collision entry
         return self.findCollisionEntry(skipFlags)
 
-    def pickWidget(self, targetNodePath = None, skipFlags = SKIP_NONE):
+    def pickWidget(self, targetNodePath = None, skipFlags = DG.SKIP_NONE):
         if targetNodePath is None:
             targetNodePath = render
         self.collideWithWidget()
@@ -657,7 +679,7 @@ class SelectionRay(SelectionQueue):
 
     def pickGeom3D(self, targetNodePath = None,
                    origin = Point3(0), dir = Vec3(0, 0, -1),
-                   skipFlags = SKIP_HIDDEN | SKIP_CAMERA):
+                   skipFlags = DG.SKIP_HIDDEN | DG.SKIP_CAMERA):
         if targetNodePath is None:
             targetNodePath = render
         self.collideWithGeom()
@@ -668,7 +690,7 @@ class SelectionRay(SelectionQueue):
     def pickBitMask3D(self, bitMask = BitMask32.allOff(),
                       targetNodePath = None,
                       origin = Point3(0), dir = Vec3(0, 0, -1),
-                      skipFlags = SKIP_ALL):
+                      skipFlags = DG.SKIP_ALL):
         if targetNodePath is None:
             targetNodePath = render
         self.collideWithBitMask(bitMask)
@@ -698,7 +720,7 @@ class SelectionSegment(SelectionQueue):
         self.numColliders += 1
 
     def pickGeom(self, targetNodePath = None, endPointList = [],
-                 skipFlags = SKIP_HIDDEN | SKIP_CAMERA):
+                 skipFlags = DG.SKIP_HIDDEN | DG.SKIP_CAMERA):
         if targetNodePath is None:
             targetNodePath = render
         self.collideWithGeom()
@@ -713,7 +735,7 @@ class SelectionSegment(SelectionQueue):
 
     def pickBitMask(self, bitMask = BitMask32.allOff(),
                     targetNodePath = None, endPointList = [],
-                 skipFlags = SKIP_HIDDEN | SKIP_CAMERA):
+                 skipFlags = DG.SKIP_HIDDEN | DG.SKIP_CAMERA):
         if targetNodePath is None:
             targetNodePath = render
         self.collideWithBitMask(bitMask)
@@ -781,7 +803,7 @@ class SelectionSphere(SelectionQueue):
         return self.findCollisionEntry(skipFlags)
 
     def pickGeom(self, targetNodePath = None,
-                 skipFlags = SKIP_HIDDEN | SKIP_CAMERA):
+                 skipFlags = DG.SKIP_HIDDEN | DG.SKIP_CAMERA):
         if targetNodePath is None:
             targetNodePath = render
         self.collideWithGeom()
@@ -789,7 +811,7 @@ class SelectionSphere(SelectionQueue):
 
     def pickBitMask(self, bitMask = BitMask32.allOff(),
                     targetNodePath = None,
-                    skipFlags = SKIP_HIDDEN | SKIP_CAMERA):
+                    skipFlags = DG.SKIP_HIDDEN | DG.SKIP_CAMERA):
         if targetNodePath is None:
             targetNodePath = render
         self.collideWithBitMask(bitMask)

+ 53 - 36
direct/src/directtools/DirectSession.py

@@ -1,26 +1,41 @@
 import math
 
-from panda3d.core import *
-from .DirectUtil import *
+from panda3d.core import (
+    CollisionNode,
+    ConfigVariableBool,
+    ConfigVariableString,
+    CSDefault,
+    NodePath,
+    Point3,
+    TextNode,
+    VBase3,
+    VBase4,
+    Vec3,
+    Vec4,
+    decomposeMatrix,
+    deg2Rad,
+)
+from .DirectUtil import useDirectRenderStyle
 
 from direct.showbase.DirectObject import DirectObject
 from direct.showbase.BulletinBoardGlobal import bulletinBoard as bboard
 from direct.task import Task
 
-from .DirectGlobals import DIRECT_NO_MOD
+from . import DirectGlobals as DG
 from .DirectCameraControl import DirectCameraControl
 from .DirectManipulation import DirectManipulationControl
 from .DirectSelection import SelectionRay, COA_ORIGIN, SelectedNodePaths
 from .DirectGrid import DirectGrid
 #from DirectGeometry import *
 from .DirectLights import DirectLights
-from direct.cluster.ClusterClient import createClusterClient, DummyClusterClient
-from direct.cluster.ClusterServer import ClusterServer
 ## from direct.tkpanels import Placer
 ## from direct.tkwidgets import Slider
 ## from direct.tkwidgets import SceneGraphExplorer
 from direct.gui import OnscreenText
-from direct.interval.IntervalGlobal import *
+from direct.interval.IntervalGlobal import Func, Sequence
+from direct.task.TaskManagerGlobal import taskMgr
+from direct.showbase.MessengerGlobal import messenger
+
 
 class DirectSession(DirectObject):
 
@@ -117,7 +132,6 @@ class DirectSession(DirectObject):
                 for i in range(len(fastrak))[1:]:
                     self.fastrak.append(DirectFastrak.DirectFastrak(fastrak[0] + ':' + fastrak[i]))
 
-
         self.fControl = 0
         self.fAlt = 0
         self.fShift = 0
@@ -179,7 +193,7 @@ class DirectSession(DirectObject):
             ['DIRECT-doWrtReparent', self.doWrtReparent],
             ['DIRECT-doReparent', self.doReparent],
             ['DIRECT-doSelect', self.doSelect],
-            ]
+        ]
 
         if base.wantTk:
             from direct.tkpanels import Placer
@@ -248,7 +262,7 @@ class DirectSession(DirectObject):
             'shift-[': ('DIRECT-Undo', 'DIRECT-Undo'),
             ']': ('DIRECT-Redo', 'DIRECT-Redo'),
             'shift-]': ('DIRECT-Redo', 'DIRECT-Redo'),
-            }
+        }
 
         self.hotKeyMap = {
             'c': ('Center Camera', 'DIRECT-centerCamIn'),
@@ -276,14 +290,14 @@ class DirectSession(DirectObject):
             'shift-a': ('Toggle Vis all', 'DIRECT-toggleVisAll'),
             'w': ('Toggle Wireframe', 'DIRECT-toggleWireframe'),
             'control-z': ('Undo', 'LE-Undo'),
-            'shift-z' : ('Redo', 'LE-Redo'),
+            'shift-z': ('Redo', 'LE-Redo'),
             'control-d': ('Duplicate', 'LE-Duplicate'),
             'control-l': ('Make Live', 'LE-MakeLive'),
             'control-n': ('New Scene', 'LE-NewScene'),
             'control-s': ('Save Scene', 'LE-SaveScene'),
             'control-o': ('Open Scene', 'LE-OpenScene'),
             'control-q': ('Quit', 'LE-Quit'),
-            }
+        }
 
         self.speicalKeyMap = {
                               'enter': 'DIRECT-enter',
@@ -303,14 +317,16 @@ class DirectSession(DirectObject):
             self.clusterMode = ConfigVariableString("cluster-mode", '').value
 
         if self.clusterMode == 'client':
+            from direct.cluster.ClusterClient import createClusterClient
             self.cluster = createClusterClient()
         elif self.clusterMode == 'server':
+            from direct.cluster.ClusterServer import ClusterServer
             self.cluster = ClusterServer(base.camera, base.cam)
         else:
+            from direct.cluster.ClusterClient import DummyClusterClient
             self.cluster = DummyClusterClient()
         __builtins__['cluster'] = self.cluster
 
-
     def addPassThroughKey(self,key):
 
         self.passThroughKeys.append(key)
@@ -541,7 +557,7 @@ class DirectSession(DirectObject):
                     base.mouseWatcher = winCtrl.mouseWatcher
                     base.mouseWatcherNode = winCtrl.mouseWatcher.node()
                     base.direct.dr.mouseUpdate()
-                    LE_showInOneCam(self.selectedNPReadout, self.camera.getName())
+                    DG.LE_showInOneCam(self.selectedNPReadout, self.camera.getName())
                     base.direct.widget = base.direct.manipulationControl.widgetList[base.camList.index(NodePath(winCtrl.camNode))]
 
                 input = input[8:] # get rid of camera prefix
@@ -590,8 +606,8 @@ class DirectSession(DirectObject):
             self.fControl = 1
             # [gjeon] to update control key information while mouse1 is pressed
             if self.fMouse1:
-                modifiers = DIRECT_NO_MOD
-                modifiers |= DIRECT_CONTROL_MOD
+                modifiers = DG.DIRECT_NO_MOD
+                modifiers |= DG.DIRECT_CONTROL_MOD
                 messenger.send('DIRECT-mouse1', sentArgs = [modifiers])
         elif input == 'control-up':
             self.fControl = 0
@@ -601,16 +617,16 @@ class DirectSession(DirectObject):
             self.fAlt = 1
             # [gjeon] to update alt key information while mouse1 is pressed
             if self.fMouse1:
-                modifiers = DIRECT_NO_MOD
-                modifiers |= DIRECT_ALT_MOD
+                modifiers = DG.DIRECT_NO_MOD
+                modifiers |= DG.DIRECT_ALT_MOD
                 messenger.send('DIRECT-mouse1', sentArgs = [modifiers])
             elif self.fMouse2:
-                modifiers = DIRECT_NO_MOD
-                modifiers |= DIRECT_ALT_MOD
+                modifiers = DG.DIRECT_NO_MOD
+                modifiers |= DG.DIRECT_ALT_MOD
                 messenger.send('DIRECT-mouse2', sentArgs = [modifiers])
             elif self.fMouse3:
-                modifiers = DIRECT_NO_MOD
-                modifiers |= DIRECT_ALT_MOD
+                modifiers = DG.DIRECT_NO_MOD
+                modifiers |= DG.DIRECT_ALT_MOD
                 messenger.send('DIRECT-mouse3', sentArgs = [modifiers])
         elif input == 'alt-up':
             self.fAlt = 0
@@ -637,24 +653,24 @@ class DirectSession(DirectObject):
             self.select(self.selected.last)
 
     def getModifiers(self, input, base):
-        modifiers = DIRECT_NO_MOD
+        modifiers = DG.DIRECT_NO_MOD
         modifierString = input[: input.find(base)]
         if modifierString.find('shift') != -1:
-            modifiers |= DIRECT_SHIFT_MOD
+            modifiers |= DG.DIRECT_SHIFT_MOD
         if modifierString.find('control') != -1:
-            modifiers |= DIRECT_CONTROL_MOD
+            modifiers |= DG.DIRECT_CONTROL_MOD
         if modifierString.find('alt') != -1:
-            modifiers |= DIRECT_ALT_MOD
+            modifiers |= DG.DIRECT_ALT_MOD
         return modifiers
 
     def gotShift(self, modifiers):
-        return modifiers & DIRECT_SHIFT_MOD
+        return modifiers & DG.DIRECT_SHIFT_MOD
 
     def gotControl(self, modifiers):
-        return modifiers & DIRECT_CONTROL_MOD
+        return modifiers & DG.DIRECT_CONTROL_MOD
 
     def gotAlt(self, modifiers):
-        return modifiers & DIRECT_ALT_MOD
+        return modifiers & DG.DIRECT_ALT_MOD
 
     def setFScaleWidgetByCam(self, flag):
         self.fScaleWidgetByCam = flag
@@ -708,7 +724,7 @@ class DirectSession(DirectObject):
             else:
                 self.widget.showWidget()
             editTypes = self.manipulationControl.getEditTypes([dnp])
-            if (editTypes & EDIT_TYPE_UNEDITABLE) == EDIT_TYPE_UNEDITABLE:
+            if (editTypes & DG.EDIT_TYPE_UNEDITABLE) == DG.EDIT_TYPE_UNEDITABLE:
                 self.manipulationControl.disableWidgetMove()
             else:
                 self.manipulationControl.enableWidgetMove()
@@ -831,7 +847,7 @@ class DirectSession(DirectObject):
             # Temporarily set node path color
             nodePath.setColor(flashColor)
             # Clean up color in a few seconds
-            t = taskMgr.doMethodLater(DIRECT_FLASH_DURATION,
+            t = taskMgr.doMethodLater(DG.DIRECT_FLASH_DURATION,
                                       # This is just a dummy task
                                       self.flashDummy,
                                       'flashNodePath',)
@@ -859,6 +875,7 @@ class DirectSession(DirectObject):
             # If nothing specified, try selected node path
             nodePath = self.selected.last
         base.direct.select(nodePath)
+
         def fitTask(state, self = self):
             self.cameraControl.fitOnWidget()
             return Task.done
@@ -1203,12 +1220,13 @@ class DisplayRegionContext(DirectObject):
                          self.near,
                          (self.nearHeight*0.5) * self.mouseY)
 
+
 class DisplayRegionList(DirectObject):
     def __init__(self):
         self.displayRegionList = []
         i = 0
         # Things are funky if we are oobe
-        if (hasattr(base, 'oobeMode') and base.oobeMode):
+        if getattr(base, 'oobeMode', False):
             # assume we only have one cam at this point
             drc = DisplayRegionContext(base.cam)
             self.displayRegionList.append(drc)
@@ -1224,10 +1242,9 @@ class DisplayRegionList(DirectObject):
             # a display region for each real display region, and then
             # keep track of which are currently active (e.g. use a flag)
             # processing only them.
-            for camIndex in range(len(base.camList)):
-                cam = base.camList[camIndex]
-                if cam.getName()=='<noname>':
-                    cam.setName('Camera%d' % camIndex)
+            for camIndex, cam in enumerate(base.camList):
+                if cam.name == '<noname>':
+                    cam.name = f'Camera{camIndex}'
                 drc = DisplayRegionContext(cam)
                 self.displayRegionList.append(drc)
 
@@ -1272,7 +1289,7 @@ class DisplayRegionList(DirectObject):
         for dr in self.displayRegionList:
             dr.setVfov(fov)
 
-    def mouseUpdate(self, modifiers = DIRECT_NO_MOD):
+    def mouseUpdate(self, modifiers = DG.DIRECT_NO_MOD):
         for dr in self.displayRegionList:
             dr.mouseUpdate()
         #base.direct.dr = self.getCurrentDr()

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

@@ -1,5 +1,3 @@
-
-from .DirectGlobals import *
 from panda3d.core import VBase4
 from direct.task.Task import Task
 from direct.task.TaskManagerGlobal import taskMgr

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

@@ -7,6 +7,8 @@ ChunkSize = 100
 FilePattern = 'largeBlob.%s'
 
 def getLargeBlobPath():
+    from panda3d.core import ConfigVariableString, ConfigFlags
+
     # this folder needs to be accessible by everyone that is going to level edit
     # an area as a group
-    return config.GetString('large-blob-path', 'i:\\toontown_in_game_editor_temp')
+    return ConfigVariableString('large-blob-path', 'i:\\toontown_in_game_editor_temp', 'DConfig', ConfigFlags.F_dconfig).value

+ 22 - 11
direct/src/directutil/Mopath.py

@@ -1,8 +1,20 @@
 from direct.showbase.DirectObject import DirectObject
 from direct.showbase.MessengerGlobal import messenger
-from direct.directtools.DirectGeometry import *
-
-from panda3d.core import NodePath, LineSegs, ClockObject
+from direct.directtools.DirectUtil import CLAMP
+from direct.task.TaskManagerGlobal import taskMgr
+
+from panda3d.core import (
+    PCTHPR,
+    PCTNONE,
+    PCTT,
+    PCTXYZ,
+    ClockObject,
+    LineSegs,
+    NodePath,
+    ParametricCurve,
+    Point3,
+    Vec3,
+)
 
 
 class Mopath(DirectObject):
@@ -11,7 +23,7 @@ class Mopath(DirectObject):
 
     def __init__(self, name = None, fluid = 1, objectToLoad = None, upVectorNodePath = None, reverseUpVector = False):
         if name is None:
-            name = 'mopath%d' % self.nameIndex
+            name = f'mopath{self.nameIndex}'
             self.nameIndex = self.nameIndex + 1
         self.name = name
         self.fluid = fluid
@@ -26,12 +38,12 @@ class Mopath(DirectObject):
         self.upVectorNodePath = upVectorNodePath
         self.reverseUpVector = reverseUpVector
         self.reset()
-        if isinstance( objectToLoad, NodePath ):
-            self.loadNodePath( objectToLoad )
-        elif isinstance( objectToLoad, str ):
-            self.loadFile( objectToLoad )
+        if isinstance(objectToLoad, NodePath):
+            self.loadNodePath(objectToLoad)
+        elif isinstance(objectToLoad, str):
+            self.loadFile(objectToLoad)
         elif objectToLoad is not None:
-            print("Mopath: Unable to load object '%s', objectToLoad must be a file name string or a NodePath" % objectToLoad)
+            print(f"Mopath: Unable to load object '{objectToLoad}', objectToLoad must be a file name string or a NodePath")
 
     def getMaxT(self):
         return self.maxT * self.timeScale
@@ -49,7 +61,7 @@ class Mopath(DirectObject):
             self.reset()
 
         self.__extractCurves(nodePath)
-        if self.tNurbsCurve != []:
+        if self.tNurbsCurve:
             self.maxT = self.tNurbsCurve[-1].getMaxT()
         elif self.xyzNurbsCurve is not None:
             self.maxT = self.xyzNurbsCurve.getMaxT()
@@ -58,7 +70,6 @@ class Mopath(DirectObject):
         else:
             print('Mopath: no valid curves in nodePath: %s' % nodePath)
 
-
     def reset(self):
         self.maxT = 0.0
         self.loop = 0

+ 2 - 0
direct/src/directutil/Verify.py

@@ -45,6 +45,8 @@ Please use assert (properly) and do proper error handling; and use
 where it helps you resist using assert for error handling.
 """
 
+__all__ = ["verify"]
+
 from panda3d.core import ConfigVariableBool
 
 # Set to true to load pdb on failure.

+ 64 - 70
direct/src/dist/FreezeTool.py

@@ -22,7 +22,7 @@ try:
 except ImportError:
     p3extend_frozen = None
 
-from panda3d.core import *
+from panda3d.core import Filename, Multifile, PandaSystem, StringStream
 
 # Check to see if we are running python_d, which implies we have a
 # debug build, and we have to build the module with debug options.
@@ -83,7 +83,9 @@ defaultHiddenImports = {
     'plyer': ['plyer.platforms'],
     'scipy.linalg': ['scipy.linalg.cython_blas', 'scipy.linalg.cython_lapack'],
     'scipy.sparse.csgraph': ['scipy.sparse.csgraph._validation'],
+    'scipy.spatial.qhull': ['scipy._lib.messagestream'],
     'scipy.spatial._qhull': ['scipy._lib.messagestream'],
+    'scipy.spatial.transform.rotation': ['scipy.spatial.transform._rotation_groups'],
     'scipy.spatial.transform._rotation': ['scipy.spatial.transform._rotation_groups'],
     'scipy.special._ufuncs': ['scipy.special._ufuncs_cxx'],
     'scipy.stats._stats': ['scipy.special.cython_special'],
@@ -273,32 +275,32 @@ class CompilationEnvironment:
 
     def compileExe(self, filename, basename, extraLink=[]):
         compile = self.compileObjExe % dict({
-            'python' : self.Python,
-            'MSVC' : self.MSVC,
-            'PSDK' : self.PSDK,
-            'suffix64' : self.suffix64,
-            'MD' : self.MD,
-            'pythonIPath' : self.PythonIPath,
-            'pythonVersion' : self.PythonVersion,
-            'arch' : self.arch,
-            'filename' : filename,
-            'basename' : basename,
-            }, **sysconf.get_config_vars())
+            'python': self.Python,
+            'MSVC': self.MSVC,
+            'PSDK': self.PSDK,
+            'suffix64': self.suffix64,
+            'MD': self.MD,
+            'pythonIPath': self.PythonIPath,
+            'pythonVersion': self.PythonVersion,
+            'arch': self.arch,
+            'filename': filename,
+            'basename': basename,
+        }, **sysconf.get_config_vars())
         sys.stderr.write(compile + '\n')
         if os.system(compile) != 0:
             raise Exception('failed to compile %s.' % basename)
 
         link = self.linkExe % dict({
-            'python' : self.Python,
-            'MSVC' : self.MSVC,
-            'PSDK' : self.PSDK,
-            'suffix64' : self.suffix64,
-            'pythonIPath' : self.PythonIPath,
-            'pythonVersion' : self.PythonVersion,
-            'arch' : self.arch,
-            'filename' : filename,
-            'basename' : basename,
-            }, **sysconf.get_config_vars())
+            'python': self.Python,
+            'MSVC': self.MSVC,
+            'PSDK': self.PSDK,
+            'suffix64': self.suffix64,
+            'pythonIPath': self.PythonIPath,
+            'pythonVersion': self.PythonVersion,
+            'arch': self.arch,
+            'filename': filename,
+            'basename': basename,
+        }, **sysconf.get_config_vars())
         link += ' ' + ' '.join(extraLink)
         sys.stderr.write(link + '\n')
         if os.system(link) != 0:
@@ -306,38 +308,39 @@ class CompilationEnvironment:
 
     def compileDll(self, filename, basename, extraLink=[]):
         compile = self.compileObjDll % dict({
-            'python' : self.Python,
-            'MSVC' : self.MSVC,
-            'PSDK' : self.PSDK,
-            'suffix64' : self.suffix64,
-            'MD' : self.MD,
-            'pythonIPath' : self.PythonIPath,
-            'pythonVersion' : self.PythonVersion,
-            'arch' : self.arch,
-            'filename' : filename,
-            'basename' : basename,
-            }, **sysconf.get_config_vars())
+            'python': self.Python,
+            'MSVC': self.MSVC,
+            'PSDK': self.PSDK,
+            'suffix64': self.suffix64,
+            'MD': self.MD,
+            'pythonIPath': self.PythonIPath,
+            'pythonVersion': self.PythonVersion,
+            'arch': self.arch,
+            'filename': filename,
+            'basename': basename,
+        }, **sysconf.get_config_vars())
         sys.stderr.write(compile + '\n')
         if os.system(compile) != 0:
             raise Exception('failed to compile %s.' % basename)
 
         link = self.linkDll % dict({
-            'python' : self.Python,
-            'MSVC' : self.MSVC,
-            'PSDK' : self.PSDK,
-            'suffix64' : self.suffix64,
-            'pythonIPath' : self.PythonIPath,
-            'pythonVersion' : self.PythonVersion,
-            'arch' : self.arch,
-            'filename' : filename,
-            'basename' : basename,
-            'dllext' : self.dllext,
-            }, **sysconf.get_config_vars())
+            'python': self.Python,
+            'MSVC': self.MSVC,
+            'PSDK': self.PSDK,
+            'suffix64': self.suffix64,
+            'pythonIPath': self.PythonIPath,
+            'pythonVersion': self.PythonVersion,
+            'arch': self.arch,
+            'filename': filename,
+            'basename': basename,
+            'dllext': self.dllext,
+        }, **sysconf.get_config_vars())
         link += ' ' + ' '.join(extraLink)
         sys.stderr.write(link + '\n')
         if os.system(link) != 0:
             raise Exception('failed to link %s.' % basename)
 
+
 # The code from frozenmain.c in the Python source repository.
 frozenMainCode = """
 /* Python interpreter main program for frozen scripts */
@@ -673,7 +676,7 @@ okMissing = [
     'direct.extensions_native.extensions_darwin', '_manylinux',
     'collections.Iterable', 'collections.Mapping', 'collections.MutableMapping',
     'collections.Sequence', 'numpy_distutils', '_winapi',
-    ]
+]
 
 # Since around macOS 10.15, Apple's codesigning process has become more strict.
 # Appending data to the end of a Mach-O binary is now explicitly forbidden. The
@@ -720,6 +723,7 @@ lc_indices_to_slide = {
     LC_DATA_IN_CODE: [2],
 }
 
+
 class Freezer:
     class ModuleDef:
         def __init__(self, moduleName, filename = None,
@@ -1139,7 +1143,7 @@ class Freezer:
             if mdef.implicit and '.' in newName:
                 # For implicit modules, check if the parent is excluded.
                 parentName, baseName = newName.rsplit('.', 1)
-                if parentName in excludeDict :
+                if parentName in excludeDict:
                     mdef = excludeDict[parentName]
 
             if mdef.exclude:
@@ -1180,7 +1184,7 @@ class Freezer:
                 self.__loadModule(mdef)
                 # Since it successfully loaded, it's no longer a guess.
                 mdef.guess = False
-            except:
+            except Exception:
                 # Something went wrong, guess it's not an importable
                 # module.
                 pass
@@ -1218,7 +1222,7 @@ class Freezer:
 
             try:
                 self.__loadModule(self.ModuleDef(modname, implicit=True))
-            except:
+            except Exception:
                 missing.append(modname)
 
         # Now, any new modules we found get added to the export list.
@@ -1594,7 +1598,7 @@ class Freezer:
         text = programFile % {
             'moduleDefs': '\n'.join(moduleDefs),
             'moduleList': '\n'.join(moduleList),
-            }
+        }
 
         if self.linkExtensionModules and self.extras:
             # Should we link in extension modules?  If so, we write out a new
@@ -1695,11 +1699,11 @@ class Freezer:
             if self.platform.startswith('win'):
                 code += self.frozenDllMainCode
             initCode = self.mainInitCode % {
-                'frozenMainCode' : code,
-                'programName' : os.path.basename(basename),
-                'dllexport' : dllexport,
-                'dllimport' : dllimport,
-                }
+                'frozenMainCode': code,
+                'programName': os.path.basename(basename),
+                'dllexport': dllexport,
+                'dllimport': dllimport,
+            }
             if self.platform.startswith('win'):
                 target = basename + '.exe'
             else:
@@ -1714,10 +1718,10 @@ class Freezer:
                 target = basename + '.so'
 
             initCode = dllInitCode % {
-                'moduleName' : os.path.basename(basename),
-                'dllexport' : dllexport,
-                'dllimport' : dllimport,
-                }
+                'moduleName': os.path.basename(basename),
+                'dllexport': dllexport,
+                'dllimport': dllimport,
+            }
             compileFunc = self.cenv.compileDll
 
         self.writeCode(filename, initCode=initCode)
@@ -2333,17 +2337,8 @@ class Freezer:
         return blob_offset
 
     def makeModuleDef(self, mangledName, code):
-        result = ''
-        result += 'static unsigned char %s[] = {' % (mangledName)
-        for i in range(0, len(code), 16):
-            result += '\n  '
-            for c in code[i:i+16]:
-                if isinstance(c, int): # Python 3
-                    result += ('%d,' % c)
-                else: # Python 2
-                    result += ('%d,' % ord(c))
-        result += '\n};\n'
-        return result
+        lines = ',\n  '.join(','.join(map(str, code[i:i+16])) for i in range(0, len(code), 16))
+        return f'static unsigned char {mangledName}[] = {{\n  {lines}\n}};\n'
 
     def makeModuleListEntry(self, mangledName, code, moduleName, module):
         size = len(code)
@@ -2355,7 +2350,6 @@ class Freezer:
     def makeForbiddenModuleListEntry(self, moduleName):
         return '  {"%s", NULL, 0},' % (moduleName)
 
-
     def __writingModule(self, moduleName):
         """ Returns true if we are outputting the named module in this
         pass, false if we have already output in a previous pass, or

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

@@ -3,9 +3,10 @@
 import xml.etree.ElementTree as ET
 
 from ._proto.targeting_pb2 import Abi
-from ._proto.config_pb2 import BundleConfig
-from ._proto.files_pb2 import NativeLibraries
-from ._proto.Resources_pb2 import XmlNode, ResourceTable
+from ._proto.config_pb2 import BundleConfig # pylint: disable=unused-import
+from ._proto.files_pb2 import NativeLibraries # pylint: disable=unused-import
+from ._proto.Resources_pb2 import ResourceTable # pylint: disable=unused-import
+from ._proto.Resources_pb2 import XmlNode
 
 
 AbiAlias = Abi.AbiAlias

+ 5 - 4
direct/src/dist/commands.py

@@ -125,7 +125,7 @@ PACKAGE_LIB_DIRS = {
     'PyQt5':  [('PyQt5/Qt5/bin', 'PyQt5_Qt5')],
 }
 
-SITE_PY = u"""
+SITE_PY = """
 import sys
 from _frozen_importlib import _imp, FrozenImporter
 
@@ -592,7 +592,7 @@ class build_apps(setuptools.Command):
 
         whlcache = os.path.join(self.build_base, '__whl_cache__')
 
-        pip_version = int(pip.__version__.split('.')[0])
+        pip_version = int(pip.__version__.split('.', 1)[0])
         if pip_version < 9:
             raise RuntimeError("pip 9.0 or greater is required, but found {}".format(pip.__version__))
 
@@ -1121,7 +1121,7 @@ class build_apps(setuptools.Command):
 
                 # Remove python version string
                 parts = basename.split('.')
-                if len(parts) >= 3 and '-' in parts[-2]:
+                if len(parts) >= 3 and ('-' in parts[-2] or parts[-2] == 'abi' + str(sys.version_info[0])):
                     parts = parts[:-2] + parts[-1:]
                     basename = '.'.join(parts)
 
@@ -1494,7 +1494,8 @@ class build_apps(setuptools.Command):
         for idx in string_tables.keys():
             elf.seek(shoff + idx * shentsize)
             type, offset, size, link, entsize = struct.unpack_from(section_struct, elf.read(shentsize))
-            if type != 3: continue
+            if type != 3:
+                continue
             elf.seek(offset)
             string_tables[idx] = elf.read(size)
 

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

@@ -1,4 +1,4 @@
-from direct.directnotify.DirectNotifyGlobal import *
+from direct.directnotify.DirectNotifyGlobal import directNotify
 from panda3d.core import PNMImage, Filename, PNMFileTypeRegistry, StringStream
 import struct
 
@@ -114,9 +114,6 @@ class Icon:
                 for x in range(size):
                     pixel = image2.get_pixel(x, size - y - 1)
                     index = colors.index(pixel)
-                    if index >= 256:
-                        # Find closest pixel instead.
-                        index = closest_indices[index - 256]
                     fp.write(struct.pack('<B', index))
                 fp.write(rowalign)
         else:

+ 2 - 2
direct/src/dist/installers.py

@@ -271,7 +271,7 @@ def create_aab(command, basename, build_dir):
     bundle.add_subfile('base/manifest/AndroidManifest.xml', p3d.StringStream(axml.dumps()), 9)
 
     # Add the classes.dex.
-    bundle.add_subfile(f'base/dex/classes.dex', build_dir_fn / 'classes.dex', 9)
+    bundle.add_subfile('base/dex/classes.dex', build_dir_fn / 'classes.dex', 9)
 
     # Add libraries, compressed.
     for abi in os.listdir(os.path.join(build_dir, 'lib')):
@@ -302,7 +302,7 @@ def create_aab(command, basename, build_dir):
             # It appears to be encrypted, and we don't have a passphrase, so we
             # must request it on the command-line.
             from getpass import getpass
-            password = getpass(f'Enter pass phrase for private key: ')
+            password = getpass('Enter pass phrase for private key: ')
 
         if not bundle.add_jar_signature(
                 p3d.Filename.from_os_specific(command.signing_certificate),

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

@@ -28,6 +28,7 @@ def _unpack_zstring(mem, offs=0):
         c = mem[offs]
     return str
 
+
 def _unpack_wstring(mem, offs=0):
     "Read a UCS-2 string from memory."
     name_len, = unpack('<H', mem[offs:offs+2])
@@ -37,6 +38,7 @@ def _unpack_wstring(mem, offs=0):
         name += chr(*unpack('<H', mem[offs:offs+2]))
     return name
 
+
 def _padded(n, boundary):
     align = n % boundary
     if align:
@@ -250,11 +252,11 @@ class VersionInfoResource(object):
             self.struct_version = dwords[1]
         if len(dwords) > 3:
             self.file_version = \
-               (int(dwords[2] >> 16), int(dwords[2] & 0xffff),
+                (int(dwords[2] >> 16), int(dwords[2] & 0xffff),
                 int(dwords[3] >> 16), int(dwords[3] & 0xffff))
         if len(dwords) > 5:
             self.product_version = \
-               (int(dwords[4] >> 16), int(dwords[4] & 0xffff),
+                (int(dwords[4] >> 16), int(dwords[4] & 0xffff),
                 int(dwords[5] >> 16), int(dwords[5] & 0xffff))
         if len(dwords) > 7:
             self.file_flags_mask = dwords[6]
@@ -302,7 +304,7 @@ class VersionInfoResource(object):
             # It contains a value.
             if type:
                 # It's a wchar array value.
-                value = u""
+                value = ""
                 c, = unpack('<H', data[offset:offset+2])
                 offset += 2
                 while c:

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

@@ -2,7 +2,8 @@
 from direct.directnotify import DirectNotifyGlobal
 from direct.showbase.DirectObject import DirectObject
 from direct.showbase.MessengerGlobal import messenger
-from .ConnectionRepository import *
+from direct.task import Task
+from direct.task.TaskManagerGlobal import taskMgr
 from panda3d.core import ConfigVariableDouble, ConfigVariableInt, ConfigVariableBool
 
 ASYNC_REQUEST_DEFAULT_TIMEOUT_IN_SECONDS = 8.0
@@ -266,4 +267,4 @@ def cleanupAsyncRequests():
     """
     for asyncRequest in AsyncRequest._asyncRequests:
         asyncRequest.delete()
-    assert AsyncRequest._asyncRequests == {}
+    assert not AsyncRequest._asyncRequests

+ 13 - 1
direct/src/distributed/ClientRepository.py

@@ -3,7 +3,19 @@
 from .ClientRepositoryBase import ClientRepositoryBase
 from direct.directnotify import DirectNotifyGlobal
 from direct.showbase.MessengerGlobal import messenger
-from .MsgTypesCMU import *
+from .MsgTypesCMU import (
+    CLIENT_DISCONNECT_CMU,
+    CLIENT_HEARTBEAT_CMU,
+    CLIENT_OBJECT_UPDATE_FIELD_TARGETED_CMU,
+    CLIENT_SET_INTEREST_CMU,
+    OBJECT_DELETE_CMU,
+    OBJECT_DISABLE_CMU,
+    OBJECT_GENERATE_CMU,
+    OBJECT_SET_ZONE_CMU,
+    OBJECT_UPDATE_FIELD_CMU,
+    REQUEST_GENERATES_CMU,
+    SET_DOID_RANGE_CMU,
+)
 from .PyDatagram import PyDatagram
 from .PyDatagramIterator import PyDatagramIterator
 from panda3d.core import UniqueIdAllocator, Notify, ClockObject

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

@@ -1,5 +1,10 @@
-from panda3d.core import *
-from panda3d.direct import *
+from panda3d.core import (
+    ClockObject,
+    ConfigVariableBool,
+    ConfigVariableDouble,
+    Datagram,
+    DatagramIterator,
+)
 from direct.task import Task
 from direct.task.TaskManagerGlobal import taskMgr
 from direct.directnotify import DirectNotifyGlobal
@@ -7,11 +12,10 @@ from direct.distributed.CRDataCache import CRDataCache
 from direct.distributed.ConnectionRepository import ConnectionRepository
 from direct.showbase.PythonUtil import safeRepr, itype, makeList
 from direct.showbase.MessengerGlobal import messenger
-from .MsgTypes import *
+from .MsgTypes import CLIENT_ENTER_OBJECT_REQUIRED_OTHER, MsgId2Names
 from . import CRCache
 from . import ParentMgr
 from . import RelatedObjectMgr
-from .ClockDelta import *
 import time
 
 
@@ -106,7 +110,7 @@ class ClientRepositoryBase(ConnectionRepository):
         ## self.flush()
 
     def specialName(self, label):
-        name = ("SpecialName %s %s" % (self.specialNameNumber, label))
+        name = f"SpecialName {self.specialNameNumber} {label}"
         self.specialNameNumber += 1
         return name
 
@@ -118,7 +122,7 @@ class ClientRepositoryBase(ConnectionRepository):
 
     def _getMsgName(self, msgId):
         # we might get a list of message names, use the first one
-        return makeList(MsgId2Names.get(msgId, 'UNKNOWN MESSAGE: %s' % msgId))[0]
+        return makeList(MsgId2Names.get(msgId, f'UNKNOWN MESSAGE: {msgId}'))[0]
 
     def allocateContext(self):
         self.context+=1
@@ -157,7 +161,7 @@ class ClientRepositoryBase(ConnectionRepository):
         # Look up the dclass
         assert parentId == self.GameGlobalsId or parentId in self.doId2do
         dclass = self.dclassesByNumber[classId]
-        assert self.notify.debug("performing generate for %s %s" % (dclass.getName(), doId))
+        assert self.notify.debug(f"performing generate for {dclass.getName()} {doId}")
         dclass.startGenerate()
         # Create a new distributed object, and put it in the dictionary
         distObj = self.generateWithRequiredOtherFields(dclass, doId, di, parentId, zoneId)
@@ -356,7 +360,6 @@ class ClientRepositoryBase(ConnectionRepository):
             # updateRequiredOtherFields calls announceGenerate
         return distObj
 
-
     def disableDoId(self, doId, ownerView=False):
         table, cache = self.getTables(ownerView)
         # Make sure the object exists
@@ -438,7 +441,6 @@ class ClientRepositoryBase(ConnectionRepository):
             # This object has been fully generated.  It's OK to update.
             self.__doUpdate(doId, di, ovUpdated)
 
-
     def __doUpdate(self, doId, di, ovUpdated):
         # Find the DO
         do = self.doId2do.get(doId)
@@ -459,13 +461,12 @@ class ClientRepositoryBase(ConnectionRepository):
                 if handle:
                     dclass = self.dclassesByName[handle.dclassName]
                     dclass.receiveUpdate(handle, di)
-
                 else:
                     self.notify.warning(
-                        "Asked to update non-existent DistObj " + str(doId))
-            except:
+                        f"Asked to update non-existent DistObj {doId}")
+            except Exception:
                 self.notify.warning(
-                        "Asked to update non-existent DistObj " + str(doId) + "and failed to find it")
+                    f"Asked to update non-existent DistObj {doId} and failed to find it")
 
     def __doUpdateOwner(self, doId, di):
         ovObj = self.doId2ownerView.get(doId)
@@ -484,7 +485,7 @@ class ClientRepositoryBase(ConnectionRepository):
             self.bootedText = di.getString()
 
             self.notify.warning(
-                "Server is booting us out (%d): %s" % (self.bootedIndex, self.bootedText))
+                f"Server is booting us out ({self.bootedIndex}): {self.bootedText}")
         else:
             self.bootedIndex = None
             self.bootedText = None
@@ -532,7 +533,6 @@ class ClientRepositoryBase(ConnectionRepository):
                 doDict[doId] = do
         return doDict
 
-
     def considerHeartbeat(self):
         """Send a heartbeat message if we haven't sent one recently."""
         if not self.heartbeatStarted:
@@ -562,7 +562,7 @@ class ClientRepositoryBase(ConnectionRepository):
 
     def waitForNextHeartBeat(self):
         taskMgr.doMethodLater(self.heartbeatInterval, self.sendHeartbeatTask,
-                              "heartBeat", taskChain = 'net')
+                              "heartBeat", taskChain='net')
 
     def replaceMethod(self, oldMethod, newFunction):
         return 0

+ 8 - 6
direct/src/distributed/ConnectionRepository.py

@@ -1,5 +1,5 @@
-from panda3d.core import *
-from panda3d.direct import *
+from panda3d.core import DocumentSpec, Filename, HTTPClient, VirtualFileSystem, getModelPath
+from panda3d.direct import CConnectionRepository, DCPacker
 from direct.task import Task
 from direct.task.TaskManagerGlobal import taskMgr
 from direct.directnotify.DirectNotifyGlobal import directNotify
@@ -13,6 +13,7 @@ import gc
 
 __all__ = ["ConnectionRepository", "GCTrigger"]
 
+
 class ConnectionRepository(
         DoInterestManager, DoCollectionManager, CConnectionRepository):
     """
@@ -233,7 +234,7 @@ class ConnectionRepository(
         distObj.parentId = 0
         distObj.zoneId = 0
         # updateRequiredFields calls announceGenerate
-        return  distObj
+        return distObj
 
     def readDCFile(self, dcFileNames = None):
         """
@@ -382,7 +383,7 @@ class ConnectionRepository(
             # in the DC file.
             for i in range(dcFile.getNumClasses()):
                 dclass = dcFile.getClass(i)
-                if (dclass.getName()+ownerDcSuffix) in ownerImportSymbols:
+                if dclass.getName() + ownerDcSuffix in ownerImportSymbols:
                     number = dclass.getNumber()
                     className = dclass.getName() + ownerDcSuffix
 
@@ -466,7 +467,7 @@ class ConnectionRepository(
         hasProxy = 0
         if self.checkHttp():
             proxies = self.http.getProxiesForUrl(serverList[0])
-            hasProxy = (proxies != 'DIRECT')
+            hasProxy = proxies != 'DIRECT'
 
         if hasProxy:
             self.notify.info("Connecting to gameserver via proxy list: %s" % (proxies))
@@ -589,7 +590,7 @@ class ConnectionRepository(
         if self.http is None:
             try:
                 self.http = HTTPClient()
-            except:
+            except Exception:
                 pass
 
         return self.http
@@ -667,6 +668,7 @@ class ConnectionRepository(
     def uniqueName(self, idString):
         return "%s-%s" % (idString, self.uniqueId)
 
+
 class GCTrigger:
     # used to trigger garbage collection
     pass

+ 3 - 4
direct/src/distributed/DistributedCamera.py

@@ -1,9 +1,8 @@
-from panda3d.core import *
-from panda3d.direct import *
+from panda3d.core import ConfigVariableInt, GeomNode, NodePath, PerspectiveLens, Point3, VBase2
 from direct.fsm.FSM import FSM
-from direct.interval.IntervalGlobal import *
+from direct.interval.IntervalGlobal import LerpScaleInterval, Sequence
 from direct.distributed.DistributedObject import DistributedObject
-
+from direct.showbase.MessengerGlobal import messenger
 
 _camera_id = ConfigVariableInt('camera-id', -1)
 _aware_of_cameras = ConfigVariableInt('aware-of-cameras', 0)

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

@@ -1,5 +1,3 @@
-from panda3d.core import *
-from panda3d.direct import *
 from direct.distributed.DistributedObjectOV import DistributedObjectOV
 
 class DistributedCameraOV(DistributedObjectOV):

+ 4 - 5
direct/src/distributed/DistributedCartesianGrid.py

@@ -1,19 +1,18 @@
-
-from panda3d.core import *
-from panda3d.direct import *
-from direct.interval.IntervalGlobal import *
+from panda3d.core import ConfigVariableBool, TextNode, VBase4, Vec3
 from direct.directnotify.DirectNotifyGlobal import directNotify
 
 from direct.distributed.DistributedNode import DistributedNode
 from direct.task import Task
+from direct.task.TaskManagerGlobal import taskMgr
 from direct.gui import DirectGuiGlobals
 from direct.showbase.EventGroup import EventGroup
 from direct.showbase.PythonUtil import report
+from direct.showbase.MessengerGlobal import messenger
 from direct.distributed.GridParent import GridParent
 
 if __debug__:
     # For grid drawing
-    from direct.directtools.DirectGeometry import *
+    from direct.directtools.DirectGeometry import LineNodePath
     from direct.showbase.PythonUtil import randFloat
 
 from .CartesianGridBase import CartesianGridBase

+ 0 - 3
direct/src/distributed/DistributedCartesianGridAI.py

@@ -1,6 +1,3 @@
-
-from panda3d.core import *
-from panda3d.direct import *
 from direct.directnotify.DirectNotifyGlobal import directNotify
 from direct.task import Task
 from direct.task.TaskManagerGlobal import taskMgr

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

@@ -1,7 +1,6 @@
 """DistributedObject module: contains the DistributedObject class"""
 
-from panda3d.core import *
-from panda3d.direct import *
+from panda3d.direct import DCPacker
 from direct.showbase.MessengerGlobal import messenger
 from direct.directnotify.DirectNotifyGlobal import directNotify
 from direct.distributed.DistributedObjectBase import DistributedObjectBase
@@ -25,7 +24,7 @@ ESNum2Str = {
     ESDisabled: 'ESDisabled',
     ESGenerating: 'ESGenerating',
     ESGenerated: 'ESGenerated',
-    }
+}
 
 
 class DistributedObject(DistributedObjectBase):
@@ -241,7 +240,6 @@ class DistributedObject(DistributedObjectBase):
         """
         assert self.notify.debug('announceGenerate(): %s' % (self.doId))
 
-
     def _deactivateDO(self):
         # after this is called, the object is no longer an active DistributedObject
         # and it may be placed in the cache
@@ -325,9 +323,9 @@ class DistributedObject(DistributedObjectBase):
         """
         return self.doId
 
-
     #This message was moved out of announce generate
     #to avoid ordering issues.
+
     def postGenerateMessage(self):
         if self.activeState != ESGenerated:
             self.activeState = ESGenerated

+ 0 - 2
direct/src/distributed/DistributedObjectAI.py

@@ -4,8 +4,6 @@ from direct.directnotify.DirectNotifyGlobal import directNotify
 from direct.distributed.DistributedObjectBase import DistributedObjectBase
 from direct.showbase.MessengerGlobal import messenger
 from direct.showbase import PythonUtil
-from panda3d.core import *
-from panda3d.direct import *
 #from PyDatagram import PyDatagram
 #from PyDatagramIterator import PyDatagramIterator
 

+ 0 - 2
direct/src/distributed/DistributedObjectUD.py

@@ -4,8 +4,6 @@ from direct.directnotify.DirectNotifyGlobal import directNotify
 from direct.distributed.DistributedObjectBase import DistributedObjectBase
 from direct.showbase.MessengerGlobal import messenger
 from direct.showbase import PythonUtil
-from panda3d.core import *
-from panda3d.direct import *
 #from PyDatagram import PyDatagram
 #from PyDatagramIterator import PyDatagramIterator
 

+ 33 - 6
direct/src/distributed/DistributedSmoothNode.py

@@ -1,8 +1,9 @@
 """DistributedSmoothNode module: contains the DistributedSmoothNode class"""
 
-from panda3d.core import *
-from panda3d.direct import *
-from .ClockDelta import *
+import math
+from panda3d.core import ClockObject, ConfigVariableBool, ConfigVariableDouble, NodePath
+from panda3d.direct import SmoothMover
+from .ClockDelta import globalClockDelta
 from . import DistributedNode
 from . import DistributedSmoothNodeBase
 from direct.task.Task import cont
@@ -35,6 +36,8 @@ PredictionLag = ConfigVariableDouble("smooth-prediction-lag", 0.0)
 
 GlobalSmoothing = 0
 GlobalPrediction = 0
+
+
 def globalActivateSmoothing(smoothing, prediction):
     """ Globally activates or deactivates smoothing and prediction on
     all DistributedSmoothNodes currently in existence, or yet to be
@@ -47,6 +50,7 @@ def globalActivateSmoothing(smoothing, prediction):
     for obj in base.cr.getAllOfType(DistributedSmoothNode):
         obj.activateSmoothing(smoothing, prediction)
 
+
 # For historical reasons, we temporarily define
 # DistributedSmoothNode.activateSmoothing() to be the global function.
 # We'll remove this soon, so it won't get confused with the instance
@@ -153,6 +157,7 @@ class DistributedSmoothNode(DistributedNode.DistributedNode,
 
     def setSmoothWrtReparents(self, flag):
         self._smoothWrtReparents = flag
+
     def getSmoothWrtReparents(self):
         return self._smoothWrtReparents
 
@@ -212,42 +217,50 @@ class DistributedSmoothNode(DistributedNode.DistributedNode,
     def setSmStop(self, timestamp=None):
         self.setComponentTLive(timestamp)
         self.stopped = True
+
     def setSmH(self, h, timestamp=None):
         self._checkResume(timestamp)
         self.setComponentH(h)
         self.setComponentTLive(timestamp)
+
     def setSmZ(self, z, timestamp=None):
         self._checkResume(timestamp)
         self.setComponentZ(z)
         self.setComponentTLive(timestamp)
+
     def setSmXY(self, x, y, timestamp=None):
         self._checkResume(timestamp)
         self.setComponentX(x)
         self.setComponentY(y)
         self.setComponentTLive(timestamp)
+
     def setSmXZ(self, x, z, timestamp=None):
         self._checkResume(timestamp)
         self.setComponentX(x)
         self.setComponentZ(z)
         self.setComponentTLive(timestamp)
+
     def setSmPos(self, x, y, z, timestamp=None):
         self._checkResume(timestamp)
         self.setComponentX(x)
         self.setComponentY(y)
         self.setComponentZ(z)
         self.setComponentTLive(timestamp)
+
     def setSmHpr(self, h, p, r, timestamp=None):
         self._checkResume(timestamp)
         self.setComponentH(h)
         self.setComponentP(p)
         self.setComponentR(r)
         self.setComponentTLive(timestamp)
+
     def setSmXYH(self, x, y, h, timestamp):
         self._checkResume(timestamp)
         self.setComponentX(x)
         self.setComponentY(y)
         self.setComponentH(h)
         self.setComponentTLive(timestamp)
+
     def setSmXYZH(self, x, y, z, h, timestamp=None):
         self._checkResume(timestamp)
         self.setComponentX(x)
@@ -255,6 +268,7 @@ class DistributedSmoothNode(DistributedNode.DistributedNode,
         self.setComponentZ(z)
         self.setComponentH(h)
         self.setComponentTLive(timestamp)
+
     def setSmPosHpr(self, x, y, z, h, p, r, timestamp=None):
         self._checkResume(timestamp)
         self.setComponentX(x)
@@ -284,26 +298,33 @@ class DistributedSmoothNode(DistributedNode.DistributedNode,
     @report(types = ['args'], dConfigParam = 'smoothnode')
     def setComponentX(self, x):
         self.smoother.setX(x)
+
     @report(types = ['args'], dConfigParam = 'smoothnode')
     def setComponentY(self, y):
         self.smoother.setY(y)
+
     @report(types = ['args'], dConfigParam = 'smoothnode')
     def setComponentZ(self, z):
         self.smoother.setZ(z)
+
     @report(types = ['args'], dConfigParam = 'smoothnode')
     def setComponentH(self, h):
         self.smoother.setH(h)
+
     @report(types = ['args'], dConfigParam = 'smoothnode')
     def setComponentP(self, p):
         self.smoother.setP(p)
+
     @report(types = ['args'], dConfigParam = 'smoothnode')
     def setComponentR(self, r):
         self.smoother.setR(r)
+
     @report(types = ['args'], dConfigParam = 'smoothnode')
     def setComponentL(self, l):
         if l != self.zoneId:
             # only perform set location if location is different
             self.setLocation(self.parentId,l)
+
     @report(types = ['args'], dConfigParam = 'smoothnode')
     def setComponentT(self, timestamp):
         # This is a little bit hacky.  If *this* function is called,
@@ -386,18 +407,25 @@ class DistributedSmoothNode(DistributedNode.DistributedNode,
     # match set* in more cases than the Disney server does.
     def getComponentL(self):
         return self.zoneId
+
     def getComponentX(self):
         return self.getX()
+
     def getComponentY(self):
         return self.getY()
+
     def getComponentZ(self):
         return self.getZ()
+
     def getComponentH(self):
         return self.getH()
+
     def getComponentP(self):
         return self.getP()
+
     def getComponentR(self):
         return self.getR()
+
     def getComponentT(self):
         return 0
 
@@ -408,7 +436,6 @@ class DistributedSmoothNode(DistributedNode.DistributedNode,
         #printStack()
         self.smoother.clearPositions(1)
 
-
     @report(types = ['args'], dConfigParam = 'smoothnode')
     def wrtReparentTo(self, parent):
         # We override this NodePath method to force it to
@@ -464,6 +491,7 @@ class DistributedSmoothNode(DistributedNode.DistributedNode,
                     "Warning: couldn't find the avatar %d" % (avId))
             elif hasattr(other, "d_returnResync") and \
                  hasattr(self.cr, 'localAvatarDoId'):
+                globalClock = ClockObject.getGlobalClock()
                 realTime = globalClock.getRealTime()
                 serverTime = realTime - globalClockDelta.getDelta()
                 assert self.notify.info(
@@ -474,7 +502,6 @@ class DistributedSmoothNode(DistributedNode.DistributedNode,
                     serverTime,
                     globalClockDelta.getUncertainty())
 
-
     def d_returnResync(self, avId, timestampB, serverTime, uncertainty):
         serverTimeSec = math.floor(serverTime)
         serverTimeUSec = (serverTime - serverTimeSec) * 10000.0
@@ -482,7 +509,7 @@ class DistributedSmoothNode(DistributedNode.DistributedNode,
             avId, timestampB, serverTimeSec, serverTimeUSec, uncertainty])
 
     def returnResync(self, avId, timestampB, serverTimeSec, serverTimeUSec,
-            uncertainty):
+                     uncertainty):
         """
         A reply sent by a client whom we recently sent suggestResync
         to, this reports the client's new delta information so we can

+ 5 - 2
direct/src/distributed/DistributedSmoothNodeBase.py

@@ -1,6 +1,6 @@
 """DistributedSmoothNodeBase module: contains the DistributedSmoothNodeBase class"""
 
-from .ClockDelta import *
+from .ClockDelta import globalClockDelta
 from direct.task import Task
 from direct.task.TaskManagerGlobal import taskMgr
 from direct.showbase.PythonUtil import randFloat
@@ -13,8 +13,10 @@ class DummyTaskClass:
     def setDelay(self, blah):
         pass
 
+
 DummyTask = DummyTaskClass()
 
+
 class DistributedSmoothNodeBase:
     """common base class for DistributedSmoothNode and DistributedSmoothNodeAI
     """
@@ -43,6 +45,7 @@ class DistributedSmoothNodeBase:
     def b_clearSmoothing(self):
         self.d_clearSmoothing()
         self.clearSmoothing()
+
     def d_clearSmoothing(self):
         self.sendUpdate("clearSmoothing", [0])
 
@@ -85,7 +88,7 @@ class DistributedSmoothNodeBase:
             BT.FULL: self.cnode.broadcastPosHprFull,
             BT.XYH:  self.cnode.broadcastPosHprXyh,
             BT.XY:  self.cnode.broadcastPosHprXy,
-            }
+        }
         # this comment is here so it will show up in a grep for 'def d_broadcastPosHpr'
         self.d_broadcastPosHpr = broadcastFuncs[self.broadcastType]
 

+ 13 - 6
direct/src/distributed/DoInterestManager.py

@@ -7,16 +7,22 @@ zone, remove interest in that zone.
 p.s. A great deal of this code is just code moved from ClientRepository.py.
 """
 
-from panda3d.core import *
-from panda3d.direct import *
-from .MsgTypes import *
-from direct.showbase.PythonUtil import *
+from panda3d.core import ConfigVariableBool
+from .MsgTypes import CLIENT_ADD_INTEREST, CLIENT_ADD_INTEREST_MULTIPLE, CLIENT_REMOVE_INTEREST
 from direct.showbase import DirectObject
 from direct.showbase.MessengerGlobal import messenger
 from .PyDatagram import PyDatagram
 from direct.directnotify.DirectNotifyGlobal import directNotify
 import types
-from direct.showbase.PythonUtil import report
+from direct.showbase.PythonUtil import (
+    FrameDelayedCall,
+    ScratchPad,
+    SerialNumGen,
+    report,
+    serialNum,
+    uniqueElements,
+    uniqueName,
+)
 
 class InterestState:
     StateActive = 'Active'
@@ -627,6 +633,7 @@ class DoInterestManager(DirectObject.DirectObject):
 
 if __debug__:
     import unittest
+    import time
 
     class AsyncTestCase(unittest.TestCase):
         def setCompleted(self):
@@ -641,7 +648,7 @@ if __debug__:
         suiteClass = AsyncTestSuite
 
     class AsyncTextTestRunner(unittest.TextTestRunner):
-        def run(self, testCase):
+        def run(self, test):
             result = self._makeResult()
             startTime = time.time()
             test(result)

+ 1 - 1
direct/src/distributed/GridChild.py

@@ -1,6 +1,6 @@
 from direct.distributed.DistributedSmoothNodeBase import DistributedSmoothNodeBase
 from direct.distributed.GridParent import GridParent
-from direct.showbase.PythonUtil import report
+from direct.showbase.PythonUtil import report, getBase
 
 
 class GridChild:

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

@@ -1,6 +1,4 @@
-
-from panda3d.core import *
-from panda3d.direct import *
+from panda3d.core import NodePath
 
 #
 # GridParent.py

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

@@ -137,7 +137,7 @@ MsgName2Id = {
     'CLIENTAGENT_ADD_INTEREST':                     1200,
     'CLIENTAGENT_ADD_INTEREST_MULTIPLE':            1201,
     'CLIENTAGENT_REMOVE_INTEREST':                  1203,
-    }
+}
 
 # create id->name table for debugging
 MsgId2Names = invertDictLossless(MsgName2Id)
@@ -157,7 +157,7 @@ QUIET_ZONE_IGNORED_LIST = [
     #CLIENT_CREATE_OBJECT_REQUIRED,
     #CLIENT_CREATE_OBJECT_REQUIRED_OTHER,
 
-    ]
+]
 
 # The following is a different set of numbers from above.
 # These are the sub-message types for CLIENT_LOGIN_2.

+ 1 - 1
direct/src/distributed/MsgTypesCMU.py

@@ -20,7 +20,7 @@ MsgName2Id = {
     'CLIENT_OBJECT_UPDATE_FIELD_TARGETED_CMU'  : 9011,
 
     'CLIENT_OBJECT_UPDATE_FIELD' : 120,  # Matches MsgTypes.CLIENT_OBJECT_SET_FIELD
-    }
+}
 
 # create id->name table for debugging
 MsgId2Names = invertDictLossless(MsgName2Id)

+ 3 - 3
direct/src/distributed/NetMessenger.py

@@ -20,9 +20,9 @@ MESSAGE_TYPES=(
 
 # This is the reverse look up for the recipient of the
 # datagram:
-MESSAGE_STRINGS={}
-for i in zip(MESSAGE_TYPES, range(1, len(MESSAGE_TYPES)+1)):
-    MESSAGE_STRINGS[i[0]]=i[1]
+MESSAGE_STRINGS = {}
+for i in zip(MESSAGE_TYPES, range(1, len(MESSAGE_TYPES) + 1)):
+    MESSAGE_STRINGS[i[0]] = i[1]
 
 
 class NetMessenger(Messenger):

+ 23 - 3
direct/src/distributed/PyDatagram.py

@@ -4,10 +4,30 @@
 # of the file rather than every time we call the putArg function.
 
 from panda3d.core import Datagram
-from panda3d.direct import *
+from panda3d.direct import (
+    STInt8,
+    STInt16,
+    STInt32,
+    STInt64,
+    STUint8,
+    STUint16,
+    STUint32,
+    STUint64,
+    STFloat64,
+    STString,
+    STBlob,
+    STBlob32,
+    STInt16array,
+    STInt32array,
+    STUint16array,
+    STUint32array,
+    STInt8array,
+    STUint8array,
+    STUint32uint8array,
+)
 # Import the type numbers
 
-from direct.distributed.MsgTypes import *
+from direct.distributed.MsgTypes import CONTROL_CHANNEL
 
 
 class PyDatagram(Datagram):
@@ -28,7 +48,7 @@ class PyDatagram(Datagram):
         STString: (Datagram.addString, None),
         STBlob: (Datagram.addBlob, None),
         STBlob32: (Datagram.addBlob32, None),
-        }
+    }
 
     addChannel = Datagram.addUint64
 

+ 23 - 3
direct/src/distributed/PyDatagramIterator.py

@@ -3,8 +3,28 @@
 # class variable FuncDict and so we can import DCSubatomicType at the top
 # of the file rather than every time we call the putArg function.
 
-from panda3d.core import *
-from panda3d.direct import *
+from panda3d.core import DatagramIterator
+from panda3d.direct import (
+    STInt8,
+    STInt16,
+    STInt32,
+    STInt64,
+    STUint8,
+    STUint16,
+    STUint32,
+    STUint64,
+    STFloat64,
+    STString,
+    STBlob,
+    STBlob32,
+    STInt16array,
+    STInt32array,
+    STUint16array,
+    STUint32array,
+    STInt8array,
+    STUint8array,
+    STUint32uint8array,
+)
 # Import the type numbers
 
 
@@ -26,7 +46,7 @@ class PyDatagramIterator(DatagramIterator):
         STString: DatagramIterator.getString,
         STBlob: DatagramIterator.getBlob,
         STBlob32: DatagramIterator.getBlob32,
-        }
+    }
 
     getChannel = DatagramIterator.getUint64
 

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

@@ -1,7 +1,7 @@
 """SampleObject module: contains the SampleObject class"""
 
-from direct.directnotify.DirectNotifyGlobal import *
-from direct.distributed.DistributedObject import *
+from direct.directnotify.DirectNotifyGlobal import directNotify
+from direct.distributed.DistributedObject import DistributedObject
 
 class SampleObject(DistributedObject):
 

+ 33 - 3
direct/src/distributed/ServerRepository.py

@@ -1,8 +1,38 @@
 """ServerRepository module: contains the ServerRepository class"""
 
-from panda3d.core import *
-from panda3d.direct import *
-from direct.distributed.MsgTypesCMU import *
+from panda3d.core import (
+    ConfigVariableBool,
+    ConfigVariableDouble,
+    ConfigVariableInt,
+    ConnectionWriter,
+    DatagramIterator,
+    Filename,
+    NetAddress,
+    NetDatagram,
+    PointerToConnection,
+    QueuedConnectionListener,
+    QueuedConnectionManager,
+    QueuedConnectionReader,
+    TPLow,
+    UniqueIdAllocator,
+    VirtualFileSystem,
+    getModelPath,
+)
+from panda3d.direct import DCFile
+from direct.distributed.MsgTypesCMU import (
+    CLIENT_DISCONNECT_CMU,
+    CLIENT_OBJECT_GENERATE_CMU,
+    CLIENT_OBJECT_UPDATE_FIELD,
+    CLIENT_OBJECT_UPDATE_FIELD_TARGETED_CMU,
+    CLIENT_SET_INTEREST_CMU,
+    OBJECT_DELETE_CMU,
+    OBJECT_DISABLE_CMU,
+    OBJECT_GENERATE_CMU,
+    OBJECT_SET_ZONE_CMU,
+    OBJECT_UPDATE_FIELD_CMU,
+    REQUEST_GENERATES_CMU,
+    SET_DOID_RANGE_CMU,
+)
 from direct.task import Task
 from direct.task.TaskManagerGlobal import taskMgr
 from direct.directnotify import DirectNotifyGlobal

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

@@ -1,10 +1,10 @@
-from panda3d.core import *
-from direct.showbase.DirectObject import *
+from panda3d.core import ClockObject, ConfigVariableDouble, ConfigVariableInt
 from direct.task import Task
 from direct.task.TaskManagerGlobal import taskMgr
 from direct.distributed import DistributedObject
 from direct.directnotify import DirectNotifyGlobal
 from direct.distributed.ClockDelta import globalClockDelta
+from direct.showbase.MessengerGlobal import messenger
 
 
 class TimeManager(DistributedObject.DistributedObject):

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

@@ -1,5 +1,5 @@
-from panda3d.core import *
-from direct.distributed.ClockDelta import *
+from direct.directnotify import DirectNotifyGlobal
+from direct.distributed.ClockDelta import globalClockDelta
 from direct.distributed import DistributedObjectAI
 
 class TimeManagerAI(DistributedObjectAI.DistributedObjectAI):

+ 58 - 58
direct/src/extensions_native/CInterval_extensions.py

@@ -61,66 +61,66 @@ del privPostEvent
 #####################################################################
 
 def popupControls(self, tl = None):
-        """
-        Popup control panel for interval.
-        """
-        import math
-        # Don't use a regular import, to prevent ModuleFinder from picking
-        # it up as a dependency when building a .p3d package.
-        import importlib
-        EntryScale = importlib.import_module('direct.tkwidgets.EntryScale')
-        tkinter = importlib.import_module('tkinter')
+    """
+    Popup control panel for interval.
+    """
+    import math
+    # Don't use a regular import, to prevent ModuleFinder from picking
+    # it up as a dependency when building a .p3d package.
+    import importlib
+    EntryScale = importlib.import_module('direct.tkwidgets.EntryScale')
+    tkinter = importlib.import_module('tkinter')
 
-        if tl is None:
-            tl = tkinter.Toplevel()
-            tl.title('Interval Controls')
-        outerFrame = tkinter.Frame(tl)
-        def entryScaleCommand(t, s=self):
-            s.setT(t)
-            s.pause()
-        self.es = es = EntryScale.EntryScale(
-            outerFrame, text = self.getName(),
-            min = 0, max = math.floor(self.getDuration() * 100) / 100,
-            command = entryScaleCommand)
-        es.set(self.getT(), fCommand = 0)
-        es.pack(expand = 1, fill = tkinter.X)
-        bf = tkinter.Frame(outerFrame)
-        # Jump to start and end
-        def toStart(s=self, es=es):
-            s.setT(0.0)
-            s.pause()
-        def toEnd(s=self):
-            s.setT(s.getDuration())
-            s.pause()
-        jumpToStart = tkinter.Button(bf, text = '<<', command = toStart)
-        # Stop/play buttons
-        def doPlay(s=self, es=es):
-            s.resume(es.get())
+    if tl is None:
+        tl = tkinter.Toplevel()
+        tl.title('Interval Controls')
+    outerFrame = tkinter.Frame(tl)
+    def entryScaleCommand(t, s=self):
+        s.setT(t)
+        s.pause()
+    self.es = es = EntryScale.EntryScale(
+        outerFrame, text = self.getName(),
+        min = 0, max = math.floor(self.getDuration() * 100) / 100,
+        command = entryScaleCommand)
+    es.set(self.getT(), fCommand = 0)
+    es.pack(expand = 1, fill = tkinter.X)
+    bf = tkinter.Frame(outerFrame)
+    # Jump to start and end
+    def toStart(s=self, es=es):
+        s.setT(0.0)
+        s.pause()
+    def toEnd(s=self):
+        s.setT(s.getDuration())
+        s.pause()
+    jumpToStart = tkinter.Button(bf, text = '<<', command = toStart)
+    # Stop/play buttons
+    def doPlay(s=self, es=es):
+        s.resume(es.get())
 
-        stop = tkinter.Button(bf, text = 'Stop',
-                      command = lambda s=self: s.pause())
-        play = tkinter.Button(
-            bf, text = 'Play',
-            command = doPlay)
-        jumpToEnd = tkinter.Button(bf, text = '>>', command = toEnd)
-        jumpToStart.pack(side = tkinter.LEFT, expand = 1, fill = tkinter.X)
-        play.pack(side = tkinter.LEFT, expand = 1, fill = tkinter.X)
-        stop.pack(side = tkinter.LEFT, expand = 1, fill = tkinter.X)
-        jumpToEnd.pack(side = tkinter.LEFT, expand = 1, fill = tkinter.X)
-        bf.pack(expand = 1, fill = tkinter.X)
-        outerFrame.pack(expand = 1, fill = tkinter.X)
-        # Add function to update slider during setT calls
-        def update(t, es=es):
-            es.set(t, fCommand = 0)
-        if not hasattr(self, "setTHooks"):
-            self.setTHooks = []
-        self.setTHooks.append(update)
-        self.setWantsTCallback(1)
-        # Clear out function on destroy
-        def onDestroy(e, s=self, u=update):
-            if u in s.setTHooks:
-                s.setTHooks.remove(u)
-        tl.bind('<Destroy>', onDestroy)
+    stop = tkinter.Button(bf, text = 'Stop',
+                  command = lambda s=self: s.pause())
+    play = tkinter.Button(
+        bf, text = 'Play',
+        command = doPlay)
+    jumpToEnd = tkinter.Button(bf, text = '>>', command = toEnd)
+    jumpToStart.pack(side = tkinter.LEFT, expand = 1, fill = tkinter.X)
+    play.pack(side = tkinter.LEFT, expand = 1, fill = tkinter.X)
+    stop.pack(side = tkinter.LEFT, expand = 1, fill = tkinter.X)
+    jumpToEnd.pack(side = tkinter.LEFT, expand = 1, fill = tkinter.X)
+    bf.pack(expand = 1, fill = tkinter.X)
+    outerFrame.pack(expand = 1, fill = tkinter.X)
+    # Add function to update slider during setT calls
+    def update(t, es=es):
+        es.set(t, fCommand = 0)
+    if not hasattr(self, "setTHooks"):
+        self.setTHooks = []
+    self.setTHooks.append(update)
+    self.setWantsTCallback(1)
+    # Clear out function on destroy
+    def onDestroy(e, s=self, u=update):
+        if u in s.setTHooks:
+            s.setTHooks.remove(u)
+    tl.bind('<Destroy>', onDestroy)
 
 Dtool_funcToMethod(popupControls, CInterval)
 del popupControls

+ 21 - 21
direct/src/extensions_native/HTTPChannel_extensions.py

@@ -8,22 +8,22 @@ from .extension_native_helpers import Dtool_funcToMethod
 
 
 def spawnTask(self, name = None, callback = None, extraArgs = []):
-        """Spawns a task to service the download recently requested
-        via beginGetDocument(), etc., and/or downloadToFile() or
-        downloadToRam().  If a callback is specified, that function is
-        called when the download is complete, passing in the extraArgs
-        given.
+    """Spawns a task to service the download recently requested
+    via beginGetDocument(), etc., and/or downloadToFile() or
+    downloadToRam().  If a callback is specified, that function is
+    called when the download is complete, passing in the extraArgs
+    given.
 
-        Returns the newly-spawned task.
-        """
-        if not name:
-            name = str(self.getUrl())
-        from direct.task import Task
-        from direct.task.TaskManagerGlobal import taskMgr
-        task = Task.Task(self.doTask)
-        task.callback = callback
-        task.callbackArgs = extraArgs
-        return taskMgr.add(task, name)
+    Returns the newly-spawned task.
+    """
+    if not name:
+        name = str(self.getUrl())
+    from direct.task import Task
+    from direct.task.TaskManagerGlobal import taskMgr
+    task = Task.Task(self.doTask)
+    task.callback = callback
+    task.callbackArgs = extraArgs
+    return taskMgr.add(task, name)
 
 if hasattr(core, 'HTTPChannel'):
     Dtool_funcToMethod(spawnTask, core.HTTPChannel)
@@ -31,12 +31,12 @@ del spawnTask
 #####################################################################
 
 def doTask(self, task):
-        from direct.task import Task
-        if self.run():
-            return Task.cont
-        if task.callback:
-            task.callback(*task.callbackArgs)
-        return Task.done
+    from direct.task import Task
+    if self.run():
+        return Task.cont
+    if task.callback:
+        task.callback(*task.callbackArgs)
+    return Task.done
 
 if hasattr(core, 'HTTPChannel'):
     Dtool_funcToMethod(doTask, core.HTTPChannel)

+ 147 - 146
direct/src/extensions_native/NodePath_extensions.py

@@ -116,6 +116,7 @@ def remove(self):
         warnings.warn("NodePath.remove() is deprecated.  Use remove_node() instead.", DeprecationWarning, stacklevel=2)
     # Send message in case anyone needs to do something
     # before node is deleted
+    from direct.showbase.MessengerGlobal import messenger
     messenger.send('preRemoveNodePath', [self])
     # Remove nodePath
     self.removeNode()
@@ -493,7 +494,7 @@ def showCS(self, mask = None):
     npc = self.findAllMatches('**/+CollisionNode')
     for p in range(0, npc.getNumPaths()):
         np = npc[p]
-        if (mask == None or (np.node().getIntoCollideMask() & mask).getWord()):
+        if mask is None or (np.node().getIntoCollideMask() & mask).getWord():
             np.show()
 
 Dtool_funcToMethod(showCS, NodePath)
@@ -512,7 +513,7 @@ def hideCS(self, mask = None):
     npc = self.findAllMatches('**/+CollisionNode')
     for p in range(0, npc.getNumPaths()):
         np = npc[p]
-        if (mask == None or (np.node().getIntoCollideMask() & mask).getWord()):
+        if mask is None or (np.node().getIntoCollideMask() & mask).getWord():
             np.hide()
 
 Dtool_funcToMethod(hideCS, NodePath)
@@ -667,40 +668,40 @@ del attachCollisionRay
 #####################################################################
 def flattenMultitex(self, stateFrom = None, target = None,
                         useGeom = 0, allowTexMat = 0, win = None):
-        from panda3d.core import MultitexReducer
-        mr = MultitexReducer()
-        if target is not None:
-            mr.setTarget(target)
-        mr.setUseGeom(useGeom)
-        mr.setAllowTexMat(allowTexMat)
-
-        if win is None:
-            win = base.win
-
-        if stateFrom is None:
-            mr.scan(self)
-        else:
-            mr.scan(self, stateFrom)
-        mr.flatten(win)
+    from panda3d.core import MultitexReducer
+    mr = MultitexReducer()
+    if target is not None:
+        mr.setTarget(target)
+    mr.setUseGeom(useGeom)
+    mr.setAllowTexMat(allowTexMat)
+
+    if win is None:
+        win = base.win
+
+    if stateFrom is None:
+        mr.scan(self)
+    else:
+        mr.scan(self, stateFrom)
+    mr.flatten(win)
 Dtool_funcToMethod(flattenMultitex, NodePath)
 del flattenMultitex
 #####################################################################
 def getNumDescendants(self):
-        return len(self.findAllMatches('**')) - 1
+    return len(self.findAllMatches('**')) - 1
 Dtool_funcToMethod(getNumDescendants, NodePath)
 del getNumDescendants
 #####################################################################
 def removeNonCollisions(self):
-        # remove anything that is not collision-related
-        print("NodePath.removeNonCollisions() is deprecated")
-        stack = [self]
-        while len(stack) > 0:
-                np = stack.pop()
-                # if there are no CollisionNodes under this node, remove it
-                if np.find('**/+CollisionNode').isEmpty():
-                        np.detachNode()
-                else:
-                        stack.extend(np.getChildren())
+    # remove anything that is not collision-related
+    print("NodePath.removeNonCollisions() is deprecated")
+    stack = [self]
+    while len(stack) > 0:
+        np = stack.pop()
+        # if there are no CollisionNodes under this node, remove it
+        if np.find('**/+CollisionNode').isEmpty():
+            np.detachNode()
+        else:
+            stack.extend(np.getChildren())
 Dtool_funcToMethod(removeNonCollisions, NodePath)
 del removeNonCollisions
 #####################################################################
@@ -729,130 +730,130 @@ def subdivideCollisions(self, numSolidsInLeaves):
         colNp.stash()
 
 def r_subdivideCollisions(self, solids, numSolidsInLeaves):
-        # takes a list of solids, returns a list containing some number of lists,
-        # with the solids evenly distributed between them (recursively nested until
-        # the lists at the leaves contain no more than numSolidsInLeaves)
-        # if solids is already small enough, returns solids unchanged
-        if len(solids) <= numSolidsInLeaves:
-            return solids
-        origins = []
-        avgX = 0
-        avgY = 0
-        avgZ = 0
-        minX = None
-        minY = None
-        minZ = None
-        maxX = None
-        maxY = None
-        maxZ = None
-        for solid in solids:
-            origin = solid.getCollisionOrigin()
-            origins.append(origin)
-            x = origin.getX()
-            y = origin.getY()
-            z = origin.getZ()
-            avgX += x
-            avgY += y
-            avgZ += z
-            if minX is None:
-                minX = x
-                minY = y
-                minZ = z
-                maxX = x
-                maxY = y
-                maxZ = z
+    # takes a list of solids, returns a list containing some number of lists,
+    # with the solids evenly distributed between them (recursively nested until
+    # the lists at the leaves contain no more than numSolidsInLeaves)
+    # if solids is already small enough, returns solids unchanged
+    if len(solids) <= numSolidsInLeaves:
+        return solids
+    origins = []
+    avgX = 0
+    avgY = 0
+    avgZ = 0
+    minX = None
+    minY = None
+    minZ = None
+    maxX = None
+    maxY = None
+    maxZ = None
+    for solid in solids:
+        origin = solid.getCollisionOrigin()
+        origins.append(origin)
+        x = origin.getX()
+        y = origin.getY()
+        z = origin.getZ()
+        avgX += x
+        avgY += y
+        avgZ += z
+        if minX is None:
+            minX = x
+            minY = y
+            minZ = z
+            maxX = x
+            maxY = y
+            maxZ = z
+        else:
+            minX = min(x, minX)
+            minY = min(y, minY)
+            minZ = min(z, minZ)
+            maxX = max(x, maxX)
+            maxY = max(y, maxY)
+            maxZ = max(z, maxZ)
+    avgX /= len(solids)
+    avgY /= len(solids)
+    avgZ /= len(solids)
+    extentX = maxX - minX
+    extentY = maxY - minY
+    extentZ = maxZ - minZ
+    maxExtent = max(extentX, extentY, extentZ)
+    # sparse octree
+    xyzSolids = []
+    XyzSolids = []
+    xYzSolids = []
+    XYzSolids = []
+    xyZSolids = []
+    XyZSolids = []
+    xYZSolids = []
+    XYZSolids = []
+    midX = avgX
+    midY = avgY
+    midZ = avgZ
+    # throw out axes that are not close to the max axis extent; try and keep
+    # the divisions square/spherical
+    if extentX < (maxExtent * .75) or extentX > (maxExtent * 1.25):
+        midX += maxExtent
+    if extentY < (maxExtent * .75) or extentY > (maxExtent * 1.25):
+        midY += maxExtent
+    if extentZ < (maxExtent * .75) or extentZ > (maxExtent * 1.25):
+        midZ += maxExtent
+    for i, solid in enumerate(solids):
+        origin = origins[i]
+        x = origin.getX()
+        y = origin.getY()
+        z = origin.getZ()
+        if x < midX:
+            if y < midY:
+                if z < midZ:
+                    xyzSolids.append(solids[i])
+                else:
+                    xyZSolids.append(solids[i])
             else:
-                minX = min(x, minX)
-                minY = min(y, minY)
-                minZ = min(z, minZ)
-                maxX = max(x, maxX)
-                maxY = max(y, maxY)
-                maxZ = max(z, maxZ)
-        avgX /= len(solids)
-        avgY /= len(solids)
-        avgZ /= len(solids)
-        extentX = maxX - minX
-        extentY = maxY - minY
-        extentZ = maxZ - minZ
-        maxExtent = max(max(extentX, extentY), extentZ)
-        # sparse octree
-        xyzSolids = []
-        XyzSolids = []
-        xYzSolids = []
-        XYzSolids = []
-        xyZSolids = []
-        XyZSolids = []
-        xYZSolids = []
-        XYZSolids = []
-        midX = avgX
-        midY = avgY
-        midZ = avgZ
-        # throw out axes that are not close to the max axis extent; try and keep
-        # the divisions square/spherical
-        if extentX < (maxExtent * .75) or extentX > (maxExtent * 1.25):
-            midX += maxExtent
-        if extentY < (maxExtent * .75) or extentY > (maxExtent * 1.25):
-            midY += maxExtent
-        if extentZ < (maxExtent * .75) or extentZ > (maxExtent * 1.25):
-            midZ += maxExtent
-        for i, solid in enumerate(solids):
-            origin = origins[i]
-            x = origin.getX()
-            y = origin.getY()
-            z = origin.getZ()
-            if x < midX:
-                if y < midY:
-                    if z < midZ:
-                        xyzSolids.append(solids[i])
-                    else:
-                        xyZSolids.append(solids[i])
+                if z < midZ:
+                    xYzSolids.append(solids[i])
                 else:
-                    if z < midZ:
-                        xYzSolids.append(solids[i])
-                    else:
-                        xYZSolids.append(solids[i])
+                    xYZSolids.append(solids[i])
+        else:
+            if y < midY:
+                if z < midZ:
+                    XyzSolids.append(solids[i])
+                else:
+                    XyZSolids.append(solids[i])
             else:
-                if y < midY:
-                    if z < midZ:
-                        XyzSolids.append(solids[i])
-                    else:
-                        XyZSolids.append(solids[i])
+                if z < midZ:
+                    XYzSolids.append(solids[i])
                 else:
-                    if z < midZ:
-                        XYzSolids.append(solids[i])
-                    else:
-                        XYZSolids.append(solids[i])
-        newSolids = []
-        if len(xyzSolids) > 0:
-            newSolids.append(self.r_subdivideCollisions(xyzSolids, numSolidsInLeaves))
-        if len(XyzSolids) > 0:
-            newSolids.append(self.r_subdivideCollisions(XyzSolids, numSolidsInLeaves))
-        if len(xYzSolids) > 0:
-            newSolids.append(self.r_subdivideCollisions(xYzSolids, numSolidsInLeaves))
-        if len(XYzSolids) > 0:
-            newSolids.append(self.r_subdivideCollisions(XYzSolids, numSolidsInLeaves))
-        if len(xyZSolids) > 0:
-            newSolids.append(self.r_subdivideCollisions(xyZSolids, numSolidsInLeaves))
-        if len(XyZSolids) > 0:
-            newSolids.append(self.r_subdivideCollisions(XyZSolids, numSolidsInLeaves))
-        if len(xYZSolids) > 0:
-            newSolids.append(self.r_subdivideCollisions(xYZSolids, numSolidsInLeaves))
-        if len(XYZSolids) > 0:
-            newSolids.append(self.r_subdivideCollisions(XYZSolids, numSolidsInLeaves))
-        #import pdb;pdb.set_trace()
-        return newSolids
+                    XYZSolids.append(solids[i])
+    newSolids = []
+    if len(xyzSolids) > 0:
+        newSolids.append(self.r_subdivideCollisions(xyzSolids, numSolidsInLeaves))
+    if len(XyzSolids) > 0:
+        newSolids.append(self.r_subdivideCollisions(XyzSolids, numSolidsInLeaves))
+    if len(xYzSolids) > 0:
+        newSolids.append(self.r_subdivideCollisions(xYzSolids, numSolidsInLeaves))
+    if len(XYzSolids) > 0:
+        newSolids.append(self.r_subdivideCollisions(XYzSolids, numSolidsInLeaves))
+    if len(xyZSolids) > 0:
+        newSolids.append(self.r_subdivideCollisions(xyZSolids, numSolidsInLeaves))
+    if len(XyZSolids) > 0:
+        newSolids.append(self.r_subdivideCollisions(XyZSolids, numSolidsInLeaves))
+    if len(xYZSolids) > 0:
+        newSolids.append(self.r_subdivideCollisions(xYZSolids, numSolidsInLeaves))
+    if len(XYZSolids) > 0:
+        newSolids.append(self.r_subdivideCollisions(XYZSolids, numSolidsInLeaves))
+    #import pdb;pdb.set_trace()
+    return newSolids
 
 def r_constructCollisionTree(self, solidTree, parentNode, colName):
-        from panda3d.core import CollisionNode
-        for item in solidTree:
-            if isinstance(item[0], list):
-                newNode = parentNode.attachNewNode('%s-branch' % colName)
-                self.r_constructCollisionTree(item, newNode, colName)
-            else:
-                cn = CollisionNode('%s-leaf' % colName)
-                for solid in item:
-                    cn.addSolid(solid)
-                parentNode.attachNewNode(cn)
+    from panda3d.core import CollisionNode
+    for item in solidTree:
+        if isinstance(item[0], list):
+            newNode = parentNode.attachNewNode(f'{colName}-branch')
+            self.r_constructCollisionTree(item, newNode, colName)
+        else:
+            cn = CollisionNode(f'{colName}-leaf')
+            for solid in item:
+                cn.addSolid(solid)
+            parentNode.attachNewNode(cn)
 
 Dtool_funcToMethod(subdivideCollisions, NodePath)
 Dtool_funcToMethod(r_subdivideCollisions, NodePath)

+ 1 - 1
direct/src/filter/CommonFilters.py

@@ -351,7 +351,7 @@ class CommonFilters:
             text += "{\n"
             text += "  o_color = tex2D(k_txcolor, %s);\n" % (texcoords["color"])
             if "CartoonInk" in configuration:
-                text += CARTOON_BODY % {"texcoord" : texcoords["aux"]}
+                text += CARTOON_BODY % {"texcoord": texcoords["aux"]}
             if "AmbientOcclusion" in configuration:
                 text += "  o_color *= tex2D(k_txssao2, %s).r;\n" % (texcoords["ssao2"])
             if "BlurSharpen" in configuration:

+ 3 - 3
direct/src/filter/FilterManager.py

@@ -25,7 +25,7 @@ from panda3d.core import Camera
 from panda3d.core import OrthographicLens
 from panda3d.core import AuxBitplaneAttrib
 from panda3d.core import LightRampAttrib
-from direct.directnotify.DirectNotifyGlobal import *
+from direct.directnotify.DirectNotifyGlobal import directNotify
 from direct.showbase.DirectObject import DirectObject
 
 __all__ = ["FilterManager"]
@@ -348,10 +348,10 @@ class FilterManager(DirectObject):
 
     def resizeBuffers(self):
         """ Resize all buffers to match the size of the window. """
-        for i in range(len(self.buffers)):
+        for i, buffer in enumerate(self.buffers):
             (mul, div, align) = self.sizes[i]
             (xsize, ysize) = self.getScaledSize(mul, div, align)
-            self.buffers[i].setSize(xsize, ysize)
+            buffer.setSize(xsize, ysize)
 
     def cleanup(self):
         """ Restore everything to its original state, deleting any

+ 4 - 8
direct/src/fsm/ClassicFSM.py

@@ -16,7 +16,6 @@ if __debug__:
     _debugFsms = {}
 
     def printDebugFsmList():
-        global _debugFsms
         for k in sorted(_debugFsms.keys()):
             print("%s %s" % (k, _debugFsms[k]()))
     __builtins__['debugFsmList'] = printDebugFsmList
@@ -83,7 +82,6 @@ class ClassicFSM(DirectObject):
         # doing this.
         self.__internalStateInFlux = 0
         if __debug__:
-            global _debugFsms
             _debugFsms[name] = weakref.ref(self)
 
     # I know this isn't how __repr__ is supposed to be used, but it
@@ -95,12 +93,12 @@ class ClassicFSM(DirectObject):
         """
         Print out something useful about the fsm
         """
+        name = self.getName()
         currentState = self.getCurrentState()
         if currentState:
-            str = ("ClassicFSM " + self.getName() + ' in state "' +
-                   currentState.getName() + '"')
+            str = f'ClassicFSM {name} in state "{currentState.getName()}"'
         else:
-            str = ("ClassicFSM " + self.getName() + ' not in any state')
+            str = f'ClassicFSM {name} not in any state'
         return str
 
     def enterInitialState(self, argList=[]):
@@ -152,7 +150,6 @@ class ClassicFSM(DirectObject):
     def getCurrentState(self):
         return self.__currentState
 
-
     # lookup funcs
 
     def getStateNamed(self, stateName):
@@ -317,7 +314,6 @@ class ClassicFSM(DirectObject):
                 ClassicFSM.notify.warning(msg)
             return 0
 
-
     def forceTransition(self, aStateName, enterArgList=[], exitArgList=[]):
         """
         force a transition -- for debugging ONLY
@@ -357,7 +353,7 @@ class ClassicFSM(DirectObject):
             self.__currentState.isTransitionDefined(aStateName) or
             aStateName in [self.__currentState.getName(),
                            self.__finalState.getName()]
-            )
+        )
 
         if transitionDefined:
             return self.request(aStateName, enterArgList, exitArgList)

+ 2 - 3
direct/src/fsm/FSM.py

@@ -615,9 +615,8 @@ class FSM(DirectObject):
         try:
             className = self.__class__.__name__
             if self.state:
-                str = ('%s FSM:%s in state "%s"' % (className, self._name, self.state))
+                return f'{className} FSM:{self._name} in state "{self.state}"'
             else:
-                str = ('%s FSM:%s in transition from \'%s\' to \'%s\'' % (className, self._name, self.oldState, self.newState))
-            return str
+                return f'{className} FSM:{self._name} in transition from \'{self.oldState}\' to \'{self.newState}\''
         finally:
             self.fsmLock.release()

+ 1 - 1
direct/src/fsm/FourState.py

@@ -115,7 +115,7 @@ class FourState:
                            self.enterState4,
                            self.exitState4,
                            [names[1]]),
-            }
+        }
         self.fsm = ClassicFSM.ClassicFSM('FourState',
                            list(self.states.values()),
                            # Initial State

+ 4 - 1
direct/src/fsm/FourStateAI.py

@@ -123,7 +123,7 @@ class FourStateAI:
                            self.enterState4,
                            self.exitState4,
                            [names[1]]),
-            }
+        }
         self.fsm = ClassicFSM.ClassicFSM('FourState',
                            list(self.states.values()),
                            # Initial State
@@ -145,6 +145,9 @@ class FourStateAI:
         assert self.__debugPrint("getState() returning %s"%(self.stateIndex,))
         return [self.stateIndex]
 
+    def sendUpdate(self, fieldName, args = [], sendToId = None):
+        raise NotImplementedError
+
     def sendState(self):
         assert self.__debugPrint("sendState()")
         self.sendUpdate('setState', self.getState())

+ 1 - 1
direct/src/fsm/SampleFSM.py

@@ -16,7 +16,7 @@ class ClassicStyle(FSM.FSM):
             'Red': ['Green'],
             'Yellow': ['Red'],
             'Green': ['Yellow'],
-            }
+        }
 
     def enterRed(self):
         print("enterRed(self, '%s', '%s')" % (self.oldState, self.newState))

+ 12 - 12
direct/src/fsm/StatePush.py

@@ -251,12 +251,12 @@ class FunctionCall(ReceivesMultipleStateChanges, PushesStateChanges):
 
 if __debug__:
     l = []
-    def handler(value, l=l):
+    def handler1(value, l=l):
         l.append(value)
-    assert l == []
+    assert not l
     sv = StateVar(0)
-    fc = FunctionCall(handler, sv)
-    assert l == []
+    fc = FunctionCall(handler1, sv)
+    assert not l
     fc.pushCurrentState()
     assert l == [0,]
     sv.set(1)
@@ -267,17 +267,17 @@ if __debug__:
     sv.destroy()
     del fc
     del sv
-    del handler
+    del handler1
     del l
 
     l = []
-    def handler(value, kDummy=None, kValue=None, l=l):
+    def handler2(value, kDummy=None, kValue=None, l=l):
         l.append((value, kValue))
-    assert l == []
+    assert not l
     sv = StateVar(0)
     ksv = StateVar('a')
-    fc = FunctionCall(handler, sv, kValue=ksv)
-    assert l == []
+    fc = FunctionCall(handler2, sv, kValue=ksv)
+    assert not l
     fc.pushCurrentState()
     assert l == [(0,'a',),]
     sv.set(1)
@@ -288,7 +288,7 @@ if __debug__:
     sv.destroy()
     del fc
     del sv
-    del handler
+    del handler2
     del l
 
 class EnterExit(StateChangeNode):
@@ -323,7 +323,7 @@ if __debug__:
     sv = StateVar(0)
     ee = EnterExit(sv, enter, exit)
     sv.set(0)
-    assert l == []
+    assert not l
     sv.set(1)
     assert l == [1,]
     sv.set(2)
@@ -357,7 +357,7 @@ if __debug__:
         l.append(value)
     p = Pulse()
     fc = FunctionCall(handler, p)
-    assert l == []
+    assert not l
     fc.pushCurrentState()
     assert l == [False, ]
     p.sendPulse()

+ 5 - 3
direct/src/gui/DirectButton.py

@@ -6,15 +6,17 @@ in-depth explanation and an example of how to use this class.
 
 __all__ = ['DirectButton']
 
-from panda3d.core import *
+from panda3d.core import Mat4, MouseButton, PGButton
 from . import DirectGuiGlobals as DGG
-from .DirectFrame import *
+from .DirectFrame import DirectFrame
+
 
 class DirectButton(DirectFrame):
     """
     DirectButton(parent) - Create a DirectGuiWidget which responds
     to mouse clicks and execute a callback function if defined
     """
+
     def __init__(self, parent = None, **kw):
         # Inherits from DirectFrame
         # A Direct Frame can have:
@@ -45,7 +47,7 @@ class DirectButton(DirectFrame):
             # Can only be specified at time of widget contruction
             # Do the text/graphics appear to move when the button is clicked
             ('pressEffect',     1,         DGG.INITOPT),
-            )
+        )
         # Merge keyword options with default options
         self.defineoptions(kw, optiondefs)
 

+ 4 - 4
direct/src/gui/DirectCheckBox.py

@@ -1,5 +1,5 @@
-from direct.gui.DirectGui import *
-from panda3d.core import *
+from direct.gui.DirectGui import DGG, DirectButton
+from panda3d.core import PGButton
 
 
 class DirectCheckBox(DirectButton):
@@ -10,6 +10,7 @@ class DirectCheckBox(DirectButton):
 
     Uses an image swap rather than a text change to indicate state.
     """
+
     def __init__(self, parent = None, **kw):
 
         optiondefs = (
@@ -33,7 +34,7 @@ class DirectCheckBox(DirectButton):
             ('uncheckedImage',  None,      None),
             ('checkedImage',    None,      None),
             ('isChecked',       False,     None),
-            )
+        )
 
         # Merge keyword options with default options
         self.defineoptions(kw, optiondefs)
@@ -42,7 +43,6 @@ class DirectCheckBox(DirectButton):
 
         self.initialiseoptions(DirectCheckBox)
 
-
     def commandFunc(self, event):
         self['isChecked'] = not self['isChecked']
 

+ 6 - 5
direct/src/gui/DirectCheckButton.py

@@ -8,9 +8,10 @@ in-depth explanation and an example of how to use this class.
 
 __all__ = ['DirectCheckButton']
 
-from panda3d.core import *
-from .DirectButton import *
-from .DirectLabel import *
+from panda3d.core import PGFrameStyle, VBase4
+from .DirectButton import DirectButton
+from .DirectLabel import DirectLabel
+
 
 class DirectCheckButton(DirectButton):
     """
@@ -18,6 +19,7 @@ class DirectCheckButton(DirectButton):
     to mouse clicks by setting a state of on or off and execute a callback
     function (passing that state through) if defined
     """
+
     def __init__(self, parent = None, **kw):
         # Inherits from DirectButton
         # A Direct Frame can have:
@@ -42,7 +44,7 @@ class DirectCheckButton(DirectButton):
             ('boxImageScale', 1, None),
             ('boxImageColor', None, None),
             ('boxRelief', 'sunken', None),
-            )
+        )
         # Merge keyword options with default options
         self.defineoptions(kw, optiondefs)
         # Initialize superclasses
@@ -167,7 +169,6 @@ class DirectCheckButton(DirectButton):
 
             self.indicator.setPos(newpos[0], newpos[1], newpos[2])
 
-
     def commandFunc(self, event):
         self['indicatorValue'] = 1 - self['indicatorValue']
         if self.colors is not None:

+ 15 - 10
direct/src/gui/DirectDialog.py

@@ -9,11 +9,11 @@ __all__ = [
     'OkCancelDialog', 'YesNoDialog', 'YesNoCancelDialog', 'RetryCancelDialog',
 ]
 
-from panda3d.core import *
+from panda3d.core import NodePath, Point3, TextNode, VBase3
 from direct.showbase import ShowBaseGlobal
 from . import DirectGuiGlobals as DGG
-from .DirectFrame import *
-from .DirectButton import *
+from .DirectFrame import DirectFrame
+from .DirectButton import DirectButton
 
 
 def findDialog(uniqueName):
@@ -120,7 +120,7 @@ class DirectDialog(DirectFrame):
             ('command',           None,          None),
             ('extraArgs',         [],            None),
             ('sortOrder',    DGG.NO_FADE_SORT_INDEX, None),
-            )
+        )
         # Merge keyword options with default options
         self.defineoptions(kw, optiondefs, dynamicGroups = ("button",))
 
@@ -178,7 +178,7 @@ class DirectDialog(DirectFrame):
                 suppressKeys = self['suppressKeys'],
                 frameSize = self['buttonSize'],
                 command = lambda s = self, v = value: s.buttonCommand(v)
-                )
+            )
             self.buttonList.append(button)
 
         # Update dialog when everything has been initialised
@@ -356,6 +356,7 @@ class DirectDialog(DirectFrame):
             button.destroy()
         DirectFrame.destroy(self)
 
+
 class OkDialog(DirectDialog):
     def __init__(self, parent = None, **kw):
         # Inherits from DirectFrame
@@ -363,12 +364,13 @@ class OkDialog(DirectDialog):
             # Define type of DirectGuiWidget
             ('buttonTextList',  ['OK'],       DGG.INITOPT),
             ('buttonValueList', [DGG.DIALOG_OK],          DGG.INITOPT),
-            )
+        )
         # Merge keyword options with default options
         self.defineoptions(kw, optiondefs)
         DirectDialog.__init__(self, parent)
         self.initialiseoptions(OkDialog)
 
+
 class OkCancelDialog(DirectDialog):
     def __init__(self, parent = None, **kw):
         # Inherits from DirectFrame
@@ -376,12 +378,13 @@ class OkCancelDialog(DirectDialog):
             # Define type of DirectGuiWidget
             ('buttonTextList',  ['OK','Cancel'],       DGG.INITOPT),
             ('buttonValueList', [DGG.DIALOG_OK, DGG.DIALOG_CANCEL], DGG.INITOPT),
-            )
+        )
         # Merge keyword options with default options
         self.defineoptions(kw, optiondefs)
         DirectDialog.__init__(self, parent)
         self.initialiseoptions(OkCancelDialog)
 
+
 class YesNoDialog(DirectDialog):
     def __init__(self, parent = None, **kw):
         # Inherits from DirectFrame
@@ -389,12 +392,13 @@ class YesNoDialog(DirectDialog):
             # Define type of DirectGuiWidget
             ('buttonTextList',  ['Yes', 'No'],       DGG.INITOPT),
             ('buttonValueList', [DGG.DIALOG_YES, DGG.DIALOG_NO], DGG.INITOPT),
-            )
+        )
         # Merge keyword options with default options
         self.defineoptions(kw, optiondefs)
         DirectDialog.__init__(self, parent)
         self.initialiseoptions(YesNoDialog)
 
+
 class YesNoCancelDialog(DirectDialog):
     def __init__(self, parent = None, **kw):
         # Inherits from DirectFrame
@@ -403,12 +407,13 @@ class YesNoCancelDialog(DirectDialog):
             ('buttonTextList',  ['Yes', 'No', 'Cancel'],  DGG.INITOPT),
             ('buttonValueList', [DGG.DIALOG_YES, DGG.DIALOG_NO, DGG.DIALOG_CANCEL],
              DGG.INITOPT),
-            )
+        )
         # Merge keyword options with default options
         self.defineoptions(kw, optiondefs)
         DirectDialog.__init__(self, parent)
         self.initialiseoptions(YesNoCancelDialog)
 
+
 class RetryCancelDialog(DirectDialog):
     def __init__(self, parent = None, **kw):
         # Inherits from DirectFrame
@@ -416,7 +421,7 @@ class RetryCancelDialog(DirectDialog):
             # Define type of DirectGuiWidget
             ('buttonTextList',  ['Retry','Cancel'],   DGG.INITOPT),
             ('buttonValueList', [DGG.DIALOG_RETRY, DGG.DIALOG_CANCEL], DGG.INITOPT),
-            )
+        )
         # Merge keyword options with default options
         self.defineoptions(kw, optiondefs)
         DirectDialog.__init__(self, parent)

+ 10 - 8
direct/src/gui/DirectEntry.py

@@ -7,13 +7,13 @@ explanation and an example of how to use this class.
 
 __all__ = ['DirectEntry']
 
-from panda3d.core import *
+from panda3d.core import ConfigVariableBool, PGEntry, Point3, TextNode, Vec3
 from direct.showbase import ShowBaseGlobal
 from . import DirectGuiGlobals as DGG
-from .DirectFrame import *
+from .DirectFrame import DirectFrame
 from .OnscreenText import OnscreenText
 # import this to make sure it gets pulled into the publish
-import encodings.utf_8
+import encodings.utf_8 # pylint: disable=unused-import
 from direct.showbase.DirectObject import DirectObject
 
 # DirectEntry States:
@@ -21,6 +21,7 @@ ENTRY_FOCUS_STATE    = PGEntry.SFocus      # 0
 ENTRY_NO_FOCUS_STATE = PGEntry.SNoFocus    # 1
 ENTRY_INACTIVE_STATE = PGEntry.SInactive   # 2
 
+
 class DirectEntry(DirectFrame):
     """
     DirectEntry(parent) - Create a DirectGuiWidget which responds
@@ -82,7 +83,7 @@ class DirectEntry(DirectFrame):
             ('autoCapitalize',  0,                self.autoCapitalizeFunc),
             ('autoCapitalizeAllowPrefixes', DirectEntry.AllowCapNamePrefixes, None),
             ('autoCapitalizeForcePrefixes', DirectEntry.ForceCapNamePrefixes, None),
-            )
+        )
         # Merge keyword options with default options
         self.defineoptions(kw, optiondefs)
 
@@ -215,15 +216,16 @@ class DirectEntry(DirectFrame):
 
     def _handleTyping(self, guiEvent):
         self._autoCapitalize()
+
     def _handleErasing(self, guiEvent):
         self._autoCapitalize()
 
     def _autoCapitalize(self):
         name = self.guiItem.getWtext()
         # capitalize each word, allowing for things like McMutton
-        capName = u''
+        capName = ''
         # track each individual word to detect prefixes like Mc
-        wordSoFar = u''
+        wordSoFar = ''
         # track whether the previous character was part of a word or not
         wasNonWordChar = True
         for i, character in enumerate(name):
@@ -232,9 +234,9 @@ class DirectEntry(DirectFrame):
             #   This assumes that string.lower and string.upper will return different
             #   values for all unicode letters.
             # - Don't count apostrophes as a break between words
-            if character.lower() == character.upper() and character != u"'":
+            if character.lower() == character.upper() and character != "'":
                 # we are between words
-                wordSoFar = u''
+                wordSoFar = ''
                 wasNonWordChar = True
             else:
                 capitalize = False

+ 4 - 7
direct/src/gui/DirectEntryScroll.py

@@ -1,10 +1,9 @@
 __all__ = ['DirectEntryScroll']
 
-from panda3d.core import *
+from panda3d.core import NodePath, OmniBoundingVolume, PGVirtualFrame
 from . import DirectGuiGlobals as DGG
-from .DirectScrolledFrame import *
-from .DirectFrame import *
-from .DirectEntry import *
+from .DirectFrame import DirectFrame
+
 
 class DirectEntryScroll(DirectFrame):
     def __init__(self, entry, parent = None, **kw):
@@ -12,7 +11,7 @@ class DirectEntryScroll(DirectFrame):
             ('pgFunc',         PGVirtualFrame,      None),
             ('relief', None, None),
             ('clipSize',     (-1, 1, -1, 1),        self.setClipSize),
-            )
+        )
 
         self.defineoptions(kw, optiondefs)
         DirectFrame.__init__(self, parent, **kw)
@@ -81,7 +80,6 @@ class DirectEntryScroll(DirectFrame):
         if abs(distanceToCenter) > (clipExtent * 0.5):
             self.moveToCenterCursor()
 
-
     def moveToCenterCursor(self):
         cursorX = self.entry.guiItem.getCursorX() * self.entry['text_scale'][0]
         canvasX = self.canvas.getX()
@@ -122,7 +120,6 @@ class DirectEntryScroll(DirectFrame):
         self.entry = None
         DirectFrame.destroy(self)
 
-
     def getCanvas(self):
         return self.canvas
 

+ 2 - 2
direct/src/gui/DirectFrame.py

@@ -18,9 +18,9 @@ explanation and an example of how to use this class.
 
 __all__ = ['DirectFrame']
 
-from panda3d.core import *
+from panda3d.core import Filename, NodePath, PGItem, Texture
 from . import DirectGuiGlobals as DGG
-from .DirectGuiBase import *
+from .DirectGuiBase import DirectGuiWidget
 from .OnscreenImage import OnscreenImage
 from .OnscreenGeom import OnscreenGeom
 from .OnscreenText import OnscreenText

+ 15 - 7
direct/src/gui/DirectGuiBase.py

@@ -87,14 +87,21 @@ Code overview:
 __all__ = ['DirectGuiBase', 'DirectGuiWidget']
 
 
-from panda3d.core import *
+from panda3d.core import (
+    ConfigVariableBool,
+    KeyboardButton,
+    MouseWatcherRegion,
+    NodePath,
+    PGFrameStyle,
+    PGItem,
+    Point3,
+    Texture,
+    Vec3,
+)
 from direct.showbase import ShowBaseGlobal
 from direct.showbase.ShowBase import ShowBase
 from direct.showbase.MessengerGlobal import messenger
 from . import DirectGuiGlobals as DGG
-from .OnscreenText import *
-from .OnscreenGeom import *
-from .OnscreenImage import *
 from direct.directtools.DirectUtil import ROUND_TO
 from direct.showbase import DirectObject
 from direct.task import Task
@@ -652,9 +659,11 @@ class DirectGuiBase(DirectObject.DirectObject):
         gEvent = event + self.guiId
         self.ignore(gEvent)
 
+
 def toggleGuiGridSnap():
     DirectGuiWidget.snapToGrid = 1 - DirectGuiWidget.snapToGrid
 
+
 def setGuiGridSpacing(spacing):
     DirectGuiWidget.gridSpacing = spacing
 
@@ -715,7 +724,7 @@ class DirectGuiWidget(DirectGuiBase, NodePath):
             ('suppressMouse',  1,            DGG.INITOPT),
             ('suppressKeys',   0,            DGG.INITOPT),
             ('enableEdit',     1,            DGG.INITOPT),
-            )
+        )
         # Merge keyword options with default options
         self.defineoptions(kw, optiondefs)
 
@@ -822,7 +831,7 @@ class DirectGuiWidget(DirectGuiBase, NodePath):
         vMouse2render2d = Point3(event.getMouse()[0], 0, event.getMouse()[1])
         editVec = Vec3(vWidget2render2d - vMouse2render2d)
         if base.mouseWatcherNode.getModifierButtons().isDown(
-            KeyboardButton.control()):
+                KeyboardButton.control()):
             t = taskMgr.add(self.guiScaleTask, 'guiEditTask')
             t.refPos = vWidget2render2d
             t.editVecLen = editVec.length()
@@ -904,7 +913,6 @@ class DirectGuiWidget(DirectGuiBase, NodePath):
             self.bounds[2] - bw[1],
             self.bounds[3] + bw[1])
 
-
     def getBounds(self, state = 0):
         self.stateNodePath[state].calcTightBounds(self.ll, self.ur)
         # Scale bounds to give a pad around graphics

+ 9 - 3
direct/src/gui/DirectGuiGlobals.py

@@ -5,7 +5,15 @@ that can be used during widget construction
 
 __all__ = []
 
-from panda3d.core import *
+from panda3d.core import (
+    KeyboardButton,
+    MouseButton,
+    PGButton,
+    PGEntry,
+    PGFrameStyle,
+    PGSliderBar,
+    TextNode,
+)
 
 defaultFont = None
 defaultFontFunc = TextNode.getDefaultFont
@@ -138,11 +146,9 @@ def setDefaultFontFunc(newFontFunc):
     defaultFontFunc = newFontFunc
 
 def getDefaultDialogGeom():
-    global defaultDialogGeom
     return defaultDialogGeom
 
 def getDefaultDialogRelief():
-    global defaultDialogRelief
     return defaultDialogRelief
 
 def setDefaultDialogGeom(newDialogGeom, relief=None):

+ 3 - 2
direct/src/gui/DirectGuiTest.py

@@ -4,12 +4,13 @@ __all__ = []
 
 
 if __name__ == "__main__":
+    from panda3d.core import Point3
     from direct.showbase.ShowBase import ShowBase
     from direct.task.TaskManagerGlobal import taskMgr
     from . import DirectGuiGlobals
-    from .DirectGui import *
+    from .DirectGui import DirectButton, DirectDialog, DirectEntry, DirectFrame, YesNoDialog
     #from whrandom import *
-    from random import *
+    from random import random
 
     base = ShowBase()
 

+ 5 - 3
direct/src/gui/DirectLabel.py

@@ -6,14 +6,16 @@ explanation and an example of how to use this class.
 
 __all__ = ['DirectLabel']
 
-from panda3d.core import *
-from .DirectFrame import *
+from panda3d.core import PGItem
+from .DirectFrame import DirectFrame
+
 
 class DirectLabel(DirectFrame):
     """
     DirectLabel(parent) - Create a DirectGuiWidget which has multiple
     states.  User explicitly chooses a state to display
     """
+
     def __init__(self, parent = None, **kw):
         # Inherits from DirectFrame
         # A Direct Frame can have:
@@ -32,7 +34,7 @@ class DirectLabel(DirectFrame):
             ('numStates',       1,         None),
             ('state',           self.inactiveInitState, None),
             ('activeState',     0,         self.setActiveState),
-            )
+        )
         # Merge keyword options with default options
         self.defineoptions(kw, optiondefs)
 

+ 5 - 5
direct/src/gui/DirectOptionMenu.py

@@ -6,12 +6,11 @@ in-depth explanation and an example of how to use this class.
 
 __all__ = ['DirectOptionMenu']
 
-from panda3d.core import *
+from panda3d.core import OmniBoundingVolume, TextNode, VBase3
 from direct.showbase import ShowBaseGlobal
 from . import DirectGuiGlobals as DGG
-from .DirectButton import *
-from .DirectLabel import *
-from .DirectFrame import *
+from .DirectButton import DirectButton
+from .DirectFrame import DirectFrame
 
 
 class DirectOptionMenu(DirectButton):
@@ -22,6 +21,7 @@ class DirectOptionMenu(DirectButton):
     To cancel the popup menu click anywhere on the screen outside of the
     popup menu.  No command is executed in this case.
     """
+
     def __init__(self, parent = None, **kw):
         # Inherits from DirectButton
         optiondefs = (
@@ -43,7 +43,7 @@ class DirectOptionMenu(DirectButton):
             ('text_align',  TextNode.ALeft, None),
             # Remove press effect because it looks a bit funny
             ('pressEffect',     0,          DGG.INITOPT),
-           )
+        )
         # Merge keyword options with default options
         self.defineoptions(kw, optiondefs)
         # Initialize superclasses

+ 6 - 5
direct/src/gui/DirectRadioButton.py

@@ -9,10 +9,11 @@ in-depth explanation and an example of how to use this class.
 
 __all__ = ['DirectRadioButton']
 
-from panda3d.core import *
+from panda3d.core import PGFrameStyle, VBase4
 from . import DirectGuiGlobals as DGG
-from .DirectButton import *
-from .DirectLabel import *
+from .DirectButton import DirectButton
+from .DirectLabel import DirectLabel
+
 
 class DirectRadioButton(DirectButton):
     """
@@ -20,6 +21,7 @@ class DirectRadioButton(DirectButton):
     to mouse clicks by setting given value to given variable and
     execute a callback function (passing that state through) if defined
     """
+
     def __init__(self, parent = None, **kw):
         # Inherits from DirectButton
         # A Direct Frame can have:
@@ -54,7 +56,7 @@ class DirectRadioButton(DirectButton):
             ('boxImageScale', 1.0, None),
             ('boxImageColor', VBase4(1, 1, 1, 1), None),
             ('boxRelief', None, None),
-            )
+        )
         # Merge keyword options with default options
         self.defineoptions(kw, optiondefs)
         # Initialize superclasses
@@ -194,7 +196,6 @@ class DirectRadioButton(DirectButton):
 
             self.indicator.setPos(newpos[0], newpos[1], newpos[2])
 
-
     def commandFunc(self, event):
         if len(self['value']) == len(self['variable']) != 0:
             for i in range(len(self['value'])):

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