Browse Source

CConnectionRepository

David Rose 21 years ago
parent
commit
ec535f800a

+ 1 - 1
direct/metalibs/direct/Sources.pp

@@ -8,7 +8,7 @@
 #define BUILDING_DLL BUILDING_DIRECT
 
 #define COMPONENT_LIBS \
-  directbase dcparser showbase deadrec directd interval
+  directbase dcparser showbase deadrec directd interval distributed
 
 #define OTHER_LIBS \
   panda:m \

+ 0 - 2
direct/src/cluster/ClusterServer.py

@@ -1,5 +1,3 @@
-"""ServerRepository module: contains the ServerRepository class"""
-
 from PandaModules import *
 from ClusterMsgs import *
 from MsgTypes import *

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

@@ -53,7 +53,7 @@ class ClientRepository(ConnectionRepository.ConnectionRepository):
         self.relatedObjectMgr.abortAllRequests()
 
     def sendDisconnect(self):
-        if self.tcpConn:
+        if self.isConnected():
             # Tell the game server that we're going:
             datagram = PyDatagram()
             # Add message type
@@ -287,8 +287,6 @@ class ClientRepository(ConnectionRepository.ConnectionRepository):
         # Get the DO Id
         doId = di.getArg(STUint32)
         #print("Updating " + str(doId))
-        if self.rsDoReport:
-            self.rsUpdateObjs[doId] = self.rsUpdateObjs.get(doId, 0) + 1
         # Find the DO
             
         do = self.doId2do.get(doId)
@@ -398,8 +396,7 @@ class ClientRepository(ConnectionRepository.ConnectionRepository):
         self.lastHeartbeat = globalClock.getRealTime()
         # This is important enough to consider flushing immediately
         # (particularly if we haven't run readerPollTask recently).
-        if self.tcpConn:
-            self.tcpConn.considerFlush()
+        self.considerFlush()
 
     def considerHeartbeat(self):
         """Send a heartbeat message if we haven't sent one recently."""

+ 49 - 165
direct/src/distributed/ConnectionRepository.py

@@ -6,7 +6,7 @@ from PyDatagram import PyDatagram
 
 import types
 
-class ConnectionRepository(DirectObject.DirectObject):
+class ConnectionRepository(DirectObject.DirectObject, CConnectionRepository):
     """
     This is a base class for things that know how to establish a
     connection (and exchange datagrams) with a gameserver.  This
@@ -19,6 +19,7 @@ class ConnectionRepository(DirectObject.DirectObject):
 
     def __init__(self, config):
         DirectObject.DirectObject.__init__(self)
+        CConnectionRepository.__init__(self)
 
         self.config = config
         
@@ -40,22 +41,11 @@ class ConnectionRepository(DirectObject.DirectObject):
         # proxy is in place, but the NSPR interface if we don't have a
         # proxy.
         self.connectMethod = self.config.GetString('connect-method', 'default')
-        
         self.connectHttp = None
         self.http = None
-        self.qcm = None
-        self.cw = None
 
-        self.tcpConn = None
         self.recorder = None
 
-        # Reader statistics
-        self.rsDatagramCount = 0
-        self.rsUpdateObjs = {}
-        self.rsLastUpdate = 0
-        self.rsDoReport = self.config.GetBool('reader-statistics', 0)
-        self.rsUpdateInterval = self.config.GetDouble('reader-statistics-interval', 10)
-
     def readDCFile(self, dcFileNames = None):
 
         """ Reads in the dc files listed in dcFileNames, or if
