From ec535f800a2a2a1a2b3d96f0b6de707dea053c99 Mon Sep 17 00:00:00 2001 From: David Rose Date: Wed, 19 May 2004 20:19:36 +0000 Subject: [PATCH] CConnectionRepository --- direct/metalibs/direct/Sources.pp | 2 +- direct/src/cluster/ClusterServer.py | 2 - direct/src/distributed/ClientRepository.py | 7 +- .../src/distributed/ConnectionRepository.py | 208 +++--------- direct/src/distributed/ServerRepository.py | 99 ------ direct/src/distributed/Sources.pp | 19 +- .../src/distributed/cConnectionRepository.I | 97 ++++++ .../src/distributed/cConnectionRepository.cxx | 305 ++++++++++++++++++ .../src/distributed/cConnectionRepository.h | 101 ++++++ direct/src/distributed/config_distributed.cxx | 57 ++++ direct/src/distributed/config_distributed.h | 35 ++ 11 files changed, 661 insertions(+), 271 deletions(-) delete mode 100644 direct/src/distributed/ServerRepository.py create mode 100644 direct/src/distributed/cConnectionRepository.I create mode 100644 direct/src/distributed/cConnectionRepository.cxx create mode 100644 direct/src/distributed/cConnectionRepository.h create mode 100644 direct/src/distributed/config_distributed.cxx create mode 100644 direct/src/distributed/config_distributed.h diff --git a/direct/metalibs/direct/Sources.pp b/direct/metalibs/direct/Sources.pp index 9c06538266..eecbe14258 100644 --- a/direct/metalibs/direct/Sources.pp +++ b/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 \ diff --git a/direct/src/cluster/ClusterServer.py b/direct/src/cluster/ClusterServer.py index 730ff437ef..7e98e8763b 100644 --- a/direct/src/cluster/ClusterServer.py +++ b/direct/src/cluster/ClusterServer.py @@ -1,5 +1,3 @@ -"""ServerRepository module: contains the ServerRepository class""" - from PandaModules import * from ClusterMsgs import * from MsgTypes import * diff --git a/direct/src/distributed/ClientRepository.py b/direct/src/distributed/ClientRepository.py index d80be01607..8c5e29fc7f 100644 --- a/direct/src/distributed/ClientRepository.py +++ b/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.""" diff --git a/direct/src/distributed/ConnectionRepository.py b/direct/src/distributed/ConnectionRepository.py index 170282de72..f77575f6c1 100644 --- a/direct/src/distributed/ConnectionRepository.py +++ b/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 + 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) +## 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 +## # 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 + if self.checkDatagram(): + dg = PyDatagram() + self.getDatagram(dg) + self.handleDatagram(dg) + return 1 - # 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.") + # 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 """ diff --git a/direct/src/distributed/ServerRepository.py b/direct/src/distributed/ServerRepository.py deleted file mode 100644 index 18721c6e9e..0000000000 --- a/direct/src/distributed/ServerRepository.py +++ /dev/null @@ -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 - diff --git a/direct/src/distributed/Sources.pp b/direct/src/distributed/Sources.pp index a03ea8c336..931be7587d 100644 --- a/direct/src/distributed/Sources.pp +++ b/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 diff --git a/direct/src/distributed/cConnectionRepository.I b/direct/src/distributed/cConnectionRepository.I new file mode 100644 index 0000000000..a835e8b994 --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + + +#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; +} diff --git a/direct/src/distributed/cConnectionRepository.cxx b/direct/src/distributed/cConnectionRepository.cxx new file mode 100644 index 0000000000..22eff8afa4 --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#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; +} diff --git a/direct/src/distributed/cConnectionRepository.h b/direct/src/distributed/cConnectionRepository.h new file mode 100644 index 0000000000..804fd47ffd --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#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 diff --git a/direct/src/distributed/config_distributed.cxx b/direct/src/distributed/config_distributed.cxx new file mode 100644 index 0000000000..2ee3b9fe99 --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#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; + +} + diff --git a/direct/src/distributed/config_distributed.h b/direct/src/distributed/config_distributed.h new file mode 100644 index 0000000000..835dbb88bd --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#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 +