mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-02 09:52:27 -04:00
CConnectionRepository
This commit is contained in:
parent
0058660904
commit
ec535f800a
@ -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 \
|
||||
|
@ -1,5 +1,3 @@
|
||||
"""ServerRepository module: contains the ServerRepository class"""
|
||||
|
||||
from PandaModules import *
|
||||
from ClusterMsgs import *
|
||||
from MsgTypes import *
|
||||
|
@ -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."""
|
||||
|
@ -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 """
|
||||
|
@ -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
|
||||
|
@ -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
direct/src/distributed/cConnectionRepository.I
Normal file
97
direct/src/distributed/cConnectionRepository.I
Normal file
@ -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;
|
||||
}
|
305
direct/src/distributed/cConnectionRepository.cxx
Normal file
305
direct/src/distributed/cConnectionRepository.cxx
Normal file
@ -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;
|
||||
}
|
101
direct/src/distributed/cConnectionRepository.h
Normal file
101
direct/src/distributed/cConnectionRepository.h
Normal file
@ -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
|
57
direct/src/distributed/config_distributed.cxx
Normal file
57
direct/src/distributed/config_distributed.cxx
Normal file
@ -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;
|
||||
|
||||
}
|
||||
|
35
direct/src/distributed/config_distributed.h
Normal file
35
direct/src/distributed/config_distributed.h
Normal file
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user