Browse Source

Merge branch 'master' into cmake

Sam Edwards 6 years ago
parent
commit
19ed9f6b47
100 changed files with 2491 additions and 5934 deletions
  1. 2 0
      .github/FUNDING.yml
  2. 6 0
      BACKERS.md
  3. 4 4
      README.md
  4. 3 0
      contrib/src/ai/aiBehaviors.h
  5. 2 1
      direct/src/actor/Actor.py
  6. 5 689
      direct/src/dcparser/dcClass.cxx
  7. 58 44
      direct/src/dcparser/dcClass.h
  8. 665 0
      direct/src/dcparser/dcClass_ext.cxx
  9. 93 0
      direct/src/dcparser/dcClass_ext.h
  10. 6 313
      direct/src/dcparser/dcField.cxx
  11. 13 15
      direct/src/dcparser/dcField.h
  12. 305 0
      direct/src/dcparser/dcField_ext.cxx
  13. 48 0
      direct/src/dcparser/dcField_ext.h
  14. 0 508
      direct/src/dcparser/dcPacker.cxx
  15. 17 14
      direct/src/dcparser/dcPacker.h
  16. 508 0
      direct/src/dcparser/dcPacker_ext.cxx
  17. 45 0
      direct/src/dcparser/dcPacker_ext.h
  18. 0 42
      direct/src/dcparser/dcPython.h
  19. 3 0
      direct/src/dcparser/p3dcparser_ext_composite.cxx
  20. 2 0
      direct/src/directtools/DirectUtil.py
  21. 14 7
      direct/src/dist/FreezeTool.py
  22. 56 0
      direct/src/dist/commands.py
  23. 4 3
      direct/src/distributed/cConnectionRepository.cxx
  24. 1 1
      direct/src/distributed/cConnectionRepository.h
  25. 4 0
      direct/src/distributed/cDistributedSmoothNodeBase.cxx
  26. 1 2
      direct/src/distributed/cDistributedSmoothNodeBase.h
  27. 4 4
      direct/src/distributed/config_distributed.h
  28. 2 1
      direct/src/gui/DirectDialog.py
  29. 9 7
      direct/src/gui/DirectEntry.py
  30. 25 3
      direct/src/gui/DirectEntryScroll.py
  31. 3 3
      direct/src/gui/DirectGuiBase.py
  32. 4 3
      direct/src/gui/DirectOptionMenu.py
  33. 6 1
      direct/src/gui/DirectScrolledFrame.py
  34. 5 4
      direct/src/gui/DirectScrolledList.py
  35. 1 19
      direct/src/interval/SoundInterval.py
  36. 199 62
      direct/src/p3d/DeploymentTools.py
  37. 18 0
      direct/src/showbase/BufferViewer.py
  38. 17 1
      direct/src/showbase/Loader.py
  39. 5 0
      direct/src/showbase/PythonUtil.py
  40. 4 1
      direct/src/showbase/ShowBase.py
  41. 1 0
      direct/src/showbase/ShowBaseGlobal.py
  42. 2 1
      direct/src/showbase/Transitions.py
  43. 2 1
      direct/src/tkpanels/MopathRecorder.py
  44. 25 15
      direct/src/tkpanels/ParticlePanel.py
  45. 0 108
      dmodels/src/level_editor/donaldsDockColors.txt
  46. 0 64
      dmodels/src/level_editor/donaldsDockStyles.txt
  47. BIN
      dmodels/src/level_editor/donalds_dock_layout.flt
  48. 0 61
      dmodels/src/level_editor/minniesMelodyLandColors.txt
  49. 0 79
      dmodels/src/level_editor/minniesMelodyLandStyles.txt
  50. BIN
      dmodels/src/level_editor/minnies_melody_land_layout.flt
  51. 0 65
      dmodels/src/level_editor/theBurrrghColors.txt
  52. 0 90
      dmodels/src/level_editor/theBurrrghStyles.txt
  53. BIN
      dmodels/src/level_editor/the_burrrgh_layout.flt
  54. 0 106
      dmodels/src/level_editor/toontownCentralColors.txt
  55. 0 84
      dmodels/src/level_editor/toontownCentralStyles.txt
  56. BIN
      dmodels/src/level_editor/toontown_central_layout.flt
  57. 43 0
      doc/ReleaseNotes
  58. 15 11
      dtool/src/cppparser/cppIdentifier.cxx
  59. 0 47
      dtool/src/dtoolbase/dllbase.txt
  60. 0 12
      dtool/src/dtoolbase/dtoolsymbols.h
  61. 6 6
      dtool/src/dtoolutil/iostream_ext.cxx
  62. 4 4
      dtool/src/dtoolutil/iostream_ext.h
  63. 10 1
      dtool/src/interrogate/interfaceMakerPythonNative.cxx
  64. 6 3
      dtool/src/interrogate/interrogate.cxx
  65. 8 1
      dtool/src/interrogatedb/dtool_super_base.cxx
  66. 1 1
      dtool/src/interrogatedb/py_compat.h
  67. 73 10
      dtool/src/interrogatedb/py_wrappers.cxx
  68. 4 4
      dtool/src/parser-inc/iostream
  69. 5 1
      dtool/src/prc/encryptStream.cxx
  70. 16 11
      dtool/src/prc/notify.cxx
  71. 6 0
      dtool/src/prc/notifyCategory.cxx
  72. 0 492
      dtool/src/pystub/pystub.cxx
  73. 0 38
      dtool/src/pystub/pystub.h
  74. 2 2
      makepanda/getversion.py
  75. 27 0
      makepanda/installer.nsi
  76. 1 1
      makepanda/installpanda.py
  77. 5 7
      makepanda/makepackage.py
  78. 31 56
      makepanda/makepanda.py
  79. 0 4
      makepanda/makepanda.vcproj
  80. 5 1
      makepanda/makepandacore.py
  81. 14 12
      makepanda/makewheel.py
  82. 8 3
      makepanda/test_wheel.py
  83. 0 8
      panda/src/audio/audioManager.cxx
  84. 0 3
      panda/src/audio/audioManager.h
  85. 0 17
      panda/src/audio/audioSound.cxx
  86. 1 8
      panda/src/audio/audioSound.h
  87. 0 23
      panda/src/audio/config_audio.cxx
  88. 1 13
      panda/src/audio/config_audio.h
  89. 1 1
      panda/src/audio/nullAudioManager.h
  90. 1 1
      panda/src/audio/nullAudioSound.h
  91. 0 102
      panda/src/audiotraits/config_milesAudio.cxx
  92. 0 37
      panda/src/audiotraits/config_milesAudio.h
  93. 0 39
      panda/src/audiotraits/globalMilesManager.I
  94. 0 446
      panda/src/audiotraits/globalMilesManager.cxx
  95. 0 117
      panda/src/audiotraits/globalMilesManager.h
  96. 0 1121
      panda/src/audiotraits/milesAudioManager.cxx
  97. 0 198
      panda/src/audiotraits/milesAudioManager.h
  98. 0 13
      panda/src/audiotraits/milesAudioSample.I
  99. 0 530
      panda/src/audiotraits/milesAudioSample.cxx
  100. 0 103
      panda/src/audiotraits/milesAudioSample.h

+ 2 - 0
.github/FUNDING.yml

@@ -0,0 +1,2 @@
+open_collective: panda3d
+

+ 6 - 0
BACKERS.md

@@ -24,6 +24,12 @@ This is a list of all the people who are contributing financially to Panda3D.  I
 * Sam Edwards
 * Sam Edwards
 * Max Voss
 * Max Voss
 
 
+## Enthusiasts
+
+![Benefactors](https://opencollective.com/panda3d/tiers/enthusiast.svg?avatarHeight=48&width=600)
+
+* Eric Thomson
+
 ## Backers
 ## Backers
 
 
 ![Backers](https://opencollective.com/panda3d/tiers/backer.svg?avatarHeight=48&width=600)
 ![Backers](https://opencollective.com/panda3d/tiers/backer.svg?avatarHeight=48&width=600)

+ 4 - 4
README.md

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

+ 3 - 0
contrib/src/ai/aiBehaviors.h

@@ -28,6 +28,9 @@ class PathFollow;
 class PathFind;
 class PathFind;
 class ObstacleAvoidance;
 class ObstacleAvoidance;
 
 
+#include "flee.h"
+#include "evade.h"
+
 typedef std::list<Flee, std::allocator<Flee> > ListFlee;
 typedef std::list<Flee, std::allocator<Flee> > ListFlee;
 typedef std::list<Evade, std::allocator<Evade> > ListEvade;
 typedef std::list<Evade, std::allocator<Evade> > ListEvade;
 
 

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

@@ -67,7 +67,7 @@ class Actor(DirectObject, NodePath):
 
 
         def __init__(self, filename = None, animBundle = None):
         def __init__(self, filename = None, animBundle = None):
             self.filename = filename
             self.filename = filename
-            self.animBundle = None
+            self.animBundle = animBundle
             self.animControl = None
             self.animControl = None
 
 
         def makeCopy(self):
         def makeCopy(self):
@@ -1947,6 +1947,7 @@ class Actor(DirectObject, NodePath):
                     animName = acc.getAnimName(i)
                     animName = acc.getAnimName(i)
 
 
                     animDef = Actor.AnimDef()
                     animDef = Actor.AnimDef()
+                    animDef.animBundle = animControl.getAnim()
                     animDef.animControl = animControl
                     animDef.animControl = animControl
                     self.__animControlDict[lodName][partName][animName] = animDef
                     self.__animControlDict[lodName][partName][animName] = animDef
 
 

+ 5 - 689
direct/src/dcparser/dcClass.cxx

@@ -16,22 +16,13 @@
 #include "dcAtomicField.h"
 #include "dcAtomicField.h"
 #include "hashGenerator.h"
 #include "hashGenerator.h"
 #include "dcindent.h"
 #include "dcindent.h"
-#include "dcmsgtypes.h"
 
 
 #include "dcClassParameter.h"
 #include "dcClassParameter.h"
 #include <algorithm>
 #include <algorithm>
 
 
-#ifdef HAVE_PYTHON
-#include "py_panda.h"
-#endif
-
-using std::ostream;
-using std::ostringstream;
 using std::string;
 using std::string;
 
 
 #ifdef WITHIN_PANDA
 #ifdef WITHIN_PANDA
-#include "pStatTimer.h"
-
 #ifndef CPPPARSER
 #ifndef CPPPARSER
 PStatCollector DCClass::_update_pcollector("App:Show code:readerPollTask:Update");
 PStatCollector DCClass::_update_pcollector("App:Show code:readerPollTask:Update");
 PStatCollector DCClass::_generate_pcollector("App:Show code:readerPollTask:Generate");
 PStatCollector DCClass::_generate_pcollector("App:Show code:readerPollTask:Generate");
@@ -86,10 +77,7 @@ DCClass(DCFile *dc_file, const string &name, bool is_struct, bool bogus_class) :
   _number = -1;
   _number = -1;
   _constructor = nullptr;
   _constructor = nullptr;
 
 
-#ifdef HAVE_PYTHON
-  _class_def = nullptr;
-  _owner_class_def = nullptr;
-#endif
+  _python_class_defs = nullptr;
 }
 }
 
 
 /**
 /**
@@ -105,11 +93,6 @@ DCClass::
   for (fi = _fields.begin(); fi != _fields.end(); ++fi) {
   for (fi = _fields.begin(); fi != _fields.end(); ++fi) {
     delete (*fi);
     delete (*fi);
   }
   }
-
-#ifdef HAVE_PYTHON
-  Py_XDECREF(_class_def);
-  Py_XDECREF(_owner_class_def);
-#endif
 }
 }
 
 
 /**
 /**
@@ -335,7 +318,7 @@ inherits_from_bogus_class() const {
  * Write a string representation of this instance to <out>.
  * Write a string representation of this instance to <out>.
  */
  */
 void DCClass::
 void DCClass::
-output(ostream &out) const {
+output(std::ostream &out) const {
   if (_is_struct) {
   if (_is_struct) {
     out << "struct";
     out << "struct";
   } else {
   } else {
@@ -346,678 +329,11 @@ output(ostream &out) const {
   }
   }
 }
 }
 
 
-#ifdef HAVE_PYTHON
-/**
- * Returns true if the DCClass object has an associated Python class
- * definition, false otherwise.
- */
-bool DCClass::
-has_class_def() const {
-  return (_class_def != nullptr);
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Sets the class object associated with this DistributedClass.  This object
- * will be used to construct new instances of the class.
- */
-void DCClass::
-set_class_def(PyObject *class_def) {
-  Py_XINCREF(class_def);
-  Py_XDECREF(_class_def);
-  _class_def = class_def;
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Returns the class object that was previously associated with this
- * DistributedClass.  This will return a new reference to the object.
- */
-PyObject *DCClass::
-get_class_def() const {
-  if (_class_def == nullptr) {
-    Py_INCREF(Py_None);
-    return Py_None;
-  }
-
-  Py_INCREF(_class_def);
-  return _class_def;
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Returns true if the DCClass object has an associated Python owner class
- * definition, false otherwise.
- */
-bool DCClass::
-has_owner_class_def() const {
-  return (_owner_class_def != nullptr);
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Sets the owner class object associated with this DistributedClass.  This
- * object will be used to construct new owner instances of the class.
- */
-void DCClass::
-set_owner_class_def(PyObject *owner_class_def) {
-  Py_XINCREF(owner_class_def);
-  Py_XDECREF(_owner_class_def);
-  _owner_class_def = owner_class_def;
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Returns the owner class object that was previously associated with this
- * DistributedClass.  This will return a new reference to the object.
- */
-PyObject *DCClass::
-get_owner_class_def() const {
-  if (_owner_class_def == nullptr) {
-    Py_INCREF(Py_None);
-    return Py_None;
-  }
-
-  Py_INCREF(_owner_class_def);
-  return _owner_class_def;
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Extracts the update message out of the packer and applies it to the
- * indicated object by calling the appropriate method.
- */
-void DCClass::
-receive_update(PyObject *distobj, DatagramIterator &di) const {
-#ifdef WITHIN_PANDA
-  PStatTimer timer(((DCClass *)this)->_class_update_pcollector);
-#endif
-    DCPacker packer;
-    const char *data = (const char *)di.get_datagram().get_data();
-    packer.set_unpack_data(data + di.get_current_index(),
-                           di.get_remaining_size(), false);
-
-    int field_id = packer.raw_unpack_uint16();
-    DCField *field = get_field_by_index(field_id);
-    if (field == nullptr) {
-            ostringstream strm;
-            strm
-                << "Received update for field " << field_id << ", not in class "
-                << get_name();
-            nassert_raise(strm.str());
-            return;
-    }
-
-    packer.begin_unpack(field);
-    field->receive_update(packer, distobj);
-    packer.end_unpack();
-
-    di.skip_bytes(packer.get_num_unpacked_bytes());
-
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Processes a big datagram that includes all of the "required" fields that
- * are sent along with a normal "generate with required" message.  This is all
- * of the atomic fields that are marked "broadcast required".
- */
-void DCClass::
-receive_update_broadcast_required(PyObject *distobj, DatagramIterator &di) const {
-#ifdef WITHIN_PANDA
-  PStatTimer timer(((DCClass *)this)->_class_update_pcollector);
-#endif
-  DCPacker packer;
-  const char *data = (const char *)di.get_datagram().get_data();
-  packer.set_unpack_data(data + di.get_current_index(),
-                         di.get_remaining_size(), false);
-
-  int num_fields = get_num_inherited_fields();
-  for (int i = 0; i < num_fields && !PyErr_Occurred(); ++i) {
-    DCField *field = get_inherited_field(i);
-    if (field->as_molecular_field() == nullptr &&
-        field->is_required() && field->is_broadcast()) {
-      packer.begin_unpack(field);
-      field->receive_update(packer, distobj);
-      if (!packer.end_unpack()) {
-        break;
-      }
-    }
-  }
-
-  di.skip_bytes(packer.get_num_unpacked_bytes());
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Processes a big datagram that includes all of the "required" fields that
- * are sent along with a normal "generate with required" message.  This is all
- * of the atomic fields that are marked "broadcast ownrecv". Should be used
- * for 'owner-view' objects.
- */
-void DCClass::
-receive_update_broadcast_required_owner(PyObject *distobj,
-                                        DatagramIterator &di) const {
-#ifdef WITHIN_PANDA
-  PStatTimer timer(((DCClass *)this)->_class_update_pcollector);
-#endif
-  DCPacker packer;
-  const char *data = (const char *)di.get_datagram().get_data();
-  packer.set_unpack_data(data + di.get_current_index(),
-                         di.get_remaining_size(), false);
-
-  int num_fields = get_num_inherited_fields();
-  for (int i = 0; i < num_fields && !PyErr_Occurred(); ++i) {
-    DCField *field = get_inherited_field(i);
-    if (field->as_molecular_field() == nullptr &&
-        field->is_required() && (field->is_ownrecv() || field->is_broadcast())) {
-      packer.begin_unpack(field);
-      field->receive_update(packer, distobj);
-      if (!packer.end_unpack()) {
-        break;
-      }
-    }
-  }
-
-  di.skip_bytes(packer.get_num_unpacked_bytes());
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Processes a big datagram that includes all of the "required" fields that
- * are sent when an avatar is created.  This is all of the atomic fields that
- * are marked "required", whether they are broadcast or not.
- */
-void DCClass::
-receive_update_all_required(PyObject *distobj, DatagramIterator &di) const {
-#ifdef WITHIN_PANDA
-  PStatTimer timer(((DCClass *)this)->_class_update_pcollector);
-#endif
-  DCPacker packer;
-  const char *data = (const char *)di.get_datagram().get_data();
-  packer.set_unpack_data(data + di.get_current_index(),
-                         di.get_remaining_size(), false);
-
-  int num_fields = get_num_inherited_fields();
-  for (int i = 0; i < num_fields && !PyErr_Occurred(); ++i) {
-    DCField *field = get_inherited_field(i);
-    if (field->as_molecular_field() == nullptr &&
-        field->is_required()) {
-      packer.begin_unpack(field);
-      field->receive_update(packer, distobj);
-      if (!packer.end_unpack()) {
-        break;
-      }
-    }
-  }
-
-  di.skip_bytes(packer.get_num_unpacked_bytes());
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Processes a datagram that lists some additional fields that are broadcast
- * in one chunk.
- */
-void DCClass::
-receive_update_other(PyObject *distobj, DatagramIterator &di) const {
-#ifdef WITHIN_PANDA
-  PStatTimer timer(((DCClass *)this)->_class_update_pcollector);
-#endif
-  int num_fields = di.get_uint16();
-  for (int i = 0; i < num_fields && !PyErr_Occurred(); ++i) {
-    receive_update(distobj, di);
-  }
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Processes an update for a named field from a packed value blob.
- */
-void DCClass::
-direct_update(PyObject *distobj, const string &field_name,
-              const vector_uchar &value_blob) {
-  DCField *field = get_field_by_name(field_name);
-  nassertv_always(field != nullptr);
-
-  DCPacker packer;
-  packer.set_unpack_data(value_blob);
-  packer.begin_unpack(field);
-  field->receive_update(packer, distobj);
-  packer.end_unpack();
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Processes an update for a named field from a packed datagram.
- */
-void DCClass::
-direct_update(PyObject *distobj, const string &field_name,
-              const Datagram &datagram) {
-  DCField *field = get_field_by_name(field_name);
-  nassertv_always(field != nullptr);
-
-  DCPacker packer;
-  packer.set_unpack_data((const char *)datagram.get_data(), datagram.get_length(), false);
-  packer.begin_unpack(field);
-  field->receive_update(packer, distobj);
-  packer.end_unpack();
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Looks up the current value of the indicated field by calling the
- * appropriate get*() function, then packs that value into the datagram.  This
- * field is presumably either a required field or a specified optional field,
- * and we are building up a datagram for the generate-with-required message.
- *
- * Returns true on success, false on failure.
- */
-bool DCClass::
-pack_required_field(Datagram &datagram, PyObject *distobj,
-                    const DCField *field) const {
-  DCPacker packer;
-  packer.begin_pack(field);
-  if (!pack_required_field(packer, distobj, field)) {
-    return false;
-  }
-  if (!packer.end_pack()) {
-    return false;
-  }
-
-  datagram.append_data(packer.get_data(), packer.get_length());
-  return true;
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Looks up the current value of the indicated field by calling the
- * appropriate get*() function, then packs that value into the packer.  This
- * field is presumably either a required field or a specified optional field,
- * and we are building up a datagram for the generate-with-required message.
- *
- * Returns true on success, false on failure.
- */
-bool DCClass::
-pack_required_field(DCPacker &packer, PyObject *distobj,
-                    const DCField *field) const {
-  const DCParameter *parameter = field->as_parameter();
-  if (parameter != nullptr) {
-    // This is the easy case: to pack a parameter, we just look on the class
-    // object for the data element.
-    string field_name = field->get_name();
-
-    if (!PyObject_HasAttrString(distobj, (char *)field_name.c_str())) {
-      // If the attribute is not defined, but the field has a default value
-      // specified, quietly pack the default value.
-      if (field->has_default_value()) {
-        packer.pack_default_value();
-        return true;
-      }
-
-      // If there is no default value specified, it's an error.
-      ostringstream strm;
-      strm << "Data element " << field_name
-           << ", required by dc file for dclass " << get_name()
-           << ", not defined on object";
-      nassert_raise(strm.str());
-      return false;
-    }
-    PyObject *result =
-      PyObject_GetAttrString(distobj, (char *)field_name.c_str());
-    nassertr(result != nullptr, false);
-
-    // Now pack the value into the datagram.
-    bool pack_ok = parameter->pack_args(packer, result);
-    Py_DECREF(result);
-
-    return pack_ok;
-  }
-
-  if (field->as_molecular_field() != nullptr) {
-    ostringstream strm;
-    strm << "Cannot pack molecular field " << field->get_name()
-         << " for generate";
-    nassert_raise(strm.str());
-    return false;
-  }
-
-  const DCAtomicField *atom = field->as_atomic_field();
-  nassertr(atom != nullptr, false);
-
-  // We need to get the initial value of this field.  There isn't a good,
-  // robust way to get this; presently, we just mangle the "setFoo()" name of
-  // the required field into "getFoo()" and call that.
-  string setter_name = atom->get_name();
-
-  if (setter_name.empty()) {
-    ostringstream strm;
-    strm << "Required field is unnamed!";
-    nassert_raise(strm.str());
-    return false;
-  }
-
-  if (atom->get_num_elements() == 0) {
-    // It sure doesn't make sense to have a required field with no parameters.
-    // What data, exactly, is required?
-    ostringstream strm;
-    strm << "Required field " << setter_name << " has no parameters!";
-    nassert_raise(strm.str());
-    return false;
-  }
-
-  string getter_name = setter_name;
-  if (setter_name.substr(0, 3) == "set") {
-    // If the original method started with "set", we mangle this directly to
-    // "get".
-    getter_name[0] = 'g';
-
-  } else {
-    // Otherwise, we add a "get" prefix, and capitalize the next letter.
-    getter_name = "get" + setter_name;
-    getter_name[3] = toupper(getter_name[3]);
-  }
-
-  // Now we have to look up the getter on the distributed object and call it.
-  if (!PyObject_HasAttrString(distobj, (char *)getter_name.c_str())) {
-    // As above, if there's no getter but the field has a default value
-    // specified, quietly pack the default value.
-    if (field->has_default_value()) {
-      packer.pack_default_value();
-      return true;
-    }
-
-    // Otherwise, with no default value it's an error.
-    ostringstream strm;
-    strm << "Distributed class " << get_name()
-         << " doesn't have getter named " << getter_name
-         << " to match required field " << setter_name;
-    nassert_raise(strm.str());
-    return false;
-  }
-  PyObject *func =
-    PyObject_GetAttrString(distobj, (char *)getter_name.c_str());
-  nassertr(func != nullptr, false);
-
-  PyObject *empty_args = PyTuple_New(0);
-  PyObject *result = PyObject_CallObject(func, empty_args);
-  Py_DECREF(empty_args);
-  Py_DECREF(func);
-  if (result == nullptr) {
-    // We don't set this as an exception, since presumably the Python method
-    // itself has already triggered a Python exception.
-    std::cerr << "Error when calling " << getter_name << "\n";
-    return false;
-  }
-
-  if (atom->get_num_elements() == 1) {
-    // In this case, we expect the getter to return one object, which we wrap
-    // up in a tuple.
-    PyObject *tuple = PyTuple_New(1);
-    PyTuple_SET_ITEM(tuple, 0, result);
-    result = tuple;
-
-  } else {
-    // Otherwise, it had better already be a sequence or tuple of some sort.
-    if (!PySequence_Check(result)) {
-      ostringstream strm;
-      strm << "Since dclass " << get_name() << " method " << setter_name
-           << " is declared to have multiple parameters, Python function "
-           << getter_name << " must return a list or tuple.\n";
-      nassert_raise(strm.str());
-      return false;
-    }
-  }
-
-  // Now pack the arguments into the datagram.
-  bool pack_ok = atom->pack_args(packer, result);
-  Py_DECREF(result);
-
-  return pack_ok;
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Generates a datagram containing the message necessary to send an update for
- * the indicated distributed object from the client.
- */
-Datagram DCClass::
-client_format_update(const string &field_name, DOID_TYPE do_id,
-                     PyObject *args) const {
-  DCField *field = get_field_by_name(field_name);
-  if (field == nullptr) {
-    ostringstream strm;
-    strm << "No field named " << field_name << " in class " << get_name()
-         << "\n";
-    nassert_raise(strm.str());
-    return Datagram();
-  }
-
-  return field->client_format_update(do_id, args);
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Generates a datagram containing the message necessary to send an update for
- * the indicated distributed object from the AI.
- */
-Datagram DCClass::
-ai_format_update(const string &field_name, DOID_TYPE do_id,
-                 CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, PyObject *args) const {
-  DCField *field = get_field_by_name(field_name);
-  if (field == nullptr) {
-    ostringstream strm;
-    strm << "No field named " << field_name << " in class " << get_name()
-         << "\n";
-    nassert_raise(strm.str());
-    return Datagram();
-  }
-
-  return field->ai_format_update(do_id, to_id, from_id, args);
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Generates a datagram containing the message necessary to send an update,
- * using the indicated msg type for the indicated distributed object from the
- * AI.
- */
-Datagram DCClass::
-ai_format_update_msg_type(const string &field_name, DOID_TYPE do_id,
-                 CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, int msg_type, PyObject *args) const {
-  DCField *field = get_field_by_name(field_name);
-  if (field == nullptr) {
-    ostringstream strm;
-    strm << "No field named " << field_name << " in class " << get_name()
-         << "\n";
-    nassert_raise(strm.str());
-    return Datagram();
-  }
-
-  return field->ai_format_update_msg_type(do_id, to_id, from_id, msg_type, args);
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Generates a datagram containing the message necessary to generate a new
- * distributed object from the client.  This requires querying the object for
- * the initial value of its required fields.
- *
- * optional_fields is a list of fieldNames to generate in addition to the
- * normal required fields.
- *
- * This method is only called by the CMU implementation.
- */
-Datagram DCClass::
-client_format_generate_CMU(PyObject *distobj, DOID_TYPE do_id,
-                           ZONEID_TYPE zone_id,
-                           PyObject *optional_fields) const {
-  DCPacker packer;
-
-  packer.raw_pack_uint16(CLIENT_OBJECT_GENERATE_CMU);
-
-  packer.raw_pack_uint32(zone_id);
-  packer.raw_pack_uint16(_number);
-  packer.raw_pack_uint32(do_id);
-
-  // Specify all of the required fields.
-  int num_fields = get_num_inherited_fields();
-  for (int i = 0; i < num_fields; ++i) {
-    DCField *field = get_inherited_field(i);
-    if (field->is_required() && field->as_molecular_field() == nullptr) {
-      packer.begin_pack(field);
-      if (!pack_required_field(packer, distobj, field)) {
-        return Datagram();
-      }
-      packer.end_pack();
-    }
-  }
-
-  // Also specify the optional fields.
-  int num_optional_fields = 0;
-  if (PyObject_IsTrue(optional_fields)) {
-    num_optional_fields = PySequence_Size(optional_fields);
-  }
-  packer.raw_pack_uint16(num_optional_fields);
-
-  for (int i = 0; i < num_optional_fields; i++) {
-    PyObject *py_field_name = PySequence_GetItem(optional_fields, i);
-#if PY_MAJOR_VERSION >= 3
-    string field_name = PyUnicode_AsUTF8(py_field_name);
-#else
-    string field_name = PyString_AsString(py_field_name);
-#endif
-    Py_XDECREF(py_field_name);
-
-    DCField *field = get_field_by_name(field_name);
-    if (field == nullptr) {
-      ostringstream strm;
-      strm << "No field named " << field_name << " in class " << get_name()
-           << "\n";
-      nassert_raise(strm.str());
-      return Datagram();
-    }
-    packer.raw_pack_uint16(field->get_number());
-    packer.begin_pack(field);
-    if (!pack_required_field(packer, distobj, field)) {
-      return Datagram();
-    }
-    packer.end_pack();
-  }
-
-  return Datagram(packer.get_data(), packer.get_length());
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Generates a datagram containing the message necessary to generate a new
- * distributed object from the AI. This requires querying the object for the
- * initial value of its required fields.
- *
- * optional_fields is a list of fieldNames to generate in addition to the
- * normal required fields.
- */
-Datagram DCClass::
-ai_format_generate(PyObject *distobj, DOID_TYPE do_id,
-                   DOID_TYPE parent_id, ZONEID_TYPE zone_id,
-                   CHANNEL_TYPE district_channel_id, CHANNEL_TYPE from_channel_id,
-                   PyObject *optional_fields) const {
-  DCPacker packer;
-
-  packer.raw_pack_uint8(1);
-  packer.RAW_PACK_CHANNEL(district_channel_id);
-  packer.RAW_PACK_CHANNEL(from_channel_id);
-    // packer.raw_pack_uint8('A');
-
-  bool has_optional_fields = (PyObject_IsTrue(optional_fields) != 0);
-
-  if (has_optional_fields) {
-    packer.raw_pack_uint16(STATESERVER_CREATE_OBJECT_WITH_REQUIRED_OTHER);
-  } else {
-    packer.raw_pack_uint16(STATESERVER_CREATE_OBJECT_WITH_REQUIRED);
-  }
-
-  packer.raw_pack_uint32(do_id);
-  // Parent is a bit overloaded; this parent is not about inheritance, this
-  // one is about the visibility container parent, i.e.  the zone parent:
-  packer.raw_pack_uint32(parent_id);
-  packer.raw_pack_uint32(zone_id);
-  packer.raw_pack_uint16(_number);
-
-  // Specify all of the required fields.
-  int num_fields = get_num_inherited_fields();
-  for (int i = 0; i < num_fields; ++i) {
-    DCField *field = get_inherited_field(i);
-    if (field->is_required() && field->as_molecular_field() == nullptr) {
-      packer.begin_pack(field);
-      if (!pack_required_field(packer, distobj, field)) {
-        return Datagram();
-      }
-      packer.end_pack();
-    }
-  }
-
-  // Also specify the optional fields.
-  if (has_optional_fields) {
-    int num_optional_fields = PySequence_Size(optional_fields);
-    packer.raw_pack_uint16(num_optional_fields);
-
-    for (int i = 0; i < num_optional_fields; ++i) {
-      PyObject *py_field_name = PySequence_GetItem(optional_fields, i);
-#if PY_MAJOR_VERSION >= 3
-      string field_name = PyUnicode_AsUTF8(py_field_name);
-#else
-      string field_name = PyString_AsString(py_field_name);
-#endif
-      Py_XDECREF(py_field_name);
-
-      DCField *field = get_field_by_name(field_name);
-      if (field == nullptr) {
-        ostringstream strm;
-        strm << "No field named " << field_name << " in class " << get_name()
-             << "\n";
-        nassert_raise(strm.str());
-        return Datagram();
-      }
-
-      packer.raw_pack_uint16(field->get_number());
-
-      packer.begin_pack(field);
-      if (!pack_required_field(packer, distobj, field)) {
-        return Datagram();
-      }
-      packer.end_pack();
-    }
-  }
-
-  return Datagram(packer.get_data(), packer.get_length());
-}
-#endif  // HAVE_PYTHON
-
 /**
 /**
  * Write a string representation of this instance to <out>.
  * Write a string representation of this instance to <out>.
  */
  */
 void DCClass::
 void DCClass::
-output(ostream &out, bool brief) const {
+output(std::ostream &out, bool brief) const {
   output_instance(out, brief, "", "", "");
   output_instance(out, brief, "", "", "");
 }
 }
 
 
@@ -1026,7 +342,7 @@ output(ostream &out, bool brief) const {
  * stream.
  * stream.
  */
  */
 void DCClass::
 void DCClass::
-write(ostream &out, bool brief, int indent_level) const {
+write(std::ostream &out, bool brief, int indent_level) const {
   indent(out, indent_level);
   indent(out, indent_level);
   if (_is_struct) {
   if (_is_struct) {
     out << "struct";
     out << "struct";
@@ -1086,7 +402,7 @@ write(ostream &out, bool brief, int indent_level) const {
  * stream.
  * stream.
  */
  */
 void DCClass::
 void DCClass::
-output_instance(ostream &out, bool brief, const string &prename,
+output_instance(std::ostream &out, bool brief, const string &prename,
                 const string &name, const string &postname) const {
                 const string &name, const string &postname) const {
   if (_is_struct) {
   if (_is_struct) {
     out << "struct";
     out << "struct";

+ 58 - 44
direct/src/dcparser/dcClass.h

@@ -17,11 +17,12 @@
 #include "dcbase.h"
 #include "dcbase.h"
 #include "dcField.h"
 #include "dcField.h"
 #include "dcDeclaration.h"
 #include "dcDeclaration.h"
-#include "dcPython.h"
 
 
 #ifdef WITHIN_PANDA
 #ifdef WITHIN_PANDA
 #include "pStatCollector.h"
 #include "pStatCollector.h"
 #include "configVariableBool.h"
 #include "configVariableBool.h"
+#include "extension.h"
+#include "datagramIterator.h"
 
 
 extern ConfigVariableBool dc_multiple_inheritance;
 extern ConfigVariableBool dc_multiple_inheritance;
 extern ConfigVariableBool dc_virtual_inheritance;
 extern ConfigVariableBool dc_virtual_inheritance;
@@ -80,44 +81,52 @@ PUBLISHED:
 
 
   virtual void output(std::ostream &out) const;
   virtual void output(std::ostream &out) const;
 
 
-#ifdef HAVE_PYTHON
-  bool has_class_def() const;
-  void set_class_def(PyObject *class_def);
-  PyObject *get_class_def() const;
-  bool has_owner_class_def() const;
-  void set_owner_class_def(PyObject *owner_class_def);
-  PyObject *get_owner_class_def() const;
-
-  void receive_update(PyObject *distobj, DatagramIterator &di) const;
-  void receive_update_broadcast_required(PyObject *distobj, DatagramIterator &di) const;
-  void receive_update_broadcast_required_owner(PyObject *distobj, DatagramIterator &di) const;
-  void receive_update_all_required(PyObject *distobj, DatagramIterator &di) const;
-  void receive_update_other(PyObject *distobj, DatagramIterator &di) const;
-
-  void direct_update(PyObject *distobj, const std::string &field_name,
-                     const vector_uchar &value_blob);
-  void direct_update(PyObject *distobj, const std::string &field_name,
-                     const Datagram &datagram);
-  bool pack_required_field(Datagram &datagram, PyObject *distobj,
-                           const DCField *field) const;
-  bool pack_required_field(DCPacker &packer, PyObject *distobj,
-                           const DCField *field) const;
-
-
-
-  Datagram client_format_update(const std::string &field_name,
-                                DOID_TYPE do_id, PyObject *args) const;
-  Datagram ai_format_update(const std::string &field_name, DOID_TYPE do_id,
-                            CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, PyObject *args) const;
-  Datagram ai_format_update_msg_type(const std::string &field_name, DOID_TYPE do_id,
-                            CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, int msg_type, PyObject *args) const;
-  Datagram ai_format_generate(PyObject *distobj, DOID_TYPE do_id, ZONEID_TYPE parent_id, ZONEID_TYPE zone_id,
-                              CHANNEL_TYPE district_channel_id, CHANNEL_TYPE from_channel_id,
-                              PyObject *optional_fields) const;
-  Datagram client_format_generate_CMU(PyObject *distobj, DOID_TYPE do_id,
-                                      ZONEID_TYPE zone_id,                                                           PyObject *optional_fields) const;
+  EXTENSION(bool has_class_def() const);
+  EXTENSION(void set_class_def(PyObject *class_def));
+  EXTENSION(PyObject *get_class_def() const);
+  EXTENSION(bool has_owner_class_def() const);
+  EXTENSION(void set_owner_class_def(PyObject *owner_class_def));
+  EXTENSION(PyObject *get_owner_class_def() const);
+
+  EXTENSION(void receive_update(PyObject *distobj, DatagramIterator &di) const);
+  EXTENSION(void receive_update_broadcast_required(PyObject *distobj, DatagramIterator &di) const);
+  EXTENSION(void receive_update_broadcast_required_owner(PyObject *distobj, DatagramIterator &di) const);
+  EXTENSION(void receive_update_all_required(PyObject *distobj, DatagramIterator &di) const);
+  EXTENSION(void receive_update_other(PyObject *distobj, DatagramIterator &di) const);
+
+  EXTENSION(void direct_update(PyObject *distobj, const std::string &field_name,
+                               const vector_uchar &value_blob));
+  EXTENSION(void direct_update(PyObject *distobj, const std::string &field_name,
+                               const Datagram &datagram));
+  EXTENSION(bool pack_required_field(Datagram &datagram, PyObject *distobj,
+                                     const DCField *field) const);
+  EXTENSION(bool pack_required_field(DCPacker &packer, PyObject *distobj,
+                                     const DCField *field) const);
+
+
+
+  EXTENSION(Datagram client_format_update(const std::string &field_name,
+                                          DOID_TYPE do_id, PyObject *args) const);
+  EXTENSION(Datagram ai_format_update(const std::string &field_name,
+                                      DOID_TYPE do_id,
+                                      CHANNEL_TYPE to_id, CHANNEL_TYPE from_id,
+                                      PyObject *args) const);
+  EXTENSION(Datagram ai_format_update_msg_type(const std::string &field_name,
+                                               DOID_TYPE do_id,
+                                               CHANNEL_TYPE to_id,
+                                               CHANNEL_TYPE from_id,
+                                               int msg_type,
+                                               PyObject *args) const);
+  EXTENSION(Datagram ai_format_generate(PyObject *distobj, DOID_TYPE do_id,
+                                        ZONEID_TYPE parent_id,
+                                        ZONEID_TYPE zone_id,
+                                        CHANNEL_TYPE district_channel_id,
+                                        CHANNEL_TYPE from_channel_id,
+                                        PyObject *optional_fields) const);
+  EXTENSION(Datagram client_format_generate_CMU(PyObject *distobj, DOID_TYPE do_id,
+                                                ZONEID_TYPE zone_id,
+                                                PyObject *optional_fields) const);
 
 
-#endif
 
 
 public:
 public:
   virtual void output(std::ostream &out, bool brief) const;
   virtual void output(std::ostream &out, bool brief) const;
@@ -136,8 +145,8 @@ private:
   void shadow_inherited_field(const std::string &name);
   void shadow_inherited_field(const std::string &name);
 
 
 #ifdef WITHIN_PANDA
 #ifdef WITHIN_PANDA
-  PStatCollector _class_update_pcollector;
-  PStatCollector _class_generate_pcollector;
+  mutable PStatCollector _class_update_pcollector;
+  mutable PStatCollector _class_generate_pcollector;
   static PStatCollector _update_pcollector;
   static PStatCollector _update_pcollector;
   static PStatCollector _generate_pcollector;
   static PStatCollector _generate_pcollector;
 #endif
 #endif
@@ -163,12 +172,17 @@ private:
   typedef pmap<int, DCField *> FieldsByIndex;
   typedef pmap<int, DCField *> FieldsByIndex;
   FieldsByIndex _fields_by_index;
   FieldsByIndex _fields_by_index;
 
 
-#ifdef HAVE_PYTHON
-  PyObject *_class_def;
-  PyObject *_owner_class_def;
-#endif
+  // See pandaNode.h for an explanation of this trick
+  class PythonClassDefs : public ReferenceCount {
+  public:
+    virtual ~PythonClassDefs() {};
+  };
+  PT(PythonClassDefs) _python_class_defs;
 
 
   friend class DCField;
   friend class DCField;
+#ifdef WITHIN_PANDA
+  friend class Extension<DCClass>;
+#endif
 };
 };
 
 
 #include "dcClass.I"
 #include "dcClass.I"

+ 665 - 0
direct/src/dcparser/dcClass_ext.cxx

@@ -0,0 +1,665 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file dcClass_ext.cxx
+ * @author CFSworks
+ * @date 2019-07-03
+ */
+
+#include "dcClass_ext.h"
+#include "dcField_ext.h"
+#include "dcAtomicField.h"
+#include "dcPacker.h"
+#include "dcmsgtypes.h"
+
+#include "datagram.h"
+#include "datagramIterator.h"
+#include "pStatTimer.h"
+
+#ifdef HAVE_PYTHON
+
+/**
+ * Returns true if the DCClass object has an associated Python class
+ * definition, false otherwise.
+ */
+bool Extension<DCClass>::
+has_class_def() const {
+  return _this->_python_class_defs != nullptr
+      && ((PythonClassDefsImpl *)_this->_python_class_defs.p())->_class_def != nullptr;
+}
+
+/**
+ * Sets the class object associated with this DistributedClass.  This object
+ * will be used to construct new instances of the class.
+ */
+void Extension<DCClass>::
+set_class_def(PyObject *class_def) {
+  PythonClassDefsImpl *defs = do_get_defs();
+
+  Py_XINCREF(class_def);
+  Py_XDECREF(defs->_class_def);
+  defs->_class_def = class_def;
+}
+
+/**
+ * Returns the class object that was previously associated with this
+ * DistributedClass.  This will return a new reference to the object.
+ */
+PyObject *Extension<DCClass>::
+get_class_def() const {
+  if (!has_class_def()) {
+    Py_INCREF(Py_None);
+    return Py_None;
+  }
+
+  PythonClassDefsImpl *defs = do_get_defs();
+  Py_INCREF(defs->_class_def);
+  return defs->_class_def;
+}
+
+/**
+ * Returns true if the DCClass object has an associated Python owner class
+ * definition, false otherwise.
+ */
+bool Extension<DCClass>::
+has_owner_class_def() const {
+  return _this->_python_class_defs != nullptr
+      && ((PythonClassDefsImpl *)_this->_python_class_defs.p())->_owner_class_def != nullptr;
+}
+
+/**
+ * Sets the owner class object associated with this DistributedClass.  This
+ * object will be used to construct new owner instances of the class.
+ */
+void Extension<DCClass>::
+set_owner_class_def(PyObject *owner_class_def) {
+  PythonClassDefsImpl *defs = do_get_defs();
+
+  Py_XINCREF(owner_class_def);
+  Py_XDECREF(defs->_owner_class_def);
+  defs->_owner_class_def = owner_class_def;
+}
+
+/**
+ * Returns the owner class object that was previously associated with this
+ * DistributedClass.  This will return a new reference to the object.
+ */
+PyObject *Extension<DCClass>::
+get_owner_class_def() const {
+  if (!has_owner_class_def()) {
+    Py_INCREF(Py_None);
+    return Py_None;
+  }
+
+  PythonClassDefsImpl *defs = do_get_defs();
+  Py_INCREF(defs->_owner_class_def);
+  return defs->_owner_class_def;
+}
+
+/**
+ * Extracts the update message out of the packer and applies it to the
+ * indicated object by calling the appropriate method.
+ */
+void Extension<DCClass>::
+receive_update(PyObject *distobj, DatagramIterator &di) const {
+  PStatTimer timer(_this->_class_update_pcollector);
+  DCPacker packer;
+  const char *data = (const char *)di.get_datagram().get_data();
+  packer.set_unpack_data(data + di.get_current_index(),
+                         di.get_remaining_size(), false);
+
+  int field_id = packer.raw_unpack_uint16();
+  DCField *field = _this->get_field_by_index(field_id);
+  if (field == nullptr) {
+    ostringstream strm;
+    strm
+        << "Received update for field " << field_id << ", not in class "
+        << _this->get_name();
+    nassert_raise(strm.str());
+    return;
+  }
+
+  packer.begin_unpack(field);
+  invoke_extension(field).receive_update(packer, distobj);
+  packer.end_unpack();
+
+  di.skip_bytes(packer.get_num_unpacked_bytes());
+}
+
+/**
+ * Processes a big datagram that includes all of the "required" fields that
+ * are sent along with a normal "generate with required" message.  This is all
+ * of the atomic fields that are marked "broadcast required".
+ */
+void Extension<DCClass>::
+receive_update_broadcast_required(PyObject *distobj, DatagramIterator &di) const {
+  PStatTimer timer(_this->_class_update_pcollector);
+  DCPacker packer;
+  const char *data = (const char *)di.get_datagram().get_data();
+  packer.set_unpack_data(data + di.get_current_index(),
+                         di.get_remaining_size(), false);
+
+  int num_fields = _this->get_num_inherited_fields();
+  for (int i = 0; i < num_fields && !PyErr_Occurred(); ++i) {
+    DCField *field = _this->get_inherited_field(i);
+    if (field->as_molecular_field() == nullptr &&
+        field->is_required() && field->is_broadcast()) {
+      packer.begin_unpack(field);
+      invoke_extension(field).receive_update(packer, distobj);
+      if (!packer.end_unpack()) {
+        break;
+      }
+    }
+  }
+
+  di.skip_bytes(packer.get_num_unpacked_bytes());
+}
+
+/**
+ * Processes a big datagram that includes all of the "required" fields that
+ * are sent along with a normal "generate with required" message.  This is all
+ * of the atomic fields that are marked "broadcast ownrecv". Should be used
+ * for 'owner-view' objects.
+ */
+void Extension<DCClass>::
+receive_update_broadcast_required_owner(PyObject *distobj,
+                                        DatagramIterator &di) const {
+  PStatTimer timer(_this->_class_update_pcollector);
+  DCPacker packer;
+  const char *data = (const char *)di.get_datagram().get_data();
+  packer.set_unpack_data(data + di.get_current_index(),
+                         di.get_remaining_size(), false);
+
+  int num_fields = _this->get_num_inherited_fields();
+  for (int i = 0; i < num_fields && !PyErr_Occurred(); ++i) {
+    DCField *field = _this->get_inherited_field(i);
+    if (field->as_molecular_field() == nullptr &&
+        field->is_required() && (field->is_ownrecv() || field->is_broadcast())) {
+      packer.begin_unpack(field);
+      invoke_extension(field).receive_update(packer, distobj);
+      if (!packer.end_unpack()) {
+        break;
+      }
+    }
+  }
+
+  di.skip_bytes(packer.get_num_unpacked_bytes());
+}
+
+/**
+ * Processes a big datagram that includes all of the "required" fields that
+ * are sent when an avatar is created.  This is all of the atomic fields that
+ * are marked "required", whether they are broadcast or not.
+ */
+void Extension<DCClass>::
+receive_update_all_required(PyObject *distobj, DatagramIterator &di) const {
+  PStatTimer timer(_this->_class_update_pcollector);
+  DCPacker packer;
+  const char *data = (const char *)di.get_datagram().get_data();
+  packer.set_unpack_data(data + di.get_current_index(),
+                         di.get_remaining_size(), false);
+
+  int num_fields = _this->get_num_inherited_fields();
+  for (int i = 0; i < num_fields && !PyErr_Occurred(); ++i) {
+    DCField *field = _this->get_inherited_field(i);
+    if (field->as_molecular_field() == nullptr &&
+        field->is_required()) {
+      packer.begin_unpack(field);
+      invoke_extension(field).receive_update(packer, distobj);
+      if (!packer.end_unpack()) {
+        break;
+      }
+    }
+  }
+
+  di.skip_bytes(packer.get_num_unpacked_bytes());
+}
+
+/**
+ * Processes a datagram that lists some additional fields that are broadcast
+ * in one chunk.
+ */
+void Extension<DCClass>::
+receive_update_other(PyObject *distobj, DatagramIterator &di) const {
+  PStatTimer timer(_this->_class_update_pcollector);
+  int num_fields = di.get_uint16();
+  for (int i = 0; i < num_fields && !PyErr_Occurred(); ++i) {
+    receive_update(distobj, di);
+  }
+}
+
+/**
+ * Processes an update for a named field from a packed value blob.
+ */
+void Extension<DCClass>::
+direct_update(PyObject *distobj, const std::string &field_name,
+              const vector_uchar &value_blob) {
+  DCField *field = _this->get_field_by_name(field_name);
+  nassertv_always(field != nullptr);
+
+  DCPacker packer;
+  packer.set_unpack_data(value_blob);
+  packer.begin_unpack(field);
+  invoke_extension(field).receive_update(packer, distobj);
+  packer.end_unpack();
+}
+
+/**
+ * Processes an update for a named field from a packed datagram.
+ */
+void Extension<DCClass>::
+direct_update(PyObject *distobj, const std::string &field_name,
+              const Datagram &datagram) {
+  DCField *field = _this->get_field_by_name(field_name);
+  nassertv_always(field != nullptr);
+
+  DCPacker packer;
+  packer.set_unpack_data((const char *)datagram.get_data(), datagram.get_length(), false);
+  packer.begin_unpack(field);
+  invoke_extension(field).receive_update(packer, distobj);
+  packer.end_unpack();
+}
+
+/**
+ * Looks up the current value of the indicated field by calling the
+ * appropriate get*() function, then packs that value into the datagram.  This
+ * field is presumably either a required field or a specified optional field,
+ * and we are building up a datagram for the generate-with-required message.
+ *
+ * Returns true on success, false on failure.
+ */
+bool Extension<DCClass>::
+pack_required_field(Datagram &datagram, PyObject *distobj,
+                    const DCField *field) const {
+  DCPacker packer;
+  packer.begin_pack(field);
+  if (!pack_required_field(packer, distobj, field)) {
+    return false;
+  }
+  if (!packer.end_pack()) {
+    return false;
+  }
+
+  datagram.append_data(packer.get_data(), packer.get_length());
+  return true;
+}
+
+/**
+ * Looks up the current value of the indicated field by calling the
+ * appropriate get*() function, then packs that value into the packer.  This
+ * field is presumably either a required field or a specified optional field,
+ * and we are building up a datagram for the generate-with-required message.
+ *
+ * Returns true on success, false on failure.
+ */
+bool Extension<DCClass>::
+pack_required_field(DCPacker &packer, PyObject *distobj,
+                    const DCField *field) const {
+  using std::ostringstream;
+
+  const DCParameter *parameter = field->as_parameter();
+  if (parameter != nullptr) {
+    // This is the easy case: to pack a parameter, we just look on the class
+    // object for the data element.
+    std::string field_name = field->get_name();
+
+    if (!PyObject_HasAttrString(distobj, (char *)field_name.c_str())) {
+      // If the attribute is not defined, but the field has a default value
+      // specified, quietly pack the default value.
+      if (field->has_default_value()) {
+        packer.pack_default_value();
+        return true;
+      }
+
+      // If there is no default value specified, it's an error.
+      ostringstream strm;
+      strm << "Data element " << field_name
+           << ", required by dc file for dclass " << _this->get_name()
+           << ", not defined on object";
+      nassert_raise(strm.str());
+      return false;
+    }
+    PyObject *result =
+      PyObject_GetAttrString(distobj, (char *)field_name.c_str());
+    nassertr(result != nullptr, false);
+
+    // Now pack the value into the datagram.
+    bool pack_ok = invoke_extension((DCField *)parameter).pack_args(packer, result);
+    Py_DECREF(result);
+
+    return pack_ok;
+  }
+
+  if (field->as_molecular_field() != nullptr) {
+    ostringstream strm;
+    strm << "Cannot pack molecular field " << field->get_name()
+         << " for generate";
+    nassert_raise(strm.str());
+    return false;
+  }
+
+  const DCAtomicField *atom = field->as_atomic_field();
+  nassertr(atom != nullptr, false);
+
+  // We need to get the initial value of this field.  There isn't a good,
+  // robust way to get this; presently, we just mangle the "setFoo()" name of
+  // the required field into "getFoo()" and call that.
+  std::string setter_name = atom->get_name();
+
+  if (setter_name.empty()) {
+    ostringstream strm;
+    strm << "Required field is unnamed!";
+    nassert_raise(strm.str());
+    return false;
+  }
+
+  if (atom->get_num_elements() == 0) {
+    // It sure doesn't make sense to have a required field with no parameters.
+    // What data, exactly, is required?
+    ostringstream strm;
+    strm << "Required field " << setter_name << " has no parameters!";
+    nassert_raise(strm.str());
+    return false;
+  }
+
+  std::string getter_name = setter_name;
+  if (setter_name.substr(0, 3) == "set") {
+    // If the original method started with "set", we mangle this directly to
+    // "get".
+    getter_name[0] = 'g';
+
+  } else {
+    // Otherwise, we add a "get" prefix, and capitalize the next letter.
+    getter_name = "get" + setter_name;
+    getter_name[3] = toupper(getter_name[3]);
+  }
+
+  // Now we have to look up the getter on the distributed object and call it.
+  if (!PyObject_HasAttrString(distobj, (char *)getter_name.c_str())) {
+    // As above, if there's no getter but the field has a default value
+    // specified, quietly pack the default value.
+    if (field->has_default_value()) {
+      packer.pack_default_value();
+      return true;
+    }
+
+    // Otherwise, with no default value it's an error.
+    ostringstream strm;
+    strm << "Distributed class " << _this->get_name()
+         << " doesn't have getter named " << getter_name
+         << " to match required field " << setter_name;
+    nassert_raise(strm.str());
+    return false;
+  }
+  PyObject *func =
+    PyObject_GetAttrString(distobj, (char *)getter_name.c_str());
+  nassertr(func != nullptr, false);
+
+  PyObject *empty_args = PyTuple_New(0);
+  PyObject *result = PyObject_CallObject(func, empty_args);
+  Py_DECREF(empty_args);
+  Py_DECREF(func);
+  if (result == nullptr) {
+    // We don't set this as an exception, since presumably the Python method
+    // itself has already triggered a Python exception.
+    std::cerr << "Error when calling " << getter_name << "\n";
+    return false;
+  }
+
+  if (atom->get_num_elements() == 1) {
+    // In this case, we expect the getter to return one object, which we wrap
+    // up in a tuple.
+    PyObject *tuple = PyTuple_New(1);
+    PyTuple_SET_ITEM(tuple, 0, result);
+    result = tuple;
+
+  } else {
+    // Otherwise, it had better already be a sequence or tuple of some sort.
+    if (!PySequence_Check(result)) {
+      ostringstream strm;
+      strm << "Since dclass " << _this->get_name() << " method " << setter_name
+           << " is declared to have multiple parameters, Python function "
+           << getter_name << " must return a list or tuple.\n";
+      nassert_raise(strm.str());
+      return false;
+    }
+  }
+
+  // Now pack the arguments into the datagram.
+  bool pack_ok = invoke_extension((DCField *)atom).pack_args(packer, result);
+  Py_DECREF(result);
+
+  return pack_ok;
+}
+
+/**
+ * Generates a datagram containing the message necessary to send an update for
+ * the indicated distributed object from the client.
+ */
+Datagram Extension<DCClass>::
+client_format_update(const std::string &field_name, DOID_TYPE do_id,
+                     PyObject *args) const {
+  DCField *field = _this->get_field_by_name(field_name);
+  if (field == nullptr) {
+    std::ostringstream strm;
+    strm << "No field named " << field_name << " in class " << _this->get_name()
+         << "\n";
+    nassert_raise(strm.str());
+    return Datagram();
+  }
+
+  return invoke_extension(field).client_format_update(do_id, args);
+}
+
+/**
+ * Generates a datagram containing the message necessary to send an update for
+ * the indicated distributed object from the AI.
+ */
+Datagram Extension<DCClass>::
+ai_format_update(const std::string &field_name, DOID_TYPE do_id,
+                 CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, PyObject *args) const {
+  DCField *field = _this->get_field_by_name(field_name);
+  if (field == nullptr) {
+    std::ostringstream strm;
+    strm << "No field named " << field_name << " in class " << _this->get_name()
+         << "\n";
+    nassert_raise(strm.str());
+    return Datagram();
+  }
+
+  return invoke_extension(field).ai_format_update(do_id, to_id, from_id, args);
+}
+
+/**
+ * Generates a datagram containing the message necessary to send an update,
+ * using the indicated msg type for the indicated distributed object from the
+ * AI.
+ */
+Datagram Extension<DCClass>::
+ai_format_update_msg_type(const std::string &field_name, DOID_TYPE do_id,
+                          CHANNEL_TYPE to_id, CHANNEL_TYPE from_id,
+                          int msg_type, PyObject *args) const {
+  DCField *field = _this->get_field_by_name(field_name);
+  if (field == nullptr) {
+    std::ostringstream strm;
+    strm << "No field named " << field_name << " in class " << _this->get_name()
+         << "\n";
+    nassert_raise(strm.str());
+    return Datagram();
+  }
+
+  return invoke_extension(field).ai_format_update_msg_type(do_id, to_id, from_id, msg_type, args);
+}
+
+/**
+ * Generates a datagram containing the message necessary to generate a new
+ * distributed object from the client.  This requires querying the object for
+ * the initial value of its required fields.
+ *
+ * optional_fields is a list of fieldNames to generate in addition to the
+ * normal required fields.
+ *
+ * This method is only called by the CMU implementation.
+ */
+Datagram Extension<DCClass>::
+client_format_generate_CMU(PyObject *distobj, DOID_TYPE do_id,
+                           ZONEID_TYPE zone_id,
+                           PyObject *optional_fields) const {
+  DCPacker packer;
+
+  packer.raw_pack_uint16(CLIENT_OBJECT_GENERATE_CMU);
+
+  packer.raw_pack_uint32(zone_id);
+  packer.raw_pack_uint16(_this->_number);
+  packer.raw_pack_uint32(do_id);
+
+  // Specify all of the required fields.
+  int num_fields = _this->get_num_inherited_fields();
+  for (int i = 0; i < num_fields; ++i) {
+    DCField *field = _this->get_inherited_field(i);
+    if (field->is_required() && field->as_molecular_field() == nullptr) {
+      packer.begin_pack(field);
+      if (!pack_required_field(packer, distobj, field)) {
+        return Datagram();
+      }
+      packer.end_pack();
+    }
+  }
+
+  // Also specify the optional fields.
+  int num_optional_fields = 0;
+  if (PyObject_IsTrue(optional_fields)) {
+    num_optional_fields = PySequence_Size(optional_fields);
+  }
+  packer.raw_pack_uint16(num_optional_fields);
+
+  for (int i = 0; i < num_optional_fields; i++) {
+    PyObject *py_field_name = PySequence_GetItem(optional_fields, i);
+#if PY_MAJOR_VERSION >= 3
+    std::string field_name = PyUnicode_AsUTF8(py_field_name);
+#else
+    std::string field_name = PyString_AsString(py_field_name);
+#endif
+    Py_XDECREF(py_field_name);
+
+    DCField *field = _this->get_field_by_name(field_name);
+    if (field == nullptr) {
+      std::ostringstream strm;
+      strm << "No field named " << field_name << " in class " << _this->get_name()
+           << "\n";
+      nassert_raise(strm.str());
+      return Datagram();
+    }
+    packer.raw_pack_uint16(field->get_number());
+    packer.begin_pack(field);
+    if (!pack_required_field(packer, distobj, field)) {
+      return Datagram();
+    }
+    packer.end_pack();
+  }
+
+  return Datagram(packer.get_data(), packer.get_length());
+}
+
+/**
+ * Generates a datagram containing the message necessary to generate a new
+ * distributed object from the AI. This requires querying the object for the
+ * initial value of its required fields.
+ *
+ * optional_fields is a list of fieldNames to generate in addition to the
+ * normal required fields.
+ */
+Datagram Extension<DCClass>::
+ai_format_generate(PyObject *distobj, DOID_TYPE do_id,
+                   DOID_TYPE parent_id, ZONEID_TYPE zone_id,
+                   CHANNEL_TYPE district_channel_id, CHANNEL_TYPE from_channel_id,
+                   PyObject *optional_fields) const {
+  DCPacker packer;
+
+  packer.raw_pack_uint8(1);
+  packer.RAW_PACK_CHANNEL(district_channel_id);
+  packer.RAW_PACK_CHANNEL(from_channel_id);
+    // packer.raw_pack_uint8('A');
+
+  bool has_optional_fields = (PyObject_IsTrue(optional_fields) != 0);
+
+  if (has_optional_fields) {
+    packer.raw_pack_uint16(STATESERVER_CREATE_OBJECT_WITH_REQUIRED_OTHER);
+  } else {
+    packer.raw_pack_uint16(STATESERVER_CREATE_OBJECT_WITH_REQUIRED);
+  }
+
+  packer.raw_pack_uint32(do_id);
+  // Parent is a bit overloaded; this parent is not about inheritance, this
+  // one is about the visibility container parent, i.e.  the zone parent:
+  packer.raw_pack_uint32(parent_id);
+  packer.raw_pack_uint32(zone_id);
+  packer.raw_pack_uint16(_this->_number);
+
+  // Specify all of the required fields.
+  int num_fields = _this->get_num_inherited_fields();
+  for (int i = 0; i < num_fields; ++i) {
+    DCField *field = _this->get_inherited_field(i);
+    if (field->is_required() && field->as_molecular_field() == nullptr) {
+      packer.begin_pack(field);
+      if (!pack_required_field(packer, distobj, field)) {
+        return Datagram();
+      }
+      packer.end_pack();
+    }
+  }
+
+  // Also specify the optional fields.
+  if (has_optional_fields) {
+    int num_optional_fields = PySequence_Size(optional_fields);
+    packer.raw_pack_uint16(num_optional_fields);
+
+    for (int i = 0; i < num_optional_fields; ++i) {
+      PyObject *py_field_name = PySequence_GetItem(optional_fields, i);
+#if PY_MAJOR_VERSION >= 3
+      std::string field_name = PyUnicode_AsUTF8(py_field_name);
+#else
+      std::string field_name = PyString_AsString(py_field_name);
+#endif
+      Py_XDECREF(py_field_name);
+
+      DCField *field = _this->get_field_by_name(field_name);
+      if (field == nullptr) {
+        std::ostringstream strm;
+        strm << "No field named " << field_name << " in class "
+             << _this->get_name() << "\n";
+        nassert_raise(strm.str());
+        return Datagram();
+      }
+
+      packer.raw_pack_uint16(field->get_number());
+
+      packer.begin_pack(field);
+      if (!pack_required_field(packer, distobj, field)) {
+        return Datagram();
+      }
+      packer.end_pack();
+    }
+  }
+
+  return Datagram(packer.get_data(), packer.get_length());
+}
+
+/**
+ * Returns the PythonClassDefsImpl object stored on the DCClass object,
+ * creating it if it didn't yet exist.
+ */
+Extension<DCClass>::PythonClassDefsImpl *Extension<DCClass>::
+do_get_defs() const {
+  if (!_this->_python_class_defs) {
+    _this->_python_class_defs = new PythonClassDefsImpl();
+  }
+  return (PythonClassDefsImpl *)_this->_python_class_defs.p();
+}
+
+#endif  // HAVE_PYTHON

+ 93 - 0
direct/src/dcparser/dcClass_ext.h

@@ -0,0 +1,93 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file dcClass_ext.h
+ * @author CFSworks
+ * @date 2019-07-03
+ */
+
+#ifndef DCCLASS_EXT_H
+#define DCCLASS_EXT_H
+
+#include "dtoolbase.h"
+
+#ifdef HAVE_PYTHON
+
+#include "extension.h"
+#include "dcClass.h"
+#include "py_panda.h"
+
+/**
+ * This class defines the extension methods for DCClass, which are called
+ * instead of any C++ methods with the same prototype.
+ */
+template<>
+class Extension<DCClass> : public ExtensionBase<DCClass> {
+public:
+  bool has_class_def() const;
+  void set_class_def(PyObject *class_def);
+  PyObject *get_class_def() const;
+  bool has_owner_class_def() const;
+  void set_owner_class_def(PyObject *owner_class_def);
+  PyObject *get_owner_class_def() const;
+
+  void receive_update(PyObject *distobj, DatagramIterator &di) const;
+  void receive_update_broadcast_required(PyObject *distobj, DatagramIterator &di) const;
+  void receive_update_broadcast_required_owner(PyObject *distobj, DatagramIterator &di) const;
+  void receive_update_all_required(PyObject *distobj, DatagramIterator &di) const;
+  void receive_update_other(PyObject *distobj, DatagramIterator &di) const;
+
+  void direct_update(PyObject *distobj, const std::string &field_name,
+                     const vector_uchar &value_blob);
+  void direct_update(PyObject *distobj, const std::string &field_name,
+                     const Datagram &datagram);
+  bool pack_required_field(Datagram &datagram, PyObject *distobj,
+                           const DCField *field) const;
+  bool pack_required_field(DCPacker &packer, PyObject *distobj,
+                           const DCField *field) const;
+
+
+
+  Datagram client_format_update(const std::string &field_name,
+                                DOID_TYPE do_id, PyObject *args) const;
+  Datagram ai_format_update(const std::string &field_name, DOID_TYPE do_id,
+                            CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, PyObject *args) const;
+  Datagram ai_format_update_msg_type(const std::string &field_name, DOID_TYPE do_id,
+                            CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, int msg_type, PyObject *args) const;
+  Datagram ai_format_generate(PyObject *distobj, DOID_TYPE do_id,
+                              ZONEID_TYPE parent_id, ZONEID_TYPE zone_id,
+                              CHANNEL_TYPE district_channel_id,
+                              CHANNEL_TYPE from_channel_id,
+                              PyObject *optional_fields) const;
+  Datagram client_format_generate_CMU(PyObject *distobj, DOID_TYPE do_id,
+                                      ZONEID_TYPE zone_id,
+                                      PyObject *optional_fields) const;
+
+private:
+  /**
+   * Implementation of DCClass::PythonClassDefs which actually stores the
+   * Python pointers.  This needs to be defined here rather than on DCClass
+   * itself, since DCClass cannot include Python.h or call Python functions.
+   */
+  class PythonClassDefsImpl : public DCClass::PythonClassDefs {
+  public:
+    virtual ~PythonClassDefsImpl() {
+      Py_XDECREF(_class_def);
+      Py_XDECREF(_owner_class_def);
+    }
+
+    PyObject *_class_def = nullptr;
+    PyObject *_owner_class_def = nullptr;
+  };
+
+  PythonClassDefsImpl *do_get_defs() const;
+};
+
+#endif  // HAVE_PYTHON
+
+#endif  // DCCLASS_EXT_H

+ 6 - 313
direct/src/dcparser/dcField.cxx

@@ -18,16 +18,6 @@
 #include "hashGenerator.h"
 #include "hashGenerator.h"
 #include "dcmsgtypes.h"
 #include "dcmsgtypes.h"
 
 
-#ifdef HAVE_PYTHON
-#include "py_panda.h"
-#endif
-
-#ifdef WITHIN_PANDA
-#include "pStatTimer.h"
-#endif
-
-using std::string;
-
 /**
 /**
  *
  *
  */
  */
@@ -58,7 +48,7 @@ DCField() :
  *
  *
  */
  */
 DCField::
 DCField::
-DCField(const string &name, DCClass *dclass) :
+DCField(const std::string &name, DCClass *dclass) :
   DCPackerInterface(name),
   DCPackerInterface(name),
   _dclass(dclass)
   _dclass(dclass)
 #ifdef WITHIN_PANDA
 #ifdef WITHIN_PANDA
@@ -161,14 +151,14 @@ as_parameter() const {
  * string formatting it for human consumption.  Returns empty string if there
  * string formatting it for human consumption.  Returns empty string if there
  * is an error.
  * is an error.
  */
  */
-string DCField::
+std::string DCField::
 format_data(const vector_uchar &packed_data, bool show_field_names) {
 format_data(const vector_uchar &packed_data, bool show_field_names) {
   DCPacker packer;
   DCPacker packer;
   packer.set_unpack_data(packed_data);
   packer.set_unpack_data(packed_data);
   packer.begin_unpack(this);
   packer.begin_unpack(this);
-  string result = packer.unpack_and_format(show_field_names);
+  std::string result = packer.unpack_and_format(show_field_names);
   if (!packer.end_unpack()) {
   if (!packer.end_unpack()) {
-    return string();
+    return std::string();
   }
   }
   return result;
   return result;
 }
 }
@@ -179,7 +169,7 @@ format_data(const vector_uchar &packed_data, bool show_field_names) {
  * the corresponding packed data.  Returns empty string if there is an error.
  * the corresponding packed data.  Returns empty string if there is an error.
  */
  */
 vector_uchar DCField::
 vector_uchar DCField::
-parse_string(const string &formatted_string) {
+parse_string(const std::string &formatted_string) {
   DCPacker packer;
   DCPacker packer;
   packer.begin_pack(this);
   packer.begin_pack(this);
   if (!packer.parse_and_pack(formatted_string)) {
   if (!packer.parse_and_pack(formatted_string)) {
@@ -212,254 +202,6 @@ validate_ranges(const vector_uchar &packed_data) const {
   return (packer.get_num_unpacked_bytes() == packed_data.size());
   return (packer.get_num_unpacked_bytes() == packed_data.size());
 }
 }
 
 
-#ifdef HAVE_PYTHON
-/**
- * Packs the Python arguments from the indicated tuple into the packer.
- * Returns true on success, false on failure.
- *
- * It is assumed that the packer is currently positioned on this field.
- */
-bool DCField::
-pack_args(DCPacker &packer, PyObject *sequence) const {
-  nassertr(!packer.had_error(), false);
-  nassertr(packer.get_current_field() == this, false);
-
-  packer.pack_object(sequence);
-  if (!packer.had_error()) {
-    /*
-    cerr << "pack " << get_name() << get_pystr(sequence) << "\n";
-    */
-
-    return true;
-  }
-
-  if (!Notify::ptr()->has_assert_failed()) {
-    std::ostringstream strm;
-    PyObject *exc_type = PyExc_Exception;
-
-    if (as_parameter() != nullptr) {
-      // If it's a parameter-type field, the value may or may not be a
-      // sequence.
-      if (packer.had_pack_error()) {
-        strm << "Incorrect arguments to field: " << get_name()
-             << " = " << get_pystr(sequence);
-        exc_type = PyExc_TypeError;
-      } else {
-        strm << "Value out of range on field: " << get_name()
-             << " = " << get_pystr(sequence);
-        exc_type = PyExc_ValueError;
-      }
-
-    } else {
-      // If it's a molecular or atomic field, the value should be a sequence.
-      PyObject *tuple = PySequence_Tuple(sequence);
-      if (tuple == nullptr) {
-        strm << "Value for " << get_name() << " not a sequence: " \
-             << get_pystr(sequence);
-        exc_type = PyExc_TypeError;
-
-      } else {
-        if (packer.had_pack_error()) {
-          strm << "Incorrect arguments to field: " << get_name()
-               << get_pystr(sequence);
-          exc_type = PyExc_TypeError;
-        } else {
-          strm << "Value out of range on field: " << get_name()
-               << get_pystr(sequence);
-          exc_type = PyExc_ValueError;
-        }
-
-        Py_DECREF(tuple);
-      }
-    }
-
-    string message = strm.str();
-    PyErr_SetString(exc_type, message.c_str());
-  }
-  return false;
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Unpacks the values from the packer, beginning at the current point in the
- * unpack_buffer, into a Python tuple and returns the tuple.
- *
- * It is assumed that the packer is currently positioned on this field.
- */
-PyObject *DCField::
-unpack_args(DCPacker &packer) const {
-  nassertr(!packer.had_error(), nullptr);
-  nassertr(packer.get_current_field() == this, nullptr);
-
-  size_t start_byte = packer.get_num_unpacked_bytes();
-  PyObject *object = packer.unpack_object();
-
-  if (!packer.had_error()) {
-    // Successfully unpacked.
-    /*
-    cerr << "recv " << get_name() << get_pystr(object) << "\n";
-    */
-
-    return object;
-  }
-
-  if (!Notify::ptr()->has_assert_failed()) {
-    std::ostringstream strm;
-    PyObject *exc_type = PyExc_Exception;
-
-    if (packer.had_pack_error()) {
-      strm << "Data error unpacking field ";
-      output(strm, true);
-      size_t length = packer.get_unpack_length() - start_byte;
-      strm << "\nGot data (" << (int)length << " bytes):\n";
-      Datagram dg(packer.get_unpack_data() + start_byte, length);
-      dg.dump_hex(strm);
-      size_t error_byte = packer.get_num_unpacked_bytes() - start_byte;
-      strm << "Error detected on byte " << error_byte
-           << " (" << std::hex << error_byte << std::dec << " hex)";
-
-      exc_type = PyExc_RuntimeError;
-    } else {
-      strm << "Value outside specified range when unpacking field "
-           << get_name() << ": " << get_pystr(object);
-      exc_type = PyExc_ValueError;
-    }
-
-    string message = strm.str();
-    PyErr_SetString(exc_type, message.c_str());
-  }
-
-  Py_XDECREF(object);
-  return nullptr;
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Extracts the update message out of the datagram and applies it to the
- * indicated object by calling the appropriate method.
- */
-void DCField::
-receive_update(DCPacker &packer, PyObject *distobj) const {
-  if (as_parameter() != nullptr) {
-    // If it's a parameter-type field, just store a new value on the object.
-    PyObject *value = unpack_args(packer);
-    if (value != nullptr) {
-      PyObject_SetAttrString(distobj, (char *)_name.c_str(), value);
-    }
-    Py_DECREF(value);
-
-  } else {
-    // Otherwise, it must be an atomic or molecular field, so call the
-    // corresponding method.
-
-    if (!PyObject_HasAttrString(distobj, (char *)_name.c_str())) {
-      // If there's no Python method to receive this message, don't bother
-      // unpacking it to a Python tuple--just skip past the message.
-      packer.unpack_skip();
-
-    } else {
-      // Otherwise, get a Python tuple from the args and call the Python
-      // method.
-      PyObject *args = unpack_args(packer);
-
-      if (args != nullptr) {
-        PyObject *func = PyObject_GetAttrString(distobj, (char *)_name.c_str());
-        nassertv(func != nullptr);
-
-        PyObject *result;
-        {
-#ifdef WITHIN_PANDA
-          PStatTimer timer(((DCField *)this)->_field_update_pcollector);
-#endif
-          result = PyObject_CallObject(func, args);
-        }
-        Py_XDECREF(result);
-        Py_DECREF(func);
-        Py_DECREF(args);
-      }
-    }
-  }
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Generates a datagram containing the message necessary to send an update for
- * the indicated distributed object from the client.
- */
-Datagram DCField::
-client_format_update(DOID_TYPE do_id, PyObject *args) const {
-  DCPacker packer;
-
-  packer.raw_pack_uint16(CLIENT_OBJECT_SET_FIELD);
-  packer.raw_pack_uint32(do_id);
-  packer.raw_pack_uint16(_number);
-
-  packer.begin_pack(this);
-  pack_args(packer, args);
-  if (!packer.end_pack()) {
-    return Datagram();
-  }
-
-  return Datagram(packer.get_data(), packer.get_length());
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Generates a datagram containing the message necessary to send an update for
- * the indicated distributed object from the AI.
- */
-Datagram DCField::
-ai_format_update(DOID_TYPE do_id, CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, PyObject *args) const {
-  DCPacker packer;
-
-  packer.raw_pack_uint8(1);
-  packer.RAW_PACK_CHANNEL(to_id);
-  packer.RAW_PACK_CHANNEL(from_id);
-  packer.raw_pack_uint16(STATESERVER_OBJECT_SET_FIELD);
-  packer.raw_pack_uint32(do_id);
-  packer.raw_pack_uint16(_number);
-
-  packer.begin_pack(this);
-  pack_args(packer, args);
-  if (!packer.end_pack()) {
-    return Datagram();
-  }
-
-  return Datagram(packer.get_data(), packer.get_length());
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Generates a datagram containing the message necessary to send an update,
- * with the msg type, for the indicated distributed object from the AI.
- */
-Datagram DCField::
-ai_format_update_msg_type(DOID_TYPE do_id, CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, int msg_type, PyObject *args) const {
-  DCPacker packer;
-
-  packer.raw_pack_uint8(1);
-  packer.RAW_PACK_CHANNEL(to_id);
-  packer.RAW_PACK_CHANNEL(from_id);
-  packer.raw_pack_uint16(msg_type);
-  packer.raw_pack_uint32(do_id);
-  packer.raw_pack_uint16(_number);
-
-  packer.begin_pack(this);
-  pack_args(packer, args);
-  if (!packer.end_pack()) {
-    return Datagram();
-  }
-
-  return Datagram(packer.get_data(), packer.get_length());
-}
-#endif  // HAVE_PYTHON
-
-
 /**
 /**
  * Accumulates the properties of this field into the hash.
  * Accumulates the properties of this field into the hash.
  */
  */
@@ -499,62 +241,13 @@ pack_default_value(DCPackData &pack_data, bool &) const {
  * Sets the name of this field.
  * Sets the name of this field.
  */
  */
 void DCField::
 void DCField::
-set_name(const string &name) {
+set_name(const std::string &name) {
   DCPackerInterface::set_name(name);
   DCPackerInterface::set_name(name);
   if (_dclass != nullptr) {
   if (_dclass != nullptr) {
     _dclass->_dc_file->mark_inherited_fields_stale();
     _dclass->_dc_file->mark_inherited_fields_stale();
   }
   }
 }
 }
 
 
-#ifdef HAVE_PYTHON
-/**
- * Returns the string representation of the indicated Python object.
- */
-string DCField::
-get_pystr(PyObject *value) {
-  if (value == nullptr) {
-    return "(null)";
-  }
-
-  PyObject *str = PyObject_Str(value);
-  if (str != nullptr) {
-#if PY_MAJOR_VERSION >= 3
-    string result = PyUnicode_AsUTF8(str);
-#else
-    string result = PyString_AsString(str);
-#endif
-    Py_DECREF(str);
-    return result;
-  }
-
-  PyObject *repr = PyObject_Repr(value);
-  if (repr != nullptr) {
-#if PY_MAJOR_VERSION >= 3
-    string result = PyUnicode_AsUTF8(repr);
-#else
-    string result = PyString_AsString(repr);
-#endif
-    Py_DECREF(repr);
-    return result;
-  }
-
-  if (value->ob_type != nullptr) {
-    PyObject *typestr = PyObject_Str((PyObject *)(value->ob_type));
-    if (typestr != nullptr) {
-#if PY_MAJOR_VERSION >= 3
-      string result = PyUnicode_AsUTF8(typestr);
-#else
-      string result = PyString_AsString(typestr);
-#endif
-      Py_DECREF(typestr);
-      return result;
-    }
-  }
-
-  return "(invalid object)";
-}
-#endif  // HAVE_PYTHON
-
 /**
 /**
  * Recomputes the default value of the field by repacking it.
  * Recomputes the default value of the field by repacking it.
  */
  */

+ 13 - 15
direct/src/dcparser/dcField.h

@@ -17,10 +17,11 @@
 #include "dcbase.h"
 #include "dcbase.h"
 #include "dcPackerInterface.h"
 #include "dcPackerInterface.h"
 #include "dcKeywordList.h"
 #include "dcKeywordList.h"
-#include "dcPython.h"
 
 
 #ifdef WITHIN_PANDA
 #ifdef WITHIN_PANDA
 #include "pStatCollector.h"
 #include "pStatCollector.h"
+#include "extension.h"
+#include "datagram.h"
 #endif
 #endif
 
 
 class DCPacker;
 class DCPacker;
@@ -76,18 +77,17 @@ PUBLISHED:
   INLINE void output(std::ostream &out) const;
   INLINE void output(std::ostream &out) const;
   INLINE void write(std::ostream &out, int indent_level) const;
   INLINE void write(std::ostream &out, int indent_level) const;
 
 
-#ifdef HAVE_PYTHON
-  bool pack_args(DCPacker &packer, PyObject *sequence) const;
-  PyObject *unpack_args(DCPacker &packer) const;
+  EXTENSION(bool pack_args(DCPacker &packer, PyObject *sequence) const);
+  EXTENSION(PyObject *unpack_args(DCPacker &packer) const);
 
 
-  void receive_update(DCPacker &packer, PyObject *distobj) const;
+  EXTENSION(void receive_update(DCPacker &packer, PyObject *distobj) const);
 
 
-  Datagram client_format_update(DOID_TYPE do_id, PyObject *args) const;
-  Datagram ai_format_update(DOID_TYPE do_id, CHANNEL_TYPE to_id, CHANNEL_TYPE from_id,
-                            PyObject *args) const;
-  Datagram ai_format_update_msg_type(DOID_TYPE do_id, CHANNEL_TYPE to_id, CHANNEL_TYPE from_id,
-                            int msg_type, PyObject *args) const;
-#endif
+  EXTENSION(Datagram client_format_update(DOID_TYPE do_id, PyObject *args) const);
+  EXTENSION(Datagram ai_format_update(DOID_TYPE do_id, CHANNEL_TYPE to_id,
+                                      CHANNEL_TYPE from_id, PyObject *args) const);
+  EXTENSION(Datagram ai_format_update_msg_type(DOID_TYPE do_id, CHANNEL_TYPE to_id,
+                                               CHANNEL_TYPE from_id, int msg_type,
+                                               PyObject *args) const);
 
 
 public:
 public:
   virtual void output(std::ostream &out, bool brief) const=0;
   virtual void output(std::ostream &out, bool brief) const=0;
@@ -100,10 +100,6 @@ public:
   INLINE void set_class(DCClass *dclass);
   INLINE void set_class(DCClass *dclass);
   INLINE void set_default_value(vector_uchar default_value);
   INLINE void set_default_value(vector_uchar default_value);
 
 
-#ifdef HAVE_PYTHON
-  static std::string get_pystr(PyObject *value);
-#endif
-
 protected:
 protected:
   void refresh_default_value();
   void refresh_default_value();
 
 
@@ -119,6 +115,8 @@ private:
 
 
 #ifdef WITHIN_PANDA
 #ifdef WITHIN_PANDA
   PStatCollector _field_update_pcollector;
   PStatCollector _field_update_pcollector;
+
+  friend class Extension<DCField>;
 #endif
 #endif
 };
 };
 
 

+ 305 - 0
direct/src/dcparser/dcField_ext.cxx

@@ -0,0 +1,305 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file dcField_ext.cxx
+ * @author CFSworks
+ * @date 2019-07-03
+ */
+
+#include "dcField_ext.h"
+#include "dcPacker_ext.h"
+#include "dcmsgtypes.h"
+
+#include "datagram.h"
+#include "pStatTimer.h"
+
+#ifdef HAVE_PYTHON
+
+/**
+ * Packs the Python arguments from the indicated tuple into the packer.
+ * Returns true on success, false on failure.
+ *
+ * It is assumed that the packer is currently positioned on this field.
+ */
+bool Extension<DCField>::
+pack_args(DCPacker &packer, PyObject *sequence) const {
+  nassertr(!packer.had_error(), false);
+  nassertr(packer.get_current_field() == _this, false);
+
+  invoke_extension(&packer).pack_object(sequence);
+  if (!packer.had_error()) {
+    /*
+    cerr << "pack " << _this->get_name() << get_pystr(sequence) << "\n";
+    */
+
+    return true;
+  }
+
+  if (!Notify::ptr()->has_assert_failed()) {
+    std::ostringstream strm;
+    PyObject *exc_type = PyExc_Exception;
+
+    if (_this->as_parameter() != nullptr) {
+      // If it's a parameter-type field, the value may or may not be a
+      // sequence.
+      if (packer.had_pack_error()) {
+        strm << "Incorrect arguments to field: " << _this->get_name()
+             << " = " << get_pystr(sequence);
+        exc_type = PyExc_TypeError;
+      } else {
+        strm << "Value out of range on field: " << _this->get_name()
+             << " = " << get_pystr(sequence);
+        exc_type = PyExc_ValueError;
+      }
+
+    } else {
+      // If it's a molecular or atomic field, the value should be a sequence.
+      PyObject *tuple = PySequence_Tuple(sequence);
+      if (tuple == nullptr) {
+        strm << "Value for " << _this->get_name() << " not a sequence: " \
+             << get_pystr(sequence);
+        exc_type = PyExc_TypeError;
+
+      } else {
+        if (packer.had_pack_error()) {
+          strm << "Incorrect arguments to field: " << _this->get_name()
+               << get_pystr(sequence);
+          exc_type = PyExc_TypeError;
+        } else {
+          strm << "Value out of range on field: " << _this->get_name()
+               << get_pystr(sequence);
+          exc_type = PyExc_ValueError;
+        }
+
+        Py_DECREF(tuple);
+      }
+    }
+
+    std::string message = strm.str();
+    PyErr_SetString(exc_type, message.c_str());
+  }
+  return false;
+}
+
+/**
+ * Unpacks the values from the packer, beginning at the current point in the
+ * unpack_buffer, into a Python tuple and returns the tuple.
+ *
+ * It is assumed that the packer is currently positioned on this field.
+ */
+PyObject *Extension<DCField>::
+unpack_args(DCPacker &packer) const {
+  nassertr(!packer.had_error(), nullptr);
+  nassertr(packer.get_current_field() == _this, nullptr);
+
+  size_t start_byte = packer.get_num_unpacked_bytes();
+  PyObject *object = invoke_extension(&packer).unpack_object();
+
+  if (!packer.had_error()) {
+    // Successfully unpacked.
+    /*
+    cerr << "recv " << _this->get_name() << get_pystr(object) << "\n";
+    */
+
+    return object;
+  }
+
+  if (!Notify::ptr()->has_assert_failed()) {
+    std::ostringstream strm;
+    PyObject *exc_type = PyExc_Exception;
+
+    if (packer.had_pack_error()) {
+      strm << "Data error unpacking field ";
+      _this->output(strm, true);
+      size_t length = packer.get_unpack_length() - start_byte;
+      strm << "\nGot data (" << (int)length << " bytes):\n";
+      Datagram dg(packer.get_unpack_data() + start_byte, length);
+      dg.dump_hex(strm);
+      size_t error_byte = packer.get_num_unpacked_bytes() - start_byte;
+      strm << "Error detected on byte " << error_byte
+           << " (" << std::hex << error_byte << std::dec << " hex)";
+
+      exc_type = PyExc_RuntimeError;
+    } else {
+      strm << "Value outside specified range when unpacking field "
+           << _this->get_name() << ": " << get_pystr(object);
+      exc_type = PyExc_ValueError;
+    }
+
+    std::string message = strm.str();
+    PyErr_SetString(exc_type, message.c_str());
+  }
+
+  Py_XDECREF(object);
+  return nullptr;
+}
+
+/**
+ * Extracts the update message out of the datagram and applies it to the
+ * indicated object by calling the appropriate method.
+ */
+void Extension<DCField>::
+receive_update(DCPacker &packer, PyObject *distobj) const {
+  if (_this->as_parameter() != nullptr) {
+    // If it's a parameter-type field, just store a new value on the object.
+    PyObject *value = unpack_args(packer);
+    if (value != nullptr) {
+      PyObject_SetAttrString(distobj, (char *)_this->_name.c_str(), value);
+    }
+    Py_DECREF(value);
+
+  } else {
+    // Otherwise, it must be an atomic or molecular field, so call the
+    // corresponding method.
+
+    if (!PyObject_HasAttrString(distobj, (char *)_this->_name.c_str())) {
+      // If there's no Python method to receive this message, don't bother
+      // unpacking it to a Python tuple--just skip past the message.
+      packer.unpack_skip();
+
+    } else {
+      // Otherwise, get a Python tuple from the args and call the Python
+      // method.
+      PyObject *args = unpack_args(packer);
+
+      if (args != nullptr) {
+        PyObject *func = PyObject_GetAttrString(distobj, (char *)_this->_name.c_str());
+        nassertv(func != nullptr);
+
+        PyObject *result;
+        {
+#ifdef WITHIN_PANDA
+          PStatTimer timer(_this->_field_update_pcollector);
+#endif
+          result = PyObject_CallObject(func, args);
+        }
+        Py_XDECREF(result);
+        Py_DECREF(func);
+        Py_DECREF(args);
+      }
+    }
+  }
+}
+
+/**
+ * Generates a datagram containing the message necessary to send an update for
+ * the indicated distributed object from the client.
+ */
+Datagram Extension<DCField>::
+client_format_update(DOID_TYPE do_id, PyObject *args) const {
+  DCPacker packer;
+
+  packer.raw_pack_uint16(CLIENT_OBJECT_SET_FIELD);
+  packer.raw_pack_uint32(do_id);
+  packer.raw_pack_uint16(_this->_number);
+
+  packer.begin_pack(_this);
+  pack_args(packer, args);
+  if (!packer.end_pack()) {
+    return Datagram();
+  }
+
+  return Datagram(packer.get_data(), packer.get_length());
+}
+
+/**
+ * Generates a datagram containing the message necessary to send an update for
+ * the indicated distributed object from the AI.
+ */
+Datagram Extension<DCField>::
+ai_format_update(DOID_TYPE do_id, CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, PyObject *args) const {
+  DCPacker packer;
+
+  packer.raw_pack_uint8(1);
+  packer.RAW_PACK_CHANNEL(to_id);
+  packer.RAW_PACK_CHANNEL(from_id);
+  packer.raw_pack_uint16(STATESERVER_OBJECT_SET_FIELD);
+  packer.raw_pack_uint32(do_id);
+  packer.raw_pack_uint16(_this->_number);
+
+  packer.begin_pack(_this);
+  pack_args(packer, args);
+  if (!packer.end_pack()) {
+    return Datagram();
+  }
+
+  return Datagram(packer.get_data(), packer.get_length());
+}
+
+/**
+ * Generates a datagram containing the message necessary to send an update,
+ * with the msg type, for the indicated distributed object from the AI.
+ */
+Datagram Extension<DCField>::
+ai_format_update_msg_type(DOID_TYPE do_id, CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, int msg_type, PyObject *args) const {
+  DCPacker packer;
+
+  packer.raw_pack_uint8(1);
+  packer.RAW_PACK_CHANNEL(to_id);
+  packer.RAW_PACK_CHANNEL(from_id);
+  packer.raw_pack_uint16(msg_type);
+  packer.raw_pack_uint32(do_id);
+  packer.raw_pack_uint16(_this->_number);
+
+  packer.begin_pack(_this);
+  pack_args(packer, args);
+  if (!packer.end_pack()) {
+    return Datagram();
+  }
+
+  return Datagram(packer.get_data(), packer.get_length());
+}
+
+/**
+ * Returns the string representation of the indicated Python object.
+ */
+std::string Extension<DCField>::
+get_pystr(PyObject *value) {
+  if (value == nullptr) {
+    return "(null)";
+  }
+
+  PyObject *str = PyObject_Str(value);
+  if (str != nullptr) {
+#if PY_MAJOR_VERSION >= 3
+    std::string result = PyUnicode_AsUTF8(str);
+#else
+    std::string result = PyString_AsString(str);
+#endif
+    Py_DECREF(str);
+    return result;
+  }
+
+  PyObject *repr = PyObject_Repr(value);
+  if (repr != nullptr) {
+#if PY_MAJOR_VERSION >= 3
+    std::string result = PyUnicode_AsUTF8(repr);
+#else
+    std::string result = PyString_AsString(repr);
+#endif
+    Py_DECREF(repr);
+    return result;
+  }
+
+  if (value->ob_type != nullptr) {
+    PyObject *typestr = PyObject_Str((PyObject *)(value->ob_type));
+    if (typestr != nullptr) {
+#if PY_MAJOR_VERSION >= 3
+      std::string result = PyUnicode_AsUTF8(typestr);
+#else
+      std::string result = PyString_AsString(typestr);
+#endif
+      Py_DECREF(typestr);
+      return result;
+    }
+  }
+
+  return "(invalid object)";
+}
+
+#endif  // HAVE_PYTHON

+ 48 - 0
direct/src/dcparser/dcField_ext.h

@@ -0,0 +1,48 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file dcField_ext.h
+ * @author CFSworks
+ * @date 2019-07-03
+ */
+
+#ifndef DCFIELD_EXT_H
+#define DCFIELD_EXT_H
+
+#include "dtoolbase.h"
+
+#ifdef HAVE_PYTHON
+
+#include "extension.h"
+#include "dcField.h"
+#include "py_panda.h"
+
+/**
+ * This class defines the extension methods for DCField, which are called
+ * instead of any C++ methods with the same prototype.
+ */
+template<>
+class Extension<DCField> : public ExtensionBase<DCField> {
+public:
+  bool pack_args(DCPacker &packer, PyObject *sequence) const;
+  PyObject *unpack_args(DCPacker &packer) const;
+
+  void receive_update(DCPacker &packer, PyObject *distobj) const;
+
+  Datagram client_format_update(DOID_TYPE do_id, PyObject *args) const;
+  Datagram ai_format_update(DOID_TYPE do_id, CHANNEL_TYPE to_id, CHANNEL_TYPE from_id,
+                            PyObject *args) const;
+  Datagram ai_format_update_msg_type(DOID_TYPE do_id, CHANNEL_TYPE to_id, CHANNEL_TYPE from_id,
+                            int msg_type, PyObject *args) const;
+
+  static std::string get_pystr(PyObject *value);
+};
+
+#endif  // HAVE_PYTHON
+
+#endif  // DCFIELD_EXT_H

+ 0 - 508
direct/src/dcparser/dcPacker.cxx

@@ -19,10 +19,6 @@
 #include "dcSwitchParameter.h"
 #include "dcSwitchParameter.h"
 #include "dcClass.h"
 #include "dcClass.h"
 
 
-#ifdef HAVE_PYTHON
-#include "py_panda.h"
-#endif
-
 using std::istream;
 using std::istream;
 using std::istringstream;
 using std::istringstream;
 using std::ostream;
 using std::ostream;
@@ -622,335 +618,6 @@ unpack_skip() {
   }
   }
 }
 }
 
 
-#ifdef HAVE_PYTHON
-/**
- * Packs the Python object of whatever type into the packer.  Each numeric
- * object and string object maps to the corresponding pack_value() call; a
- * tuple or sequence maps to a push() followed by all of the tuple's contents
- * followed by a pop().
- */
-void DCPacker::
-pack_object(PyObject *object) {
-  nassertv(_mode == M_pack || _mode == M_repack);
-  DCPackType pack_type = get_pack_type();
-
-  // had to add this for basic 64 and unsigned data to get packed right .. Not
-  // sure if we can just do the rest this way..
-
- switch(pack_type)
-  {
-  case PT_int64:
-      if(PyLong_Check(object))
-      {
-            pack_int64(PyLong_AsLongLong(object));
-            return;
-      }
-#if PY_MAJOR_VERSION < 3
-      else if (PyInt_Check(object))
-      {
-            pack_int64(PyInt_AsLong(object));
-            return;
-      }
-#endif
-      break;
-  case PT_uint64:
-      if(PyLong_Check(object))
-      {
-            pack_uint64(PyLong_AsUnsignedLongLong(object));
-            return;
-      }
-#if PY_MAJOR_VERSION < 3
-      else if(PyInt_Check(object))
-      {
-            PyObject  *obj1 = PyNumber_Long(object);
-            pack_int(PyLong_AsUnsignedLongLong(obj1));
-            Py_DECREF(obj1);
-            return;
-      }
-#endif
-      break;
-  case PT_int:
-      if(PyLong_Check(object))
-      {
-            pack_int(PyLong_AsLong(object));
-            return;
-      }
-#if PY_MAJOR_VERSION < 3
-      else if (PyInt_Check(object))
-      {
-            pack_int(PyInt_AsLong(object));
-            return;
-      }
-#endif
-      break;
-  case PT_uint:
-      if(PyLong_Check(object))
-      {
-            pack_uint(PyLong_AsUnsignedLong(object));
-            return;
-      }
-#if PY_MAJOR_VERSION < 3
-      else if (PyInt_Check(object))
-      {
-            PyObject *obj1 = PyNumber_Long(object);
-            pack_uint(PyLong_AsUnsignedLong(obj1));
-            Py_DECREF(obj1);
-            return;
-      }
-#endif
-      break;
-  default:
-      break;
-  }
-  if (PyLong_Check(object)) {
-    pack_int(PyLong_AsLong(object));
-#if PY_MAJOR_VERSION < 3
-  } else if (PyInt_Check(object)) {
-    pack_int(PyInt_AS_LONG(object));
-#endif
-  } else if (PyFloat_Check(object)) {
-    pack_double(PyFloat_AS_DOUBLE(object));
-  } else if (PyLong_Check(object)) {
-    pack_int64(PyLong_AsLongLong(object));
-#if PY_MAJOR_VERSION >= 3
-  } else if (PyUnicode_Check(object)) {
-    const char *buffer;
-    Py_ssize_t length;
-    buffer = PyUnicode_AsUTF8AndSize(object, &length);
-    if (buffer) {
-      pack_string(string(buffer, length));
-    }
-  } else if (PyBytes_Check(object)) {
-    const unsigned char *buffer;
-    Py_ssize_t length;
-    PyBytes_AsStringAndSize(object, (char **)&buffer, &length);
-    if (buffer) {
-      pack_blob(vector_uchar(buffer, buffer + length));
-    }
-#else
-  } else if (PyString_Check(object) || PyUnicode_Check(object)) {
-    char *buffer;
-    Py_ssize_t length;
-    PyString_AsStringAndSize(object, &buffer, &length);
-    if (buffer) {
-      pack_string(string(buffer, length));
-    }
-#endif
-  } else {
-    // For some reason, PySequence_Check() is incorrectly reporting that a
-    // class instance is a sequence, even if it doesn't provide __len__, so we
-    // double-check by testing for __len__ explicitly.
-    bool is_sequence =
-      (PySequence_Check(object) != 0) &&
-      (PyObject_HasAttrString(object, "__len__") != 0);
-    bool is_instance = false;
-
-    const DCClass *dclass = nullptr;
-    const DCPackerInterface *current_field = get_current_field();
-    if (current_field != nullptr) {
-      const DCClassParameter *class_param = get_current_field()->as_class_parameter();
-      if (class_param != nullptr) {
-        dclass = class_param->get_class();
-
-        if (dclass->has_class_def()) {
-          PyObject *class_def = dclass->get_class_def();
-          is_instance = (PyObject_IsInstance(object, dclass->get_class_def()) != 0);
-          Py_DECREF(class_def);
-        }
-      }
-    }
-
-    // If dclass is not NULL, the packer is expecting a class object.  There
-    // are then two cases: (1) the user has supplied a matching class object,
-    // or (2) the user has supplied a sequence object.  Unfortunately, it may
-    // be difficult to differentiate these two cases, since a class object may
-    // also be a sequence object.
-
-    // The rule to differentiate them is:
-
-    // (1) If the supplied class object is an instance of the expected class
-    // object, it is considered to be a class object.
-
-    // (2) Otherwise, if the supplied class object has a __len__() method
-    // (i.e.  PySequence_Check() returns true), then it is considered to be a
-    // sequence.
-
-    // (3) Otherwise, it is considered to be a class object.
-
-    if (dclass != nullptr && (is_instance || !is_sequence)) {
-      // The supplied object is either an instance of the expected class
-      // object, or it is not a sequence--this is case (1) or (3).
-      pack_class_object(dclass, object);
-    } else if (is_sequence) {
-      // The supplied object is not an instance of the expected class object,
-      // but it is a sequence.  This is case (2).
-      push();
-      int size = PySequence_Size(object);
-      for (int i = 0; i < size; ++i) {
-        PyObject *element = PySequence_GetItem(object, i);
-        if (element != nullptr) {
-          pack_object(element);
-          Py_DECREF(element);
-        } else {
-          std::cerr << "Unable to extract item " << i << " from sequence.\n";
-        }
-      }
-      pop();
-    } else {
-      // The supplied object is not a sequence, and we weren't expecting a
-      // class parameter.  This is none of the above, an error.
-      ostringstream strm;
-      strm << "Don't know how to pack object: "
-           << DCField::get_pystr(object);
-      nassert_raise(strm.str());
-      _pack_error = true;
-    }
-  }
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Unpacks a Python object of the appropriate type from the stream for the
- * current field.  This may be an integer or a string for a simple field
- * object; if the current field represents a list of fields it will be a
- * tuple.
- */
-PyObject *DCPacker::
-unpack_object() {
-  PyObject *object = nullptr;
-
-  DCPackType pack_type = get_pack_type();
-
-  switch (pack_type) {
-  case PT_invalid:
-    object = Py_None;
-    Py_INCREF(object);
-    unpack_skip();
-    break;
-
-  case PT_double:
-    {
-      double value = unpack_double();
-      object = PyFloat_FromDouble(value);
-    }
-    break;
-
-  case PT_int:
-    {
-      int value = unpack_int();
-#if PY_MAJOR_VERSION >= 3
-      object = PyLong_FromLong(value);
-#else
-      object = PyInt_FromLong(value);
-#endif
-    }
-    break;
-
-  case PT_uint:
-    {
-      unsigned int value = unpack_uint();
-#if PY_MAJOR_VERSION >= 3
-      object = PyLong_FromLong(value);
-#else
-      if (value & 0x80000000) {
-        object = PyLong_FromUnsignedLong(value);
-      } else {
-        object = PyInt_FromLong(value);
-      }
-#endif
-    }
-    break;
-
-  case PT_int64:
-    {
-      int64_t value = unpack_int64();
-      object = PyLong_FromLongLong(value);
-    }
-    break;
-
-  case PT_uint64:
-    {
-      uint64_t value = unpack_uint64();
-      object = PyLong_FromUnsignedLongLong(value);
-    }
-    break;
-
-  case PT_blob:
-#if PY_MAJOR_VERSION >= 3
-    {
-      string str;
-      unpack_string(str);
-      object = PyBytes_FromStringAndSize(str.data(), str.size());
-    }
-    break;
-#endif
-    // On Python 2, fall through to below.
-
-  case PT_string:
-    {
-      string str;
-      unpack_string(str);
-#if PY_MAJOR_VERSION >= 3
-      object = PyUnicode_FromStringAndSize(str.data(), str.size());
-#else
-      object = PyString_FromStringAndSize(str.data(), str.size());
-#endif
-    }
-    break;
-
-  case PT_class:
-    {
-      const DCClassParameter *class_param = get_current_field()->as_class_parameter();
-      if (class_param != nullptr) {
-        const DCClass *dclass = class_param->get_class();
-        if (dclass->has_class_def()) {
-          // If we know what kind of class object this is and it has a valid
-          // constructor, create the class object instead of just a tuple.
-          object = unpack_class_object(dclass);
-          if (object == nullptr) {
-            std::cerr << "Unable to construct object of class "
-                 << dclass->get_name() << "\n";
-          } else {
-            break;
-          }
-        }
-      }
-    }
-    // Fall through (if no constructor)
-
-    // If we don't know what kind of class object it is, or it doesn't have a
-    // constructor, fall through and make a tuple.
-  default:
-    {
-      // First, build up a list from the nested objects.
-      object = PyList_New(0);
-
-      push();
-      while (more_nested_fields()) {
-        PyObject *element = unpack_object();
-        PyList_Append(object, element);
-        Py_DECREF(element);
-      }
-      pop();
-
-      if (pack_type != PT_array) {
-        // For these other kinds of objects, we'll convert the list into a
-        // tuple.
-        PyObject *tuple = PyList_AsTuple(object);
-        Py_DECREF(object);
-        object = tuple;
-      }
-    }
-    break;
-  }
-
-  nassertr(object != nullptr, nullptr);
-  return object;
-}
-#endif  // HAVE_PYTHON
-
-
 /**
 /**
  * Parses an object's value according to the DC file syntax (e.g.  as a
  * Parses an object's value according to the DC file syntax (e.g.  as a
  * default value string) and packs it.  Returns true on success, false on a
  * default value string) and packs it.  Returns true on success, false on a
@@ -1206,178 +873,3 @@ clear_stack() {
     _stack = next;
     _stack = next;
   }
   }
 }
 }
-
-#ifdef HAVE_PYTHON
-/**
- * Given that the current element is a ClassParameter for a Python class
- * object, try to extract the appropriate values from the class object and
- * pack in.
- */
-void DCPacker::
-pack_class_object(const DCClass *dclass, PyObject *object) {
-  push();
-  while (more_nested_fields() && !_pack_error) {
-    const DCField *field = get_current_field()->as_field();
-    nassertv(field != nullptr);
-    get_class_element(dclass, object, field);
-  }
-  pop();
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-/**
- * Given that the current element is a ClassParameter for a Python class for
- * which we have a valid constructor, unpack it and fill in its values.
- */
-PyObject *DCPacker::
-unpack_class_object(const DCClass *dclass) {
-  PyObject *class_def = dclass->get_class_def();
-  nassertr(class_def != nullptr, nullptr);
-
-  PyObject *object = nullptr;
-
-  if (!dclass->has_constructor()) {
-    // If the class uses a default constructor, go ahead and create the Python
-    // object for it now.
-    object = PyObject_CallObject(class_def, nullptr);
-    if (object == nullptr) {
-      return nullptr;
-    }
-  }
-
-  push();
-  if (object == nullptr && more_nested_fields()) {
-    // The first nested field will be the constructor.
-    const DCField *field = get_current_field()->as_field();
-    nassertr(field != nullptr, object);
-    nassertr(field == dclass->get_constructor(), object);
-
-    set_class_element(class_def, object, field);
-
-    // By now, the object should have been constructed.
-    if (object == nullptr) {
-      return nullptr;
-    }
-  }
-  while (more_nested_fields()) {
-    const DCField *field = get_current_field()->as_field();
-    nassertr(field != nullptr, object);
-
-    set_class_element(class_def, object, field);
-  }
-  pop();
-
-  return object;
-}
-#endif  // HAVE_PYTHON
-
-
-#ifdef HAVE_PYTHON
-/**
- * Unpacks the current element and stuffs it on the Python class object in
- * whatever way is appropriate.
- */
-void DCPacker::
-set_class_element(PyObject *class_def, PyObject *&object,
-                  const DCField *field) {
-  string field_name = field->get_name();
-  DCPackType pack_type = get_pack_type();
-
-  if (field_name.empty()) {
-    switch (pack_type) {
-    case PT_class:
-    case PT_switch:
-      // If the field has no name, but it is one of these container objects,
-      // we want to unpack its nested objects directly into the class.
-      push();
-      while (more_nested_fields()) {
-        const DCField *field = get_current_field()->as_field();
-        nassertv(field != nullptr);
-        nassertv(object != nullptr);
-        set_class_element(class_def, object, field);
-      }
-      pop();
-      break;
-
-    default:
-      // Otherwise, we just skip over the field.
-      unpack_skip();
-    }
-
-  } else {
-    // If the field does have a name, we will want to store it on the class,
-    // either by calling a method (for a PT_field pack_type) or by setting a
-    // value (for any other kind of pack_type).
-
-    PyObject *element = unpack_object();
-
-    if (pack_type == PT_field) {
-      if (object == nullptr) {
-        // If the object hasn't been constructed yet, assume this is the
-        // constructor.
-        object = PyObject_CallObject(class_def, element);
-
-      } else {
-        if (PyObject_HasAttrString(object, (char *)field_name.c_str())) {
-          PyObject *func = PyObject_GetAttrString(object, (char *)field_name.c_str());
-          if (func != nullptr) {
-            PyObject *result = PyObject_CallObject(func, element);
-            Py_XDECREF(result);
-            Py_DECREF(func);
-          }
-        }
-      }
-
-    } else {
-      nassertv(object != nullptr);
-      PyObject_SetAttrString(object, (char *)field_name.c_str(), element);
-    }
-
-    Py_DECREF(element);
-  }
-}
-#endif  // HAVE_PYTHON
-
-
-#ifdef HAVE_PYTHON
-/**
- * Gets the current element from the Python object and packs it.
- */
-void DCPacker::
-get_class_element(const DCClass *dclass, PyObject *object,
-                  const DCField *field) {
-  string field_name = field->get_name();
-  DCPackType pack_type = get_pack_type();
-
-  if (field_name.empty()) {
-    switch (pack_type) {
-    case PT_class:
-    case PT_switch:
-      // If the field has no name, but it is one of these container objects,
-      // we want to get its nested objects directly from the class.
-      push();
-      while (more_nested_fields() && !_pack_error) {
-        const DCField *field = get_current_field()->as_field();
-        nassertv(field != nullptr);
-        get_class_element(dclass, object, field);
-      }
-      pop();
-      break;
-
-    default:
-      // Otherwise, we just pack the default value.
-      pack_default_value();
-    }
-
-  } else {
-    // If the field does have a name, we will want to get it from the class
-    // and pack it.  It just so happens that there's already a method that
-    // does this on DCClass.
-
-    if (!dclass->pack_required_field(*this, object, field)) {
-      _pack_error = true;
-    }
-  }
-}
-#endif  // HAVE_PYTHON

+ 17 - 14
direct/src/dcparser/dcPacker.h

@@ -19,7 +19,10 @@
 #include "dcSubatomicType.h"
 #include "dcSubatomicType.h"
 #include "dcPackData.h"
 #include "dcPackData.h"
 #include "dcPackerCatalog.h"
 #include "dcPackerCatalog.h"
-#include "dcPython.h"
+
+#ifdef WITHIN_PANDA
+#include "extension.h"
+#endif
 
 
 class DCClass;
 class DCClass;
 class DCSwitchParameter;
 class DCSwitchParameter;
@@ -104,10 +107,8 @@ public:
 
 
 PUBLISHED:
 PUBLISHED:
 
 
-#ifdef HAVE_PYTHON
-  void pack_object(PyObject *object);
-  PyObject *unpack_object();
-#endif
+  EXTENSION(void pack_object(PyObject *object));
+  EXTENSION(PyObject *unpack_object());
 
 
   bool parse_and_pack(const std::string &formatted_object);
   bool parse_and_pack(const std::string &formatted_object);
   bool parse_and_pack(std::istream &in);
   bool parse_and_pack(std::istream &in);
@@ -195,14 +196,12 @@ private:
   void clear();
   void clear();
   void clear_stack();
   void clear_stack();
 
 
-#ifdef HAVE_PYTHON
-  void pack_class_object(const DCClass *dclass, PyObject *object);
-  PyObject *unpack_class_object(const DCClass *dclass);
-  void set_class_element(PyObject *class_def, PyObject *&object,
-                         const DCField *field);
-  void get_class_element(const DCClass *dclass, PyObject *object,
-                         const DCField *field);
-#endif
+  EXTENSION(void pack_class_object(const DCClass *dclass, PyObject *object));
+  EXTENSION(PyObject *unpack_class_object(const DCClass *dclass));
+  EXTENSION(void set_class_element(PyObject *class_def, PyObject *&object,
+                                   const DCField *field));
+  EXTENSION(void get_class_element(const DCClass *dclass, PyObject *object,
+                                   const DCField *field));
 
 
 private:
 private:
   enum Mode {
   enum Mode {
@@ -223,7 +222,7 @@ private:
   const DCPackerCatalog *_catalog;
   const DCPackerCatalog *_catalog;
   const DCPackerCatalog::LiveCatalog *_live_catalog;
   const DCPackerCatalog::LiveCatalog *_live_catalog;
 
 
-  class StackElement {
+  class EXPCL_DIRECT_DCPARSER StackElement {
   public:
   public:
     // As an optimization, we implement operator new and delete here to
     // As an optimization, we implement operator new and delete here to
     // minimize allocation overhead during push() and pop().
     // minimize allocation overhead during push() and pop().
@@ -258,6 +257,10 @@ private:
   bool _parse_error;
   bool _parse_error;
   bool _pack_error;
   bool _pack_error;
   bool _range_error;
   bool _range_error;
+
+#ifdef WITHIN_PANDA
+  friend class Extension<DCPacker>;
+#endif
 };
 };
 
 
 #include "dcPacker.I"
 #include "dcPacker.I"

+ 508 - 0
direct/src/dcparser/dcPacker_ext.cxx

@@ -0,0 +1,508 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file dcPacker_ext.cxx
+ * @author CFSworks
+ * @date 2019-07-03
+ */
+
+#include "dcPacker_ext.h"
+#include "dcClass_ext.h"
+#include "dcField_ext.h"
+
+#include "dcClassParameter.h"
+
+#ifdef HAVE_PYTHON
+
+/**
+ * Packs the Python object of whatever type into the packer.  Each numeric
+ * object and string object maps to the corresponding pack_value() call; a
+ * tuple or sequence maps to a push() followed by all of the tuple's contents
+ * followed by a pop().
+ */
+void Extension<DCPacker>::
+pack_object(PyObject *object) {
+  nassertv(_this->_mode == DCPacker::Mode::M_pack ||
+           _this->_mode == DCPacker::Mode::M_repack);
+  DCPackType pack_type = _this->get_pack_type();
+
+  // had to add this for basic 64 and unsigned data to get packed right .. Not
+  // sure if we can just do the rest this way..
+
+  switch(pack_type) {
+  case PT_int64:
+    if (PyLong_Check(object)) {
+      _this->pack_int64(PyLong_AsLongLong(object));
+      return;
+    }
+#if PY_MAJOR_VERSION < 3
+    else if (PyInt_Check(object)) {
+      _this->pack_int64(PyInt_AsLong(object));
+      return;
+    }
+#endif
+    break;
+
+  case PT_uint64:
+    if (PyLong_Check(object)) {
+      _this->pack_uint64(PyLong_AsUnsignedLongLong(object));
+      return;
+    }
+#if PY_MAJOR_VERSION < 3
+    else if (PyInt_Check(object)) {
+      PyObject *obj1 = PyNumber_Long(object);
+      _this->pack_int(PyLong_AsUnsignedLongLong(obj1));
+      Py_DECREF(obj1);
+      return;
+    }
+#endif
+    break;
+
+  case PT_int:
+    if (PyLong_Check(object)) {
+      _this->pack_int(PyLong_AsLong(object));
+      return;
+    }
+#if PY_MAJOR_VERSION < 3
+    else if (PyInt_Check(object)) {
+      _this->pack_int(PyInt_AsLong(object));
+      return;
+    }
+#endif
+    break;
+
+  case PT_uint:
+    if (PyLong_Check(object)) {
+      _this->pack_uint(PyLong_AsUnsignedLong(object));
+      return;
+    }
+#if PY_MAJOR_VERSION < 3
+    else if (PyInt_Check(object)) {
+      PyObject *obj1 = PyNumber_Long(object);
+      _this->pack_uint(PyLong_AsUnsignedLong(obj1));
+      Py_DECREF(obj1);
+      return;
+    }
+#endif
+    break;
+
+  default:
+    break;
+  }
+
+  if (PyLong_Check(object)) {
+    _this->pack_int(PyLong_AsLong(object));
+#if PY_MAJOR_VERSION < 3
+  } else if (PyInt_Check(object)) {
+    _this->pack_int(PyInt_AS_LONG(object));
+#endif
+  } else if (PyFloat_Check(object)) {
+    _this->pack_double(PyFloat_AS_DOUBLE(object));
+  } else if (PyLong_Check(object)) {
+    _this->pack_int64(PyLong_AsLongLong(object));
+#if PY_MAJOR_VERSION >= 3
+  } else if (PyUnicode_Check(object)) {
+    const char *buffer;
+    Py_ssize_t length;
+    buffer = PyUnicode_AsUTF8AndSize(object, &length);
+    if (buffer) {
+      _this->pack_string(std::string(buffer, length));
+    }
+  } else if (PyBytes_Check(object)) {
+    const unsigned char *buffer;
+    Py_ssize_t length;
+    PyBytes_AsStringAndSize(object, (char **)&buffer, &length);
+    if (buffer) {
+      _this->pack_blob(vector_uchar(buffer, buffer + length));
+    }
+#else
+  } else if (PyString_Check(object) || PyUnicode_Check(object)) {
+    char *buffer;
+    Py_ssize_t length;
+    PyString_AsStringAndSize(object, &buffer, &length);
+    if (buffer) {
+      _this->pack_string(std::string(buffer, length));
+    }
+#endif
+  } else {
+    // For some reason, PySequence_Check() is incorrectly reporting that a
+    // class instance is a sequence, even if it doesn't provide __len__, so we
+    // double-check by testing for __len__ explicitly.
+    bool is_sequence =
+      (PySequence_Check(object) != 0) &&
+      (PyObject_HasAttrString(object, "__len__") != 0);
+    bool is_instance = false;
+
+    const DCClass *dclass = nullptr;
+    const DCPackerInterface *current_field = _this->get_current_field();
+    if (current_field != nullptr) {
+      const DCClassParameter *class_param = _this->get_current_field()->as_class_parameter();
+      if (class_param != nullptr) {
+        dclass = class_param->get_class();
+
+        if (invoke_extension(dclass).has_class_def()) {
+          PyObject *class_def = invoke_extension(dclass).get_class_def();
+          is_instance = (PyObject_IsInstance(object, invoke_extension(dclass).get_class_def()) != 0);
+          Py_DECREF(class_def);
+        }
+      }
+    }
+
+    // If dclass is not NULL, the packer is expecting a class object.  There
+    // are then two cases: (1) the user has supplied a matching class object,
+    // or (2) the user has supplied a sequence object.  Unfortunately, it may
+    // be difficult to differentiate these two cases, since a class object may
+    // also be a sequence object.
+
+    // The rule to differentiate them is:
+
+    // (1) If the supplied class object is an instance of the expected class
+    // object, it is considered to be a class object.
+
+    // (2) Otherwise, if the supplied class object has a __len__() method
+    // (i.e.  PySequence_Check() returns true), then it is considered to be a
+    // sequence.
+
+    // (3) Otherwise, it is considered to be a class object.
+
+    if (dclass != nullptr && (is_instance || !is_sequence)) {
+      // The supplied object is either an instance of the expected class
+      // object, or it is not a sequence--this is case (1) or (3).
+      pack_class_object(dclass, object);
+    } else if (is_sequence) {
+      // The supplied object is not an instance of the expected class object,
+      // but it is a sequence.  This is case (2).
+      _this->push();
+      int size = PySequence_Size(object);
+      for (int i = 0; i < size; ++i) {
+        PyObject *element = PySequence_GetItem(object, i);
+        if (element != nullptr) {
+          pack_object(element);
+          Py_DECREF(element);
+        } else {
+          std::cerr << "Unable to extract item " << i << " from sequence.\n";
+        }
+      }
+      _this->pop();
+    } else {
+      // The supplied object is not a sequence, and we weren't expecting a
+      // class parameter.  This is none of the above, an error.
+      std::ostringstream strm;
+      strm << "Don't know how to pack object: "
+           << Extension<DCField>::get_pystr(object);
+      nassert_raise(strm.str());
+      _this->_pack_error = true;
+    }
+  }
+}
+
+/**
+ * Unpacks a Python object of the appropriate type from the stream for the
+ * current field.  This may be an integer or a string for a simple field
+ * object; if the current field represents a list of fields it will be a
+ * tuple.
+ */
+PyObject *Extension<DCPacker>::
+unpack_object() {
+  PyObject *object = nullptr;
+
+  DCPackType pack_type = _this->get_pack_type();
+
+  switch (pack_type) {
+  case PT_invalid:
+    object = Py_None;
+    Py_INCREF(object);
+    _this->unpack_skip();
+    break;
+
+  case PT_double:
+    {
+      double value = _this->unpack_double();
+      object = PyFloat_FromDouble(value);
+    }
+    break;
+
+  case PT_int:
+    {
+      int value = _this->unpack_int();
+#if PY_MAJOR_VERSION >= 3
+      object = PyLong_FromLong(value);
+#else
+      object = PyInt_FromLong(value);
+#endif
+    }
+    break;
+
+  case PT_uint:
+    {
+      unsigned int value = _this->unpack_uint();
+#if PY_MAJOR_VERSION >= 3
+      object = PyLong_FromLong(value);
+#else
+      if (value & 0x80000000) {
+        object = PyLong_FromUnsignedLong(value);
+      } else {
+        object = PyInt_FromLong(value);
+      }
+#endif
+    }
+    break;
+
+  case PT_int64:
+    {
+      int64_t value = _this->unpack_int64();
+      object = PyLong_FromLongLong(value);
+    }
+    break;
+
+  case PT_uint64:
+    {
+      uint64_t value = _this->unpack_uint64();
+      object = PyLong_FromUnsignedLongLong(value);
+    }
+    break;
+
+  case PT_blob:
+#if PY_MAJOR_VERSION >= 3
+    {
+      std::string str;
+      _this->unpack_string(str);
+      object = PyBytes_FromStringAndSize(str.data(), str.size());
+    }
+    break;
+#endif
+    // On Python 2, fall through to below.
+
+  case PT_string:
+    {
+      std::string str;
+      _this->unpack_string(str);
+#if PY_MAJOR_VERSION >= 3
+      object = PyUnicode_FromStringAndSize(str.data(), str.size());
+#else
+      object = PyString_FromStringAndSize(str.data(), str.size());
+#endif
+    }
+    break;
+
+  case PT_class:
+    {
+      const DCClassParameter *class_param = _this->get_current_field()->as_class_parameter();
+      if (class_param != nullptr) {
+        const DCClass *dclass = class_param->get_class();
+        if (invoke_extension(dclass).has_class_def()) {
+          // If we know what kind of class object this is and it has a valid
+          // constructor, create the class object instead of just a tuple.
+          object = unpack_class_object(dclass);
+          if (object == nullptr) {
+            std::cerr << "Unable to construct object of class "
+                 << dclass->get_name() << "\n";
+          } else {
+            break;
+          }
+        }
+      }
+    }
+    // Fall through (if no constructor)
+
+    // If we don't know what kind of class object it is, or it doesn't have a
+    // constructor, fall through and make a tuple.
+  default:
+    {
+      // First, build up a list from the nested objects.
+      object = PyList_New(0);
+
+      _this->push();
+      while (_this->more_nested_fields()) {
+        PyObject *element = unpack_object();
+        PyList_Append(object, element);
+        Py_DECREF(element);
+      }
+      _this->pop();
+
+      if (pack_type != PT_array) {
+        // For these other kinds of objects, we'll convert the list into a
+        // tuple.
+        PyObject *tuple = PyList_AsTuple(object);
+        Py_DECREF(object);
+        object = tuple;
+      }
+    }
+    break;
+  }
+
+  nassertr(object != nullptr, nullptr);
+  return object;
+}
+
+/**
+ * Given that the current element is a ClassParameter for a Python class
+ * object, try to extract the appropriate values from the class object and
+ * pack in.
+ */
+void Extension<DCPacker>::
+pack_class_object(const DCClass *dclass, PyObject *object) {
+  _this->push();
+  while (_this->more_nested_fields() && !_this->_pack_error) {
+    const DCField *field = _this->get_current_field()->as_field();
+    nassertv(field != nullptr);
+    get_class_element(dclass, object, field);
+  }
+  _this->pop();
+}
+
+/**
+ * Given that the current element is a ClassParameter for a Python class for
+ * which we have a valid constructor, unpack it and fill in its values.
+ */
+PyObject *Extension<DCPacker>::
+unpack_class_object(const DCClass *dclass) {
+  PyObject *class_def = invoke_extension(dclass).get_class_def();
+  nassertr(class_def != nullptr, nullptr);
+
+  PyObject *object = nullptr;
+
+  if (!dclass->has_constructor()) {
+    // If the class uses a default constructor, go ahead and create the Python
+    // object for it now.
+    object = PyObject_CallObject(class_def, nullptr);
+    if (object == nullptr) {
+      return nullptr;
+    }
+  }
+
+  _this->push();
+  if (object == nullptr && _this->more_nested_fields()) {
+    // The first nested field will be the constructor.
+    const DCField *field = _this->get_current_field()->as_field();
+    nassertr(field != nullptr, object);
+    nassertr(field == dclass->get_constructor(), object);
+
+    set_class_element(class_def, object, field);
+
+    // By now, the object should have been constructed.
+    if (object == nullptr) {
+      return nullptr;
+    }
+  }
+  while (_this->more_nested_fields()) {
+    const DCField *field = _this->get_current_field()->as_field();
+    nassertr(field != nullptr, object);
+
+    set_class_element(class_def, object, field);
+  }
+  _this->pop();
+
+  return object;
+}
+
+/**
+ * Unpacks the current element and stuffs it on the Python class object in
+ * whatever way is appropriate.
+ */
+void Extension<DCPacker>::
+set_class_element(PyObject *class_def, PyObject *&object,
+                  const DCField *field) {
+  std::string field_name = field->get_name();
+  DCPackType pack_type = _this->get_pack_type();
+
+  if (field_name.empty()) {
+    switch (pack_type) {
+    case PT_class:
+    case PT_switch:
+      // If the field has no name, but it is one of these container objects,
+      // we want to unpack its nested objects directly into the class.
+      _this->push();
+      while (_this->more_nested_fields()) {
+        const DCField *field = _this->get_current_field()->as_field();
+        nassertv(field != nullptr);
+        nassertv(object != nullptr);
+        set_class_element(class_def, object, field);
+      }
+      _this->pop();
+      break;
+
+    default:
+      // Otherwise, we just skip over the field.
+      _this->unpack_skip();
+    }
+
+  } else {
+    // If the field does have a name, we will want to store it on the class,
+    // either by calling a method (for a PT_field pack_type) or by setting a
+    // value (for any other kind of pack_type).
+
+    PyObject *element = unpack_object();
+
+    if (pack_type == PT_field) {
+      if (object == nullptr) {
+        // If the object hasn't been constructed yet, assume this is the
+        // constructor.
+        object = PyObject_CallObject(class_def, element);
+
+      } else {
+        if (PyObject_HasAttrString(object, (char *)field_name.c_str())) {
+          PyObject *func = PyObject_GetAttrString(object, (char *)field_name.c_str());
+          if (func != nullptr) {
+            PyObject *result = PyObject_CallObject(func, element);
+            Py_XDECREF(result);
+            Py_DECREF(func);
+          }
+        }
+      }
+
+    } else {
+      nassertv(object != nullptr);
+      PyObject_SetAttrString(object, (char *)field_name.c_str(), element);
+    }
+
+    Py_DECREF(element);
+  }
+}
+
+/**
+ * Gets the current element from the Python object and packs it.
+ */
+void Extension<DCPacker>::
+get_class_element(const DCClass *dclass, PyObject *object,
+                  const DCField *field) {
+  std::string field_name = field->get_name();
+  DCPackType pack_type = _this->get_pack_type();
+
+  if (field_name.empty()) {
+    switch (pack_type) {
+    case PT_class:
+    case PT_switch:
+      // If the field has no name, but it is one of these container objects,
+      // we want to get its nested objects directly from the class.
+      _this->push();
+      while (_this->more_nested_fields() && !_this->_pack_error) {
+        const DCField *field = _this->get_current_field()->as_field();
+        nassertv(field != nullptr);
+        get_class_element(dclass, object, field);
+      }
+      _this->pop();
+      break;
+
+    default:
+      // Otherwise, we just pack the default value.
+      _this->pack_default_value();
+    }
+
+  } else {
+    // If the field does have a name, we will want to get it from the class
+    // and pack it.  It just so happens that there's already a method that
+    // does this on DCClass.
+
+    if (!invoke_extension(dclass).pack_required_field(*_this, object, field)) {
+      _this->_pack_error = true;
+    }
+  }
+}
+
+#endif  // HAVE_PYTHON

+ 45 - 0
direct/src/dcparser/dcPacker_ext.h

@@ -0,0 +1,45 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file dcPacker_ext.h
+ * @author CFSworks
+ * @date 2019-07-03
+ */
+
+#ifndef DCPACKER_EXT_H
+#define DCPACKER_EXT_H
+
+#include "dtoolbase.h"
+
+#ifdef HAVE_PYTHON
+
+#include "extension.h"
+#include "dcPacker.h"
+#include "py_panda.h"
+
+/**
+ * This class defines the extension methods for DCPacker, which are called
+ * instead of any C++ methods with the same prototype.
+ */
+template<>
+class Extension<DCPacker> : public ExtensionBase<DCPacker> {
+public:
+  void pack_object(PyObject *object);
+  PyObject *unpack_object();
+
+  void pack_class_object(const DCClass *dclass, PyObject *object);
+  PyObject *unpack_class_object(const DCClass *dclass);
+  void set_class_element(PyObject *class_def, PyObject *&object,
+                         const DCField *field);
+  void get_class_element(const DCClass *dclass, PyObject *object,
+                         const DCField *field);
+};
+
+#endif  // HAVE_PYTHON
+
+#endif  // DCPACKER_EXT_H

+ 0 - 42
direct/src/dcparser/dcPython.h

@@ -1,42 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file dcPython.h
- * @author drose
- * @date 2004-06-22
- */
-
-#ifndef DCPYTHON_H
-#define DCPYTHON_H
-
-// The only purpose of this file is to serve as a common place to put the
-// nonsense associated with #including <Python.h>.
-
-#ifdef HAVE_PYTHON
-
-#define PY_SSIZE_T_CLEAN 1
-
-#undef _POSIX_C_SOURCE
-#undef _XOPEN_SOURCE
-#include <Python.h>
-
-// Python 2.5 adds Py_ssize_t; earlier versions don't have it.
-#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
-typedef int Py_ssize_t;
-#define PY_SSIZE_T_MAX INT_MAX
-#define PY_SSIZE_T_MIN INT_MIN
-#endif
-
-// Several interfaces in this module that use Python also require these header
-// files, so we might as well pick them up too.
-#include "datagram.h"
-#include "datagramIterator.h"
-
-#endif  // HAVE_PYTHON
-
-#endif

+ 3 - 0
direct/src/dcparser/p3dcparser_ext_composite.cxx

@@ -0,0 +1,3 @@
+#include "dcClass_ext.cxx"
+#include "dcField_ext.cxx"
+#include "dcPacker_ext.cxx"

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

@@ -1,5 +1,7 @@
 
 
 from .DirectGlobals import *
 from .DirectGlobals import *
+from panda3d.core import VBase4
+from direct.task.Task import Task
 
 
 # Routines to adjust values
 # Routines to adjust values
 def ROUND_TO(value, divisor):
 def ROUND_TO(value, divisor):

+ 14 - 7
direct/src/dist/FreezeTool.py

@@ -800,29 +800,36 @@ class Freezer:
                     self.moduleSuffixes[i] = (suffix[0], 'rb', imp.PY_SOURCE)
                     self.moduleSuffixes[i] = (suffix[0], 'rb', imp.PY_SOURCE)
         else:
         else:
             self.moduleSuffixes = [('.py', 'rb', 1), ('.pyc', 'rb', 2)]
             self.moduleSuffixes = [('.py', 'rb', 1), ('.pyc', 'rb', 2)]
+
+            abi_version = '{0}{1}'.format(*sys.version_info)
+            abi_flags = ''
+            if sys.version_info < (3, 8):
+                abi_flags += 'm'
+
             if 'linux' in self.platform:
             if 'linux' in self.platform:
                 self.moduleSuffixes += [
                 self.moduleSuffixes += [
-                    ('.cpython-{0}{1}m-x86_64-linux-gnu.so'.format(*sys.version_info), 'rb', 3),
-                    ('.cpython-{0}{1}m-i686-linux-gnu.so'.format(*sys.version_info), 'rb', 3),
+                    ('.cpython-{0}{1}-x86_64-linux-gnu.so'.format(abi_version, abi_flags), 'rb', 3),
+                    ('.cpython-{0}{1}-i686-linux-gnu.so'.format(abi_version, abi_flags), 'rb', 3),
                     ('.abi{0}.so'.format(sys.version_info[0]), 'rb', 3),
                     ('.abi{0}.so'.format(sys.version_info[0]), 'rb', 3),
                     ('.so', 'rb', 3),
                     ('.so', 'rb', 3),
                 ]
                 ]
             elif 'win' in self.platform:
             elif 'win' in self.platform:
+                # ABI flags are not appended on Windows.
                 self.moduleSuffixes += [
                 self.moduleSuffixes += [
-                    ('.cp{0}{1}-win_amd64.pyd'.format(*sys.version_info), 'rb', 3),
-                    ('.cp{0}{1}-win32.pyd'.format(*sys.version_info), 'rb', 3),
+                    ('.cp{0}-win_amd64.pyd'.format(abi_version), 'rb', 3),
+                    ('.cp{0}-win32.pyd'.format(abi_version), 'rb', 3),
                     ('.pyd', 'rb', 3),
                     ('.pyd', 'rb', 3),
                 ]
                 ]
             elif 'mac' in self.platform:
             elif 'mac' in self.platform:
                 self.moduleSuffixes += [
                 self.moduleSuffixes += [
-                    ('.cpython-{0}{1}m-darwin.so'.format(*sys.version_info), 'rb', 3),
+                    ('.cpython-{0}{1}-darwin.so'.format(abi_version, abi_flags), 'rb', 3),
                     ('.abi{0}.so'.format(sys.version_info[0]), 'rb', 3),
                     ('.abi{0}.so'.format(sys.version_info[0]), 'rb', 3),
                     ('.so', 'rb', 3),
                     ('.so', 'rb', 3),
                 ]
                 ]
             else: # FreeBSD et al.
             else: # FreeBSD et al.
                 self.moduleSuffixes += [
                 self.moduleSuffixes += [
-                    ('.cpython-{0}{1}m.so'.format(*sys.version_info), 'rb', 3),
-                    ('.abi{0}.so'.format(*sys.version_info), 'rb', 3),
+                    ('.cpython-{0}{1}.so'.format(abi_version, abi_flags), 'rb', 3),
+                    ('.abi{0}.so'.format(sys.version_info[0]), 'rb', 3),
                     ('.so', 'rb', 3),
                     ('.so', 'rb', 3),
                 ]
                 ]
 
 

+ 56 - 0
direct/src/dist/commands.py

@@ -14,12 +14,14 @@ import struct
 import imp
 import imp
 import string
 import string
 import time
 import time
+import tempfile
 
 
 import setuptools
 import setuptools
 import distutils.log
 import distutils.log
 
 
 from . import FreezeTool
 from . import FreezeTool
 from . import pefile
 from . import pefile
+from direct.p3d.DeploymentTools import Icon
 import panda3d.core as p3d
 import panda3d.core as p3d
 
 
 
 
@@ -224,6 +226,7 @@ class build_apps(setuptools.Command):
         self.exclude_patterns = []
         self.exclude_patterns = []
         self.include_modules = {}
         self.include_modules = {}
         self.exclude_modules = {}
         self.exclude_modules = {}
+        self.icons = {}
         self.platforms = [
         self.platforms = [
             'manylinux1_x86_64',
             'manylinux1_x86_64',
             'macosx_10_6_x86_64',
             'macosx_10_6_x86_64',
@@ -298,6 +301,7 @@ class build_apps(setuptools.Command):
             key: _parse_list(value)
             key: _parse_list(value)
             for key, value in _parse_dict(self.exclude_modules).items()
             for key, value in _parse_dict(self.exclude_modules).items()
         }
         }
+        self.icons = _parse_dict(self.icons)
         self.platforms = _parse_list(self.platforms)
         self.platforms = _parse_list(self.platforms)
         self.plugins = _parse_list(self.plugins)
         self.plugins = _parse_list(self.plugins)
         self.extra_prc_files = _parse_list(self.extra_prc_files)
         self.extra_prc_files = _parse_list(self.extra_prc_files)
@@ -357,6 +361,18 @@ class build_apps(setuptools.Command):
         tmp.update(self.package_data_dirs)
         tmp.update(self.package_data_dirs)
         self.package_data_dirs = tmp
         self.package_data_dirs = tmp
 
 
+        self.icon_objects = {}
+        for app, iconpaths in self.icons.items():
+            if not isinstance(iconpaths, list) and not isinstance(iconpaths, tuple):
+                iconpaths = (iconpaths,)
+
+            iconobj = Icon()
+            for iconpath in iconpaths:
+                iconobj.addImage(iconpath)
+
+            iconobj.generateMissingImages()
+            self.icon_objects[app] = iconobj
+
     def run(self):
     def run(self):
         self.announce('Building platforms: {0}'.format(','.join(self.platforms)), distutils.log.INFO)
         self.announce('Building platforms: {0}'.format(','.join(self.platforms)), distutils.log.INFO)
 
 
@@ -433,6 +449,22 @@ class build_apps(setuptools.Command):
 
 
         return wheelpaths
         return wheelpaths
 
 
+    def update_pe_resources(self, appname, runtime):
+        """Update resources (e.g., icons) in windows PE file"""
+
+        icon = self.icon_objects.get(
+            appname,
+            self.icon_objects.get('*', None),
+        )
+
+        if icon is not None:
+            pef = pefile.PEFile()
+            pef.open(runtime, 'r+')
+            pef.add_icon(icon)
+            pef.add_resource_section()
+            pef.write_changes()
+            pef.close()
+
     def bundle_macos_app(self, builddir):
     def bundle_macos_app(self, builddir):
         """Bundle built runtime into a .app for macOS"""
         """Bundle built runtime into a .app for macOS"""
 
 
@@ -474,6 +506,15 @@ class build_apps(setuptools.Command):
             'CFBundleSignature': '', #TODO
             'CFBundleSignature': '', #TODO
             'CFBundleExecutable': self.macos_main_app,
             'CFBundleExecutable': self.macos_main_app,
         }
         }
+
+        icon = self.icon_objects.get(
+            self.macos_main_app,
+            self.icon_objects.get('*', None)
+        )
+        if icon is not None:
+            plist['CFBundleIconFile'] = 'iconfile'
+            icon.makeICNS(os.path.join(resdir, 'iconfile.icns'))
+
         with open(os.path.join(contentsdir, 'Info.plist'), 'wb') as f:
         with open(os.path.join(contentsdir, 'Info.plist'), 'wb') as f:
             if hasattr(plistlib, 'dump'):
             if hasattr(plistlib, 'dump'):
                 plistlib.dump(plist, f)
                 plistlib.dump(plist, f)
@@ -618,6 +659,18 @@ class build_apps(setuptools.Command):
                 stub_path = os.path.join(os.path.dirname(dtool_path), '..', 'bin', stub_name)
                 stub_path = os.path.join(os.path.dirname(dtool_path), '..', 'bin', stub_name)
                 stub_file = open(stub_path, 'rb')
                 stub_file = open(stub_path, 'rb')
 
 
+            # Do we need an icon?  On Windows, we need to add this to the stub
+            # before we add the blob.
+            if 'win' in platform:
+                temp_file = tempfile.NamedTemporaryFile(suffix='-icon.exe', delete=False)
+                temp_file.write(stub_file.read())
+                stub_file.close()
+                temp_file.close()
+                self.update_pe_resources(appname, temp_file.name)
+                stub_file = open(temp_file.name, 'rb')
+            else:
+                temp_file = None
+
             freezer.generateRuntimeFromStub(target_path, stub_file, use_console, {
             freezer.generateRuntimeFromStub(target_path, stub_file, use_console, {
                 'prc_data': prcexport if self.embed_prc_data else None,
                 'prc_data': prcexport if self.embed_prc_data else None,
                 'default_prc_dir': self.default_prc_dir,
                 'default_prc_dir': self.default_prc_dir,
@@ -633,6 +686,9 @@ class build_apps(setuptools.Command):
             }, self.log_append)
             }, self.log_append)
             stub_file.close()
             stub_file.close()
 
 
+            if temp_file:
+                os.unlink(temp_file.name)
+
             # Copy the dependencies.
             # Copy the dependencies.
             search_path = [builddir]
             search_path = [builddir]
             if use_wheels:
             if use_wheels:

+ 4 - 3
direct/src/distributed/cConnectionRepository.cxx

@@ -26,6 +26,7 @@
 
 
 #ifdef HAVE_PYTHON
 #ifdef HAVE_PYTHON
 #include "py_panda.h"
 #include "py_panda.h"
+#include "dcClass_ext.h"
 #endif
 #endif
 
 
 using std::endl;
 using std::endl;
@@ -736,7 +737,7 @@ handle_update_field() {
       // get into trouble if it tried to delete the object from the doId2do
       // get into trouble if it tried to delete the object from the doId2do
       // map.
       // map.
       Py_INCREF(distobj);
       Py_INCREF(distobj);
-      dclass->receive_update(distobj, _di);
+      invoke_extension(dclass).receive_update(distobj, _di);
       Py_DECREF(distobj);
       Py_DECREF(distobj);
 
 
       if (PyErr_Occurred()) {
       if (PyErr_Occurred()) {
@@ -820,7 +821,7 @@ handle_update_field_owner() {
         // make a copy of the datagram iterator so that we can use the main
         // make a copy of the datagram iterator so that we can use the main
         // iterator for the non-owner update
         // iterator for the non-owner update
         DatagramIterator _odi(_di);
         DatagramIterator _odi(_di);
-        dclass->receive_update(distobjOV, _odi);
+        invoke_extension(dclass).receive_update(distobjOV, _odi);
         Py_DECREF(distobjOV);
         Py_DECREF(distobjOV);
 
 
         if (PyErr_Occurred()) {
         if (PyErr_Occurred()) {
@@ -861,7 +862,7 @@ handle_update_field_owner() {
         // get into trouble if it tried to delete the object from the doId2do
         // get into trouble if it tried to delete the object from the doId2do
         // map.
         // map.
         Py_INCREF(distobj);
         Py_INCREF(distobj);
-        dclass->receive_update(distobj, _di);
+        invoke_extension(dclass).receive_update(distobj, _di);
         Py_DECREF(distobj);
         Py_DECREF(distobj);
 
 
         if (PyErr_Occurred()) {
         if (PyErr_Occurred()) {

+ 1 - 1
direct/src/distributed/cConnectionRepository.h

@@ -53,7 +53,7 @@ class SocketStream;
  * the C++ layer, while server messages that are not understood by the C++
  * the C++ layer, while server messages that are not understood by the C++
  * layer are returned up to the Python layer for processing.
  * layer are returned up to the Python layer for processing.
  */
  */
-class EXPCL_DIRECT_DISTRIBUTED CConnectionRepository {
+class CConnectionRepository {
 PUBLISHED:
 PUBLISHED:
   explicit CConnectionRepository(bool has_owner_view = false,
   explicit CConnectionRepository(bool has_owner_view = false,
                                  bool threaded_net = false);
                                  bool threaded_net = false);

+ 4 - 0
direct/src/distributed/cDistributedSmoothNodeBase.cxx

@@ -18,6 +18,10 @@
 #include "dcmsgtypes.h"
 #include "dcmsgtypes.h"
 #include "config_distributed.h"
 #include "config_distributed.h"
 
 
+#ifdef HAVE_PYTHON
+#include "py_panda.h"
+#endif
+
 static const PN_stdfloat smooth_node_epsilon = 0.01;
 static const PN_stdfloat smooth_node_epsilon = 0.01;
 static const double network_time_precision = 100.0;  // Matches ClockDelta.py
 static const double network_time_precision = 100.0;  // Matches ClockDelta.py
 
 

+ 1 - 2
direct/src/distributed/cDistributedSmoothNodeBase.h

@@ -18,7 +18,6 @@
 #include "nodePath.h"
 #include "nodePath.h"
 #include "dcbase.h"
 #include "dcbase.h"
 #include "dcPacker.h"
 #include "dcPacker.h"
-#include "dcPython.h"  // to pick up Python.h
 #include "clockObject.h"
 #include "clockObject.h"
 
 
 class DCClass;
 class DCClass;
@@ -28,7 +27,7 @@ class CConnectionRepository;
  * This class defines some basic methods of DistributedSmoothNodeBase which
  * This class defines some basic methods of DistributedSmoothNodeBase which
  * have been moved into C++ as a performance optimization.
  * have been moved into C++ as a performance optimization.
  */
  */
-class EXPCL_DIRECT_DISTRIBUTED CDistributedSmoothNodeBase {
+class CDistributedSmoothNodeBase {
 PUBLISHED:
 PUBLISHED:
   CDistributedSmoothNodeBase();
   CDistributedSmoothNodeBase();
   ~CDistributedSmoothNodeBase();
   ~CDistributedSmoothNodeBase();

+ 4 - 4
direct/src/distributed/config_distributed.h

@@ -23,10 +23,10 @@
 
 
 NotifyCategoryDecl(distributed, EXPCL_DIRECT_DISTRIBUTED, EXPTP_DIRECT_DISTRIBUTED);
 NotifyCategoryDecl(distributed, EXPCL_DIRECT_DISTRIBUTED, EXPTP_DIRECT_DISTRIBUTED);
 
 
-extern ConfigVariableInt game_server_timeout_ms;
-extern ConfigVariableDouble min_lag;
-extern ConfigVariableDouble max_lag;
-extern ConfigVariableBool handle_datagrams_internally;
+extern EXPCL_DIRECT_DISTRIBUTED ConfigVariableInt game_server_timeout_ms;
+extern EXPCL_DIRECT_DISTRIBUTED ConfigVariableDouble min_lag;
+extern EXPCL_DIRECT_DISTRIBUTED ConfigVariableDouble max_lag;
+extern EXPCL_DIRECT_DISTRIBUTED ConfigVariableBool handle_datagrams_internally;
 
 
 extern EXPCL_DIRECT_DISTRIBUTED void init_libdistributed();
 extern EXPCL_DIRECT_DISTRIBUTED void init_libdistributed();
 
 

+ 2 - 1
direct/src/gui/DirectDialog.py

@@ -3,6 +3,7 @@
 __all__ = ['findDialog', 'cleanupDialog', 'DirectDialog', 'OkDialog', 'OkCancelDialog', 'YesNoDialog', 'YesNoCancelDialog', 'RetryCancelDialog']
 __all__ = ['findDialog', 'cleanupDialog', 'DirectDialog', 'OkDialog', 'OkCancelDialog', 'YesNoDialog', 'YesNoCancelDialog', 'RetryCancelDialog']
 
 
 from panda3d.core import *
 from panda3d.core import *
+from direct.showbase import ShowBaseGlobal
 from . import DirectGuiGlobals as DGG
 from . import DirectGuiGlobals as DGG
 from .DirectFrame import *
 from .DirectFrame import *
 from .DirectButton import *
 from .DirectButton import *
@@ -207,7 +208,7 @@ class DirectDialog(DirectFrame):
             image = None
             image = None
         # Get size of text/geom without image (for state 0)
         # Get size of text/geom without image (for state 0)
         if image:
         if image:
-            image.reparentTo(hidden)
+            image.reparentTo(ShowBaseGlobal.hidden)
         bounds = self.stateNodePath[0].getTightBounds()
         bounds = self.stateNodePath[0].getTightBounds()
         if image:
         if image:
             image.reparentTo(self.stateNodePath[0])
             image.reparentTo(self.stateNodePath[0])

+ 9 - 7
direct/src/gui/DirectEntry.py

@@ -4,6 +4,7 @@ text entered using the keyboard."""
 __all__ = ['DirectEntry']
 __all__ = ['DirectEntry']
 
 
 from panda3d.core import *
 from panda3d.core import *
+from direct.showbase import ShowBaseGlobal
 from . import DirectGuiGlobals as DGG
 from . import DirectGuiGlobals as DGG
 from .DirectFrame import *
 from .DirectFrame import *
 from .OnscreenText import OnscreenText
 from .OnscreenText import OnscreenText
@@ -94,7 +95,7 @@ class DirectEntry(DirectFrame):
         self.onscreenText = self.createcomponent(
         self.onscreenText = self.createcomponent(
             'text', (), None,
             'text', (), None,
             OnscreenText,
             OnscreenText,
-            (), parent = hidden,
+            (), parent = ShowBaseGlobal.hidden,
             # Pass in empty text to avoid extra work, since its really
             # Pass in empty text to avoid extra work, since its really
             # The PGEntry which will use the TextNode to generate geometry
             # The PGEntry which will use the TextNode to generate geometry
             text = '',
             text = '',
@@ -215,11 +216,11 @@ class DirectEntry(DirectFrame):
         self._autoCapitalize()
         self._autoCapitalize()
 
 
     def _autoCapitalize(self):
     def _autoCapitalize(self):
-        name = self.get().decode('utf-8')
+        name = self.guiItem.getWtext()
         # capitalize each word, allowing for things like McMutton
         # capitalize each word, allowing for things like McMutton
-        capName = ''
+        capName = u''
         # track each individual word to detect prefixes like Mc
         # track each individual word to detect prefixes like Mc
-        wordSoFar = ''
+        wordSoFar = u''
         # track whether the previous character was part of a word or not
         # track whether the previous character was part of a word or not
         wasNonWordChar = True
         wasNonWordChar = True
         for i, character in enumerate(name):
         for i, character in enumerate(name):
@@ -228,9 +229,9 @@ class DirectEntry(DirectFrame):
             #   This assumes that string.lower and string.upper will return different
             #   This assumes that string.lower and string.upper will return different
             #   values for all unicode letters.
             #   values for all unicode letters.
             # - Don't count apostrophes as a break between words
             # - Don't count apostrophes as a break between words
-            if character.lower() == character.upper() and character != "'":
+            if character.lower() == character.upper() and character != u"'":
                 # we are between words
                 # we are between words
-                wordSoFar = ''
+                wordSoFar = u''
                 wasNonWordChar = True
                 wasNonWordChar = True
             else:
             else:
                 capitalize = False
                 capitalize = False
@@ -254,7 +255,8 @@ class DirectEntry(DirectFrame):
                 wordSoFar += character
                 wordSoFar += character
                 wasNonWordChar = False
                 wasNonWordChar = False
             capName += character
             capName += character
-        self.enterText(capName.encode('utf-8'))
+        self.guiItem.setWtext(capName)
+        self.guiItem.setCursorPosition(self.guiItem.getNumCharacters())
 
 
     def focusOutCommandFunc(self):
     def focusOutCommandFunc(self):
         if self['focusOutCommand']:
         if self['focusOutCommand']:

+ 25 - 3
direct/src/gui/DirectEntryScroll.py

@@ -29,19 +29,41 @@ class DirectEntryScroll(DirectFrame):
            # frameSize = (-0.006, 3.2, -0.015, 0.036),
            # frameSize = (-0.006, 3.2, -0.015, 0.036),
         # if you need to scale the entry scale it's parent instead
         # if you need to scale the entry scale it's parent instead
 
 
-        self.entry = entry
         self.canvas = NodePath(self.guiItem.getCanvasNode())
         self.canvas = NodePath(self.guiItem.getCanvasNode())
-        self.entry.reparentTo(self.canvas)
         self.canvas.setPos(0,0,0)
         self.canvas.setPos(0,0,0)
 
 
-        self.entry.bind(DGG.CURSORMOVE,self.cursorMove)
+        self.entry = None
+        if entry is not None:
+            self.entry = entry
+            self.entry.reparentTo(self.canvas)
+            self.entry.bind(DGG.CURSORMOVE, self.cursorMove)
 
 
         self.canvas.node().setBounds(OmniBoundingVolume())
         self.canvas.node().setBounds(OmniBoundingVolume())
         self.canvas.node().setFinal(1)
         self.canvas.node().setFinal(1)
         self.resetCanvas()
         self.resetCanvas()
 
 
+    def setEntry(self, entry):
+        """
+        Sets a DirectEntry element for this scroll frame. A DirectEntryScroll
+        can only hold one entry at a time, so make sure to not call this
+        function twice or call clearEntry before to make sure no entry
+        is already set.
+        """
+        assert self.entry is None, "An entry was already set for this DirectEntryScroll element"
+        self.entry = entry
+        self.entry.reparentTo(self.canvas)
 
 
+        self.entry.bind(DGG.CURSORMOVE, self.cursorMove)
 
 
+    def clearEntry(self):
+        """
+        detaches and unbinds the entry from the scroll frame and its
+        events. You'll be responsible for destroying it.
+        """
+        if self.entry is None: return
+        self.entry.unbind(DGG.CURSORMOVE)
+        self.entry.detachNode()
+        self.entry = None
 
 
     def cursorMove(self, cursorX, cursorY):
     def cursorMove(self, cursorX, cursorY):
         cursorX = self.entry.guiItem.getCursorX() * self.entry['text_scale'][0]
         cursorX = self.entry.guiItem.getCursorX() * self.entry['text_scale'][0]

+ 3 - 3
direct/src/gui/DirectGuiBase.py

@@ -634,7 +634,7 @@ class DirectGuiBase(DirectObject.DirectObject):
         """
         """
         # Need to tack on gui item specific id
         # Need to tack on gui item specific id
         gEvent = event + self.guiId
         gEvent = event + self.guiId
-        if ShowBase.config.GetBool('debug-directgui-msgs', False):
+        if ShowBaseGlobal.config.GetBool('debug-directgui-msgs', False):
             from direct.showbase.PythonUtil import StackTrace
             from direct.showbase.PythonUtil import StackTrace
             print(gEvent)
             print(gEvent)
             print(StackTrace())
             print(StackTrace())
@@ -663,7 +663,7 @@ class DirectGuiWidget(DirectGuiBase, NodePath):
     # Determine the default initial state for inactive (or
     # Determine the default initial state for inactive (or
     # unclickable) components.  If we are in edit mode, these are
     # unclickable) components.  If we are in edit mode, these are
     # actually clickable by default.
     # actually clickable by default.
-    guiEdit = ShowBase.config.GetBool('direct-gui-edit', False)
+    guiEdit = ShowBaseGlobal.config.GetBool('direct-gui-edit', False)
     if guiEdit:
     if guiEdit:
         inactiveInitState = DGG.NORMAL
         inactiveInitState = DGG.NORMAL
     else:
     else:
@@ -729,7 +729,7 @@ class DirectGuiWidget(DirectGuiBase, NodePath):
             guiObjectCollector.addLevel(1)
             guiObjectCollector.addLevel(1)
             guiObjectCollector.flushLevel()
             guiObjectCollector.flushLevel()
             # track gui items by guiId for tracking down leaks
             # track gui items by guiId for tracking down leaks
-            if ShowBase.config.GetBool('track-gui-items', False):
+            if ShowBaseGlobal.config.GetBool('track-gui-items', False):
                 if not hasattr(ShowBase, 'guiItems'):
                 if not hasattr(ShowBase, 'guiItems'):
                     ShowBase.guiItems = {}
                     ShowBase.guiItems = {}
                 if self.guiId in ShowBase.guiItems:
                 if self.guiId in ShowBase.guiItems:

+ 4 - 3
direct/src/gui/DirectOptionMenu.py

@@ -27,7 +27,7 @@ class DirectOptionMenu(DirectButton):
             # Amount of padding to place around popup button indicator
             # Amount of padding to place around popup button indicator
             ('popupMarkerBorder', (.1, .1), None),
             ('popupMarkerBorder', (.1, .1), None),
             # The initial position of the popup marker
             # The initial position of the popup marker
-            ('popupMarker_pos', (0, 0, 0), None),
+            ('popupMarker_pos', None, None),
             # Background color to use to highlight popup menu items
             # Background color to use to highlight popup menu items
             ('highlightColor', (.5, .5, .5, 1), None),
             ('highlightColor', (.5, .5, .5, 1), None),
             # Extra scale to use on highlight popup menu items
             # Extra scale to use on highlight popup menu items
@@ -44,8 +44,6 @@ class DirectOptionMenu(DirectButton):
         DirectButton.__init__(self, parent)
         DirectButton.__init__(self, parent)
         # Record any user specified frame size
         # Record any user specified frame size
         self.initFrameSize = self['frameSize']
         self.initFrameSize = self['frameSize']
-        # Record any user specified popup marker position
-        self.initPopupMarkerPos = self['popupMarker_pos']
         # Create a small rectangular marker to distinguish this button
         # Create a small rectangular marker to distinguish this button
         # as a popup menu button
         # as a popup menu button
         self.popupMarker = self.createcomponent(
         self.popupMarker = self.createcomponent(
@@ -54,6 +52,8 @@ class DirectOptionMenu(DirectButton):
             frameSize = (-0.5, 0.5, -0.2, 0.2),
             frameSize = (-0.5, 0.5, -0.2, 0.2),
             scale = 0.4,
             scale = 0.4,
             relief = DGG.RAISED)
             relief = DGG.RAISED)
+        # Record any user specified popup marker position
+        self.initPopupMarkerPos = self['popupMarker_pos']
         # This needs to popup the menu too
         # This needs to popup the menu too
         self.popupMarker.bind(DGG.B1PRESS, self.showPopupMenu)
         self.popupMarker.bind(DGG.B1PRESS, self.showPopupMenu)
         # Check if item is highlighted on release and select it if it is
         # Check if item is highlighted on release and select it if it is
@@ -77,6 +77,7 @@ class DirectOptionMenu(DirectButton):
             state = 'normal')
             state = 'normal')
         # Make sure this is on top of all the other widgets
         # Make sure this is on top of all the other widgets
         self.cancelFrame.setBin('gui-popup', 0)
         self.cancelFrame.setBin('gui-popup', 0)
+        self.cancelFrame.node().setBounds(OmniBoundingVolume())
         self.cancelFrame.bind(DGG.B1PRESS, self.hidePopupMenu)
         self.cancelFrame.bind(DGG.B1PRESS, self.hidePopupMenu)
         # Default action on press is to show popup menu
         # Default action on press is to show popup menu
         self.bind(DGG.B1PRESS, self.showPopupMenu)
         self.bind(DGG.B1PRESS, self.showPopupMenu)

+ 6 - 1
direct/src/gui/DirectScrolledFrame.py

@@ -33,7 +33,7 @@ class DirectScrolledFrame(DirectFrame):
             ('canvasSize',     (-1, 1, -1, 1),        self.setCanvasSize),
             ('canvasSize',     (-1, 1, -1, 1),        self.setCanvasSize),
             ('manageScrollBars', 1,                self.setManageScrollBars),
             ('manageScrollBars', 1,                self.setManageScrollBars),
             ('autoHideScrollBars', 1,              self.setAutoHideScrollBars),
             ('autoHideScrollBars', 1,              self.setAutoHideScrollBars),
-            ('scrollBarWidth', 0.08,               None),
+            ('scrollBarWidth', 0.08,               self.setScrollBarWidth),
             ('borderWidth',    (0.01, 0.01),       self.setBorderWidth),
             ('borderWidth',    (0.01, 0.01),       self.setBorderWidth),
             )
             )
 
 
@@ -72,6 +72,11 @@ class DirectScrolledFrame(DirectFrame):
         # Call option initialization functions
         # Call option initialization functions
         self.initialiseoptions(DirectScrolledFrame)
         self.initialiseoptions(DirectScrolledFrame)
 
 
+    def setScrollBarWidth(self):
+        w = self['scrollBarWidth']
+        self.verticalScroll["frameSize"] = (-w / 2.0, w / 2.0, -1, 1)
+        self.horizontalScroll["frameSize"] = (-1, 1, -w / 2.0, w / 2.0)
+
     def setCanvasSize(self):
     def setCanvasSize(self):
         f = self['canvasSize']
         f = self['canvasSize']
         self.guiItem.setVirtualFrame(f[0], f[1], f[2], f[3])
         self.guiItem.setVirtualFrame(f[0], f[1], f[2], f[3])

+ 5 - 4
direct/src/gui/DirectScrolledList.py

@@ -3,6 +3,7 @@
 __all__ = ['DirectScrolledListItem', 'DirectScrolledList']
 __all__ = ['DirectScrolledListItem', 'DirectScrolledList']
 
 
 from panda3d.core import *
 from panda3d.core import *
+from direct.showbase import ShowBaseGlobal
 from . import DirectGuiGlobals as DGG
 from . import DirectGuiGlobals as DGG
 from direct.directnotify import DirectNotifyGlobal
 from direct.directnotify import DirectNotifyGlobal
 from direct.task.Task import Task
 from direct.task.Task import Task
@@ -369,7 +370,7 @@ class DirectScrolledList(DirectFrame):
                 del self.currentSelected
                 del self.currentSelected
             self["items"].remove(item)
             self["items"].remove(item)
             if type(item) != type(''):
             if type(item) != type(''):
-                item.reparentTo(hidden)
+                item.reparentTo(ShowBaseGlobal.hidden)
             self.refresh()
             self.refresh()
             return 1
             return 1
         else:
         else:
@@ -387,7 +388,7 @@ class DirectScrolledList(DirectFrame):
                 item.destroy()
                 item.destroy()
             self["items"].remove(item)
             self["items"].remove(item)
             if type(item) != type(''):
             if type(item) != type(''):
-                item.reparentTo(hidden)
+                item.reparentTo(ShowBaseGlobal.hidden)
             self.refresh()
             self.refresh()
             return 1
             return 1
         else:
         else:
@@ -410,7 +411,7 @@ class DirectScrolledList(DirectFrame):
             self["items"].remove(item)
             self["items"].remove(item)
             if type(item) != type(''):
             if type(item) != type(''):
                 #RAU possible leak here, let's try to do the right thing
                 #RAU possible leak here, let's try to do the right thing
-                #item.reparentTo(hidden)
+                #item.reparentTo(ShowBaseGlobal.hidden)
                 item.removeNode()
                 item.removeNode()
             retval = 1
             retval = 1
 
 
@@ -435,7 +436,7 @@ class DirectScrolledList(DirectFrame):
             self["items"].remove(item)
             self["items"].remove(item)
             if type(item) != type(''):
             if type(item) != type(''):
                 #RAU possible leak here, let's try to do the right thing
                 #RAU possible leak here, let's try to do the right thing
-                #item.reparentTo(hidden)
+                #item.reparentTo(ShowBaseGlobal.hidden)
                 item.removeNode()
                 item.removeNode()
             retval = 1
             retval = 1
         if (refresh):
         if (refresh):

+ 1 - 19
direct/src/interval/SoundInterval.py

@@ -27,8 +27,7 @@ class SoundInterval(Interval.Interval):
     # than explicitly restarting the sound every time around. This
     # than explicitly restarting the sound every time around. This
     # prevents a skip in the sound at every repetition (the gap in
     # prevents a skip in the sound at every repetition (the gap in
     # the sound is caused by the delay between the end of the sound
     # the sound is caused by the delay between the end of the sound
-    # and the next taskMgr cycle). There still seems to be a skip
-    # in Miles when looping MP3s. =(
+    # and the next taskMgr cycle).
     # RAU 03/01/07 add listenerNode in case we don't want to
     # RAU 03/01/07 add listenerNode in case we don't want to
     # use base.camera as the listener, node must not be None
     # use base.camera as the listener, node must not be None
     def __init__(self, sound, loop = 0, duration = 0.0, name = None,
     def __init__(self, sound, loop = 0, duration = 0.0, name = None,
@@ -62,23 +61,6 @@ class SoundInterval(Interval.Interval):
             #if (duration == 0):
             #if (duration == 0):
             #    self.notify.warning('zero length duration!')
             #    self.notify.warning('zero length duration!')
 
 
-            # MPG - hack for Miles bug
-            #duration += 1.5
-
-            # DCR - hack for Miles bug - adding 1.5 seconds caused
-            # problems for MG_neg_buzzer.wav
-
-            # DCR - what this is all about: Miles is under-reporting the
-            # length of MP3 files, and they're getting cut off too early.
-            # This is a temporary hack. We could:
-            # - hack Miles to fix its MP3 length calculation
-            # - complain louder about this to RAD
-            # - precompute MP3 durations and store them in a table
-
-            # drose - ok, I've put in a lower-level workaround in the
-            # MilesAudioManager.  This is no longer necessary up here,
-            # where it pollutes SoundInterval for everyone.
-            #duration += min(duration * 2.4, 1.5)
 
 
         # Generate unique name if necessary
         # Generate unique name if necessary
         if (name == None):
         if (name == None):

+ 199 - 62
direct/src/p3d/DeploymentTools.py

@@ -10,7 +10,7 @@ from direct.directnotify.DirectNotifyGlobal import *
 from direct.showbase.AppRunnerGlobal import appRunner
 from direct.showbase.AppRunnerGlobal import appRunner
 from panda3d.core import PandaSystem, HTTPClient, Filename, VirtualFileSystem, Multifile
 from panda3d.core import PandaSystem, HTTPClient, Filename, VirtualFileSystem, Multifile
 from panda3d.core import TiXmlDocument, TiXmlDeclaration, TiXmlElement, readXmlStream
 from panda3d.core import TiXmlDocument, TiXmlDeclaration, TiXmlElement, readXmlStream
-from panda3d.core import PNMImage, PNMFileTypeRegistry
+from panda3d.core import PNMImage, PNMFileTypeRegistry, StringStream
 from direct.stdpy.file import *
 from direct.stdpy.file import *
 from direct.p3d.HostInfo import HostInfo
 from direct.p3d.HostInfo import HostInfo
 # This is important for some reason
 # This is important for some reason
@@ -332,6 +332,135 @@ class Icon:
 
 
         return True
         return True
 
 
+    def generateMissingImages(self):
+        """ Generates image sizes that should be present but aren't by scaling
+        from the next higher size. """
+
+        for required_size in (256, 128, 48, 32, 16):
+            if required_size in self.images:
+                continue
+
+            sizes = sorted(self.images.keys())
+            if required_size * 2 in sizes:
+                from_size = required_size * 2
+            else:
+                for from_size in sizes:
+                    if from_size > required_size:
+                        break
+
+            if from_size > required_size:
+                Icon.notify.warning("Generating %dx%d icon by scaling down %dx%d image" % (required_size, required_size, from_size, from_size))
+
+                image = PNMImage(required_size, required_size)
+                if self.images[from_size].hasAlpha():
+                    image.addAlpha()
+                image.quickFilterFrom(self.images[from_size])
+                self.images[required_size] = image
+            else:
+                Icon.notify.warning("Cannot generate %dx%d icon; no higher resolution image available" % (required_size, required_size))
+
+    def _write_bitmap(self, fp, image, size, bpp):
+        """ Writes the bitmap header and data of an .ico file. """
+
+        fp.write(struct.pack('<IiiHHIIiiII', 40, size, size * 2, 1, bpp, 0, 0, 0, 0, 0, 0))
+
+        # XOR mask
+        if bpp == 24:
+            # Align rows to 4-byte boundary
+            rowalign = '\0' * (-(size * 3) & 3)
+            for y in xrange(size):
+                for x in xrange(size):
+                    r, g, b = image.getXel(x, size - y - 1)
+                    fp.write(struct.pack('<BBB', int(b * 255), int(g * 255), int(r * 255)))
+                fp.write(rowalign)
+
+        elif bpp == 32:
+            for y in xrange(size):
+                for x in xrange(size):
+                    r, g, b, a = image.getXelA(x, size - y - 1)
+                    fp.write(struct.pack('<BBBB', int(b * 255), int(g * 255), int(r * 255), int(a * 255)))
+
+        elif bpp == 8:
+            # We'll have to generate a palette of 256 colors.
+            hist = PNMImage.Histogram()
+            if image.hasAlpha():
+                # Make a copy without alpha channel.
+                image2 = PNMImage(image)
+                image2.premultiplyAlpha()
+                image2.removeAlpha()
+            else:
+                image2 = image
+            image2.make_histogram(hist)
+            colors = list(hist.get_pixels())
+            if len(colors) > 256:
+                # Palette too large; remove infrequent colors.
+                colors.sort(key=hist.get_count, reverse=True)
+
+                # Find the closest color on the palette matching each color
+                # that didn't fit.  This is certainly not the best palette
+                # generation code, but it'll do for now.
+                closest_indices = []
+                for color in colors[256:]:
+                    closest_index = 0
+                    closest_diff = 1025
+                    for i, closest_color in enumerate(colors[:256]):
+                        diff = abs(color.get_red() - closest_color.get_red()) \
+                             + abs(color.get_green() - closest_color.get_green()) \
+                             + abs(color.get_blue() - closest_color.get_blue())
+                        if diff < closest_diff:
+                            closest_index = i
+                            closest_diff = diff
+                    assert closest_diff < 100
+                    closest_indices.append(closest_index)
+
+            # Write the palette.
+            i = 0
+            while i < 256 and i < len(colors):
+                r, g, b, a = colors[i]
+                fp.write(struct.pack('<BBBB', b, g, r, 0))
+                i += 1
+            if i < 256:
+                # Fill the rest with zeroes.
+                fp.write(b'\x00' * (4 * (256 - i)))
+
+            # Write indices.  Align rows to 4-byte boundary.
+            rowalign = b'\0' * (-size & 3)
+            for y in xrange(size):
+                for x in xrange(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:
+            raise ValueError("Invalid bpp %d" % (bpp))
+
+        # Create an AND mask, aligned to 4-byte boundary
+        if image.hasAlpha() and bpp <= 8:
+            rowalign = b'\0' * (-((size + 7) >> 3) & 3)
+            for y in xrange(size):
+                mask = 0
+                num_bits = 7
+                for x in xrange(size):
+                    a = image.get_alpha_val(x, size - y - 1)
+                    if a <= 1:
+                        mask |= (1 << num_bits)
+                    num_bits -= 1
+                    if num_bits < 0:
+                        fp.write(struct.pack('<B', mask))
+                        mask = 0
+                        num_bits = 7
+                if num_bits < 7:
+                    fp.write(struct.pack('<B', mask))
+                fp.write(rowalign)
+        else:
+            andsize = (size + 7) >> 3
+            if andsize % 4 != 0:
+                andsize += 4 - (andsize % 4)
+            fp.write(b'\x00' * (andsize * size))
+
     def makeICO(self, fn):
     def makeICO(self, fn):
         """ Writes the images to a Windows ICO file.  Returns True on success. """
         """ Writes the images to a Windows ICO file.  Returns True on success. """
 
 
@@ -339,57 +468,71 @@ class Icon:
             fn = Filename.fromOsSpecific(fn)
             fn = Filename.fromOsSpecific(fn)
         fn.setBinary()
         fn.setBinary()
 
 
+        # ICO files only support resolutions up to 256x256.
         count = 0
         count = 0
         for size in self.images.keys():
         for size in self.images.keys():
+            if size < 256:
+                count += 1
             if size <= 256:
             if size <= 256:
                 count += 1
                 count += 1
+        dataoffs = 6 + count * 16
 
 
         ico = open(fn, 'wb')
         ico = open(fn, 'wb')
         ico.write(struct.pack('<HHH', 0, 1, count))
         ico.write(struct.pack('<HHH', 0, 1, count))
 
 
-        # Write the directory
+        # Write 8-bpp image headers for sizes under 256x256.
         for size, image in self.images.items():
         for size, image in self.images.items():
-            if size == 256:
+            if size >= 256:
+                continue
+            ico.write(struct.pack('<BB', size, size))
+
+            # Calculate row sizes
+            xorsize = size
+            if xorsize % 4 != 0:
+                xorsize += 4 - (xorsize % 4)
+            andsize = (size + 7) >> 3
+            if andsize % 4 != 0:
+                andsize += 4 - (andsize % 4)
+            datasize = 40 + 256 * 4 + (xorsize + andsize) * size
+
+            ico.write(struct.pack('<BBHHII', 0, 0, 1, 8, datasize, dataoffs))
+            dataoffs += datasize
+
+        # Write 24/32-bpp image headers.
+        for size, image in self.images.items():
+            if size > 256:
+                continue
+            elif size == 256:
                 ico.write('\0\0')
                 ico.write('\0\0')
             else:
             else:
                 ico.write(struct.pack('<BB', size, size))
                 ico.write(struct.pack('<BB', size, size))
-            bpp = 32 if image.hasAlpha() else 24
-            ico.write(struct.pack('<BBHHII', 0, 0, 1, bpp, 0, 0))
 
 
-        # Now write the actual icons
-        ptr = 14
-        for size, image in self.images.items():
-            loc = ico.tell()
-            bpp = 32 if image.hasAlpha() else 24
-            ico.write(struct.pack('<IiiHHIIiiII', 40, size, size * 2, 1, bpp, 0, 0, 0, 0, 0, 0))
-
-            # XOR mask
-            if bpp == 24:
-                # Align rows to 4-byte boundary
-                rowalign = '\0' * (-(size * 3) & 3)
-                for y in xrange(size):
-                    for x in xrange(size):
-                        r, g, b = image.getXel(x, size - y - 1)
-                        ico.write(struct.pack('<BBB', int(b * 255), int(g * 255), int(r * 255)))
-                    ico.write(rowalign)
+            # Calculate the size so we can write the offset within the file.
+            if image.hasAlpha():
+                bpp = 32
+                xorsize = size * 4
             else:
             else:
-                for y in xrange(size):
-                    for x in xrange(size):
-                        r, g, b, a = image.getXelA(x, size - y - 1)
-                        ico.write(struct.pack('<BBBB', int(b * 255), int(g * 255), int(r * 255), int(a * 255)))
+                bpp = 24
+                xorsize = size * 3 + (-(size * 3) & 3)
+            andsize = (size + 7) >> 3
+            if andsize % 4 != 0:
+                andsize += 4 - (andsize % 4)
+            datasize = 40 + (xorsize + andsize) * size
+
+            ico.write(struct.pack('<BBHHII', 0, 0, 1, bpp, datasize, dataoffs))
+            dataoffs += datasize
 
 
-            # Empty AND mask, aligned to 4-byte boundary
-            #TODO: perhaps we should convert alpha into an AND mask
-            # to support older versions of Windows that don't support alpha.
-            ico.write('\0' * (size * (size / 8 + (-((size / 8) * 3) & 3))))
+        # Now write the actual icon bitmap data.
+        for size, image in self.images.items():
+            if size < 256:
+                self._write_bitmap(ico, image, size, 8)
 
 
-            # Go back to write the location
-            dataend = ico.tell()
-            ico.seek(ptr)
-            ico.write(struct.pack('<II', dataend - loc, loc))
-            ico.seek(dataend)
-            ptr += 16
+        for size, image in self.images.items():
+            if size <= 256:
+                bpp = 32 if image.hasAlpha() else 24
+                self._write_bitmap(ico, image, size, bpp)
 
 
+        assert ico.tell() == dataoffs
         ico.close()
         ico.close()
 
 
         return True
         return True
@@ -401,32 +544,35 @@ class Icon:
             fn = Filename.fromOsSpecific(fn)
             fn = Filename.fromOsSpecific(fn)
         fn.setBinary()
         fn.setBinary()
 
 
-        vfs = VirtualFileSystem.getGlobalPtr()
-        stream = vfs.openWriteFile(fn, False, True)
-        icns = open(stream, 'wb')
+        icns = open(fn, 'wb')
         icns.write(b'icns\0\0\0\0')
         icns.write(b'icns\0\0\0\0')
 
 
-        icon_types = {16: 'is32', 32: 'il32', 48: 'ih32', 128: 'it32'}
-        mask_types = {16: 's8mk', 32: 'l8mk', 48: 'h8mk', 128: 't8mk'}
-        png_types = {256: 'ic08', 512: 'ic09'}
+        icon_types = {16: b'is32', 32: b'il32', 48: b'ih32', 128: b'it32'}
+        mask_types = {16: b's8mk', 32: b'l8mk', 48: b'h8mk', 128: b't8mk'}
+        png_types = {256: b'ic08', 512: b'ic09', 1024: b'ic10'}
 
 
         pngtype = PNMFileTypeRegistry.getGlobalPtr().getTypeFromExtension("png")
         pngtype = PNMFileTypeRegistry.getGlobalPtr().getTypeFromExtension("png")
 
 
-        for size, image in self.images.items():
-            if size in png_types:
-                if pngtype is None:
-                    continue
-                icns.write(png_types[size])
-                icns.write(b'\0\0\0\0')
-                start = icns.tell()
-
+        for size, image in sorted(self.images.items(), key=lambda item:item[0]):
+            if size in png_types and pngtype is not None:
+                stream = StringStream()
                 image.write(stream, "", pngtype)
                 image.write(stream, "", pngtype)
-                pngsize = icns.tell() - start
-                icns.seek(start - 4)
-                icns.write(struct.pack('>I', pngsize + 8))
-                icns.seek(start + pngsize)
+                pngdata = stream.data
+
+                icns.write(png_types[size])
+                icns.write(struct.pack('>I', len(pngdata)))
+                icns.write(pngdata)
 
 
             elif size in icon_types:
             elif size in icon_types:
+                # If it has an alpha channel, we write out a mask too.
+                if image.hasAlpha():
+                    icns.write(mask_types[size])
+                    icns.write(struct.pack('>I', size * size + 8))
+
+                    for y in xrange(size):
+                        for x in xrange(size):
+                            icns.write(struct.pack('<B', int(image.getAlpha(x, y) * 255)))
+
                 icns.write(icon_types[size])
                 icns.write(icon_types[size])
                 icns.write(struct.pack('>I', size * size * 4 + 8))
                 icns.write(struct.pack('>I', size * size * 4 + 8))
 
 
@@ -435,15 +581,6 @@ class Icon:
                         r, g, b = image.getXel(x, y)
                         r, g, b = image.getXel(x, y)
                         icns.write(struct.pack('>BBBB', 0, int(r * 255), int(g * 255), int(b * 255)))
                         icns.write(struct.pack('>BBBB', 0, int(r * 255), int(g * 255), int(b * 255)))
 
 
-                if not image.hasAlpha():
-                    continue
-                icns.write(mask_types[size])
-                icns.write(struct.pack('>I', size * size + 8))
-
-                for y in xrange(size):
-                    for x in xrange(size):
-                        icns.write(struct.pack('<B', int(image.getAlpha(x, y) * 255)))
-
         length = icns.tell()
         length = icns.tell()
         icns.seek(4)
         icns.seek(4)
         icns.write(struct.pack('>I', length))
         icns.write(struct.pack('>I', length))

+ 18 - 0
direct/src/showbase/BufferViewer.py

@@ -442,3 +442,21 @@ class BufferViewer(DirectObject):
                     cards[index] = placer
                     cards[index] = placer
 
 
         return Task.cont
         return Task.cont
+
+    # Snake-case aliases, for people who prefer these.
+    advance_card = advanceCard
+    analyze_texture_set = analyzeTextureSet
+    is_enabled = isEnabled
+    is_valid_texture_set = isValidTextureSet
+    maintain_readout = maintainReadout
+    make_frame = makeFrame
+    refresh_readout = refreshReadout
+    select_card = selectCard
+    set_card_size = setCardSize
+    set_exclude = setExclude
+    set_include = setInclude
+    set_layout = setLayout
+    set_position = setPosition
+    set_render_parent = setRenderParent
+    set_sort = setSort
+    toggle_enable = toggleEnable

+ 17 - 1
direct/src/showbase/Loader.py

@@ -147,12 +147,28 @@ class Loader(DirectObject):
         Loader.loaderIndex += 1
         Loader.loaderIndex += 1
         self.accept(self.hook, self.__gotAsyncObject)
         self.accept(self.hook, self.__gotAsyncObject)
 
 
+        if ConfigVariableBool('loader-support-entry-points', True):
+            self._loadPythonFileTypes()
+
     def destroy(self):
     def destroy(self):
         self.ignore(self.hook)
         self.ignore(self.hook)
         self.loader.stopThreads()
         self.loader.stopThreads()
         del self.base
         del self.base
         del self.loader
         del self.loader
 
 
+    def _loadPythonFileTypes(self):
+        import importlib
+        try:
+            pkg_resources = importlib.import_module('pkg_resources')
+        except ImportError:
+            pkg_resources = None
+
+        if pkg_resources:
+            registry = LoaderFileTypeRegistry.getGlobalPtr()
+
+            for entry_point in pkg_resources.iter_entry_points('panda3d.loaders'):
+                registry.register_deferred_type(entry_point)
+
     # model loading funcs
     # model loading funcs
     def loadModel(self, modelPath, loaderOptions = None, noCache = None,
     def loadModel(self, modelPath, loaderOptions = None, noCache = None,
                   allowInstance = False, okMissing = None,
                   allowInstance = False, okMissing = None,
@@ -214,7 +230,7 @@ class Loader(DirectObject):
 
 
         """
         """
 
 
-        assert Loader.notify.debug("Loading model: %s" % (modelPath))
+        assert Loader.notify.debug("Loading model: %s" % (modelPath,))
         if loaderOptions is None:
         if loaderOptions is None:
             loaderOptions = LoaderOptions()
             loaderOptions = LoaderOptions()
         else:
         else:

+ 5 - 0
direct/src/showbase/PythonUtil.py

@@ -1130,6 +1130,10 @@ def weightedChoice(choiceList, rng=random.random, sum=None):
     """given a list of (weight, item) pairs, chooses an item based on the
     """given a list of (weight, item) pairs, chooses an item based on the
     weights. rng must return 0..1. if you happen to have the sum of the
     weights. rng must return 0..1. if you happen to have the sum of the
     weights, pass it in 'sum'."""
     weights, pass it in 'sum'."""
+    # Throw an IndexError if we got an empty list.
+    if not choiceList:
+        raise IndexError('Cannot choose from an empty sequence')
+
     # TODO: add support for dicts
     # TODO: add support for dicts
     if sum is None:
     if sum is None:
         sum = 0.
         sum = 0.
@@ -1138,6 +1142,7 @@ def weightedChoice(choiceList, rng=random.random, sum=None):
 
 
     rand = rng()
     rand = rng()
     accum = rand * sum
     accum = rand * sum
+    item = None
     for weight, item in choiceList:
     for weight, item in choiceList:
         accum -= weight
         accum -= weight
         if accum <= 0.:
         if accum <= 0.:

+ 4 - 1
direct/src/showbase/ShowBase.py

@@ -207,7 +207,10 @@ class ShowBase(DirectObject.DirectObject):
             # Has the clusterSyncFlag been set via a config variable
             # Has the clusterSyncFlag been set via a config variable
             self.clusterSyncFlag = self.config.GetBool('cluster-sync', 0)
             self.clusterSyncFlag = self.config.GetBool('cluster-sync', 0)
 
 
-        self.hidden = NodePath('hidden')
+        # We've already created aspect2d in ShowBaseGlobal, for the
+        # benefit of creating DirectGui elements before ShowBase.
+        from . import ShowBaseGlobal
+        self.hidden = ShowBaseGlobal.hidden
 
 
         ## The global graphics engine, ie. GraphicsEngine.getGlobalPtr()
         ## The global graphics engine, ie. GraphicsEngine.getGlobalPtr()
         self.graphicsEngine = GraphicsEngine.getGlobalPtr()
         self.graphicsEngine = GraphicsEngine.getGlobalPtr()

+ 1 - 0
direct/src/showbase/ShowBaseGlobal.py

@@ -25,6 +25,7 @@ pandaSystem = PandaSystem.getGlobalPtr()
 
 
 # This is defined here so GUI elements can be instantiated before ShowBase.
 # This is defined here so GUI elements can be instantiated before ShowBase.
 aspect2d = NodePath(PGTop("aspect2d"))
 aspect2d = NodePath(PGTop("aspect2d"))
+hidden = NodePath("hidden")
 
 
 # Set direct notify categories now that we have config
 # Set direct notify categories now that we have config
 directNotify.setDconfigLevels()
 directNotify.setDconfigLevels()

+ 2 - 1
direct/src/showbase/Transitions.py

@@ -5,6 +5,7 @@ a particular color."""
 __all__ = ['Transitions']
 __all__ = ['Transitions']
 
 
 from panda3d.core import *
 from panda3d.core import *
+from direct.showbase import ShowBaseGlobal
 from direct.gui.DirectGui import DirectFrame
 from direct.gui.DirectGui import DirectFrame
 from direct.gui import DirectGuiGlobals as DGG
 from direct.gui import DirectGuiGlobals as DGG
 from direct.interval.LerpInterval import LerpColorScaleInterval, LerpColorInterval, LerpScaleInterval, LerpPosInterval
 from direct.interval.LerpInterval import LerpColorScaleInterval, LerpColorInterval, LerpScaleInterval, LerpPosInterval
@@ -73,7 +74,7 @@ class Transitions:
             # so that it will also obscure mouse events for objects
             # so that it will also obscure mouse events for objects
             # positioned behind it.
             # positioned behind it.
             self.fade = DirectFrame(
             self.fade = DirectFrame(
-                parent = hidden,
+                parent = ShowBaseGlobal.hidden,
                 guiId = 'fade',
                 guiId = 'fade',
                 relief = None,
                 relief = None,
                 image = self.fadeModel,
                 image = self.fadeModel,

+ 2 - 1
direct/src/tkpanels/MopathRecorder.py

@@ -4,6 +4,7 @@ __all__ = ['MopathRecorder']
 
 
 # Import Tkinter, Pmw, and the dial code from this directory tree.
 # Import Tkinter, Pmw, and the dial code from this directory tree.
 from panda3d.core import *
 from panda3d.core import *
+from direct.showbase import ShowBaseGlobal
 from direct.showbase.DirectObject import DirectObject
 from direct.showbase.DirectObject import DirectObject
 from direct.showbase.TkGlobal import *
 from direct.showbase.TkGlobal import *
 from direct.tkwidgets.AppShell import *
 from direct.tkwidgets.AppShell import *
@@ -852,7 +853,7 @@ class MopathRecorder(AppShell, DirectObject):
         if self.getVariable('Style', 'Marker').get():
         if self.getVariable('Style', 'Marker').get():
             self.playbackMarker.reparentTo(self.recorderNodePath)
             self.playbackMarker.reparentTo(self.recorderNodePath)
         else:
         else:
-            self.playbackMarker.reparentTo(hidden)
+            self.playbackMarker.reparentTo(ShowBaseGlobal.hidden)
 
 
     def setNumSegs(self, value):
     def setNumSegs(self, value):
         self.numSegs = int(value)
         self.numSegs = int(value)

+ 25 - 15
direct/src/tkpanels/ParticlePanel.py

@@ -214,23 +214,23 @@ class ParticlePanel(AppShell):
             ('System', 'Pool Size',
             ('System', 'Pool Size',
              'Max number of simultaneous particles',
              'Max number of simultaneous particles',
              self.setSystemPoolSize,
              self.setSystemPoolSize,
-             1.0, 1.0),
+             1.0, 2000000, 1.0),
             ('System', 'Birth Rate',
             ('System', 'Birth Rate',
              'Seconds between particle births',
              'Seconds between particle births',
              self.setSystemBirthRate,
              self.setSystemBirthRate,
-             0.0, None),
+             0.0, None, None),
             ('System', 'Litter Size',
             ('System', 'Litter Size',
              'Number of particle created at each birth',
              'Number of particle created at each birth',
              self.setSystemLitterSize,
              self.setSystemLitterSize,
-             1.0, 1.0),
+             1.0, 0x7fffffff, 1.0),
             ('System', 'Litter Spread',
             ('System', 'Litter Spread',
              'Variation in litter size',
              'Variation in litter size',
              self.setSystemLitterSpread,
              self.setSystemLitterSpread,
-             0.0, 1.0),
+             0.0, 0x7fffffff, 1.0),
             ('System', 'Lifespan',
             ('System', 'Lifespan',
              'Age in seconds at which the system (vs. particles) should die',
              'Age in seconds at which the system (vs. particles) should die',
              self.setSystemLifespan,
              self.setSystemLifespan,
-             0.0, None)
+             0.0, None, None)
             )
             )
         self.createFloaters(systemPage, systemFloaterDefs)
         self.createFloaters(systemPage, systemFloaterDefs)
 
 
@@ -269,27 +269,27 @@ class ParticlePanel(AppShell):
             ('Factory', 'Life Span',
             ('Factory', 'Life Span',
              'Average particle lifespan in seconds',
              'Average particle lifespan in seconds',
              self.setFactoryLifeSpan,
              self.setFactoryLifeSpan,
-             0.0, None),
+             0.0, None, None),
             ('Factory', 'Life Span Spread',
             ('Factory', 'Life Span Spread',
              'Variation in lifespan',
              'Variation in lifespan',
              self.setFactoryLifeSpanSpread,
              self.setFactoryLifeSpanSpread,
-             0.0, None),
+             0.0, None, None),
             ('Factory', 'Mass',
             ('Factory', 'Mass',
              'Average particle mass',
              'Average particle mass',
              self.setFactoryParticleMass,
              self.setFactoryParticleMass,
-             0.001, None),
+             0.001, None, None),
             ('Factory', 'Mass Spread',
             ('Factory', 'Mass Spread',
              'Variation in particle mass',
              'Variation in particle mass',
              self.setFactoryParticleMassSpread,
              self.setFactoryParticleMassSpread,
-             0.0, None),
+             0.0, None, None),
             ('Factory', 'Terminal Velocity',
             ('Factory', 'Terminal Velocity',
              'Cap on average particle velocity',
              'Cap on average particle velocity',
              self.setFactoryTerminalVelocity,
              self.setFactoryTerminalVelocity,
-             0.0, None),
+             0.0, None, None),
             ('Factory', 'Terminal Vel. Spread',
             ('Factory', 'Terminal Vel. Spread',
              'Variation in terminal velocity',
              'Variation in terminal velocity',
              self.setFactoryTerminalVelocitySpread,
              self.setFactoryTerminalVelocitySpread,
-             0.0, None),
+             0.0, None, None),
         )
         )
         self.createFloaters(factoryPage, factoryWidgets)
         self.createFloaters(factoryPage, factoryWidgets)
 
 
@@ -966,19 +966,29 @@ class ParticlePanel(AppShell):
 
 
     def createFloaters(self, parent, widgetDefinitions):
     def createFloaters(self, parent, widgetDefinitions):
         widgets = []
         widgets = []
-        for category, label, balloonHelp, command, min, resolution in widgetDefinitions:
+        for category, label, balloonHelp, command, min, max, resolution in widgetDefinitions:
             widgets.append(
             widgets.append(
                 self.createFloater(parent, category, label, balloonHelp,
                 self.createFloater(parent, category, label, balloonHelp,
-                                   command, min, resolution)
+                                   command, min, max, resolution)
                 )
                 )
         return widgets
         return widgets
 
 
     def createFloater(self, parent, category, text, balloonHelp,
     def createFloater(self, parent, category, text, balloonHelp,
-                      command = None, min = 0.0, resolution = None,
-                      numDigits = 3, **kw):
+                      command = None, min = 0.0, max = None, resolution = None,
+                      numDigits = None, **kw):
         kw['text'] = text
         kw['text'] = text
         kw['min'] = min
         kw['min'] = min
+        if max is not None:
+            kw['max'] = max
         kw['resolution'] = resolution
         kw['resolution'] = resolution
+        if numDigits is None:
+            # If this is apparently an integer setting, show no decimals.
+            if resolution is not None and int(resolution) == resolution and \
+                (min is None or int(min) == min) and \
+                (max is None or int(max) == max):
+                numDigits = 0
+            else:
+                numDigits = 3
         kw['numDigits'] = numDigits
         kw['numDigits'] = numDigits
         widget = Floater.Floater(parent, **kw)
         widget = Floater.Floater(parent, **kw)
         # Do this after the widget so command isn't called on creation
         # Do this after the widget so command isn't called on creation

+ 0 - 108
dmodels/src/level_editor/donaldsDockColors.txt

@@ -1,108 +0,0 @@
-corniceColor: Vec4(0.11, 0.80, 0.22, 1.0)
-corniceColor: Vec4(0.21, 0.73, 0.31, 1.0)
-corniceColor: Vec4(0.22, 0.96, 0.27, 1.0)
-corniceColor: Vec4(0.57, 0.39, 0.34, 1.0)
-corniceColor: Vec4(0.59, 1.00, 0.92, 1.0)
-corniceColor: Vec4(0.66, 1.00, 0.86, 1.0)
-corniceColor: Vec4(0.73, 0.51, 0.51, 1.0)
-corniceColor: Vec4(0.75, 0.45, 0.45, 1.0)
-corniceColor: Vec4(0.76, 0.57, 0.29, 1.0)
-corniceColor: Vec4(0.76, 0.73, 0.64, 1.0)
-corniceColor: Vec4(0.82, 0.91, 0.44, 1.0)
-corniceColor: Vec4(0.90, 0.56, 0.34, 1.0)
-corniceColor: Vec4(0.91, 0.54, 0.44, 1.0)
-corniceColor: Vec4(0.91, 0.63, 0.44, 1.0)
-corniceColor: Vec4(0.94, 0.35, 0.35, 1.0)
-corniceColor: Vec4(0.94, 0.72, 0.66, 1.0)
-corniceColor: Vec4(1.00, 0.42, 0.27, 1.0)
-corniceColor: Vec4(1.00, 0.50, 0.38, 1.0)
-corniceColor: Vec4(1.00, 0.69, 0.49, 1.0)
-corniceColor: Vec4(1.00, 0.71, 0.27, 1.0)
-corniceColor: Vec4(1.00, 0.84, 0.59, 1.0)
-corniceColor: Vec4(1.00, 0.88, 0.80, 1.0)
-corniceColor: Vec4(1.00, 0.92, 0.59, 1.0)
-corniceColor: Vec4(1.00, 1.00, 0.59, 1.0)
-
-doorColor: Vec4(0.45, 0.53, 0.45, 1.0)
-doorColor: Vec4(0.52, 0.61, 0.52, 1.0)
-doorColor: Vec4(0.60, 0.67, 0.54, 1.0)
-doorColor: Vec4(0.63, 0.48, 0.34, 1.0)
-doorColor: Vec4(0.79, 0.47, 0.47, 1.0)
-doorColor: Vec4(0.86, 0.48, 0.23, 1.0)
-doorColor: Vec4(0.88, 0.43, 0.43, 1.0)
-doorColor: Vec4(0.91, 0.34, 0.34, 1.0)
-doorColor: Vec4(0.94, 0.70, 0.49, 1.0)
-doorColor: Vec4(0.97, 0.55, 0.40, 1.0)
-doorColor: Vec4(1.00, 0.59, 0.59, 1.0)
-
-wallColor: Vec4(0.17, 0.44, 0.28, 1.0)
-wallColor: Vec4(0.17, 0.45, 0.23, 1.0)
-wallColor: Vec4(0.36, 0.45, 0.22, 1.0)
-wallColor: Vec4(0.38, 0.31, 0.19, 1.0)
-wallColor: Vec4(0.38, 0.68, 0.46, 1.0)
-wallColor: Vec4(0.42, 0.16, 0.16, 1.0)
-wallColor: Vec4(0.42, 0.25, 0.25, 1.0)
-wallColor: Vec4(0.44, 0.67, 0.45, 1.0)
-wallColor: Vec4(0.45, 0.31, 0.17, 1.0)
-wallColor: Vec4(0.48, 0.93, 0.74, 1.0)
-wallColor: Vec4(0.52, 0.96, 0.79, 1.0)
-wallColor: Vec4(0.57, 0.70, 0.35, 1.0)
-wallColor: Vec4(0.63, 0.47, 0.24, 1.0)
-wallColor: Vec4(0.63, 0.60, 0.40, 1.0)
-wallColor: Vec4(0.64, 0.24, 0.32, 1.0)
-wallColor: Vec4(0.71, 0.49, 0.35, 1.0)
-wallColor: Vec4(0.75, 0.45, 0.45, 1.0)
-wallColor: Vec4(0.81, 0.48, 0.48, 1.0)
-wallColor: Vec4(0.87, 0.61, 0.61, 1.0)
-wallColor: Vec4(0.87, 0.69, 0.42, 1.0)
-wallColor: Vec4(0.91, 0.54, 0.54, 1.0)
-wallColor: Vec4(0.92, 0.63, 0.42, 1.0)
-wallColor: Vec4(0.93, 0.15, 0.15, 1.0)
-wallColor: Vec4(1.00, 0.00, 0.00, 1.0)
-
-windowColor: Vec4(0.00, 0.53, 0.32, 1.0)
-windowColor: Vec4(0.00, 0.61, 0.36, 1.0)
-windowColor: Vec4(0.07, 0.47, 0.31, 1.0)
-windowColor: Vec4(0.08, 0.47, 0.31, 1.0)
-windowColor: Vec4(0.17, 0.64, 0.45, 1.0)
-windowColor: Vec4(0.50, 0.38, 0.22, 1.0)
-windowColor: Vec4(0.64, 0.93, 0.45, 1.0)
-windowColor: Vec4(0.69, 0.44, 0.15, 1.0)
-windowColor: Vec4(0.73, 0.46, 0.37, 1.0)
-windowColor: Vec4(0.73, 0.63, 0.37, 1.0)
-windowColor: Vec4(0.73, 0.63, 0.45, 1.0)
-windowColor: Vec4(0.74, 0.55, 0.32, 1.0)
-windowColor: Vec4(0.78, 0.49, 0.29, 1.0)
-windowColor: Vec4(0.79, 0.47, 0.47, 1.0)
-windowColor: Vec4(0.82, 0.82, 0.40, 1.0)
-windowColor: Vec4(0.87, 0.55, 0.33, 1.0)
-windowColor: Vec4(0.87, 0.65, 0.33, 1.0)
-windowColor: Vec4(0.88, 0.43, 0.43, 1.0)
-windowColor: Vec4(0.91, 0.34, 0.45, 1.0)
-windowColor: Vec4(1.00, 0.50, 0.38, 1.0)
-windowColor: Vec4(1.00, 0.63, 0.38, 1.0)
-
-propColor: Vec4(0.17, 0.44, 0.28, 1.0)
-propColor: Vec4(0.17, 0.45, 0.23, 1.0)
-propColor: Vec4(0.36, 0.45, 0.22, 1.0)
-propColor: Vec4(0.38, 0.31, 0.19, 1.0)
-propColor: Vec4(0.38, 0.68, 0.46, 1.0)
-propColor: Vec4(0.42, 0.16, 0.16, 1.0)
-propColor: Vec4(0.42, 0.25, 0.25, 1.0)
-propColor: Vec4(0.44, 0.67, 0.45, 1.0)
-propColor: Vec4(0.45, 0.31, 0.17, 1.0)
-propColor: Vec4(0.48, 0.93, 0.74, 1.0)
-propColor: Vec4(0.52, 0.96, 0.79, 1.0)
-propColor: Vec4(0.57, 0.70, 0.35, 1.0)
-propColor: Vec4(0.63, 0.47, 0.24, 1.0)
-propColor: Vec4(0.63, 0.60, 0.40, 1.0)
-propColor: Vec4(0.64, 0.24, 0.32, 1.0)
-propColor: Vec4(0.71, 0.49, 0.35, 1.0)
-propColor: Vec4(0.75, 0.45, 0.45, 1.0)
-propColor: Vec4(0.81, 0.48, 0.48, 1.0)
-propColor: Vec4(0.87, 0.61, 0.61, 1.0)
-propColor: Vec4(0.87, 0.69, 0.42, 1.0)
-propColor: Vec4(0.91, 0.54, 0.54, 1.0)
-propColor: Vec4(0.92, 0.63, 0.42, 1.0)
-propColor: Vec4(0.93, 0.15, 0.15, 1.0)
-propColor: Vec4(1.00, 0.00, 0.00, 1.0)

+ 0 - 64
dmodels/src/level_editor/donaldsDockStyles.txt

@@ -1,64 +0,0 @@
-wallTexture: wall_md_blank_ur
-wallColor: Vec4(0.417323, 0.15711, 0.15711, 1.0)
-windowTexture: window_sm_square_ur
-windowColor: Vec4(0.874016, 0.654655, 0.329041, 1.0)
-corniceTexture: cornice_marble_ur
-corniceColor: Vec4(0.76378, 0.572086, 0.287541, 1.0)
-
-wallTexture: wall_sm_wood_ur
-wallColor: Vec4(0.874016, 0.610097, 0.610097, 1.0)
-windowTexture: window_sm_shuttered_ur
-windowColor: Vec4(0.874016, 0.548402, 0.329041, 1.0)
-corniceTexture: None
-corniceColor: None
-
-wallTexture: wall_sm_wood_ur
-wallColor: Vec4(0.913386, 0.540868, 0.540868, 1.0)
-windowTexture: window_porthole_ur
-windowColor: Vec4(0.0778138, 0.472441, 0.314961, 1.0)
-corniceTexture: cornice_horizontal_ur
-corniceColor: Vec4(1.0, 0.501961, 0.376471, 1.0)
-
-wallTexture: wall_sm_wood_ur
-wallColor: Vec4(0.913386, 0.540868, 0.540868, 1.0)
-windowTexture: window_porthole_ur
-windowColor: Vec4(0.0778138, 0.472441, 0.314961, 1.0)
-corniceTexture: cornice_shingles_ur
-corniceColor: Vec4(0.732283, 0.511163, 0.511163, 1.0)
-
-wallTexture: wall_md_blank_ur
-wallColor: Vec4(0.384314, 0.305635, 0.187618, 1.0)
-windowTexture: window_sm_round_ur
-windowColor: Vec4(0.779528, 0.489115, 0.293469, 1.0)
-corniceTexture: cornice_dental_ur
-corniceColor: Vec4(0.574803, 0.38771, 0.340374, 1.0)
-
-wallTexture: wall_bricks_dr
-wallColor: Vec4(0.629921, 0.471823, 0.237147, 1.0)
-windowTexture: window_sm_shuttered_ur
-windowColor: Vec4(1.0, 0.627451, 0.376471, 1.0)
-corniceTexture: None
-corniceColor: None
-
-wallTexture: wall_md_board_ur
-wallColor: Vec4(0.929134, 0.153034, 0.153034, 1.0)
-windowTexture: window_porthole_ur
-windowColor: Vec4(0.0, 0.532747, 0.317894, 1.0)
-corniceTexture: cornice_shingles_ur
-corniceColor: Vec4(0.944882, 0.715146, 0.659565, 1.0)
-
-wallTexture: wall_lg_brick_ur
-wallColor: Vec4(0.166003, 0.440945, 0.276671, 1.0)
-windowTexture: window_md_curtains_ur
-windowColor: Vec4(0.17258, 0.637795, 0.450208, 1.0)
-corniceTexture: None
-corniceColor: None
-
-wallTexture: wall_md_board_ur
-wallColor: Vec4(0.929134, 0.153034, 0.153034, 1.0)
-windowTexture: window_porthole_ur
-windowColor: Vec4(0.0, 0.532747, 0.317894, 1.0)
-corniceTexture: None
-corniceColor: None
-
-

BIN
dmodels/src/level_editor/donalds_dock_layout.flt


+ 0 - 61
dmodels/src/level_editor/minniesMelodyLandColors.txt

@@ -1,61 +0,0 @@
-corniceColor: Vec4(0.43, 0.32, 0.85, 1.0)
-corniceColor: Vec4(0.67, 0.40, 0.99, 1.0)
-corniceColor: Vec4(0.68, 0.22, 0.80, 1.0)
-corniceColor: Vec4(0.80, 0.22, 0.68, 1.0)
-corniceColor: Vec4(0.81, 0.30, 0.51, 1.0)
-corniceColor: Vec4(1.00, 1.00, 0.58, 1.0)
-
-doorColor: Vec4(0.42, 0.42, 0.85, 1.0)
-doorColor: Vec4(0.45, 0.22, 0.60, 1.0)
-doorColor: Vec4(0.49, 0.79, 1.00, 1.0)
-doorColor: Vec4(0.56, 0.80, 0.65, 1.0)
-doorColor: Vec4(0.59, 0.49, 1.00, 1.0)
-doorColor: Vec4(0.64, 0.45, 0.92, 1.0)
-doorColor: Vec4(0.65, 0.40, 0.82, 1.0)
-doorColor: Vec4(0.69, 0.33, 0.69, 1.0)
-doorColor: Vec4(0.80, 0.56, 0.65, 1.0)
-doorColor: Vec4(0.87, 0.80, 0.47, 1.0)
-doorColor: Vec4(0.97, 0.87, 0.47, 1.0)
-doorColor: Vec4(1.00, 0.42, 0.85, 1.0)
-doorColor: Vec4(1.00, 0.79, 0.49, 1.0)
-
-wallColor: Vec4(0.49, 0.69, 1.00, 1.0)
-wallColor: Vec4(0.59, 0.75, 1.00, 1.0)
-wallColor: Vec4(0.70, 0.76, 1.00, 1.0)
-wallColor: Vec4(0.80, 0.47, 0.80, 1.0)
-wallColor: Vec4(0.80, 0.80, 0.47, 1.0)
-wallColor: Vec4(0.91, 0.91, 0.54, 1.0)
-wallColor: Vec4(0.96, 0.96, 0.47, 1.0)
-wallColor: Vec4(0.96, 0.96, 0.73, 1.0)
-wallColor: Vec4(0.97, 0.36, 0.36, 1.0)
-wallColor: Vec4(0.97, 0.47, 0.67, 1.0)
-wallColor: Vec4(1.00, 0.70, 0.70, 1.0)
-wallColor: Vec4(1.00, 1.00, 1.00, 1.0)
-
-windowColor: Vec4(0.42, 0.42, 0.85, 1.0)
-windowColor: Vec4(0.45, 0.22, 0.60, 1.0)
-windowColor: Vec4(0.49, 0.79, 1.00, 1.0)
-windowColor: Vec4(0.56, 0.80, 0.65, 1.0)
-windowColor: Vec4(0.59, 0.49, 1.00, 1.0)
-windowColor: Vec4(0.64, 0.45, 0.92, 1.0)
-windowColor: Vec4(0.65, 0.40, 0.82, 1.0)
-windowColor: Vec4(0.69, 0.33, 0.69, 1.0)
-windowColor: Vec4(0.80, 0.45, 0.71, 1.0)
-windowColor: Vec4(0.80, 0.56, 0.65, 1.0)
-windowColor: Vec4(0.97, 0.87, 0.47, 1.0)
-windowColor: Vec4(1.00, 0.79, 0.49, 1.0)
-
-propColor: Vec4(0.49, 0.69, 1.00, 1.0)
-propColor: Vec4(0.59, 0.75, 1.00, 1.0)
-propColor: Vec4(0.70, 0.76, 1.00, 1.0)
-propColor: Vec4(0.80, 0.47, 0.80, 1.0)
-propColor: Vec4(0.80, 0.80, 0.47, 1.0)
-propColor: Vec4(0.91, 0.91, 0.54, 1.0)
-propColor: Vec4(0.96, 0.96, 0.47, 1.0)
-propColor: Vec4(0.96, 0.96, 0.73, 1.0)
-propColor: Vec4(0.97, 0.36, 0.36, 1.0)
-propColor: Vec4(0.97, 0.47, 0.67, 1.0)
-propColor: Vec4(1.00, 0.70, 0.70, 1.0)
-propColor: Vec4(1.00, 1.00, 1.00, 1.0)
-propColor: Vec4(0.92, 0.92, 0.92, 1.0)
-wallColor: Vec4(0.97, 0.58, 0.55, 1.0)

+ 0 - 79
dmodels/src/level_editor/minniesMelodyLandStyles.txt

@@ -1,79 +0,0 @@
-wallTexture: wall_md_bricks_ur
-wallColor: Vec4(0.59, 0.75, 1.00, 1.0)
-windowTexture: window_sm_round_ur
-windowColor: Vec4(0.45, 0.22, 0.60, 1.0)
-corniceTexture: None
-corniceColor: None
-
-wallTexture: wall_md_blank_ur
-wallColor: Vec4(0.91, 0.91, 0.54, 1.0)
-windowTexture: window_sm_square_ur
-windowColor: Vec4(0.65, 0.40, 0.82, 1.0)
-corniceTexture: cornice_curved_ur
-corniceColor: Vec4(0.68, 0.22, 0.80, 1.0)
-
-wallTexture: wall_sm_brick_pink_ur
-wallColor: Vec4(1.00, 1.00, 1.00, 1.0)
-windowTexture: window_sm_curved_ur
-windowColor: Vec4(0.49, 0.79, 1.00, 1.0)
-corniceTexture: None
-corniceColor: None
-
-wallTexture: wall_lg_rock_ur
-wallColor: Vec4(0.80, 0.47, 0.80, 1.0)
-windowTexture: window_sm_square_ur
-windowColor: Vec4(0.80, 0.56, 0.65, 1.0)
-corniceTexture: None
-corniceColor: None
-
-wallTexture: wall_lg_rock_ur
-wallColor: Vec4(0.80, 0.80, 0.47, 1.0)
-windowTexture: window_sm_square_ur
-windowColor: Vec4(0.56, 0.80, 0.65, 1.0)
-corniceTexture: cornice_marble_ur
-corniceColor: Vec4(0.80, 0.22, 0.68, 1.0)
-
-wallTexture: wall_md_blank_ur
-wallColor: Vec4(0.49, 0.69, 1.00, 1.0)
-windowTexture: window_sm_curved_ur
-windowColor: Vec4(0.59, 0.49, 1.00, 1.0)
-corniceTexture: None
-corniceColor: None
-
-wallTexture: wall_bricks_ur
-wallColor: Vec4(1.00, 0.70, 0.70, 1.0)
-windowTexture: window_md_curved_ur
-windowColor: Vec4(1.00, 0.79, 0.49, 1.0)
-corniceTexture: None
-corniceColor: None
-
-wallTexture: wall_md_blank_ur
-wallColor: Vec4(0.96, 0.96, 0.47, 1.0)
-windowTexture: window_sm_round_ur
-windowColor: Vec4(0.69, 0.33, 0.69, 1.0)
-corniceTexture: cornice_brick_ur
-corniceColor: Vec4(0.81, 0.30, 0.51, 1.0)
-
-wallTexture: wall_md_bricks_ur
-wallColor: Vec4(0.70, 0.76, 1.00, 1.0)
-windowTexture: window_porthole_ur
-windowColor: Vec4(0.42, 0.42, 0.85, 1.0)
-corniceTexture: None
-corniceColor: None
-
-wallTexture: wall_lg_brick_ur
-wallColor: Vec4(0.97, 0.47, 0.67, 1.0)
-windowTexture: window_md_curtains_ur
-windowColor: Vec4(0.64, 0.45, 0.92, 1.0)
-corniceTexture: None
-corniceColor: None
-
-wallTexture: wall_md_blank_ur
-wallColor: Vec4(0.97, 0.36, 0.36, 1.0)
-windowTexture: window_sm_curved_ur
-windowColor: Vec4(0.97, 0.87, 0.47, 1.0)
-corniceTexture: cornice_curved_ur
-corniceColor: Vec4(0.43, 0.32, 0.85, 1.0)
-
-
-

BIN
dmodels/src/level_editor/minnies_melody_land_layout.flt


+ 0 - 65
dmodels/src/level_editor/theBurrrghColors.txt

@@ -1,65 +0,0 @@
-corniceColor: Vec4(0.00, 0.34, 0.58, 1.0)
-corniceColor: Vec4(0.34, 0.90, 0.87, 1.0)
-corniceColor: Vec4(0.41, 0.58, 0.85, 1.0)
-corniceColor: Vec4(0.42, 0.98, 0.74, 1.0)
-corniceColor: Vec4(1.00, 1.00, 1.00, 1.0)
-
-doorColor: Vec4(0.00, 0.87, 0.69, 1.0)
-doorColor: Vec4(0.22, 0.83, 0.83, 1.0)
-doorColor: Vec4(0.23, 0.71, 0.84, 1.0)
-doorColor: Vec4(0.23, 0.85, 0.60, 1.0)
-doorColor: Vec4(0.24, 0.76, 0.89, 1.0)
-doorColor: Vec4(0.27, 1.00, 0.85, 1.0)
-doorColor: Vec4(0.38, 0.63, 1.00, 1.0)
-doorColor: Vec4(0.49, 0.69, 1.00, 1.0)
-doorColor: Vec4(0.49, 0.89, 1.00, 1.0)
-doorColor: Vec4(0.50, 0.64, 0.85, 1.0)
-doorColor: Vec4(0.59, 1.00, 0.92, 1.0)
-doorColor: Vec4(1.00, 1.00, 1.00, 1.0)
-
-wallColor: Vec4(0.00, 0.87, 0.69, 1.0)
-wallColor: Vec4(0.22, 0.83, 0.83, 1.0)
-wallColor: Vec4(0.23, 0.85, 0.60, 1.0)
-wallColor: Vec4(0.24, 0.76, 0.89, 1.0)
-wallColor: Vec4(0.25, 0.64, 0.91, 1.0)
-wallColor: Vec4(0.27, 1.00, 0.85, 1.0)
-wallColor: Vec4(0.38, 0.63, 1.00, 1.0)
-wallColor: Vec4(0.49, 0.69, 1.00, 1.0)
-wallColor: Vec4(0.49, 0.89, 1.00, 1.0)
-wallColor: Vec4(0.50, 0.64, 0.85, 1.0)
-wallColor: Vec4(0.59, 1.00, 0.92, 1.0)
-wallColor: Vec4(1.00, 1.00, 1.00, 1.0)
-
-windowColor: Vec4(0.00, 0.69, 0.69, 1.0)
-windowColor: Vec4(0.00, 0.87, 0.69, 1.0)
-windowColor: Vec4(0.14, 0.42, 0.85, 1.0)
-windowColor: Vec4(0.16, 0.67, 1.00, 1.0)
-windowColor: Vec4(0.20, 0.53, 0.46, 1.0)
-windowColor: Vec4(0.22, 0.83, 0.83, 1.0)
-windowColor: Vec4(0.23, 0.71, 0.84, 1.0)
-windowColor: Vec4(0.23, 0.85, 0.60, 1.0)
-windowColor: Vec4(0.24, 0.76, 0.89, 1.0)
-windowColor: Vec4(0.27, 1.00, 0.85, 1.0)
-windowColor: Vec4(0.38, 0.63, 1.00, 1.0)
-windowColor: Vec4(0.38, 0.87, 1.00, 1.0)
-windowColor: Vec4(0.49, 0.69, 1.00, 1.0)
-windowColor: Vec4(0.49, 0.89, 1.00, 1.0)
-windowColor: Vec4(0.49, 1.00, 1.00, 1.0)
-windowColor: Vec4(0.50, 0.64, 0.85, 1.0)
-windowColor: Vec4(0.59, 0.85, 0.69, 1.0)
-windowColor: Vec4(0.59, 0.92, 0.61, 1.0)
-windowColor: Vec4(0.59, 1.00, 0.92, 1.0)
-windowColor: Vec4(1.00, 1.00, 1.00, 1.0)
-
-propColor: Vec4(0.00, 0.87, 0.69, 1.0)
-propColor: Vec4(0.22, 0.83, 0.83, 1.0)
-propColor: Vec4(0.23, 0.85, 0.60, 1.0)
-propColor: Vec4(0.24, 0.76, 0.89, 1.0)
-propColor: Vec4(0.25, 0.64, 0.91, 1.0)
-propColor: Vec4(0.27, 1.00, 0.85, 1.0)
-propColor: Vec4(0.38, 0.63, 1.00, 1.0)
-propColor: Vec4(0.49, 0.69, 1.00, 1.0)
-propColor: Vec4(0.49, 0.89, 1.00, 1.0)
-propColor: Vec4(0.50, 0.64, 0.85, 1.0)
-propColor: Vec4(0.59, 1.00, 0.92, 1.0)
-propColor: Vec4(1.00, 1.00, 1.00, 1.0)

+ 0 - 90
dmodels/src/level_editor/theBurrrghStyles.txt

@@ -1,90 +0,0 @@
-wallTexture: wall_lg_brick_ur
-wallColor: Vec4(0.25, 0.64, 0.91, 1.0)
-windowTexture: window_sm_round_ur
-windowColor: Vec4(0.23, 0.71, 0.84, 1.0)
-corniceTexture: None
-corniceColor: None
-
-wallTexture: wall_md_blank_ur
-wallColor: Vec4(0.00, 0.87, 0.69, 1.0)
-windowTexture: window_sm_round_ur
-windowColor: Vec4(0.23, 0.71, 0.84, 1.0)
-corniceTexture: cornice_curved_ur
-corniceColor: Vec4(0.00, 0.34, 0.58, 1.0)
-
-wallTexture: wall_md_blank_ur
-wallColor: Vec4(0.49, 0.69, 1.00, 1.0)
-windowTexture: window_sm_curved_ur
-windowColor: Vec4(0.20, 0.53, 0.46, 1.0)
-corniceTexture: cornice_ice_ur
-corniceColor: Vec4(1.00, 1.00, 1.00, 1.0)
-
-wallTexture: wall_sm_brick_blue_ur
-wallColor: Vec4(1.00, 1.00, 1.00, 1.0)
-windowTexture: window_sm_square_ur
-windowColor: Vec4(0.49, 1.00, 1.00, 1.0)
-corniceTexture: None
-corniceColor: None
-
-wallTexture: wall_md_blank_ur
-wallColor: Vec4(0.27, 1.00, 0.85, 1.0)
-windowTexture: window_sm_square_ur
-windowColor: Vec4(0.49, 1.00, 1.00, 1.0)
-corniceTexture: None
-corniceColor: None
-
-wallTexture: wall_md_blank_ur
-wallColor: Vec4(0.49, 0.89, 1.00, 1.0)
-windowTexture: window_sm_pointed_ur
-windowColor: Vec4(0.00, 0.69, 0.69, 1.0)
-corniceTexture: cornice_ice_ur
-corniceColor: Vec4(1.00, 1.00, 1.00, 1.0)
-
-wallTexture: wall_md_bricks_ur
-wallColor: Vec4(0.50, 0.64, 0.85, 1.0)
-windowTexture: window_porthole_ur
-windowColor: Vec4(0.14, 0.42, 0.85, 1.0)
-corniceTexture: None
-corniceColor: None
-
-wallTexture: wall_md_blank_ur
-wallColor: Vec4(0.38, 0.63, 1.00, 1.0)
-windowTexture: window_porthole_ur
-windowColor: Vec4(0.14, 0.42, 0.85, 1.0)
-corniceTexture: cornice_dental_ur
-corniceColor: Vec4(0.41, 0.58, 0.85, 1.0)
-
-wallTexture: wall_bricks_ur
-wallColor: Vec4(0.23, 0.85, 0.60, 1.0)
-windowTexture: window_sm_square_ur
-windowColor: Vec4(0.59, 0.85, 0.69, 1.0)
-corniceTexture: None
-corniceColor: None
-
-wallTexture: wall_md_bricks_ur
-wallColor: Vec4(0.59, 1.00, 0.92, 1.0)
-windowTexture: window_md_curved_ur
-windowColor: Vec4(0.16, 0.67, 1.00, 1.0)
-corniceTexture: None
-corniceColor: None
-
-wallTexture: wall_md_blank_ur
-wallColor: Vec4(0.22, 0.83, 0.83, 1.0)
-windowTexture: window_sm_curved_ur
-windowColor: Vec4(0.38, 0.87, 1.00, 1.0)
-corniceTexture: cornice_ice_ur
-corniceColor: Vec4(1.00, 1.00, 1.00, 1.0)
-
-wallTexture: wall_sm_cement_blue_ur
-wallColor: Vec4(1.00, 1.00, 1.00, 1.0)
-windowTexture: window_sm_round_ur
-windowColor: Vec4(0.59, 0.92, 0.61, 1.0)
-corniceTexture: None
-corniceColor: None
-
-wallTexture: wall_bricks_ur
-wallColor: Vec4(0.24, 0.76, 0.89, 1.0)
-windowTexture: window_sm_round_ur
-windowColor: Vec4(0.59, 0.92, 0.61, 1.0)
-corniceTexture: None
-corniceColor: None

BIN
dmodels/src/level_editor/the_burrrgh_layout.flt


+ 0 - 106
dmodels/src/level_editor/toontownCentralColors.txt

@@ -1,106 +0,0 @@
-corniceColor: Vec4(0.11, 0.80, 0.22, 1.0)
-corniceColor: Vec4(0.21, 0.73, 0.31, 1.0)
-corniceColor: Vec4(0.22, 0.96, 0.27, 1.0)
-corniceColor: Vec4(0.27, 0.77, 0.42, 1.0)
-corniceColor: Vec4(0.32, 0.54, 0.36, 1.0)
-corniceColor: Vec4(0.44, 0.91, 0.63, 1.0)
-corniceColor: Vec4(0.76, 0.73, 0.64, 1.0)
-corniceColor: Vec4(0.79, 0.36, 0.19, 1.0)
-corniceColor: Vec4(0.82, 0.91, 0.44, 1.0)
-corniceColor: Vec4(0.90, 0.56, 0.34, 1.0)
-corniceColor: Vec4(0.91, 0.54, 0.44, 1.0)
-corniceColor: Vec4(0.91, 0.63, 0.44, 1.0)
-corniceColor: Vec4(0.94, 0.35, 0.35, 1.0)
-corniceColor: Vec4(0.94, 0.71, 0.66, 1.0)
-corniceColor: Vec4(0.98, 0.58, 0.42, 1.0)
-corniceColor: Vec4(1.00, 0.42, 0.27, 1.0)
-corniceColor: Vec4(1.00, 0.56, 0.21, 1.0)
-corniceColor: Vec4(1.00, 0.69, 0.49, 1.0)
-corniceColor: Vec4(1.00, 0.71, 0.27, 1.0)
-corniceColor: Vec4(1.00, 0.75, 0.38, 1.0)
-corniceColor: Vec4(1.00, 0.84, 0.59, 1.0)
-corniceColor: Vec4(1.00, 0.88, 0.80, 1.0)
-corniceColor: Vec4(1.00, 0.92, 0.59, 1.0)
-corniceColor: Vec4(1.00, 1.00, 0.59, 1.0)
-
-doorColor: Vec4(0.75, 0.79, 0.55, 1.0)
-doorColor: Vec4(0.81, 0.61, 0.48, 1.0)
-doorColor: Vec4(1.00, 0.59, 0.59, 1.0)
-doorColor: Vec4(1.00, 0.63, 0.38, 1.0)
-doorColor: Vec4(1.00, 0.87, 0.38, 1.0)
-doorColor: Vec4(1.00, 0.88, 0.56, 1.0)
-doorColor: Vec4(1.00, 0.88, 0.56, 1.0)
-
-wallColor: Vec4(0.11, 0.80, 0.22, 1.0)
-wallColor: Vec4(0.21, 0.73, 0.31, 1.0)
-wallColor: Vec4(0.22, 0.96, 0.27, 1.0)
-wallColor: Vec4(0.24, 0.79, 0.32, 1.0)
-wallColor: Vec4(0.25, 0.65, 0.29, 1.0)
-wallColor: Vec4(0.49, 0.96, 0.42, 1.0)
-wallColor: Vec4(0.76, 0.73, 0.64, 1.0)
-wallColor: Vec4(0.82, 0.91, 0.44, 1.0)
-wallColor: Vec4(0.90, 0.56, 0.34, 1.0)
-wallColor: Vec4(0.91, 0.54, 0.44, 1.0)
-wallColor: Vec4(0.91, 0.63, 0.44, 1.0)
-wallColor: Vec4(0.94, 0.35, 0.35, 1.0)
-wallColor: Vec4(0.95, 0.89, 0.17, 1.0)
-wallColor: Vec4(0.99, 0.67, 0.25, 1.0)
-wallColor: Vec4(0.99, 0.69, 0.49, 1.0)
-wallColor: Vec4(0.99, 0.89, 0.49, 1.0)
-wallColor: Vec4(1.00, 0.42, 0.27, 1.0)
-wallColor: Vec4(1.00, 0.67, 0.59, 1.0)
-wallColor: Vec4(1.00, 0.69, 0.49, 1.0)
-wallColor: Vec4(1.00, 0.71, 0.27, 1.0)
-wallColor: Vec4(1.00, 0.84, 0.59, 1.0)
-wallColor: Vec4(1.00, 0.88, 0.80, 1.0)
-wallColor: Vec4(1.00, 0.90, 0.33, 1.0)
-wallColor: Vec4(1.00, 0.92, 0.59, 1.0)
-wallColor: Vec4(1.00, 1.00, 0.59, 1.0)
-
-windowColor: Vec4(0.10, 0.59, 0.39, 1.0)
-windowColor: Vec4(0.11, 0.67, 0.33, 1.0)
-windowColor: Vec4(0.13, 0.78, 0.52, 1.0)
-windowColor: Vec4(0.14, 0.53, 0.30, 1.0)
-windowColor: Vec4(0.27, 1.00, 0.42, 1.0)
-windowColor: Vec4(0.31, 0.63, 0.37, 1.0)
-windowColor: Vec4(0.32, 0.54, 0.36, 1.0)
-windowColor: Vec4(0.32, 0.67, 0.60, 1.0)
-windowColor: Vec4(0.40, 0.61, 0.67, 1.0)
-windowColor: Vec4(0.59, 1.00, 0.49, 1.0)
-windowColor: Vec4(0.88, 0.44, 0.15, 1.0)
-windowColor: Vec4(0.91, 0.63, 0.44, 1.0)
-windowColor: Vec4(0.95, 0.89, 0.17, 1.0)
-windowColor: Vec4(0.99, 0.99, 0.49, 1.0)
-windowColor: Vec4(1.00, 0.27, 0.27, 1.0)
-windowColor: Vec4(1.00, 0.50, 0.38, 1.0)
-windowColor: Vec4(1.00, 0.59, 0.59, 1.0)
-windowColor: Vec4(1.00, 0.63, 0.30, 1.0)
-windowColor: Vec4(1.00, 0.63, 0.38, 1.0)
-windowColor: Vec4(1.00, 0.87, 0.38, 1.0)
-windowColor: Vec4(1.00, 0.92, 0.59, 1.0)
-
-propColor: Vec4(0.11, 0.80, 0.22, 1.0)
-propColor: Vec4(0.21, 0.73, 0.31, 1.0)
-propColor: Vec4(0.22, 0.96, 0.27, 1.0)
-propColor: Vec4(0.24, 0.79, 0.32, 1.0)
-propColor: Vec4(0.25, 0.65, 0.29, 1.0)
-propColor: Vec4(0.49, 0.96, 0.42, 1.0)
-propColor: Vec4(0.76, 0.73, 0.64, 1.0)
-propColor: Vec4(0.82, 0.91, 0.44, 1.0)
-propColor: Vec4(0.90, 0.56, 0.34, 1.0)
-propColor: Vec4(0.91, 0.54, 0.44, 1.0)
-propColor: Vec4(0.91, 0.63, 0.44, 1.0)
-propColor: Vec4(0.94, 0.35, 0.35, 1.0)
-propColor: Vec4(0.95, 0.89, 0.17, 1.0)
-propColor: Vec4(0.99, 0.67, 0.25, 1.0)
-propColor: Vec4(0.99, 0.69, 0.49, 1.0)
-propColor: Vec4(0.99, 0.89, 0.49, 1.0)
-propColor: Vec4(1.00, 0.42, 0.27, 1.0)
-propColor: Vec4(1.00, 0.67, 0.59, 1.0)
-propColor: Vec4(1.00, 0.69, 0.49, 1.0)
-propColor: Vec4(1.00, 0.71, 0.27, 1.0)
-propColor: Vec4(1.00, 0.84, 0.59, 1.0)
-propColor: Vec4(1.00, 0.88, 0.80, 1.0)
-propColor: Vec4(1.00, 0.90, 0.33, 1.0)
-propColor: Vec4(1.00, 0.92, 0.59, 1.0)
-propColor: Vec4(1.00, 1.00, 0.59, 1.0)

+ 0 - 84
dmodels/src/level_editor/toontownCentralStyles.txt

@@ -1,84 +0,0 @@
-wallTexture: wall_md_pillars_ur
-wallColor: Vec4(1.0, 0.917, 0.592, 1.0)
-windowTexture: window_sm_pointed_ur
-windowColor: Vec4(0.396,  0.611,  0.666,  1.0)
-corniceTexture: cornice_stone_ur
-corniceColor: Vec4(1.0, 1.0, 0.592,  1.0)
-
-wallTexture: wall_md_pillars_ur
-wallColor: Vec4(1.0, 1.0, 0.592157, 1.0)
-windowTexture: window_sm_pointed_ur
-windowColor: Vec4(0.142751, 0.527559, 0.295847, 1.0)
-corniceTexture: cornice_stone_ur
-corniceColor: Vec4(1.0, 1.0, 0.592157, 1.0)
-        
-wallTexture: wall_lg_brick_ur
-wallColor: Vec4(1.0, 0.415686, 0.270588, 1.0)
-windowTexture: window_porthole_ur
-windowColor: Vec4(0.306315, 0.627451, 0.370542, 1.0)
-corniceTexture: None
-corniceColor: None
-        
-wallTexture: wall_sm_cement_ur
-wallColor: Vec4(1.0, 0.882353, 0.803922, 1.0)
-windowTexture: window_porthole_ur
-windowColor: Vec4(0.0972673, 0.590551, 0.393701, 1.0)
-corniceTexture: None
-corniceColor: None
-
-wallTexture: wall_md_dental_ur
-wallColor: Vec4(0.996078, 0.894118, 0.486275, 1.0)
-windowTexture: window_md_curved_ur
-windowColor: Vec4(1.0, 0.87451, 0.376471, 1.0)
-corniceTexture: None
-corniceColor: None
-        
-wallTexture: wall_md_pillars_ur
-wallColor: Vec4(0.996078, 0.690196, 0.486275, 1.0)
-windowTexture: window_porthole_ur
-windowColor: Vec4(0.31706, 0.535433, 0.361155, 1.0)
-corniceTexture: None
-corniceColor: None
-        
-wallTexture: wall_md_pillars_ur
-wallColor: Vec4(0.996078, 0.690196, 0.486275, 1.0)
-windowTexture: window_porthole_ur
-windowColor: Vec4(0.31706, 0.535433, 0.361155, 1.0)
-corniceTexture: cornice_brick_ur
-corniceColor: Vec4(0.31706, 0.535433, 0.361155, 1.0)
-
-wallTexture: wall_lg_brick_ur
-wallColor: Vec4(0.996078, 0.690196, 0.486275, 1.0)
-windowTexture: window_sm_curved_ur
-windowColor: Vec4(0.996078, 0.996078, 0.486275, 1.0)
-corniceTexture: None
-corniceColor: None
-
-wallTexture: wall_md_blank_ur
-wallColor: Vec4(1.0, 0.415686, 0.270588, 1.0)
-windowTexture: window_sm_curved_ur
-windowColor: Vec4(1.0, 0.87451, 0.376471, 1.0)
-corniceTexture: cornice_marble_ur
-corniceColor: Vec4(1.0, 0.74902, 0.376471, 1.0)
-
-wallTexture: wall_sm_brick_ur
-wallColor: Vec4(1.0, 0.67451, 0.592157, 1.0)
-windowTexture: window_sm_pointed_ur
-windowColor: Vec4(0.88189, 0.439216, 0.145252, 1.0)
-corniceTexture: None
-corniceColor: None
-
-wallTexture: wall_md_blank_ur
-wallColor: Vec4(1.0, 0.705882, 0.270588, 1.0)
-windowTexture: window_sm_pointed_ur
-windowColor: Vec4(0.110236, 0.669291, 0.333333, 1.0)
-corniceTexture: cornice_stone_ur
-corniceColor: Vec4(0.944882, 0.711441, 0.559518, 1.0)
-
-wallTexture: wall_md_dental_ur
-wallColor: Vec4(0.909804, 0.630415, 0.444156, 1.0)
-windowTexture: window_sm_round_ur
-windowColor: Vec4(1.0, 0.270588, 0.270588, 1.0)
-corniceTexture: None
-corniceColor: None
-

BIN
dmodels/src/level_editor/toontown_central_layout.flt


+ 43 - 0
doc/ReleaseNotes

@@ -1,3 +1,46 @@
+------------------------  RELEASE 1.10.4.1  ---------------------
+
+This release fixes only one critical regression: calling destroy()
+on a DirectGUI item would cause an exception.
+
+------------------------  RELEASE 1.10.4  -----------------------
+
+This release fixes a regression with DirectScrolledList in 1.10.3,
+fixes various other bugs, and introduces a few minor features.
+
+* Fix exception trying to create DirectScrolledList
+* Fix flickering in DirectScrolledFrame and other scissor issues (#681)
+* Experimental support for Python 3.8
+* Support adding icons to deployed applications
+* Support non-affine (eg. projective) transforms in calc_tight_bounds
+* Allow setting notify-output after initial import
+* Fix macOS issue locating Panda3D using Python 2.7.13+ from python.org
+* Support for Maya 2019
+* On Windows, pip is now installed by the installer (#690)
+* Fix Actor.makeSubpart on models with pre-bound animations (#647)
+* Properly interrupt task manager if first task chain raises error (#692)
+* Fix return value of encrypt_string in Python 3 (#684)
+* Support writing loader plug-ins in Python
+* Fix reading multiple p3d_TextureMatrix[] values from GLSL shaders
+* Fix shader error flag not being set if GLSL compilation failed (#622)
+* Add NodePath.replace_texture() convenience method
+* Fix deadlock when building with SIMPLE_THREADS=1 (#704)
+* Fix DirectOptionMenu cancelFrame not working inside scrolled frame (#658)
+* Fix assertion when calling analyze() on geometry with strip cut index
+* Implement fallback in GL renderer when F_sluminance is not supported (#693)
+* Set reasonable limits for sliders in ParticlePanel
+* Fix for DirectEntry autoCapitalize feature on Python 3 (#628)
+* Fix various DirectGUI items not working before ShowBase is instantiated
+* Work around an MSVC compiler bug in the release build
+* PythonUtil.weightedChoice now raises IndexError on empty list
+* Support changing DirectScrollBar width after initialiation (#699)
+* Workaround for Bullet deadlock when adding shape to a scaled body (#689)
+* Support setting DirectEntryScroll entry after initialization (#702)
+* Fix some missing imports in directtools (#698)
+* Fix undefined behavior issue when using musl-libc
+* Update Eigen in Windows thirdparty packages to 3.3.7
+* Update metadata of pip wheels
+
 ------------------------  RELEASE 1.10.3  -----------------------
 ------------------------  RELEASE 1.10.3  -----------------------
 
 
 This is another bugfix release that addresses a variety of issues
 This is another bugfix release that addresses a variety of issues

+ 15 - 11
dtool/src/cppparser/cppIdentifier.cxx

@@ -254,19 +254,23 @@ get_scope(CPPScope *current_scope, CPPScope *global_scope,
   }
   }
 
 
   while (i + 1 < (int)_names.size() && scope != nullptr) {
   while (i + 1 < (int)_names.size() && scope != nullptr) {
-    CPPScope *next_scope = scope->find_scope(_names[i].get_name(), global_scope);
+    // Check for an explicitly specialized scope first.
+    CPPScope *next_scope = scope->find_scope(_names[i].get_name_with_templ(), global_scope);
     if (next_scope == nullptr) {
     if (next_scope == nullptr) {
-      if (error_sink != nullptr) {
-        error_sink->error("Symbol " + _names[i].get_name() +
-                          " is not a known scope in " +
-                          scope->get_fully_scoped_name(),
-                          _loc);
+      next_scope = scope->find_scope(_names[i].get_name(), global_scope);
+      if (next_scope == nullptr) {
+        if (error_sink != nullptr) {
+          error_sink->error("Symbol " + _names[i].get_name() +
+                            " is not a known scope in " +
+                            scope->get_fully_scoped_name(),
+                            _loc);
+        }
+        return nullptr;
+      }
+      if (_names[i].has_templ()) {
+        next_scope = next_scope->instantiate(_names[i].get_templ(),
+                                             current_scope, global_scope);
       }
       }
-      return nullptr;
-    }
-    if (_names[i].has_templ()) {
-      next_scope = next_scope->instantiate(_names[i].get_templ(),
-                                           current_scope, global_scope);
     }
     }
     scope = next_scope;
     scope = next_scope;
     i++;
     i++;

+ 0 - 47
dtool/src/dtoolbase/dllbase.txt

@@ -1,47 +0,0 @@
-; This file is used by the MSVC linker to specify DLL base addresses to
-; prevent runtime relocation of DLLs by the windows loader.  This speeds
-; loading.  This file was generated using $PANDATOOL/src/scripts/basedlls.pl
-; which uses rebase.exe from the MS platform SDK.  It may additionally be
-; hand-hacked to simplify addresses for popular dlls to aid debugging.
-
-; release dlls
-
-libpanda         0x0000000060000000 0x004c0000
-libwindisplay    0x00000000604c0000 0x00010000
-libpandadx8      0x0000000061000000 0x00090000
-libpandadx7      0x0000000061000000 0x00040000
-libpandagl       0x0000000061000000 0x00030000
-libpandaexpress  0x00000000605d0000 0x00150000
-libpandaphysics  0x0000000060720000 0x00060000
-libpandafx       0x0000000060780000 0x00020000
-libdtool         0x00000000607a0000 0x00020000
-libdtoolconfig   0x00000000607c0000 0x00040000
-libpystub        0x0000000060800000 0x00010000
-libdirect        0x0000000060810000 0x00050000
-libtoontown      0x0000000060860000 0x000b0000
-; TODO put in a base for Pirates Opt 4 DLL
-libpirates       0x0000000060860000 0x000b0000
-libmiles_audio   0x0000000060910000 0x00020000
-libpandaegg      0x0000000060930000 0x00130000
-libframework     0x0000000060a60000 0x00020000
-
-; debug dlls
-
-libpanda_d       0x0000000060000000 0x007e0000
-libwindisplay_d  0x00000000607e0000 0x00020000
-libpandadx8_d    0x0000000060800000 0x000d0000
-libpandadx7_d    0x00000000608d0000 0x00060000
-libpandagl_d     0x0000000060930000 0x00040000
-libpandaexpress_d 0x0000000060970000 0x00220000
-libpandaphysics_d 0x0000000060b90000 0x000a0000
-libpandafx_d     0x0000000060c30000 0x00030000
-libdtool_d       0x0000000060c60000 0x00030000
-libdtoolconfig_d 0x0000000060c90000 0x00070000
-libpystub_d      0x0000000060d00000 0x00010000
-libdirect_d      0x0000000060d10000 0x00080000
-libtoontown_d    0x0000000060d90000 0x00130000
-; TODO put in a base for Pirates
-libpirates_d    0x0000000060d90000 0x00130000
-libmiles_audio_d 0x0000000060ec0000 0x00030000
-libpandaegg_d    0x0000000060ef0000 0x00210000
-libframework_d   0x0000000061100000 0x00030000

+ 0 - 12
dtool/src/dtoolbase/dtoolsymbols.h

@@ -125,16 +125,4 @@
   #define EXPTP_MISC IMPORT_TEMPL
   #define EXPTP_MISC IMPORT_TEMPL
 #endif /* BUILDING_MISC */
 #endif /* BUILDING_MISC */
 
 
-
-/* These two are always defined empty, because pystub is statically
-   built.  But we leave the symbol around in case we change our minds
-   to make pystub once again be a dynamic library. */
-#if __GNUC__ >= 4
-/* In GCC, though, we still need to mark the symbols as visible. */
-#define EXPCL_PYSTUB __attribute__((visibility("default")))
-#else
-#define EXPCL_PYSTUB
-#endif
-#define EXPTP_PYSTUB
-
 #endif
 #endif

+ 6 - 6
dtool/src/dtoolutil/iostream_ext.cxx

@@ -24,12 +24,12 @@ extern struct Dtool_PyTypedObject Dtool_std_istream;
  * If the given size is -1, all bytes are read from the stream.
  * If the given size is -1, all bytes are read from the stream.
  */
  */
 PyObject *Extension<istream>::
 PyObject *Extension<istream>::
-read(int size) {
+read(Py_ssize_t size) {
   if (size < 0) {
   if (size < 0) {
     return readall();
     return readall();
   }
   }
 
 
-  char *buffer;
+  char *buffer = nullptr;
   std::streamsize read_bytes = 0;
   std::streamsize read_bytes = 0;
 
 
   if (size > 0) {
   if (size > 0) {
@@ -62,7 +62,7 @@ read(int size) {
  * will always be greater than 0 until EOF is reached.
  * will always be greater than 0 until EOF is reached.
  */
  */
 PyObject *Extension<istream>::
 PyObject *Extension<istream>::
-read1(int size) {
+read1(Py_ssize_t size) {
   std::streambuf *buf = _this->rdbuf();
   std::streambuf *buf = _this->rdbuf();
   nassertr(buf != nullptr, nullptr);
   nassertr(buf != nullptr, nullptr);
 
 
@@ -171,7 +171,7 @@ readinto(PyObject *b) {
  * Returns empty string when the end of file is reached.
  * Returns empty string when the end of file is reached.
  */
  */
 PyObject *Extension<istream>::
 PyObject *Extension<istream>::
-readline(int size) {
+readline(Py_ssize_t size) {
   std::streambuf *buf = _this->rdbuf();
   std::streambuf *buf = _this->rdbuf();
   nassertr(buf != nullptr, nullptr);
   nassertr(buf != nullptr, nullptr);
 
 
@@ -207,7 +207,7 @@ readline(int size) {
  * for readline().
  * for readline().
  */
  */
 PyObject *Extension<istream>::
 PyObject *Extension<istream>::
-readlines(int hint) {
+readlines(Py_ssize_t hint) {
   PyObject *lst = PyList_New(0);
   PyObject *lst = PyList_New(0);
   if (lst == nullptr) {
   if (lst == nullptr) {
     return nullptr;
     return nullptr;
@@ -223,7 +223,7 @@ readlines(int hint) {
       py_line = readline(-1);
       py_line = readline(-1);
     }
     }
   } else {
   } else {
-    size_t totchars = 0;
+    Py_ssize_t totchars = 0;
     while (Py_SIZE(py_line) > 0) {
     while (Py_SIZE(py_line) > 0) {
       totchars += Py_SIZE(py_line);
       totchars += Py_SIZE(py_line);
       PyList_Append(lst, py_line);
       PyList_Append(lst, py_line);

+ 4 - 4
dtool/src/dtoolutil/iostream_ext.h

@@ -31,13 +31,13 @@
 template<>
 template<>
 class Extension<istream> : public ExtensionBase<istream> {
 class Extension<istream> : public ExtensionBase<istream> {
 public:
 public:
-  PyObject *read(int size=-1);
-  PyObject *read1(int size=-1);
+  PyObject *read(Py_ssize_t size=-1);
+  PyObject *read1(Py_ssize_t size=-1);
   PyObject *readall();
   PyObject *readall();
   std::streamsize readinto(PyObject *b);
   std::streamsize readinto(PyObject *b);
 
 
-  PyObject *readline(int size=-1);
-  PyObject *readlines(int hint=-1);
+  PyObject *readline(Py_ssize_t size=-1);
+  PyObject *readlines(Py_ssize_t hint=-1);
   PyObject *__iter__(PyObject *self);
   PyObject *__iter__(PyObject *self);
 };
 };
 
 

+ 10 - 1
dtool/src/interrogate/interfaceMakerPythonNative.cxx

@@ -2878,8 +2878,13 @@ write_module_class(ostream &out, Object *obj) {
 
 
   // destructor tp_dealloc;
   // destructor tp_dealloc;
   out << "    &Dtool_FreeInstance_" << ClassName << ",\n";
   out << "    &Dtool_FreeInstance_" << ClassName << ",\n";
-  // printfunc tp_print;
+
+  out << "#if PY_VERSION_HEX >= 0x03080000\n";
+  out << "    0, // tp_vectorcall_offset\n";
+  out << "#else\n";
   write_function_slot(out, 4, slots, "tp_print");
   write_function_slot(out, 4, slots, "tp_print");
+  out << "#endif\n";
+
   // getattrfunc tp_getattr;
   // getattrfunc tp_getattr;
   write_function_slot(out, 4, slots, "tp_getattr");
   write_function_slot(out, 4, slots, "tp_getattr");
   // setattrfunc tp_setattr;
   // setattrfunc tp_setattr;
@@ -3070,6 +3075,10 @@ write_module_class(ostream &out, Object *obj) {
   out << "#if PY_VERSION_HEX >= 0x03040000\n";
   out << "#if PY_VERSION_HEX >= 0x03040000\n";
   out << "    nullptr, // tp_finalize\n";
   out << "    nullptr, // tp_finalize\n";
   out << "#endif\n";
   out << "#endif\n";
+  // vectorcallfunc tp_vectorcall
+  out << "#if PY_VERSION_HEX >= 0x03080000\n";
+  out << "    nullptr, // tp_vectorcall\n";
+  out << "#endif\n";
   out << "  },\n";
   out << "  },\n";
 
 
   // It's tempting to initialize the type handle here, but this causes static
   // It's tempting to initialize the type handle here, but this causes static

+ 6 - 3
dtool/src/interrogate/interrogate.cxx

@@ -574,6 +574,8 @@ main(int argc, char **argv) {
     the_output_include = &output_include;
     the_output_include = &output_include;
   }
   }
 
 
+  int status = 0;
+
   // Now output all of the wrapper functions.
   // Now output all of the wrapper functions.
   if (!output_code_filename.empty())
   if (!output_code_filename.empty())
   {
   {
@@ -595,6 +597,7 @@ main(int argc, char **argv) {
 
 
     if (output_code.fail()) {
     if (output_code.fail()) {
       nout << "Unable to write to " << output_code_filename << "\n";
       nout << "Unable to write to " << output_code_filename << "\n";
+      status = -1;
     } else {
     } else {
       builder.write_code(output_code,the_output_include, def);
       builder.write_code(output_code,the_output_include, def);
     }
     }
@@ -609,13 +612,13 @@ main(int argc, char **argv) {
     pofstream output_data;
     pofstream output_data;
     output_data_filename.open_write(output_data);
     output_data_filename.open_write(output_data);
 
 
-    if (output_data.fail())
-    {
+    if (output_data.fail()) {
       nout << "Unable to write to " << output_data_filename << "\n";
       nout << "Unable to write to " << output_data_filename << "\n";
+      status = -1;
     } else {
     } else {
       InterrogateDatabase::get_ptr()->write(output_data, def);
       InterrogateDatabase::get_ptr()->write(output_data, def);
     }
     }
   }
   }
 
 
-  return (0);
+  return status;
 }
 }

+ 8 - 1
dtool/src/interrogatedb/dtool_super_base.cxx

@@ -80,7 +80,7 @@ Dtool_PyTypedObject *Dtool_GetSuperBase() {
       sizeof(Dtool_PyInstDef),
       sizeof(Dtool_PyInstDef),
       0, // tp_itemsize
       0, // tp_itemsize
       &Dtool_FreeInstance_DTOOL_SUPER_BASE,
       &Dtool_FreeInstance_DTOOL_SUPER_BASE,
-      nullptr, // tp_print
+      0, // tp_vectorcall_offset
       nullptr, // tp_getattr
       nullptr, // tp_getattr
       nullptr, // tp_setattr
       nullptr, // tp_setattr
 #if PY_MAJOR_VERSION >= 3
 #if PY_MAJOR_VERSION >= 3
@@ -129,6 +129,13 @@ Dtool_PyTypedObject *Dtool_GetSuperBase() {
       nullptr, // tp_subclasses
       nullptr, // tp_subclasses
       nullptr, // tp_weaklist
       nullptr, // tp_weaklist
       nullptr, // tp_del
       nullptr, // tp_del
+      0, // tp_version_tag,
+#if PY_VERSION_HEX >= 0x03040000
+      nullptr, // tp_finalize
+#endif
+#if PY_VERSION_HEX >= 0x03080000
+      nullptr, // tp_vectorcall
+#endif
     },
     },
     TypeHandle::none(),
     TypeHandle::none(),
     Dtool_PyModuleClassInit_DTOOL_SUPER_BASE,
     Dtool_PyModuleClassInit_DTOOL_SUPER_BASE,

+ 1 - 1
dtool/src/interrogatedb/py_compat.h

@@ -139,7 +139,7 @@ typedef long Py_hash_t;
 
 
 /* Python 3.6 */
 /* Python 3.6 */
 
 
-#ifndef _PyObject_CallNoArg
+#if PY_VERSION_HEX < 0x03080000 && !defined(_PyObject_CallNoArg)
 INLINE PyObject *_PyObject_CallNoArg(PyObject *func) {
 INLINE PyObject *_PyObject_CallNoArg(PyObject *func) {
   static PyTupleObject empty_tuple = {PyVarObject_HEAD_INIT(nullptr, 0)};
   static PyTupleObject empty_tuple = {PyVarObject_HEAD_INIT(nullptr, 0)};
 #ifdef Py_TRACE_REFS
 #ifdef Py_TRACE_REFS

+ 73 - 10
dtool/src/interrogatedb/py_wrappers.cxx

@@ -541,7 +541,7 @@ static PyObject *Dtool_MappingWrapper_keys(PyObject *self, PyObject *) {
     sizeof(Dtool_SequenceWrapper),
     sizeof(Dtool_SequenceWrapper),
     0, // tp_itemsize
     0, // tp_itemsize
     Dtool_WrapperBase_dealloc,
     Dtool_WrapperBase_dealloc,
-    nullptr, // tp_print
+    0, // tp_vectorcall_offset
     nullptr, // tp_getattr
     nullptr, // tp_getattr
     nullptr, // tp_setattr
     nullptr, // tp_setattr
     nullptr, // tp_compare
     nullptr, // tp_compare
@@ -582,6 +582,13 @@ static PyObject *Dtool_MappingWrapper_keys(PyObject *self, PyObject *) {
     nullptr, // tp_subclasses
     nullptr, // tp_subclasses
     nullptr, // tp_weaklist
     nullptr, // tp_weaklist
     nullptr, // tp_del
     nullptr, // tp_del
+    0, // tp_version_tag,
+#if PY_VERSION_HEX >= 0x03040000
+    nullptr, // tp_finalize
+#endif
+#if PY_VERSION_HEX >= 0x03080000
+    nullptr, // tp_vectorcall
+#endif
   };
   };
 
 
   static bool registered = false;
   static bool registered = false;
@@ -675,7 +682,7 @@ static PyObject *Dtool_MappingWrapper_values(PyObject *self, PyObject *) {
     sizeof(Dtool_MappingWrapper),
     sizeof(Dtool_MappingWrapper),
     0, // tp_itemsize
     0, // tp_itemsize
     Dtool_WrapperBase_dealloc,
     Dtool_WrapperBase_dealloc,
-    nullptr, // tp_print
+    0, // tp_vectorcall_offset
     nullptr, // tp_getattr
     nullptr, // tp_getattr
     nullptr, // tp_setattr
     nullptr, // tp_setattr
     nullptr, // tp_compare
     nullptr, // tp_compare
@@ -716,6 +723,13 @@ static PyObject *Dtool_MappingWrapper_values(PyObject *self, PyObject *) {
     nullptr, // tp_subclasses
     nullptr, // tp_subclasses
     nullptr, // tp_weaklist
     nullptr, // tp_weaklist
     nullptr, // tp_del
     nullptr, // tp_del
+    0, // tp_version_tag,
+#if PY_VERSION_HEX >= 0x03040000
+    nullptr, // tp_finalize
+#endif
+#if PY_VERSION_HEX >= 0x03080000
+    nullptr, // tp_vectorcall
+#endif
   };
   };
 
 
   static bool registered = false;
   static bool registered = false;
@@ -817,7 +831,7 @@ static PyObject *Dtool_MappingWrapper_items(PyObject *self, PyObject *) {
     sizeof(Dtool_MappingWrapper),
     sizeof(Dtool_MappingWrapper),
     0, // tp_itemsize
     0, // tp_itemsize
     Dtool_WrapperBase_dealloc,
     Dtool_WrapperBase_dealloc,
-    nullptr, // tp_print
+    0, // tp_vectorcall_offset
     nullptr, // tp_getattr
     nullptr, // tp_getattr
     nullptr, // tp_setattr
     nullptr, // tp_setattr
     nullptr, // tp_compare
     nullptr, // tp_compare
@@ -858,6 +872,13 @@ static PyObject *Dtool_MappingWrapper_items(PyObject *self, PyObject *) {
     nullptr, // tp_subclasses
     nullptr, // tp_subclasses
     nullptr, // tp_weaklist
     nullptr, // tp_weaklist
     nullptr, // tp_del
     nullptr, // tp_del
+    0, // tp_version_tag,
+#if PY_VERSION_HEX >= 0x03040000
+    nullptr, // tp_finalize
+#endif
+#if PY_VERSION_HEX >= 0x03080000
+    nullptr, // tp_vectorcall
+#endif
   };
   };
 
 
   static bool registered = false;
   static bool registered = false;
@@ -1192,7 +1213,7 @@ Dtool_SequenceWrapper *Dtool_NewSequenceWrapper(PyObject *self, const char *name
     sizeof(Dtool_SequenceWrapper),
     sizeof(Dtool_SequenceWrapper),
     0, // tp_itemsize
     0, // tp_itemsize
     Dtool_WrapperBase_dealloc,
     Dtool_WrapperBase_dealloc,
-    nullptr, // tp_print
+    0, // tp_vectorcall_offset
     nullptr, // tp_getattr
     nullptr, // tp_getattr
     nullptr, // tp_setattr
     nullptr, // tp_setattr
     nullptr, // tp_compare
     nullptr, // tp_compare
@@ -1233,6 +1254,13 @@ Dtool_SequenceWrapper *Dtool_NewSequenceWrapper(PyObject *self, const char *name
     nullptr, // tp_subclasses
     nullptr, // tp_subclasses
     nullptr, // tp_weaklist
     nullptr, // tp_weaklist
     nullptr, // tp_del
     nullptr, // tp_del
+    0, // tp_version_tag,
+#if PY_VERSION_HEX >= 0x03040000
+    nullptr, // tp_finalize
+#endif
+#if PY_VERSION_HEX >= 0x03080000
+    nullptr, // tp_vectorcall
+#endif
   };
   };
 
 
   static bool registered = false;
   static bool registered = false;
@@ -1296,7 +1324,7 @@ Dtool_MutableSequenceWrapper *Dtool_NewMutableSequenceWrapper(PyObject *self, co
     sizeof(Dtool_MutableSequenceWrapper),
     sizeof(Dtool_MutableSequenceWrapper),
     0, // tp_itemsize
     0, // tp_itemsize
     Dtool_WrapperBase_dealloc,
     Dtool_WrapperBase_dealloc,
-    nullptr, // tp_print
+    0, // tp_vectorcall_offset
     nullptr, // tp_getattr
     nullptr, // tp_getattr
     nullptr, // tp_setattr
     nullptr, // tp_setattr
     nullptr, // tp_compare
     nullptr, // tp_compare
@@ -1337,6 +1365,13 @@ Dtool_MutableSequenceWrapper *Dtool_NewMutableSequenceWrapper(PyObject *self, co
     nullptr, // tp_subclasses
     nullptr, // tp_subclasses
     nullptr, // tp_weaklist
     nullptr, // tp_weaklist
     nullptr, // tp_del
     nullptr, // tp_del
+    0, // tp_version_tag,
+#if PY_VERSION_HEX >= 0x03040000
+    nullptr, // tp_finalize
+#endif
+#if PY_VERSION_HEX >= 0x03080000
+    nullptr, // tp_vectorcall
+#endif
   };
   };
 
 
   static bool registered = false;
   static bool registered = false;
@@ -1404,7 +1439,7 @@ Dtool_MappingWrapper *Dtool_NewMappingWrapper(PyObject *self, const char *name)
     sizeof(Dtool_MappingWrapper),
     sizeof(Dtool_MappingWrapper),
     0, // tp_itemsize
     0, // tp_itemsize
     Dtool_WrapperBase_dealloc,
     Dtool_WrapperBase_dealloc,
-    nullptr, // tp_print
+    0, // tp_vectorcall_offset
     nullptr, // tp_getattr
     nullptr, // tp_getattr
     nullptr, // tp_setattr
     nullptr, // tp_setattr
     nullptr, // tp_compare
     nullptr, // tp_compare
@@ -1445,6 +1480,13 @@ Dtool_MappingWrapper *Dtool_NewMappingWrapper(PyObject *self, const char *name)
     nullptr, // tp_subclasses
     nullptr, // tp_subclasses
     nullptr, // tp_weaklist
     nullptr, // tp_weaklist
     nullptr, // tp_del
     nullptr, // tp_del
+    0, // tp_version_tag,
+#if PY_VERSION_HEX >= 0x03040000
+    nullptr, // tp_finalize
+#endif
+#if PY_VERSION_HEX >= 0x03080000
+    nullptr, // tp_vectorcall
+#endif
   };
   };
 
 
   static bool registered = false;
   static bool registered = false;
@@ -1517,7 +1559,7 @@ Dtool_MappingWrapper *Dtool_NewMutableMappingWrapper(PyObject *self, const char
     sizeof(Dtool_MappingWrapper),
     sizeof(Dtool_MappingWrapper),
     0, // tp_itemsize
     0, // tp_itemsize
     Dtool_WrapperBase_dealloc,
     Dtool_WrapperBase_dealloc,
-    nullptr, // tp_print
+    0, // tp_vectorcall_offset
     nullptr, // tp_getattr
     nullptr, // tp_getattr
     nullptr, // tp_setattr
     nullptr, // tp_setattr
     nullptr, // tp_compare
     nullptr, // tp_compare
@@ -1558,6 +1600,13 @@ Dtool_MappingWrapper *Dtool_NewMutableMappingWrapper(PyObject *self, const char
     nullptr, // tp_subclasses
     nullptr, // tp_subclasses
     nullptr, // tp_weaklist
     nullptr, // tp_weaklist
     nullptr, // tp_del
     nullptr, // tp_del
+    0, // tp_version_tag,
+#if PY_VERSION_HEX >= 0x03040000
+    nullptr, // tp_finalize
+#endif
+#if PY_VERSION_HEX >= 0x03080000
+    nullptr, // tp_vectorcall
+#endif
   };
   };
 
 
   static bool registered = false;
   static bool registered = false;
@@ -1594,7 +1643,7 @@ Dtool_NewGenerator(PyObject *self, iternextfunc gen_next) {
     sizeof(Dtool_GeneratorWrapper),
     sizeof(Dtool_GeneratorWrapper),
     0, // tp_itemsize
     0, // tp_itemsize
     Dtool_WrapperBase_dealloc,
     Dtool_WrapperBase_dealloc,
-    nullptr, // tp_print
+    0, // tp_vectorcall_offset
     nullptr, // tp_getattr
     nullptr, // tp_getattr
     nullptr, // tp_setattr
     nullptr, // tp_setattr
     nullptr, // tp_compare
     nullptr, // tp_compare
@@ -1635,6 +1684,13 @@ Dtool_NewGenerator(PyObject *self, iternextfunc gen_next) {
     nullptr, // tp_subclasses
     nullptr, // tp_subclasses
     nullptr, // tp_weaklist
     nullptr, // tp_weaklist
     nullptr, // tp_del
     nullptr, // tp_del
+    0, // tp_version_tag,
+#if PY_VERSION_HEX >= 0x03040000
+    nullptr, // tp_finalize
+#endif
+#if PY_VERSION_HEX >= 0x03080000
+    nullptr, // tp_vectorcall
+#endif
   };
   };
 
 
   if (PyType_Ready(&wrapper_type) < 0) {
   if (PyType_Ready(&wrapper_type) < 0) {
@@ -1663,7 +1719,7 @@ Dtool_NewStaticProperty(PyTypeObject *type, const PyGetSetDef *getset) {
     sizeof(PyGetSetDescrObject),
     sizeof(PyGetSetDescrObject),
     0, // tp_itemsize
     0, // tp_itemsize
     (destructor)Dtool_StaticProperty_dealloc,
     (destructor)Dtool_StaticProperty_dealloc,
-    nullptr, // tp_print
+    0, // tp_vectorcall_offset
     nullptr, // tp_getattr
     nullptr, // tp_getattr
     nullptr, // tp_setattr
     nullptr, // tp_setattr
     nullptr, // tp_reserved
     nullptr, // tp_reserved
@@ -1696,7 +1752,7 @@ Dtool_NewStaticProperty(PyTypeObject *type, const PyGetSetDef *getset) {
     nullptr, // tp_init
     nullptr, // tp_init
     nullptr, // tp_alloc
     nullptr, // tp_alloc
     nullptr, // tp_new
     nullptr, // tp_new
-    nullptr, // tp_del
+    nullptr, // tp_free
     nullptr, // tp_is_gc
     nullptr, // tp_is_gc
     nullptr, // tp_bases
     nullptr, // tp_bases
     nullptr, // tp_mro
     nullptr, // tp_mro
@@ -1704,6 +1760,13 @@ Dtool_NewStaticProperty(PyTypeObject *type, const PyGetSetDef *getset) {
     nullptr, // tp_subclasses
     nullptr, // tp_subclasses
     nullptr, // tp_weaklist
     nullptr, // tp_weaklist
     nullptr, // tp_del
     nullptr, // tp_del
+    0, // tp_version_tag,
+#if PY_VERSION_HEX >= 0x03040000
+    nullptr, // tp_finalize
+#endif
+#if PY_VERSION_HEX >= 0x03080000
+    nullptr, // tp_vectorcall
+#endif
   };
   };
 
 
   if (PyType_Ready(&wrapper_type) < 0) {
   if (PyType_Ready(&wrapper_type) < 0) {

+ 4 - 4
dtool/src/parser-inc/iostream

@@ -50,13 +50,13 @@ namespace std {
   __published:
   __published:
     istream(const istream&) = delete;
     istream(const istream&) = delete;
 
 
-    __extension PyObject *read(int size=-1);
-    __extension PyObject *read1(int size=-1);
+    __extension PyObject *read(Py_ssize_t size=-1);
+    __extension PyObject *read1(Py_ssize_t size=-1);
     __extension PyObject *readall();
     __extension PyObject *readall();
     __extension std::streamsize readinto(PyObject *b);
     __extension std::streamsize readinto(PyObject *b);
 
 
-    __extension PyObject *readline(int size=-1);
-    __extension PyObject *readlines(int hint=-1);
+    __extension PyObject *readline(Py_ssize_t size=-1);
+    __extension PyObject *readlines(Py_ssize_t hint=-1);
     __extension PyObject *__iter__(PyObject *self);
     __extension PyObject *__iter__(PyObject *self);
 
 
     int get();
     int get();

+ 5 - 1
dtool/src/prc/encryptStream.cxx

@@ -13,6 +13,8 @@
 
 
 #include "encryptStream.h"
 #include "encryptStream.h"
 
 
+#ifdef HAVE_OPENSSL
+
 /**
 /**
  * Must be called immediately after open_read().  Decrypts the given number of
  * Must be called immediately after open_read().  Decrypts the given number of
  * bytes and checks that they match.  The amount of header bytes are added to
  * bytes and checks that they match.  The amount of header bytes are added to
@@ -25,10 +27,12 @@ read_magic(const char *magic, size_t size) {
   char *this_magic = (char *)alloca(size);
   char *this_magic = (char *)alloca(size);
   read(this_magic, size);
   read(this_magic, size);
 
 
-  if (!fail() && gcount() == size && memcmp(this_magic, magic, size) == 0) {
+  if (!fail() && (size_t)gcount() == size && memcmp(this_magic, magic, size) == 0) {
     _buf.set_magic_length(size);
     _buf.set_magic_length(size);
     return true;
     return true;
   } else {
   } else {
     return false;
     return false;
   }
   }
 }
 }
+
+#endif

+ 16 - 11
dtool/src/prc/notify.cxx

@@ -21,6 +21,10 @@
 
 
 #include <ctype.h>
 #include <ctype.h>
 
 
+#ifdef PHAVE_ATOMIC
+#include <atomic>
+#endif
+
 #ifdef BUILD_IPHONE
 #ifdef BUILD_IPHONE
 #include <fcntl.h>
 #include <fcntl.h>
 #endif
 #endif
@@ -422,28 +426,29 @@ string_severity(const string &str) {
  */
  */
 void Notify::
 void Notify::
 config_initialized() {
 config_initialized() {
-  static bool already_initialized = false;
-  if (already_initialized) {
-    nout << "Notify::config_initialized() called more than once.\n";
-    return;
-  }
-  already_initialized = true;
+  // We allow this to be called more than once to allow the user to specify a
+  // notify-output even after the initial import of Panda3D modules.  However,
+  // it cannot be changed after the first time it is set.
 
 
   if (_ostream_ptr == &cerr) {
   if (_ostream_ptr == &cerr) {
-    ConfigVariableFilename notify_output
+    static ConfigVariableFilename notify_output
       ("notify-output", "",
       ("notify-output", "",
        "The filename to which to write all the output of notify");
        "The filename to which to write all the output of notify");
 
 
-    if (!notify_output.empty()) {
-      if (notify_output == "stdout") {
+    // We use this to ensure that only one thread can initialize the output.
+    static std::atomic_flag initialized = ATOMIC_FLAG_INIT;
+
+    std::string value = notify_output.get_value();
+    if (!value.empty() && !initialized.test_and_set()) {
+      if (value == "stdout") {
         cout.setf(std::ios::unitbuf);
         cout.setf(std::ios::unitbuf);
         set_ostream_ptr(&cout, false);
         set_ostream_ptr(&cout, false);
 
 
-      } else if (notify_output == "stderr") {
+      } else if (value == "stderr") {
         set_ostream_ptr(&cerr, false);
         set_ostream_ptr(&cerr, false);
 
 
       } else {
       } else {
-        Filename filename = notify_output;
+        Filename filename = value;
         filename.set_text();
         filename.set_text();
 #ifdef BUILD_IPHONE
 #ifdef BUILD_IPHONE
         // On the iPhone, route everything through cerr, and then send cerr to
         // On the iPhone, route everything through cerr, and then send cerr to

+ 6 - 0
dtool/src/prc/notifyCategory.cxx

@@ -181,10 +181,16 @@ update_severity_cache() {
     } else {
     } else {
       // Unless, of course, we're the root.
       // Unless, of course, we're the root.
       _severity_cache = NS_info;
       _severity_cache = NS_info;
+
+      // Take this opportunity to have Notify check whether the notify-output
+      // variable changed.
+      Notify::ptr()->config_initialized();
     }
     }
   } else {
   } else {
     _severity_cache = _severity;
     _severity_cache = _severity;
+    Notify::ptr()->config_initialized();
   }
   }
+
   mark_cache_valid(_local_modified);
   mark_cache_valid(_local_modified);
 }
 }
 
 

+ 0 - 492
dtool/src/pystub/pystub.cxx

@@ -1,492 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file pystub.cxx
- * @author drose
- * @date 2000-08-09
- */
-
-#include "pystub.h"
-
-extern "C" {
-  EXPCL_PYSTUB int PyArg_Parse(...);
-  EXPCL_PYSTUB int PyArg_ParseTuple(...);
-  EXPCL_PYSTUB int PyArg_ParseTupleAndKeywords(...);
-  EXPCL_PYSTUB int PyArg_UnpackTuple(...);
-  EXPCL_PYSTUB int PyBool_FromLong(...);
-  EXPCL_PYSTUB int PyBuffer_Release(...);
-  EXPCL_PYSTUB int PyBytes_AsString(...);
-  EXPCL_PYSTUB int PyBytes_AsStringAndSize(...);
-  EXPCL_PYSTUB int PyBytes_FromStringAndSize(...);
-  EXPCL_PYSTUB int PyBytes_Size(...);
-  EXPCL_PYSTUB int PyCFunction_New(...);
-  EXPCL_PYSTUB int PyCFunction_NewEx(...);
-  EXPCL_PYSTUB int PyCallable_Check(...);
-  EXPCL_PYSTUB int PyCapsule_GetPointer(...);
-  EXPCL_PYSTUB int PyCapsule_New(...);
-  EXPCL_PYSTUB int PyDict_DelItem(...);
-  EXPCL_PYSTUB int PyDict_DelItemString(...);
-  EXPCL_PYSTUB int PyDict_GetItem(...);
-  EXPCL_PYSTUB int PyDict_GetItemString(...);
-  EXPCL_PYSTUB int PyDict_New(...);
-  EXPCL_PYSTUB int PyDict_Next(...);
-  EXPCL_PYSTUB int PyDict_SetItem(...);
-  EXPCL_PYSTUB int PyDict_SetItemString(...);
-  EXPCL_PYSTUB int PyDict_Size(...);
-  EXPCL_PYSTUB int PyErr_Clear(...);
-  EXPCL_PYSTUB int PyErr_ExceptionMatches(...);
-  EXPCL_PYSTUB int PyErr_Fetch(...);
-  EXPCL_PYSTUB int PyErr_Format(...);
-  EXPCL_PYSTUB int PyErr_NoMemory(...);
-  EXPCL_PYSTUB int PyErr_Occurred(...);
-  EXPCL_PYSTUB int PyErr_Print(...);
-  EXPCL_PYSTUB int PyErr_Restore(...);
-  EXPCL_PYSTUB int PyErr_SetString(...);
-  EXPCL_PYSTUB int PyErr_Warn(...);
-  EXPCL_PYSTUB int PyErr_WarnEx(...);
-  EXPCL_PYSTUB int PyEval_CallFunction(...);
-  EXPCL_PYSTUB int PyEval_CallObjectWithKeywords(...);
-  EXPCL_PYSTUB int PyEval_RestoreThread(...);
-  EXPCL_PYSTUB int PyEval_SaveThread(...);
-  EXPCL_PYSTUB int PyFloat_AsDouble(...);
-  EXPCL_PYSTUB int PyFloat_FromDouble(...);
-  EXPCL_PYSTUB int PyGen_Check(...);
-  EXPCL_PYSTUB int PyGILState_Ensure(...);
-  EXPCL_PYSTUB int PyGILState_Release(...);
-  EXPCL_PYSTUB int PyImport_GetModuleDict(...);
-  EXPCL_PYSTUB int PyImport_ImportModule(...);
-  EXPCL_PYSTUB int PyInt_AsLong(...);
-  EXPCL_PYSTUB int PyInt_AsSsize_t(...);
-  EXPCL_PYSTUB int PyInt_FromLong(...);
-  EXPCL_PYSTUB int PyInt_FromSize_t(...);
-  EXPCL_PYSTUB int PyIter_Next(...);
-  EXPCL_PYSTUB int PyList_Append(...);
-  EXPCL_PYSTUB int PyList_AsTuple(...);
-  EXPCL_PYSTUB int PyList_GetItem(...);
-  EXPCL_PYSTUB int PyList_New(...);
-  EXPCL_PYSTUB int PyList_SetItem(...);
-  EXPCL_PYSTUB int PyLong_AsLong(...);
-  EXPCL_PYSTUB int PyLong_AsLongLong(...);
-  EXPCL_PYSTUB int PyLong_AsSsize_t(...);
-  EXPCL_PYSTUB int PyLong_AsUnsignedLong(...);
-  EXPCL_PYSTUB int PyLong_AsUnsignedLongLong(...);
-  EXPCL_PYSTUB int PyLong_FromLong(...);
-  EXPCL_PYSTUB int PyLong_FromLongLong(...);
-  EXPCL_PYSTUB int PyLong_FromSize_t(...);
-  EXPCL_PYSTUB int PyLong_FromUnsignedLong(...);
-  EXPCL_PYSTUB int PyLong_FromUnsignedLongLong(...);
-  EXPCL_PYSTUB int PyMapping_GetItemString(...);
-  EXPCL_PYSTUB int PyMem_Free(...);
-  EXPCL_PYSTUB int PyMemoryView_FromObject(...);
-  EXPCL_PYSTUB int PyModule_AddIntConstant(...);
-  EXPCL_PYSTUB int PyModule_AddObject(...);
-  EXPCL_PYSTUB int PyModule_AddStringConstant(...);
-  EXPCL_PYSTUB int PyModule_Create2(...);
-  EXPCL_PYSTUB int PyModule_Create2TraceRefs(...);
-  EXPCL_PYSTUB int PyModule_GetDict(...);
-  EXPCL_PYSTUB int PyNumber_AsSsize_t(...);
-  EXPCL_PYSTUB int PyNumber_Check(...);
-  EXPCL_PYSTUB int PyNumber_Float(...);
-  EXPCL_PYSTUB int PyNumber_Int(...);
-  EXPCL_PYSTUB int PyNumber_Long(...);
-  EXPCL_PYSTUB int PyObject_ASCII(...);
-  EXPCL_PYSTUB int PyObject_Call(...);
-  EXPCL_PYSTUB int PyObject_CallFunction(...);
-  EXPCL_PYSTUB int PyObject_CallFunctionObjArgs(...);
-  EXPCL_PYSTUB int PyObject_CallMethod(...);
-  EXPCL_PYSTUB int PyObject_CallMethodObjArgs(...);
-  EXPCL_PYSTUB int PyObject_CallObject(...);
-  EXPCL_PYSTUB int PyObject_Cmp(...);
-  EXPCL_PYSTUB int PyObject_Compare(...);
-  EXPCL_PYSTUB int PyObject_Free(...);
-  EXPCL_PYSTUB int PyObject_GC_Del(...);
-  EXPCL_PYSTUB int PyObject_GenericGetAttr(...);
-  EXPCL_PYSTUB int PyObject_GenericSetAttr(...);
-  EXPCL_PYSTUB int PyObject_GetAttrString(...);
-  EXPCL_PYSTUB int PyObject_GetBuffer(...);
-  EXPCL_PYSTUB int PyObject_GetIter(...);
-  EXPCL_PYSTUB int PyObject_HasAttrString(...);
-  EXPCL_PYSTUB int PyObject_IsInstance(...);
-  EXPCL_PYSTUB int PyObject_IsTrue(...);
-  EXPCL_PYSTUB int PyObject_Malloc(...);
-  EXPCL_PYSTUB int PyObject_Repr(...);
-  EXPCL_PYSTUB int PyObject_RichCompareBool(...);
-  EXPCL_PYSTUB int PyObject_SelfIter(...);
-  EXPCL_PYSTUB int PyObject_SetAttr(...);
-  EXPCL_PYSTUB int PyObject_SetAttrString(...);
-  EXPCL_PYSTUB int PyObject_Str(...);
-  EXPCL_PYSTUB int PySeqIter_New(...);
-  EXPCL_PYSTUB int PySequence_Check(...);
-  EXPCL_PYSTUB int PySequence_Fast(...);
-  EXPCL_PYSTUB int PySequence_GetItem(...);
-  EXPCL_PYSTUB int PySequence_Size(...);
-  EXPCL_PYSTUB int PySequence_Tuple(...);
-  EXPCL_PYSTUB int PyString_AsString(...);
-  EXPCL_PYSTUB int PyString_AsStringAndSize(...);
-  EXPCL_PYSTUB int PyString_FromFormat(...);
-  EXPCL_PYSTUB int PyString_FromString(...);
-  EXPCL_PYSTUB int PyString_FromStringAndSize(...);
-  EXPCL_PYSTUB int PyString_InternFromString(...);
-  EXPCL_PYSTUB int PyString_InternInPlace(...);
-  EXPCL_PYSTUB int PyString_Size(...);
-  EXPCL_PYSTUB int PySys_GetObject(...);
-  EXPCL_PYSTUB int PySys_SetObject(...);
-  EXPCL_PYSTUB int PyThreadState_Clear(...);
-  EXPCL_PYSTUB int PyThreadState_Delete(...);
-  EXPCL_PYSTUB int PyThreadState_Get(...);
-  EXPCL_PYSTUB int PyThreadState_New(...);
-  EXPCL_PYSTUB int PyThreadState_Swap(...);
-  EXPCL_PYSTUB int PyTuple_GetItem(...);
-  EXPCL_PYSTUB int PyTuple_New(...);
-  EXPCL_PYSTUB int PyTuple_Pack(...);
-  EXPCL_PYSTUB int PyTuple_Size(...);
-  EXPCL_PYSTUB int PyType_GenericAlloc(...);
-  EXPCL_PYSTUB int PyType_IsSubtype(...);
-  EXPCL_PYSTUB int PyType_Ready(...);
-  EXPCL_PYSTUB int PyUnicodeUCS2_AsWideChar(...);
-  EXPCL_PYSTUB int PyUnicodeUCS2_AsWideCharString(...);
-  EXPCL_PYSTUB int PyUnicodeUCS2_CompareWithASCIIString(...);
-  EXPCL_PYSTUB int PyUnicodeUCS2_FromFormat(...);
-  EXPCL_PYSTUB int PyUnicodeUCS2_FromString(...);
-  EXPCL_PYSTUB int PyUnicodeUCS2_FromStringAndSize(...);
-  EXPCL_PYSTUB int PyUnicodeUCS2_FromWideChar(...);
-  EXPCL_PYSTUB int PyUnicodeUCS2_GetSize(...);
-  EXPCL_PYSTUB int PyUnicodeUCS4_AsWideChar(...);
-  EXPCL_PYSTUB int PyUnicodeUCS4_AsWideCharString(...);
-  EXPCL_PYSTUB int PyUnicodeUCS4_CompareWithASCIIString(...);
-  EXPCL_PYSTUB int PyUnicodeUCS4_FromFormat(...);
-  EXPCL_PYSTUB int PyUnicodeUCS4_FromString(...);
-  EXPCL_PYSTUB int PyUnicodeUCS4_FromStringAndSize(...);
-  EXPCL_PYSTUB int PyUnicodeUCS4_FromWideChar(...);
-  EXPCL_PYSTUB int PyUnicodeUCS4_GetSize(...);
-  EXPCL_PYSTUB int PyUnicode_AsUTF8(...);
-  EXPCL_PYSTUB int PyUnicode_AsUTF8AndSize(...);
-  EXPCL_PYSTUB int PyUnicode_AsWideChar(...);
-  EXPCL_PYSTUB int PyUnicode_AsWideCharString(...);
-  EXPCL_PYSTUB int PyUnicode_CompareWithASCIIString(...);
-  EXPCL_PYSTUB int PyUnicode_FromFormat(...);
-  EXPCL_PYSTUB int PyUnicode_FromString(...);
-  EXPCL_PYSTUB int PyUnicode_FromStringAndSize(...);
-  EXPCL_PYSTUB int PyUnicode_FromWideChar(...);
-  EXPCL_PYSTUB int PyUnicode_GetSize(...);
-  EXPCL_PYSTUB int PyUnicode_InternFromString(...);
-  EXPCL_PYSTUB int PyUnicode_InternInPlace(...);
-  EXPCL_PYSTUB int Py_BuildValue(...);
-  EXPCL_PYSTUB int Py_GetVersion(...);
-  EXPCL_PYSTUB int Py_InitModule4(...);
-  EXPCL_PYSTUB int Py_InitModule4_64(...);
-  EXPCL_PYSTUB int Py_InitModule4TraceRefs(...);
-  EXPCL_PYSTUB int Py_InitModule4TraceRefs_64(...);
-  EXPCL_PYSTUB int _PyArg_ParseTuple_SizeT(...);
-  EXPCL_PYSTUB int _PyArg_ParseTupleAndKeywords_SizeT(...);
-  EXPCL_PYSTUB int _PyArg_Parse_SizeT(...);
-  EXPCL_PYSTUB int _PyErr_BadInternalCall(...);
-  EXPCL_PYSTUB int _PyLong_AsByteArray(...);
-  EXPCL_PYSTUB int _PyLong_Sign(...);
-  EXPCL_PYSTUB int _PyObject_CallFunction_SizeT(...);
-  EXPCL_PYSTUB int _PyObject_CallMethod_SizeT(...);
-  EXPCL_PYSTUB int _PyObject_DebugFree(...);
-  EXPCL_PYSTUB int _PyObject_Del(...);
-  EXPCL_PYSTUB int _PyObject_FastCallDict(...);
-  EXPCL_PYSTUB int _PyUnicode_AsString(...);
-  EXPCL_PYSTUB int _PyUnicode_AsStringAndSize(...);
-  EXPCL_PYSTUB int _PyUnicode_EqualToASCIIString(...);
-  EXPCL_PYSTUB int _Py_AddToAllObjects(...);
-  EXPCL_PYSTUB int _Py_BuildValue_SizeT(...);
-  EXPCL_PYSTUB int _Py_Dealloc(...);
-  EXPCL_PYSTUB int _Py_ForgetReference(...);
-  EXPCL_PYSTUB int _Py_NegativeRefcount(...);
-  EXPCL_PYSTUB int _Py_NewReference(...);
-  EXPCL_PYSTUB int _Py_PrintReferenceAddresses(...);
-  EXPCL_PYSTUB int _Py_PrintReferences(...);
-  EXPCL_PYSTUB int _Py_RefTotal(...);
-
-  EXPCL_PYSTUB void Py_Initialize();
-  EXPCL_PYSTUB int Py_IsInitialized();
-  EXPCL_PYSTUB void PyEval_InitThreads();
-
-  EXPCL_PYSTUB extern void *PyExc_AssertionError;
-  EXPCL_PYSTUB extern void *PyExc_AttributeError;
-  EXPCL_PYSTUB extern void *PyExc_BufferError;
-  EXPCL_PYSTUB extern void *PyExc_ConnectionError;
-  EXPCL_PYSTUB extern void *PyExc_Exception;
-  EXPCL_PYSTUB extern void *PyExc_FutureWarning;
-  EXPCL_PYSTUB extern void *PyExc_ImportError;
-  EXPCL_PYSTUB extern void *PyExc_IndexError;
-  EXPCL_PYSTUB extern void *PyExc_KeyError;
-  EXPCL_PYSTUB extern void *PyExc_NameError;
-  EXPCL_PYSTUB extern void *PyExc_OSError;
-  EXPCL_PYSTUB extern void *PyExc_OverflowError;
-  EXPCL_PYSTUB extern void *PyExc_RuntimeError;
-  EXPCL_PYSTUB extern void *PyExc_StandardError;
-  EXPCL_PYSTUB extern void *PyExc_StopIteration;
-  EXPCL_PYSTUB extern void *PyExc_SystemExit;
-  EXPCL_PYSTUB extern void *PyExc_TypeError;
-  EXPCL_PYSTUB extern void *PyExc_ValueError;
-  EXPCL_PYSTUB extern void *PyDict_Type;
-  EXPCL_PYSTUB extern void *PyFloat_Type;
-  EXPCL_PYSTUB extern void *PyGen_Type;
-  EXPCL_PYSTUB extern void *PyInt_Type;
-  EXPCL_PYSTUB extern void *PyList_Type;
-  EXPCL_PYSTUB extern void *PyLong_Type;
-  EXPCL_PYSTUB extern void *PyObject_Type;
-  EXPCL_PYSTUB extern void *PyString_Type;
-  EXPCL_PYSTUB extern void *PyTuple_Type;
-  EXPCL_PYSTUB extern void *PyType_Type;
-  EXPCL_PYSTUB extern void *PyUnicode_Type;
-  EXPCL_PYSTUB extern void *_PyThreadState_Current;
-  EXPCL_PYSTUB extern void *_Py_FalseStruct;
-  EXPCL_PYSTUB extern void *_Py_NoneStruct;
-  EXPCL_PYSTUB extern void *_Py_NotImplementedStruct;
-  EXPCL_PYSTUB extern void *_Py_TrueStruct;
-  EXPCL_PYSTUB extern void *_Py_ZeroStruct;
-};
-
-
-int PyArg_Parse(...) { return 0; };
-int PyArg_ParseTuple(...) { return 0; }
-int PyArg_ParseTupleAndKeywords(...) { return 0; }
-int PyArg_UnpackTuple(...) { return 0; };
-int PyBool_FromLong(...) { return 0; }
-int PyBuffer_Release(...) { return 0; }
-int PyBytes_AsString(...) { return 0; }
-int PyBytes_AsStringAndSize(...) { return 0; }
-int PyBytes_FromStringAndSize(...) { return 0; }
-int PyBytes_Size(...) { return 0; }
-int PyCFunction_New(...) { return 0; };
-int PyCFunction_NewEx(...) { return 0; };
-int PyCallable_Check(...) { return 0; }
-int PyCapsule_GetPointer(...) { return 0; }
-int PyCapsule_New(...) { return 0; }
-int PyDict_DelItem(...) { return 0; }
-int PyDict_DelItemString(...) { return 0; }
-int PyDict_GetItem(...) { return 0; }
-int PyDict_GetItemString(...) { return 0; }
-int PyDict_New(...) { return 0; };
-int PyDict_Next(...) { return 0; };
-int PyDict_SetItem(...) { return 0; };
-int PyDict_SetItemString(...) { return 0; };
-int PyDict_Size(...){ return 0; }
-int PyErr_Clear(...) { return 0; };
-int PyErr_ExceptionMatches(...) { return 0; };
-int PyErr_Fetch(...) { return 0; }
-int PyErr_Format(...) { return 0; };
-int PyErr_NoMemory(...) { return 0; }
-int PyErr_Occurred(...) { return 0; }
-int PyErr_Print(...) { return 0; }
-int PyErr_Restore(...) { return 0; }
-int PyErr_SetString(...) { return 0; }
-int PyErr_Warn(...) { return 0; }
-int PyErr_WarnEx(...) { return 0; }
-int PyEval_CallFunction(...) { return 0; }
-int PyEval_CallObjectWithKeywords(...) { return 0; }
-int PyEval_InitThreads(...) { return 0; }
-int PyEval_RestoreThread(...) { return 0; }
-int PyEval_SaveThread(...) { return 0; }
-int PyFloat_AsDouble(...) { return 0; }
-int PyFloat_FromDouble(...) { return 0; }
-int PyGen_Check(...) { return 0; }
-int PyGILState_Ensure(...) { return 0; }
-int PyGILState_Release(...) { return 0; }
-int PyImport_GetModuleDict(...) { return 0; }
-int PyImport_ImportModule(...) { return 0; }
-int PyInt_AsLong(...) { return 0; }
-int PyInt_AsSsize_t(...) { return 0; }
-int PyInt_FromLong(...) { return 0; }
-int PyInt_FromSize_t(...) { return 0; }
-int PyIter_Next(...) { return 0; }
-int PyList_Append(...) { return 0; }
-int PyList_AsTuple(...) { return 0; }
-int PyList_GetItem(...) { return 0; }
-int PyList_New(...) { return 0; }
-int PyList_SetItem(...) { return 0; }
-int PyLong_AsLong(...) { return 0; }
-int PyLong_AsLongLong(...) { return 0; }
-int PyLong_AsSsize_t(...) { return 0; }
-int PyLong_AsUnsignedLong(...) { return 0; }
-int PyLong_AsUnsignedLongLong(...) { return 0; }
-int PyLong_FromLong(...) { return 0; }
-int PyLong_FromLongLong(...) { return 0; }
-int PyLong_FromSize_t(...) { return 0; }
-int PyLong_FromUnsignedLong(...) { return 0; }
-int PyLong_FromUnsignedLongLong(...) { return 0; }
-int PyMapping_GetItemString(...) { return 0; }
-int PyMem_Free(...) { return 0; }
-int PyMemoryView_FromObject(...) { return 0; }
-int PyModule_AddIntConstant(...) { return 0; };
-int PyModule_AddObject(...) { return 0; };
-int PyModule_AddStringConstant(...) { return 0; };
-int PyModule_Create2(...) { return 0; };
-int PyModule_Create2TraceRefs(...) { return 0; };
-int PyModule_GetDict(...) { return 0; };
-int PyNumber_AsSsize_t(...) { return 0; }
-int PyNumber_Check(...) { return 0; }
-int PyNumber_Float(...) { return 0; }
-int PyNumber_Int(...) { return 0; }
-int PyNumber_Long(...) { return 0; }
-int PyObject_ASCII(...) { return 0; }
-int PyObject_Call(...) { return 0; }
-int PyObject_CallFunction(...) { return 0; }
-int PyObject_CallFunctionObjArgs(...) { return 0; }
-int PyObject_CallMethod(...) { return 0; }
-int PyObject_CallMethodObjArgs(...) { return 0; }
-int PyObject_CallObject(...) { return 0; }
-int PyObject_Cmp(...) { return 0; }
-int PyObject_Compare(...) { return 0; }
-int PyObject_Free(...) { return 0; }
-int PyObject_GC_Del(...) { return 0; }
-int PyObject_GenericGetAttr(...) { return 0; };
-int PyObject_GenericSetAttr(...) { return 0; };
-int PyObject_GetAttrString(...) { return 0; }
-int PyObject_GetBuffer(...) { return 0; }
-int PyObject_GetIter(...) { return 0; }
-int PyObject_HasAttrString(...) { return 0; }
-int PyObject_IsInstance(...) { return 0; }
-int PyObject_IsTrue(...) { return 0; }
-int PyObject_Malloc(...) { return 0; }
-int PyObject_Repr(...) { return 0; }
-int PyObject_RichCompareBool(...) { return 0; }
-int PyObject_SelfIter(...) { return 0; }
-int PyObject_SetAttr(...) { return 0; }
-int PyObject_SetAttrString(...) { return 0; }
-int PyObject_Str(...) { return 0; }
-int PySeqIter_New(...) { return 0; }
-int PySequence_Check(...) { return 0; }
-int PySequence_Fast(...) { return 0; }
-int PySequence_GetItem(...) { return 0; }
-int PySequence_Size(...) { return 0; }
-int PySequence_Tuple(...) { return 0; }
-int PyString_AsString(...) { return 0; }
-int PyString_AsStringAndSize(...) { return 0; }
-int PyString_FromFormat(...) { return 0; }
-int PyString_FromString(...) { return 0; }
-int PyString_FromStringAndSize(...) { return 0; }
-int PyString_InternFromString(...) { return 0; }
-int PyString_InternInPlace(...) { return 0; }
-int PySys_GetObject(...) { return 0; }
-int PySys_SetObject(...) { return 0; }
-int PyThreadState_Clear(...) { return 0; }
-int PyThreadState_Delete(...) { return 0; }
-int PyThreadState_Get(...) { return 0; }
-int PyThreadState_New(...) { return 0; }
-int PyThreadState_Swap(...) { return 0; }
-int PyTuple_GetItem(...) { return 0; }
-int PyTuple_New(...) { return 0; }
-int PyTuple_Pack(...) { return 0; }
-int PyTuple_Size(...) { return 0; };
-int PyType_GenericAlloc(...) { return 0; };
-int PyType_IsSubtype(...) { return 0; }
-int PyType_Ready(...) { return 0; };
-int PyUnicodeUCS2_AsWideChar(...) { return 0; }
-int PyUnicodeUCS2_AsWideCharString(...) { return 0; }
-int PyUnicodeUCS2_CompareWithASCIIString(...) { return 0; }
-int PyUnicodeUCS2_FromFormat(...) { return 0; }
-int PyUnicodeUCS2_FromString(...) { return 0; }
-int PyUnicodeUCS2_FromStringAndSize(...) { return 0; }
-int PyUnicodeUCS2_FromWideChar(...) { return 0; }
-int PyUnicodeUCS2_GetSize(...) { return 0; }
-int PyUnicodeUCS4_AsWideChar(...) { return 0; }
-int PyUnicodeUCS4_AsWideCharString(...) { return 0; }
-int PyUnicodeUCS4_CompareWithASCIIString(...) { return 0; }
-int PyUnicodeUCS4_FromFormat(...) { return 0; }
-int PyUnicodeUCS4_FromString(...) { return 0; }
-int PyUnicodeUCS4_FromStringAndSize(...) { return 0; }
-int PyUnicodeUCS4_FromWideChar(...) { return 0; }
-int PyUnicodeUCS4_GetSize(...) { return 0; }
-int PyUnicode_AsUTF8(...) { return 0; }
-int PyUnicode_AsUTF8AndSize(...) { return 0; }
-int PyUnicode_AsWideChar(...) { return 0; }
-int PyUnicode_AsWideCharString(...) { return 0; }
-int PyUnicode_CompareWithASCIIString(...) { return 0; }
-int PyUnicode_FromFormat(...) { return 0; }
-int PyUnicode_FromString(...) { return 0; }
-int PyUnicode_FromStringAndSize(...) { return 0; }
-int PyUnicode_FromWideChar(...) { return 0; }
-int PyUnicode_GetSize(...) { return 0; }
-int PyUnicode_InternFromString(...) { return 0; }
-int PyUnicode_InternInPlace(...) { return 0; }
-int Py_GetVersion(...) { return 0; }
-int Py_BuildValue(...) { return 0; }
-int Py_InitModule4(...) { return 0; }
-int Py_InitModule4_64(...) { return 0; }
-int Py_InitModule4TraceRefs(...) { return 0; };
-int Py_InitModule4TraceRefs_64(...) { return 0; };
-int _PyArg_ParseTuple_SizeT(...) { return 0; };
-int _PyArg_ParseTupleAndKeywords_SizeT(...) { return 0; };
-int _PyArg_Parse_SizeT(...) { return 0; };
-int _PyErr_BadInternalCall(...) { return 0; };
-int _PyLong_AsByteArray(...) { return 0; };
-int _PyLong_Sign(...) { return 0; };
-int _PyObject_CallFunction_SizeT(...) { return 0; };
-int _PyObject_CallMethod_SizeT(...) { return 0; };
-int _PyObject_DebugFree(...) { return 0; };
-int _PyObject_Del(...) { return 0; };
-int _PyObject_FastCallDict(...) { return 0; };
-int _PyUnicode_AsString(...) { return 0; };
-int _PyUnicode_AsStringAndSize(...) { return 0; };
-int _PyUnicode_EqualToASCIIString(...) { return 0; };
-int _Py_AddToAllObjects(...) { return 0; };
-int _Py_BuildValue_SizeT(...) { return 0; };
-int _Py_Dealloc(...) { return 0; };
-int _Py_ForgetReference(...) { return 0; };
-int _Py_NegativeRefcount(...) { return 0; };
-int _Py_NewReference(...) { return 0; };
-int _Py_PrintReferenceAddresses(...) { return 0; };
-int _Py_PrintReferences(...) { return 0; };
-int _Py_RefTotal(...) { return 0; };
-
-// We actually might call this one.
-void Py_Initialize() {
-}
-int Py_IsInitialized() {
-  return 0;
-}
-void PyEval_InitThreads() {
-}
-
-
-void *PyExc_AssertionError = nullptr;
-void *PyExc_AttributeError = nullptr;
-void *PyExc_BufferError = nullptr;
-void *PyExc_ConnectionError = nullptr;
-void *PyExc_Exception = nullptr;
-void *PyExc_FutureWarning = nullptr;
-void *PyExc_ImportError = nullptr;
-void *PyExc_IndexError = nullptr;
-void *PyExc_KeyError = nullptr;
-void *PyExc_NameError = nullptr;
-void *PyExc_OSError = nullptr;
-void *PyExc_OverflowError = nullptr;
-void *PyExc_RuntimeError = nullptr;
-void *PyExc_StandardError = nullptr;
-void *PyExc_StopIteration = nullptr;
-void *PyExc_SystemExit = nullptr;
-void *PyExc_TypeError = nullptr;
-void *PyExc_ValueError = nullptr;
-void *PyDict_Type = nullptr;
-void *PyFloat_Type = nullptr;
-void *PyGen_Type = nullptr;
-void *PyInt_Type = nullptr;
-void *PyList_Type = nullptr;
-void *PyLong_Type = nullptr;
-void *PyObject_Type = nullptr;
-void *PyString_Type = nullptr;
-void *PyTuple_Type = nullptr;
-void *PyType_Type = nullptr;
-void *PyUnicode_Type = nullptr;
-void *_PyThreadState_Current = nullptr;
-void *_Py_FalseStruct = nullptr;
-void *_Py_NoneStruct = nullptr;
-void *_Py_NotImplementedStruct = nullptr;
-void *_Py_TrueStruct = nullptr;
-void *_Py_ZeroStruct = nullptr;
-
-
-void
-pystub() {
-}

+ 0 - 38
dtool/src/pystub/pystub.h

@@ -1,38 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file pystub.h
- * @author drose
- * @date 2000-08-08
- */
-
-#ifndef PYSTUB_H
-#define PYSTUB_H
-
-#include "dtoolbase.h"
-
-// The sole purpose of this header file is to allow a program other than
-// Python to load in a module that includes Python wrappers.
-
-// We need this if we build the Panda libraries with Python wrappers, but want
-// to run a standalone program with those libraries.
-
-// This header file just stubs out the Python functions that these wrappers
-// will call.  You should include this header file in exactly one .C file in
-// your project, preferably in the .C file that defines main(), and then link
-// with -lpystub.  Do not include this header file in a .C or .h file that
-// will become part of an .so that might eventually link with Python.
-
-
-// You might need to call this function in main() or somewhere to force the
-// .so to be linked in--some OS'es try to be smart about not pulling in shared
-// libraries whose symbols aren't referenced anywhere.
-EXPCL_PYSTUB void pystub();
-
-
-#endif

+ 2 - 2
makepanda/getversion.py

@@ -4,13 +4,13 @@
 # and returns it on the command-line.  This is useful for the
 # and returns it on the command-line.  This is useful for the
 # automated scripts that build the Panda3D releases.
 # automated scripts that build the Panda3D releases.
 
 
-from makepandacore import ParsePandaVersion, ParsePluginVersion
+from makepandacore import ParsePandaVersion, ParsePluginVersion, GetMetadataValue
 import sys
 import sys
 
 
 if '--runtime' in sys.argv:
 if '--runtime' in sys.argv:
     version = ParsePluginVersion("dtool/PandaVersion.pp")
     version = ParsePluginVersion("dtool/PandaVersion.pp")
 else:
 else:
-    version = ParsePandaVersion("dtool/PandaVersion.pp")
+    version = GetMetadataValue('version')
 
 
 version = version.strip()
 version = version.strip()
 sys.stdout.write(version)
 sys.stdout.write(version)

+ 27 - 0
makepanda/installer.nsi

@@ -84,6 +84,7 @@ LangString DESC_SecTools ${LANG_ENGLISH} "Useful tools and model converters to h
 LangString DESC_SecGroupPython ${LANG_ENGLISH} "Contains modules that provide Python support for Panda3D."
 LangString DESC_SecGroupPython ${LANG_ENGLISH} "Contains modules that provide Python support for Panda3D."
 LangString DESC_SecPyShared ${LANG_ENGLISH} "Contains the common Python code used by the Panda3D Python bindings."
 LangString DESC_SecPyShared ${LANG_ENGLISH} "Contains the common Python code used by the Panda3D Python bindings."
 LangString DESC_SecPython ${LANG_ENGLISH} "Contains a ${REGVIEW}-bit copy of Python ${INCLUDE_PYVER} preconfigured to make use of Panda3D."
 LangString DESC_SecPython ${LANG_ENGLISH} "Contains a ${REGVIEW}-bit copy of Python ${INCLUDE_PYVER} preconfigured to make use of Panda3D."
+LangString DESC_SecEnsurePip ${LANG_ENGLISH} "Installs the pip package manager into the included Python installation."
 LangString DESC_SecHeadersLibs ${LANG_ENGLISH} "Headers and libraries needed for C++ development with Panda3D."
 LangString DESC_SecHeadersLibs ${LANG_ENGLISH} "Headers and libraries needed for C++ development with Panda3D."
 LangString DESC_SecSamples ${LANG_ENGLISH} "The sample programs demonstrate how to make Python applications with Panda3D."
 LangString DESC_SecSamples ${LANG_ENGLISH} "The sample programs demonstrate how to make Python applications with Panda3D."
 LangString DESC_SecMaxPlugins ${LANG_ENGLISH} "Plug-ins for Autodesk 3ds Max (${REGVIEW}-bit) that can be used to export models to Panda3D."
 LangString DESC_SecMaxPlugins ${LANG_ENGLISH} "Plug-ins for Autodesk 3ds Max (${REGVIEW}-bit) that can be used to export models to Panda3D."
@@ -477,6 +478,19 @@ Section "Python ${INCLUDE_PYVER}" SecPython
     SkipRegPath:
     SkipRegPath:
 
 
 SectionEnd
 SectionEnd
+
+Section "Install pip" SecEnsurePip
+    SectionIn 1 2 3
+
+    SetDetailsPrint both
+    DetailPrint "Installing the pip package manager..."
+    SetDetailsPrint listonly
+
+    SetOutPath $INSTDIR
+    nsExec::ExecToLog '"$INSTDIR\python\python.exe" -m ensurepip --default-pip'
+    Pop $0
+    DetailPrint "Command returned exit status $0"
+SectionEnd
 !endif
 !endif
 
 
 !macro MaybeEnablePyBindingSection PYVER
 !macro MaybeEnablePyBindingSection PYVER
@@ -537,6 +551,18 @@ Function .onSelChange
         IntOp $R0 $R0 | ${SF_SELECTED}
         IntOp $R0 $R0 | ${SF_SELECTED}
         SectionSetFlags ${SecPyShared} $R0
         SectionSetFlags ${SecPyShared} $R0
     ${EndIf}
     ${EndIf}
+
+    !ifdef INCLUDE_PYVER
+        ${If} ${SectionIsSelected} ${SecPython}
+            !insertmacro SectionFlagIsSet ${SecEnsurePip} ${SF_RO} 0 SkipSelectEnsurePip
+            !insertmacro SelectSection ${SecEnsurePip}
+            SkipSelectEnsurePip:
+            !insertmacro ClearSectionFlag ${SecEnsurePip} ${SF_RO}
+        ${Else}
+            !insertmacro UnselectSection ${SecEnsurePip}
+            !insertmacro SetSectionFlag ${SecEnsurePip} ${SF_RO}
+        ${EndIf}
+    !endif
 FunctionEnd
 FunctionEnd
 
 
 !ifdef INCLUDE_PYVER
 !ifdef INCLUDE_PYVER
@@ -917,6 +943,7 @@ SectionEnd
   !endif
   !endif
   !ifdef INCLUDE_PYVER
   !ifdef INCLUDE_PYVER
     !insertmacro MUI_DESCRIPTION_TEXT ${SecPython} $(DESC_SecPython)
     !insertmacro MUI_DESCRIPTION_TEXT ${SecPython} $(DESC_SecPython)
+    !insertmacro MUI_DESCRIPTION_TEXT ${SecEnsurePip} $(DESC_SecEnsurePip)
   !endif
   !endif
   !insertmacro MUI_DESCRIPTION_TEXT ${SecHeadersLibs} $(DESC_SecHeadersLibs)
   !insertmacro MUI_DESCRIPTION_TEXT ${SecHeadersLibs} $(DESC_SecHeadersLibs)
   !ifdef HAVE_SAMPLES
   !ifdef HAVE_SAMPLES

+ 1 - 1
makepanda/installpanda.py

@@ -226,7 +226,7 @@ def InstallPanda(destdir="", prefix="/usr", outputdir="built", libdir=GetLibDir(
         oscmd("echo '"+libdir+"/panda3d'>    "+destdir+"/etc/ld.so.conf.d/panda3d.conf")
         oscmd("echo '"+libdir+"/panda3d'>    "+destdir+"/etc/ld.so.conf.d/panda3d.conf")
 
 
     for base in os.listdir(outputdir+"/lib"):
     for base in os.listdir(outputdir+"/lib"):
-        if (not base.endswith(".a")) or base == "libp3pystub.a":
+        if not base.endswith(".a"):
             # We really need to specify -R in order not to follow symlinks on non-GNU
             # We really need to specify -R in order not to follow symlinks on non-GNU
             oscmd("cp -R -P "+outputdir+"/lib/"+base+" "+destdir+libdir+"/panda3d/"+base)
             oscmd("cp -R -P "+outputdir+"/lib/"+base+" "+destdir+libdir+"/panda3d/"+base)
 
 

+ 5 - 7
makepanda/makepackage.py

@@ -562,9 +562,9 @@ def MakeInstallerOSX(version, runtime=False, python_versions=[], **kwargs):
         oscmd("mkdir -p dstroot/pybindings%s/Library/Python/%s/site-packages" % (pyver, pyver))
         oscmd("mkdir -p dstroot/pybindings%s/Library/Python/%s/site-packages" % (pyver, pyver))
         WriteFile("dstroot/pybindings%s/Library/Python/%s/site-packages/Panda3D.pth" % (pyver, pyver), "/Developer/Panda3D")
         WriteFile("dstroot/pybindings%s/Library/Python/%s/site-packages/Panda3D.pth" % (pyver, pyver), "/Developer/Panda3D")
 
 
-        # Evidently not all Python 3.7 installations read the above path, so do this for now
-        # See GitHub #502
-        if pyver == "3.7":
+        # Somewhere in Python 2.7.13 and 3.7, the above path was removed from
+        # sys.path of the python.org distribution.  See bpo-28440 and GH #502.
+        if pyver not in ("3.0", "3.1", "3.2", "3.3", "3.4", "3.5", "3.6"):
             dir = "dstroot/pybindings%s/Library/Frameworks/Python.framework/Versions/%s/lib/python%s/site-packages" % (pyver, pyver, pyver)
             dir = "dstroot/pybindings%s/Library/Frameworks/Python.framework/Versions/%s/lib/python%s/site-packages" % (pyver, pyver, pyver)
             oscmd("mkdir -p %s" % (dir))
             oscmd("mkdir -p %s" % (dir))
             WriteFile("%s/Panda3D.pth" % (dir), "/Developer/Panda3D")
             WriteFile("%s/Panda3D.pth" % (dir), "/Developer/Panda3D")
@@ -584,8 +584,6 @@ def MakeInstallerOSX(version, runtime=False, python_versions=[], **kwargs):
 
 
     oscmd("mkdir -p dstroot/headers/Developer/Panda3D/lib")
     oscmd("mkdir -p dstroot/headers/Developer/Panda3D/lib")
     oscmd("cp -R %s/include               dstroot/headers/Developer/Panda3D/include" % outputdir)
     oscmd("cp -R %s/include               dstroot/headers/Developer/Panda3D/include" % outputdir)
-    if os.path.isfile(outputdir + "/lib/libp3pystub.a"):
-        oscmd("cp -R -P %s/lib/libp3pystub.a dstroot/headers/Developer/Panda3D/lib/" % outputdir)
 
 
     if os.path.isdir("samples"):
     if os.path.isdir("samples"):
         oscmd("mkdir -p dstroot/samples/Developer/Examples/Panda3D")
         oscmd("mkdir -p dstroot/samples/Developer/Examples/Panda3D")
@@ -1039,7 +1037,7 @@ def MakeInstaller(version, **kwargs):
 
 
 
 
 if __name__ == "__main__":
 if __name__ == "__main__":
-    version = ParsePandaVersion("dtool/PandaVersion.pp")
+    version = GetMetadataValue('version')
 
 
     parser = OptionParser()
     parser = OptionParser()
     parser.add_option('', '--version', dest='version', help='Panda3D version number (default: %s)' % (version), default=version)
     parser.add_option('', '--version', dest='version', help='Panda3D version number (default: %s)' % (version), default=version)
@@ -1071,7 +1069,7 @@ if __name__ == "__main__":
             PkgDisable(pkg)
             PkgDisable(pkg)
 
 
     # Parse the version.
     # Parse the version.
-    match = re.match(r'^\d+\.\d+\.\d+', options.version)
+    match = re.match(r'^\d+\.\d+(\.\d+)+', options.version)
     if not match:
     if not match:
         exit("version requires three digits")
         exit("version requires three digits")
 
 

+ 31 - 56
makepanda/makepanda.py

@@ -222,7 +222,7 @@ def parseopts(args):
             elif (option=="--arch"): target_arch = value.strip()
             elif (option=="--arch"): target_arch = value.strip()
             elif (option=="--nocolor"): DisableColors()
             elif (option=="--nocolor"): DisableColors()
             elif (option=="--version"):
             elif (option=="--version"):
-                match = re.match(r'^\d+\.\d+\.\d+', value)
+                match = re.match(r'^\d+\.\d+(\.\d+)+', value)
                 if not match:
                 if not match:
                     usage("version requires three digits")
                     usage("version requires three digits")
                 WHLVERSION = value
                 WHLVERSION = value
@@ -924,6 +924,10 @@ if (COMPILER=="GCC"):
         else:
         else:
             PkgDisable("OPENCV")
             PkgDisable("OPENCV")
 
 
+        if GetTarget() == "darwin" and not PkgSkip("OPENAL"):
+            LibName("OPENAL", "-framework AudioToolbox")
+            LibName("OPENAL", "-framework CoreAudio")
+
         if not PkgSkip("ASSIMP") and \
         if not PkgSkip("ASSIMP") and \
             os.path.isfile(GetThirdpartyDir() + "assimp/lib/libassimp.a"):
             os.path.isfile(GetThirdpartyDir() + "assimp/lib/libassimp.a"):
             # Also pick up IrrXML, which is needed when linking statically.
             # Also pick up IrrXML, which is needed when linking statically.
@@ -1716,12 +1720,6 @@ def CompileLink(dll, obj, opts):
                     else:
                     else:
                         cmd += ' /NOD:{}.lib'.format(pythonv)
                         cmd += ' /NOD:{}.lib'.format(pythonv)
 
 
-                # Yes, we know we are importing "locally defined symbols".
-                for x in obj:
-                    if x.endswith('libp3pystub.lib'):
-                        cmd += ' /ignore:4049,4217'
-                        break
-
             # Set the subsystem.  Specify that we want to target Windows XP.
             # Set the subsystem.  Specify that we want to target Windows XP.
             subsystem = GetValueOption(opts, "SUBSYSTEM:") or "CONSOLE"
             subsystem = GetValueOption(opts, "SUBSYSTEM:") or "CONSOLE"
             cmd += " /SUBSYSTEM:" + subsystem
             cmd += " /SUBSYSTEM:" + subsystem
@@ -2680,7 +2678,7 @@ PANDAVERSION_H="""
 #define PANDA_SEQUENCE_VERSION $VERSION3
 #define PANDA_SEQUENCE_VERSION $VERSION3
 #define PANDA_VERSION $NVERSION
 #define PANDA_VERSION $NVERSION
 #define PANDA_NUMERIC_VERSION $NVERSION
 #define PANDA_NUMERIC_VERSION $NVERSION
-#define PANDA_VERSION_STR "$VERSION1.$VERSION2.$VERSION3"
+#define PANDA_VERSION_STR "$VERSION"
 #define PANDA_ABI_VERSION_STR "$VERSION1.$VERSION2"
 #define PANDA_ABI_VERSION_STR "$VERSION1.$VERSION2"
 #define PANDA_DISTRIBUTOR "$DISTRIBUTOR"
 #define PANDA_DISTRIBUTOR "$DISTRIBUTOR"
 #define PANDA_PACKAGE_VERSION_STR "$RTDIST_VERSION"
 #define PANDA_PACKAGE_VERSION_STR "$RTDIST_VERSION"
@@ -2799,6 +2797,7 @@ def CreatePandaVersionFiles():
     pandaversion_h = pandaversion_h.replace("$VERSION1",str(version1))
     pandaversion_h = pandaversion_h.replace("$VERSION1",str(version1))
     pandaversion_h = pandaversion_h.replace("$VERSION2",str(version2))
     pandaversion_h = pandaversion_h.replace("$VERSION2",str(version2))
     pandaversion_h = pandaversion_h.replace("$VERSION3",str(version3))
     pandaversion_h = pandaversion_h.replace("$VERSION3",str(version3))
+    pandaversion_h = pandaversion_h.replace("$VERSION",VERSION)
     pandaversion_h = pandaversion_h.replace("$NVERSION",str(nversion))
     pandaversion_h = pandaversion_h.replace("$NVERSION",str(nversion))
     pandaversion_h = pandaversion_h.replace("$DISTRIBUTOR",DISTRIBUTOR)
     pandaversion_h = pandaversion_h.replace("$DISTRIBUTOR",DISTRIBUTOR)
     pandaversion_h = pandaversion_h.replace("$RTDIST_VERSION",RTDIST_VERSION)
     pandaversion_h = pandaversion_h.replace("$RTDIST_VERSION",RTDIST_VERSION)
@@ -2899,7 +2898,9 @@ if '__file__' in locals():
 
 
     bindir = os.path.join(os.path.dirname(__file__), '..', 'bin')
     bindir = os.path.join(os.path.dirname(__file__), '..', 'bin')
     if os.path.isdir(bindir):
     if os.path.isdir(bindir):
-        if not os.environ.get('PATH'):
+        if hasattr(os, 'add_dll_directory'):
+            os.add_dll_directory(bindir)
+        elif not os.environ.get('PATH'):
             os.environ['PATH'] = bindir
             os.environ['PATH'] = bindir
         else:
         else:
             os.environ['PATH'] = bindir + os.pathsep + os.environ['PATH']
             os.environ['PATH'] = bindir + os.pathsep + os.environ['PATH']
@@ -3206,9 +3207,8 @@ if tp_dir is not None:
 
 
 # Copy over the MSVC runtime.
 # Copy over the MSVC runtime.
 if GetTarget() == 'windows' and "VISUALSTUDIO" in SDK:
 if GetTarget() == 'windows' and "VISUALSTUDIO" in SDK:
-    vsver = "%s%s" % SDK["VISUALSTUDIO_VERSION"]
     vcver = "%s%s" % (SDK["MSVC_VERSION"][0], 0)        # ignore minor version.
     vcver = "%s%s" % (SDK["MSVC_VERSION"][0], 0)        # ignore minor version.
-    crtname = "Microsoft.VC%s.CRT" % (vsver)
+    crtname = "Microsoft.VC%s.CRT" % (vcver)
     if ("VCTOOLSVERSION" in SDK):
     if ("VCTOOLSVERSION" in SDK):
         dir = os.path.join(SDK["VISUALSTUDIO"], "VC", "Redist", "MSVC", SDK["VCTOOLSVERSION"], "onecore", GetTargetArch(), crtname)
         dir = os.path.join(SDK["VISUALSTUDIO"], "VC", "Redist", "MSVC", SDK["VCTOOLSVERSION"], "onecore", GetTargetArch(), crtname)
     else:
     else:
@@ -3272,7 +3272,6 @@ CopyAllHeaders('dtool/src/prc', skip=["prc_parameters.h"])
 CopyAllHeaders('dtool/src/dconfig')
 CopyAllHeaders('dtool/src/dconfig')
 CopyAllHeaders('dtool/src/interrogatedb')
 CopyAllHeaders('dtool/src/interrogatedb')
 CopyAllHeaders('dtool/metalibs/dtoolconfig')
 CopyAllHeaders('dtool/metalibs/dtoolconfig')
-CopyAllHeaders('dtool/src/pystub')
 CopyAllHeaders('dtool/src/interrogate')
 CopyAllHeaders('dtool/src/interrogate')
 CopyAllHeaders('dtool/src/test_interrogate')
 CopyAllHeaders('dtool/src/test_interrogate')
 CopyAllHeaders('panda/src/putil')
 CopyAllHeaders('panda/src/putil')
@@ -3577,17 +3576,6 @@ PyTargetAdd('interrogatedb.pyd', input='libp3dtool.dll')
 PyTargetAdd('interrogatedb.pyd', input='libp3dtoolconfig.dll')
 PyTargetAdd('interrogatedb.pyd', input='libp3dtoolconfig.dll')
 PyTargetAdd('interrogatedb.pyd', input='libp3interrogatedb.dll')
 PyTargetAdd('interrogatedb.pyd', input='libp3interrogatedb.dll')
 
 
-#
-# DIRECTORY: dtool/src/pystub/
-#
-
-if not RUNTIME and not RTDIST:
-  OPTS=['DIR:dtool/src/pystub']
-  TargetAdd('p3pystub_pystub.obj', opts=OPTS, input='pystub.cxx')
-  TargetAdd('libp3pystub.lib', input='p3pystub_pystub.obj')
-  #TargetAdd('libp3pystub.lib', input='libp3dtool.dll')
-  TargetAdd('libp3pystub.lib', opts=['ADVAPI'])
-
 #
 #
 # DIRECTORY: dtool/src/interrogate/
 # DIRECTORY: dtool/src/interrogate/
 #
 #
@@ -3734,7 +3722,6 @@ OPTS=['DIR:panda/src/downloader', 'OPENSSL', 'ZLIB']
 IGATEFILES=GetDirectoryContents('panda/src/downloader', ["*.h", "*_composite*.cxx"])
 IGATEFILES=GetDirectoryContents('panda/src/downloader', ["*.h", "*_composite*.cxx"])
 TargetAdd('libp3downloader.in', opts=OPTS, input=IGATEFILES)
 TargetAdd('libp3downloader.in', opts=OPTS, input=IGATEFILES)
 TargetAdd('libp3downloader.in', opts=['IMOD:panda3d.core', 'ILIB:libp3downloader', 'SRCDIR:panda/src/downloader'])
 TargetAdd('libp3downloader.in', opts=['IMOD:panda3d.core', 'ILIB:libp3downloader', 'SRCDIR:panda/src/downloader'])
-PyTargetAdd('p3downloader_stringStream_ext.obj', opts=OPTS, input='stringStream_ext.cxx')
 
 
 #
 #
 # DIRECTORY: panda/metalibs/pandaexpress/
 # DIRECTORY: panda/metalibs/pandaexpress/
@@ -4357,7 +4344,6 @@ if (not RUNTIME):
   PyTargetAdd('core.pyd', input='p3prc_ext_composite.obj')
   PyTargetAdd('core.pyd', input='p3prc_ext_composite.obj')
 
 
   PyTargetAdd('core.pyd', input='libp3downloader_igate.obj')
   PyTargetAdd('core.pyd', input='libp3downloader_igate.obj')
-  PyTargetAdd('core.pyd', input='p3downloader_stringStream_ext.obj')
   PyTargetAdd('core.pyd', input='p3express_ext_composite.obj')
   PyTargetAdd('core.pyd', input='p3express_ext_composite.obj')
   PyTargetAdd('core.pyd', input='libp3express_igate.obj')
   PyTargetAdd('core.pyd', input='libp3express_igate.obj')
 
 
@@ -5252,11 +5238,11 @@ if (PkgSkip("DIRECT")==0):
 if (PkgSkip("DIRECT")==0):
 if (PkgSkip("DIRECT")==0):
   OPTS=['DIR:direct/src/dcparser', 'BUILDING:DIRECT_DCPARSER', 'WITHINPANDA', 'BISONPREFIX_dcyy']
   OPTS=['DIR:direct/src/dcparser', 'BUILDING:DIRECT_DCPARSER', 'WITHINPANDA', 'BISONPREFIX_dcyy']
   CreateFile(GetOutputDir()+"/include/dcParser.h")
   CreateFile(GetOutputDir()+"/include/dcParser.h")
-  PyTargetAdd('p3dcparser_dcParser.obj', opts=OPTS, input='dcParser.yxx')
+  TargetAdd('p3dcparser_dcParser.obj', opts=OPTS, input='dcParser.yxx')
   #TargetAdd('dcParser.h', input='p3dcparser_dcParser.obj', opts=['DEPENDENCYONLY'])
   #TargetAdd('dcParser.h', input='p3dcparser_dcParser.obj', opts=['DEPENDENCYONLY'])
-  PyTargetAdd('p3dcparser_dcLexer.obj', opts=OPTS, input='dcLexer.lxx')
-  PyTargetAdd('p3dcparser_composite1.obj', opts=OPTS, input='p3dcparser_composite1.cxx')
-  PyTargetAdd('p3dcparser_composite2.obj', opts=OPTS, input='p3dcparser_composite2.cxx')
+  TargetAdd('p3dcparser_dcLexer.obj', opts=OPTS, input='dcLexer.lxx')
+  TargetAdd('p3dcparser_composite1.obj', opts=OPTS, input='p3dcparser_composite1.cxx')
+  TargetAdd('p3dcparser_composite2.obj', opts=OPTS, input='p3dcparser_composite2.cxx')
 
 
   OPTS=['DIR:direct/src/dcparser', 'WITHINPANDA']
   OPTS=['DIR:direct/src/dcparser', 'WITHINPANDA']
   IGATEFILES=GetDirectoryContents('direct/src/dcparser', ["*.h", "*_composite*.cxx"])
   IGATEFILES=GetDirectoryContents('direct/src/dcparser', ["*.h", "*_composite*.cxx"])
@@ -5264,6 +5250,7 @@ if (PkgSkip("DIRECT")==0):
   if "dcmsgtypes.h" in IGATEFILES: IGATEFILES.remove('dcmsgtypes.h')
   if "dcmsgtypes.h" in IGATEFILES: IGATEFILES.remove('dcmsgtypes.h')
   TargetAdd('libp3dcparser.in', opts=OPTS, input=IGATEFILES)
   TargetAdd('libp3dcparser.in', opts=OPTS, input=IGATEFILES)
   TargetAdd('libp3dcparser.in', opts=['IMOD:panda3d.direct', 'ILIB:libp3dcparser', 'SRCDIR:direct/src/dcparser'])
   TargetAdd('libp3dcparser.in', opts=['IMOD:panda3d.direct', 'ILIB:libp3dcparser', 'SRCDIR:direct/src/dcparser'])
+  PyTargetAdd('p3dcparser_ext_composite.obj', opts=OPTS, input='p3dcparser_ext_composite.cxx')
 
 
 #
 #
 # DIRECTORY: direct/src/deadrec/
 # DIRECTORY: direct/src/deadrec/
@@ -5285,13 +5272,13 @@ if (PkgSkip("DIRECT")==0):
 if (PkgSkip("DIRECT")==0):
 if (PkgSkip("DIRECT")==0):
   OPTS=['DIR:direct/src/distributed', 'DIR:direct/src/dcparser', 'WITHINPANDA', 'BUILDING:DIRECT', 'OPENSSL']
   OPTS=['DIR:direct/src/distributed', 'DIR:direct/src/dcparser', 'WITHINPANDA', 'BUILDING:DIRECT', 'OPENSSL']
   TargetAdd('p3distributed_config_distributed.obj', opts=OPTS, input='config_distributed.cxx')
   TargetAdd('p3distributed_config_distributed.obj', opts=OPTS, input='config_distributed.cxx')
-  PyTargetAdd('p3distributed_cConnectionRepository.obj', opts=OPTS, input='cConnectionRepository.cxx')
-  PyTargetAdd('p3distributed_cDistributedSmoothNodeBase.obj', opts=OPTS, input='cDistributedSmoothNodeBase.cxx')
 
 
   OPTS=['DIR:direct/src/distributed', 'WITHINPANDA', 'OPENSSL']
   OPTS=['DIR:direct/src/distributed', 'WITHINPANDA', 'OPENSSL']
   IGATEFILES=GetDirectoryContents('direct/src/distributed', ["*.h", "*.cxx"])
   IGATEFILES=GetDirectoryContents('direct/src/distributed', ["*.h", "*.cxx"])
   TargetAdd('libp3distributed.in', opts=OPTS, input=IGATEFILES)
   TargetAdd('libp3distributed.in', opts=OPTS, input=IGATEFILES)
   TargetAdd('libp3distributed.in', opts=['IMOD:panda3d.direct', 'ILIB:libp3distributed', 'SRCDIR:direct/src/distributed'])
   TargetAdd('libp3distributed.in', opts=['IMOD:panda3d.direct', 'ILIB:libp3distributed', 'SRCDIR:direct/src/distributed'])
+  PyTargetAdd('p3distributed_cConnectionRepository.obj', opts=OPTS, input='cConnectionRepository.cxx')
+  PyTargetAdd('p3distributed_cDistributedSmoothNodeBase.obj', opts=OPTS, input='cDistributedSmoothNodeBase.cxx')
 
 
 #
 #
 # DIRECTORY: direct/src/interval/
 # DIRECTORY: direct/src/interval/
@@ -5341,10 +5328,15 @@ if (PkgSkip("DIRECT")==0):
 
 
 if (PkgSkip("DIRECT")==0):
 if (PkgSkip("DIRECT")==0):
   TargetAdd('libp3direct.dll', input='p3directbase_directbase.obj')
   TargetAdd('libp3direct.dll', input='p3directbase_directbase.obj')
+  TargetAdd('libp3direct.dll', input='p3dcparser_composite1.obj')
+  TargetAdd('libp3direct.dll', input='p3dcparser_composite2.obj')
+  TargetAdd('libp3direct.dll', input='p3dcparser_dcParser.obj')
+  TargetAdd('libp3direct.dll', input='p3dcparser_dcLexer.obj')
   TargetAdd('libp3direct.dll', input='p3showbase_showBase.obj')
   TargetAdd('libp3direct.dll', input='p3showbase_showBase.obj')
   if GetTarget() == 'darwin':
   if GetTarget() == 'darwin':
     TargetAdd('libp3direct.dll', input='p3showbase_showBase_assist.obj')
     TargetAdd('libp3direct.dll', input='p3showbase_showBase_assist.obj')
   TargetAdd('libp3direct.dll', input='p3deadrec_composite1.obj')
   TargetAdd('libp3direct.dll', input='p3deadrec_composite1.obj')
+  TargetAdd('libp3direct.dll', input='p3distributed_config_distributed.obj')
   TargetAdd('libp3direct.dll', input='p3interval_composite1.obj')
   TargetAdd('libp3direct.dll', input='p3interval_composite1.obj')
   TargetAdd('libp3direct.dll', input='p3motiontrail_config_motiontrail.obj')
   TargetAdd('libp3direct.dll', input='p3motiontrail_config_motiontrail.obj')
   TargetAdd('libp3direct.dll', input='p3motiontrail_cMotionTrail.obj')
   TargetAdd('libp3direct.dll', input='p3motiontrail_cMotionTrail.obj')
@@ -5369,11 +5361,7 @@ if (PkgSkip("DIRECT")==0):
   # These are part of direct.pyd, not libp3direct.dll, because they rely on
   # These are part of direct.pyd, not libp3direct.dll, because they rely on
   # the Python libraries.  If a C++ user needs these modules, we can move them
   # the Python libraries.  If a C++ user needs these modules, we can move them
   # back and filter out the Python-specific code.
   # back and filter out the Python-specific code.
-  PyTargetAdd('direct.pyd', input='p3dcparser_composite1.obj')
-  PyTargetAdd('direct.pyd', input='p3dcparser_composite2.obj')
-  PyTargetAdd('direct.pyd', input='p3dcparser_dcParser.obj')
-  PyTargetAdd('direct.pyd', input='p3dcparser_dcLexer.obj')
-  PyTargetAdd('direct.pyd', input='p3distributed_config_distributed.obj')
+  PyTargetAdd('direct.pyd', input='p3dcparser_ext_composite.obj')
   PyTargetAdd('direct.pyd', input='p3distributed_cConnectionRepository.obj')
   PyTargetAdd('direct.pyd', input='p3distributed_cConnectionRepository.obj')
   PyTargetAdd('direct.pyd', input='p3distributed_cDistributedSmoothNodeBase.obj')
   PyTargetAdd('direct.pyd', input='p3distributed_cDistributedSmoothNodeBase.obj')
 
 
@@ -5387,18 +5375,13 @@ if (PkgSkip("DIRECT")==0):
 # DIRECTORY: direct/src/dcparse/
 # DIRECTORY: direct/src/dcparse/
 #
 #
 
 
-if (PkgSkip("PYTHON")==0 and PkgSkip("DIRECT")==0 and not RTDIST and not RUNTIME):
+if (PkgSkip("DIRECT")==0 and not RTDIST and not RUNTIME):
   OPTS=['DIR:direct/src/dcparse', 'DIR:direct/src/dcparser', 'WITHINPANDA', 'ADVAPI']
   OPTS=['DIR:direct/src/dcparse', 'DIR:direct/src/dcparser', 'WITHINPANDA', 'ADVAPI']
-  PyTargetAdd('dcparse_dcparse.obj', opts=OPTS, input='dcparse.cxx')
-  PyTargetAdd('p3dcparse.exe', input='p3dcparser_composite1.obj')
-  PyTargetAdd('p3dcparse.exe', input='p3dcparser_composite2.obj')
-  PyTargetAdd('p3dcparse.exe', input='p3dcparser_dcParser.obj')
-  PyTargetAdd('p3dcparse.exe', input='p3dcparser_dcLexer.obj')
-  PyTargetAdd('p3dcparse.exe', input='dcparse_dcparse.obj')
-  PyTargetAdd('p3dcparse.exe', input='libp3direct.dll')
-  PyTargetAdd('p3dcparse.exe', input=COMMON_PANDA_LIBS)
-  PyTargetAdd('p3dcparse.exe', input='libp3pystub.lib')
-  PyTargetAdd('p3dcparse.exe', opts=['ADVAPI'])
+  TargetAdd('dcparse_dcparse.obj', opts=OPTS, input='dcparse.cxx')
+  TargetAdd('p3dcparse.exe', input='dcparse_dcparse.obj')
+  TargetAdd('p3dcparse.exe', input='libp3direct.dll')
+  TargetAdd('p3dcparse.exe', input=COMMON_PANDA_LIBS)
+  TargetAdd('p3dcparse.exe', opts=['ADVAPI'])
 
 
 #
 #
 # DIRECTORY: direct/src/plugin/
 # DIRECTORY: direct/src/plugin/
@@ -5602,7 +5585,6 @@ if (RUNTIME):
   TargetAdd('panda3d.exe', input='libpandaexpress.dll')
   TargetAdd('panda3d.exe', input='libpandaexpress.dll')
   TargetAdd('panda3d.exe', input='libp3dtoolconfig.dll')
   TargetAdd('panda3d.exe', input='libp3dtoolconfig.dll')
   TargetAdd('panda3d.exe', input='libp3dtool.dll')
   TargetAdd('panda3d.exe', input='libp3dtool.dll')
-  #TargetAdd('panda3d.exe', input='libp3pystub.lib')
   TargetAdd('panda3d.exe', input='libp3tinyxml.ilb')
   TargetAdd('panda3d.exe', input='libp3tinyxml.ilb')
   TargetAdd('panda3d.exe', opts=['NOICON', 'OPENSSL', 'ZLIB', 'WINGDI', 'WINUSER', 'WINSHELL', 'ADVAPI', 'WINSOCK2', 'WINOLE', 'CARBON'])
   TargetAdd('panda3d.exe', opts=['NOICON', 'OPENSSL', 'ZLIB', 'WINGDI', 'WINUSER', 'WINSHELL', 'ADVAPI', 'WINSOCK2', 'WINOLE', 'CARBON'])
 
 
@@ -5616,7 +5598,6 @@ if (RUNTIME):
     TargetAdd('Panda3D.app', input='libpandaexpress.dll')
     TargetAdd('Panda3D.app', input='libpandaexpress.dll')
     TargetAdd('Panda3D.app', input='libp3dtoolconfig.dll')
     TargetAdd('Panda3D.app', input='libp3dtoolconfig.dll')
     TargetAdd('Panda3D.app', input='libp3dtool.dll')
     TargetAdd('Panda3D.app', input='libp3dtool.dll')
-    #TargetAdd('Panda3D.app', input='libp3pystub.lib')
     TargetAdd('Panda3D.app', input='libp3tinyxml.ilb')
     TargetAdd('Panda3D.app', input='libp3tinyxml.ilb')
     TargetAdd('Panda3D.app', input='panda3d_mac.plist', ipath=OPTS)
     TargetAdd('Panda3D.app', input='panda3d_mac.plist', ipath=OPTS)
     TargetAdd('Panda3D.app', input='models/plugin_images/panda3d.icns')
     TargetAdd('Panda3D.app', input='models/plugin_images/panda3d.icns')
@@ -5631,18 +5612,16 @@ if (RUNTIME):
     TargetAdd('panda3dw.exe', input='libpandaexpress.dll')
     TargetAdd('panda3dw.exe', input='libpandaexpress.dll')
     TargetAdd('panda3dw.exe', input='libp3dtoolconfig.dll')
     TargetAdd('panda3dw.exe', input='libp3dtoolconfig.dll')
     TargetAdd('panda3dw.exe', input='libp3dtool.dll')
     TargetAdd('panda3dw.exe', input='libp3dtool.dll')
-    #TargetAdd('panda3dw.exe', input='libp3pystub.lib')
     TargetAdd('panda3dw.exe', input='libp3tinyxml.ilb')
     TargetAdd('panda3dw.exe', input='libp3tinyxml.ilb')
     TargetAdd('panda3dw.exe', opts=['SUBSYSTEM:WINDOWS', 'OPENSSL', 'ZLIB', 'WINGDI', 'WINUSER', 'WINSHELL', 'ADVAPI', 'WINSOCK2', 'WINOLE', 'CARBON'])
     TargetAdd('panda3dw.exe', opts=['SUBSYSTEM:WINDOWS', 'OPENSSL', 'ZLIB', 'WINGDI', 'WINUSER', 'WINSHELL', 'ADVAPI', 'WINSOCK2', 'WINOLE', 'CARBON'])
 
 
 if (RTDIST):
 if (RTDIST):
-  OPTS=['BUILDING:P3D_PLUGIN', 'DIR:direct/src/plugin_standalone', 'DIR:direct/src/plugin', 'DIR:dtool/src/dtoolbase', 'DIR:dtool/src/dtoolutil', 'DIR:dtool/src/pystub', 'DIR:dtool/src/prc', 'DIR:dtool/src/dconfig', 'DIR:panda/src/express', 'DIR:panda/src/downloader', 'RUNTIME', 'P3DEMBED', 'OPENSSL', 'ZLIB']
+  OPTS=['BUILDING:P3D_PLUGIN', 'DIR:direct/src/plugin_standalone', 'DIR:direct/src/plugin', 'DIR:dtool/src/dtoolbase', 'DIR:dtool/src/dtoolutil', 'DIR:dtool/src/prc', 'DIR:dtool/src/dconfig', 'DIR:panda/src/express', 'DIR:panda/src/downloader', 'RUNTIME', 'P3DEMBED', 'OPENSSL', 'ZLIB']
   # This is arguably a big fat ugly hack, but doing it otherwise would complicate the build process considerably.
   # This is arguably a big fat ugly hack, but doing it otherwise would complicate the build process considerably.
   DefSymbol("P3DEMBED", "LINK_ALL_STATIC", "")
   DefSymbol("P3DEMBED", "LINK_ALL_STATIC", "")
   TargetAdd('plugin_standalone_panda3dBase.obj', opts=OPTS, input='panda3dBase.cxx')
   TargetAdd('plugin_standalone_panda3dBase.obj', opts=OPTS, input='panda3dBase.cxx')
   TargetAdd('plugin_standalone_p3dEmbedMain.obj', opts=OPTS, input='p3dEmbedMain.cxx')
   TargetAdd('plugin_standalone_p3dEmbedMain.obj', opts=OPTS, input='p3dEmbedMain.cxx')
   TargetAdd('plugin_standalone_p3dEmbed.obj', opts=OPTS, input='p3dEmbed.cxx')
   TargetAdd('plugin_standalone_p3dEmbed.obj', opts=OPTS, input='p3dEmbed.cxx')
-  #TargetAdd('plugin_standalone_pystub.obj', opts=OPTS, input='pystub.cxx')
   TargetAdd('plugin_standalone_dtoolbase_composite1.obj', opts=OPTS, input='p3dtoolbase_composite1.cxx')
   TargetAdd('plugin_standalone_dtoolbase_composite1.obj', opts=OPTS, input='p3dtoolbase_composite1.cxx')
   TargetAdd('plugin_standalone_dtoolbase_composite2.obj', opts=OPTS, input='p3dtoolbase_composite2.cxx')
   TargetAdd('plugin_standalone_dtoolbase_composite2.obj', opts=OPTS, input='p3dtoolbase_composite2.cxx')
   TargetAdd('plugin_standalone_lookup3.obj', opts=OPTS, input='lookup3.c')
   TargetAdd('plugin_standalone_lookup3.obj', opts=OPTS, input='lookup3.c')
@@ -5660,7 +5639,6 @@ if (RTDIST):
   TargetAdd('p3dembed.exe', input='plugin_standalone_panda3dBase.obj')
   TargetAdd('p3dembed.exe', input='plugin_standalone_panda3dBase.obj')
   TargetAdd('p3dembed.exe', input='plugin_standalone_p3dEmbedMain.obj')
   TargetAdd('p3dembed.exe', input='plugin_standalone_p3dEmbedMain.obj')
   TargetAdd('p3dembed.exe', input='plugin_standalone_p3dEmbed.obj')
   TargetAdd('p3dembed.exe', input='plugin_standalone_p3dEmbed.obj')
-  #TargetAdd('p3dembed.exe', input='plugin_standalone_pystub.obj')
   TargetAdd('p3dembed.exe', input='plugin_standalone_dtoolbase_composite1.obj')
   TargetAdd('p3dembed.exe', input='plugin_standalone_dtoolbase_composite1.obj')
   TargetAdd('p3dembed.exe', input='plugin_standalone_dtoolbase_composite2.obj')
   TargetAdd('p3dembed.exe', input='plugin_standalone_dtoolbase_composite2.obj')
   TargetAdd('p3dembed.exe', input='plugin_standalone_lookup3.obj')
   TargetAdd('p3dembed.exe', input='plugin_standalone_lookup3.obj')
@@ -5690,7 +5668,6 @@ if (RTDIST):
     TargetAdd('p3dembedw.exe', input='plugin_standalone_panda3dBase.obj')
     TargetAdd('p3dembedw.exe', input='plugin_standalone_panda3dBase.obj')
     TargetAdd('p3dembedw.exe', input='plugin_standalone_p3dEmbedWinMain.obj')
     TargetAdd('p3dembedw.exe', input='plugin_standalone_p3dEmbedWinMain.obj')
     TargetAdd('p3dembedw.exe', input='plugin_standalone_p3dEmbed.obj')
     TargetAdd('p3dembedw.exe', input='plugin_standalone_p3dEmbed.obj')
-    #TargetAdd('p3dembedw.exe', input='plugin_standalone_pystub.obj')
     TargetAdd('p3dembedw.exe', input='plugin_standalone_dtoolbase_composite1.obj')
     TargetAdd('p3dembedw.exe', input='plugin_standalone_dtoolbase_composite1.obj')
     TargetAdd('p3dembedw.exe', input='plugin_standalone_dtoolbase_composite2.obj')
     TargetAdd('p3dembedw.exe', input='plugin_standalone_dtoolbase_composite2.obj')
     TargetAdd('p3dembedw.exe', input='plugin_standalone_lookup3.obj')
     TargetAdd('p3dembedw.exe', input='plugin_standalone_lookup3.obj')
@@ -6445,8 +6422,6 @@ for VER in MAYAVERSIONS:
     TargetAdd('mayaeggimport'+VNUM+'.mll', input='mayaeggimport'+VNUM+'_mayaeggimport.obj')
     TargetAdd('mayaeggimport'+VNUM+'.mll', input='mayaeggimport'+VNUM+'_mayaeggimport.obj')
     TargetAdd('mayaeggimport'+VNUM+'.mll', input='libpandaegg.dll')
     TargetAdd('mayaeggimport'+VNUM+'.mll', input='libpandaegg.dll')
     TargetAdd('mayaeggimport'+VNUM+'.mll', input=COMMON_PANDA_LIBS)
     TargetAdd('mayaeggimport'+VNUM+'.mll', input=COMMON_PANDA_LIBS)
-    #if GetTarget() == 'windows':
-    #  TargetAdd('mayaeggimport'+VNUM+'.mll', input='libp3pystub.lib')
     TargetAdd('mayaeggimport'+VNUM+'.mll', opts=['ADVAPI', VER])
     TargetAdd('mayaeggimport'+VNUM+'.mll', opts=['ADVAPI', VER])
 
 
     TargetAdd('mayaloader'+VNUM+'_config_mayaloader.obj', opts=OPTS, input='config_mayaloader.cxx')
     TargetAdd('mayaloader'+VNUM+'_config_mayaloader.obj', opts=OPTS, input='config_mayaloader.cxx')

+ 0 - 4
makepanda/makepanda.vcproj

@@ -361,10 +361,6 @@
 				<File RelativePath="..\dtool\src\dtoolbase\typedObject.h"></File>
 				<File RelativePath="..\dtool\src\dtoolbase\typedObject.h"></File>
 				<File RelativePath="..\dtool\src\dtoolbase\mutexWin32Impl.cxx"></File>
 				<File RelativePath="..\dtool\src\dtoolbase\mutexWin32Impl.cxx"></File>
 			</Filter>
 			</Filter>
-			<Filter Name="pystub">
-				<File RelativePath="..\dtool\src\pystub\pystub.h"></File>
-				<File RelativePath="..\dtool\src\pystub\pystub.cxx"></File>
-			</Filter>
 			<Filter Name="dtoolutil">
 			<Filter Name="dtoolutil">
 				<File RelativePath="..\dtool\src\dtoolutil\pandaFileStreamBuf.h"></File>
 				<File RelativePath="..\dtool\src\dtoolutil\pandaFileStreamBuf.h"></File>
 				<File RelativePath="..\dtool\src\dtoolutil\vector_src.h"></File>
 				<File RelativePath="..\dtool\src\dtoolutil\vector_src.h"></File>

+ 5 - 1
makepanda/makepandacore.py

@@ -105,6 +105,7 @@ MAYAVERSIONINFO = [("MAYA6",   "6.0"),
                    ("MAYA20165","2016.5"),
                    ("MAYA20165","2016.5"),
                    ("MAYA2017","2017"),
                    ("MAYA2017","2017"),
                    ("MAYA2018","2018"),
                    ("MAYA2018","2018"),
+                   ("MAYA2019","2019"),
 ]
 ]
 
 
 MAXVERSIONINFO = [("MAX6", "SOFTWARE\\Autodesk\\3DSMAX\\6.0", "installdir", "maxsdk\\cssdk\\include"),
 MAXVERSIONINFO = [("MAX6", "SOFTWARE\\Autodesk\\3DSMAX\\6.0", "installdir", "maxsdk\\cssdk\\include"),
@@ -2788,7 +2789,7 @@ def SetupVisualStudioEnviron():
 
 
     # Targeting the 7.1 SDK (which is the only way to have Windows XP support)
     # Targeting the 7.1 SDK (which is the only way to have Windows XP support)
     # with Visual Studio 2015 requires use of the Universal CRT.
     # with Visual Studio 2015 requires use of the Universal CRT.
-    if winsdk_ver == '7.1' and SDK["VISUALSTUDIO_VERSION"] >= (14,0):
+    if winsdk_ver in ('7.1', '7.1A') and SDK["VISUALSTUDIO_VERSION"] >= (14,0):
         win_kit = GetRegistryKey("SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", "KitsRoot10")
         win_kit = GetRegistryKey("SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", "KitsRoot10")
 
 
         # Fallback in case we can't read the registry.
         # Fallback in case we can't read the registry.
@@ -3392,6 +3393,9 @@ def GetPythonABI():
 
 
     soabi = 'cpython-%d%d' % (sys.version_info[:2])
     soabi = 'cpython-%d%d' % (sys.version_info[:2])
 
 
+    if sys.version_info >= (3, 8):
+        return soabi
+
     debug_flag = sysconfig.get_config_var('Py_DEBUG')
     debug_flag = sysconfig.get_config_var('Py_DEBUG')
     if (debug_flag is None and hasattr(sys, 'gettotalrefcount')) or debug_flag:
     if (debug_flag is None and hasattr(sys, 'gettotalrefcount')) or debug_flag:
         soabi += 'd'
         soabi += 'd'

+ 14 - 12
makepanda/makewheel.py

@@ -1,10 +1,5 @@
 """
 """
 Generates a wheel (.whl) file from the output of makepanda.
 Generates a wheel (.whl) file from the output of makepanda.
-
-Since the wheel requires special linking, this will only work if compiled with
-the `--wheel` parameter.
-
-Please keep this file work with Panda3D 1.9 until that reaches EOL.
 """
 """
 from __future__ import print_function, unicode_literals
 from __future__ import print_function, unicode_literals
 from distutils.util import get_platform
 from distutils.util import get_platform
@@ -20,7 +15,7 @@ import tempfile
 import subprocess
 import subprocess
 from distutils.sysconfig import get_config_var
 from distutils.sysconfig import get_config_var
 from optparse import OptionParser
 from optparse import OptionParser
-from makepandacore import ColorText, LocateBinary, ParsePandaVersion, GetExtensionSuffix, SetVerbose, GetVerbose, GetMetadataValue
+from makepandacore import ColorText, LocateBinary, GetExtensionSuffix, SetVerbose, GetVerbose, GetMetadataValue
 from base64 import urlsafe_b64encode
 from base64 import urlsafe_b64encode
 
 
 
 
@@ -34,6 +29,9 @@ def get_abi_tag():
 
 
     soabi = 'cp%d%d' % (sys.version_info[:2])
     soabi = 'cp%d%d' % (sys.version_info[:2])
 
 
+    if sys.version_info >= (3, 8):
+        return soabi
+
     debug_flag = get_config_var('Py_DEBUG')
     debug_flag = get_config_var('Py_DEBUG')
     if (debug_flag is None and hasattr(sys, 'gettotalrefcount')) or debug_flag:
     if (debug_flag is None and hasattr(sys, 'gettotalrefcount')) or debug_flag:
         soabi += 'd'
         soabi += 'd'
@@ -118,6 +116,8 @@ Root-Is-Purelib: false
 Tag: {0}-{1}-{2}
 Tag: {0}-{1}-{2}
 """
 """
 
 
+PROJECT_URLS = dict([line.split('=', 1) for line in GetMetadataValue('project_urls').strip().splitlines()])
+
 METADATA = {
 METADATA = {
     "license": GetMetadataValue('license'),
     "license": GetMetadataValue('license'),
     "name": GetMetadataValue('name'),
     "name": GetMetadataValue('name'),
@@ -126,9 +126,7 @@ METADATA = {
     "summary": GetMetadataValue('description'),
     "summary": GetMetadataValue('description'),
     "extensions": {
     "extensions": {
         "python.details": {
         "python.details": {
-            "project_urls": {
-                "Home": GetMetadataValue('url'),
-            },
+            "project_urls": dict(PROJECT_URLS, Home=GetMetadataValue('url')),
             "document_names": {
             "document_names": {
                 "license": "LICENSE.txt"
                 "license": "LICENSE.txt"
             },
             },
@@ -173,15 +171,18 @@ questions.
 PANDA3D_TOOLS_INIT = """import os, sys
 PANDA3D_TOOLS_INIT = """import os, sys
 import panda3d
 import panda3d
 
 
+dir = os.path.dirname(panda3d.__file__)
+del panda3d
+
 if sys.platform in ('win32', 'cygwin'):
 if sys.platform in ('win32', 'cygwin'):
     path_var = 'PATH'
     path_var = 'PATH'
+    if hasattr(os, 'add_dll_directory'):
+        os.add_dll_directory(dir)
 elif sys.platform == 'darwin':
 elif sys.platform == 'darwin':
     path_var = 'DYLD_LIBRARY_PATH'
     path_var = 'DYLD_LIBRARY_PATH'
 else:
 else:
     path_var = 'LD_LIBRARY_PATH'
     path_var = 'LD_LIBRARY_PATH'
 
 
-dir = os.path.dirname(panda3d.__file__)
-del panda3d
 if not os.environ.get(path_var):
 if not os.environ.get(path_var):
     os.environ[path_var] = dir
     os.environ[path_var] = dir
 else:
 else:
@@ -565,6 +566,7 @@ def makewheel(version, output_dir, platform=None):
         "Summary: {summary}\n" \
         "Summary: {summary}\n" \
         "License: {license}\n".format(**METADATA),
         "License: {license}\n".format(**METADATA),
         "Home-page: {0}\n".format(homepage),
         "Home-page: {0}\n".format(homepage),
+    ] + ["Project-URL: {0}, {1}\n".format(*url) for url in PROJECT_URLS.items()] + [
         "Author: {0}\n".format(author),
         "Author: {0}\n".format(author),
         "Author-email: {0}\n".format(email),
         "Author-email: {0}\n".format(email),
         "Platform: {0}\n".format(platform),
         "Platform: {0}\n".format(platform),
@@ -728,7 +730,7 @@ if __debug__:
 
 
 
 
 if __name__ == "__main__":
 if __name__ == "__main__":
-    version = ParsePandaVersion("dtool/PandaVersion.pp")
+    version = GetMetadataValue('version')
 
 
     parser = OptionParser()
     parser = OptionParser()
     parser.add_option('', '--version', dest = 'version', help = 'Panda3D version number (default: %s)' % (version), default = version)
     parser.add_option('', '--version', dest = 'version', help = 'Panda3D version number (default: %s)' % (version), default = version)

+ 8 - 3
makepanda/test_wheel.py

@@ -19,9 +19,6 @@ def test_wheel(wheel, verbose=False):
     print("Setting up virtual environment in {0}".format(envdir))
     print("Setting up virtual environment in {0}".format(envdir))
     sys.stdout.flush()
     sys.stdout.flush()
 
 
-    # Make sure pip is up-to-date first.
-    subprocess.call([sys.executable, "-B", "-m", "pip", "install", "-U", "pip"])
-
     # Create a virtualenv.
     # Create a virtualenv.
     if sys.version_info >= (3, 0):
     if sys.version_info >= (3, 0):
         subprocess.call([sys.executable, "-B", "-m", "venv", "--clear", envdir])
         subprocess.call([sys.executable, "-B", "-m", "venv", "--clear", envdir])
@@ -39,6 +36,14 @@ def test_wheel(wheel, verbose=False):
         shutil.rmtree(envdir)
         shutil.rmtree(envdir)
         sys.exit(1)
         sys.exit(1)
 
 
+    # Temp hack to patch issue pypa/pip#6885 in pip 19.2.2 and Python 3.8.
+    if sys.platform == "win32" and "-cp38-cp38-" in wheel and os.path.isdir(os.path.join(envdir, "Lib", "site-packages", "pip-19.2.2.dist-info")):
+        pep425tags = os.path.join(envdir, "Lib", "site-packages", "pip", "_internal", "pep425tags.py")
+        if os.path.isfile(pep425tags):
+            data = open(pep425tags, "r").read()
+            data = data.replace(" m = 'm'\n", " m = ''\n")
+            open(pep425tags, "w").write(data)
+
     # Install pytest into the environment, as well as our wheel.
     # Install pytest into the environment, as well as our wheel.
     if subprocess.call([python, "-m", "pip", "install", "pytest", wheel]) != 0:
     if subprocess.call([python, "-m", "pip", "install", "pytest", wheel]) != 0:
         shutil.rmtree(envdir)
         shutil.rmtree(envdir)

+ 0 - 8
panda/src/audio/audioManager.cxx

@@ -325,11 +325,3 @@ void AudioManager::
 write(std::ostream &out) const {
 write(std::ostream &out) const {
   out << (*this) << "\n";
   out << (*this) << "\n";
 }
 }
-
-/**
- * For use only with Miles.
- */
-void AudioManager::
-set_speaker_configuration(LVecBase3 *speaker1, LVecBase3 *speaker2, LVecBase3 *speaker3, LVecBase3 *speaker4, LVecBase3 *speaker5, LVecBase3 *speaker6, LVecBase3 *speaker7, LVecBase3 *speaker8, LVecBase3 *speaker9) {
-  // intentionally blank
-}

+ 0 - 3
panda/src/audio/audioManager.h

@@ -178,9 +178,6 @@ PUBLISHED:
   virtual void output(std::ostream &out) const;
   virtual void output(std::ostream &out) const;
   virtual void write(std::ostream &out) const;
   virtual void write(std::ostream &out) const;
 
 
-  // set_speaker_configuration is a Miles only method.
-  virtual void set_speaker_configuration(LVecBase3 *speaker1, LVecBase3 *speaker2=nullptr, LVecBase3 *speaker3=nullptr, LVecBase3 *speaker4=nullptr, LVecBase3 *speaker5=nullptr, LVecBase3 *speaker6=nullptr, LVecBase3 *speaker7=nullptr, LVecBase3 *speaker8=nullptr, LVecBase3 *speaker9=nullptr);
-
 public:
 public:
   static void register_AudioManager_creator(Create_AudioManager_proc* proc);
   static void register_AudioManager_creator(Create_AudioManager_proc* proc);
 
 

+ 0 - 17
panda/src/audio/audioSound.cxx

@@ -83,23 +83,6 @@ set_speaker_mix(PN_stdfloat frontleft, PN_stdfloat frontright, PN_stdfloat cente
     // intentionally blank
     // intentionally blank
 }
 }
 
 
-/**
- * For use only with Miles.
- */
-PN_stdfloat AudioSound::
-get_speaker_level(int index) {
-  // intentionally blank
-  return 0.0;
-}
-
-/**
- * For use only with Miles.
- */
-void AudioSound::
-set_speaker_levels(PN_stdfloat level1, PN_stdfloat level2, PN_stdfloat level3, PN_stdfloat level4, PN_stdfloat level5, PN_stdfloat level6, PN_stdfloat level7, PN_stdfloat level8, PN_stdfloat level9) {
-  // intentionally blank
-}
-
 /**
 /**
  * Configure the local DSP filter chain.
  * Configure the local DSP filter chain.
  *
  *

+ 1 - 8
panda/src/audio/audioSound.h

@@ -104,17 +104,10 @@ PUBLISHED:
   virtual void set_3d_max_distance(PN_stdfloat dist);
   virtual void set_3d_max_distance(PN_stdfloat dist);
   virtual PN_stdfloat get_3d_max_distance() const;
   virtual PN_stdfloat get_3d_max_distance() const;
 
 
-  // *_speaker_mix and *_speaker_level(s) serve the same purpose.
-  // *_speaker_mix is for use with FMOD. *_speaker_level(s) is for use with
-  // Miles.  Both interfaces exist because of a significant difference in the
-  // two APIs.  Hopefully the difference can be reconciled into a single
-  // interface at some point.
+  // *_speaker_mix is for use with FMOD.
   virtual PN_stdfloat get_speaker_mix(int speaker);
   virtual PN_stdfloat get_speaker_mix(int speaker);
   virtual void set_speaker_mix(PN_stdfloat frontleft, PN_stdfloat frontright, PN_stdfloat center, PN_stdfloat sub, PN_stdfloat backleft, PN_stdfloat backright, PN_stdfloat sideleft, PN_stdfloat  sideright);
   virtual void set_speaker_mix(PN_stdfloat frontleft, PN_stdfloat frontright, PN_stdfloat center, PN_stdfloat sub, PN_stdfloat backleft, PN_stdfloat backright, PN_stdfloat sideleft, PN_stdfloat  sideright);
 
 
-  virtual PN_stdfloat get_speaker_level(int index);
-  virtual void set_speaker_levels(PN_stdfloat level1, PN_stdfloat level2=-1.0f, PN_stdfloat level3=-1.0f, PN_stdfloat level4=-1.0f, PN_stdfloat level5=-1.0f, PN_stdfloat level6=-1.0f, PN_stdfloat level7=-1.0f, PN_stdfloat level8=-1.0f, PN_stdfloat level9=-1.0f);
-
   virtual int get_priority();
   virtual int get_priority();
   virtual void set_priority(int priority);
   virtual void set_priority(int priority);
 
 

+ 0 - 23
panda/src/audio/config_audio.cxx

@@ -100,11 +100,6 @@ ConfigVariableEnum<FmodSpeakerMode> fmod_speaker_mode
           "Options: raw, mono, stereo, quad, surround, 5.1 and 7.1. "));
           "Options: raw, mono, stereo, quad, surround, 5.1 and 7.1. "));
 
 
 
 
-// Config variables for Miles:
-
-ConfigVariableBool audio_software_midi
-("audio-software-midi", true);
-
 ConfigVariableFilename audio_dls_file
 ConfigVariableFilename audio_dls_file
 ("audio-dls-file", Filename(),
 ("audio-dls-file", Filename(),
  PRC_DESC("Specifies a DLS file that defines an instrument set to load "
  PRC_DESC("Specifies a DLS file that defines an instrument set to load "
@@ -113,24 +108,6 @@ ConfigVariableFilename audio_dls_file
           "one is available; the likely success of this depends on the "
           "one is available; the likely success of this depends on the "
           "operating system."));
           "operating system."));
 
 
-ConfigVariableBool audio_play_midi
-("audio-play-midi", true);
-
-ConfigVariableBool audio_play_wave
-("audio-play-wave", true);
-
-ConfigVariableBool audio_play_mp3
-("audio-play-mp3", true);
-
-ConfigVariableInt audio_output_rate
-("audio-output-rate", 22050);
-
-ConfigVariableInt audio_output_bits
-("audio-output-bits", 16);
-
-ConfigVariableInt audio_output_channels
-("audio-output-channels", 2);
-
 ConfigureFn(config_audio) {
 ConfigureFn(config_audio) {
   FilterProperties::init_type();
   FilterProperties::init_type();
   AudioLoadRequest::init_type();
   AudioLoadRequest::init_type();

+ 1 - 13
panda/src/audio/config_audio.h

@@ -58,6 +58,7 @@ EXPCL_PANDA_AUDIO std::istream &operator >> (std::istream &in, FmodSpeakerMode &
 extern EXPCL_PANDA_AUDIO ConfigVariableInt fmod_number_of_sound_channels;
 extern EXPCL_PANDA_AUDIO ConfigVariableInt fmod_number_of_sound_channels;
 extern EXPCL_PANDA_AUDIO ConfigVariableBool fmod_use_surround_sound;
 extern EXPCL_PANDA_AUDIO ConfigVariableBool fmod_use_surround_sound;
 extern EXPCL_PANDA_AUDIO ConfigVariableEnum<FmodSpeakerMode> fmod_speaker_mode;
 extern EXPCL_PANDA_AUDIO ConfigVariableEnum<FmodSpeakerMode> fmod_speaker_mode;
+extern EXPCL_PANDA_AUDIO ConfigVariableFilename audio_dls_file;
 
 
 // Config vars for OpenAL:
 // Config vars for OpenAL:
 
 
@@ -67,19 +68,6 @@ extern EXPCL_PANDA_AUDIO ConfigVariableDouble audio_drop_off_factor;
 extern EXPCL_PANDA_AUDIO ConfigVariableDouble audio_buffering_seconds;
 extern EXPCL_PANDA_AUDIO ConfigVariableDouble audio_buffering_seconds;
 extern EXPCL_PANDA_AUDIO ConfigVariableInt    audio_preload_threshold;
 extern EXPCL_PANDA_AUDIO ConfigVariableInt    audio_preload_threshold;
 
 
-// Config vars for Miles:
-
-extern EXPCL_PANDA_AUDIO ConfigVariableBool audio_software_midi;
-extern EXPCL_PANDA_AUDIO ConfigVariableFilename audio_dls_file;
-extern EXPCL_PANDA_AUDIO ConfigVariableBool audio_play_midi;
-extern EXPCL_PANDA_AUDIO ConfigVariableBool audio_play_wave;
-extern EXPCL_PANDA_AUDIO ConfigVariableBool audio_play_mp3;
-extern EXPCL_PANDA_AUDIO ConfigVariableInt audio_output_rate;
-extern EXPCL_PANDA_AUDIO ConfigVariableInt audio_output_bits;
-extern EXPCL_PANDA_AUDIO ConfigVariableInt audio_output_channels;
-
-
-
 #ifdef NOTIFY_DEBUG //[
 #ifdef NOTIFY_DEBUG //[
   // Non-release build:
   // Non-release build:
   #define audio_debug(msg) \
   #define audio_debug(msg) \

+ 1 - 1
panda/src/audio/nullAudioManager.h

@@ -21,7 +21,7 @@
 class EXPCL_PANDA_AUDIO NullAudioManager : public AudioManager {
 class EXPCL_PANDA_AUDIO NullAudioManager : public AudioManager {
   // All of these methods are stubbed out to some degree.  If you're looking
   // All of these methods are stubbed out to some degree.  If you're looking
   // for a starting place for a new AudioManager, please consider looking at
   // for a starting place for a new AudioManager, please consider looking at
-  // the milesAudioManager.
+  // the openalAudioManager.
 
 
 public:
 public:
   NullAudioManager();
   NullAudioManager();

+ 1 - 1
panda/src/audio/nullAudioSound.h

@@ -23,7 +23,7 @@
 class EXPCL_PANDA_AUDIO NullAudioSound : public AudioSound {
 class EXPCL_PANDA_AUDIO NullAudioSound : public AudioSound {
   // All of these methods are stubbed out to some degree.  If you're looking
   // All of these methods are stubbed out to some degree.  If you're looking
   // for a starting place for a new AudioManager, please consider looking at
   // for a starting place for a new AudioManager, please consider looking at
-  // the milesAudioManager.
+  // the openalAudioManager.
 
 
 public:
 public:
   ~NullAudioSound();
   ~NullAudioSound();

+ 0 - 102
panda/src/audiotraits/config_milesAudio.cxx

@@ -1,102 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file config_milesAudio.cxx
- * @author skyler
- */
-
-#include "pandabase.h"
-#ifdef HAVE_RAD_MSS //[
-
-#include "config_milesAudio.h"
-#include "milesAudioManager.h"
-#include "milesAudioSound.h"
-#include "milesAudioSample.h"
-#include "milesAudioSequence.h"
-#include "milesAudioStream.h"
-#include "pandaSystem.h"
-#include "dconfig.h"
-
-#if !defined(CPPPARSER) && !defined(LINK_ALL_STATIC) && !defined(BUILDING_MILES_AUDIO)
-  #error Buildsystem error: BUILDING_MILES_AUDIO not defined
-#endif
-
-ConfigureDef(config_milesAudio);
-NotifyCategoryDef(milesAudio, ":audio");
-
-ConfigureFn(config_milesAudio) {
-  init_libMilesAudio();
-}
-
-ConfigVariableBool miles_audio_force_midi_reset
-("audio-force-midi-reset", true);
-
-ConfigVariableInt miles_audio_expand_mp3_threshold
-("miles-audio-expand-mp3-threshold", 16384,
- PRC_DESC("This enables a Miles workaround in which small MP3 files are "
-          "expanded in-memory at load time into WAV format, which can "
-          "work around problems with Miles being unable to correctly "
-          "report the length of, or seek within, a variable bit-rate encoded "
-          "MP3 file.  Any MP3 file whose length in bytes is less than "
-          "this value will be expanded.  This only applies to files "
-          "within the miles-audio-preload-threshold."));
-
-ConfigVariableInt miles_audio_preload_threshold
-("miles-audio-preload-threshold", -1,
- PRC_DESC("This should be no smaller "
-          "than miles-audio-expand-mp3-threshold.  Files that are smaller "
-          "than this number of bytes will be preloaded and kept "
-          "resident in memory, while files that are this size or larger "
-          "will be streamed from disk.  Set this to -1 to preload "
-          "every file."));
-
-ConfigVariableBool miles_audio_panda_threads
-("miles-audio-panda-threads", true,
- PRC_DESC("Set this true to service Miles background audio via Panda's "
-          "threading interface, instead of Miles' built-in threading "
-          "interface.  This gives Panda more control over the threading, "
-          "and ensures better lock protection within Panda.  This has "
-          "no meaning unless Panda is compiled with thread support."));
-
-/**
- * Initializes the library.  This must be called at least once before any of
- * the functions or classes in this library can be used.  Normally it will be
- * called by the static initializers and need not be called explicitly, but
- * special cases exist.
- */
-void
-init_libMilesAudio() {
-  static bool initialized = false;
-  if (initialized) {
-    return;
-  }
-  initialized = true;
-  MilesAudioManager::init_type();
-  MilesAudioSound::init_type();
-  MilesAudioSample::init_type();
-  MilesAudioSequence::init_type();
-  MilesAudioStream::init_type();
-
-  PandaSystem *ps = PandaSystem::get_global_ptr();
-  ps->add_system("Miles");
-  ps->add_system("audio");
-  ps->set_system_tag("audio", "implementation", "Miles");
-}
-
-/**
- * This function is called when the dynamic library is loaded; it should
- * return the Create_AudioManager function appropriate to create a
- * MilesAudioManager.
- */
-Create_AudioManager_proc *
-get_audio_manager_func_miles_audio() {
-  init_libMilesAudio();
-  return &Create_MilesAudioManager;
-}
-
-#endif //]

+ 0 - 37
panda/src/audiotraits/config_milesAudio.h

@@ -1,37 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file config_milesAudio.h
- * @author skyler
- */
-
-#ifndef CONFIG_MILESAUDIO_H
-#define CONFIG_MILESAUDIO_H
-
-#include "pandabase.h"
-
-#ifdef HAVE_RAD_MSS //[
-#include "notifyCategoryProxy.h"
-#include "configVariableBool.h"
-#include "configVariableInt.h"
-#include "dconfig.h"
-#include "audioManager.h"
-
-ConfigureDecl(config_milesAudio, EXPCL_MILES_AUDIO, EXPTP_MILES_AUDIO);
-NotifyCategoryDecl(milesAudio, EXPCL_MILES_AUDIO, EXPTP_MILES_AUDIO);
-
-extern ConfigVariableBool miles_audio_force_midi_reset;
-extern ConfigVariableInt miles_audio_expand_mp3_threshold;
-extern ConfigVariableInt miles_audio_preload_threshold;
-extern ConfigVariableBool miles_audio_panda_threads;
-
-extern EXPCL_MILES_AUDIO void init_libMilesAudio();
-extern "C" EXPCL_MILES_AUDIO Create_AudioManager_proc *get_audio_manager_func_miles_audio();
-#endif //]
-
-#endif // CONFIG_MILESAUDIO_H

+ 0 - 39
panda/src/audiotraits/globalMilesManager.I

@@ -1,39 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file globalMilesManager.I
- * @author drose
- * @date 2007-07-30
- */
-
-/**
- * Returns true if the Miles Sound System is open (and active), false if it is
- * not.
- */
-INLINE bool GlobalMilesManager::
-is_open() const {
-  return _is_open;
-}
-
-/**
- * Returns the number of sample handles that have been allocated.
- */
-INLINE int GlobalMilesManager::
-get_num_samples() const {
-  LightMutexHolder holder(_samples_lock);
-  return _samples.size();
-}
-
-/**
- * Returns the number of sequence handles that have been allocated.
- */
-INLINE int GlobalMilesManager::
-get_num_sequences() const {
-  LightMutexHolder holder(_sequences_lock);
-  return _sequences.size();
-}

+ 0 - 446
panda/src/audiotraits/globalMilesManager.cxx

@@ -1,446 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file globalMilesManager.cxx
- * @author drose
- * @date 2007-07-26
- */
-
-#include "globalMilesManager.h"
-
-#ifdef HAVE_RAD_MSS //[
-
-#include "lightMutexHolder.h"
-#include "milesAudioManager.h"
-#include "milesAudioSample.h"
-#include "milesAudioSequence.h"
-
-#ifdef WIN32
-// For midiOutReset()
-#include <windows.h>
-#include <mmsystem.h>
-#endif
-
-using std::istream;
-using std::string;
-
-GlobalMilesManager *GlobalMilesManager::_global_ptr;
-
-/**
- *
- */
-GlobalMilesManager::
-GlobalMilesManager() :
-  _managers_lock("GlobalMilesManager::_managers_lock"),
-  _samples_lock("GlobalMilesManager::_samples_lock"),
-  _sequences_lock("GlobalMilesManager::_sequences_lock")
-{
-  _digital_driver = 0;
-  _midi_driver = 0;
-  _dls_device = 0;
-  _dls_file = 0;
-  _is_open = false;
-}
-
-/**
- * Records a new MilesAudioManager in the world.  This will open the Miles API
- * when the first audio manager is added.
- */
-void GlobalMilesManager::
-add_manager(MilesAudioManager *manager) {
-  LightMutexHolder holder(_managers_lock);
-  _managers.insert(manager);
-  if (!_is_open) {
-    open_api();
-  }
-}
-
-/**
- * Records that a MilesAudioManager is destructing.  This will clsoe the Miles
- * API when the last audio manager is removed.
- */
-void GlobalMilesManager::
-remove_manager(MilesAudioManager *manager) {
-  LightMutexHolder holder(_managers_lock);
-  _managers.erase(manager);
-  if (_managers.empty() && _is_open) {
-    close_api();
-  }
-}
-
-/**
- * Calls cleanup() on all MilesAudioManagers, to cause a clean shutdown.
- */
-void GlobalMilesManager::
-cleanup() {
-  LightMutexHolder holder(_managers_lock);
-  Managers::iterator mi;
-  for (mi = _managers.begin(); mi != _managers.end(); ++mi) {
-    (*mi)->cleanup();
-  }
-}
-
-/**
- * Gets a sample handle from the global pool for the digital output device, to
- * be used with the indicated AudioSound.
- *
- * If successful, sets the sample handle and the index (which should later be
- * used to release the sample) and returns true.  If unsuccessful (because
- * there are no more available handles), returns false.
- *
- * This is a very limited resource; you should only get a sample just before
- * playing a sound.
- */
-bool GlobalMilesManager::
-get_sample(HSAMPLE &sample, size_t &index, MilesAudioSample *sound) {
-  LightMutexHolder holder(_samples_lock);
-
-  for (size_t i = 0; i < _samples.size(); ++i) {
-    SampleData &smp = _samples[i];
-    if (AIL_sample_status(smp._sample) == SMP_DONE) {
-      if (smp._sound != nullptr) {
-        // Tell the last sound that was using this sample that it's done now.
-        smp._sound->internal_stop();
-      }
-      smp._sound = sound;
-      sample = smp._sample;
-      index = i;
-      return true;
-    }
-  }
-
-  // No more already-allocated samples; get a new one from the system.
-  sample = AIL_allocate_sample_handle(_digital_driver);
-  if (sample == 0) {
-    return false;
-  }
-
-  AIL_init_sample(sample, DIG_F_STEREO_16, 0);
-  index = _samples.size();
-
-  SampleData smp;
-  smp._sound = sound;
-  smp._sample = sample;
-  _samples.push_back(smp);
-  return true;
-}
-
-/**
- * Indicates that the indicated AudioSound no longer needs this sample.
- */
-void GlobalMilesManager::
-release_sample(size_t index, MilesAudioSample *sound) {
-  LightMutexHolder holder(_samples_lock);
-  nassertv(index < _samples.size());
-
-  SampleData &smp = _samples[index];
-  if (smp._sound == sound) {
-    smp._sound = nullptr;
-  }
-}
-
-/**
- * Gets a sequence handle from the global pool for the digital output device,
- * to be used with the indicated AudioSound.
- *
- * If successful, sets the sequence handle and the index (which should later
- * be used to release the sequence) and returns true.  If unsuccessful
- * (because there are no more available handles), returns false.
- *
- * This is a very limited resource; you should only get a sequence just before
- * playing a sound.
- */
-bool GlobalMilesManager::
-get_sequence(HSEQUENCE &sequence, size_t &index, MilesAudioSequence *sound) {
-  LightMutexHolder holder(_sequences_lock);
-
-  for (size_t i = 0; i < _sequences.size(); ++i) {
-    SequenceData &seq = _sequences[i];
-    if (AIL_sequence_status(seq._sequence) == SEQ_DONE) {
-      if (seq._sound != nullptr) {
-        // Tell the last sound that was using this sequence that it's done
-        // now.
-        seq._sound->internal_stop();
-      }
-      seq._sound = sound;
-      sequence = seq._sequence;
-      index = i;
-      return true;
-    }
-  }
-
-  // No more already-allocated sequences; get a new one from the system.
-  sequence = AIL_allocate_sequence_handle(_midi_driver);
-  if (sequence == 0) {
-    return false;
-  }
-
-  index = _sequences.size();
-
-  SequenceData seq;
-  seq._sound = sound;
-  seq._sequence = sequence;
-  _sequences.push_back(seq);
-  return true;
-}
-
-/**
- * Indicates that the indicated AudioSound no longer needs this sequence.
- */
-void GlobalMilesManager::
-release_sequence(size_t index, MilesAudioSequence *sound) {
-  LightMutexHolder holder(_sequences_lock);
-  nassertv(index < _sequences.size());
-
-  SequenceData &seq = _sequences[index];
-  if (seq._sound == sound) {
-    seq._sound = nullptr;
-  }
-}
-
-/**
- * Sometimes Miles seems to leave midi notes hanging, even after stop is
- * called, so call this method to perform an explicit reset using winMM.dll
- * calls, just to ensure silence.
- */
-void GlobalMilesManager::
-force_midi_reset() {
-  if (!miles_audio_force_midi_reset) {
-    audio_debug("MilesAudioManager::skipping force_midi_reset");
-    return;
-  }
-  audio_debug("MilesAudioManager::force_midi_reset");
-
-#ifdef WIN32
-  if ((_midi_driver!=nullptr) && (_midi_driver->deviceid != MIDI_nullptr_DRIVER) && (_midi_driver->hMidiOut != nullptr)) {
-    audio_debug("MilesAudioManager::calling midiOutReset");
-    midiOutReset(_midi_driver->hMidiOut);
-  }
-#endif
-}
-
-/**
- * Returns the pointer to the one GlobalMilesManager object.
- */
-GlobalMilesManager *GlobalMilesManager::
-get_global_ptr() {
-  if (_global_ptr == nullptr) {
-    _global_ptr = new GlobalMilesManager;
-  }
-  return _global_ptr;
-}
-
-/**
- * Called internally to initialize the Miles API.
- */
-void GlobalMilesManager::
-open_api() {
-  audio_debug("GlobalMilesManager::open_api()")
-  nassertv(!_is_open);
-
-  bool use_digital = (audio_play_wave || audio_play_mp3);
-  if (audio_play_midi && audio_software_midi) {
-    use_digital = true;
-  }
-
-#ifdef IS_OSX
-  audio_software_midi = true;
-#endif
-
-  audio_debug("  use_digital="<<use_digital);
-  audio_debug("  audio_play_midi="<<audio_play_midi);
-  audio_debug("  audio_software_midi="<<audio_software_midi);
-  audio_debug("  audio_output_rate="<<audio_output_rate);
-  audio_debug("  audio_output_bits="<<audio_output_bits);
-  audio_debug("  audio_output_channels="<<audio_output_channels);
-  audio_debug("  audio_software_midi="<<audio_software_midi);
-
-#if !defined(NDEBUG) && defined(AIL_MSS_version) //[
-  char version[8];
-  AIL_MSS_version(version, 8);
-  audio_debug("  Mss32.dll Version: "<<version);
-#endif //]
-
-  if (!AIL_startup()) {
-    milesAudio_cat.warning()
-      << "Miles Sound System already initialized!\n";
-  }
-
-  AIL_set_file_callbacks(open_callback, close_callback,
-                         seek_callback, read_callback);
-
-  if (use_digital) {
-    _digital_driver =
-      AIL_open_digital_driver(audio_output_rate, audio_output_bits,
-                              audio_output_channels, 0);
-  }
-
-  if (audio_play_midi) {
-    if (audio_software_midi) {
-      _midi_driver = AIL_open_XMIDI_driver(AIL_OPEN_XMIDI_NULL_DRIVER);
-
-      // Load the downloadable sounds file:
-      _dls_device = AIL_DLS_open(_midi_driver, _digital_driver, nullptr, 0,
-                                 audio_output_rate, audio_output_bits,
-                                 audio_output_channels);
-
-      Filename dls_pathname = AudioManager::get_dls_pathname();
-
-      VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
-      vfs->resolve_filename(dls_pathname, get_model_path());
-
-      _dls_data.clear();
-      PT(VirtualFile) file = vfs->get_file(dls_pathname);
-      if (file == nullptr) {
-        milesAudio_cat.warning()
-          << "DLS file does not exist: " << dls_pathname << "\n";
-
-      } else if (!file->read_file(_dls_data, true)) {
-        milesAudio_cat.warning()
-          << "Could not read DLS file: " << dls_pathname << "\n";
-
-      } else if (_dls_data.empty()) {
-        milesAudio_cat.warning()
-          << "DLS file is empty: " << dls_pathname << "\n";
-
-      } else {
-        _dls_file = AIL_DLS_load_memory(_dls_device, &_dls_data[0], 0);
-      }
-
-      if (_dls_file == 0) {
-        audio_error("  Could not get DLS file, switching to hardware MIDI.");
-        AIL_DLS_close(_dls_device, 0);
-        _dls_device = 0;
-        AIL_close_XMIDI_driver(_midi_driver);
-        _midi_driver = AIL_open_XMIDI_driver(0);
-
-      } else {
-        audio_info("  using Miles software midi");
-      }
-    } else {
-      _midi_driver = AIL_open_XMIDI_driver(0);
-      audio_info("  using Miles hardware midi");
-    }
-  }
-
-  _is_open = true;
-}
-
-/**
- * Called internally to shut down the Miles API.
- */
-void GlobalMilesManager::
-close_api() {
-  audio_debug("GlobalMilesManager::close_api()")
-  nassertv(_is_open);
-
-  Samples::iterator si;
-  for (si = _samples.begin(); si != _samples.end(); ++si) {
-    SampleData &smp = (*si);
-    AIL_release_sample_handle(smp._sample);
-  }
-  _samples.clear();
-
-  Sequences::iterator qi;
-  for (qi = _sequences.begin(); qi != _sequences.end(); ++qi) {
-    SequenceData &smp = (*qi);
-    AIL_release_sequence_handle(smp._sequence);
-  }
-  _sequences.clear();
-
-  if (_dls_file != 0) {
-    AIL_DLS_unload(_dls_device, _dls_file);
-    _dls_file = 0;
-  }
-
-  if (_dls_device != 0) {
-    AIL_DLS_close(_dls_device, 0);
-    _dls_device = 0;
-  }
-
-  if (_midi_driver != 0) {
-    AIL_close_XMIDI_driver(_midi_driver);
-    _midi_driver = 0;
-  }
-
-  if (_digital_driver != 0) {
-    AIL_close_digital_driver(_digital_driver);
-    _digital_driver = 0;
-  }
-
-  AIL_shutdown();
-
-  _is_open = false;
-}
-
-/**
- * This callback function is given to Miles to handle file I/O via the Panda
- * VFS.  It's only used to implemented streaming audio files, since in all
- * other cases we open files directly.
- */
-U32 AILCALLBACK GlobalMilesManager::
-open_callback(char const *filename, UINTa *file_handle) {
-  VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
-  istream *strm = vfs->open_read_file(Filename::binary_filename(string(filename)), true);
-  if (strm == nullptr) {
-    // Failure.
-    return 0;
-  }
-  // Success.
-  (*file_handle) = (UINTa)strm;
-  return 1;
-}
-
-/**
- * This callback function is given to Miles to handle file I/O via the Panda
- * VFS.
- */
-void AILCALLBACK GlobalMilesManager::
-close_callback(UINTa file_handle) {
-  istream *strm = (istream *)file_handle;
-  VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
-  vfs->close_read_file(strm);
-}
-
-/**
- * This callback function is given to Miles to handle file I/O via the Panda
- * VFS.
- */
-S32 AILCALLBACK GlobalMilesManager::
-seek_callback(UINTa file_handle, S32 offset, U32 type) {
-  istream *strm = (istream *)file_handle;
-  strm->clear();
-  switch (type) {
-  case AIL_FILE_SEEK_BEGIN:
-    strm->seekg(offset, std::ios::beg);
-    break;
-
-  case AIL_FILE_SEEK_CURRENT:
-    strm->seekg(offset, std::ios::cur);
-    break;
-
-  case AIL_FILE_SEEK_END:
-    strm->seekg(offset, std::ios::end);
-    break;
-  }
-
-  return strm->tellg();
-}
-
-/**
- * This callback function is given to Miles to handle file I/O via the Panda
- * VFS.
- */
-U32 AILCALLBACK GlobalMilesManager::
-read_callback(UINTa file_handle, void *buffer, U32 bytes) {
-  istream *strm = (istream *)file_handle;
-  strm->read((char *)buffer, bytes);
-  return strm->gcount();
-}
-
-#endif //]

+ 0 - 117
panda/src/audiotraits/globalMilesManager.h

@@ -1,117 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file globalMilesManager.h
- * @author drose
- * @date 2007-07-26
- */
-
-#ifndef GLOBALMILESMANAGER_H
-#define GLOBALMILESMANAGER_H
-
-#include "pandabase.h"
-#ifdef HAVE_RAD_MSS //[
-
-#include "pset.h"
-#include "lightMutex.h"
-#include "lightMutexHolder.h"
-
-#include <mss.h>
-
-#ifndef UINTa
-#define UINTa U32
-#endif
-
-#ifndef SINTa
-#define SINTa S32
-#endif
-
-class MilesAudioSample;
-class MilesAudioSequence;
-
-/**
- * This is a wrapper around the parts of the Miles API that should only be
- * created once.  This represents the global data common to all
- * MilesAudioManagers.
- */
-class EXPCL_MILES_AUDIO GlobalMilesManager {
-private:
-  GlobalMilesManager();
-
-public:
-  void add_manager(MilesAudioManager *manager);
-  void remove_manager(MilesAudioManager *manager);
-  void cleanup();
-  INLINE bool is_open() const;
-
-  bool get_sample(HSAMPLE &sample, size_t &index, MilesAudioSample *sound);
-  void release_sample(size_t index, MilesAudioSample *sound);
-  INLINE int get_num_samples() const;
-
-  bool get_sequence(HSEQUENCE &sequence, size_t &index, MilesAudioSequence *sound);
-  void release_sequence(size_t index, MilesAudioSequence *sound);
-  INLINE int get_num_sequences() const;
-
-  void force_midi_reset();
-
-  static GlobalMilesManager *get_global_ptr();
-
-public:
-  HDIGDRIVER _digital_driver;
-  HMDIDRIVER _midi_driver;
-
-  // For software MIDI:
-  HDLSDEVICE _dls_device;
-  HDLSFILEID _dls_file;
-  vector_uchar _dls_data;
-
-private:
-  void open_api();
-  void close_api();
-
-  static U32 AILCALLBACK open_callback(char const *filename, UINTa *file_handle);
-  static void AILCALLBACK close_callback(UINTa file_handle);
-  static S32 AILCALLBACK seek_callback(UINTa file_handle, S32 offset, U32 type);
-  static U32 AILCALLBACK read_callback(UINTa file_handle, void *buffer, U32 bytes);
-
-
-private:
-  bool _is_open;
-
-  typedef pset<MilesAudioManager *> Managers;
-  Managers _managers;
-  LightMutex _managers_lock;
-
-  class SampleData {
-  public:
-    HSAMPLE _sample;
-    MilesAudioSample *_sound;
-  };
-
-  typedef pvector<SampleData> Samples;
-  Samples _samples;
-  LightMutex _samples_lock;
-
-  class SequenceData {
-  public:
-    HSEQUENCE _sequence;
-    MilesAudioSequence *_sound;
-  };
-
-  typedef pvector<SequenceData> Sequences;
-  Sequences _sequences;
-  LightMutex _sequences_lock;
-
-  static GlobalMilesManager *_global_ptr;
-};
-
-#include "globalMilesManager.I"
-
-#endif //]
-
-#endif

+ 0 - 1121
panda/src/audiotraits/milesAudioManager.cxx

@@ -1,1121 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file milesAudioManager.cxx
- * @author skyler
- * @date 2001-06-06
- * Prior system by: cary
- */
-
-#include "milesAudioManager.h"
-
-#ifdef HAVE_RAD_MSS //[
-
-#include "milesAudioSound.h"
-#include "milesAudioSample.h"
-#include "milesAudioStream.h"
-#include "globalMilesManager.h"
-#include "config_audio.h"
-#include "config_putil.h"
-#include "config_express.h"
-#include "virtualFileSystem.h"
-#include "nullAudioSound.h"
-#include "string_utils.h"
-#include "mutexHolder.h"
-#include "lightReMutexHolder.h"
-
-#include <algorithm>
-
-using std::string;
-
-
-TypeHandle MilesAudioManager::_type_handle;
-
-AudioManager *Create_MilesAudioManager() {
-  audio_debug("Create_MilesAudioManager()");
-  return new MilesAudioManager();
-}
-
-/**
- * Create an audio manager.  This may open the Miles sound system if there
- * were no other MilesAudioManager instances.  Subsequent managers may use the
- * same Miles resources.
- */
-MilesAudioManager::
-MilesAudioManager() :
-  _lock("MilesAudioManager::_lock"),
-  _streams_lock("MilesAudioManager::_streams_lock"),
-  _streams_cvar(_streams_lock)
-{
-  audio_debug("MilesAudioManager::MilesAudioManager(), this = "
-              << (void *)this);
-  GlobalMilesManager::get_global_ptr()->add_manager(this);
-  audio_debug("  audio_active="<<audio_active);
-  audio_debug("  audio_volume="<<audio_volume);
-  _cleanup_required = true;
-  _active = audio_active;
-  _volume = audio_volume;
-  _play_rate = 1.0f;
-  _cache_limit = audio_cache_limit;
-  _concurrent_sound_limit = 0;
-  _is_valid = true;
-  _hasMidiSounds = false;
-  _sounds_finished = false;
-
-  // We used to hang a call to a force-shutdown function on atexit(), so that
-  // any running sounds (particularly MIDI sounds) would be silenced on exit,
-  // especially a sudden exit triggered by a Python exception.  But that
-  // causes problems because Miles itself also hangs a force-shutdown function
-  // on atexit(), and you can't call AIL_cleanup() twice--that results in a
-  // crash.
-
-  // Nowadays, we provide the AudioManager::shutdown() method instead, which
-  // allows the app to force all sounds to stop cleanly before we get to the
-  // atexit() stack.  In Python, we guarantee that this method will be called
-  // in the sys.exitfunc chain.
-}
-
-/**
- * Clean up this audio manager and possibly release the Miles resources that
- * are reserved by the application (the later happens if this is the last
- * active manager).
- */
-MilesAudioManager::
-~MilesAudioManager() {
-  audio_debug("MilesAudioManager::~MilesAudioManager(), this = "
-              << (void *)this);
-  cleanup();
-  GlobalMilesManager::get_global_ptr()->remove_manager(this);
-
-  audio_debug("MilesAudioManager::~MilesAudioManager() finished");
-}
-
-/**
- * Call this at exit time to shut down the audio system.  This will invalidate
- * all currently-active AudioManagers and AudioSounds in the system.  If you
- * change your mind and want to play sounds again, you will have to recreate
- * all of these objects.
- */
-void MilesAudioManager::
-shutdown() {
-  audio_debug("shutdown() started");
-  GlobalMilesManager::get_global_ptr()->cleanup();
-  audio_debug("shutdown() finished");
-}
-
-/**
- * This is mostly for debugging, but it it could be used to detect errors in a
- * release build if you don't mind the cpu cost.
- */
-bool MilesAudioManager::
-is_valid() {
-  LightReMutexHolder holder(_lock);
-  return do_is_valid();
-}
-
-/**
- *
- */
-PT(AudioSound) MilesAudioManager::
-get_sound(const Filename &file_name, bool, int) {
-  LightReMutexHolder holder(_lock);
-  audio_debug("MilesAudioManager::get_sound(file_name=\""<<file_name<<"\")");
-
-  if (!do_is_valid()) {
-     audio_debug("invalid MilesAudioManager returning NullSound");
-     return get_null_sound();
-  }
-
-  Filename path = file_name;
-
-  VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
-  vfs->resolve_filename(path, get_model_path());
-  audio_debug("Reading "<<path);
-  audio_debug("  resolved file_name is '"<<path<<"'");
-
-  PT(SoundData) sd;
-  // Get the sound, either from the cache or from a loader:
-  SoundMap::const_iterator si=_sounds.find(path);
-  if (si != _sounds.end()) {
-    // ...found the sound in the cache.
-    sd = (*si).second;
-    audio_debug("  sound found in pool 0x" << (void*)sd);
-
-  } else {
-    // ...the sound was not found in the cachepool.
-    sd = load(path);
-    if (sd != nullptr) {
-      while (_sounds.size() >= (unsigned int)_cache_limit) {
-        uncache_a_sound();
-      }
-      // Put it in the pool: The following is roughly like: _sounds[path] =
-      // sd; But, it gives us an iterator into the map.
-      std::pair<SoundMap::const_iterator, bool> ib
-          = _sounds.insert(SoundMap::value_type(path, sd));
-      if (!ib.second) {
-        // The insert failed.
-        audio_debug("  failed map insert of "<<path);
-        nassertr(do_is_valid(), nullptr);
-        return get_null_sound();
-      }
-      // Set si, so that we can get a reference to the path for the
-      // MilesAudioSound.
-      si=ib.first;
-    }
-  }
-  // Create an AudioSound from the sound:
-  PT(AudioSound) audioSound;
-
-  if (sd != nullptr) {
-    most_recently_used((*si).first);
-    if (sd->_file_type == AILFILETYPE_MIDI ||
-        sd->_file_type == AILFILETYPE_XMIDI ||
-        sd->_file_type == AILFILETYPE_XMIDI_DLS ||
-        sd->_file_type == AILFILETYPE_XMIDI_MLS) {
-      // MIDI file.
-      audioSound = new MilesAudioSequence(this, sd, file_name);
-
-    } else if (!sd->_raw_data.empty()) {
-      // WAV or MP3 file preloaded into memory.
-      audioSound = new MilesAudioSample(this, sd, file_name);
-
-    } else {
-      // WAV or MP3 file streamed from disk.
-      audioSound = new MilesAudioStream(this, file_name, path);
-    }
-
-    audioSound->set_active(_active);
-
-    bool inserted = _sounds_on_loan.insert((MilesAudioSound *)audioSound.p()).second;
-    nassertr(inserted, audioSound);
-
-    _hasMidiSounds |= (file_name.find(".mid")!=string::npos);
-  } else {
-    // Couldn't load the file; just return a NullAudioSound.
-    audioSound = new NullAudioSound;
-  }
-
-  audio_debug("  returning 0x" << (void*)audioSound);
-  nassertr(do_is_valid(), nullptr);
-  return audioSound;
-}
-
-/**
- *
- */
-PT(AudioSound) MilesAudioManager::
-get_sound(MovieAudio *sound, bool, int) {
-  nassert_raise("Miles audio manager does not support MovieAudio sources.");
-  return nullptr;
-}
-
-/**
- *
- */
-void MilesAudioManager::
-uncache_sound(const Filename &file_name) {
-  audio_debug("MilesAudioManager::uncache_sound(file_name=\""
-      <<file_name<<"\")");
-  LightReMutexHolder holder(_lock);
-  nassertv(do_is_valid());
-  Filename path = file_name;
-
-  VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
-  vfs->resolve_filename(path, get_model_path());
-
-  audio_debug("  path=\""<<path<<"\"");
-  SoundMap::iterator i = _sounds.find(path);
-  if (i != _sounds.end()) {
-    nassertv(_lru.size() > 0);
-    LRU::iterator lru_i = find(_lru.begin(), _lru.end(), &(i->first));
-    nassertv(lru_i != _lru.end());
-    _lru.erase(lru_i);
-    _sounds.erase(i);
-  }
-  nassertv(do_is_valid());
-}
-
-/**
- * Clear out the sound cache.
- */
-void MilesAudioManager::
-clear_cache() {
-  audio_debug("MilesAudioManager::clear_cache()");
-  LightReMutexHolder holder(_lock);
-  do_clear_cache();
-}
-
-/**
- * Set the number of sounds that the cache can hold.
- */
-void MilesAudioManager::
-set_cache_limit(unsigned int count) {
-  LightReMutexHolder holder(_lock);
-
-  audio_debug("MilesAudioManager::set_cache_limit(count="<<count<<")");
-  nassertv(do_is_valid());
-  while (_lru.size() > count) {
-    uncache_a_sound();
-  }
-  _cache_limit=count;
-  nassertv(do_is_valid());
-}
-
-/**
- *
- */
-unsigned int MilesAudioManager::
-get_cache_limit() const {
-  return _cache_limit;
-}
-
-/**
- * set the overall volume
- */
-void MilesAudioManager::
-set_volume(PN_stdfloat volume) {
-  audio_debug("MilesAudioManager::set_volume(volume="<<volume<<")");
-  LightReMutexHolder holder(_lock);
-  if (_volume != volume) {
-    _volume = volume;
-    // Tell our AudioSounds to adjust:
-    AudioSet::iterator i = _sounds_on_loan.begin();
-    for (; i!=_sounds_on_loan.end(); ++i) {
-      (*i)->set_volume((*i)->get_volume());
-    }
-  }
-}
-
-/**
- * get the overall volume
- */
-PN_stdfloat MilesAudioManager::
-get_volume() const {
-  return _volume;
-}
-
-/**
- * set the overall play rate
- */
-void MilesAudioManager::
-set_play_rate(PN_stdfloat play_rate) {
-  audio_debug("MilesAudioManager::set_play_rate(play_rate="<<play_rate<<")");
-  LightReMutexHolder holder(_lock);
-  if (_play_rate != play_rate) {
-    _play_rate = play_rate;
-    // Tell our AudioSounds to adjust:
-    AudioSet::iterator i = _sounds_on_loan.begin();
-    for (; i != _sounds_on_loan.end(); ++i) {
-      (*i)->set_play_rate((*i)->get_play_rate());
-    }
-  }
-}
-
-/**
- * get the overall speed/pitch/play rate
- */
-PN_stdfloat MilesAudioManager::
-get_play_rate() const {
-  return _play_rate;
-}
-
-/**
- * turn on/off
- */
-void MilesAudioManager::
-set_active(bool active) {
-  audio_debug("MilesAudioManager::set_active(flag="<<active<<")");
-  LightReMutexHolder holder(_lock);
-  if (_active != active) {
-    _active=active;
-    // Tell our AudioSounds to adjust:
-    AudioSet::iterator i = _sounds_on_loan.begin();
-    for (; i != _sounds_on_loan.end(); ++i) {
-      (*i)->set_active(_active);
-    }
-
-    if ((!_active) && _hasMidiSounds) {
-      GlobalMilesManager::get_global_ptr()->force_midi_reset();
-    }
-  }
-}
-
-/**
- *
- */
-bool MilesAudioManager::
-get_active() const {
-  return _active;
-}
-
-/**
- *
- */
-void MilesAudioManager::
-set_concurrent_sound_limit(unsigned int limit) {
-  LightReMutexHolder holder(_lock);
-  _concurrent_sound_limit = limit;
-  do_reduce_sounds_playing_to(_concurrent_sound_limit);
-}
-
-/**
- *
- */
-unsigned int MilesAudioManager::
-get_concurrent_sound_limit() const {
-  return _concurrent_sound_limit;
-}
-
-/**
- *
- */
-void MilesAudioManager::
-reduce_sounds_playing_to(unsigned int count) {
-  LightReMutexHolder holder(_lock);
-  do_reduce_sounds_playing_to(count);
-}
-
-/**
- * Stop playback on all sounds managed by this manager.
- */
-void MilesAudioManager::
-stop_all_sounds() {
-  audio_debug("MilesAudioManager::stop_all_sounds()");
-  reduce_sounds_playing_to(0);
-}
-
-/**
- * Set spatial attributes of the listener for 3D sounds.  Note that Y and Z
- * are switched to translate into Miles's coordinate system.
- */
-void MilesAudioManager::audio_3d_set_listener_attributes(PN_stdfloat px, PN_stdfloat py, PN_stdfloat pz, PN_stdfloat vx, PN_stdfloat vy, PN_stdfloat vz, PN_stdfloat fx, PN_stdfloat fy, PN_stdfloat fz, PN_stdfloat ux, PN_stdfloat uy, PN_stdfloat uz) {
-  audio_debug("MilesAudioManager::audio_3d_set_listener_attributes()");
-
-  GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
-  AIL_set_listener_3D_position(mgr->_digital_driver, px, pz, py);
-  AIL_set_listener_3D_velocity_vector(mgr->_digital_driver, vx, vz, vy);
-  AIL_set_listener_3D_orientation(mgr->_digital_driver, fx, fz, fy, ux, uz, uy);
-}
-
-/**
- * Get spatial attributes of the listener for 3D sounds.  Note that Y and Z
- * are switched to translate from Miles's coordinate system.
- */
-void MilesAudioManager::audio_3d_get_listener_attributes(PN_stdfloat *px, PN_stdfloat *py, PN_stdfloat *pz, PN_stdfloat *vx, PN_stdfloat *vy, PN_stdfloat *vz, PN_stdfloat *fx, PN_stdfloat *fy, PN_stdfloat *fz, PN_stdfloat *ux, PN_stdfloat *uy, PN_stdfloat *uz) {
-  audio_debug("MilesAudioManager::audio_3d_get_listener_attributes()");
-
-  GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
-  float lpx, lpy, lpz, lvx, lvy, lvz, lfx, lfy, lfz, lux, luy, luz;
-  AIL_listener_3D_position(mgr->_digital_driver, &lpx, &lpz, &lpy);
-  AIL_listener_3D_velocity(mgr->_digital_driver, &lvx, &lvz, &lvy);
-  AIL_listener_3D_orientation(mgr->_digital_driver, &lfx, &lfz, &lfy, &lux, &luz, &luy);
-
-  *px = lpx;
-  *py = lpy;
-  *pz = lpz;
-  *vx = lvx;
-  *vy = lvy;
-  *vz = lvz;
-  *fx = lfx;
-  *fy = lfy;
-  *fz = lfz;
-  *ux = lux;
-  *uy = luy;
-  *uz = luz;
-}
-
-/**
- * Set factor to allow user to easily work in a different scale.  1.0
- * represents meters.
- */
-void MilesAudioManager::audio_3d_set_distance_factor(PN_stdfloat factor) {
-  audio_debug("MilesAudioManager::audio_3d_set_distance_factor( factor= " << factor << ")");
-
-  GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
-  AIL_set_3D_distance_factor(mgr->_digital_driver, factor);
-}
-
-/**
- * Get factor controlling working units.
- */
-PN_stdfloat MilesAudioManager::audio_3d_get_distance_factor() const {
-  audio_debug("MilesAudioManager::audio_3d_get_distance_factor()");
-
-  GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
-  return AIL_3D_distance_factor(mgr->_digital_driver);
-}
-
-/**
- * Exaggerates or diminishes the Doppler effect.  Defaults to 1.0
- */
-void MilesAudioManager::audio_3d_set_doppler_factor(PN_stdfloat factor) {
-  audio_debug("MilesAudioManager::audio_3d_set_doppler_factor(factor="<<factor<<")");
-
-  GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
-  AIL_set_3D_doppler_factor(mgr->_digital_driver, factor);
-}
-
-/**
- * Get the factor controlling the Doppler effect.
- */
-PN_stdfloat MilesAudioManager::audio_3d_get_doppler_factor() const {
-  audio_debug("MilesAudioManager::audio_3d_get_doppler_factor()");
-
-  GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
-  return AIL_3D_doppler_factor(mgr->_digital_driver);
-}
-
-/**
- * Control the effect distance has on audability.  Defaults to 1.0
- */
-void MilesAudioManager::audio_3d_set_drop_off_factor(PN_stdfloat factor) {
-  audio_debug("MilesAudioManager::audio_3d_set_drop_off_factor("<<factor<<")");
-
-  GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
-  AIL_set_3D_rolloff_factor(mgr->_digital_driver, factor);
-}
-
-/**
- * Get the factor controlling how quickly sound falls off with distance.
- */
-PN_stdfloat MilesAudioManager::audio_3d_get_drop_off_factor() const {
-  audio_debug("MilesAudioManager::audio_3d_get_drop_off_factor()");
-
-  GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
-  return AIL_3D_rolloff_factor(mgr->_digital_driver);
-}
-
-/**
- * Works similarly to MilesAudioSound::set_speaker_levels, but specifies the
- * 3D positions of the speakers in space.
- *
- * Once a NULL value is found for a speaker position, no more speaker
- * positions will be used.
- *
- * Note that Y and Z are switched to translate from Miles's coordinate system.
- */
-void MilesAudioManager::
-set_speaker_configuration(LVecBase3 *speaker1, LVecBase3 *speaker2, LVecBase3 *speaker3, LVecBase3 *speaker4, LVecBase3 *speaker5, LVecBase3 *speaker6, LVecBase3 *speaker7, LVecBase3 *speaker8, LVecBase3 *speaker9) {
-  audio_debug("MilesAudioManager::set_speaker_configuration()");
-
-  GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
-
-  MSSVECTOR3D speakers[9];
-
-  if(speaker1 != nullptr) {
-    speakers[0].x = speaker1->get_x();
-    speakers[0].y = speaker1->get_z();
-    speakers[0].z = speaker1->get_y();
-  }
-  if(speaker2 != nullptr) {
-    speakers[1].x = speaker2->get_x();
-    speakers[1].y = speaker2->get_z();
-    speakers[1].z = speaker2->get_y();
-  }
-  if(speaker3 != nullptr) {
-    speakers[2].x = speaker3->get_x();
-    speakers[2].y = speaker3->get_z();
-    speakers[2].z = speaker3->get_y();
-  }
-  if(speaker4 != nullptr) {
-    speakers[3].x = speaker4->get_x();
-    speakers[3].y = speaker4->get_z();
-    speakers[3].z = speaker4->get_y();
-  }
-  if(speaker5 != nullptr) {
-    speakers[4].x = speaker5->get_x();
-    speakers[4].y = speaker5->get_z();
-    speakers[4].z = speaker5->get_y();
-  }
-  if(speaker6 != nullptr) {
-    speakers[5].x = speaker6->get_x();
-    speakers[5].y = speaker6->get_z();
-    speakers[5].z = speaker6->get_y();
-  }
-  if(speaker7 != nullptr) {
-    speakers[6].x = speaker7->get_x();
-    speakers[6].y = speaker7->get_z();
-    speakers[6].z = speaker7->get_y();
-  }
-  if(speaker8 != nullptr) {
-    speakers[7].x = speaker8->get_x();
-    speakers[7].y = speaker8->get_z();
-    speakers[7].z = speaker8->get_y();
-  }
-  if(speaker9 != nullptr) {
-    speakers[8].x = speaker9->get_x();
-    speakers[8].y = speaker9->get_z();
-    speakers[8].z = speaker9->get_y();
-  }
-
-  if(speaker1 == nullptr) {
-    audio_error("No valid speaker positions specified in MilesAudioManager::set_speaker_configuration().");
-  } else if(speaker2 == nullptr) {
-    AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 1, 1.0);
-  } else if(speaker3 == nullptr) {
-    AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 2, 1.0);
-  } else if(speaker4 == nullptr) {
-    AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 3, 1.0);
-  } else if(speaker5 == nullptr) {
-    AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 4, 1.0);
-  } else if(speaker6 == nullptr) {
-    AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 5, 1.0);
-  } else if(speaker7 == nullptr) {
-    AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 6, 1.0);
-  } else if(speaker8 == nullptr) {
-    AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 7, 1.0);
-  } else if(speaker9 == nullptr) {
-    AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 8, 1.0);
-  } else {
-    AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 9, 1.0);
-  }
-}
-
-/**
- * Must be called every frame.  Failure to call this every frame could cause
- * problems for some audio managers.
- */
-void MilesAudioManager::
-update() {
-  {
-    MutexHolder holder(_streams_lock);
-    if (_stream_thread.is_null() && !_streams.empty()) {
-      // If we don't have a sub-thread, we have to service the streams in the
-      // main thread.
-      do_service_streams();
-    }
-  }
-
-  if (_sounds_finished) {
-    _sounds_finished = false;
-
-    // If the _sounds_finished flag was set, we should scan our list of
-    // playing sounds and see if any of them have finished recently.  We don't
-    // do this in the finished callback, because that might have been called
-    // in a sub-thread (and we may not have threading supported--and mutex
-    // protection--compiled in).
-
-    SoundsPlaying::iterator si = _sounds_playing.begin();
-    while (si != _sounds_playing.end()) {
-      MilesAudioSound *sound = (*si);
-      ++si;
-
-      if (sound->status() == AudioSound::READY) {
-        sound->stop();
-      }
-    }
-  }
-}
-
-/**
- *
- */
-void MilesAudioManager::
-release_sound(MilesAudioSound *audioSound) {
-  audio_debug("MilesAudioManager::release_sound(audioSound=\""
-              <<audioSound->get_name()<<"\"), this = " << (void *)this);
-  LightReMutexHolder holder(_lock);
-  AudioSet::iterator ai = _sounds_on_loan.find(audioSound);
-  if (ai != _sounds_on_loan.end()) {
-    _sounds_on_loan.erase(ai);
-  }
-
-  audio_debug("MilesAudioManager::release_sound() finished");
-}
-
-/**
- * Shuts down the audio manager and releases any resources associated with it.
- * Also cleans up all AudioSounds created via the manager.
- */
-void MilesAudioManager::
-cleanup() {
-  audio_debug("MilesAudioManager::cleanup(), this = " << (void *)this
-              << ", _cleanup_required = " << _cleanup_required);
-  LightReMutexHolder holder(_lock);
-  if (!_cleanup_required) {
-    return;
-  }
-
-  // Be sure to cleanup associated sounds before cleaning up the manager:
-  AudioSet orig_sounds;
-  orig_sounds.swap(_sounds_on_loan);
-  AudioSet::iterator ai;
-  for (ai = orig_sounds.begin(); ai != orig_sounds.end(); ++ai) {
-    (*ai)->cleanup();
-  }
-
-  do_clear_cache();
-
-  // Now stop the thread, if it has been started.
-  if (!_stream_thread.is_null()) {
-    milesAudio_cat.info()
-      << "Stopping audio streaming thread.\n";
-    PT(StreamThread) old_thread;
-    {
-      MutexHolder holder(_streams_lock);
-      nassertv(!_stream_thread.is_null());
-      _stream_thread->_keep_running = false;
-      _streams_cvar.notify();
-      old_thread = _stream_thread;
-      _stream_thread.clear();
-    }
-    old_thread->join();
-  }
-
-  _cleanup_required = false;
-  audio_debug("MilesAudioManager::cleanup() finished");
-}
-
-/**
- *
- */
-void MilesAudioManager::
-output(std::ostream &out) const {
-  LightReMutexHolder holder(_lock);
-  out << get_type() << ": " << _sounds_playing.size()
-      << " / " << _sounds_on_loan.size() << " sounds playing / total";
-}
-
-/**
- *
- */
-void MilesAudioManager::
-write(std::ostream &out) const {
-  LightReMutexHolder holder(_lock);
-
-  out << (*this) << "\n";
-  AudioSet::const_iterator ai;
-  for (ai = _sounds_on_loan.begin(); ai != _sounds_on_loan.end(); ++ai) {
-    MilesAudioSound *sound = (*ai);
-    out << "  " << *sound << "\n";
-  }
-
-  size_t total_preload = 0;
-  size_t num_preloaded = 0;
-  SoundMap::const_iterator si;
-  for (si = _sounds.begin(); si != _sounds.end(); ++si) {
-    if (!(*si).second->_raw_data.empty()) {
-      ++num_preloaded;
-      total_preload += (*si).second->_raw_data.size();
-    }
-  }
-  out << num_preloaded << " of " << _sounds.size() << " sounds preloaded, size used is " << (total_preload + 1023) / 1024 << "K\n";
-
-  {
-    MutexHolder holder(_streams_lock);
-    out << _streams.size() << " streams opened.\n";
-    if (!_stream_thread.is_null()) {
-      out << "(Audio streaming thread has been started.)\n";
-    }
-  }
-
-  GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
-
-  int num_samples = mgr->get_num_samples();
-  out << num_samples << " sample handles allocated globally.\n";
-
-  int num_sequences = mgr->get_num_sequences();
-  out << num_sequences << " sequence handles allocated globally.\n";
-}
-
-
-/**
- * Implementation of is_valid().  Assumes the lock is already held.
- */
-bool MilesAudioManager::
-do_is_valid() {
-  bool check = true;
-  if (_sounds.size() != _lru.size()) {
-    audio_debug("-- Error _sounds.size() != _lru.size() --");
-    check = false;
-
-  } else {
-    LRU::const_iterator i = _lru.begin();
-    for (; i != _lru.end(); ++i) {
-      SoundMap::const_iterator smi = _sounds.find(**i);
-      if (smi == _sounds.end()) {
-        audio_debug("-- "<<**i<<" in _lru and not in _sounds --");
-        check = false;
-        break;
-      }
-    }
-  }
-  return _is_valid && check;
-}
-
-/**
- * Assumes the lock is already held.
- */
-void MilesAudioManager::
-do_reduce_sounds_playing_to(unsigned int count) {
-  int limit = _sounds_playing.size() - count;
-  while (limit-- > 0) {
-    SoundsPlaying::iterator sound = _sounds_playing.begin();
-    assert(sound != _sounds_playing.end());
-    (**sound).stop();
-  }
-}
-
-/**
- * Assumes the lock is already held.
- */
-void MilesAudioManager::
-do_clear_cache() {
-  if (_is_valid) { nassertv(do_is_valid()); }
-  _sounds.clear();
-  _lru.clear();
-  if (_is_valid) { nassertv(do_is_valid()); }
-}
-
-/**
- * Adds the indicated stream to the list of streams to be serviced by a Panda
- * sub-thread.  This is in lieu of Miles' auto-service-stream mechanism.
- */
-void MilesAudioManager::
-start_service_stream(HSTREAM stream) {
-  MutexHolder holder(_streams_lock);
-  nassertv(find(_streams.begin(), _streams.end(), stream) == _streams.end());
-  _streams.push_back(stream);
-  _streams_cvar.notify();
-
-  if (_stream_thread.is_null() && Thread::is_threading_supported()) {
-    milesAudio_cat.info()
-      << "Starting audio streaming thread.\n";
-    _stream_thread = new StreamThread(this);
-    _stream_thread->start(TP_low, true);
-  }
-}
-
-/**
- * Removes the indicated stream from the list of streams to be serviced by a
- * Panda sub-thread.
- */
-void MilesAudioManager::
-stop_service_stream(HSTREAM stream) {
-  MutexHolder holder(_streams_lock);
-  Streams::iterator si = find(_streams.begin(), _streams.end(), stream);
-  if (si != _streams.end()) {
-    _streams.erase(si);
-  }
-}
-
-
-/**
- * Assumes the lock is already held.
- */
-void MilesAudioManager::
-most_recently_used(const string &path) {
-  audio_debug("MilesAudioManager::most_recently_used(path=\""
-      <<path<<"\")");
-  LRU::iterator i=find(_lru.begin(), _lru.end(), &path);
-  if (i != _lru.end()) {
-    _lru.erase(i);
-  }
-  // At this point, path should not exist in the _lru:
-  assert(find(_lru.begin(), _lru.end(), &path) == _lru.end());
-  _lru.push_back(&path);
-  nassertv(do_is_valid());
-}
-
-/**
- * Assumes the lock is already held.
- */
-void MilesAudioManager::
-uncache_a_sound() {
-  audio_debug("MilesAudioManager::uncache_a_sound()");
-  nassertv(do_is_valid());
-  // uncache least recently used:
-  assert(_lru.size()>0);
-  LRU::reference path=_lru.front();
-  SoundMap::iterator i = _sounds.find(*path);
-  assert(i != _sounds.end());
-  _lru.pop_front();
-
-  if (i != _sounds.end()) {
-    audio_debug("  uncaching \""<<i->first<<"\"");
-    _sounds.erase(i);
-  }
-  nassertv(do_is_valid());
-}
-
-/**
- * Inform the manager that a sound is about to play.
- */
-void MilesAudioManager::
-starting_sound(MilesAudioSound *audio) {
-  LightReMutexHolder holder(_lock);
-  if (_concurrent_sound_limit) {
-    do_reduce_sounds_playing_to(_concurrent_sound_limit);
-  }
-  _sounds_playing.insert(audio);
-}
-
-/**
- * Inform the manager that a sound is finished or someone called stop on the
- * sound (this should not be called if a sound is only paused).
- */
-void MilesAudioManager::
-stopping_sound(MilesAudioSound *audio) {
-  LightReMutexHolder holder(_lock);
-  _sounds_playing.erase(audio);
-  if (_hasMidiSounds && _sounds_playing.size() == 0) {
-    GlobalMilesManager::get_global_ptr()->force_midi_reset();
-  }
-}
-
-/**
- * Reads a sound file and allocates a SoundData pointer for it.  Returns NULL
- * if the sound file cannot be loaded.
- *
- * Assumes the lock is already held.
- */
-PT(MilesAudioManager::SoundData) MilesAudioManager::
-load(const Filename &file_name) {
-  PT(SoundData) sd = new SoundData;
-
-  VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
-  PT(VirtualFile) file = vfs->get_file(file_name);
-  if (file == nullptr) {
-    milesAudio_cat.warning()
-      << "No such file: " << file_name << "\n";
-    return nullptr;
-  }
-
-  if (file->get_file_size() == 0) {
-    milesAudio_cat.warning()
-      << "File " << file_name << " is empty\n";
-    return nullptr;
-  }
-
-  sd->_basename = file_name.get_basename();
-
-  string extension = sd->_basename.get_extension();
-  if (extension == "pz" || extension == "gz") {
-    extension = Filename(sd->_basename.get_basename_wo_extension()).get_extension();
-  }
-
-  bool is_midi_file = (downcase(extension) == "mid");
-
-  if ((miles_audio_preload_threshold == -1 || file->get_file_size() < (std::streamsize)miles_audio_preload_threshold) ||
-      is_midi_file) {
-    // If the file is sufficiently small, we'll preload it into memory.  MIDI
-    // files cannot be streamed, so we always preload them, regardless of
-    // size.
-
-    if (!file->read_file(sd->_raw_data, true)) {
-      milesAudio_cat.warning()
-        << "Unable to read " << file_name << "\n";
-      return nullptr;
-    }
-
-    sd->_file_type =
-      AIL_file_type(&sd->_raw_data[0], sd->_raw_data.size());
-
-    if (sd->_file_type == AILFILETYPE_MIDI) {
-      // A standard MIDI file.  We have to convert this to XMIDI for Miles.
-      void *xmi;
-      U32 xmi_size;
-      if (AIL_MIDI_to_XMI(&sd->_raw_data[0], sd->_raw_data.size(),
-                          &xmi, &xmi_size, 0)) {
-        audio_debug("converted " << sd->_basename << " from standard MIDI ("
-                    << sd->_raw_data.size() << " bytes) to XMIDI ("
-                    << xmi_size << " bytes)");
-
-        // Copy the data to our own buffer and free the Miles-allocated data.
-        sd->_raw_data.clear();
-        sd->_raw_data.insert(sd->_raw_data.end(),
-                             (unsigned char *)xmi, (unsigned char *)xmi + xmi_size);
-        AIL_mem_free_lock(xmi);
-        sd->_file_type = AILFILETYPE_XMIDI;
-      } else {
-        milesAudio_cat.warning()
-          << "Could not convert " << sd->_basename << " to XMIDI.\n";
-      }
-    }
-
-    bool expand_to_wav = false;
-
-    if (sd->_file_type != AILFILETYPE_MPEG_L3_AUDIO) {
-      audio_debug(sd->_basename << " is not an mp3 file.");
-    } else if ((int)sd->_raw_data.size() >= miles_audio_expand_mp3_threshold) {
-      audio_debug(sd->_basename << " is too large to expand in-memory.");
-    } else {
-      audio_debug(sd->_basename << " will be expanded in-memory.");
-      expand_to_wav = true;
-    }
-
-    if (expand_to_wav) {
-      // Now convert the file to WAV format in-memory.  This is useful to work
-      // around seek and length problems associated with variable bit-rate MP3
-      // encoding.
-      void *wav_data;
-      U32 wav_data_size;
-      if (AIL_decompress_ASI(&sd->_raw_data[0], sd->_raw_data.size(),
-                             sd->_basename.c_str(), &wav_data, &wav_data_size,
-                             nullptr)) {
-        audio_debug("expanded " << sd->_basename << " from " << sd->_raw_data.size()
-                    << " bytes to " << wav_data_size << " bytes.");
-
-        if (wav_data_size != 0) {
-          // Now copy the memory into our own buffers, and free the Miles-
-          // allocated memory.
-          sd->_raw_data.clear();
-          sd->_raw_data.insert(sd->_raw_data.end(),
-                               (unsigned char *)wav_data, (unsigned char *)wav_data + wav_data_size);
-          sd->_file_type = AILFILETYPE_PCM_WAV;
-          sd->_basename.set_extension("wav");
-        }
-        AIL_mem_free_lock(wav_data);
-
-      } else {
-        audio_debug("unable to expand " << sd->_basename);
-      }
-    }
-
-  } else {
-    // If the file is large, we'll stream it from disk instead of preloading
-    // it.  This means we don't need to load any data at this point.
-  }
-
-  return sd;
-}
-
-/**
- * Called to service the streaming audio channels currently playing on the
- * audio manager.
- */
-void MilesAudioManager::
-thread_main(volatile bool &keep_running) {
-  MutexHolder holder(_streams_lock);
-
-  while (keep_running) {
-    if (_streams.empty()) {
-      // If there are no streams to service, block on the condition variable.
-      _streams_cvar.wait();
-    } else {
-      do_service_streams();
-    }
-
-    // Now yield to be polite to the main application.
-    _streams_lock.release();
-    Thread::force_yield();
-    _streams_lock.acquire();
-  }
-}
-
-/**
- * Internal function to service all the streams.  Assumes _streams_lock is
- * already held.
- */
-void MilesAudioManager::
-do_service_streams() {
-  size_t i = 0;
-  while (i < _streams.size()) {
-    HSTREAM stream = _streams[i];
-
-    // We must release the lock while we are servicing stream i.
-    _streams_lock.release();
-    AIL_service_stream(stream, 0);
-    Thread::consider_yield();
-    _streams_lock.acquire();
-
-    ++i;
-  }
-}
-
-/**
- *
- */
-MilesAudioManager::StreamThread::
-StreamThread(MilesAudioManager *mgr) :
-  Thread("StreamThread", "StreamThread"),
-  _mgr(mgr)
-{
-  _keep_running = true;
-}
-
-/**
- *
- */
-void MilesAudioManager::StreamThread::
-thread_main() {
-  _mgr->thread_main(_keep_running);
-}
-
-/**
- *
- */
-MilesAudioManager::SoundData::
-SoundData() :
-  _raw_data(MilesAudioManager::get_class_type()),
-  _has_length(false),
-  _length(0.0f)
-{
-}
-
-/**
- *
- */
-MilesAudioManager::SoundData::
-~SoundData() {
-}
-
-/**
- *
- */
-PN_stdfloat MilesAudioManager::SoundData::
-get_length() {
-  if (!_has_length) {
-    // Time to determine the length of the file.
-
-    if (_raw_data.empty()) {
-      _length = 0.0f;
-      _has_length = true;
-
-    } else if (_file_type == AILFILETYPE_MPEG_L3_AUDIO) {
-      // If it's an mp3 file, we have to calculate its length by walking
-      // through all of its frames.
-      audio_debug("Computing length of mp3 file " << _basename);
-
-      MP3_INFO info;
-      AIL_inspect_MP3(&info, &_raw_data[0], _raw_data.size());
-      _length = 0.0f;
-      while (AIL_enumerate_MP3_frames(&info)) {
-        _length += info.data_size * 8.0f / info.bit_rate;
-      }
-      _has_length = true;
-
-    } else if (_file_type == AILFILETYPE_PCM_WAV ||
-               _file_type == AILFILETYPE_ADPCM_WAV ||
-               _file_type == AILFILETYPE_OTHER_ASI_WAV) {
-      audio_debug("Getting length of wav file " << _basename);
-
-      AILSOUNDINFO info;
-      if (AIL_WAV_info(&_raw_data[0], &info)) {
-        _length = (PN_stdfloat)info.samples / (PN_stdfloat)info.rate;
-        audio_debug(info.samples << " samples at " << info.rate
-                    << "; length is " << _length << " seconds.");
-        _has_length = true;
-      }
-    }
-  }
-
-  nassertr(_has_length, 0.0f);
-  return _length;
-}
-
-/**
- * Records the sample length, as determined externally.
- */
-void MilesAudioManager::SoundData::
-set_length(PN_stdfloat length) {
-  _length = length;
-  _has_length = true;
-}
-
-#endif //]

+ 0 - 198
panda/src/audiotraits/milesAudioManager.h

@@ -1,198 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file milesAudioManager.h
- * @author skyler
- * @date 2001-06-06
- * Prior system by: cary
- */
-
-#ifndef __MILES_AUDIO_MANAGER_H__ //[
-#define __MILES_AUDIO_MANAGER_H__
-
-#include "pandabase.h"
-#ifdef HAVE_RAD_MSS //[
-
-#include "audioManager.h"
-#include "pset.h"
-#include "pmap.h"
-#include "pdeque.h"
-#include "pvector.h"
-#include "thread.h"
-#include "pmutex.h"
-#include "lightReMutex.h"
-#include "conditionVar.h"
-#include "vector_uchar.h"
-
-#include <mss.h>
-
-class MilesAudioSound;
-
-class EXPCL_MILES_AUDIO MilesAudioManager: public AudioManager {
-public:
-  // See AudioManager.h for documentation.
-
-  MilesAudioManager();
-  virtual ~MilesAudioManager();
-
-  virtual void shutdown();
-
-  virtual bool is_valid();
-
-  virtual PT(AudioSound) get_sound(const Filename &file_name, bool positional = false, int mode=SM_heuristic);
-  virtual PT(AudioSound) get_sound(MovieAudio *sound, bool positional = false, int mode=SM_heuristic);
-  virtual void uncache_sound(const Filename &file_name);
-  virtual void clear_cache();
-  virtual void set_cache_limit(unsigned int count);
-  virtual unsigned int get_cache_limit() const;
-
-  virtual void set_volume(PN_stdfloat volume);
-  virtual PN_stdfloat get_volume() const;
-
-  void set_play_rate(PN_stdfloat play_rate);
-  PN_stdfloat get_play_rate() const;
-
-  virtual void set_active(bool active);
-  virtual bool get_active() const;
-
-  virtual void set_concurrent_sound_limit(unsigned int limit = 0);
-  virtual unsigned int get_concurrent_sound_limit() const;
-
-  virtual void reduce_sounds_playing_to(unsigned int count);
-  virtual void stop_all_sounds();
-
-  virtual void update();
-
-  // Tell the manager that the sound dtor was called.
-  void release_sound(MilesAudioSound *audioSound);
-  void cleanup();
-
-  // 3D spatialized sound support.  Spatialized sound was originally added for
-  // FMOD, so there are parts of the interface in the Miles implementation
-  // that are a little more awkward than they would be otherwise.
-  virtual void audio_3d_set_listener_attributes(PN_stdfloat px, PN_stdfloat py, PN_stdfloat pz, PN_stdfloat vx, PN_stdfloat xy, PN_stdfloat xz, PN_stdfloat fx, PN_stdfloat fy, PN_stdfloat fz, PN_stdfloat ux, PN_stdfloat uy, PN_stdfloat uz);
-  virtual void audio_3d_get_listener_attributes(PN_stdfloat *px, PN_stdfloat *py, PN_stdfloat *pz, PN_stdfloat *vx, PN_stdfloat *vy, PN_stdfloat *vz, PN_stdfloat *fx, PN_stdfloat *fy, PN_stdfloat *fz, PN_stdfloat *ux, PN_stdfloat *uy, PN_stdfloat *uz);
-  virtual void audio_3d_set_distance_factor(PN_stdfloat factor);
-  virtual PN_stdfloat audio_3d_get_distance_factor() const;
-  virtual void audio_3d_set_doppler_factor(PN_stdfloat factor);
-  virtual PN_stdfloat audio_3d_get_doppler_factor() const;
-  virtual void audio_3d_set_drop_off_factor(PN_stdfloat factor);
-  virtual PN_stdfloat audio_3d_get_drop_off_factor() const;
-  virtual void set_speaker_configuration(LVecBase3 *speaker1, LVecBase3 *speaker2=nullptr, LVecBase3 *speaker3=nullptr, LVecBase3 *speaker4=nullptr, LVecBase3 *speaker5=nullptr, LVecBase3 *speaker6=nullptr, LVecBase3 *speaker7=nullptr, LVecBase3 *speaker8=nullptr, LVecBase3 *speaker9=nullptr);
-
-  virtual void output(std::ostream &out) const;
-  virtual void write(std::ostream &out) const;
-
-private:
-  bool do_is_valid();
-  void do_reduce_sounds_playing_to(unsigned int count);
-  void do_clear_cache();
-
-  void start_service_stream(HSTREAM stream);
-  void stop_service_stream(HSTREAM stream);
-
-  void most_recently_used(const std::string &path);
-  void uncache_a_sound();
-
-  void starting_sound(MilesAudioSound *audio);
-  void stopping_sound(MilesAudioSound *audio);
-
-  class SoundData;
-  PT(SoundData) load(const Filename &file_name);
-
-  void thread_main(volatile bool &keep_running);
-  void do_service_streams();
-
-private:
-  class StreamThread : public Thread {
-  public:
-    StreamThread(MilesAudioManager *mgr);
-    virtual void thread_main();
-
-    MilesAudioManager *_mgr;
-    volatile bool _keep_running;
-  };
-
-  // The sound cache:
-  class SoundData : public ReferenceCount {
-  public:
-    SoundData();
-    ~SoundData();
-    PN_stdfloat get_length();
-    void set_length(PN_stdfloat length);
-
-    Filename _basename;
-    S32 _file_type;
-    vector_uchar _raw_data;
-    bool _has_length;
-    PN_stdfloat _length;  // in seconds.
-  };
-  typedef pmap<std::string, PT(SoundData) > SoundMap;
-  SoundMap _sounds;
-
-  typedef pset<MilesAudioSound *> AudioSet;
-  // The offspring of this manager:
-  AudioSet _sounds_on_loan;
-
-  typedef pset<MilesAudioSound *> SoundsPlaying;
-  // The sounds from this manager that are currently playing:
-  SoundsPlaying _sounds_playing;
-
-  // The Least Recently Used mechanism:
-  typedef pdeque<const std::string *> LRU;
-  LRU _lru;
-  // State:
-  PN_stdfloat _volume;
-  PN_stdfloat _play_rate;
-  bool _active;
-  int _cache_limit;
-  bool _cleanup_required;
-  unsigned int _concurrent_sound_limit;
-
-  bool _is_valid;
-  bool _hasMidiSounds;
-
-  // This mutex protects everything above.
-  LightReMutex _lock;
-  bool _sounds_finished;
-
-  typedef pvector<HSTREAM> Streams;
-  PT(StreamThread) _stream_thread;
-  Streams _streams;
-  Mutex _streams_lock;
-  ConditionVar _streams_cvar;
-
-public:
-  static TypeHandle get_class_type() {
-    return _type_handle;
-  }
-  static void init_type() {
-    AudioManager::init_type();
-    register_type(_type_handle, "MilesAudioManager",
-                  AudioManager::get_class_type());
-  }
-  virtual TypeHandle get_type() const {
-    return get_class_type();
-  }
-  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
-
-private:
-  static TypeHandle _type_handle;
-
-  friend class MilesAudioSound;
-  friend class MilesAudioSample;
-  friend class MilesAudioSequence;
-  friend class MilesAudioStream;
-};
-
-EXPCL_MILES_AUDIO AudioManager *Create_MilesAudioManager();
-
-
-#endif //]
-
-#endif //]

+ 0 - 13
panda/src/audiotraits/milesAudioSample.I

@@ -1,13 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file milesAudioSample.I
- * @author skyler
- * @date 2001-06-06
- * Prior system by: cary
- */

+ 0 - 530
panda/src/audiotraits/milesAudioSample.cxx

@@ -1,530 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file milesAudioSample.cxx
- * @author skyler
- * @date 2001-06-06
- */
-
-#include "milesAudioSample.h"
-
-#ifdef HAVE_RAD_MSS //[
-
-#include "milesAudioManager.h"
-
-
-TypeHandle MilesAudioSample::_type_handle;
-
-#undef miles_audio_debug
-
-#ifndef NDEBUG //[
-#define miles_audio_debug(x) \
-    audio_debug("MilesAudioSample \""<<get_name()<<"\" "<< x )
-#else //][
-#define miles_audio_debug(x) ((void)0)
-#endif //]
-
-/**
- * This constructor is called only by the MilesAudioManager.
- */
-MilesAudioSample::
-MilesAudioSample(MilesAudioManager *manager, MilesAudioManager::SoundData *sd,
-                 const std::string &file_name) :
-  MilesAudioSound(manager, file_name),
-  _sd(sd)
-{
-  nassertv(sd != nullptr);
-  audio_debug("MilesAudioSample(manager=0x"<<(void*)&manager
-              <<", sd=0x"<<(void*)sd<<", file_name="<<file_name<<")");
-
-  _sample = 0;
-  _sample_index = 0;
-  _original_playback_rate = 1.0f;
-}
-
-/**
- *
- */
-MilesAudioSample::
-~MilesAudioSample() {
-  miles_audio_debug("~MilesAudioSample()");
-  cleanup();
-  miles_audio_debug("~MilesAudioSample() done");
-}
-
-/**
- *
- */
-void MilesAudioSample::
-play() {
-  miles_audio_debug("play()");
-  if (_active) {
-    if (_sd->_raw_data.empty()) {
-      milesAudio_cat.warning()
-        << "Could not play " << _file_name << ": no data\n";
-    } else {
-      stop();
-      _manager->starting_sound(this);
-
-      nassertv(_sample == 0);
-
-      GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
-      if (!mgr->get_sample(_sample, _sample_index, this)){
-        milesAudio_cat.warning()
-          << "Could not play " << _file_name << ": too many open samples\n";
-        _sample = 0;
-      } else {
-        AIL_set_named_sample_file(_sample, _sd->_basename.c_str(),
-                                  &_sd->_raw_data[0], _sd->_raw_data.size(),
-                                  0);
-        _original_playback_rate = AIL_sample_playback_rate(_sample);
-        AIL_set_sample_user_data(_sample, 0, (SINTa)this);
-        AIL_register_EOS_callback(_sample, finish_callback);
-
-        set_volume(_volume);
-        set_play_rate(_play_rate);
-        AIL_set_sample_loop_count(_sample, _loop_count);
-
-        if (_got_start_time) {
-          do_set_time(_start_time);
-          AIL_resume_sample(_sample);
-        } else {
-          AIL_start_sample(_sample);
-        }
-      }
-
-      _got_start_time = false;
-    }
-  } else {
-    // In case _loop_count gets set to forever (zero):
-    audio_debug("  paused "<<_file_name );
-    _paused = true;
-  }
-}
-
-/**
- *
- */
-void MilesAudioSample::
-stop() {
-  if (_manager == nullptr) {
-    return;
-  }
-
-  miles_audio_debug("stop()");
-  _manager->stopping_sound(this);
-  // The _paused flag should not be cleared here.  _paused is not like the
-  // Pause button on a cddvd player.  It is used as a flag to say that it was
-  // looping when it was set inactive.  There is no need to make this
-  // symmetrical with play().  set_active() is the 'owner' of _paused.  play()
-  // accesses _paused to help in the situation where someone calls play on an
-  // inactive sound().
-
-  // it fixes audio bug, I don't understand the reasoning of the above comment
-  _paused = false;
-
-  if (_sample != 0) {
-    AIL_end_sample(_sample);
-
-    GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
-    mgr->release_sample(_sample_index, this);
-
-    _sample = 0;
-    _sample_index = 0;
-  }
-}
-
-/**
- *
- */
-PN_stdfloat MilesAudioSample::
-get_time() const {
-  if (_sample == 0) {
-    if (_got_start_time) {
-      return _start_time;
-    }
-    return 0.0f;
-  }
-
-  S32 current_ms;
-  AIL_sample_ms_position(_sample, nullptr, &current_ms);
-  PN_stdfloat time = PN_stdfloat(current_ms * 0.001f);
-
-  return time;
-}
-
-/**
- *
- */
-void MilesAudioSample::
-set_volume(PN_stdfloat volume) {
-  miles_audio_debug("set_volume(volume="<<volume<<")");
-
-  // Set the volume even if our volume is not changing, because the
-  // MilesAudioManager will call set_volume() when *its* volume changes.
-
-  // Set the volume:
-  _volume = volume;
-
-  if (_sample != 0) {
-    volume *= _manager->get_volume();
-
-    // Change to Miles volume, range 0 to 1.0:
-    F32 milesVolume = volume;
-    milesVolume = std::min(milesVolume, 1.0f);
-    milesVolume = std::max(milesVolume, 0.0f);
-
-    // Convert balance of -1.0..1.0 to 0-1.0:
-    F32 milesBalance = (F32)((_balance + 1.0f) * 0.5f);
-
-    AIL_set_sample_volume_pan(_sample, milesVolume, milesBalance);
-  }
-}
-
-/**
- *
- */
-void MilesAudioSample::
-set_balance(PN_stdfloat balance_right) {
-  miles_audio_debug("set_balance(balance_right="<<balance_right<<")");
-  _balance = balance_right;
-
-  // Call set_volume to effect the change:
-  set_volume(_volume);
-}
-
-/**
- *
- */
-void MilesAudioSample::
-set_play_rate(PN_stdfloat play_rate) {
-  miles_audio_debug("set_play_rate(play_rate="<<play_rate<<")");
-
-  // Set the play_rate:
-  _play_rate = play_rate;
-
-  if (_sample != 0) {
-    play_rate *= _manager->get_play_rate();
-
-    // wave and mp3 use sample rate (e.g.  44100)
-    S32 speed = (S32)(play_rate * (PN_stdfloat)_original_playback_rate);
-    AIL_set_sample_playback_rate(_sample, speed);
-    audio_debug("  play_rate for this wav or mp3 is now " << speed);
-  }
-}
-
-/**
- *
- */
-PN_stdfloat MilesAudioSample::
-length() const {
-  return _sd->get_length();
-}
-
-/**
- *
- */
-AudioSound::SoundStatus MilesAudioSample::
-status() const {
-  if (_sample == 0) {
-    return AudioSound::READY;
-  }
-  switch (AIL_sample_status(_sample)) {
-  case SMP_DONE:
-  case SMP_STOPPED:
-  case SMP_FREE:
-    return AudioSound::READY;
-
-  case SMP_PLAYING:
-  case SMP_PLAYINGBUTRELEASED:
-    return AudioSound::PLAYING;
-
-  default:
-    return AudioSound::BAD;
-  }
-}
-
-/**
- * Stops the sound from playing and releases any associated resources, in
- * preparation for releasing the sound or shutting down the sound system.
- */
-void MilesAudioSample::
-cleanup() {
-  stop();
-  set_active(false);
-  nassertv(_sample == 0);
-
-  if (_manager != nullptr) {
-    _manager->release_sound(this);
-    _manager = nullptr;
-  }
-}
-
-/**
- *
- */
-void MilesAudioSample::
-output(std::ostream &out) const {
-  out << get_type() << " " << get_name() << " " << status();
-  if (!_sd.is_null()) {
-    out << " " << (_sd->_raw_data.size() + 1023) / 1024 << "K";
-  }
-}
-
-/**
- * Set position and velocity of this sound.  Note that Y and Z are switched to
- * translate from Miles's coordinate system.
- */
-void MilesAudioSample::set_3d_attributes(PN_stdfloat px, PN_stdfloat py, PN_stdfloat pz, PN_stdfloat vx, PN_stdfloat vy, PN_stdfloat vz) {
-  audio_debug("MilesAudioSample::set_3d_attributes()  Setting a sound's 3D Coordinates.");
-
-  if(_sample != 0) {
-    AIL_set_sample_3D_position(_sample, px, pz, py);
-    AIL_set_sample_3D_velocity_vector(_sample, vx, vz, vy);
-  } else {
-    audio_warning("_sample == 0 in MilesAudioSample::set_3d_attributes().");
-  }
-}
-
-/**
- * Get position and velocity of this sound.
- */
-void MilesAudioSample::get_3d_attributes(PN_stdfloat *px, PN_stdfloat *py, PN_stdfloat *pz, PN_stdfloat *vx, PN_stdfloat *vy, PN_stdfloat *vz) {
-  audio_debug("MilesAudioSample::get_3d_attributes().");
-
-  if(_sample != 0) {
-    float lpx, lpy, lpz, lvx, lvy, lvz;
-    AIL_sample_3D_position(_sample, &lpx, &lpz, &lpy);
-    AIL_sample_3D_velocity(_sample, &lvx, &lvz, &lvy);
-    *px = lpx;
-    *py = lpy;
-    *pz = lpz;
-    *vx = lvx;
-    *vy = lvy;
-    *vz = lvz;
-  } else {
-    audio_warning("_sample == 0 in MilesAudioSample::get_3d_attributes().");
-  }
-}
-
-/**
- * Set the distance that this sound begins to fall off.  With Miles's default
- * falloff behavior, when the distance between the sound and the listener is
- * doubled, the volume is halved, and vice versa.
- */
-void MilesAudioSample::set_3d_min_distance(PN_stdfloat dist) {
-  audio_debug("MilesAudioSample::set_3d_min_distance() Setting the sound's 3D min distance ( min= " << dist << " ) ");
-
-  if(_sample != 0) {
-    // Implementation is awkward, since Miles gets and sets min and max
-    // distances in a single operation.
-    float max_dist;
-    int auto_3D_wet_atten;
-    AIL_sample_3D_distances(_sample, &max_dist, nullptr, &auto_3D_wet_atten);
-
-    AIL_set_sample_3D_distances(_sample, max_dist, dist, auto_3D_wet_atten);
-  } else {
-    audio_warning("_sample == 0 in MilesAudioSample::set_3d_min_distance().");
-  }
-}
-
-/**
- * Get the distance that this sound begins to fall off.
- */
-PN_stdfloat MilesAudioSample::get_3d_min_distance() const {
-  audio_debug("MilesAudioSample::get_3d_min_distance() ");
-
-  if(_sample != 0) {
-    float min_dist;
-    AIL_sample_3D_distances(_sample, nullptr, &min_dist, nullptr);
-    return (PN_stdfloat)min_dist;
-  } else {
-    audio_warning("_sample == 0 in MilesAudioSample::get_3d_min_distance().");
-    return -1.0;
-  }
-}
-
-/**
- * Set the distance at which this sound is clipped to silence.  Note that this
- * value does not affect the rate at which the sound falls off, but only the
- * distance at which it gets clipped.
- */
-void MilesAudioSample::set_3d_max_distance(PN_stdfloat dist) {
-  audio_debug("MilesAudioSample::set_3d_max_distance() Setting the sound's 3D max distance ( max= " << dist << " ) ");
-
-  if(_sample != 0) {
-    // Implementation is awkward, since Miles gets and sets min and max
-    // distances in a single operation.
-    float min_dist;
-    int auto_3D_wet_atten;
-    AIL_sample_3D_distances(_sample, nullptr, &min_dist, &auto_3D_wet_atten);
-
-    AIL_set_sample_3D_distances(_sample, dist, min_dist, auto_3D_wet_atten);
-  } else {
-    audio_warning("_sample == 0 in MilesAudioSample::set_3d_max_distance().");
-  }
-}
-
-/**
- * Get the distance at which this sound is clipped to silence.
- */
-PN_stdfloat MilesAudioSample::get_3d_max_distance() const {
-  audio_debug("MilesAudioSample::get_3d_max_distance() ");
-
-  if(_sample != 0) {
-    float max_dist;
-    AIL_sample_3D_distances(_sample, &max_dist, nullptr, nullptr);
-    return (PN_stdfloat)max_dist;
-  } else {
-    audio_warning("_sample == 0 in MilesAudioSample::get_3d_max_distance().");
-    return -1.0;
-  }
-}
-
-/**
- * Get the level of a particular logical channel (speaker). "index" specifies
- * which speaker in an array of all the logical channels currently in use to
- * retrieve the level of.
- *
- * For instance, in a standard 4.0 channel setup, speakers are setup as
- * [frontLeft, frontRight, backLeft, backRight]. Thus, get_speaker_level(2)
- * will retrieve the level of the backLeft speaker.
- *
- * The order in which speakers appear in the array for standard speaker setups
- * is defined to be:
- *
- * FRONT_LEFT FRONT_RIGHT FRONT_CENTER LOW_FREQUENCY (sub woofer) BACK_LEFT
- * BACK_RIGHT FRONT_LEFT_OF_CENTER FRONT_RIGHT_OF_CENTER BACK_CENTER SIDE_LEFT
- * SIDE_RIGHT TOP_CENTER TOP_FRONT_LEFT TOP_FRONT_CENTER TOP_FRONT_RIGHT
- * TOP_BACK_LEFT TOP_BACK_CENTER TOP_BACK_RIGHT
- *
- */
-PN_stdfloat MilesAudioSample::
-get_speaker_level(int index) {
-  audio_debug("MilesAudioSample::get_speaker_level(" << index << ")");
-
-  if(_sample != 0) {
-    int numLevels;
-    float *levels = AIL_sample_channel_levels(_sample, &numLevels);
-
-    if(index < numLevels) {
-      return (PN_stdfloat)levels[index];
-    } else {
-      audio_error("index out of range in MilesAudioSample::get_speaker_level.  numLevels: " << numLevels);
-      return -1.0;
-    }
-  } else {
-    audio_warning("Warning: MilesAudioSample::get_speaker_level only works for sounds that are currently playing");
-    return -1.0;
-  }
-}
-
-/**
- * Set the output levels on the logical channels (speakers) for this sound.
- * Values should be in the range 0.0 to 1.0. Levels for up to nine channels
- * may be specified.  As soon as a level is reached that falls outside the
- * range 0.0 to 1.0, the levels specified up to that point will be sent and
- * all other levels will be ignored.
- *
- * The user must know what the current speaker setup is in order to know which
- * level corresponds to which speaker.
- *
- * This method will have no effect if 3D attributes have been set for this
- * sound.
- *
- * The order in which speakers appear in the array for standard speaker setups
- * is defined to be:
- *
- * FRONT_LEFT FRONT_RIGHT FRONT_CENTER LOW_FREQUENCY (sub woofer) BACK_LEFT
- * BACK_RIGHT FRONT_LEFT_OF_CENTER FRONT_RIGHT_OF_CENTER BACK_CENTER SIDE_LEFT
- * SIDE_RIGHT TOP_CENTER TOP_FRONT_LEFT TOP_FRONT_CENTER TOP_FRONT_RIGHT
- * TOP_BACK_LEFT TOP_BACK_CENTER TOP_BACK_RIGHT
- *
- */
-void MilesAudioSample::
-set_speaker_levels(PN_stdfloat level1, PN_stdfloat level2, PN_stdfloat level3, PN_stdfloat level4, PN_stdfloat level5, PN_stdfloat level6, PN_stdfloat level7, PN_stdfloat level8, PN_stdfloat level9) {
-  audio_debug("MilesAudioSample::set_speaker_levels()");
-
-  if(_sample != 0) {
-    float levels[9] = {level1, level2, level3, level4, level5, level6, level7, level8, level9};
-
-    if((level1 < 0.0) || (level1 > 1.0)) {
-      audio_error("No valid levels specified in MilesAudioSample::set_speaker_levels().");
-    } else if((level2 < 0.0) || (level2 > 1.0)) {
-      AIL_set_sample_channel_levels(_sample, levels, 1);
-    } else if((level3 < 0.0) || (level3 > 1.0)) {
-      AIL_set_sample_channel_levels(_sample, levels, 2);
-    } else if((level4 < 0.0) || (level4 > 1.0)) {
-      AIL_set_sample_channel_levels(_sample, levels, 3);
-    } else if((level5 < 0.0) || (level5 > 1.0)) {
-      AIL_set_sample_channel_levels(_sample, levels, 4);
-    } else if((level6 < 0.0) || (level6 > 1.0)) {
-      AIL_set_sample_channel_levels(_sample, levels, 5);
-    } else if((level7 < 0.0) || (level7 > 1.0)) {
-      AIL_set_sample_channel_levels(_sample, levels, 6);
-    } else if((level8 < 0.0) || (level8 > 1.0)) {
-      AIL_set_sample_channel_levels(_sample, levels, 7);
-    } else if((level9 < 0.0) || (level9 > 1.0)) {
-      AIL_set_sample_channel_levels(_sample, levels, 8);
-    } else {
-      AIL_set_sample_channel_levels(_sample, levels, 9);
-    }
-  } else {
-    audio_warning("Warning: MilesAudioSample::set_speaker_levels only works for sounds that are currently playing");
-  }
-}
-
-/**
- * Called by the GlobalMilesManager when it is detected that this particular
- * sound has already stopped, and its sample handle will be recycled.
- */
-void MilesAudioSample::
-internal_stop() {
-  _sample = 0;
-  _sample_index = 0;
-}
-
-/**
- * This callback is made by Miles (possibly in a sub-thread) when the sample
- * finishes.
- */
-void AILCALLBACK MilesAudioSample::
-finish_callback(HSAMPLE sample) {
-  MilesAudioSample *self = (MilesAudioSample *)AIL_sample_user_data(sample, 0);
-  if (milesAudio_cat.is_debug()) {
-    milesAudio_cat.debug()
-      << "finished " << *self << "\n";
-  }
-  if (self->_manager == nullptr) {
-    return;
-  }
-  self->_manager->_sounds_finished = true;
-}
-
-/**
- * Sets the start time of an already allocated sample.
- */
-void MilesAudioSample::
-do_set_time(PN_stdfloat time) {
-  miles_audio_debug("do_set_time(time="<<time<<")");
-  nassertv(_sample != 0);
-
-  // Ensure we don't inadvertently run off the end of the sound.
-  PN_stdfloat max_time = length();
-  if (time > max_time) {
-    milesAudio_cat.warning()
-      << "set_time(" << time << ") requested for sound of length "
-      << max_time << "\n";
-    time = max_time;
-  }
-
-  S32 time_ms = (S32)(1000.0f * time);
-  AIL_set_sample_ms_position(_sample, time_ms);
-}
-
-#endif //]

+ 0 - 103
panda/src/audiotraits/milesAudioSample.h

@@ -1,103 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file milesAudioSample.h
- * @author skyler
- * @date 2001-06-06
- * Prior system by: cary
- */
-
-#ifndef MILESAUDIOSAMPLE_H
-#define MILESAUDIOSAMPLE_H
-
-#include "pandabase.h"
-#ifdef HAVE_RAD_MSS //[
-
-#include "milesAudioSound.h"
-#include "milesAudioManager.h"
-
-#include <mss.h>
-
-/**
- * A sound file, such as a WAV or MP3 file, that is preloaded into memory and
- * played from memory.
- */
-class EXPCL_MILES_AUDIO MilesAudioSample : public MilesAudioSound {
-private:
-  MilesAudioSample(MilesAudioManager *manager,
-                   MilesAudioManager::SoundData *sd,
-                   const std::string &file_name);
-
-public:
-  virtual ~MilesAudioSample();
-
-  virtual void play();
-  virtual void stop();
-
-  virtual PN_stdfloat get_time() const;
-
-  virtual void set_volume(PN_stdfloat volume=1.0f);
-  virtual void set_balance(PN_stdfloat balance_right=0.0f);
-  virtual void set_play_rate(PN_stdfloat play_rate=1.0f);
-
-  virtual PN_stdfloat length() const;
-
-  virtual AudioSound::SoundStatus status() const;
-
-  virtual void cleanup();
-  virtual void output(std::ostream &out) const;
-
-  // 3D spatialized sound support.  Spatialized sound was originally added for
-  // FMOD, so there are parts of the interface in the Miles implementation
-  // that are a little more awkward than they would be otherwise.
-  void set_3d_attributes(PN_stdfloat px, PN_stdfloat py, PN_stdfloat pz, PN_stdfloat vx, PN_stdfloat vy, PN_stdfloat vz);
-  void get_3d_attributes(PN_stdfloat *px, PN_stdfloat *py, PN_stdfloat *pz, PN_stdfloat *vx, PN_stdfloat *vy, PN_stdfloat *vz);
-  void set_3d_min_distance(PN_stdfloat dist);
-  PN_stdfloat get_3d_min_distance() const;
-  void set_3d_max_distance(PN_stdfloat dist);
-  PN_stdfloat get_3d_max_distance() const;
-
-  virtual PN_stdfloat get_speaker_level(int index);
-  virtual void set_speaker_levels(PN_stdfloat level1, PN_stdfloat level2=-1.0f, PN_stdfloat level3=-1.0f, PN_stdfloat level4=-1.0f, PN_stdfloat level5=-1.0f, PN_stdfloat level6=-1.0f, PN_stdfloat level7=-1.0f, PN_stdfloat level8=-1.0f, PN_stdfloat level9=-1.0f);
-
-private:
-  void internal_stop();
-  static void AILCALLBACK finish_callback(HSAMPLE sample);
-  void do_set_time(PN_stdfloat time);
-
-  PT(MilesAudioManager::SoundData) _sd;
-  HSAMPLE _sample;
-  size_t _sample_index;
-  S32 _original_playback_rate;
-
-public:
-  static TypeHandle get_class_type() {
-    return _type_handle;
-  }
-  static void init_type() {
-    MilesAudioSound::init_type();
-    register_type(_type_handle, "MilesAudioSample",
-                  MilesAudioSound::get_class_type());
-  }
-  virtual TypeHandle get_type() const {
-    return get_class_type();
-  }
-  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
-
-private:
-  static TypeHandle _type_handle;
-
-  friend class GlobalMilesManager;
-  friend class MilesAudioManager;
-};
-
-#include "milesAudioSample.I"
-
-#endif //]
-
-#endif  /* MILESAUDIOSAMPLE_H */

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