Explorar o código

fix on Roger's new server

Asad M. Zaman %!s(int64=20) %!d(string=hai) anos
pai
achega
1e0716a20b

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

@@ -143,7 +143,7 @@ get_datagram_iterator(DatagramIterator &di) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE CHANNEL_TYPE CConnectionRepository::
 INLINE CHANNEL_TYPE CConnectionRepository::
 get_msg_channel(int offset) const {
 get_msg_channel(int offset) const {
-   nassertr(offset < (int)_msg_channels.size(),0);
+   nassertr(offset < (int)_msg_channels.size(),0);
    return _msg_channels[offset];
    return _msg_channels[offset];
 }
 }
 
 

+ 545 - 544
direct/src/distributed/cConnectionRepository.cxx

@@ -1,544 +1,545 @@
-// Filename: cConnectionRepository.cxx
-// Created by:  drose (17May04)
-//
-////////////////////////////////////////////////////////////////////
-//
-// PANDA 3D SOFTWARE
-// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
-//
-// All use of this software is subject to the terms of the Panda 3d
-// Software license.  You should have received a copy of this license
-// along with this source code; you will also find a current copy of
-// the license at http://etc.cmu.edu/panda3d/docs/license/ .
-//
-// To contact the maintainers of this program write to
-// [email protected] .
-//
-////////////////////////////////////////////////////////////////////
-
-#include "cConnectionRepository.h"
-#include "dcmsgtypes.h"
-#include "dcClass.h"
-#include "dcPacker.h"
-
-#include "config_distributed.h"
-#include "httpChannel.h"
-#include "urlSpec.h"
-#include "datagramIterator.h"
-#include "throw_event.h"
-#include "pStatTimer.h"
-
-
-const string CConnectionRepository::_overflow_event_name = "CRDatagramOverflow";
-
-#ifndef CPPPARSER
-PStatCollector CConnectionRepository::_update_pcollector("App:Show code:readerPollTask:Update");
-#endif  // CPPPARSER
-
-////////////////////////////////////////////////////////////////////
-//     Function: CConnectionRepository::Constructor
-//       Access: Published
-//  Description: 
-////////////////////////////////////////////////////////////////////
-CConnectionRepository::
-CConnectionRepository() :
-#ifdef HAVE_PYTHON
-  _python_repository(NULL),
-#endif
-#ifdef HAVE_SSL
-  _http_conn(NULL),
-#endif
-#ifdef HAVE_NSPR
-  _cw(&_qcm, 0),
-  _qcr(&_qcm, 0),
-#endif
-  _client_datagram(true),
-  _simulated_disconnect(false),
-  _verbose(distributed_cat.is_spam()),
-//  _msg_channels(),
-  _msg_sender(0),
-  _msg_type(0)
-{
-#if defined(HAVE_NSPR) && defined(SIMULATE_NETWORK_DELAY)
-  if (min_lag != 0.0 || max_lag != 0.0) {
-    _qcr.start_delay(min_lag, max_lag);
-  }
-#endif
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: CConnectionRepository::Destructor
-//       Access: Published
-//  Description: 
-////////////////////////////////////////////////////////////////////
-CConnectionRepository::
-~CConnectionRepository() {
-  disconnect();
-}
-
-#ifdef HAVE_SSL
-////////////////////////////////////////////////////////////////////
-//     Function: CConnectionRepository::set_connection_http
-//       Access: Published
-//  Description: Once a connection has been established via the HTTP
-//               interface, gets the connection and uses it.  The
-//               supplied HTTPChannel object must have a connection
-//               available via get_connection().
-////////////////////////////////////////////////////////////////////
-void CConnectionRepository::
-set_connection_http(HTTPChannel *channel) {
-  disconnect();
-  nassertv(channel->is_connection_ready());
-  _http_conn = channel->get_connection();
-}
-#endif  // HAVE_SSL
-
-
-#ifdef HAVE_NSPR
-////////////////////////////////////////////////////////////////////
-//     Function: CConnectionRepository::try_connect_nspr
-//       Access: Published
-//  Description: Uses NSPR to try to connect to the server and port
-//               named in the indicated URL.  Returns true if
-//               successful, false otherwise.
-////////////////////////////////////////////////////////////////////
-bool CConnectionRepository::
-try_connect_nspr(const URLSpec &url) {
-  disconnect();
-
-  _nspr_conn = 
-    _qcm.open_TCP_client_connection(url.get_server(), url.get_port(),
-                                    game_server_timeout_ms);
-
-  if (_nspr_conn != (Connection *)NULL) {
-    _nspr_conn->set_no_delay(true);
-    _qcr.add_connection(_nspr_conn);
-    return true;
-  }
-
-  return false;
-}
-#endif  // HAVE_NSPR
-
-////////////////////////////////////////////////////////////////////
-//     Function: CConnectionRepository::check_datagram
-//       Access: Published
-//  Description: Returns true if a new datagram is available, false
-//               otherwise.  If the return value is true, the new
-//               datagram may be retrieved via get_datagram(), or
-//               preferably, with get_datagram_iterator() and
-//               get_msg_type().
-////////////////////////////////////////////////////////////////////
-bool CConnectionRepository::
-check_datagram() {
-  if (_simulated_disconnect) {
-    return false;
-  }
-  while (do_check_datagram()) {
-#ifndef NDEBUG
-    if (get_verbose()) {
-      describe_message(nout, "receive ", _dg.get_message());
-    }
-#endif  // NDEBUG
-
-    // Start breaking apart the datagram.
-    _di = DatagramIterator(_dg);
-
-    if (!_client_datagram) 
-    {
-      unsigned char  wc_cnt;
-      wc_cnt = _di.get_uint8();
-      for(unsigned char lp1 = 0; lp1 < wc_cnt; lp1++)
-      {
-            CHANNEL_TYPE  schan  = _di.get_uint64();
-            _msg_channels.push_back(schan);
-      }
-      _msg_sender = _di.get_uint64();
-      
-#ifdef HAVE_PYTHON
-      // For now, we need to stuff this field onto the Python
-      // structure, to support legacy code that expects to find it
-      // there.
-      if (_python_repository != (PyObject *)NULL) {
-        PyObject *value = PyLong_FromUnsignedLongLong(_msg_sender);
-        PyObject_SetAttrString(_python_repository, "msgSender", value);
-        Py_DECREF(value);
-      }
-#endif  // HAVE_PYTHON
-    }
-
-    _msg_type = _di.get_uint16();
-    // Is this a message that we can process directly?
-    switch (_msg_type) {
-#ifdef HAVE_PYTHON
-    case CLIENT_OBJECT_UPDATE_FIELD:
-    case STATESERVER_OBJECT_UPDATE_FIELD:
-      if (!handle_update_field()) {
-        return false;
-      }
-      break;
-#endif  // HAVE_PYTHON
-
-    default:
-      // Some unknown message; let the caller deal with it.
-      return true;
-    }
-  }
-
-  // No datagrams available.
-  return false;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: CConnectionRepository::is_connected
-//       Access: Published
-//  Description: Returns true if the connection to the gameserver is
-//               established and still good, false if we are not
-//               connected.  A false value means either (a) we never
-//               successfully connected, (b) we explicitly called
-//               disconnect(), or (c) we were connected, but the
-//               connection was spontaneously lost.
-////////////////////////////////////////////////////////////////////
-bool CConnectionRepository::
-is_connected() {
-#ifdef HAVE_NSPR
-  if (_nspr_conn) {
-    if (_qcm.reset_connection_available()) {
-      PT(Connection) reset_connection;
-      if (_qcm.get_reset_connection(reset_connection)) {
-        _qcm.close_connection(reset_connection);
-        if (reset_connection == _nspr_conn) {
-          // Whoops, lost our connection.
-          _nspr_conn = NULL;
-          return false;
-        }
-      }
-    }
-    return true;
-  }
-#endif  // HAVE_NSPR
-
-#ifdef HAVE_SSL
-  if (_http_conn) {
-    if (!_http_conn->is_closed()) {
-      return true;
-    }
-
-    // Connection lost.
-    delete _http_conn;
-    _http_conn = NULL;
-  }
-#endif  // HAVE_SSL
-
-  return false;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: CConnectionRepository::send_datagram
-//       Access: Published
-//  Description: Queues the indicated datagram for sending to the
-//               server.  It may not get send immediately if
-//               collect_tcp is in effect; call flush() to guarantee
-//               it is sent now.
-////////////////////////////////////////////////////////////////////
-bool CConnectionRepository::
-send_datagram(const Datagram &dg) {
-  if (_simulated_disconnect) {
-    distributed_cat.warning()
-      << "Unable to send datagram during simulated disconnect.\n";
-    return false;
-  }
-
-#ifndef NDEBUG
-  if (get_verbose()) {
-    describe_message(nout, "send ", dg.get_message());
-  }
-#endif  // NDEBUG
-
-#ifdef HAVE_NSPR
-  if (_nspr_conn) {
-    _cw.send(dg, _nspr_conn);
-    return true;
-  }
-#endif  // HAVE_NSPR
-
-#ifdef HAVE_SSL
-  if (_http_conn) {
-    if (!_http_conn->send_datagram(dg)) {
-      distributed_cat.warning()
-        << "Could not send datagram.\n";
-      return false;
-    }
-
-    return true;
-  }
-#endif  // HAVE_SSL
-
-  distributed_cat.warning()
-    << "Unable to send datagram after connection is closed.\n";
-  return false;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: CConnectionRepository::consider_flush
-//       Access: Published
-//  Description: Sends the most recently queued data if enough time
-//               has elapsed.  This only has meaning if
-//               set_collect_tcp() has been set to true.
-////////////////////////////////////////////////////////////////////
-bool CConnectionRepository::
-consider_flush() {
-  if (_simulated_disconnect) {
-    return false;
-  }
-
-#ifdef HAVE_NSPR
-  if (_nspr_conn) {
-    return _nspr_conn->consider_flush();
-  }
-#endif  // HAVE_NSPR
-
-#ifdef HAVE_SSL
-  if (_http_conn) {
-    return _http_conn->consider_flush();
-  }
-#endif  // HAVE_SSL
-
-  return false;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: CConnectionRepository::flush
-//       Access: Published
-//  Description: Sends the most recently queued data now.  This only
-//               has meaning if set_collect_tcp() has been set to
-//               true.
-////////////////////////////////////////////////////////////////////
-bool CConnectionRepository::
-flush() {
-  if (_simulated_disconnect) {
-    return false;
-  }
-
-  #ifdef HAVE_NSPR
-  if (_nspr_conn) {
-    return _nspr_conn->flush();
-  }
-  #endif  // HAVE_NSPR
-
-  #ifdef HAVE_SSL
-  if (_http_conn) {
-    return _http_conn->flush();
-  }
-  #endif  // HAVE_SSL
-
-  return false;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: CConnectionRepository::disconnect
-//       Access: Published
-//  Description: Closes the connection to the server.
-////////////////////////////////////////////////////////////////////
-void CConnectionRepository::
-disconnect() {
-  #ifdef HAVE_NSPR
-  if (_nspr_conn) {
-    _qcm.close_connection(_nspr_conn);
-    _nspr_conn = NULL;
-  }
-  #endif  // HAVE_NSPR
-
-  #ifdef HAVE_SSL
-  if (_http_conn) {
-    _http_conn->close();
-    delete _http_conn;
-    _http_conn = NULL;
-  }
-  #endif  // HAVE_SSL
-
-  _simulated_disconnect = false;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: CConnectionRepository::do_check_datagram
-//       Access: Private
-//  Description: The private implementation of check_datagram(), this
-//               gets one datagram if it is available.
-////////////////////////////////////////////////////////////////////
-bool CConnectionRepository::
-do_check_datagram() {
-  #ifdef HAVE_NSPR
-  if (_nspr_conn) {
-    _nspr_conn->consider_flush();
-    if (_qcr.get_overflow_flag()) {
-      throw_event(get_overflow_event_name());
-      _qcr.reset_overflow_flag();
-    }
-    return (_qcr.data_available() && _qcr.get_data(_dg));
-  }
-  #endif  // HAVE_NSPR
-
-  #ifdef HAVE_SSL
-  if (_http_conn) {
-    _http_conn->consider_flush();
-    return _http_conn->receive_datagram(_dg);
-  }
-  #endif  // HAVE_SSL
-
-  return false;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: CConnectionRepository::handle_update_field
-//       Access: Private
-//  Description: Directly handles an update message on a field.
-//               Python never touches the datagram; it just gets its
-//               distributed method called with the appropriate
-//               parameters.  Returns true if everything is ok, false
-//               if there was an error processing the field's update
-//               method.
-////////////////////////////////////////////////////////////////////
-bool CConnectionRepository::
-handle_update_field() {
-  #ifdef HAVE_PYTHON
-  PStatTimer timer(_update_pcollector);
-  unsigned int do_id = _di.get_uint32();
-  if (_python_repository != (PyObject *)NULL) {
-    PyObject *doId2do =
-      PyObject_GetAttrString(_python_repository, "doId2do");
-    nassertr(doId2do != NULL, false);
-
-    #ifdef USE_PYTHON_2_2_OR_EARLIER
-    PyObject *doId = PyInt_FromLong(do_id);
-    #else
-    PyObject *doId = PyLong_FromUnsignedLong(do_id);
-    #endif
-    PyObject *distobj = PyDict_GetItem(doId2do, doId);
-    Py_DECREF(doId);
-    Py_DECREF(doId2do);
-
-    if (distobj != NULL) {
-      PyObject *dclass_obj = PyObject_GetAttrString(distobj, "dclass");
-      nassertr(dclass_obj != NULL, false);
-
-      PyObject *dclass_this = PyObject_GetAttrString(dclass_obj, "this");
-      Py_DECREF(dclass_obj);
-      nassertr(dclass_this != NULL, false);
-
-      DCClass *dclass = (DCClass *)PyInt_AsLong(dclass_this);
-      Py_DECREF(dclass_this);
-
-      // It's a good idea to ensure the reference count to distobj is
-      // raised while we call the update method--otherwise, the update
-      // method might get into trouble if it tried to delete the
-      // object from the doId2do map.
-      Py_INCREF(distobj);
-      dclass->receive_update(distobj, _di); 
-      Py_DECREF(distobj);
-      
-      if (PyErr_Occurred()) {
-        return false;
-      }
-    }
-  }
-  #endif  // HAVE_PYTHON  
-
-  return true;
-}
-
-#ifndef NDEBUG
-////////////////////////////////////////////////////////////////////
-//     Function: CConnectionRepository::describe_message
-//       Access: Private
-//  Description: Unpacks the message and reformats it for user
-//               consumption, writing a description on the indicated
-//               output stream.
-////////////////////////////////////////////////////////////////////
-void CConnectionRepository::
-describe_message(ostream &out, const string &prefix, 
-                 const string &message_data) const {
-  DCPacker packer;
-  packer.set_unpack_data(message_data);
-  CHANNEL_TYPE do_id;
-  int msg_type;
-  bool is_update = false;
-
-  if (!_client_datagram) 
-  {
-    unsigned char mcnt = packer.raw_unpack_uint8();
-    for( ;mcnt > 0; mcnt--)
-        packer.RAW_UNPACK_CHANNEL();  // msg_channel
-
-    packer.RAW_UNPACK_CHANNEL();  // msg_sender
-    msg_type = packer.raw_unpack_uint16();
-    is_update = (msg_type == STATESERVER_OBJECT_UPDATE_FIELD);
-    
-  } else {
-    msg_type = packer.raw_unpack_uint16();
-    is_update = (msg_type == CLIENT_OBJECT_UPDATE_FIELD);
-  }
-
-  if (!is_update) {
-    out << prefix << "message " << msg_type << "\n";
-
-  } else {
-    // It's an update message.  Figure out what dclass the object is
-    // based on its doId, so we can decode the rest of the message.
-    do_id = packer.raw_unpack_uint32();
-    DCClass *dclass = NULL;
-
-    #ifdef HAVE_PYTHON
-    if (_python_repository != (PyObject *)NULL) {
-      PyObject *doId2do =
-        PyObject_GetAttrString(_python_repository, "doId2do");
-      nassertv(doId2do != NULL);
-
-      #ifdef USE_PYTHON_2_2_OR_EARLIER
-      PyObject *doId = PyInt_FromLong(do_id);
-      #else
-      PyObject *doId = PyLong_FromUnsignedLong(do_id);
-      #endif
-      PyObject *distobj = PyDict_GetItem(doId2do, doId);
-      Py_DECREF(doId);
-      Py_DECREF(doId2do);
-
-      if (distobj != NULL) {
-        PyObject *dclass_obj = PyObject_GetAttrString(distobj, "dclass");
-        nassertv(dclass_obj != NULL);
-
-        PyObject *dclass_this = PyObject_GetAttrString(dclass_obj, "this");
-        Py_DECREF(dclass_obj);
-        nassertv(dclass_this != NULL);
-        
-        dclass = (DCClass *)PyInt_AsLong(dclass_this);
-        Py_DECREF(dclass_this);
-      }
-    }
-    #endif  // HAVE_PYTHON  
-
-    int field_id = packer.raw_unpack_uint16();
-
-    if (dclass == (DCClass *)NULL) {
-      out << prefix << "update for unknown object " << do_id 
-          << ", field " << field_id << "\n";
-
-    } else {
-      out << prefix << "update for " << dclass->get_name()
-          << " " << do_id << ": ";
-      DCField *field = dclass->get_field_by_index(field_id);
-      if (field == (DCField *)NULL) {
-        out << "unknown field " << field_id << "\n";
-        
-      } else {
-        out << field->get_name();
-        packer.begin_unpack(field);
-        packer.unpack_and_format(out);
-        packer.end_unpack();
-        out << "\n";
-      }
-    }
-  }
-}
-#endif  // NDEBUG
-
+// Filename: cConnectionRepository.cxx
+// Created by:  drose (17May04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "cConnectionRepository.h"
+#include "dcmsgtypes.h"
+#include "dcClass.h"
+#include "dcPacker.h"
+
+#include "config_distributed.h"
+#include "httpChannel.h"
+#include "urlSpec.h"
+#include "datagramIterator.h"
+#include "throw_event.h"
+#include "pStatTimer.h"
+
+
+const string CConnectionRepository::_overflow_event_name = "CRDatagramOverflow";
+
+#ifndef CPPPARSER
+PStatCollector CConnectionRepository::_update_pcollector("App:Show code:readerPollTask:Update");
+#endif  // CPPPARSER
+
+////////////////////////////////////////////////////////////////////
+//     Function: CConnectionRepository::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+CConnectionRepository::
+CConnectionRepository() :
+#ifdef HAVE_PYTHON
+  _python_repository(NULL),
+#endif
+#ifdef HAVE_SSL
+  _http_conn(NULL),
+#endif
+#ifdef HAVE_NSPR
+  _cw(&_qcm, 0),
+  _qcr(&_qcm, 0),
+#endif
+  _client_datagram(true),
+  _simulated_disconnect(false),
+  _verbose(distributed_cat.is_spam()),
+//  _msg_channels(),
+  _msg_sender(0),
+  _msg_type(0)
+{
+#if defined(HAVE_NSPR) && defined(SIMULATE_NETWORK_DELAY)
+  if (min_lag != 0.0 || max_lag != 0.0) {
+    _qcr.start_delay(min_lag, max_lag);
+  }
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CConnectionRepository::Destructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+CConnectionRepository::
+~CConnectionRepository() {
+  disconnect();
+}
+
+#ifdef HAVE_SSL
+////////////////////////////////////////////////////////////////////
+//     Function: CConnectionRepository::set_connection_http
+//       Access: Published
+//  Description: Once a connection has been established via the HTTP
+//               interface, gets the connection and uses it.  The
+//               supplied HTTPChannel object must have a connection
+//               available via get_connection().
+////////////////////////////////////////////////////////////////////
+void CConnectionRepository::
+set_connection_http(HTTPChannel *channel) {
+  disconnect();
+  nassertv(channel->is_connection_ready());
+  _http_conn = channel->get_connection();
+}
+#endif  // HAVE_SSL
+
+
+#ifdef HAVE_NSPR
+////////////////////////////////////////////////////////////////////
+//     Function: CConnectionRepository::try_connect_nspr
+//       Access: Published
+//  Description: Uses NSPR to try to connect to the server and port
+//               named in the indicated URL.  Returns true if
+//               successful, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool CConnectionRepository::
+try_connect_nspr(const URLSpec &url) {
+  disconnect();
+
+  _nspr_conn = 
+    _qcm.open_TCP_client_connection(url.get_server(), url.get_port(),
+                                    game_server_timeout_ms);
+
+  if (_nspr_conn != (Connection *)NULL) {
+    _nspr_conn->set_no_delay(true);
+    _qcr.add_connection(_nspr_conn);
+    return true;
+  }
+
+  return false;
+}
+#endif  // HAVE_NSPR
+
+////////////////////////////////////////////////////////////////////
+//     Function: CConnectionRepository::check_datagram
+//       Access: Published
+//  Description: Returns true if a new datagram is available, false
+//               otherwise.  If the return value is true, the new
+//               datagram may be retrieved via get_datagram(), or
+//               preferably, with get_datagram_iterator() and
+//               get_msg_type().
+////////////////////////////////////////////////////////////////////
+bool CConnectionRepository::
+check_datagram() {
+  if (_simulated_disconnect) {
+    return false;
+  }
+  while (do_check_datagram()) {
+#ifndef NDEBUG
+    if (get_verbose()) {
+      describe_message(nout, "receive ", _dg.get_message());
+    }
+#endif  // NDEBUG
+
+    // Start breaking apart the datagram.
+    _di = DatagramIterator(_dg);
+
+    if (!_client_datagram) 
+    {
+      unsigned char  wc_cnt;
+      wc_cnt = _di.get_uint8();
+      _msg_channels.clear();
+      for(unsigned char lp1 = 0; lp1 < wc_cnt; lp1++)
+      {
+            CHANNEL_TYPE  schan  = _di.get_uint64();
+            _msg_channels.push_back(schan);
+      }
+      _msg_sender = _di.get_uint64();
+      
+#ifdef HAVE_PYTHON
+      // For now, we need to stuff this field onto the Python
+      // structure, to support legacy code that expects to find it
+      // there.
+      if (_python_repository != (PyObject *)NULL) {
+        PyObject *value = PyLong_FromUnsignedLongLong(_msg_sender);
+        PyObject_SetAttrString(_python_repository, "msgSender", value);
+        Py_DECREF(value);
+      }
+#endif  // HAVE_PYTHON
+    }
+
+    _msg_type = _di.get_uint16();
+    // Is this a message that we can process directly?
+    switch (_msg_type) {
+#ifdef HAVE_PYTHON
+    case CLIENT_OBJECT_UPDATE_FIELD:
+    case STATESERVER_OBJECT_UPDATE_FIELD:
+      if (!handle_update_field()) {
+        return false;
+      }
+      break;
+#endif  // HAVE_PYTHON
+
+    default:
+      // Some unknown message; let the caller deal with it.
+      return true;
+    }
+  }
+
+  // No datagrams available.
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CConnectionRepository::is_connected
+//       Access: Published
+//  Description: Returns true if the connection to the gameserver is
+//               established and still good, false if we are not
+//               connected.  A false value means either (a) we never
+//               successfully connected, (b) we explicitly called
+//               disconnect(), or (c) we were connected, but the
+//               connection was spontaneously lost.
+////////////////////////////////////////////////////////////////////
+bool CConnectionRepository::
+is_connected() {
+#ifdef HAVE_NSPR
+  if (_nspr_conn) {
+    if (_qcm.reset_connection_available()) {
+      PT(Connection) reset_connection;
+      if (_qcm.get_reset_connection(reset_connection)) {
+        _qcm.close_connection(reset_connection);
+        if (reset_connection == _nspr_conn) {
+          // Whoops, lost our connection.
+          _nspr_conn = NULL;
+          return false;
+        }
+      }
+    }
+    return true;
+  }
+#endif  // HAVE_NSPR
+
+#ifdef HAVE_SSL
+  if (_http_conn) {
+    if (!_http_conn->is_closed()) {
+      return true;
+    }
+
+    // Connection lost.
+    delete _http_conn;
+    _http_conn = NULL;
+  }
+#endif  // HAVE_SSL
+
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CConnectionRepository::send_datagram
+//       Access: Published
+//  Description: Queues the indicated datagram for sending to the
+//               server.  It may not get send immediately if
+//               collect_tcp is in effect; call flush() to guarantee
+//               it is sent now.
+////////////////////////////////////////////////////////////////////
+bool CConnectionRepository::
+send_datagram(const Datagram &dg) {
+  if (_simulated_disconnect) {
+    distributed_cat.warning()
+      << "Unable to send datagram during simulated disconnect.\n";
+    return false;
+  }
+
+#ifndef NDEBUG
+  if (get_verbose()) {
+    describe_message(nout, "send ", dg.get_message());
+  }
+#endif  // NDEBUG
+
+#ifdef HAVE_NSPR
+  if (_nspr_conn) {
+    _cw.send(dg, _nspr_conn);
+    return true;
+  }
+#endif  // HAVE_NSPR
+
+#ifdef HAVE_SSL
+  if (_http_conn) {
+    if (!_http_conn->send_datagram(dg)) {
+      distributed_cat.warning()
+        << "Could not send datagram.\n";
+      return false;
+    }
+
+    return true;
+  }
+#endif  // HAVE_SSL
+
+  distributed_cat.warning()
+    << "Unable to send datagram after connection is closed.\n";
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CConnectionRepository::consider_flush
+//       Access: Published
+//  Description: Sends the most recently queued data if enough time
+//               has elapsed.  This only has meaning if
+//               set_collect_tcp() has been set to true.
+////////////////////////////////////////////////////////////////////
+bool CConnectionRepository::
+consider_flush() {
+  if (_simulated_disconnect) {
+    return false;
+  }
+
+#ifdef HAVE_NSPR
+  if (_nspr_conn) {
+    return _nspr_conn->consider_flush();
+  }
+#endif  // HAVE_NSPR
+
+#ifdef HAVE_SSL
+  if (_http_conn) {
+    return _http_conn->consider_flush();
+  }
+#endif  // HAVE_SSL
+
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CConnectionRepository::flush
+//       Access: Published
+//  Description: Sends the most recently queued data now.  This only
+//               has meaning if set_collect_tcp() has been set to
+//               true.
+////////////////////////////////////////////////////////////////////
+bool CConnectionRepository::
+flush() {
+  if (_simulated_disconnect) {
+    return false;
+  }
+
+  #ifdef HAVE_NSPR
+  if (_nspr_conn) {
+    return _nspr_conn->flush();
+  }
+  #endif  // HAVE_NSPR
+
+  #ifdef HAVE_SSL
+  if (_http_conn) {
+    return _http_conn->flush();
+  }
+  #endif  // HAVE_SSL
+
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CConnectionRepository::disconnect
+//       Access: Published
+//  Description: Closes the connection to the server.
+////////////////////////////////////////////////////////////////////
+void CConnectionRepository::
+disconnect() {
+  #ifdef HAVE_NSPR
+  if (_nspr_conn) {
+    _qcm.close_connection(_nspr_conn);
+    _nspr_conn = NULL;
+  }
+  #endif  // HAVE_NSPR
+
+  #ifdef HAVE_SSL
+  if (_http_conn) {
+    _http_conn->close();
+    delete _http_conn;
+    _http_conn = NULL;
+  }
+  #endif  // HAVE_SSL
+
+  _simulated_disconnect = false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CConnectionRepository::do_check_datagram
+//       Access: Private
+//  Description: The private implementation of check_datagram(), this
+//               gets one datagram if it is available.
+////////////////////////////////////////////////////////////////////
+bool CConnectionRepository::
+do_check_datagram() {
+  #ifdef HAVE_NSPR
+  if (_nspr_conn) {
+    _nspr_conn->consider_flush();
+    if (_qcr.get_overflow_flag()) {
+      throw_event(get_overflow_event_name());
+      _qcr.reset_overflow_flag();
+    }
+    return (_qcr.data_available() && _qcr.get_data(_dg));
+  }
+  #endif  // HAVE_NSPR
+
+  #ifdef HAVE_SSL
+  if (_http_conn) {
+    _http_conn->consider_flush();
+    return _http_conn->receive_datagram(_dg);
+  }
+  #endif  // HAVE_SSL
+
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CConnectionRepository::handle_update_field
+//       Access: Private
+//  Description: Directly handles an update message on a field.
+//               Python never touches the datagram; it just gets its
+//               distributed method called with the appropriate
+//               parameters.  Returns true if everything is ok, false
+//               if there was an error processing the field's update
+//               method.
+////////////////////////////////////////////////////////////////////
+bool CConnectionRepository::
+handle_update_field() {
+  #ifdef HAVE_PYTHON
+  PStatTimer timer(_update_pcollector);
+  unsigned int do_id = _di.get_uint32();
+  if (_python_repository != (PyObject *)NULL) {
+    PyObject *doId2do =
+      PyObject_GetAttrString(_python_repository, "doId2do");
+    nassertr(doId2do != NULL, false);
+
+    #ifdef USE_PYTHON_2_2_OR_EARLIER
+    PyObject *doId = PyInt_FromLong(do_id);
+    #else
+    PyObject *doId = PyLong_FromUnsignedLong(do_id);
+    #endif
+    PyObject *distobj = PyDict_GetItem(doId2do, doId);
+    Py_DECREF(doId);
+    Py_DECREF(doId2do);
+
+    if (distobj != NULL) {
+      PyObject *dclass_obj = PyObject_GetAttrString(distobj, "dclass");
+      nassertr(dclass_obj != NULL, false);
+
+      PyObject *dclass_this = PyObject_GetAttrString(dclass_obj, "this");
+      Py_DECREF(dclass_obj);
+      nassertr(dclass_this != NULL, false);
+
+      DCClass *dclass = (DCClass *)PyInt_AsLong(dclass_this);
+      Py_DECREF(dclass_this);
+
+      // It's a good idea to ensure the reference count to distobj is
+      // raised while we call the update method--otherwise, the update
+      // method might get into trouble if it tried to delete the
+      // object from the doId2do map.
+      Py_INCREF(distobj);
+      dclass->receive_update(distobj, _di); 
+      Py_DECREF(distobj);
+      
+      if (PyErr_Occurred()) {
+        return false;
+      }
+    }
+  }
+  #endif  // HAVE_PYTHON  
+
+  return true;
+}
+
+#ifndef NDEBUG
+////////////////////////////////////////////////////////////////////
+//     Function: CConnectionRepository::describe_message
+//       Access: Private
+//  Description: Unpacks the message and reformats it for user
+//               consumption, writing a description on the indicated
+//               output stream.
+////////////////////////////////////////////////////////////////////
+void CConnectionRepository::
+describe_message(ostream &out, const string &prefix, 
+                 const string &message_data) const {
+  DCPacker packer;
+  packer.set_unpack_data(message_data);
+  CHANNEL_TYPE do_id;
+  int msg_type;
+  bool is_update = false;
+
+  if (!_client_datagram) 
+  {
+    unsigned char mcnt = packer.raw_unpack_uint8();
+    for( ;mcnt > 0; mcnt--)
+        packer.RAW_UNPACK_CHANNEL();  // msg_channel
+
+    packer.RAW_UNPACK_CHANNEL();  // msg_sender
+    msg_type = packer.raw_unpack_uint16();
+    is_update = (msg_type == STATESERVER_OBJECT_UPDATE_FIELD);
+    
+  } else {
+    msg_type = packer.raw_unpack_uint16();
+    is_update = (msg_type == CLIENT_OBJECT_UPDATE_FIELD);
+  }
+
+  if (!is_update) {
+    out << prefix << "message " << msg_type << "\n";
+
+  } else {
+    // It's an update message.  Figure out what dclass the object is
+    // based on its doId, so we can decode the rest of the message.
+    do_id = packer.raw_unpack_uint32();
+    DCClass *dclass = NULL;
+
+    #ifdef HAVE_PYTHON
+    if (_python_repository != (PyObject *)NULL) {
+      PyObject *doId2do =
+        PyObject_GetAttrString(_python_repository, "doId2do");
+      nassertv(doId2do != NULL);
+
+      #ifdef USE_PYTHON_2_2_OR_EARLIER
+      PyObject *doId = PyInt_FromLong(do_id);
+      #else
+      PyObject *doId = PyLong_FromUnsignedLong(do_id);
+      #endif
+      PyObject *distobj = PyDict_GetItem(doId2do, doId);
+      Py_DECREF(doId);
+      Py_DECREF(doId2do);
+
+      if (distobj != NULL) {
+        PyObject *dclass_obj = PyObject_GetAttrString(distobj, "dclass");
+        nassertv(dclass_obj != NULL);
+
+        PyObject *dclass_this = PyObject_GetAttrString(dclass_obj, "this");
+        Py_DECREF(dclass_obj);
+        nassertv(dclass_this != NULL);
+        
+        dclass = (DCClass *)PyInt_AsLong(dclass_this);
+        Py_DECREF(dclass_this);
+      }
+    }
+    #endif  // HAVE_PYTHON  
+
+    int field_id = packer.raw_unpack_uint16();
+
+    if (dclass == (DCClass *)NULL) {
+      out << prefix << "update for unknown object " << do_id 
+          << ", field " << field_id << "\n";
+
+    } else {
+      out << prefix << "update for " << dclass->get_name()
+          << " " << do_id << ": ";
+      DCField *field = dclass->get_field_by_index(field_id);
+      if (field == (DCField *)NULL) {
+        out << "unknown field " << field_id << "\n";
+        
+      } else {
+        out << field->get_name();
+        packer.begin_unpack(field);
+        packer.unpack_and_format(out);
+        packer.end_unpack();
+        out << "\n";
+      }
+    }
+  }
+}
+#endif  // NDEBUG
+