@@ -162,21 +152,21 @@ class ConnectionRepository(DirectObject.DirectObject):
         known.
         """
 
-        if self.recorder and self.recorder.isPlaying():
+##         if self.recorder and self.recorder.isPlaying():
 
-            # If we have a recorder and it's already in playback mode,
-            # don't actually attempt to connect to a gameserver since
-            # we don't need to.  Just let it play back the data.
-            self.notify.info("Not connecting to gameserver; using playback data instead.")
+##             # If we have a recorder and it's already in playback mode,
+##             # don't actually attempt to connect to a gameserver since
+##             # we don't need to.  Just let it play back the data.
+##             self.notify.info("Not connecting to gameserver; using playback data instead.")
 
-            self.connectHttp = 1
-            self.tcpConn = SocketStreamRecorder()
-            self.recorder.addRecorder('gameserver', self.tcpConn)
+##             self.connectHttp = 1
+##             self.tcpConn = SocketStreamRecorder()
+##             self.recorder.addRecorder('gameserver', self.tcpConn)
             
-            self.startReaderPollTask()
-            if successCallback:
-                successCallback(*successArgs)
-            return
+##             self.startReaderPollTask()
+##             if successCallback:
+##                 successCallback(*successArgs)
+##             return
 
         hasProxy = 0
         if self.checkHttp():
@@ -212,31 +202,10 @@ class ConnectionRepository(DirectObject.DirectObject):
                                      failureCallback, failureArgs)
 
         else:
-            if self.qcm == None:
-                self.qcm = QueuedConnectionManager()
-
-            if self.cw == None:
-                self.cw = ConnectionWriter(self.qcm, 0)
-                self.qcr = QueuedConnectionReader(self.qcm, 0)
-                minLag = self.config.GetFloat('min-lag', 0.)
-                maxLag = self.config.GetFloat('max-lag', 0.)
-                if minLag or maxLag:
-                    self.qcr.startDelay(minLag, maxLag)
-
-            # A big old 20 second timeout.
-            gameServerTimeoutMs = self.config.GetInt("game-server-timeout-ms",
-                                                     20000)
-
             # Try each of the servers in turn.
             for url in serverList:
                 self.notify.info("Connecting to %s via NSPR interface." % (url.cStr()))
-                self.tcpConn = self.qcm.openTCPClientConnection(
-                    url.getServer(), url.getPort(),
-                    gameServerTimeoutMs)
-
-                if self.tcpConn:
-                    self.tcpConn.setNoDelay(1)
-                    self.qcr.addConnection(self.tcpConn)
+                if self.tryConnectNspr(url):
                     self.startReaderPollTask()
                     if successCallback:
                         successCallback(*successArgs)
@@ -250,37 +219,31 @@ class ConnectionRepository(DirectObject.DirectObject):
         """Closes the previously-established connection.
         """
         self.notify.info("Closing connection to server.")
-        if self.tcpConn != None:
-            if self.connectHttp:
-                self.tcpConn.close()
-            else: 
-                self.qcm.closeConnection(self.tcpConn)
-            self.tcpConn = None
+        CConnectionRepository.disconnect(self)
         self.stopReaderPollTask()
                     
     def httpConnectCallback(self, ch, serverList, serverIndex,
                             successCallback, successArgs,
                             failureCallback, failureArgs):
         if ch.isConnectionReady():
-            self.tcpConn = ch.getConnection()
-            self.tcpConn.userManagesMemory = 1
-
-            if self.recorder:
-                # If we have a recorder, we wrap the connect inside a
-                # SocketStreamRecorder, which will trap incoming data
-                # when the recorder is set to record mode.  (It will
-                # also play back data when the recorder is in playback
-                # mode, but in that case we never get this far in the
-                # code, since we just create an empty
-                # SocketStreamRecorder without actually connecting to
-                # the gameserver.)
-                stream = SocketStreamRecorder(self.tcpConn, 1)
-                self.recorder.addRecorder('gameserver', stream)
-
-                # In this case, we pass ownership of the original
-                # connection to the SocketStreamRecorder object.
-                self.tcpConn.userManagesMemory = 0
-                self.tcpConn = stream
+            self.setConnectionHttp(ch)
+
+##             if self.recorder:
+##                 # If we have a recorder, we wrap the connect inside a
+##                 # SocketStreamRecorder, which will trap incoming data
+##                 # when the recorder is set to record mode.  (It will
+##                 # also play back data when the recorder is in playback
+##                 # mode, but in that case we never get this far in the
+##                 # code, since we just create an empty
+##                 # SocketStreamRecorder without actually connecting to
+##                 # the gameserver.)
+##                 stream = SocketStreamRecorder(self.tcpConn, 1)
+##                 self.recorder.addRecorder('gameserver', stream)
+
+##                 # In this case, we pass ownership of the original
+##                 # connection to the SocketStreamRecorder object.
+##                 self.tcpConn.userManagesMemory = 0
+##                 self.tcpConn = stream
             
             self.startReaderPollTask()
             if successCallback:
@@ -334,68 +297,17 @@ class ConnectionRepository(DirectObject.DirectObject):
         return Task.cont
 
     def readerPollOnce(self):
-        # we simulate the network plug being pulled by setting tcpConn
-        # to None; enforce that condition
-        if not self.tcpConn:
-            return 0
-
-        # Make sure any recently-sent datagrams are flushed when the
-        # time expires, if we're in collect-tcp mode.
-        self.tcpConn.considerFlush()
-
-        if self.rsDoReport:
-            self.reportReaderStatistics()
-        
-        if self.connectHttp:
-            datagram = PyDatagram()
-            if self.tcpConn.receiveDatagram(datagram):
-                if self.rsDoReport:
-                    self.rsDatagramCount += 1
-                self.handleDatagram(datagram)
-                return 1
-
-            # Unable to receive a datagram: did we lose the connection?
-            if self.tcpConn.isClosed():
-                self.tcpConn = None
-                self.stopReaderPollTask()
-                self.lostConnection()
-            return 0
-        
-        else:
-            self.ensureValidConnection()
-            if self.qcr.dataAvailable():
-                datagram = NetDatagram()
-                if self.qcr.getData(datagram):
-                    if self.rsDoReport:
-                        self.rsDatagramCount += 1
-                    self.handleDatagram(datagram)
-                    return 1
-            return 0
-
-    def flush(self):
-        # Ensure the latest has been sent to the server.
-        if self.tcpConn:
-            self.tcpConn.flush()
-
-    def ensureValidConnection(self):
-        # Was the connection reset?
-        if self.connectHttp:
-            pass
-        else:
-            if self.qcm.resetConnectionAvailable():
-                resetConnectionPointer = PointerToConnection()
-                if self.qcm.getResetConnection(resetConnectionPointer):
-                    resetConn = resetConnectionPointer.p()
-                    self.qcm.closeConnection(resetConn)
-                    # if we've simulated a network plug pull, restore the
-                    # simulated plug
-                    self.restoreNetworkPlug()
-                    if self.tcpConn.this == resetConn.this:
-                        self.tcpConn = None
-                        self.stopReaderPollTask()
-                        self.lostConnection()
-                    else:
-                        self.notify.warning("Lost unknown connection.")
+        if self.checkDatagram():
+            dg = PyDatagram()
+            self.getDatagram(dg)
+            self.handleDatagram(dg)
+            return 1
+
+        # Unable to receive a datagram: did we lose the connection?
+        if not self.isConnected():
+            self.stopReaderPollTask()
+            self.lostConnection()
+        return 0
 
     def lostConnection(self):
         # This should be overrided by a derived class to handle an
@@ -407,50 +319,22 @@ class ConnectionRepository(DirectObject.DirectObject):
         # inherit from it need to make their own handleDatagram method
         pass
 
-    def reportReaderStatistics(self):
-        now = globalClock.getRealTime()
-        if now - self.rsLastUpdate < self.rsUpdateInterval:
-            return
-
-        self.rsLastUpdate = now
-        self.notify.info("Received %s datagrams" % (self.rsDatagramCount))
-        if self.rsUpdateObjs:
-            self.notify.info("Updates: %s" % (self.rsUpdateObjs))
-
-        self.rsDatagramCount = 0
-        self.rsUpdateObjs = {}
-
     def send(self, datagram):
-        #if self.notify.getDebug():
-        #    print "ConnectionRepository sending datagram:"
-        #    datagram.dumpHex(ostream)
-
-        if not self.tcpConn:
-            self.notify.warning("Unable to send message after connection is closed.")
-            return
-
-        if self.connectHttp:
-            if not self.tcpConn.sendDatagram(datagram):
-                self.notify.warning("Could not send datagram.")
-        else:
-            self.cw.send(datagram, self.tcpConn)
+        self.sendDatagram(datagram)
 
 
     # debugging funcs for simulating a network-plug-pull
     def pullNetworkPlug(self):
-        self.restoreNetworkPlug()
         self.notify.warning('*** SIMULATING A NETWORK-PLUG-PULL ***')
-        self.hijackedTcpConn = self.tcpConn
-        self.tcpConn = None
+        self.setSimulatedDisconnect(1)
 
     def networkPlugPulled(self):
-        return hasattr(self, 'hijackedTcpConn')
+        return self.getSimulatedDisconnect()
 
     def restoreNetworkPlug(self):
         if self.networkPlugPulled():
             self.notify.info('*** RESTORING SIMULATED PULLED-NETWORK-PLUG ***')
-            self.tcpConn = self.hijackedTcpConn
-            del self.hijackedTcpConn
+            self.setSimulatedDisconnect(0)
 
     def doFind(self, str):
         """ returns list of distributed objects with matching str in value """

+ 0 - 99
direct/src/distributed/ServerRepository.py

@@ -1,99 +0,0 @@
-"""ServerRepository module: contains the ServerRepository class"""
-
-from PandaModules import *
-from TaskManagerGlobal import *
-from MsgTypes import *
-import Task
-import DirectNotifyGlobal
-from PyDatagram import PyDatagram
-from PyDatagramIterator import PyDatagramIterator
-
-class ServerRepository:
-
-    def __init__(self, tcpPort, udpPort):
-        self.qcm = QueuedConnectionManager()
-        self.qcl = QueuedConnectionListener(self.qcm, 0)
-        self.qcr = QueuedConnectionReader(self.qcm, 0)
-        self.cw = ConnectionWriter(self.qcm,0)
-        self.tcpRendezvous = self.qcm.openTCPServerRendezvous(tcpPort, 10)
-        print self.tcpRendezvous
-        self.qcl.addConnection(self.tcpRendezvous)
-        self.startListenerPollTask()
-        self.startReaderPollTask()
-        self.startResetPollTask()
-
-    def startListenerPollTask(self):
-        taskMgr.add(self.listenerPoll, "serverListenerPollTask")
-
-    def listenerPoll(self, task):
-        if self.qcl.newConnectionAvailable():
-            print "New connection is available"
-            rendezvous = PointerToConnection()
-            netAddress = NetAddress()
-            newConnection = PointerToConnection()
-            retVal = self.qcl.getNewConnection(rendezvous, netAddress,
-                                               newConnection)
-            if retVal:
-                # Crazy dereferencing
-                newConnection=newConnection.p()
-                self.qcr.addConnection(newConnection)
-                print "Got a connection!"
-                self.lastConnection = newConnection
-            else:
-                ServerRepository.notify.warning(
-                    "getNewConnection returned false")
-        return Task.cont
-
-    def startReaderPollTask(self):
-        taskMgr.add(self.readerPollUntilEmpty, "serverReaderPollTask")
-
-    def readerPollUntilEmpty(self, task):
-        while self.readerPollOnce():
-            pass
-        return Task.cont
-
-    def readerPollOnce(self):
-        availGetVal = self.qcr.dataAvailable()
-        if availGetVal:
-            datagram = NetDatagram()
-            readRetVal = self.qcr.getData(datagram)
-            if readRetVal:
-                self.handleDatagram(datagram)
-            else:
-                ClientRepository.notify.warning("getData returned false")
-        return availGetVal
-
-    def handleDatagram(self, datagram):
-        print "Server got a datagram!"
-        dgi = PyDatagramIterator(datagram)
-        print dgi.getUint16()
-        print dgi.getString()
-        print dgi.getUint32()
-        print dgi.getUint16()
-
-        newDatagram = PyDatagram()
-        newDatagram.addUint16(LOGIN_RESPONSE)
-        newDatagram.addUint8(ord('s'))
-        self.cw.send(newDatagram, self.lastConnection)
-
-    def sendAvatarGenerate(self):
-        datagram = PyDatagram()
-        # Message type is 1
-        datagram.addUint16(ALL_OBJECT_GENERATE_WITH_REQUIRED)
-        # Avatar class type is 2
-        datagram.addUint8(2)
-        # A sample id
-        datagram.addUint32(10)
-        # The only required field is the zone field
-        datagram.addUint32(999)
-        self.cw.send(datagram, self.lastConnection)
-
-    def startResetPollTask(self):
-        return None
-    
-    def resetPollUntilEmpty(self):
-        return None
-
-    def resetPollOnce(self):
-        return None
-    

+ 17 - 2
direct/src/distributed/Sources.pp

@@ -1,3 +1,18 @@
-// For now, since we are not installing Python files, this file can
-// remain empty.
+#begin lib_target
+  #define BUILD_TARGET $[HAVE_PYTHON]
 
+  #define TARGET distributed
+  #define LOCAL_LIBS \
+    directbase
+  #define OTHER_LIBS \
+    downloader:c net:c panda:m express:c pandaexpress:m \
+    interrogatedb:c dconfig:c dtoolconfig:m \
+    dtoolutil:c dtoolbase:c dtool:m
+
+  #define SOURCES \
+    config_distributed.cxx config_distributed.h \
+    cConnectionRepository.cxx cConnectionRepository.I \
+    cConnectionRepository.h
+
+  #define IGATESCAN all
+#end lib_target

+ 97 - 0
direct/src/distributed/cConnectionRepository.I

@@ -0,0 +1,97 @@
+// Filename: cConnectionRepository.I
+// 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] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+#ifdef HAVE_NSPR
+////////////////////////////////////////////////////////////////////
+//     Function: CConnectionRepository::get_qcm
+//       Access: Published
+//  Description: Returns the QueuedConnectionManager object associated
+//               with the repository.
+////////////////////////////////////////////////////////////////////
+INLINE QueuedConnectionManager &CConnectionRepository::
+get_qcm() {
+  return _qcm;
+}
+#endif  // HAVE_NSPR
+
+#ifdef HAVE_NSPR
+////////////////////////////////////////////////////////////////////
+//     Function: CConnectionRepository::get_cw
+//       Access: Published
+//  Description: Returns the ConnectionWriter object associated
+//               with the repository.
+////////////////////////////////////////////////////////////////////
+INLINE ConnectionWriter &CConnectionRepository::
+get_cw() {
+  return _cw;
+}
+#endif  // HAVE_NSPR
+
+#ifdef HAVE_NSPR
+////////////////////////////////////////////////////////////////////
+//     Function: CConnectionRepository::get_qcr
+//       Access: Published
+//  Description: Returns the QueuedConnectionReader object associated
+//               with the repository.
+////////////////////////////////////////////////////////////////////
+INLINE QueuedConnectionReader &CConnectionRepository::
+get_qcr() {
+  return _qcr;
+}
+#endif  // HAVE_NSPR
+
+  INLINE ConnectionWriter &get_cw();
+  INLINE QueuedConnectionReader &get_qcr();
+
+////////////////////////////////////////////////////////////////////
+//     Function: CConnectionRepository::get_datagram
+//       Access: Published
+//  Description: Fills the datagram object with the datagram most
+//               recently retrieved by check_datagram().
+////////////////////////////////////////////////////////////////////
+INLINE void CConnectionRepository::
+get_datagram(Datagram &dg) {
+  dg = _dg;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CConnectionRepository::set_simulated_disconnect
+//       Access: Published
+//  Description: Sets the simulated disconnect flag.  While this is
+//               true, no datagrams will be retrieved from or sent to
+//               the server.  The idea is to simulate a temporary
+//               network outage.
+////////////////////////////////////////////////////////////////////
+INLINE void CConnectionRepository::
+set_simulated_disconnect(bool simulated_disconnect) {
+  _simulated_disconnect = simulated_disconnect;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CConnectionRepository::get_simulated_disconnect
+//       Access: Published
+//  Description: Returns the simulated disconnect flag.  While this is
+//               true, no datagrams will be retrieved from or sent to
+//               the server.  The idea is to simulate a temporary
+//               network outage.
+////////////////////////////////////////////////////////////////////
+INLINE bool CConnectionRepository::
+get_simulated_disconnect() const {
+  return _simulated_disconnect;
+}

+ 305 - 0
direct/src/distributed/cConnectionRepository.cxx

@@ -0,0 +1,305 @@
+// 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 "config_distributed.h"
+#include "httpChannel.h"
+#include "urlSpec.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: CConnectionRepository::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+CConnectionRepository::
+CConnectionRepository() :
+#ifdef HAVE_SSL
+  _http_conn(NULL),
+#endif
+#ifdef HAVE_NSPR
+  _cw(&_qcm, 0),
+  _qcr(&_qcm, 0),
+#endif
+  _simulated_disconnect(false)
+{
+#ifdef HAVE_NSPR
+  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().
+////////////////////////////////////////////////////////////////////
+bool CConnectionRepository::
+check_datagram() {
+  if (_simulated_disconnect) {
+    return false;
+  }
+
+  return do_check_datagram();
+}
+
+////////////////////////////////////////////////////////////////////
+//     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;
+  }
+
+#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();
+    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;
+}

+ 101 - 0
direct/src/distributed/cConnectionRepository.h

@@ -0,0 +1,101 @@
+// Filename: cConnectionRepository.h
+// 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] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef CCONNECTIONREPOSITORY_H
+#define CCONNECTIONREPOSITORY_H
+
+#include "directbase.h"
+#include "pointerTo.h"
+
+#ifdef HAVE_NSPR
+#include "queuedConnectionManager.h"
+#include "connectionWriter.h"
+#include "queuedConnectionReader.h"
+#include "connection.h"
+#endif
+
+class URLSpec;
+class HTTPChannel;
+class SocketStream;
+
+////////////////////////////////////////////////////////////////////
+//       Class : CConnectionRepository
+// Description : This class implements the C++ side of the
+//               ConnectionRepository object.  In particular, it
+//               manages the connection to the server once it has been
+//               opened (but does not open it directly).  It manages
+//               reading and writing datagrams on the connection and
+//               monitoring for unexpected disconnects as well as
+//               handling intentional disconnects.
+//
+//               Certain server messages, like field updates, are
+//               handled entirely within the C++ layer, while server
+//               messages that are not understood by the C++ layer are
+//               returned up to the Python layer for processing.
+////////////////////////////////////////////////////////////////////
+class EXPCL_DIRECT CConnectionRepository {
+PUBLISHED:
+  CConnectionRepository();
+  ~CConnectionRepository();
+
+#ifdef HAVE_SSL
+  void set_connection_http(HTTPChannel *channel);
+#endif
+#ifdef HAVE_NSPR
+  bool try_connect_nspr(const URLSpec &url);
+
+  INLINE QueuedConnectionManager &get_qcm();
+  INLINE ConnectionWriter &get_cw();
+  INLINE QueuedConnectionReader &get_qcr();
+#endif
+
+  bool check_datagram();
+  INLINE void get_datagram(Datagram &dg);
+  bool is_connected();
+
+  bool send_datagram(const Datagram &dg);
+
+  bool consider_flush();
+  bool flush();
+
+  void disconnect();
+
+  INLINE void set_simulated_disconnect(bool simulated_disconnect);
+  INLINE bool get_simulated_disconnect() const;
+
+private:
+  bool do_check_datagram();
+
+#ifdef HAVE_SSL
+  SocketStream *_http_conn;
+#endif
+
+#ifdef HAVE_NSPR
+  QueuedConnectionManager _qcm;
+  ConnectionWriter _cw;
+  QueuedConnectionReader _qcr;
+  PT(Connection) _nspr_conn;
+#endif
+
+  Datagram _dg;
+  bool _simulated_disconnect;
+};
+
+#include "cConnectionRepository.I"
+
+#endif  // CCONNECTIONREPOSITORY_H

+ 57 - 0
direct/src/distributed/config_distributed.cxx

@@ -0,0 +1,57 @@
+// Filename: config_distributed.cxx
+// Created by:  drose (19May04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "config_distributed.h"
+#include "dconfig.h"
+
+Configure(config_distributed);
+NotifyCategoryDef(distributed, "");
+
+ConfigureFn(config_distributed) {
+  init_libdistributed();
+}
+
+// This represents the amount of time to block waiting for the TCP
+// connection to the game server.  It is only used when the connection
+// method is NSPR.
+const int game_server_timeout_ms = config_distributed.GetInt("game-server-timeout-ms", 20000);
+
+// These represent the time in seconds by which to artificially lag
+// inbound messages.  It is only used when the connection method is
+// NSPR.
+const double min_lag = config_distributed.GetDouble("min-lag", 0.0);
+const double max_lag = config_distributed.GetDouble("max-lag", 0.0);
+
+////////////////////////////////////////////////////////////////////
+//     Function: init_libdistributed
+//  Description: 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_libdistributed() {
+  static bool initialized = false;
+  if (initialized) {
+    return;
+  }
+  initialized = true;
+
+}
+

+ 35 - 0
direct/src/distributed/config_distributed.h

@@ -0,0 +1,35 @@
+// Filename: config_distributed.h
+// Created by:  drose (19May04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef CONFIG_DISTRIBUTED_H
+#define CONFIG_DISTRIBUTED_H
+
+#include "directbase.h"
+#include "notifyCategoryProxy.h"
+#include "dconfig.h"
+
+NotifyCategoryDecl(distributed, EXPCL_DIRECT, EXPTP_DIRECT);
+
+extern const int game_server_timeout_ms;
+extern const double min_lag;
+extern const double max_lag;
+
+extern EXPCL_DIRECT void init_libdistributed();
+
+#endif
+